From 90ee575397544158a603cd7946bd6fec5e4c1407 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 7 Mar 2022 17:25:26 +0100 Subject: [PATCH 001/878] Remove explicit content-type as it results in requests from banana cake pop responding with 404 --- backend/src/Startup.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 826343bd..017b3937 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -190,10 +190,6 @@ public void Configure(IApplicationBuilder app) IncludeCookies = false, GraphQLEndpoint = "/graphql", HttpMethod = DefaultHttpMethod.Post, - HttpHeaders = new HeaderDictionary - { - { "Content-Type", "application/json" } - }, Title = "GraphQL" } } From fe4d4ba8af10055e58962496c8efd24baa627f8b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 7 Mar 2022 17:30:06 +0100 Subject: [PATCH 002/878] Link commit 90ee575397544158a603cd7946bd6fec5e4c1407 --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc1d5730..42675532 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [v1.0.0] - 2022-02-28 - ### Added - @@ -95,6 +93,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Remove explicit content-type as it results in requests from banana cake pop responding with 404 in commit [90ee575397544158a603cd7946bd6fec5e4c1407](https://github.com/building-envelope-data/database/commit/90ee575397544158a603cd7946bd6fec5e4c1407) - - - @@ -119,8 +118,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - - -- + +## [v1.0.0] - 2022-02-28 [Unreleased]: https://github.com/building-envelope-data/database/compare/v1.0.0...HEAD -[v1.0.0]: https://github.com/building-envelope-data/database/compare/d45f78d150b58e016d63a827124b740765c4d350...v1.0.0 +[v1.0.0]: https://github.com/building-envelope-data/database/compare/352f44e492ca66d7835970cdf007b2897e686e30...v1.0.0 From 81fbc2938cd653c1fc8eeda60583d53e688b9553 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 7 Mar 2022 18:05:59 +0100 Subject: [PATCH 003/878] Upgrade backend packages to latest compatible versions --- backend/src/Database.csproj | 18 +++++++++--------- backend/test/Database.Tests.csproj | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 0d23ecf9..0383d942 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -19,23 +19,23 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - - - + + + + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index f8e9b348..b12d8dd6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -12,9 +12,9 @@ - - - + + + @@ -23,7 +23,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From cf018b7ddd7dd5d363d853d7b4f1b047fc4dd0b6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 7 Mar 2022 18:06:11 +0100 Subject: [PATCH 004/878] Upgrade frontend packages to latest compatible versions --- frontend/yarn.lock | 3976 +++++++++++++++++--------------------------- 1 file changed, 1557 insertions(+), 2419 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 62ee9010..96edf6a0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,6 +2,13 @@ # yarn lockfile v1 +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + "@ant-design/colors@^6.0.0": version "6.0.0" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" @@ -68,17 +75,17 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" - integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.16.0" + "@babel/highlight" "^7.16.7" -"@babel/compat-data@^7.16.0", "@babel/compat-data@^7.16.4": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e" - integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q== +"@babel/compat-data@^7.16.4", "@babel/compat-data@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" + integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== "@babel/core@7.15.5": version "7.15.5" @@ -101,158 +108,158 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.7.2", "@babel/core@^7.7.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.5.tgz#924aa9e1ae56e1e55f7184c8bf073a50d8677f5c" - integrity sha512-wUcenlLzuWMZ9Zt8S0KmFwGlH6QKRh3vsm/dhDA3CHkiTA45YuG1XkHRcNRl73EFPXDp/d5kVOU0/y7x2w6OaQ== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.5" - "@babel/helper-compilation-targets" "^7.16.3" - "@babel/helper-module-transforms" "^7.16.5" - "@babel/helpers" "^7.16.5" - "@babel/parser" "^7.16.5" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" +"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.17.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" + integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.17.2" + "@babel/parser" "^7.17.3" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.1.2" semver "^6.3.0" - source-map "^0.5.0" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.16.0", "@babel/generator@^7.16.5", "@babel/generator@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.5.tgz#26e1192eb8f78e0a3acaf3eede3c6fc96d22bedf" - integrity sha512-kIvCdjZqcdKqoDbVVdt5R99icaRtrtYhYK/xux5qiWCBmfdvEYMFZ68QCrpE5cbFM1JsuArUNs1ZkuKtTtUcZA== +"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.17.3", "@babel/generator@^7.7.2": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" + integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.17.0" jsesc "^2.5.1" source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d" - integrity sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg== +"@babel/helper-annotate-as-pure@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" + integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.3.tgz#5b480cd13f68363df6ec4dc8ac8e2da11363cbf0" - integrity sha512-vKsoSQAyBmxS35JUOOt+07cLc6Nk/2ljLIHwmq2/NM6hdioUaqEXq/S+nXvbvXbZkNDlWOymPanJGOc4CBjSJA== +"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" + integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== dependencies: - "@babel/compat-data" "^7.16.0" - "@babel/helper-validator-option" "^7.14.5" + "@babel/compat-data" "^7.16.4" + "@babel/helper-validator-option" "^7.16.7" browserslist "^4.17.5" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.5.tgz#5d1bcd096792c1ebec6249eebc6358eec55d0cad" - integrity sha512-NEohnYA7mkB8L5JhU7BLwcBdU3j83IziR9aseMueWGeAjblbul3zzb8UvJ3a1zuBiqCMObzCJHFqKIQE6hTVmg== +"@babel/helper-create-class-features-plugin@^7.16.7": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" + integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-member-expression-to-functions" "^7.16.5" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/helper-replace-supers" "^7.16.5" - "@babel/helper-split-export-declaration" "^7.16.0" + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" -"@babel/helper-environment-visitor@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.5.tgz#f6a7f38b3c6d8b07c88faea083c46c09ef5451b8" - integrity sha512-ODQyc5AnxmZWm/R2W7fzhamOk1ey8gSguo5SGvF0zcB3uUzRpTRmM/jmLSm9bDMyPlvbyJ+PwPEK0BWIoZ9wjg== +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481" - integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog== +"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" + integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== dependencies: - "@babel/helper-get-function-arity" "^7.16.0" - "@babel/template" "^7.16.0" - "@babel/types" "^7.16.0" + "@babel/helper-get-function-arity" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-get-function-arity@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa" - integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ== +"@babel/helper-get-function-arity@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" + integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a" - integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg== +"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-member-expression-to-functions@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.5.tgz#1bc9f7e87354e86f8879c67b316cb03d3dc2caab" - integrity sha512-7fecSXq7ZrLE+TWshbGT+HyCLkxloWNhTbU2QM1NTI/tDqyf0oZiMcEfYtDuUDCo528EOlt39G1rftea4bRZIw== +"@babel/helper-member-expression-to-functions@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" + integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3" - integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg== +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.5.tgz#530ebf6ea87b500f60840578515adda2af470a29" - integrity sha512-CkvMxgV4ZyyioElFwcuWnDCcNIeyqTkCm9BxXZi73RR1ozqlpboqsbGUNvRTflgZtFbbJ1v5Emvm+lkjMYY/LQ== - dependencies: - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-simple-access" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.16.7": + version "7.17.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz#3c3b03cc6617e33d68ef5a27a67419ac5199ccd0" + integrity sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" -"@babel/helper-optimise-call-expression@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338" - integrity sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw== +"@babel/helper-optimise-call-expression@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" + integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" "@babel/helper-plugin-utils@7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.5", "@babel/helper-plugin-utils@^7.8.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.5.tgz#afe37a45f39fce44a3d50a7958129ea5b1a5c074" - integrity sha512-59KHWHXxVA9K4HNF4sbHCf+eJeFe0Te/ZFGqBT4OjXhrwvA04sGfaEGsVTdsjoszq0YTP49RC9UKe5g8uN2RwQ== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-replace-supers@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.5.tgz#96d3988bd0ab0a2d22c88c6198c3d3234ca25326" - integrity sha512-ao3seGVa/FZCMCCNDuBcqnBFSbdr8N2EW35mzojx3TwfIbdPmNK+JV6+2d5bR0Z71W5ocLnQp9en/cTF7pBJiQ== +"@babel/helper-replace-supers@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" + integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== dependencies: - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-member-expression-to-functions" "^7.16.5" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-member-expression-to-functions" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/traverse" "^7.16.7" + "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.0.tgz#21d6a27620e383e37534cf6c10bba019a6f90517" - integrity sha512-o1rjBT/gppAqKsYfUdfHq5Rk03lMQrkPHG1OWzHWpLgVXRH4HnMM9Et9CVdIqwkCQlobnGHEJMsgWP/jE1zUiw== +"@babel/helper-simple-access@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" + integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" @@ -261,38 +268,38 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438" - integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw== +"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== dependencies: - "@babel/types" "^7.16.0" + "@babel/types" "^7.16.7" -"@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": - version "7.15.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389" - integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helper-validator-option@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helpers@^7.15.4", "@babel/helpers@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.5.tgz#29a052d4b827846dd76ece16f565b9634c554ebd" - integrity sha512-TLgi6Lh71vvMZGEkFuIxzaPsyeYCHQ5jJOOX1f0xXn0uciFuE8cEk0wyBquMcCxBXZ5BJhE2aUB7pnWTD150Tw== +"@babel/helpers@^7.15.4", "@babel/helpers@^7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" + integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== dependencies: - "@babel/template" "^7.16.0" - "@babel/traverse" "^7.16.5" - "@babel/types" "^7.16.0" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.0" + "@babel/types" "^7.17.0" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a" - integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== dependencies: - "@babel/helper-validator-identifier" "^7.15.7" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" @@ -301,34 +308,29 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@7.16.4": - version "7.16.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" - integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.0", "@babel/parser@^7.16.3", "@babel/parser@^7.16.5", "@babel/parser@^7.7.2": - version "7.16.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.6.tgz#8f194828193e8fa79166f34a4b4e52f3e769a314" - integrity sha512-Gr86ujcNuPDnNOY8mi383Hvi8IYrJVJYuf3XcuBM/Dgd+bINn/7tHqsj+tKkoreMbmGsFLsltI/JJd8fOFWGDQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" + integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== "@babel/plugin-proposal-class-properties@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.5.tgz#3269f44b89122110f6339806e05d43d84106468a" - integrity sha512-pJD3HjgRv83s5dv1sTnDbZOaTjghKEz8KUn1Kbh2eAIRhGuyQ1XSeI4xVXU3UlIEVA3DAyIdxqT1eRn7Wcn55A== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.5" - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.5.tgz#f30f80dacf7bc1404bf67f99c8d9c01665e830ad" - integrity sha512-UEd6KpChoyPhCoE840KRHOlGhEZFutdPDMGj+0I56yuTTOaT51GzmnEl/0uT41fB/vD2nT+Pci2KjezyE3HmUw== + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" + integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-compilation-targets" "^7.16.3" - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/compat-data" "^7.17.0" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.5" + "@babel/plugin-transform-parameters" "^7.16.7" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -351,12 +353,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.5.tgz#ca0d85e12d71b825b4e9fd1f8d29b64acdf1b46e" - integrity sha512-Nrx+7EAJx1BieBQseZa2pavVH2Rp7hADK2xn7coYqVbWRu9C2OFizYcsKo6TrrqJkJl+qF/+Qqzrk/+XDu4GnA== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -372,19 +374,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.14.5.tgz#000e2e25d8673cce49300517a3eda44c263e4201" - integrity sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.5.tgz#bf255d252f78bc8b77a17cadc37d1aa5b8ed4394" - integrity sha512-42OGssv9NPk4QHKVgIHlzeLgPOW5rGgfV5jzG90AhcXXIv6hu/eqj63w4VgvRxdvZY3AlYeDgPiSJ3BqAd1Y6Q== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" + integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -436,200 +431,194 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.5.tgz#f47a33e4eee38554f00fb6b2f894fa1f5649b0b3" - integrity sha512-/d4//lZ1Vqb4mZ5xTep3dDK888j7BGM/iKqBmndBaoYAFPlPKrGU608VVBz5JeyAb6YQDjRu1UKqj86UhwWVgw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.5.tgz#04c18944dd55397b521d9d7511e791acea7acf2d" - integrity sha512-8bTHiiZyMOyfZFULjsCnYOWG059FVMes0iljEHSfARhNgFfpsqE92OrCffv3veSw9rwMkYcFe9bj0ZoXU2IGtQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" + integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.5.tgz#af087494e1c387574260b7ee9b58cdb5a4e9b0b0" - integrity sha512-BxmIyKLjUGksJ99+hJyL/HIxLIGnLKtw772zYDER7UuycDZ+Xvzs98ZQw6NGgM2ss4/hlFAaGiZmMNKvValEjw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" + integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.5.tgz#b91f254fe53e210eabe4dd0c40f71c0ed253c5e7" - integrity sha512-JxjSPNZSiOtmxjX7PBRBeRJTUKTyJ607YUYeT0QJCNdsedOe+/rXITjP08eG8xUpsLfPirgzdCFN+h0w6RI+pQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" + integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-classes@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.5.tgz#6acf2ec7adb50fb2f3194dcd2909dbd056dcf216" - integrity sha512-DzJ1vYf/7TaCYy57J3SJ9rV+JEuvmlnvvyvYKFbk5u46oQbBvuB9/0w+YsVsxkOv8zVWKpDmUoj4T5ILHoXevA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-optimise-call-expression" "^7.16.0" - "@babel/helper-plugin-utils" "^7.16.5" - "@babel/helper-replace-supers" "^7.16.5" - "@babel/helper-split-export-declaration" "^7.16.0" + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" + integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-optimise-call-expression" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.5.tgz#2af91ebf0cceccfcc701281ada7cfba40a9b322a" - integrity sha512-n1+O7xtU5lSLraRzX88CNcpl7vtGdPakKzww74bVwpAIRgz9JVLJJpOLb0uYqcOaXVM0TL6X0RVeIJGD2CnCkg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" + integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.5.tgz#89ebc87499ac4a81b897af53bb5d3eed261bd568" - integrity sha512-GuRVAsjq+c9YPK6NeTkRLWyQskDC099XkBSVO+6QzbnOnH2d/4mBVXYStaPrZD3dFRfg00I6BFJ9Atsjfs8mlg== + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" + integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.5.tgz#8ceb65ab6ca4a349e04d1887e2470a5bfe8f046f" - integrity sha512-skE02E/MptkZdBS4HwoRhjWXqeKQj0BWKEAPfPC+8R4/f6bjQqQ9Nftv/+HkxWwnVxh/E2NV9TNfzLN5H/oiBw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" - "@babel/plugin-syntax-flow" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" "@babel/plugin-transform-for-of@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.5.tgz#9b544059c6ca11d565457c0ff1f08e13ce225261" - integrity sha512-+DpCAJFPAvViR17PIMi9x2AE34dll5wNlXO43wagAX2YcRGgEVHCNFC4azG85b4YyyFarvkc/iD5NPrz4Oneqw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" + integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-function-name@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.5.tgz#6896ebb6a5538a75d6a4086a277752f655a7bd15" - integrity sha512-Fuec/KPSpVLbGo6z1RPw4EE1X+z9gZk1uQmnYy7v4xr4TO9p41v1AoUuXEtyqAI7H+xNJYSICzRqZBhDEkd3kQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" + integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== dependencies: - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-literals@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.5.tgz#af392b90e3edb2bd6dc316844cbfd6b9e009d320" - integrity sha512-B1j9C/IfvshnPcklsc93AVLTrNVa69iSqztylZH6qnmiAsDDOmmjEYqOm3Ts2lGSgTSywnBNiqC949VdD0/gfw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" + integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.5.tgz#4bd6ecdc11932361631097b779ca5c7570146dd5" - integrity sha512-d57i3vPHWgIde/9Y8W/xSFUndhvhZN5Wu2TjRrN1MVz5KzdUihKnfDVlfP1U7mS5DNj/WHHhaE4/tTi4hIyHwQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" + integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.5.tgz#4ee03b089536f076b2773196529d27c32b9d7bde" - integrity sha512-ABhUkxvoQyqhCWyb8xXtfwqNMJD7tx+irIRnUh6lmyFud7Jln1WzONXKlax1fg/ey178EXbs4bSGNd6PngO+SQ== + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" + integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== dependencies: - "@babel/helper-module-transforms" "^7.16.5" - "@babel/helper-plugin-utils" "^7.16.5" - "@babel/helper-simple-access" "^7.16.0" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-simple-access" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" "@babel/plugin-transform-object-super@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.5.tgz#8ccd9a1bcd3e7732ff8aa1702d067d8cd70ce380" - integrity sha512-tded+yZEXuxt9Jdtkc1RraW1zMF/GalVxaVVxh41IYwirdRgyAxxxCKZ9XB7LxZqmsjfjALxupNE1MIz9KH+Zg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" + integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" - "@babel/helper-replace-supers" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.5": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.5.tgz#4fc74b18a89638bd90aeec44a11793ecbe031dde" - integrity sha512-B3O6AL5oPop1jAVg8CV+haeUte9oFuY85zu0jwnRNZZi3tVAbJriu5tag/oaO2kGaQM/7q7aGPBlTI5/sr9enA== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" + integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.5.tgz#58f1465a7202a2bb2e6b003905212dd7a79abe3f" - integrity sha512-+IRcVW71VdF9pEH/2R/Apab4a19LVvdVsr/gEeotH00vSDVlKD+XgfSIw+cgGWsjDB/ziqGv/pGoQZBIiQVXHg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" + integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.5.tgz#d5e910327d7931fb9f8f9b6c6999473ceae5a286" - integrity sha512-dHYCOnzSsXFz8UcdNQIHGvg94qPL/teF7CCiCEMRxmA1G2p5Mq4JnKVowCDxYfiQ9D7RstaAp9kwaSI+sXbnhw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" + integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.5.tgz#5298aedc5f81e02b1cb702e597e8d6a346675765" - integrity sha512-+arLIz1d7kmwX0fKxTxbnoeG85ONSnLpvdODa4P3pc1sS7CV1hfmtYWufkW/oYsPnkDrEeQFxhUWcFnrXW7jQQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.0" - "@babel/helper-module-imports" "^7.16.0" - "@babel/helper-plugin-utils" "^7.16.5" - "@babel/plugin-syntax-jsx" "^7.16.5" - "@babel/types" "^7.16.0" + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" + integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-jsx" "^7.16.7" + "@babel/types" "^7.17.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.5.tgz#ccb60b1a23b799f5b9a14d97c5bc81025ffd96d7" - integrity sha512-ZbuWVcY+MAXJuuW7qDoCwoxDUNClfZxoo7/4swVbOW1s/qYLOMHlm9YRWMsxMFuLs44eXsv4op1vAaBaBaDMVg== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" + integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-transform-spread@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.5.tgz#912b06cff482c233025d3e69cf56d3e8fa166c29" - integrity sha512-5d6l/cnG7Lw4tGHEoga4xSkYp1euP7LAtrah1h1PgJ3JY7yNsjybsxQAnVK4JbtReZ/8z6ASVmd3QhYYKLaKZw== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" + integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.5.tgz#343651385fd9923f5aa2275ca352c5d9183e1773" - integrity sha512-gnyKy9RyFhkovex4BjKWL3BVYzUDG6zC0gba7VMLbQoDuqMfJ1SDXs8k/XK41Mmt1Hyp4qNAvGFb9hKzdCqBRQ== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" + integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== dependencies: - "@babel/helper-plugin-utils" "^7.16.5" + "@babel/helper-plugin-utils" "^7.16.7" "@babel/runtime-corejs3@^7.10.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.5.tgz#9057d879720c136193f0440bc400088212a74894" - integrity sha512-F1pMwvTiUNSAM8mc45kccMQxj31x3y3P+tA/X8hKNWp3/hUsxdGxZ3D3H8JIkxtfA8qGkaBTKvcmvStaYseAFw== - dependencies: - core-js-pure "^3.19.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" - integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" + integrity sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg== dependencies: + core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.5.tgz#7f3e34bf8bdbbadf03fbb7b1ea0d929569c9487a" - integrity sha512-TXWihFIS3Pyv5hzR7j6ihmeLkZfrXGxAr5UfSl8CHf+6q/wpiYDkUau0czckpYG8QmnCIuPpdLtuA9VmuGGyMA== + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" + integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.15.4", "@babel/template@^7.16.0", "@babel/template@^7.3.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6" - integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A== +"@babel/template@^7.15.4", "@babel/template@^7.16.7", "@babel/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/parser" "^7.16.0" - "@babel/types" "^7.16.0" + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" "@babel/traverse@7.15.4": version "7.15.4" @@ -646,51 +635,28 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@7.16.3": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787" - integrity sha512-eolumr1vVMjqevCpwVO99yN/LoGL0EyHiLO5I043aYQvwOJ9eR5UsZSClHVCzfhBduMAsSzgA/6AyqPjNayJag== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.0" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.3" - "@babel/types" "^7.16.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.1.0", "@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.5", "@babel/traverse@^7.7.2": - version "7.16.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.5.tgz#d7d400a8229c714a59b87624fc67b0f1fbd4b2b3" - integrity sha512-FOCODAzqUMROikDYLYxl4nmwiLlu85rNqBML/A5hKRVXG2LV8d0iMqgPzdYTcIpjZEBB7D6UDU9vxRZiriASdQ== - dependencies: - "@babel/code-frame" "^7.16.0" - "@babel/generator" "^7.16.5" - "@babel/helper-environment-visitor" "^7.16.5" - "@babel/helper-function-name" "^7.16.0" - "@babel/helper-hoist-variables" "^7.16.0" - "@babel/helper-split-export-declaration" "^7.16.0" - "@babel/parser" "^7.16.5" - "@babel/types" "^7.16.0" +"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@7.15.0": - version "7.15.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.0.tgz#61af11f2286c4e9c69ca8deb5f4375a73c72dcbd" - integrity sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ== +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== dependencies: - "@babel/helper-validator-identifier" "^7.14.9" - to-fast-properties "^2.0.0" - -"@babel/types@7.16.0", "@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba" - integrity sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" + "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -728,21 +694,21 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@graphql-codegen/add@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.0.tgz#cd02fd6d80a7f62839cb27160b62e48366a237c5" - integrity sha512-vRRHpuUFadYXnPrb5RNiIzm3Ao39UxjvrdX760lEfXh2qeG7YddM5QFC+ev2BH3X452R15gv/gf/rXy0+Hqm1A== +"@graphql-codegen/add@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.1.tgz#e161ff1c7cdf74ce20b32f75f640f9592b9a18ca" + integrity sha512-XkVwcqosa0CVBlL1HaQT0gp+EUfhuQE3LzrEpzMQLwchxaj/NPVYtOJL6MUHaYDsHzLqxWrufjfbeB3y2NQgRw== dependencies: - "@graphql-codegen/plugin-helpers" "^2.1.0" + "@graphql-codegen/plugin-helpers" "^2.3.2" tslib "~2.3.0" "@graphql-codegen/cli@^2.0.1": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.3.0.tgz#07ef86eea6d42c56f9c7118871115d779dee49f3" - integrity sha512-5pgrcnBFeOJbDGfZwhnytwMqpwFDrmdaAgD6HYywT9Fggsrx2yWSkVrxoAjEcbDMFLhxzjxECwfJYklm967AKA== + version "2.6.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.6.2.tgz#a9aa4656141ee0998cae8c7ad7d0bf9ca8e0c9ae" + integrity sha512-UO75msoVgvLEvfjCezM09cQQqp32+mR8Ma1ACsBpr7nroFvHbgcu2ulx1cMovg4sxDBCsvd9Eq/xOOMpARUxtw== dependencies: - "@graphql-codegen/core" "2.3.0" - "@graphql-codegen/plugin-helpers" "^2.3.0" + "@graphql-codegen/core" "2.5.1" + "@graphql-codegen/plugin-helpers" "^2.4.1" "@graphql-tools/apollo-engine-loader" "^7.0.5" "@graphql-tools/code-file-loader" "^7.0.6" "@graphql-tools/git-loader" "^7.0.5" @@ -765,14 +731,14 @@ glob "^7.1.6" globby "^11.0.4" graphql-config "^4.1.0" - inquirer "^7.3.3" + inquirer "^8.0.0" is-glob "^4.0.1" json-to-pretty-yaml "^1.2.2" latest-version "5.1.0" listr "^0.14.3" listr-update-renderer "^0.5.0" log-symbols "^4.0.0" - minimatch "^3.0.4" + minimatch "^4.0.0" mkdirp "^1.0.4" string-env-interpolation "^1.0.1" ts-log "^2.2.3" @@ -782,32 +748,32 @@ yaml "^1.10.0" yargs "^17.0.0" -"@graphql-codegen/core@2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.3.0.tgz#0903b315e90a1b7575b5684401c8d0c26256ff4f" - integrity sha512-dGBd5DEOB1hJ3Ddd6l+3U4cbDZ91e0BIJQGTyZPLRYyffqJP+Y7IEYm4lMaBNY9m6qVXeGq+fgCS2SXlXEzJoA== +"@graphql-codegen/core@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.5.1.tgz#e3d50d3449b8c58b74ea08e97faf656a1b7fc8a1" + integrity sha512-alctBVl2hMnBXDLwkgmnFPrZVIiBDsWJSmxJcM4GKg1PB23+xuov35GE47YAyAhQItE1B1fbYnbb1PtGiDZ4LA== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" + "@graphql-codegen/plugin-helpers" "^2.4.1" "@graphql-tools/schema" "^8.1.2" "@graphql-tools/utils" "^8.1.1" tslib "~2.3.0" "@graphql-codegen/fragment-matcher@^3.1.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-3.2.0.tgz#03857bdefe70cf1b50900ba4eb1815906a461f44" - integrity sha512-+i7wfatf48exKL9gWTOqu4bH/+5+34W+n20kzDN0naWmmC4LxcML2/n4MUKkU4TfqfwiA6RZ4E/wzvwTMUY7Mg== + version "3.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-3.2.1.tgz#cbaf886d2322cba9ae66e68d7cee512edeaab5b0" + integrity sha512-Hy7tgp5dqDHSUFAfePsK7h5qNk1JWCljpR9gqeY2hnTYzI4YI4jdvH19RiSJ97Px5Ykd15KHzSgj9g0SHm4l8A== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" + "@graphql-codegen/plugin-helpers" "^2.3.2" tslib "~2.3.0" "@graphql-codegen/import-types-preset@^2.0.0": - version "2.1.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.1.8.tgz#2259e93aeeaefef66cfbf31faa023a4c78efe751" - integrity sha512-mnjDwKhKq3LL24/6d92tsGHu1J9UfMiRSDCWgnzEZEK+0AN27uw2OszpABWe47kRNJ4gjQ19Z/V9AyWxDttMWw== + version "2.1.12" + resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.1.12.tgz#344baae04ed3e1f20ca44a88a061a264728048a2" + integrity sha512-nFipXzsLJnjZY06PKBovg1hxnueB5n/RSlBPYA29ymahEenNR1vMejbq8/+iuRoxexLuYVSVnJAEcqoBriUKrg== dependencies: - "@graphql-codegen/add" "^3.1.0" - "@graphql-codegen/plugin-helpers" "^2.3.0" - "@graphql-codegen/visitor-plugin-common" "2.5.1" + "@graphql-codegen/add" "^3.1.1" + "@graphql-codegen/plugin-helpers" "^2.4.0" + "@graphql-codegen/visitor-plugin-common" "2.7.1" tslib "~2.3.0" "@graphql-codegen/plugin-helpers@^1.18.5", "@graphql-codegen/plugin-helpers@^1.18.8": @@ -821,45 +787,45 @@ lodash "~4.17.0" tslib "~2.3.0" -"@graphql-codegen/plugin-helpers@^2.0.0", "@graphql-codegen/plugin-helpers@^2.1.0", "@graphql-codegen/plugin-helpers@^2.3.0": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.3.1.tgz#b66c742d3209a85bc2f72f9e2eceb6b08fc81f85" - integrity sha512-rWH7igcjYqZ6rqNFTb4Wyp31863fRmmVpsRN8VHzBCltrepOO97jwTwB93aAw+T6Dm8aZto3QFfDIC79u8wA2Q== +"@graphql-codegen/plugin-helpers@^2.0.0", "@graphql-codegen/plugin-helpers@^2.3.2", "@graphql-codegen/plugin-helpers@^2.4.0", "@graphql-codegen/plugin-helpers@^2.4.1": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.4.2.tgz#e4f6b74dddcf8a9974fef5ce48562ae0980f9fed" + integrity sha512-LJNvwAPv/sKtI3RnRDm+nPD+JeOfOuSOS4FFIpQCMUCyMnFcchV/CPTTv7tT12fLUpEg6XjuFfDBvOwndti30Q== dependencies: "@graphql-tools/utils" "^8.5.2" change-case-all "1.0.14" - common-tags "1.8.0" + common-tags "1.8.2" import-from "4.0.0" lodash "~4.17.0" tslib "~2.3.0" -"@graphql-codegen/schema-ast@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-2.4.0.tgz#a41fdf2bacf88ec8318c78fdf03f19e620e5534a" - integrity sha512-xsLd4mF0H3mPp7Z2PHZ2Sv1KKIb/FOORqhDc9XHCnHJJ/h9nmaleKSTxTGzSUfIAQ1aCNzVilcaUwlenK+MMBQ== +"@graphql-codegen/schema-ast@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-2.4.1.tgz#ad742b53e32f7a2fbff8ea8a91ba7e617e6ef236" + integrity sha512-bIWlKk/ShoVJfghA4Rt1OWnd34/dQmZM/vAe6fu6QKyOh44aAdqPtYQ2dbTyFXoknmu504etKJGEDllYNUJRfg== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" + "@graphql-codegen/plugin-helpers" "^2.3.2" "@graphql-tools/utils" "^8.1.1" tslib "~2.3.0" "@graphql-codegen/typescript-operations@^2.0.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.2.1.tgz#78a4ca25fa7d82ef259791f7b8a5a146f9e89830" - integrity sha512-f1Y/qhn6yCGGVE8cAL2xzFryW4UQjnYS+vHYEKvr2X0DeMoMhp/5QCJ07HGWbjHYGBy4L5FhND/6lCbsJxG2EQ== + version "2.3.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.3.2.tgz#1a94c352b196030d0bf3701ed38343c8619c94e0" + integrity sha512-VLB466ffcT25imuq85ANVqVotVaT4T1gJ6FLJsiJkn5U/iMMZa8CvP+l9oSqDlaBotv38LIZX6pdWGNGAn7kDA== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" - "@graphql-codegen/typescript" "^2.4.1" - "@graphql-codegen/visitor-plugin-common" "2.5.1" + "@graphql-codegen/plugin-helpers" "^2.4.0" + "@graphql-codegen/typescript" "^2.4.5" + "@graphql-codegen/visitor-plugin-common" "2.7.1" auto-bind "~4.0.0" tslib "~2.3.0" "@graphql-codegen/typescript-react-apollo@^3.0.0": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.2.2.tgz#d17301f5c0e5b9bb719de187861b06633e32f9fb" - integrity sha512-5bAWTaQlc274cxUT3Quc13tIDIXuU3BnzXsm28egQUof+UbzQKn5TDt+wPxVYXPQeAj8ti+Ak5Af5Ql7/zeddw== + version "3.2.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.2.8.tgz#776e4f219c9484ff43249569079091fe912bb8d4" + integrity sha512-XmYeBSS3KZF9WePMN5RqYwOHbDybhOxB89C+fvUscFX8MVI5DugMI99qYsdqS8mWQfElRe8ib68zKPaIGTnC0w== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" - "@graphql-codegen/visitor-plugin-common" "2.5.1" + "@graphql-codegen/plugin-helpers" "^2.4.0" + "@graphql-codegen/visitor-plugin-common" "2.7.1" auto-bind "~4.0.0" change-case-all "1.0.14" tslib "~2.3.0" @@ -877,13 +843,13 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^2.0.0": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.4.2.tgz#e6d217c240a21fbf4510e7a486755746b8126b4d" - integrity sha512-ygAl2PdZsmzUtDJrmxCRQUemxZcyi71GX3xz2LzVpYduvuKmjejCm9emxUppbaN3j42F7MhqMkcB+nbsvWfaHw== + version "2.5.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.5.2.tgz#dc19cc4fd8b7750269651adfa331f9f5f6e33032" + integrity sha512-iYgAttxqE/1TFcKmApwC/VzPFiPa22WYXq6XKQH8eDZUQvG2O4yG+2RfBxfPNGM5DnNkiOGBPuGipTSxV7lmaA== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" - "@graphql-codegen/typescript" "^2.4.1" - "@graphql-codegen/visitor-plugin-common" "2.5.1" + "@graphql-codegen/plugin-helpers" "^2.4.0" + "@graphql-codegen/typescript" "^2.4.5" + "@graphql-codegen/visitor-plugin-common" "2.7.1" "@graphql-tools/utils" "^8.1.1" auto-bind "~4.0.0" tslib "~2.3.0" @@ -898,14 +864,14 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.4.1.tgz#96d427993982edc27c50c4cd8841bdbbe26689d9" - integrity sha512-1+dZ0Qar+jGISXTdm7nWpv7Agt5uRj3q2GtpTJ92aA/AEHh+MTharanYcExEbdqNQm7b859MusI+Jo1Hlqsfmw== +"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.4.5": + version "2.4.5" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.4.5.tgz#80ca7a343511a27cd587c0b220b0a0dd99a4dc4b" + integrity sha512-Ytb8phNHKl/v/wxudsMOAV1dmzIbckWHm2J83PLNOvnu9CGEhgsd67vfe3ZoF95VU2BKSG8BXGa6uL9z2xDmuA== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" - "@graphql-codegen/schema-ast" "^2.4.0" - "@graphql-codegen/visitor-plugin-common" "2.5.1" + "@graphql-codegen/plugin-helpers" "^2.4.0" + "@graphql-codegen/schema-ast" "^2.4.1" + "@graphql-codegen/visitor-plugin-common" "2.7.1" auto-bind "~4.0.0" tslib "~2.3.0" @@ -925,12 +891,12 @@ parse-filepath "^1.0.2" tslib "~2.3.0" -"@graphql-codegen/visitor-plugin-common@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.5.1.tgz#4401bc401ff501b9b9330d1681e64685f519b327" - integrity sha512-hLnVB6u7qB1rIh9oJnGVWQmrUsXw3nEUMF+LGdf4b5qEWh5f9HSzciWMS6M47h4fSqpLyW4qgk2glH/DKwFgRA== +"@graphql-codegen/visitor-plugin-common@2.7.1": + version "2.7.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.7.1.tgz#bdcded24d1d6f329d32b90704397d6001374b893" + integrity sha512-Ew/+0GwGwL3ZSXS0cYz6lVvE7S5SkbTr7caRpLa4FpAdLGNskCgdA1n6W5tX5c7qsaYstqlxp30hJTfgTsYS6A== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.0" + "@graphql-codegen/plugin-helpers" "^2.4.0" "@graphql-tools/optimize" "^1.0.1" "@graphql-tools/relay-operation-optimizer" "^6.3.7" "@graphql-tools/utils" "^8.3.0" @@ -942,91 +908,92 @@ tslib "~2.3.0" "@graphql-tools/apollo-engine-loader@^7.0.5": - version "7.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.2.1.tgz#14e5d0b1032a7d882d22a7533c8969ee3fa797f2" - integrity sha512-Fj/A8+9SXPTXkpKqhcSq7O9WZuMdy5zynGrnMyewbCuw1kSfzgC4pJB76ILSPa5ajOcC5bBmmvXm+yVFVRgVMg== + version "7.2.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.2.3.tgz#6bebabefa3fd8fb0fc8215a61e53448490b1764c" + integrity sha512-c1AVwAoKf/dSQw6yn/OfwobybplDuAJWLvJS7K7Bm4BiFv0/YXiizPTMsYvxlJYg3uGvmA7fsoeicrzBdlwOpA== dependencies: - "@graphql-tools/utils" "^8.5.1" - cross-undici-fetch "^0.0.20" + "@graphql-tools/utils" "^8.6.2" + cross-undici-fetch "^0.1.19" sync-fetch "0.3.1" tslib "~2.3.0" -"@graphql-tools/batch-execute@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.3.1.tgz#0b74c54db5ac1c5b9a273baefc034c2343ebbb74" - integrity sha512-63kHY8ZdoO5FoeDXYHnAak1R3ysMViMPwWC2XUblFckuVLMUPmB2ONje8rjr2CvzWBHAW8c1Zsex+U3xhKtGIA== +"@graphql-tools/batch-execute@^8.3.2": + version "8.3.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.3.2.tgz#8b5a731d5343f0147734f12d480aafde2a1b6eba" + integrity sha512-ICWqM+MvEkIPHm18Q0cmkvm134zeQMomBKmTRxyxMNhL/ouz6Nqld52/brSlaHnzA3fczupeRJzZ0YatruGBcQ== dependencies: - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/utils" "^8.6.2" dataloader "2.0.0" tslib "~2.3.0" value-or-promise "1.0.11" "@graphql-tools/code-file-loader@^7.0.6": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.2.3.tgz#b53e8809528da07911423c3a511e5fccf9121a12" - integrity sha512-aNVG3/VG5cUpS389rpCum+z7RY98qvPwOzd+J4LVr+f5hWQbDREnSFM+5RVTDfULujrsi7edKaGxGKp68pGmAA== + version "7.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.2.4.tgz#f35bf3050b4375ee5c2da0c34a896392cc7bea3f" + integrity sha512-KjIxYKDIbrtRGzeboYC98OnRnCvDVDC3E+suH48J4/KxweSjrG+ZpD++T/A11FdIcFb1Y5OceCw+OwjHI5OoyQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.3" - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/graphql-tag-pluck" "^7.1.6" + "@graphql-tools/utils" "^8.6.2" globby "^11.0.3" tslib "~2.3.0" unixify "^1.0.0" -"@graphql-tools/delegate@^8.4.1", "@graphql-tools/delegate@^8.4.2": - version "8.4.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-8.4.3.tgz#ad73ed7cc3b4cad9242c6d4835a5ae0b640f7164" - integrity sha512-hKTJdJXJnKL0+2vpU+Kt7OHQTIXZ9mBmNBwHsYiG5WNArz/vNI7910r6TC2XMf/e7zhyyK+mXxMDBmDQkkJagA== +"@graphql-tools/delegate@^8.5.1": + version "8.5.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-8.5.1.tgz#3d146cc3bb74935116d3f4bddb3affdf14a9712d" + integrity sha512-/YPmVxitt57F8sH50pnfXASzOOjEfaUDkX48eF5q6f16+JBncej2zeu+Zm2c68q8MbIxhPlEGfpd0QZeqTvAxw== dependencies: - "@graphql-tools/batch-execute" "^8.3.1" - "@graphql-tools/schema" "^8.3.1" - "@graphql-tools/utils" "^8.5.4" + "@graphql-tools/batch-execute" "^8.3.2" + "@graphql-tools/schema" "^8.3.2" + "@graphql-tools/utils" "^8.6.2" dataloader "2.0.0" + graphql-executor "0.0.18" tslib "~2.3.0" value-or-promise "1.0.11" "@graphql-tools/git-loader@^7.0.5": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.1.2.tgz#7a7b5fc366bcc9e2e14e0463ff73f1a19aafabbd" - integrity sha512-vIMrISQPKQgHS893b8K/pEE1InPV+7etzFhHoyQRhYkVHXP2RBkfI64Wq9bNPezF8Ss/dwIjI/keLaPp9EQDmA== + version "7.1.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.1.3.tgz#080c57ec2ab83bc0d8d1e3c881c6960b0c7afebd" + integrity sha512-Ya0jRizD6F1hbajk2rwfqJKAp6dQRvzW1gzkOQmlNcQOTtTjWITsFtzk7fS02gZRWkfFBenlTBguGufh91I6bg== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.3" - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/graphql-tag-pluck" "^7.1.6" + "@graphql-tools/utils" "^8.6.2" is-glob "4.0.3" micromatch "^4.0.4" tslib "~2.3.0" unixify "^1.0.0" "@graphql-tools/github-loader@^7.0.5": - version "7.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.2.1.tgz#53ce2bf215a0eb083ff985b213402a24f1302da2" - integrity sha512-vqwh2H11ZkAATDam/JqiP0CSqQRPUbjgCDxPdUu/xvST2QKyA4+uVXLBcpBRJc5kJCQjELijeRWVHSk9oN1q6g== + version "7.2.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.2.4.tgz#fe5688037015be0f190a1684c953b57279f0fa58" + integrity sha512-QuSN2GWgm/h3lp7o5zpi8TzHnzom4b/f5Zq4Hvprn1OsGaOviHLXQUx6AaWa07cmFvPL0se79R0sEkMZlXlpQQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.3" - "@graphql-tools/utils" "^8.5.1" - cross-undici-fetch "^0.0.20" + "@graphql-tools/graphql-tag-pluck" "^7.1.6" + "@graphql-tools/utils" "^8.6.2" + cross-undici-fetch "^0.1.19" sync-fetch "0.3.1" tslib "~2.3.0" "@graphql-tools/graphql-file-loader@^7.0.5", "@graphql-tools/graphql-file-loader@^7.3.2": - version "7.3.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.3.3.tgz#7cee2f84f08dc13fa756820b510248b857583d36" - integrity sha512-6kUJZiNpYKVhum9E5wfl5PyLLupEDYdH7c8l6oMrk6c7EPEVs6iSUyB7yQoWrtJccJLULBW2CRQ5IHp5JYK0mA== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.3.4.tgz#61e3e7e6223a21fbdd987f2abaa6f14104ab7b4a" + integrity sha512-Q0/YtDq0APR6syRclsQMNguWKRlchd8nFTOpLhfc7Xeiy21VhEEi4Ik+quRySfb7ubDfJGhiUq4MQW43FhWJvg== dependencies: - "@graphql-tools/import" "^6.5.7" - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/import" "^6.6.6" + "@graphql-tools/utils" "^8.6.2" globby "^11.0.3" tslib "~2.3.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@^7.1.3": - version "7.1.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.1.4.tgz#174b69d40988c3450d310173c5be5af894929c41" - integrity sha512-0V2AY68ip3YmJ9rnIwQGxXsokCeGD9FTQOeSLzpwG74U0VY6bphfaCp5KVGW+W5sGJchTj3HvnmvdmWZnEZWZA== +"@graphql-tools/graphql-tag-pluck@^7.1.6": + version "7.1.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.1.6.tgz#c78a3f416e06194069609fac6d44c09dd40f6bda" + integrity sha512-VdubvdS8pIrAPVDq6hV7ARXz2Yh8/2153+RO6i+RJOMgyFw8wOW3jRCKE93eN+Hk2pZBC2x3kzdNeUAyVpuslg== dependencies: - "@babel/parser" "7.16.4" - "@babel/traverse" "7.16.3" - "@babel/types" "7.16.0" - "@graphql-tools/utils" "^8.5.1" + "@babel/parser" "^7.16.8" + "@babel/traverse" "^7.16.8" + "@babel/types" "^7.16.8" + "@graphql-tools/utils" "^8.6.2" tslib "~2.3.0" "@graphql-tools/import@6.4.0": @@ -1037,21 +1004,21 @@ resolve-from "5.0.0" tslib "~2.3.0" -"@graphql-tools/import@^6.5.7": - version "6.6.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.6.3.tgz#e2983d9623d4abd7a5ef2f65f7cc8ff745a1a691" - integrity sha512-k/QkWCZ5rPVgFw1eE4GXPXltw9/mEiJj6F6bJvFKJr1C6im8Y60pl0Pv+SByGZQGuukXE0uR16Mv4OFGSMQIaQ== +"@graphql-tools/import@^6.6.6": + version "6.6.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.6.6.tgz#a4ff216e6b8a49c392bb8a4378d4e9caf2b303d7" + integrity sha512-a0aVajxqu1MsL8EwavA44Osw20lBOIhq8IM2ZIHFPP62cPAcOB26P+Sq57DHMsSyX5YQ0ab9XPM2o4e1dQhs0w== dependencies: - "@graphql-tools/utils" "8.5.5" + "@graphql-tools/utils" "8.6.2" resolve-from "5.0.0" tslib "~2.3.0" "@graphql-tools/json-file-loader@^7.1.2", "@graphql-tools/json-file-loader@^7.3.2": - version "7.3.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.3.3.tgz#45cfde77b9dc4ab6c21575305ae537d2814d237f" - integrity sha512-CN2Qk9rt+Gepa3rb3X/mpxYA5MIYLwZBPj2Njw6lbZ6AaxG+O1ArDCL5ACoiWiBimn1FCOM778uhRM9znd0b3Q== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.3.4.tgz#41e505f83885f2710ce6781bb150144368ff843a" + integrity sha512-1AROMFh8Lyorf2gTWXgVaUbU3ic84gzAgpRmJCsCla/Nnvn6JiCs4aWHsalk4ZWVXCaK04c8gk8Px1uNQUj02Q== dependencies: - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/utils" "^8.6.2" globby "^11.0.3" tslib "~2.3.0" unixify "^1.0.0" @@ -1066,44 +1033,44 @@ unixify "1.0.0" "@graphql-tools/load@^7.3.0", "@graphql-tools/load@^7.4.1": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.5.0.tgz#cede03b1222a01c55f5e8a85f099444cf0a98785" - integrity sha512-f0k12xZzxfV4BxWyvt/f8opqICXvz4WzDrVVG4udHFgUJIjhGeE67SRTWFAK/jlIp4QgaDux7L1DXQkJnmx64w== + version "7.5.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.5.2.tgz#0e46129f412bd038ac56996083458c1b8828526f" + integrity sha512-URPqVP77mYxdZxT895DzrWf2C23S3yC/oAmXD4D4YlxR5eVVH/fxu0aZR78WcEKF331fWSiFwWy9j7BZWvkj7g== dependencies: - "@graphql-tools/schema" "8.3.1" - "@graphql-tools/utils" "^8.5.5" + "@graphql-tools/schema" "8.3.2" + "@graphql-tools/utils" "^8.6.2" p-limit "3.1.0" tslib "~2.3.0" -"@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.1.tgz#bf83aa06a0cfc6a839e52a58057a84498d0d51ff" - integrity sha512-Q240kcUszhXiAYudjuJgNuLgy9CryDP3wp83NOZQezfA6h3ByYKU7xI6DiKrdjyVaGpYN3ppUmdj0uf5GaXzMA== +"@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.1", "@graphql-tools/merge@^8.2.3": + version "8.2.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.3.tgz#a2861fec230ee7be9dc42d72fed2ac075c31669f" + integrity sha512-XCSmL6/Xg8259OTWNp69B57CPWiVL69kB7pposFrufG/zaAlI9BS68dgzrxmmSqZV5ZHU4r/6Tbf6fwnEJGiSw== dependencies: - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/utils" "^8.6.2" tslib "~2.3.0" "@graphql-tools/optimize@^1.0.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.1.1.tgz#dcd59ba1ee34431e5e9b086b57fe0bdb1a176669" - integrity sha512-y0TEfPyGmJaQjnsTRs/UP7/ZHaB3i68VAsXW4H2doUFKY6rIOUz+ruME/uWsfy/VeTWBNqGX8/m/X7YFEi5OJQ== + version "1.2.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.2.0.tgz#292d0a269f95d04bc6d822c034569bb7e591fb26" + integrity sha512-l0PTqgHeorQdeOizUor6RB49eOAng9+abSxiC5/aHRo6hMmXVaqv5eqndlmxCpx9BkgNb3URQbK+ZZHVktkP/g== dependencies: tslib "~2.3.0" "@graphql-tools/prisma-loader@^7.0.6": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.1.1.tgz#2a769919c97a3f7f7807668d3155c47999b0965c" - integrity sha512-9hVpG3BNsXAYMLPlZhSHubk6qBmiHLo/UlU0ldL100sMpqI46iBaHNhTNXZCSdd81hT+4HNqaDXNFqyKJ22OGQ== + version "7.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.1.2.tgz#a4cb15eacca5e182f36ee0d3a94d76fce002dc86" + integrity sha512-AK/MIEaCDtcV41JTtdTmRBV8I6DM102FWJDbb3rTOVtIYSjU62G23yrPca8aMVcnIneQQNJ7MKYO18agCYXzqw== dependencies: - "@graphql-tools/url-loader" "^7.4.2" - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/url-loader" "^7.7.2" + "@graphql-tools/utils" "^8.6.2" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" "@types/jsonwebtoken" "^8.5.0" chalk "^4.1.0" debug "^4.3.1" - dotenv "^10.0.0" - graphql-request "^3.3.0" + dotenv "^16.0.0" + graphql-request "^4.0.0" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" isomorphic-fetch "^3.0.0" @@ -1117,36 +1084,36 @@ yaml-ast-parser "^0.0.43" "@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.3.7": - version "6.4.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.1.tgz#28572444e2c00850c889a84472f3cc7405dc1ad8" - integrity sha512-2b9D5L+31sIBnvmcmIW5tfvNUV+nJFtbHpUyarTRDmFT6EZ2cXo4WZMm9XJcHQD/Z5qvMXfPHxzQ3/JUs4xI+w== + version "6.4.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.2.tgz#18e20fed783f5de3081ce90d3b4d82047ea8d46b" + integrity sha512-pc/cliYO0veVbMyM5H54lZzQh+9SxnjawqR623rc+jPuY9JUQcuIKkZzM1+E5blbtr4dvh7Bi4uzf3rJ0sxG0Q== dependencies: - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/utils" "^8.6.2" relay-compiler "12.0.0" tslib "~2.3.0" -"@graphql-tools/schema@8.3.1", "@graphql-tools/schema@^8.1.1", "@graphql-tools/schema@^8.1.2", "@graphql-tools/schema@^8.3.1": - version "8.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.3.1.tgz#1ee9da494d2da457643b3c93502b94c3c4b68c74" - integrity sha512-3R0AJFe715p4GwF067G5i0KCr/XIdvSfDLvTLEiTDQ8V/hwbOHEKHKWlEBHGRQwkG5lwFQlW1aOn7VnlPERnWQ== +"@graphql-tools/schema@8.3.2", "@graphql-tools/schema@^8.1.1", "@graphql-tools/schema@^8.1.2", "@graphql-tools/schema@^8.3.2": + version "8.3.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.3.2.tgz#5b949d7a2cc3936f73507d91cc609996f1266d11" + integrity sha512-77feSmIuHdoxMXRbRyxE8rEziKesd/AcqKV6fmxe7Zt+PgIQITxNDew2XJJg7qFTMNM43W77Ia6njUSBxNOkwg== dependencies: - "@graphql-tools/merge" "^8.2.1" - "@graphql-tools/utils" "^8.5.1" + "@graphql-tools/merge" "^8.2.3" + "@graphql-tools/utils" "^8.6.2" tslib "~2.3.0" value-or-promise "1.0.11" -"@graphql-tools/url-loader@^7.0.11", "@graphql-tools/url-loader@^7.4.2": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.7.0.tgz#504f0030c75b61bca4ac07da49e8cd872c316972" - integrity sha512-mBBb+aJqI4E0MVEzyfi76Pi/G6lGxGTVt/tP1YtKJly7UnonNoWOtDusdL3zIVAGhGgLsNrLbGhLDbwSd6TV6A== +"@graphql-tools/url-loader@^7.0.11", "@graphql-tools/url-loader@^7.4.2", "@graphql-tools/url-loader@^7.7.2": + version "7.8.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.8.0.tgz#f2e6f093dd283734a8d81cc6515898199f897170" + integrity sha512-4j5X40hpYInM5J7KbURSspEJCi6tPqvEE2kyBOyP0C0YIOYtKgUJTAryjbBXqr+HWGyEwJ2zuQ2cQdVMn8l78A== dependencies: - "@graphql-tools/delegate" "^8.4.1" - "@graphql-tools/utils" "^8.5.1" - "@graphql-tools/wrap" "^8.3.1" + "@graphql-tools/delegate" "^8.5.1" + "@graphql-tools/utils" "^8.6.2" + "@graphql-tools/wrap" "^8.4.2" "@n1ru4l/graphql-live-query" "^0.9.0" "@types/websocket" "^1.0.4" "@types/ws" "^8.0.0" - cross-undici-fetch "^0.1.4" + cross-undici-fetch "^0.1.19" dset "^3.1.0" extract-files "^11.0.0" graphql-sse "^1.0.1" @@ -1160,10 +1127,10 @@ value-or-promise "^1.0.11" ws "^8.3.0" -"@graphql-tools/utils@8.5.5", "@graphql-tools/utils@^8.1.1", "@graphql-tools/utils@^8.3.0", "@graphql-tools/utils@^8.5.1", "@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.5.3", "@graphql-tools/utils@^8.5.4", "@graphql-tools/utils@^8.5.5": - version "8.5.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.5.5.tgz#019ddb99719feb19602afdb537c06e463df674a9" - integrity sha512-y7zRXWIUI73X+9/rf/0KzrNFMlpRKFfzLiwdbIeWwgLs+NV9vfUOoVkX8luXX6LwQxhSypHATMiwZGM2ro/wJA== +"@graphql-tools/utils@8.6.2", "@graphql-tools/utils@^8.1.1", "@graphql-tools/utils@^8.3.0", "@graphql-tools/utils@^8.5.1", "@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.6.2": + version "8.6.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.6.2.tgz#095408135f091aac68fe18a0a21b708e685500da" + integrity sha512-x1DG0cJgpJtImUlNE780B/dfp8pxvVxOD6UeykFH5rHes26S4kGokbgU8F1IgrJ1vAPm/OVBHtd2kicTsPfwdA== dependencies: tslib "~2.3.0" @@ -1176,14 +1143,14 @@ camel-case "4.1.2" tslib "~2.2.0" -"@graphql-tools/wrap@^8.3.1": - version "8.3.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-8.3.2.tgz#d3bcecb7529d071e4ecc4dfc75b9566e3da79d4f" - integrity sha512-7DcOBFB+Dd84x9dxSm7qS4iJONMyfLnCJb8A19vGPffpu4SMJ3sFcgwibKFu5l6mMUiigKgXna2RRgWI+02bKQ== +"@graphql-tools/wrap@^8.4.2": + version "8.4.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-8.4.3.tgz#cb7d703c41cebe3c32b999d3a28c17e3eac12783" + integrity sha512-2bd3GtlqV72idEphLTM8SRiSxEDrMPGE556faW8N5Q2qK7qlCEu9AQK0HNKWKdtEPTHqahndZtIPcdpNN6xGig== dependencies: - "@graphql-tools/delegate" "^8.4.2" - "@graphql-tools/schema" "^8.3.1" - "@graphql-tools/utils" "^8.5.3" + "@graphql-tools/delegate" "^8.5.1" + "@graphql-tools/schema" "^8.3.2" + "@graphql-tools/utils" "^8.6.2" tslib "~2.3.0" value-or-promise "1.0.11" @@ -1192,26 +1159,6 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== -"@hapi/accept@5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@hapi/accept/-/accept-5.0.2.tgz#ab7043b037e68b722f93f376afb05e85c0699523" - integrity sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw== - dependencies: - "@hapi/boom" "9.x.x" - "@hapi/hoek" "9.x.x" - -"@hapi/boom@9.x.x": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.4.tgz#1f9dad367c6a7da9f8def24b4a986fc5a7bd9db6" - integrity sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw== - dependencies: - "@hapi/hoek" "9.x.x" - -"@hapi/hoek@9.x.x": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" - integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== - "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -1247,168 +1194,168 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.4.2.tgz#7a95612d38c007ddb528ee446fe5e5e785e685ce" - integrity sha512-xknHThRsPB/To1FUbi6pCe43y58qFC03zfb6R7fDb/FfC7k2R3i1l+izRBJf8DI46KhYGRaF14Eo9A3qbBoixg== +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" + integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.4.2" - jest-util "^27.4.2" + jest-message-util "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" -"@jest/core@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.5.tgz#cae2dc34259782f4866c6606c3b480cce920ed4c" - integrity sha512-3tm/Pevmi8bDsgvo73nX8p/WPng6KWlCyScW10FPEoN1HU4pwI83tJ3TsFvi1FfzsjwUlMNEPowgb/rPau/LTQ== +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" + integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== dependencies: - "@jest/console" "^27.4.2" - "@jest/reporters" "^27.4.5" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.8.1" exit "^0.1.2" - graceful-fs "^4.2.4" - jest-changed-files "^27.4.2" - jest-config "^27.4.5" - jest-haste-map "^27.4.5" - jest-message-util "^27.4.2" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-resolve-dependencies "^27.4.5" - jest-runner "^27.4.5" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" - jest-watcher "^27.4.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" micromatch "^4.0.4" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.4.4": - version "27.4.4" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.4.4.tgz#66ebebc79673d84aad29d2bb70a8c51e6c29bb4d" - integrity sha512-q+niMx7cJgt/t/b6dzLOh4W8Ef/8VyKG7hxASK39jakijJzbFBGpptx3RXz13FFV7OishQ9lTbv+dQ5K3EhfDQ== +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" + integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== dependencies: - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.2" + jest-mock "^27.5.1" -"@jest/fake-timers@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.4.2.tgz#d217f86c3ba2027bf29e0b731fd0cb761a72d093" - integrity sha512-f/Xpzn5YQk5adtqBgvw1V6bF8Nx3hY0OIRRpCvWcfPl0EAjdqWPdhH3t/3XpiWZqtjIEHDyMKP9ajpva1l4Zmg== +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" + integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@sinonjs/fake-timers" "^8.0.1" "@types/node" "*" - jest-message-util "^27.4.2" - jest-mock "^27.4.2" - jest-util "^27.4.2" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" -"@jest/globals@^27.4.4": - version "27.4.4" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.4.4.tgz#fe501a80c23ea2dab585c42be2a519bb5e38530d" - integrity sha512-bqpqQhW30BOreXM8bA8t8JbOQzsq/WnPTnBl+It3UxAD9J8yxEAaBEylHx1dtBapAr/UBk8GidXbzmqnee8tYQ== +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" + integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== dependencies: - "@jest/environment" "^27.4.4" - "@jest/types" "^27.4.2" - expect "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" -"@jest/reporters@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.4.5.tgz#e229acca48d18ea39e805540c1c322b075ae63ad" - integrity sha512-3orsG4vi8zXuBqEoy2LbnC1kuvkg1KQUgqNxmxpQgIOQEPeV0onvZu+qDQnEoX8qTQErtqn/xzcnbpeTuOLSiA== +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" + integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.4.2" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.3" + istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^27.4.5" - jest-resolve "^27.4.5" - jest-util "^27.4.2" - jest-worker "^27.4.5" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" slash "^3.0.0" source-map "^0.6.0" string-length "^4.0.1" terminal-link "^2.0.0" v8-to-istanbul "^8.1.0" -"@jest/source-map@^27.4.0": - version "27.4.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.4.0.tgz#2f0385d0d884fb3e2554e8f71f8fa957af9a74b6" - integrity sha512-Ntjx9jzP26Bvhbm93z/AKcPRj/9wrkI88/gK60glXDx1q+IeI0rf7Lw2c89Ch6ofonB0On/iRDreQuQ6te9pgQ== +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" + integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" source-map "^0.6.0" -"@jest/test-result@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.4.2.tgz#05fd4a5466ec502f3eae0b39dff2b93ea4d5d9ec" - integrity sha512-kr+bCrra9jfTgxHXHa2UwoQjxvQk3Am6QbpAiJ5x/50LW8llOYrxILkqY0lZRW/hu8FXesnudbql263+EW9iNA== +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" + integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== dependencies: - "@jest/console" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.4.5.tgz#1d7e026844d343b60d2ca7fd82c579a17b445d7d" - integrity sha512-n5woIn/1v+FT+9hniymHPARA9upYUmfi5Pw9ewVwXCDlK4F5/Gkees9v8vdjGdAIJ2MPHLHodiajLpZZanWzEQ== +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" + integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== dependencies: - "@jest/test-result" "^27.4.2" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-runtime "^27.4.5" + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" -"@jest/transform@^27.1.0", "@jest/transform@^27.4.5": - version "27.4.5" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.4.5.tgz#3dfe2e3680cd4aa27356172bf25617ab5b94f195" - integrity sha512-PuMet2UlZtlGzwc6L+aZmR3I7CEBpqadO03pU40l2RNY2fFJ191b9/ITB44LNOhVtsyykx0OZvj0PCyuLm7Eew== +"@jest/transform@^27.1.0", "@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" + integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^27.4.2" - babel-plugin-istanbul "^6.0.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-regex-util "^27.4.0" - jest-util "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" micromatch "^4.0.4" - pirates "^4.0.1" + pirates "^4.0.4" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^27.1.0", "@jest/types@^27.4.2": - version "27.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5" - integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg== +"@jest/types@^27.1.0", "@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" @@ -1416,16 +1363,29 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" + integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@n1ru4l/graphql-live-query@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc" integrity sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg== -"@napi-rs/triples@1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@napi-rs/triples/-/triples-1.0.3.tgz#76d6d0c3f4d16013c61e45dfca5ff1e6c31ae53c" - integrity sha512-jDJTpta+P4p1NZTFVLHJ/TLFVYVcOqv6l8xwOeBKNPMgY/zDYH/YH7SJbvrr/h1RcS9GzbPcLKGzpuK9cV56UA== - "@next-auth/prisma-legacy-adapter@0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@next-auth/prisma-legacy-adapter/-/prisma-legacy-adapter-0.1.2.tgz#d19b65696bf16bda5dbc88f85da98aee6f30e2f3" @@ -1442,99 +1402,72 @@ require_optional "^1.0.1" typeorm "^0.2.30" -"@next/env@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.0.7.tgz#316f7bd1b6b69f554d2676cfc91a16bc7e32ee79" - integrity sha512-TNDqBV37wd95SiNdZsSUq8gnnrTwr+aN9wqy4Zxrxw4bC/jCHNsbK94DxjkG99VL30VCRXXDBTA1/Wa2jIpF9Q== +"@next/env@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" + integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== -"@next/eslint-plugin-next@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.0.7.tgz#2c71bb66b8f8ff1080086342113406aa3156976f" - integrity sha512-xk7eMjw4+roWWR/0ETIoToCNs2wdvCGgQUiUO390Rj33/82yxZsh+ODRSaFWkiKp8zHWQN5GCW+U5pfjt/gyQg== +"@next/eslint-plugin-next@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz#32586a11378b3ffa5a93ac40a3c44ad99d70e95a" + integrity sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g== dependencies: glob "7.1.7" -"@next/polyfill-module@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/polyfill-module/-/polyfill-module-12.0.7.tgz#140e698557113cd3a3c0833f15ca8af1b608f2dc" - integrity sha512-sA8LAMMlmcspIZw/jeQuJTyA3uGrqOhTBaQE+G9u6DPohqrBFRkaz7RzzJeqXkUXw600occsIBknSjyVd1R67A== - -"@next/react-dev-overlay@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/react-dev-overlay/-/react-dev-overlay-12.0.7.tgz#ae8f9bd14b1786e52330b729ff63061735d21c77" - integrity sha512-dSQLgpZ5uzyittFtIHlJCLAbc0LlMFbRBSYuGsIlrtGyjYN+WMcnz8lK48VLxNPFGuB/hEzkWV4TW5Zu75+Fzg== - dependencies: - "@babel/code-frame" "7.12.11" - anser "1.4.9" - chalk "4.0.0" - classnames "2.2.6" - css.escape "1.5.1" - data-uri-to-buffer "3.0.1" - platform "1.3.6" - shell-quote "1.7.3" - source-map "0.8.0-beta.0" - stacktrace-parser "0.1.10" - strip-ansi "6.0.1" - -"@next/react-refresh-utils@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/react-refresh-utils/-/react-refresh-utils-12.0.7.tgz#921c403798e188b4f1d9e609283c0e8d3e532f89" - integrity sha512-Pglj1t+7RxH0txEqVcD8ZxrJgqLDmKvQDqxKq3ZPRWxMv7LTl7FVT2Pnb36QFeBwCvMVl67jxsADKsW0idz8sA== - -"@next/swc-android-arm64@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.0.7.tgz#9b0a9e4bc646a045eef725764112096f0a6ea204" - integrity sha512-yViT7EEc7JqxncRT+ZTeTsrAYXLlcefo0Y0eAfYmmalGD2605L4FWAVrJi4WnrSLji7l+veczw1WBmNeHICKKA== - -"@next/swc-darwin-arm64@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.0.7.tgz#2fd506dba91e4a35036b9fc7930a4d6b8895f16a" - integrity sha512-vhAyW2rDEUcQesRVaj0z1hSoz7QhDzzGd0V1/5/5i9YJOfOtyrPsVJ82tlf7BfXl6/Ep+eKNfWVIb5/Jv89EKg== - -"@next/swc-darwin-x64@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.0.7.tgz#b3016503caa5ed5cc6a20051517d5b2a79cfdc58" - integrity sha512-km+6Rx6TvbraoQ1f0MXa69ol/x0RxzucFGa2OgZaYJERas0spy0iwW8hpASsGcf597D8VRW1x+R2C7ZdjVBSTw== - -"@next/swc-linux-arm-gnueabihf@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.0.7.tgz#8e91ecddc2d6d26946949a67d481110db3063d09" - integrity sha512-d0zWr877YqZ2cf/DQy6obouaR39r0FPebcXj2nws9AC99m68CO2xVpWv9jT7mFvpY+T40HJisLH80jSZ2iQ9sA== - -"@next/swc-linux-arm64-gnu@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.0.7.tgz#1eefcf7b063610315b74e5c7dc24c3437370e49d" - integrity sha512-fdobh5u6gG13Gd5LkHhJ+W8tF9hbaFolRW99FhzArMe5/nMKlLdBymOxvitE3K4gSFQxbXJA6TbU0Vv0e59Kww== - -"@next/swc-linux-arm64-musl@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.0.7.tgz#e9e764519dfb75e43355c442181346cd6e72459b" - integrity sha512-vx0c5Q3oIScFNT/4jI9rCe0yPzKuCqWOkiO/OOV0ixSI2gLhbrwDIcdkm79fKVn3i8JOJunxE4zDoFeR/g8xqQ== - -"@next/swc-linux-x64-gnu@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.0.7.tgz#fef02e14ed8f9c114479dabba1475ae2d3bb040d" - integrity sha512-9ITyp6s6uGVKNx3C/GP7GrYycbcwTADG7TdIXzXUxOOZORrdB1GNg3w/EL3Am4VMPPEpO6v1RfKo2IKZpVKfTA== - -"@next/swc-linux-x64-musl@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.0.7.tgz#07dc334b1924d9f5a8c4a891b91562af19ff5de4" - integrity sha512-C+k+cygbIZXYfc+Hx2fNPUBEg7jzio+mniP5ywZevuTXW14zodIfQ3ZMoMJR8EpOVvYpjWFk2uAjiwqgx8vo/g== - -"@next/swc-win32-arm64-msvc@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.0.7.tgz#6c559d87ce142693173039a18b1c1d65519762dd" - integrity sha512-7jTRjOKkDVnb5s7VoHT7eX+eyT/5BQJ/ljP2G56riAgKGqPL63/V7FXemLhhLT67D+OjoP8DRA2E2ne6IPHk4w== - -"@next/swc-win32-ia32-msvc@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.0.7.tgz#16b23f2301b16877b3623f0e8364e8177e2ef7db" - integrity sha512-2u5pGDsk7H6gGxob2ATIojzlwKzgYsrijo7RRpXOiPePVqwPWg6/pmhaJzLdpfjaBgRg1NFmwSp/7Ump9X8Ijg== - -"@next/swc-win32-x64-msvc@12.0.7": - version "12.0.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.0.7.tgz#8d75d3b6a872ab97ab73e3b4173d56dbb2991917" - integrity sha512-frEWtbf+q8Oz4e2UqKJrNssk6DZ6/NLCQXn5/ORWE9dPAfe9XS6aK5FRZ6DuEPmmKd5gOoRkKJFFz5nYd+TeyQ== +"@next/swc-android-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39" + integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA== + +"@next/swc-darwin-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135" + integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg== + +"@next/swc-darwin-x64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd" + integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug== + +"@next/swc-linux-arm-gnueabihf@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7" + integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog== + +"@next/swc-linux-arm64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093" + integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q== + +"@next/swc-linux-arm64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566" + integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA== + +"@next/swc-linux-x64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e" + integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A== + +"@next/swc-linux-x64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31" + integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw== + +"@next/swc-win32-arm64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283" + integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw== + +"@next/swc-win32-ia32-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1" + integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q== + +"@next/swc-win32-x64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064" + integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1616,9 +1549,9 @@ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.17" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.17.tgz#f50ac9d20d64153b510578d84f9643f9a3afbe64" - integrity sha512-6zzkezS9QEIL8yCBvXWxPTJPNuMeECJVxSOhxNY/jfq9LxOTHivaYTqr37n9LknWWRTIkzqH2UilS5QFvfa90A== + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -1627,9 +1560,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.3" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.3.tgz#f456b4b2ce79137f768aa130d2423d2f0ccfaba5" - integrity sha512-/GWCmzJWqV7diQW54smJZzWbSFf4QYtF71WCKhcx6Ru/tFyQIY2eiiITcCAeuPbNSvT9YCGkVMqqvSk2Z0mXiA== + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== dependencies: "@babel/types" "^7.0.0" @@ -1669,9 +1602,9 @@ hoist-non-react-statics "^3.3.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" - integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== "@types/istanbul-lib-report@*": version "3.0.0" @@ -1698,9 +1631,9 @@ integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== "@types/json-stable-stringify@^1.0.32": - version "1.0.33" - resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.33.tgz#099b0712d824d15e2660c20e1c16e6a8381f308c" - integrity sha512-qEWiQff6q2tA5gcJGWwzplQcXdJtm+0oy6IHGHzlOf3eFAkGE/FIPXZK9ofWgNSHVp8AFFI33PJJshS0ei3Gvw== + version "1.0.34" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75" + integrity sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw== "@types/json5@^0.0.29": version "0.0.29" @@ -1708,21 +1641,21 @@ integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= "@types/jsonwebtoken@^8.5.0": - version "8.5.6" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.6.tgz#1913e5a61e70a192c5a444623da4901a7b1a9d42" - integrity sha512-+P3O/xC7nzVizIi5VbF34YtqSonFsdnbXBnWUCYRiKOi1f9gA4sEFvXkrGr/QVV23IbMYvcoerI7nnhDUiWXRQ== + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44" + integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A== dependencies: "@types/node" "*" "@types/node@*": - version "17.0.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.0.tgz#62797cee3b8b497f6547503b2312254d4fe3c2bb" - integrity sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw== + version "17.0.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" + integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== "@types/node@^16.11.6": - version "16.11.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.14.tgz#4939fb42e5b0ffb3ea7e193c28244fe7414977a6" - integrity sha512-mK6BKLpL0bG6v2CxHbm0ed6RcZrAtTHBTd/ZpnlVPVa3HkumsqLE4BC4u6TQ8D7pnrRbOU0am6epuALs+Ncnzw== + version "16.11.26" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.26.tgz#63d204d136c9916fb4dcd1b50f9740fe86884e47" + integrity sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1730,9 +1663,9 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.2.tgz#4c62fae93eb479660c3bd93f9d24d561597a8281" - integrity sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== "@types/prop-types@*": version "15.7.4" @@ -1747,16 +1680,16 @@ "@types/react" "*" "@types/react-dom@^17.0": - version "17.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" - integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809" + integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ== dependencies: "@types/react" "*" "@types/react-highlight-words@^0.16.3": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.3.tgz#a62e8d31197a33559c38fcbe4c7c1e10d3f78cd8" - integrity sha512-ZarU4+/R593xctDXy/SkyWeS/dg5sG8TxZ2DATd2EdDKHlfqCPnOYzHEo/7XaYpCoSCgk5Sp1sVNhnOzC/ezJg== + version "0.16.4" + resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.4.tgz#441b6f05c27ce6ab76833803a84bc385f0a1e8f2" + integrity sha512-KITBX3xzheQLu2s3bUgLmRE7ekmhc52zRjRTwkKayQARh30L4fjEGzGm7ULK9TuX2LgxWWavZqyQGDGjAHbL3w== dependencies: "@types/react" "*" @@ -1768,9 +1701,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^17.0": - version "17.0.37" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959" - integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg== + version "17.0.39" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" + integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1787,23 +1720,23 @@ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/websocket@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.4.tgz#1dc497280d8049a5450854dd698ee7e6ea9e60b8" - integrity sha512-qn1LkcFEKK8RPp459jkjzsfpbsx36BBt3oC3pITYtkoBw/aVX+EZFa5j3ThCRTNpLFvIMr5dSTD4RaMdilIOpA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c" + integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ== dependencies: "@types/node" "*" "@types/ws@^8.0.0": - version "8.2.2" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.2.2.tgz#7c5be4decb19500ae6b3d563043cd407bf366c21" - integrity sha512-NOn5eIcgWLOo6qW8AcuLZ7G8PycXu0xTxxkS6Q18VWFxgPUSOwV0pBj2a/4viNZVu25i7RIB7GttdkAIUUXOOg== + version "8.5.2" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.2.tgz#77e0c2e360e9579da930ffcfa53c5975ea3bdd26" + integrity sha512-VXI82ykONr5tacHEojnErTQk+KQSoYbW1NB6iz6wUwrNd+BqfkfggQNoNdCqhJSzbNumShPERbM+Pc5zpfhlbw== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "20.2.1" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" - integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^16.0.0": version "16.0.4" @@ -1818,47 +1751,47 @@ integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== "@typescript-eslint/parser@^5.0.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.7.0.tgz#4dca6de463d86f02d252e681136a67888ea3b181" - integrity sha512-m/gWCCcS4jXw6vkrPQ1BjZ1vomP01PArgzvauBqzsoZ3urLbsRChexB8/YV8z9HwE3qlJM35FxfKZ1nfP/4x8g== + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c" + integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg== dependencies: - "@typescript-eslint/scope-manager" "5.7.0" - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/typescript-estree" "5.7.0" + "@typescript-eslint/scope-manager" "5.13.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/typescript-estree" "5.13.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.7.0.tgz#70adf960e5a58994ad50438ba60d98ecadd79452" - integrity sha512-7mxR520DGq5F7sSSgM0HSSMJ+TFUymOeFRMfUfGFAVBv8BR+Jv1vHgAouYUvWRZeszVBJlLcc9fDdktxb5kmxA== +"@typescript-eslint/scope-manager@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6" + integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA== dependencies: - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/visitor-keys" "5.7.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/visitor-keys" "5.13.0" -"@typescript-eslint/types@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.7.0.tgz#2d4cae0105ba7d08bffa69698197a762483ebcbe" - integrity sha512-5AeYIF5p2kAneIpnLFve8g50VyAjq7udM7ApZZ9JYjdPjkz0LvODfuSHIDUVnIuUoxafoWzpFyU7Sqbxgi79mA== +"@typescript-eslint/types@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5" + integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg== -"@typescript-eslint/typescript-estree@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.7.0.tgz#968fad899050ccce4f08a40cd5fabc0798525006" - integrity sha512-aO1Ql+izMrTnPj5aFFlEJkpD4jRqC4Gwhygu2oHK2wfVQpmOPbyDSveJ+r/NQo+PWV43M6uEAeLVbTi09dFLhg== +"@typescript-eslint/typescript-estree@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141" + integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA== dependencies: - "@typescript-eslint/types" "5.7.0" - "@typescript-eslint/visitor-keys" "5.7.0" + "@typescript-eslint/types" "5.13.0" + "@typescript-eslint/visitor-keys" "5.13.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.7.0.tgz#e05164239eb7cb8aa9fa06c516ede480ce260178" - integrity sha512-hdohahZ4lTFcglZSJ3DGdzxQHBSxsLVqHzkiOmKi7xVAWC4y2c1bIMKmPJSrA4aOEoRUPOKQ87Y/taC7yVHpFg== +"@typescript-eslint/visitor-keys@5.13.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd" + integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g== dependencies: - "@typescript-eslint/types" "5.7.0" + "@typescript-eslint/types" "5.13.0" eslint-visitor-keys "^3.0.0" "@wry/context@^0.6.0": @@ -1912,20 +1845,15 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== - acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.2.4: - version "8.6.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" - integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== agent-base@6: version "6.0.2" @@ -1950,20 +1878,15 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: uri-js "^4.2.2" ajv@^8.0.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" - integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" uri-js "^4.2.2" -anser@1.4.9: - version "1.4.9" - resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760" - integrity sha512-AI+BjTeGt2+WFk4eWcqbQ7snZpDBt8SaLlj0RT2h5xfdWaiy51OjYvqwMrNzJLGy8iOAL6nKDITWO+rd4MkYEA== - ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" @@ -2021,49 +1944,48 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== antd@^4.0: - version "4.17.3" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.17.3.tgz#48e2cfaec75cb414782a16918c0f322af1f2d509" - integrity sha512-enA6rsOAGtw0uN+khzvPoCui9j6m1ZvtAHY2IWC/mOUIwfycC8iuToND9ptAqeNF5yX8RZhFubmcc7Xeqk6wWg== + version "4.18.9" + resolved "https://registry.yarnpkg.com/antd/-/antd-4.18.9.tgz#a0d246786d5076ea7a53b67191cd39295fc2322a" + integrity sha512-MbtFY2J8LvXUnxYH2QehdhP9qMEpHvOp7PmiTIHc7v6aSb+LILCibskRIMGNEKvvhBvsTdq0cnjanR9/IbqEAw== dependencies: "@ant-design/colors" "^6.0.0" "@ant-design/icons" "^4.7.0" "@ant-design/react-slick" "~0.28.1" "@babel/runtime" "^7.12.5" "@ctrl/tinycolor" "^3.4.0" - array-tree-filter "^2.1.0" classnames "^2.2.6" copy-to-clipboard "^3.2.0" lodash "^4.17.21" memoize-one "^6.0.0" moment "^2.25.3" - rc-cascader "~2.3.0" + rc-cascader "~3.2.1" rc-checkbox "~2.3.0" rc-collapse "~3.1.0" rc-dialog "~8.6.0" rc-drawer "~4.4.2" - rc-dropdown "~3.2.0" - rc-field-form "~1.21.0" + rc-dropdown "~3.2.5" + rc-field-form "~1.23.0" rc-image "~5.2.5" rc-input-number "~7.3.0" rc-mentions "~1.6.1" - rc-menu "~9.0.12" + rc-menu "~9.2.1" rc-motion "^2.4.4" rc-notification "~4.5.7" rc-pagination "~3.1.9" rc-picker "~2.5.17" - rc-progress "~3.1.0" + rc-progress "~3.2.1" rc-rate "~2.9.0" - rc-resize-observer "^1.1.0" - rc-select "~13.2.1" + rc-resize-observer "^1.2.0" + rc-select "~14.0.0-alpha.15" rc-slider "~9.7.4" rc-steps "~4.1.0" rc-switch "~3.2.0" - rc-table "~7.19.0" + rc-table "~7.23.0" rc-tabs "~11.10.0" rc-textarea "~0.3.0" rc-tooltip "~5.1.1" - rc-tree "~5.3.0" - rc-tree-select "~4.8.0" + rc-tree "~5.4.3" + rc-tree-select "~5.1.1" rc-trigger "^5.2.10" rc-upload "~4.3.0" rc-util "^5.14.0" @@ -2079,7 +2001,7 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= -anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -2161,26 +2083,6 @@ asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -assert@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" - integrity sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A== - dependencies: - es6-object-assign "^1.1.0" - is-nan "^1.2.1" - object-is "^1.0.1" - util "^0.12.0" - ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" @@ -2206,15 +2108,10 @@ auto-bind@~4.0.0: resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - axe-core@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5" - integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA== + version "4.4.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" + integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== axobject-query@^2.2.0: version "2.2.0" @@ -2235,18 +2132,18 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^27.0, babel-jest@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.4.5.tgz#d38bd0be8ea71d8b97853a5fc9f76deeb095c709" - integrity sha512-3uuUTjXbgtODmSv/DXO9nZfD52IyC2OYTFaXGRzL0kpykzroaquCrD5+lZNafTvZlnNqZHt5pb0M08qVBZnsnA== +babel-jest@^27.0, babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" + integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== dependencies: - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.4.0" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" slash "^3.0.0" babel-plugin-dynamic-import-node@^2.3.3: @@ -2256,7 +2153,7 @@ babel-plugin-dynamic-import-node@^2.3.3: dependencies: object.assign "^4.1.0" -babel-plugin-istanbul@^6.0.0: +babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== @@ -2267,10 +2164,10 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.4.0.tgz#d7831fc0f93573788d80dee7e682482da4c730d6" - integrity sha512-Jcu7qS4OX5kTWBc45Hz7BMmgXuJqRnhatqpUhnzGC3OBYpOmf2tv6jFNwZpwM7wU7MUuv2r9IPS/ZlYOuburVw== +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" + integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2333,12 +2230,12 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.0.6, babel-preset-jest@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.4.0.tgz#70d0e676a282ccb200fbabd7f415db5fdf393bca" - integrity sha512-NK4jGYpnBvNxcGo7/ZpZJr51jCGT+3bwwpVIDY2oNfTxJJldRtB4VAcYdgp1loDE50ODuTu+yBjpMAswv5tlpg== +babel-preset-jest@^27.0.6, babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" + integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== dependencies: - babel-plugin-jest-hoist "^27.4.0" + babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" backo2@^1.0.2: @@ -2351,7 +2248,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -2366,15 +2263,14 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" brace-expansion@^1.1.7: version "1.1.11" @@ -2391,97 +2287,20 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@4.16.6: - version "4.16.6" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2" - integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ== - dependencies: - caniuse-lite "^1.0.30001219" - colorette "^1.2.2" - electron-to-chromium "^1.3.723" - escalade "^3.1.1" - node-releases "^1.1.71" - browserslist@^4.17.5: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== + version "4.20.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.0.tgz#35951e3541078c125d36df76056e94738a52ebe9" + integrity sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ== dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" + caniuse-lite "^1.0.30001313" + electron-to-chromium "^1.4.76" escalade "^3.1.1" - node-releases "^2.0.1" + node-releases "^2.0.2" picocolors "^1.0.0" bser@2.1.1: @@ -2501,20 +2320,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" - integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - -buffer@^5.7.0: +buffer@^5.5.0, buffer@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -2530,16 +2336,6 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -2585,14 +2381,14 @@ camelcase@^5.0.0, camelcase@^5.3.1: integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.2.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e" - integrity sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA== + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001202, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001228, caniuse-lite@^1.0.30001286: - version "1.0.30001287" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001287.tgz#5fab6a46ab9e47146d5dd35abfe47beaf8073c71" - integrity sha512-4udbs9bc0hfNrcje++AxBuc6PfLNHwh3PO9kbwnfCQWyqtlzg3py0YgFu8jyRTTo85VAz4U+VLxSlID09vNtWA== +caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001313: + version "1.0.30001313" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz#a380b079db91621e1b7120895874e2fd62ed2e2f" + integrity sha512-rI1UN0koZUiKINjysQDuRi2VeSCce3bYJNmDcj3PIKREiAmjakugBul1QSkg/fPrlULYl6oWfGg3PbgOSY9X4Q== capital-case@^1.0.4: version "1.0.4" @@ -2603,23 +2399,6 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@2.4.2, chalk@^2.0.0, chalk@^2.4.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" - integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2631,7 +2410,16 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -2683,25 +2471,10 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - chokidar@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== dependencies: anymatch "~3.1.2" braces "~3.0.2" @@ -2718,24 +2491,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -classnames@2.2.6: - version "2.2.6" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" - integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== - classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" @@ -2767,6 +2527,11 @@ cli-highlight@^2.1.11: parse5-htmlparser2-tree-adapter "^6.0.0" yargs "^16.0.0" +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -2805,6 +2570,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2844,11 +2614,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" - integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== - combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2861,16 +2626,11 @@ common-tags@1.8.0: resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== -common-tags@^1.8.0: +common-tags@1.8.2, common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - compute-scroll-into-view@^1.0.17: version "1.0.17" resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab" @@ -2890,18 +2650,6 @@ constant-case@^3.0.4: tslib "^2.0.3" upper-case "^2.0.2" -constants-browserify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -convert-source-map@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" @@ -2910,9 +2658,9 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: safe-buffer "~5.1.1" cookie@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== copy-to-clipboard@^3.2.0: version "3.3.1" @@ -2921,10 +2669,10 @@ copy-to-clipboard@^3.2.0: dependencies: toggle-selection "^1.0.6" -core-js-pure@^3.19.0: - version "3.20.0" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.20.0.tgz#7253feccf8bb05b72c153ddccdbe391ddbffbe03" - integrity sha512-qsrbIwWSEEYOM7z616jAVgwhuDDtPLwZSpUsU3vyUkHYqKTf/uwOJBZg2V7lMurYWkpVlaVOxBrfX0Q3ppvjfg== +core-js-pure@^3.20.2: + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" + integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== cosmiconfig-toml-loader@1.0.0: version "1.0.0" @@ -2944,48 +2692,17 @@ cosmiconfig@7.0.1, cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@^3.0.4, cross-fetch@^3.0.6: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== +cross-fetch@^3.0.6, cross-fetch@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== dependencies: - node-fetch "2.6.1" + node-fetch "2.6.7" cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" @@ -2996,62 +2713,17 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cross-undici-fetch@^0.0.20: - version "0.0.20" - resolved "https://registry.yarnpkg.com/cross-undici-fetch/-/cross-undici-fetch-0.0.20.tgz#6b7c5ac82a3601edd439f37275ac0319d77a120a" - integrity sha512-5d3WBC4VRHpFndECK9bx4TngXrw0OUXdhX561Ty1ZoqMASz9uf55BblhTC1CO6GhMWnvk9SOqYEXQliq6D2P4A== - dependencies: - abort-controller "^3.0.0" - form-data "^4.0.0" - node-fetch "^2.6.5" - undici "^4.9.3" - -cross-undici-fetch@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/cross-undici-fetch/-/cross-undici-fetch-0.1.4.tgz#6058950dc6bf451f81a51582258f4e5a5a01b702" - integrity sha512-jIcyxjQ6mEvUS3y2yhIVktOR7wlGZuyjFAK8woo7pMb+BR1f2ylfTxsN01UPwhqT8gtYPmZ2rPdyoInr1Cdrrw== +cross-undici-fetch@^0.1.19: + version "0.1.25" + resolved "https://registry.yarnpkg.com/cross-undici-fetch/-/cross-undici-fetch-0.1.25.tgz#8c6826dd0ffbb45fcb1a554be5984e0eaef7f3ba" + integrity sha512-KS6hm/VuRO+3jIrg4uidz3mQ8NWvCbiTTOg3yoH30zuGVUvjqZlnXw66h0kuzyfP21hDkrdIbufXCW6BAQdSNw== dependencies: abort-controller "^3.0.0" form-data-encoder "^1.7.1" formdata-node "^4.3.1" - node-fetch "^2.6.5" + node-fetch "^2.6.7" undici "^4.9.3" - -crypto-browserify@3.12.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css.escape@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" - integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= - -cssnano-preset-simple@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-3.0.0.tgz#e95d0012699ca2c741306e9a3b8eeb495a348dbe" - integrity sha512-vxQPeoMRqUT3c/9f0vWeVa2nKQIHFpogtoBvFdW4GQ3IvEJ6uauCP6p3Y5zQDLFcI7/+40FTgX12o7XUL0Ko+w== - dependencies: - caniuse-lite "^1.0.30001202" - -cssnano-simple@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-3.0.0.tgz#a4b8ccdef4c7084af97e19bc5b93b4ecf211e90f" - integrity sha512-oU3ueli5Dtwgh0DyeohcIEE00QVfbPR3HzyXdAl89SfnQG3y0/qcpfLVW+jPIh3/rgMZGwuW96rejZGaYE9eUg== - dependencies: - cssnano-preset-simple "^3.0.0" + web-streams-polyfill "^3.2.0" cssom@^0.4.4: version "0.4.4" @@ -3071,19 +2743,14 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^3.0.2: - version "3.0.10" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5" - integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA== + version "3.0.11" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" + integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== damerau-levenshtein@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz#64368003512a1a6992593741a09a9d31a836f55d" - integrity sha512-VvdQIPGdWP0SqFXghj79Wf/5LArmreyMsGLa6FG6iC4t3j7j5s71TrwWmT/4akbDQIqjfACkLZmjXhA7g2oUZw== - -data-uri-to-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" - integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== + version "1.0.8" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== data-urls@^2.0.0: version "2.0.0" @@ -3100,9 +2767,9 @@ dataloader@2.0.0: integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== date-fns@2.x: - version "2.27.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.27.0.tgz#e1ff3c3ddbbab8a2eaadbb6106be2929a5a2d92b" - integrity sha512-sj+J0Mo2p2X1e306MHq282WS4/A8Pz/95GIFcsPNMPMZVI3EUrAdSv90al1k+p74WGLCruMXk23bfEDZa71X9Q== + version "2.28.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" + integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== date-fns@^1.27.2: version "1.30.1" @@ -3110,22 +2777,15 @@ date-fns@^1.27.2: integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== dayjs@1.x, dayjs@^1.0: - version "1.10.7" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" - integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + version "1.10.8" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.8.tgz#267df4bc6276fcb33c04a6735287e3f429abec41" + integrity sha512-wbNwDfBHHur9UOzNUjeKUOJ0fCb0a52Wx0xInmQ7Y8FstyajiV1NmK1e00cxsr9YrE9r7yAChE0VvpuY5Rnlow== debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@2, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -3133,6 +2793,13 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: dependencies: ms "2.1.2" +debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -3177,6 +2844,13 @@ deepmerge@^4.0, deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -3194,24 +2868,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - dependency-graph@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -3222,25 +2883,16 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" - integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3267,11 +2919,6 @@ dom-align@^1.7.0: resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b" integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg== -domain-browser@4.19.0: - version "4.19.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-4.19.0.tgz#1093e17c0a17dbd521182fe90d49ac1370054af1" - integrity sha512-fRA+BaAWOR/yr/t7T9E9GJztHPeFjj8U35ajyAjCDtAAnTn1Rc1f6W6VGPJrO1tkQv9zWu+JRof7z6oQtiYVFQ== - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -3287,10 +2934,10 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== dotenv@^8.2.0: version "8.6.0" @@ -3314,29 +2961,16 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -electron-to-chromium@^1.3.723, electron-to-chromium@^1.4.17: - version "1.4.23" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.23.tgz#c8fa28db6354062bbd388e24d95c4a263fff5cac" - integrity sha512-q3tB59Api3+DMbLnDPkW/UBHBO7KTGcF+rDCeb0GAGyqFj562s6y+c/2tDKTS/y5lbC+JOvT4MSUALJLPqlcSA== +electron-to-chromium@^1.4.76: + version "1.4.76" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.76.tgz#a0494baedaf51094b1c172999919becd9975a934" + integrity sha512-3Vftv7cenJtQb+k00McEBZ2vVmZ/x+HEF7pcZONZIkOsESqAqVuACmBxMv0JhzX7u0YltU0vSqRqgBSTAhFUjA== elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emittery@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" @@ -3352,23 +2986,11 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -encoding@0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3390,7 +3012,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1: +es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== @@ -3425,11 +3047,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es6-object-assign@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" - integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -3463,11 +3080,11 @@ escodegen@^2.0.0: source-map "~0.6.1" eslint-config-next@^12.0.2: - version "12.0.7" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.0.7.tgz#985f06c3d749673f6b4b214db6b9321da1bf0b5f" - integrity sha512-kWOaym5qjyzR190zFKkZMaHetmiRORmzJiKML7Kr9CL213S6SwkrHHCEL58TRdpx0NA+HzrsFR9zgcV2pvV2Yg== + version "12.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.0.tgz#8ace680dc5207e6ab6c915f3989adec122f582e7" + integrity sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA== dependencies: - "@next/eslint-plugin-next" "12.0.7" + "@next/eslint-plugin-next" "12.1.0" "@rushstack/eslint-patch" "^1.0.8" "@typescript-eslint/parser" "^5.0.0" eslint-import-resolver-node "^0.3.4" @@ -3496,33 +3113,32 @@ eslint-import-resolver-typescript@^2.4.0: resolve "^1.20.0" tsconfig-paths "^3.9.0" -eslint-module-utils@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c" - integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ== +eslint-module-utils@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== dependencies: debug "^3.2.7" find-up "^2.1.0" - pkg-dir "^2.0.0" eslint-plugin-import@^2.25.2: - version "2.25.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.3.tgz#a554b5f66e08fb4f6dc99221866e57cfff824766" - integrity sha512-RzAVbby+72IB3iOEL8clzPLzL3wpDrlwjsTBAQXgyp5SeTqqY+0bFubwuo+y/HLhNZcXV4XqTBO4LGsfyHIDXg== + version "2.25.4" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" + integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== dependencies: array-includes "^3.1.4" array.prototype.flat "^1.2.5" debug "^2.6.9" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.1" + eslint-module-utils "^2.7.2" has "^1.0.3" is-core-module "^2.8.0" is-glob "^4.0.3" minimatch "^3.0.4" object.values "^1.1.5" resolve "^1.20.0" - tsconfig-paths "^3.11.0" + tsconfig-paths "^3.12.0" eslint-plugin-jsx-a11y@^6.5.1: version "6.5.1" @@ -3548,21 +3164,21 @@ eslint-plugin-react-hooks@^4.3.0: integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== eslint-plugin-react@^7.27.0: - version "7.27.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.27.1.tgz#469202442506616f77a854d91babaae1ec174b45" - integrity sha512-meyunDjMMYeWr/4EBLTV1op3iSG3mjT/pz5gti38UzfM4OPpNc2m0t2xvKCOMU5D6FSdd34BIMFOvQbW+i8GAA== + version "7.29.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz#f4eab757f2756d25d6d4c2a58a9e20b004791f05" + integrity sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg== dependencies: array-includes "^3.1.4" array.prototype.flatmap "^1.2.5" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" + minimatch "^3.1.2" object.entries "^1.1.5" object.fromentries "^2.0.5" object.hasown "^1.1.0" object.values "^1.1.5" - prop-types "^15.7.2" + prop-types "^15.8.1" resolve "^2.0.0-next.3" semver "^6.3.0" string.prototype.matchall "^4.0.6" @@ -3593,9 +3209,9 @@ eslint-visitor-keys@^2.0.0: integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== eslint-visitor-keys@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2" - integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@<8.0.0: version "7.32.0" @@ -3686,11 +3302,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -3701,19 +3312,6 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -events@3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -3734,17 +3332,15 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expect@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.4.2.tgz#4429b0f7e307771d176de9bdf23229b101db6ef6" - integrity sha512-BjAXIDC6ZOW+WBFNg96J22D27Nq5ohn+oGcuP2rtOtcjuxNoV9McpQ60PcQWhdFOSBIQdR72e+4HdnbZTFSTyg== +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" + integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== dependencies: - "@jest/types" "^27.4.2" - ansi-styles "^5.0.0" - jest-get-type "^27.4.0" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-regex-util "^27.4.0" + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" external-editor@^3.0.3: version "3.1.0" @@ -3770,10 +3366,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" - integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== +fast-glob@^3.1.1, fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3811,11 +3407,11 @@ fbjs-css-vars@^1.0.0: integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.2.tgz#dfae08a85c66a58372993ce2caf30863f569ff94" - integrity sha512-qv+boqYndjElAJHNN3NoM8XuwQZ1j2m3kEvTgdle8IDjr6oUbkEpvABWtj/rQl3vq4ew7dnElBxL4YJAwTVqQQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" + integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== dependencies: - cross-fetch "^3.0.4" + cross-fetch "^3.1.5" fbjs-css-vars "^1.0.0" loose-envify "^1.0.0" object-assign "^4.1.0" @@ -3859,15 +3455,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -3892,14 +3479,9 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.4.tgz#28d9969ea90661b5134259f312ab6aa7929ac5e2" - integrity sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw== - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== form-data-encoder@^1.7.1: version "1.7.1" @@ -3915,19 +3497,10 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - formdata-node@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.3.1.tgz#e7b78a2a51f524ccc483b4d223c89690fd22061d" - integrity sha512-8xKSa9et4zb+yziWsD/bI+EYjdg1z2p9EpKr+o+Yk12F/wP66bmDdvjj2ZXd2K/MJlR3HBzWnuV7f82jzHRqCA== + version "4.3.2" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.3.2.tgz#0262e94931e36db7239c2b08bdb6aaf18ec47d21" + integrity sha512-k7lYJyzDOSL6h917favP8j1L0/wNyylzU+x+1w4p5haGVHNlP58dbpdJhiCUsDbWsa9HwEtLp89obQgXl2e0qg== dependencies: node-domexception "1.0.0" web-streams-polyfill "4.0.0-beta.1" @@ -3937,7 +3510,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^2.3.2, fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3953,9 +3526,9 @@ functional-red-black-tree@^1.0.1: integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= futoin-hkdf@^1.3.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.4.2.tgz#fd534e848e0e50339b8bfbd81250b09cbff10ba3" - integrity sha512-2BggwLEJOTfXzKq4Tl2bIT37p0IqqKkblH4e0cMp2sXTdmwg/ADBKMxvxaEytYYcgdxgng8+acsi3WgMVUl6CQ== + version "1.5.0" + resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.5.0.tgz#f10cc4d32f1e26568ded58d5a6535a97aa3a064c" + integrity sha512-4CerDhtTgx4i5PKccQIpEp4T9wqmosPIP9Kep35SdCpYkQeriD3zddUVhrO1Fc4QvGhsAnd2rXyoOr5047mJEg== gensync@1.0.0-beta.2, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -3976,13 +3549,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-orientation@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-orientation/-/get-orientation-1.1.2.tgz#20507928951814f8a91ded0a0e67b29dfab98947" - integrity sha512-/pViTfifW+gBbh/RnlFYHINvELT9Znt+SYyDKAUL6uV6By019AK/s+i9XP4jSwq7lwP38Fd8HVeTxym3+hkwmQ== - dependencies: - stream-parser "^0.3.1" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -4015,18 +3581,13 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -4057,13 +3618,13 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" - integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== + version "13.12.1" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb" + integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw== dependencies: type-fest "^0.20.2" -globby@11.0.4, globby@^11.0.3, globby@^11.0.4: +globby@11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -4075,6 +3636,18 @@ globby@11.0.4, globby@^11.0.3, globby@^11.0.4: merge2 "^1.3.0" slash "^3.0.0" +globby@^11.0.3, globby@^11.0.4: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + got@^9.6.0: version "9.6.0" resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" @@ -4092,10 +3665,10 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.2.4: - version "4.2.8" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" - integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== graphql-config@^4.1.0: version "4.1.0" @@ -4114,6 +3687,11 @@ graphql-config@^4.1.0: minimatch "3.0.4" string-env-interpolation "1.0.1" +graphql-executor@0.0.18: + version "0.0.18" + resolved "https://registry.yarnpkg.com/graphql-executor/-/graphql-executor-0.0.18.tgz#6aa4b39e1ca773e159c2a602621e90606df0109a" + integrity sha512-upUSl7tfZCZ5dWG1XkOvpG70Yk3duZKcCoi/uJso4WxJVT6KIrcK4nZ4+2X/hzx46pL8wAukgYHY6iNmocRN+g== + graphql-let@^0.18: version "0.18.6" resolved "https://registry.yarnpkg.com/graphql-let/-/graphql-let-0.18.6.tgz#52424d3be32d6e42eaca9f2b6c12a76b858660cb" @@ -4139,10 +3717,10 @@ graphql-let@^0.18: "@graphql-codegen/typescript-resolvers" "1.19.1" babel-jest "27.1.0" -graphql-request@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-3.7.0.tgz#c7406e537084f8b9788541e3e6704340ca13055b" - integrity sha512-dw5PxHCgBneN2DDNqpWu8QkbbJ07oOziy8z+bK/TAXufsOLaETuVO4GkXrbs0WjhdKhBMN3BkpN/RIvUHkmNUQ== +graphql-request@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-4.0.0.tgz#5e4361d33df1a95ccd7ad23a8ebb6bbca9d5622f" + integrity sha512-cdqQLCXlBGkaLdkLYRl4LtkwaZU6TfpE7/tnUQFl3wXfUPWN74Ov+Q61VuIh+AltS789YfGB6whghmCmeXLvTw== dependencies: cross-fetch "^3.0.6" extract-files "^9.0.0" @@ -4161,14 +3739,14 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.3: tslib "^2.1.0" graphql-ws@^5.4.1: - version "5.5.5" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.5.5.tgz#f375486d3f196e2a2527b503644693ae3a8670a9" - integrity sha512-hvyIS71vs4Tu/yUYHPvGXsTgo0t3arU820+lT5VjZS2go0ewp2LqyCgxEN56CzOG7Iys52eRhHBiD1gGRdiQtw== + version "5.6.2" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.6.2.tgz#c7e5e382bd80d7fef637ea0b86ef4b1cb3d0b09b" + integrity sha512-TsjovINNEGfv52uKWYSVCOLX9LFe6wAhf9n7hIsV3zjflky1dv/mAP+kjXAXsnzV1jH5Sx0S73CtBFNvxus+SQ== graphql@^16.0.1: - version "16.2.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.2.0.tgz#de3150e80f1fc009590b92a9d16ab1b46e12b656" - integrity sha512-MuQd7XXrdOcmfwuLwC2jNvx0n3rxIuNYOxUtiee5XOmfrWo613ar2U8pE7aHAKh8VwfpifubpD9IP+EdEAEOsA== + version "16.3.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05" + integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== has-ansi@^2.0.0: version "2.0.0" @@ -4193,9 +3771,9 @@ has-flag@^4.0.0: integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== has-tostringtag@^1.0.0: version "1.0.0" @@ -4211,28 +3789,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -4251,15 +3807,6 @@ highlight.js@^10.7.1: resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -4284,17 +3831,6 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -http-errors@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -4313,11 +3849,6 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-browserify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -4338,14 +3869,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -4355,17 +3879,10 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: - version "5.1.9" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" - integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== - -image-size@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.0.tgz#58b31fe4743b1cec0a0ac26f5c914d3c5b2f0750" - integrity sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw== - dependencies: - queue "6.0.2" +ignore@^5.1.4, ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== immutable@~3.7.6: version "3.7.6" @@ -4386,9 +3903,9 @@ import-from@4.0.0: integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== import-local@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.3.tgz#4d51c2c495ca9393da259ec66b62e022920211e0" - integrity sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -4411,7 +3928,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4421,21 +3938,22 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -inquirer@^7.3.3: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== +inquirer@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.0.tgz#f44f008dd344bbfc4b30031f45d984e034a3ac3a" + integrity sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ== dependencies: ansi-escapes "^4.2.1" - chalk "^4.1.0" + chalk "^4.1.1" cli-cursor "^3.1.0" cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.19" + lodash "^4.17.21" mute-stream "0.0.8" + ora "^5.4.1" run-async "^2.4.0" - rxjs "^6.6.0" + rxjs "^7.2.0" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" @@ -4464,14 +3982,6 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -4504,10 +4014,10 @@ is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.2.0, is-core-module@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" - integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== +is-core-module@^2.2.0, is-core-module@^2.8.0, is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -4545,13 +4055,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4559,6 +4062,11 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" @@ -4566,14 +4074,6 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" -is-nan@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" - integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - is-negative-zero@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -4652,17 +4152,6 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.3, is-typed-array@^1.1.7: - version "1.1.8" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" - integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4722,17 +4211,7 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-instrument@^5.0.4: +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== @@ -4761,10 +4240,10 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.1.tgz#7085857f17d2441053c6ce5c3b8fdf6882289397" - integrity sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw== +istanbul-reports@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -4774,227 +4253,228 @@ iterall@^1.2.1: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== -jest-changed-files@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.4.2.tgz#da2547ea47c6e6a5f6ed336151bd2075736eb4a5" - integrity sha512-/9x8MjekuzUQoPjDHbBiXbNEBauhrPU2ct7m8TfCg69ywt1y/N+yYwGh3gCpnqUS3klYWDU/lSNgv+JhoD2k1A== +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" + integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" execa "^5.0.0" throat "^6.0.1" -jest-circus@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.4.5.tgz#70bfb78e0200cab9b84747bf274debacaa538467" - integrity sha512-eTNWa9wsvBwPykhMMShheafbwyakcdHZaEYh5iRrQ0PFJxkDP/e3U/FvzGuKWu2WpwUA3C3hPlfpuzvOdTVqnw== +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" + integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== dependencies: - "@jest/environment" "^27.4.4" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.4.2" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.2" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - pretty-format "^27.4.2" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" slash "^3.0.0" stack-utils "^2.0.3" throat "^6.0.1" -jest-cli@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.5.tgz#8708f54c28d13681f3255ec9026a2b15b03d41e8" - integrity sha512-hrky3DSgE0u7sQxaCL7bdebEPHx5QzYmrGuUjaPLmPE8jx5adtvGuOlRspvMoVLTTDOHRnZDoRLYJuA+VCI7Hg== +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" + integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== dependencies: - "@jest/core" "^27.4.5" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" prompts "^2.0.1" yargs "^16.2.0" -jest-config@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.5.tgz#77ed7f2ba7bcfd7d740ade711d0d13512e08a59e" - integrity sha512-t+STVJtPt+fpqQ8GBw850NtSQbnDOw/UzdPfzDaHQ48/AylQlW7LHj3dH+ndxhC1UxJ0Q3qkq7IH+nM1skwTwA== +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" + integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.4.5" - "@jest/types" "^27.4.2" - babel-jest "^27.4.5" + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.1" - graceful-fs "^4.2.4" - jest-circus "^27.4.5" - jest-environment-jsdom "^27.4.4" - jest-environment-node "^27.4.4" - jest-get-type "^27.4.0" - jest-jasmine2 "^27.4.5" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-runner "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" micromatch "^4.0.4" - pretty-format "^27.4.2" + parse-json "^5.2.0" + pretty-format "^27.5.1" slash "^3.0.0" + strip-json-comments "^3.1.1" -jest-diff@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.2.tgz#786b2a5211d854f848e2dcc1e324448e9481f36f" - integrity sha512-ujc9ToyUZDh9KcqvQDkk/gkbf6zSaeEg9AiBxtttXW59H/AcqEYp1ciXAtJp+jXWva5nAf/ePtSsgWwE5mqp4Q== +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== dependencies: chalk "^4.0.0" - diff-sequences "^27.4.0" - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-docblock@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.4.0.tgz#06c78035ca93cbbb84faf8fce64deae79a59f69f" - integrity sha512-7TBazUdCKGV7svZ+gh7C8esAnweJoG+SvcF6Cjqj4l17zA2q1cMwx2JObSioubk317H+cjcHgP+7fTs60paulg== +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" + integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== dependencies: detect-newline "^3.0.0" -jest-each@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.4.2.tgz#19364c82a692d0d26557642098d1f4619c9ee7d3" - integrity sha512-53V2MNyW28CTruB3lXaHNk6PkiIFuzdOC9gR3C6j8YE/ACfrPnz+slB0s17AgU1TtxNzLuHyvNlLJ+8QYw9nBg== +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" + integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - jest-get-type "^27.4.0" - jest-util "^27.4.2" - pretty-format "^27.4.2" - -jest-environment-jsdom@^27.4.4: - version "27.4.4" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.4.4.tgz#94f738e99514d7a880e8ed8e03e3a321d43b49db" - integrity sha512-cYR3ndNfHBqQgFvS1RL7dNqSvD//K56j/q1s2ygNHcfTCAp12zfIromO1w3COmXrxS8hWAh7+CmZmGCIoqGcGA== - dependencies: - "@jest/environment" "^27.4.4" - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" + integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.2" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" jsdom "^16.6.0" -jest-environment-node@^27.4.4: - version "27.4.4" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.4.4.tgz#42fe5e3b224cb69b99811ebf6f5eaa5a59618514" - integrity sha512-D+v3lbJ2GjQTQR23TK0kY3vFVmSeea05giInI41HHOaJnAwOnmUHTZgUaZL+VxUB43pIzoa7PMwWtCVlIUoVoA== +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" + integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== dependencies: - "@jest/environment" "^27.4.4" - "@jest/fake-timers" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" - jest-mock "^27.4.2" - jest-util "^27.4.2" + jest-mock "^27.5.1" + jest-util "^27.5.1" -jest-get-type@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" - integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== -jest-haste-map@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.4.5.tgz#c2921224a59223f91e03ec15703905978ef0cc1a" - integrity sha512-oJm1b5qhhPs78K24EDGifWS0dELYxnoBiDhatT/FThgB9yxqUm5F6li3Pv+Q+apMBmmPNzOBnZ7ZxWMB1Leq1Q== +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" + integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/graceful-fs" "^4.1.2" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-regex-util "^27.4.0" - jest-serializer "^27.4.0" - jest-util "^27.4.2" - jest-worker "^27.4.5" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" micromatch "^4.0.4" walker "^1.0.7" optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.4.5.tgz#ff79d11561679ff6c89715b0cd6b1e8c0dfbc6dc" - integrity sha512-oUnvwhJDj2LhOiUB1kdnJjkx8C5PwgUZQb9urF77mELH9DGR4e2GqpWQKBOYXWs5+uTN9BGDqRz3Aeg5Wts7aw== +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" + integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== dependencies: - "@babel/traverse" "^7.1.0" - "@jest/environment" "^27.4.4" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - expect "^27.4.2" + expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.4.2" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-runtime "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - pretty-format "^27.4.2" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" throat "^6.0.1" -jest-leak-detector@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.4.2.tgz#7fc3120893a7a911c553f3f2bdff9faa4454abbb" - integrity sha512-ml0KvFYZllzPBJWDei3mDzUhyp/M4ubKebX++fPaudpe8OsxUE+m+P6ciVLboQsrzOCWDjE20/eXew9QMx/VGw== +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" + integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== dependencies: - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-matcher-utils@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.4.2.tgz#d17c5038607978a255e0a9a5c32c24e984b6c60b" - integrity sha512-jyP28er3RRtMv+fmYC/PKG8wvAmfGcSNproVTW2Y0P/OY7/hWUOmsPfxN1jOhM+0u2xU984u2yEagGivz9OBGQ== +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== dependencies: chalk "^4.0.0" - jest-diff "^27.4.2" - jest-get-type "^27.4.0" - pretty-format "^27.4.2" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" -jest-message-util@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.4.2.tgz#07f3f1bf207d69cf798ce830cc57f1a849f99388" - integrity sha512-OMRqRNd9E0DkBLZpFtZkAGYOXl6ZpoMtQJWTAREJKDOFa0M6ptB7L67tp+cszMBkvSgKOhNtQp2Vbcz3ZZKo/w== +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" + integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.4.2" + pretty-format "^27.5.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.4.2.tgz#184ff197a25491bfe4570c286daa5d62eb760b88" - integrity sha512-PDDPuyhoukk20JrQKeofK12hqtSka7mWH0QQuxSNgrdiPsrnYYLS6wbzu/HDlxZRzji5ylLRULeuI/vmZZDrYA== +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" jest-pnp-resolver@^1.2.2: @@ -5002,197 +4482,181 @@ jest-pnp-resolver@^1.2.2: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.4.0.tgz#e4c45b52653128843d07ad94aec34393ea14fbca" - integrity sha512-WeCpMpNnqJYMQoOjm1nTtsgbR4XHAk1u00qDoNBQoykM280+/TmgA5Qh5giC1ecy6a5d4hbSsHzpBtu5yvlbEg== +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" + integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.4.5.tgz#9398af854bdb12d6a9e5a8a536ee401f889a3ecf" - integrity sha512-elEVvkvRK51y037NshtEkEnukMBWvlPzZHiL847OrIljJ8yIsujD2GXRPqDXC4rEVKbcdsy7W0FxoZb4WmEs7w== +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" + integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== dependencies: - "@jest/types" "^27.4.2" - jest-regex-util "^27.4.0" - jest-snapshot "^27.4.5" + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" -jest-resolve@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.4.5.tgz#8dc44f5065fb8d58944c20f932cb7b9fe9760cca" - integrity sha512-xU3z1BuOz/hUhVUL+918KqUgK+skqOuUsAi7A+iwoUldK6/+PW+utK8l8cxIWT9AW7IAhGNXjSAh1UYmjULZZw== +jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" + integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" chalk "^4.0.0" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" jest-pnp-resolver "^1.2.2" - jest-util "^27.4.2" - jest-validate "^27.4.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" resolve "^1.20.0" resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.4.5.tgz#daba2ba71c8f34137dc7ac45616add35370a681e" - integrity sha512-/irauncTfmY1WkTaRQGRWcyQLzK1g98GYG/8QvIPviHgO1Fqz1JYeEIsSfF+9mc/UTA6S+IIHFgKyvUrtiBIZg== +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" + integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== dependencies: - "@jest/console" "^27.4.2" - "@jest/environment" "^27.4.4" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.4.0" - jest-environment-jsdom "^27.4.4" - jest-environment-node "^27.4.4" - jest-haste-map "^27.4.5" - jest-leak-detector "^27.4.2" - jest-message-util "^27.4.2" - jest-resolve "^27.4.5" - jest-runtime "^27.4.5" - jest-util "^27.4.2" - jest-worker "^27.4.5" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" source-map-support "^0.5.6" throat "^6.0.1" -jest-runtime@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.4.5.tgz#97703ad2a1799d4f50ab59049bd21a9ceaed2813" - integrity sha512-CIYqwuJQXHQtPd/idgrx4zgJ6iCb6uBjQq1RSAGQrw2S8XifDmoM1Ot8NRd80ooAm+ZNdHVwsktIMGlA1F1FAQ== - dependencies: - "@jest/console" "^27.4.2" - "@jest/environment" "^27.4.4" - "@jest/globals" "^27.4.4" - "@jest/source-map" "^27.4.0" - "@jest/test-result" "^27.4.2" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" - "@types/yargs" "^16.0.0" +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" + integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" execa "^5.0.0" - exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.4.5" - jest-message-util "^27.4.2" - jest-mock "^27.4.2" - jest-regex-util "^27.4.0" - jest-resolve "^27.4.5" - jest-snapshot "^27.4.5" - jest-util "^27.4.2" - jest-validate "^27.4.2" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" slash "^3.0.0" strip-bom "^4.0.0" - yargs "^16.2.0" -jest-serializer@^27.4.0: - version "27.4.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.4.0.tgz#34866586e1cae2388b7d12ffa2c7819edef5958a" - integrity sha512-RDhpcn5f1JYTX2pvJAGDcnsNTnsV9bjYPU8xcV+xPwOXnUPOQwf4ZEuiU6G9H1UztH+OapMgu/ckEVwO87PwnQ== +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" + integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== dependencies: "@types/node" "*" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" -jest-snapshot@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.4.5.tgz#2ea909b20aac0fe62504bc161331f730b8a7ecc7" - integrity sha512-eCi/iM1YJFrJWiT9de4+RpWWWBqsHiYxFG9V9o/n0WXs6GpW4lUt4FAHAgFPTLPqCUVzrMQmSmTZSgQzwqR7IQ== +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" + integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== dependencies: "@babel/core" "^7.7.2" "@babel/generator" "^7.7.2" - "@babel/parser" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.0.0" - "@jest/transform" "^27.4.5" - "@jest/types" "^27.4.2" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" "@types/babel__traverse" "^7.0.4" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.4.2" - graceful-fs "^4.2.4" - jest-diff "^27.4.2" - jest-get-type "^27.4.0" - jest-haste-map "^27.4.5" - jest-matcher-utils "^27.4.2" - jest-message-util "^27.4.2" - jest-resolve "^27.4.5" - jest-util "^27.4.2" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" natural-compare "^1.4.0" - pretty-format "^27.4.2" + pretty-format "^27.5.1" semver "^7.3.2" -jest-util@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621" - integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA== +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" + integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" - graceful-fs "^4.2.4" + graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.4.2.tgz#eecfcc1b1c9429aa007da08a2bae4e32a81bbbc3" - integrity sha512-hWYsSUej+Fs8ZhOm5vhWzwSLmVaPAxRy+Mr+z5MzeaHm9AxUpXdoVMEW4R86y5gOobVfBsMFLk4Rb+QkiEpx1A== +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" + integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== dependencies: - "@jest/types" "^27.4.2" + "@jest/types" "^27.5.1" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.4.0" + jest-get-type "^27.5.1" leven "^3.1.0" - pretty-format "^27.4.2" + pretty-format "^27.5.1" -jest-watcher@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.4.2.tgz#c9037edfd80354c9fe90de4b6f8b6e2b8e736744" - integrity sha512-NJvMVyyBeXfDezhWzUOCOYZrUmkSCiatpjpm+nFUid74OZEHk6aMLrZAukIiFDwdbqp6mTM6Ui1w4oc+8EobQg== +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" + integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== dependencies: - "@jest/test-result" "^27.4.2" - "@jest/types" "^27.4.2" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.4.2" + jest-util "^27.5.1" string-length "^4.0.1" -jest-worker@27.0.0-next.5: - version "27.0.0-next.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.0-next.5.tgz#5985ee29b12a4e191f4aae4bb73b97971d86ec28" - integrity sha512-mk0umAQ5lT+CaOJ+Qp01N6kz48sJG2kr2n1rX0koqKf6FIygQV0qLOdN9SCYID4IVeSigDOcPeGLozdMLYfb5g== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest-worker@^27.4.5: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.5.tgz#d696e3e46ae0f24cff3fa7195ffba22889262242" - integrity sha512-f2s8kEdy15cv9r7q4KkzGXvlY0JTcmCbMHZBfSQDwW77REr45IDWwd0lksDFeVHH2jJ5pqb90T77XscrjeGzzg== +jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^27.0: - version "27.4.5" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.5.tgz#66e45acba44137fac26be9d3cc5bb031e136dc0f" - integrity sha512-uT5MiVN3Jppt314kidCk47MYIRilJjA/l2mxwiuzzxGUeJIvA8/pDaJOAX5KWvjAo7SCydcW0/4WEtgbLMiJkg== + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" + integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== dependencies: - "@jest/core" "^27.4.5" + "@jest/core" "^27.5.1" import-local "^3.0.2" - jest-cli "^27.4.5" + jest-cli "^27.5.1" jose@^1.27.2: version "1.28.1" @@ -5467,15 +4931,6 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - loader-utils@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" @@ -5554,17 +5009,12 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0, lodash@~4.17.0: +lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -5576,7 +5026,7 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^4.0.0: +log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -5641,7 +5091,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2: +make-dir@3.1.0, make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -5665,15 +5115,6 @@ map-cache@^0.2.0: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - memoize-one@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" @@ -5689,15 +5130,15 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0: +merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.1.4.tgz#c17994d3133db8b23807f62bec7f0cb276cfd948" - integrity sha512-E9ZXfK9iQfG9s73ars9qvvvbSIkJZF5yOo9j4tcwM5tN8mUKfj/EKN5PzOr3ZH0y5wL7dLAHw3RVEfpQV9Q7VQ== + version "1.2.0" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.0.tgz#096cdede2eb0b1610b219b1031b935260de1ad08" + integrity sha512-3QRZIS707pZQnijHdhbttXRWwrHhZJ/gzolneoxKVz9N/xmsvY/7Ls8lpnI9gxbgxjcHsAVEW3mgwiZCo6kkJQ== micromatch@^4.0.4: version "4.0.4" @@ -5707,14 +5148,6 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - mime-db@1.51.0: version "1.51.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" @@ -5742,23 +5175,27 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + minimist@1.2.5, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -5803,10 +5240,10 @@ mz@^2.4.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@^3.1.23: - version "3.1.30" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" - integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ== +nanoid@^3.1.30: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== natural-compare@^1.4.0: version "1.4.0" @@ -5832,71 +5269,27 @@ next-auth@^3.29.0: querystring "^0.2.0" next@^12.0.2: - version "12.0.7" - resolved "https://registry.yarnpkg.com/next/-/next-12.0.7.tgz#33ebf229b81b06e583ab5ae7613cffe1ca2103fc" - integrity sha512-sKO8GJJYfuk9c+q+zHSNumvff+wP7ufmOlwT6BuzwiYfFJ61VTTkfTcDLSJ+95ErQJiC54uS4Yg5JEE8H6jXRA== - dependencies: - "@babel/runtime" "7.15.4" - "@hapi/accept" "5.0.2" - "@napi-rs/triples" "1.0.3" - "@next/env" "12.0.7" - "@next/polyfill-module" "12.0.7" - "@next/react-dev-overlay" "12.0.7" - "@next/react-refresh-utils" "12.0.7" - acorn "8.5.0" - assert "2.0.0" - browserify-zlib "0.2.0" - browserslist "4.16.6" - buffer "5.6.0" - caniuse-lite "^1.0.30001228" - chalk "2.4.2" - chokidar "3.5.1" - constants-browserify "1.0.0" - crypto-browserify "3.12.0" - cssnano-simple "3.0.0" - domain-browser "4.19.0" - encoding "0.1.13" - etag "1.8.1" - events "3.3.0" - find-cache-dir "3.3.1" - get-orientation "1.1.2" - https-browserify "1.0.0" - image-size "1.0.0" - jest-worker "27.0.0-next.5" - node-fetch "2.6.1" - node-html-parser "1.4.9" - os-browserify "0.3.0" - p-limit "3.1.0" - path-browserify "1.0.1" - postcss "8.2.15" - process "0.11.10" - querystring-es3 "0.2.1" - raw-body "2.4.1" - react-is "17.0.2" - react-refresh "0.8.3" - regenerator-runtime "0.13.4" - stream-browserify "3.0.0" - stream-http "3.1.1" - string_decoder "1.3.0" - styled-jsx "5.0.0-beta.3" - timers-browserify "2.0.12" - tty-browserify "0.0.1" + version "12.1.0" + resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d" + integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q== + dependencies: + "@next/env" "12.1.0" + caniuse-lite "^1.0.30001283" + postcss "8.4.5" + styled-jsx "5.0.0" use-subscription "1.5.1" - util "0.12.4" - vm-browserify "1.1.2" - watchpack "2.3.0" optionalDependencies: - "@next/swc-android-arm64" "12.0.7" - "@next/swc-darwin-arm64" "12.0.7" - "@next/swc-darwin-x64" "12.0.7" - "@next/swc-linux-arm-gnueabihf" "12.0.7" - "@next/swc-linux-arm64-gnu" "12.0.7" - "@next/swc-linux-arm64-musl" "12.0.7" - "@next/swc-linux-x64-gnu" "12.0.7" - "@next/swc-linux-x64-musl" "12.0.7" - "@next/swc-win32-arm64-msvc" "12.0.7" - "@next/swc-win32-ia32-msvc" "12.0.7" - "@next/swc-win32-x64-msvc" "12.0.7" + "@next/swc-android-arm64" "12.1.0" + "@next/swc-darwin-arm64" "12.1.0" + "@next/swc-darwin-x64" "12.1.0" + "@next/swc-linux-arm-gnueabihf" "12.1.0" + "@next/swc-linux-arm64-gnu" "12.1.0" + "@next/swc-linux-arm64-musl" "12.1.0" + "@next/swc-linux-x64-gnu" "12.1.0" + "@next/swc-linux-x64-musl" "12.1.0" + "@next/swc-win32-arm64-msvc" "12.1.0" + "@next/swc-win32-ia32-msvc" "12.1.0" + "@next/swc-win32-x64-msvc" "12.1.0" no-case@^3.0.4: version "3.0.4" @@ -5911,39 +5304,22 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@^2.6.1, node-fetch@^2.6.5: - version "2.6.6" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89" - integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA== +node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" -node-html-parser@1.4.9: - version "1.4.9" - resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-1.4.9.tgz#3c8f6cac46479fae5800725edb532e9ae8fd816c" - integrity sha512-UVcirFD1Bn0O+TSmloHeHqZZCxHjvtIeGdVdGMhyZ8/PWlEiZaZ5iJzR189yKZr8p0FXN58BUeC7RHRkf/KYGw== - dependencies: - he "1.2.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-releases@^1.1.71: - version "1.1.77" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.77.tgz#50b0cfede855dd374e7585bf228ff34e57c1c32e" - integrity sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ== - -node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== nodemailer@^6.4.16: version "6.7.2" @@ -6000,17 +5376,9 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" - integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== - -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" @@ -6115,10 +5483,20 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-browserify@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" os-tmpdir@~1.0.2: version "1.0.2" @@ -6190,11 +5568,6 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@~1.0.5: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -6210,17 +5583,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" @@ -6230,7 +5592,7 @@ parse-filepath@^1.0.2: map-cache "^0.2.0" path-root "^0.1.1" -parse-json@^5.0.0: +parse-json@^5.0.0, parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6265,11 +5627,6 @@ pascal-case@^3.1.2: no-case "^3.0.4" tslib "^2.0.3" -path-browserify@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" - integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== - path-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" @@ -6298,7 +5655,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -6320,76 +5677,53 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pirates@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.4.tgz#07df81e61028e402735cdd49db701e4885b4e6e6" - integrity sha512-ZIrVPH+A52Dw84R0L3/VS9Op04PuQ2SEoJL6bkshmiTic/HldyW9Tf7oH5mhJZBK7NmDx27vSMrYEXPXclpDKw== +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== pkce-challenge@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-2.2.0.tgz#02622e0498b82aab248c8c7dbf6507e8bbe20abf" integrity sha512-Ly0Y0OwhtG2N1ynk5ruqoyJxkrWhAPmvdRk0teiLh9Dp2+J4URKpv1JSKWD0j1Sd+QCeiwO9lTl0EjmrB2jWeA== -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= - dependencies: - find-up "^2.1.0" - -pkg-dir@^4.1.0, pkg-dir@^4.2.0: +pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" -platform@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7" - integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg== - -postcss@8.2.15: - version "8.2.15" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65" - integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q== +postcss@8.4.5: + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== dependencies: - colorette "^1.2.2" - nanoid "^3.1.23" - source-map "^0.6.1" + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" preact-render-to-string@^5.1.14: - version "5.1.19" - resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.1.19.tgz#ffae7c3bd1680be5ecf5991d41fe3023b3051e0e" - integrity sha512-bj8sn/oytIKO6RtOGSS/1+5CrQyRSC99eLUnEVbqUa6MzJX5dYh7wu9bmT0d6lm/Vea21k9KhCQwvr2sYN3rrQ== + version "5.1.20" + resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.1.20.tgz#9e61a478e8514e5c4140282c7c43d9747a6d32a9" + integrity sha512-ivh2oOGzth0o7XqbatWUQ81WQGoJwSqDKP5z917SoqTWYCAr7dlBzMv3SAMTAu3Gr5g47BJwrvyO44H2Y10ubg== dependencies: pretty-format "^3.8.0" preact@^10.4.1: - version "10.6.4" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.6.4.tgz#ad12c409ff1b4316158486e0a7b8d43636f7ced8" - integrity sha512-WyosM7pxGcndU8hY0OQlLd54tOU+qmG45QXj2dAYrL11HoyU/EzOSTlpJsirbBr1QW7lICxSsVJJmcmUglovHQ== + version "10.6.6" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.6.6.tgz#f1899bc8dab7c0788b858481532cb3b5d764a520" + integrity sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw== prelude-ls@^1.2.1: version "1.2.1" @@ -6406,12 +5740,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -pretty-format@^27.4.2: - version "27.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.2.tgz#e4ce92ad66c3888423d332b40477c87d1dac1fb8" - integrity sha512-p0wNtJ9oLuvgOQDEIZ9zQjZffK7KtyR6Si0jnXULIDwrlNF8Cuir3AZP0hHv0jmKuNN/edOnbMjnzd4uTcmWiw== +pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== dependencies: - "@jest/types" "^27.4.2" ansi-regex "^5.0.1" ansi-styles "^5.0.0" react-is "^17.0.1" @@ -6421,11 +5754,6 @@ pretty-format@^3.8.0: resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" integrity sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U= -process@0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -6446,32 +5774,20 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.8, prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== +prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" object-assign "^4.1.1" - react-is "^16.8.1" + react-is "^16.13.1" psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -6485,11 +5801,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -querystring-es3@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - querystring@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" @@ -6500,38 +5811,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -queue@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" - integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== - dependencies: - inherits "~2.0.3" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -raw-body@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" - integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - dependencies: - bytes "3.1.0" - http-errors "1.7.3" - iconv-lite "0.4.24" - unpipe "1.0.0" - rc-align@^4.0.0: version "4.0.11" resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.11.tgz#8198c62db266bc1b8ef05e56c13275bf72628a5e" @@ -6544,18 +5823,17 @@ rc-align@^4.0.0: rc-util "^5.3.0" resize-observer-polyfill "^1.5.1" -rc-cascader@~2.3.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-2.3.3.tgz#605d04ef469cdd5d892e38cce291157d5a8af941" - integrity sha512-ckD8rKJjS8mdXxylWh1PtIHSFhbj/yf1NimyooqeJlvtLhzRZXIHCj4IuOjwYZ6J1DqoOCCdJfVtc7UeZia38w== +rc-cascader@~3.2.1: + version "3.2.7" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.7.tgz#74ac3ab9258f930e0c84dfacffd838b122b2cedf" + integrity sha512-M8VtKtifTXXo/qqXj63p12tsMNXm1z45Lytj7tu86L6gxIF8keDPcJ16/ZqrhS5JwlBPfoJNA1VooNl/KId15A== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-tree-select "~4.8.0" - rc-trigger "^5.0.4" + rc-select "~14.0.0-alpha.23" + rc-tree "~5.4.3" rc-util "^5.6.1" - warning "^4.0.1" rc-checkbox@~2.3.0: version "2.3.2" @@ -6595,19 +5873,28 @@ rc-drawer@~4.4.2: classnames "^2.2.6" rc-util "^5.7.0" -rc-dropdown@^3.2.0, rc-dropdown@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.2.0.tgz#da6c2ada403842baee3a9e909a0b1a91ba3e1090" - integrity sha512-j1HSw+/QqlhxyTEF6BArVZnTmezw2LnSmRk6I9W7BCqNCKaRwleRmMMs1PHbuaG8dKHVqP6e21RQ7vPBLVnnNw== +rc-dropdown@^3.2.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.2.tgz#097c2ec1b6d55c10eeb94dcf6120ba034c7a58e0" + integrity sha512-49GOz42oNvLtYGoJ2X5UWXJFp7aUiSZkj9OcgTV1UpxFZqHQMw+xijkaL5k3XDkMbb92XsuFnFt7IGG3/C0DKw== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "^2.2.6" + rc-trigger "^5.0.4" + +rc-dropdown@~3.2.5: + version "3.2.5" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.2.5.tgz#c211e571d29d15e7f725b5a75fc8c7f371fc3348" + integrity sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-trigger "^5.0.4" -rc-field-form@~1.21.0: - version "1.21.2" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.21.2.tgz#85bda1ee006ae9f1d146e1000337c69b4bb6d101" - integrity sha512-LR/bURt/Tf5g39mb0wtMtQuWn42d/7kEzpzlC5fNC7yaRVmLTtlPP4sBBlaViETM9uZQKLoaB0Pt9Mubhm9gow== +rc-field-form@~1.23.0: + version "1.23.1" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.23.1.tgz#638c11d05d7ed2efdcb862ff3da5fe2a7d199aaa" + integrity sha512-Mun+eaFmX1Pjud9bz0fD0IvxwDfFKWk2Q8tkt4sg4aKR9/FML/rzYC5MjY77p86X45XBurBDUR3gAda+Cg/ULw== dependencies: "@babel/runtime" "^7.8.4" async-validator "^4.0.2" @@ -6633,9 +5920,9 @@ rc-input-number@~7.3.0: rc-util "^5.9.8" rc-mentions@~1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.1.tgz#46035027d64aa33ef840ba0fbd411871e34617ae" - integrity sha512-LDzGI8jJVGnkhpTZxZuYBhMz3avcZZqPGejikchh97xPni/g4ht714Flh7DVvuzHQ+BoKHhIjobHnw1rcP8erg== + version "1.6.2" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.2.tgz#62ed7cdd8fa86d857c3ce3f9e73438022130815e" + integrity sha512-cntfJkNMq8B910rXuvnsnOV88DfmoUidnQnSIeXzWiYiUX4RL5oWUfSZzs+HAXYRU4SL1l8Mwjx95wHETiZ/fQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" @@ -6644,10 +5931,23 @@ rc-mentions@~1.6.1: rc-trigger "^5.0.4" rc-util "^5.0.1" -rc-menu@^9.0.0, rc-menu@~9.0.12: - version "9.0.14" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.0.14.tgz#289bda4a2f6c5ebb3248e2e305d52cf0c73cb9d5" - integrity sha512-CIox5mZeLDAi32SlHrV7UeSjv7tmJJhwRyxQtZCKt351w3q59XlL4WMFOmtT9gwIfP9h0XoxdBZUMe/xzkp78A== +rc-menu@^9.0.0: + version "9.3.2" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.3.2.tgz#bb842d37ebf71da912bea201cf7ef0a27267ad49" + integrity sha512-h3m45oY1INZyqphGELkdT0uiPnFzxkML8m0VMhJnk2fowtqfiT7F5tJLT3znEVaPIY80vMy1bClCkgq8U91CzQ== + dependencies: + "@babel/runtime" "^7.10.1" + classnames "2.x" + rc-motion "^2.4.3" + rc-overflow "^1.2.0" + rc-trigger "^5.1.2" + rc-util "^5.12.0" + shallowequal "^1.1.0" + +rc-menu@~9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.2.1.tgz#6fbe47f4846363bb81a5a21f0960026c3ada497a" + integrity sha512-UbEtn3rflJ8zS+etYGTVQuzy7Fm+yWXR5c0Rl6ecNTS/dPknRyWAyhJcbeR0Hu1+RdQT+0VCqrUPrgKnm4iY+w== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6658,13 +5958,13 @@ rc-menu@^9.0.0, rc-menu@~9.0.12: shallowequal "^1.1.0" rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4: - version "2.4.4" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.4.tgz#e995d5fa24fc93065c24f714857cf2677d655bb0" - integrity sha512-ms7n1+/TZQBS0Ydd2Q5P4+wJTSOrhIrwNxLXCZpR7Fa3/oac7Yi803HDALc2hLAKaCTQtw9LmQeB58zcwOsqlQ== + version "2.4.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.5.tgz#b061c50bb29ecd3d735d5f4c40924a3c78226cbd" + integrity sha512-f3uJHR4gcpeZS/s8/nYFSOrXt2Wu/h9GrEcbJmC0qmKrVNgwL1pTgrT5kW7lgG6PFeoL4yHDmpQoEKkrPtKIzQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.2.1" + rc-util "^5.18.1" rc-notification@~4.5.7: version "4.5.7" @@ -6677,19 +5977,19 @@ rc-notification@~4.5.7: rc-util "^5.0.1" rc-overflow@^1.0.0, rc-overflow@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.2.tgz#95b0222016c0cdbdc0db85f569c262e7706a5f22" - integrity sha512-X5kj9LDU1ue5wHkqvCprJWLKC+ZLs3p4He/oxjZ1Q4NKaqKBaYf5OdSzRSgh3WH8kSdrfU8LjvlbWnHgJOEkNQ== + version "1.2.3" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.3.tgz#1754216d807f5473304272b0321c3aba7615f47a" + integrity sha512-Bz6dXTn/ww8nmu70tUQfRV0wT3BkfXY6j1lB1O38OVkDPz4xwfAcGK+LJ2zewUR5cTXkJ8hAN7YULohG8z4M7Q== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.5.1" + rc-util "^5.15.0" rc-pagination@~3.1.9: - version "3.1.14" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.14.tgz#1f7d0342edb80dca0989e4ddbe937b1d4657d88d" - integrity sha512-tcugvxrtPiVU00Uh0IwC8NIUlxa4KtA9pXcaMNJdSHeO2uQqVkHEwllsULTAWRF3zRV2ozo2weP/DRKIUrX+Zg== + version "3.1.15" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.15.tgz#e05eddf4c15717a5858290bed0857e27e2f957ff" + integrity sha512-4L3fot8g4E+PjWEgoVGX0noFCg+8ZFZmeLH4vsnZpB3O2T2zThtakjNxG+YvSaYtyMVT4B+GLayjKrKbXQpdAg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" @@ -6708,13 +6008,14 @@ rc-picker@~2.5.17: rc-util "^5.4.0" shallowequal "^1.1.0" -rc-progress@~3.1.0: - version "3.1.4" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.1.4.tgz#66040d0fae7d8ced2b38588378eccb2864bad615" - integrity sha512-XBAif08eunHssGeIdxMXOmRQRULdHaDdIFENQ578CMb4dyewahmmfJRyab+hw4KH4XssEzzYOkAInTLS7JJG+Q== +rc-progress@~3.2.1: + version "3.2.4" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.2.4.tgz#4036acdae2566438545bc4df2203248babaf7549" + integrity sha512-M9WWutRaoVkPUPIrTpRIDpX0SPSrVHzxHdCRCbeoBFrd9UFWTYNWRlHsruJM5FH1AZI+BwB4wOJUNNylg/uFSw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" + rc-util "^5.16.1" rc-rate@~2.9.0: version "2.9.1" @@ -6725,27 +6026,27 @@ rc-rate@~2.9.0: classnames "^2.2.5" rc-util "^5.0.1" -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.1.2.tgz#214bc5d0de19e0a5fcd7d8352d9c1560dd7531b7" - integrity sha512-Qp+1x6D88FxyWBFRYP95IV9A1o0xlkC6qhiTX3YakE+o86QH9IzC7UVnltwmm4Q8uYH+E3F/HRmEiuxXJECdSw== +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz#9f46052f81cdf03498be35144cb7c53fd282c4c7" + integrity sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" rc-util "^5.15.0" resize-observer-polyfill "^1.5.1" -rc-select@~13.2.1: - version "13.2.1" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-13.2.1.tgz#d69675f8bc72622a8f3bc024fa21bfee8d56257d" - integrity sha512-L2cJFAjVEeDiNVa/dlOVKE79OUb0J7sUBvWN3Viav3XHcjvv9Ovn4D8J9QhBSlDXeGuczZ81CZI3BbdHD25+Gg== +rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8: + version "14.0.0" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.0.tgz#87735dbc548f1cc8e94d579b21682ed2d34f7653" + integrity sha512-DkoWMhyxmrfpc1KJSqPORZdkKevzgOINvjR4WI+dibRe6i6DyqGB4Jk21sencnK9di6dumzOCHf93x9t9+gp3Q== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-overflow "^1.0.0" rc-trigger "^5.0.4" - rc-util "^5.9.8" + rc-util "^5.16.1" rc-virtual-list "^3.2.0" rc-slider@~9.7.4: @@ -6777,21 +6078,21 @@ rc-switch@~3.2.0: classnames "^2.2.1" rc-util "^5.0.1" -rc-table@~7.19.0: - version "7.19.2" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.19.2.tgz#976337a5dace3b8e04bea9554d72bc83aa5ab301" - integrity sha512-NdpnoM50MK02H5/hGOsObfxCvGFUG5cHB9turE5BKJ81T5Ycbq193w5tLhnpILXe//Oanzr47MdMxkUnVGP+qg== +rc-table@~7.23.0: + version "7.23.0" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.23.0.tgz#e5f76998ecf3246147d45ed311417c08886e6507" + integrity sha512-Q1gneB2+lUa8EzCCfbrq+jO1qNSwQv1RUUXKB84W/Stdp4EvGOt2+QqGyfotMNM4JUw0fgGLwY+WjnhUhnLuQQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-resize-observer "^1.0.0" + rc-resize-observer "^1.1.0" rc-util "^5.14.0" shallowequal "^1.1.0" rc-tabs@~11.10.0: - version "11.10.5" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.10.5.tgz#53bbb642d04b307f8ce86e318ab99d519507b29b" - integrity sha512-DDuUdV6b9zGRYLtjI5hyejWLKoz1QiLWNgMeBzc3aMeQylZFhTYnFGdDc6HRqj5IYearNTsFPVSA+6VIT8g5cg== + version "11.10.7" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.10.7.tgz#7d8b5dcc17f1608cf3b9425d80069f1415479335" + integrity sha512-7IKmcU7QU3CdYnJTabeXs2DDeLiXLyALC8fvOtgyWWFXUD47G5vG+4bFO3f9+AI+rcFAPpfwapZbXxgmiRuWYQ== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" @@ -6801,14 +6102,15 @@ rc-tabs@~11.10.0: rc-util "^5.5.0" rc-textarea@^0.3.0, rc-textarea@~0.3.0: - version "0.3.6" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.3.6.tgz#b255a9dd78648033bac8daa281ad95249122420a" - integrity sha512-6esiDE4AG3lqnmCdgAp/nutrZAqZEfRT3K749H8vMzNtxy9JScuMJ9MMtWN2DDQh+LCDvX4dgflaRjDCqtDHWw== + version "0.3.7" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.3.7.tgz#987142891efdedb774883c07e2f51b318fde5a11" + integrity sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" rc-util "^5.7.0" + shallowequal "^1.1.0" rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: version "5.1.1" @@ -6818,27 +6120,27 @@ rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: "@babel/runtime" "^7.11.2" rc-trigger "^5.0.0" -rc-tree-select@~4.8.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-4.8.0.tgz#bcbcfb45553f84a878e4ff037ff00b526a4afa62" - integrity sha512-evuVIF7GHCGDdvISdBWl4ZYmG/8foof/RDtzCu/WFLA1tFKZD77RRC3khEsjh4WgsB0vllLe7j+ODJ7jHRcDRQ== +rc-tree-select@~5.1.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.4.tgz#3577135399d1f4931b0f4d8245e0845861802e2b" + integrity sha512-sA6vTUQghzbjh3u6YAwJIebKkJEHUWDPFHQpfiPObqsEYqi9TKE1LvWqbJ77NbOlOARZq0KIb7LDGF8X0dikDQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~13.2.1" - rc-tree "~5.3.0" - rc-util "^5.7.0" + rc-select "~14.0.0-alpha.8" + rc-tree "~5.4.3" + rc-util "^5.16.1" -rc-tree@~5.3.0: - version "5.3.4" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.3.4.tgz#a0f54ef5c21f43de2664fe0fb2831460149e9776" - integrity sha512-iYsjT30gXgo/w08tAcOeSqnTmfFAXwNENQ3fDGLRybIlBHhPMJdksMUlqYIpB28ZROM22QJ13jxuXBikDStYKg== +rc-tree@~5.4.3: + version "5.4.4" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.4.4.tgz#2ea3663ad3c566aef79a46ba6a1e050d24323e01" + integrity sha512-2qoObRgp31DBXmVzMJmo4qmwP20XEa4hR3imWQtRPcgN3pmljW3WKFmZRrYdOFHz7CyTnRsFZR065bBkIoUpiA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.1" + rc-virtual-list "^3.4.2" rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10: version "5.2.10" @@ -6860,16 +6162,16 @@ rc-upload@~4.3.0: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.5.1, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: - version "5.16.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.16.1.tgz#374db7cb735512f05165ddc3d6b2c61c21b8b4e3" - integrity sha512-kSCyytvdb3aRxQacS/71ta6c+kBWvM1v8/2h9d/HaNWauc3qB8pLnF20PJ8NajkNN8gb+rR1l0eWO+D4Pz+LLQ== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: + version "5.18.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.18.1.tgz#80bd1450b5254655d2fbea63e3d34f6871e9be79" + integrity sha512-24xaSrMZUEKh1+suDOtJWfPe9E6YrwryViZcoPO0miJTKzP4qhUlV5AAlKQ82AJilz/AOHfi3l6HoX8qa1ye8w== dependencies: "@babel/runtime" "^7.12.5" react-is "^16.12.0" shallowequal "^1.1.0" -rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.1: +rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.2: version "3.4.2" resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.2.tgz#1078327aa7230b5e456d679ed2ce99f3c036ebd1" integrity sha512-OyVrrPvvFcHvV0ssz5EDZ+7Rf5qLat/+mmujjchNw5FfbJWNDwkpQ99EcVE6+FtNRmX9wFa1LGNpZLUTvp/4GQ== @@ -6915,20 +6217,15 @@ react-highlight-words@^0.17.0: memoize-one "^4.0.0" prop-types "^15.5.8" -react-is@17.0.2, "react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-refresh@0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" - integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg== +"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== react-shallow-renderer@^16.13.1: version "16.14.1" @@ -6956,7 +6253,7 @@ react@^17.0: loose-envify "^1.1.0" object-assign "^4.1.1" -readable-stream@^3.5.0, readable-stream@^3.6.0: +readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6965,13 +6262,6 @@ readable-stream@^3.5.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -6984,20 +6274,15 @@ reflect-metadata@^0.1.13: resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerator-runtime@0.13.4: - version "0.13.4" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91" - integrity sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g== - regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== regexp.prototype.flags@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" @@ -7129,12 +6414,13 @@ resolve.exports@^1.1.0: integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== resolve@^1.20.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.3: version "2.0.0-next.3" @@ -7179,14 +6465,6 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -7199,14 +6477,21 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.3.3, rxjs@^6.6.0: +rxjs@^6.3.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +rxjs@^7.2.0: + version "7.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" + integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -7216,7 +6501,7 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: +"safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -7251,9 +6536,9 @@ schema-utils@3.0.0: ajv-keywords "^3.5.2" scroll-into-view-if-needed@^2.2.25: - version "2.2.28" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.28.tgz#5a15b2f58a52642c88c8eca584644e01703d645a" - integrity sha512-8LuxJSuFVc92+0AdNv4QOxRL4Abeo1DgLnGNkn1XlaujPH/3cCFz3QI60r2VNu4obJJROzgnIUw5TKQkZvZI1w== + version "2.2.29" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885" + integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg== dependencies: compute-scroll-into-view "^1.0.17" @@ -7293,17 +6578,12 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -setimmediate@^1.0.4, setimmediate@^1.0.5: +setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8: +sha.js@^2.4.11: version "2.4.11" resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== @@ -7328,11 +6608,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -7343,9 +6618,9 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signedsource@^1.0.0: version "1.0.0" @@ -7384,6 +6659,11 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +source-map-js@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + source-map-support@^0.5.17, source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -7392,18 +6672,6 @@ source-map-support@^0.5.17, source-map-support@^0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@0.7.3, source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -source-map@0.8.0-beta.0: - version "0.8.0-beta.0" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11" - integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA== - dependencies: - whatwg-url "^7.0.0" - source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7414,6 +6682,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +source-map@^0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + sponge-case@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" @@ -7433,43 +6706,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stacktrace-parser@0.1.10: - version "0.1.10" - resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" - integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== - dependencies: - type-fest "^0.7.1" - -"statuses@>= 1.5.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stream-browserify@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-3.0.0.tgz#22b0a2850cdf6503e73085da1fc7b7d0c2122f2f" - integrity sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA== - dependencies: - inherits "~2.0.4" - readable-stream "^3.5.0" - -stream-http@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-3.1.1.tgz#0370a8017cf8d050b9a8554afe608f043eaff564" - integrity sha512-S7OqaYu0EkFpgeGFb/NPOoPLxFko7TPqtEeFg5DXPB4v/KETHG0Ln6fRFrNezoelpaDKmycEmmZ81cC9DAwgYg== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.4" - readable-stream "^3.6.0" - xtend "^4.0.2" - -stream-parser@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" - integrity sha1-FhhUhpRCACGhGC/wrxkRwSl2F3M= - dependencies: - debug "2" - string-convert@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" @@ -7480,11 +6716,6 @@ string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== -string-hash@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" - integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -7549,20 +6780,13 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@1.3.0, string_decoder@^1.1.1: +string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" -strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -7577,6 +6801,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -7602,29 +6833,10 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -styled-jsx@5.0.0-beta.3: - version "5.0.0-beta.3" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0-beta.3.tgz#400d16179b5dff10d5954ab8be27a9a1b7780dd2" - integrity sha512-HtDDGSFPvmjHIqWf9n8Oo54tAoY/DTplvlyOH2+YOtD80Sp31Ap8ffSmxhgk5EkUoJ7xepdXMGT650mSffWuRA== - dependencies: - "@babel/plugin-syntax-jsx" "7.14.5" - "@babel/types" "7.15.0" - convert-source-map "1.7.0" - loader-utils "1.2.3" - source-map "0.7.3" - string-hash "1.1.3" - stylis "3.5.4" - stylis-rule-sheet "0.0.10" - -stylis-rule-sheet@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz#44e64a2b076643f4b52e5ff71efc04d8c3c4a430" - integrity sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw== - -stylis@3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe" - integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q== +styled-jsx@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77" + integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA== subscriptions-transport-ws@^0.11.0: version "0.11.0" @@ -7671,6 +6883,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + swap-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" @@ -7702,9 +6919,9 @@ sync-fetch@0.3.1, sync-fetch@^0.3.1: node-fetch "^2.6.1" table@^6.0.9: - version "6.7.5" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.5.tgz#f04478c351ef3d8c7904f0e8be90a1b62417d238" - integrity sha512-LFNeryOqiQHqCVKzhkymKwt6ozeRhlm8IL1mE8rNUurkir4heF6PzMyRgaTa4tlyPTGGgXuvVOF/OLWiH09Lqw== + version "6.8.0" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" + integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== dependencies: ajv "^8.0.1" lodash.truncate "^4.4.2" @@ -7758,13 +6975,6 @@ through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -timers-browserify@2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -7806,11 +7016,6 @@ toggle-selection@^1.0.6: resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -7820,13 +7025,6 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" - tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -7863,10 +7061,10 @@ ts-node@^9: source-map-support "^0.5.17" yn "3.1.1" -tsconfig-paths@^3.11.0, tsconfig-paths@^3.9.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b" - integrity sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg== +tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: + version "3.13.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7" + integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" @@ -7900,11 +7098,6 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tty-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811" - integrity sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -7934,11 +7127,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" - integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -7947,9 +7135,9 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typeorm@^0.2.30: - version "0.2.41" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.41.tgz#88758101ac158dc0a0a903d70eaacea2974281cc" - integrity sha512-/d8CLJJxKPgsnrZWiMyPI0rz2MFZnBQrnQ5XP3Vu3mswv2WPexb58QM6BEtmRmlTMYN5KFWUz8SKluze+wS9xw== + version "0.2.45" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.45.tgz#e5bbb3af822dc4646bad96cfa48cd22fa4687cea" + integrity sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA== dependencies: "@sqltools/formatter" "^1.2.2" app-root-path "^3.0.0" @@ -7964,14 +7152,15 @@ typeorm@^0.2.30: reflect-metadata "^0.1.13" sha.js "^2.4.11" tslib "^2.1.0" + uuid "^8.3.2" xml2js "^0.4.23" yargs "^17.0.1" zen-observable-ts "^1.0.0" typescript@^4.0: - version "4.5.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8" - integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg== + version "4.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" + integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== ua-parser-js@^0.7.30: version "0.7.31" @@ -7994,9 +7183,9 @@ unc-path-regex@^0.1.2: integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= undici@^4.9.3: - version "4.12.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-4.12.0.tgz#89dfd99f0257c89a971b933b195188747864c730" - integrity sha512-sJ4CyO3ZPaoxWpLQTJpH/gWD+tCIra2OJ9UPvrX1siyJkgh8NOAybRejJ/g2xHyOdAuoSE0lPRJwRl8AZSXYJQ== + version "4.15.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.15.1.tgz#c2c0e75f232178f0e6781f6b46c81ccc15065f6e" + integrity sha512-h8LJybhMKD09IyQZoQadNtIR/GmugVhTOVREunJrpV6RStriKBFdSVoFzEzTihwXi/27DIBO+Z0OGF+Mzfi0lA== universal-cookie@^4.0.0: version "4.0.4" @@ -8018,11 +7207,6 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -unpipe@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - upper-case-first@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" @@ -8063,17 +7247,10 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@0.12.4, util@^0.12.0: - version "0.12.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253" - integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - safe-buffer "^5.1.2" - which-typed-array "^1.1.2" +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: version "2.3.0" @@ -8081,9 +7258,9 @@ v8-compile-cache@^2.0.3: integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== v8-to-istanbul@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.0.tgz#0aeb763894f1a0a1676adf8a8b7612a38902446c" - integrity sha512-/PRhfd8aTNp9Ggr62HPzXg2XasNFGy5PBt0Rp04du7/8GNNSgxFL6WBTkgMKSL9bFjH+8kKEG3f37FmxiTqUUA== + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -8099,11 +7276,6 @@ value-or-promise@1.0.11, value-or-promise@^1.0.11: resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== -vm-browserify@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -8125,36 +7297,28 @@ walker@^1.0.7: dependencies: makeerror "1.0.12" -warning@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" - integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== - dependencies: - loose-envify "^1.0.0" - -watchpack@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.0.tgz#a41bca3da6afaff31e92a433f4c856a0c25ea0c4" - integrity sha512-MnN0Q1OsvB/GGHETrFeZPQaOelWh/7O+EiFlj8sM9GPjtQkis7k01aAxrg/18kTfoIVcLL+haEVFlXDaSRwKRw== +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" + defaults "^1.0.3" web-streams-polyfill@4.0.0-beta.1: version "4.0.0-beta.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== +web-streams-polyfill@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" + integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -8190,15 +7354,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" @@ -8224,18 +7379,6 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which-typed-array@^1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" - integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-abstract "^1.18.5" - foreach "^2.0.5" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.7" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -8290,14 +7433,14 @@ write-file-atomic@^3.0.0: typedarray-to-buffer "^3.1.5" "ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.6: - version "7.5.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" - integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== ws@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d" - integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw== + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== xml-name-validator@^3.0.0: version "3.0.0" @@ -8322,11 +7465,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -8374,9 +7512,9 @@ yargs-parser@^20.2.2: integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^21.0.0: - version "21.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55" - integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA== + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== yargs@^15.3.1: version "15.4.1" @@ -8409,9 +7547,9 @@ yargs@^16.0.0, yargs@^16.2.0: yargs-parser "^20.2.2" yargs@^17.0.0, yargs@^17.0.1: - version "17.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.0.tgz#295c4ffd0eef148ef3e48f7a2e0f58d0e4f26b1c" - integrity sha512-GQl1pWyDoGptFPJx9b9L6kmR33TGusZvXIZUT+BOz9f7X2L94oeAskFYLEg/FkhV06zZPBYLvLZRWeYId29lew== + version "17.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" + integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== dependencies: cliui "^7.0.2" escalade "^3.1.1" From e177f1eaecae3fd4dc34e87f1a9ec03c26cd2b8b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 7 Mar 2022 18:10:06 +0100 Subject: [PATCH 005/878] Link pull request #29 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42675532..bfe997cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- +- Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) - - - From 2047e377d42dd3197413b84c34baa5751a418909 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 8 Nov 2022 18:28:23 +0100 Subject: [PATCH 006/878] Pull latest images and install security fixes as described on https://pythonspeed.com/articles/docker-cache-insecure-images/ --- Makefile.production | 10 ++++++++-- backend/Dockerfile-production | 9 +++++++++ frontend/Dockerfile-production | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/Makefile.production b/Makefile.production index 0f53b539..9a292dbb 100644 --- a/Makefile.production +++ b/Makefile.production @@ -23,8 +23,14 @@ name : ## Print value of variable `${NAME}` @echo ${NAME} .PHONY : name -build : ## Build images - ${docker_compose} build +pull : ## Pull images + ${docker_compose} pull +.PHONY : pull + +build : pull ## Build images + ${docker_compose} build \ + --pull \ + --no-cache .PHONY : build up : ## (Re)create and start containers diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 609da4c2..454bb4e5 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -10,6 +10,14 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-amd64 AS build +RUN \ + apt-get update && \ + apt-get upgrade && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + COPY ./ /app WORKDIR /app @@ -32,6 +40,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:5.0-bullseye-slim-amd64 RUN \ apt-get update && \ + apt-get upgrade && \ apt-get install \ --yes \ --no-install-recommends \ diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 8920a5ce..99676417 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,11 +1,25 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image FROM node:16.13-buster-slim AS dependencies +RUN \ + apt-get update && \ + apt-get upgrade && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile FROM node:16.13-buster-slim AS build +RUN \ + apt-get update && \ + apt-get upgrade && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* WORKDIR /app COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules @@ -22,6 +36,7 @@ COPY --from=build /app/package.json ./ COPY --from=build /app/public ./public RUN \ apt-get update && \ + apt-get upgrade && \ apt-get install \ --yes \ --no-install-recommends \ From 6231287b499ec3341cee5b34ea35dba40f6d5a26 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Jan 2023 15:48:42 +0100 Subject: [PATCH 007/878] Ignore `tags*` files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d038597..72f15aa8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /nginx/html/maintenance.html /commit /backup +/tags* .env .vscode From fadb1c60037faef219ce0070b506cbcca7e52457 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Jan 2023 15:54:17 +0100 Subject: [PATCH 008/878] Update .NET and backend packages and tools without updating the using code yet --- backend/Dockerfile | 7 +++++-- backend/Dockerfile-bootstrap | 2 +- backend/Dockerfile-production | 4 ++-- backend/dotnet-tools.json | 22 +++++++++++----------- backend/src/Database.csproj | 26 +++++++++++++------------- backend/test/Database.Tests.csproj | 18 +++++++++--------- 6 files changed, 41 insertions(+), 38 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 65a0cea4..7a145b1d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,7 +1,7 @@ # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 ARG GROUP_ID ARG USER_ID @@ -288,6 +288,9 @@ RUN \ ./test/obj && \ mkdir --parents \ ${HOME}/.vscode-server/extensions \ - ${HOME}/.vscode-server-insiders/extensions + ${HOME}/.vscode-server-insiders/extensions && \ + mkdir --parents \ + ${HOME}/.nuget \ + ${HOME}/.dotnet ENTRYPOINT ["/usr/bin/dumb-init", "--"] diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index dc4cb6a5..91700af0 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,5 +1,5 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 ARG GROUP_ID ARG USER_ID diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 454bb4e5..7fd9865c 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -8,7 +8,7 @@ ######### # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:5.0-bullseye-slim-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 AS build RUN \ apt-get update && \ @@ -36,7 +36,7 @@ RUN \ ########### # https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/aspnet:5.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim-amd64 RUN \ apt-get update && \ diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 380edb4c..91fef8dc 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,25 +3,25 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "5.0.2", + "version": "6.0.11", "commands": [ "dotnet-aspnet-codegenerator" ] }, "dotnet-counters": { - "version": "5.0.251802", + "version": "6.0.351802", "commands": [ "dotnet-counters" ] }, "dotnet-dump": { - "version": "5.0.251802", + "version": "6.0.351802", "commands": [ "dotnet-dump" ] }, "dotnet-ef": { - "version": "5.0.11", + "version": "7.0.2", "commands": [ "dotnet-ef" ] @@ -33,43 +33,43 @@ ] }, "dotnet-script": { - "version": "1.2.1", + "version": "1.4.0", "commands": [ "dotnet-script" ] }, "dotnet-trace": { - "version": "5.0.251802", + "version": "6.0.351802", "commands": [ "dotnet-trace" ] }, "dotnet-gcdump": { - "version": "5.0.251802", + "version": "6.0.351802", "commands": [ "dotnet-gcdump" ] }, "dotnet-sos": { - "version": "5.0.251802", + "version": "6.0.351802", "commands": [ "dotnet-sos" ] }, "roslynator.dotnet.cli": { - "version": "0.2.0", + "version": "0.5.0", "commands": [ "roslynator" ] }, "jetbrains.resharper.globaltools": { - "version": "2021.2.2", + "version": "2022.3.1", "commands": [ "jb" ] }, "dotnet-certificate-tool": { - "version": "2.0.6", + "version": "2.0.7", "commands": [ "certificate-tool" ] diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 0383d942..19563cdb 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 9.0 31666837686487542159 @@ -17,27 +17,27 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - - + + + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index b12d8dd6..087ad1ed 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,7 +1,7 @@ - net5.0 + net6.0 9.0 false enable @@ -11,19 +11,19 @@ - - + + - + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 92302b64301ab3cd4c403465679516c82c00664d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 23 Jan 2023 15:20:04 +0100 Subject: [PATCH 009/878] Update backend source code as needed by the new package versions --- backend/src/Data/ApplicationDbContext.cs | 13 +--- backend/src/Data/Entity.cs | 2 +- .../Extensions/SchemaNameOptionsExtension.cs | 9 ++- backend/src/Data/IEntity.cs | 2 +- backend/src/GraphQl/Entities/EntityType.cs | 2 +- backend/src/Startup.cs | 60 +++++++++++++++---- backend/src/Utilities/FileHelpers.cs | 4 +- .../src/Utilities/MultipartRequestHelper.cs | 2 +- 8 files changed, 63 insertions(+), 31 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 67567022..2c0f0652 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Npgsql; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; namespace Database.Data @@ -8,19 +7,11 @@ namespace Database.Data // Inspired by // [Authentication and authorization for SPAs](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0) // [Customize Identity Model](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.0) - [Owned] public sealed class ApplicationDbContext : DbContext { static ApplicationDbContext() { - RegisterEnumerations(); - } - - private static void RegisterEnumerations() - { - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - NpgsqlConnection.GlobalTypeMapper.MapEnum(); } private static void CreateEnumerations(ModelBuilder builder) @@ -43,7 +34,8 @@ EntityTypeBuilder builder .HasDefaultValueSql("gen_random_uuid()"); // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column builder - .UseXminAsConcurrencyToken(); + .Property(e => e.Version) + .IsRowVersion(); return builder; } @@ -86,6 +78,7 @@ protected override void OnModelCreating(ModelBuilder builder) ConfigureEntity( builder.Entity() ) + .OwnsOne(data => data.AppliedMethod, method => { method.OwnsMany(m => m.Arguments); method.OwnsMany(m => m.Sources); }) .ToTable("calorimetric_data"); ConfigureEntity( builder.Entity() diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index 69e311e7..f94e988c 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -11,7 +11,7 @@ public abstract class Entity // [NotMapped] // public Guid Uuid { get => Id; } - public uint xmin { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html + public uint Version { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html protected Entity( ) diff --git a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs index d67ab313..80c090e4 100644 --- a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs +++ b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs @@ -42,7 +42,12 @@ public SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) { } - public override long GetServiceProviderHashCode() + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) + { + return true; + } + + public override int GetServiceProviderHashCode() { return 0; } @@ -51,7 +56,7 @@ public override void PopulateDebugInfo( IDictionary debugInfo ) { - debugInfo[$"Infrastructure.Data:${nameof(SchemaNameOptionsExtension.SchemaName)}"] + debugInfo[$"Database.Data.Extensions:${nameof(SchemaNameOptionsExtension.SchemaName)}"] = Extension.SchemaName; } } diff --git a/backend/src/Data/IEntity.cs b/backend/src/Data/IEntity.cs index 8fcca168..a63b8b19 100644 --- a/backend/src/Data/IEntity.cs +++ b/backend/src/Data/IEntity.cs @@ -6,6 +6,6 @@ public interface IEntity { Guid Id { get; } - uint xmin { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html + uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index 47fe0a71..f41ceed7 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -30,7 +30,7 @@ IObjectTypeDescriptor descriptor ); // TODO Do we want to expose this, require it as input, and use it to discover concurrent writes? descriptor - .Field(t => t.xmin) + .Field(t => t.Version) .Type>() .Name("version") .Ignore(); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 017b3937..fe4957ee 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.Text; using System.Text.Json; using System.Threading.Tasks; using Database.Configuration; @@ -14,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; +using Npgsql; using Serilog; namespace Database @@ -101,10 +104,11 @@ private void ConfigureDatabaseServices(IServiceCollection services) { services.AddPooledDbContextFactory(options => { + var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); + dataSourceBuilder.MapEnum(); options - .UseNpgsql(_appSettings.Database.ConnectionString) + .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName); - /* .UseNodaTime() */ // https://www.npgsql.org/efcore/mapping/nodatime.html if (!_environment.IsProduction()) { options @@ -198,23 +202,53 @@ public void Configure(IApplicationBuilder app) _.MapHealthChecks("/health", new HealthCheckOptions { - ResponseWriter = JsonResponseWriter + ResponseWriter = WriteJsonResponse } ); }); } - private async Task JsonResponseWriter(HttpContext context, HealthReport report) + // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output + private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) +{ + context.Response.ContentType = "application/json; charset=utf-8"; + + var options = new JsonWriterOptions { Indented = true }; + + using var memoryStream = new MemoryStream(); + using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) + { + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("status", healthReport.Status.ToString()); + jsonWriter.WriteStartObject("results"); + + foreach (var healthReportEntry in healthReport.Entries) { - context.Response.ContentType = "application/json"; - await JsonSerializer.SerializeAsync( - context.Response.Body, - report, - new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - } - ).ConfigureAwait(false); + jsonWriter.WriteStartObject(healthReportEntry.Key); + jsonWriter.WriteString("status", + healthReportEntry.Value.Status.ToString()); + jsonWriter.WriteString("description", + healthReportEntry.Value.Description); + jsonWriter.WriteStartObject("data"); + + foreach (var item in healthReportEntry.Value.Data) + { + jsonWriter.WritePropertyName(item.Key); + + JsonSerializer.Serialize(jsonWriter, item.Value, + item.Value?.GetType() ?? typeof(object)); + } + + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); } + + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); + } + + return context.Response.WriteAsync( + Encoding.UTF8.GetString(memoryStream.ToArray())); +} } } \ No newline at end of file diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 9bae5ba3..a7d068aa 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -17,7 +17,7 @@ public static class FileHelpers { // If you require a check on specific characters in the IsValidFileExtensionAndSignature // method, supply the characters in the _allowedChars field. - private static readonly byte[] _allowedChars = Array.Empty(); + // private static readonly byte[] _allowedChars = Array.Empty(); // For more file signatures, see the File Signatures Database (https://www.filesignatures.net/) // and the official specifications for the file types you wish to add. private static readonly Dictionary> _fileSignature = new() @@ -194,7 +194,7 @@ public static async Task ProcessStreamedFile( return Array.Empty(); } - private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions) + private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) { if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) { diff --git a/backend/src/Utilities/MultipartRequestHelper.cs b/backend/src/Utilities/MultipartRequestHelper.cs index f6852c3f..90070750 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -26,7 +26,7 @@ public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLim return boundary; } - public static bool IsMultipartContentType(string contentType) + public static bool IsMultipartContentType(string? contentType) { return !string.IsNullOrEmpty(contentType) && contentType.Contains("multipart/", StringComparison.OrdinalIgnoreCase); From dc462aea14e3bca75f434d6a98a366bb8a11ad06 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 23 Jan 2023 16:30:07 +0100 Subject: [PATCH 010/878] Upgrade frontend --- frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 6 +- .../components/FloatPropositionFormList.tsx | 6 +- .../components/UuidPropositionFormList.tsx | 6 +- frontend/lib/freeTextFilter.tsx | 6 +- frontend/package.json | 37 +- frontend/pages/_app.tsx | 1 - frontend/pages/data/calorimetric.tsx | 6 +- frontend/pages/data/hygrothermal.tsx | 6 +- frontend/pages/data/optical.tsx | 6 +- frontend/pages/data/photovoltaic.tsx | 6 +- frontend/yarn.lock | 6896 ++++++++--------- 12 files changed, 3369 insertions(+), 3615 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 5ce3a092..395680dd 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,7 +1,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:16.13-buster-slim +FROM node:18.13-bullseye-slim ARG GROUP_ID ARG USER_ID diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 99676417..51ccd948 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,6 +1,6 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:16.13-buster-slim AS dependencies +FROM node:18.13-bullseye-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade && \ @@ -12,7 +12,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:16.13-buster-slim AS build +FROM node:18.13-bullseye-slim AS build RUN \ apt-get update && \ apt-get upgrade && \ @@ -25,7 +25,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:16.13-buster-slim +FROM node:18.13-bullseye-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ diff --git a/frontend/components/FloatPropositionFormList.tsx b/frontend/components/FloatPropositionFormList.tsx index 229aa937..1c50a7ad 100644 --- a/frontend/components/FloatPropositionFormList.tsx +++ b/frontend/components/FloatPropositionFormList.tsx @@ -37,7 +37,7 @@ export function FloatPropositionFormList({ {...restField} key={`negator${key}`} name={[name, "negator"]} - fieldKey={[fieldKey, "negator"]} + fieldKey={[fieldKey ?? -1, "negator"]} noStyle initialValue={Negator.Is} > @@ -53,7 +53,7 @@ export function FloatPropositionFormList({ {...restField} key={`comparator${key}`} name={[name, "comparator"]} - fieldKey={[fieldKey, "comparator"]} + fieldKey={[fieldKey ?? -1, "comparator"]} noStyle initialValue={FloatPropositionComparator.EqualTo} > @@ -83,7 +83,7 @@ export function FloatPropositionFormList({ {...restField} key={`value${key}`} name={[name, "value"]} - fieldKey={[fieldKey, "value"]} + fieldKey={[fieldKey ?? -1, "value"]} noStyle > @@ -47,7 +47,7 @@ export function UuidPropositionFormList({ {...restField} key={`comparator${key}`} name={[name, "comparator"]} - fieldKey={[fieldKey, "comparator"]} + fieldKey={[fieldKey ?? -1, "comparator"]} noStyle initialValue={UuidPropositionComparator.EqualTo} > @@ -65,7 +65,7 @@ export function UuidPropositionFormList({ {...restField} key={`value${key}`} name={[name, "value"]} - fieldKey={[fieldKey, "value"]} + fieldKey={[fieldKey ?? -1, "value"]} noStyle > diff --git a/frontend/lib/freeTextFilter.tsx b/frontend/lib/freeTextFilter.tsx index 62f8eab3..7d0bfcc4 100644 --- a/frontend/lib/freeTextFilter.tsx +++ b/frontend/lib/freeTextFilter.tsx @@ -1,4 +1,4 @@ -import { Input, Space, Button } from "antd"; +import { Input, Space, Button, InputRef } from "antd"; import { FilterFilled } from "@ant-design/icons"; import { FilterConfirmProps, @@ -49,7 +49,7 @@ export function getFreeTextFilterProps( } }; - let searchInput: Input | null = null; + let searchInput: InputRef | null = null; return { filterDropdown: ({ @@ -98,7 +98,7 @@ export function getFreeTextFilterProps( const field = getField(record); return field ? doesFieldIncludeFilterValue(field, value) : false; }, - onFilterDropdownVisibleChange: (visible: boolean) => { + onFilterDropdownOpenChange: (visible: boolean) => { if (visible) { setTimeout(() => searchInput?.select(), 100); } diff --git a/frontend/package.json b/frontend/package.json index d7dbb02d..77ba738c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,45 +13,46 @@ }, "dependencies": { "@apollo/client": "3.4.*", - "antd": "^4.0", + "antd": "^5.0", "dayjs": "^1.0", "graphql": "^16.0.1", - "next": "^12.0.2", - "next-auth": "^3.29.0", - "react": "^17.0", + "next": "^13.1.4", + "next-auth": "^4.18.8", + "react": "^18.2.0", "react-cookie": "^4.0.3", - "react-dom": "^17.0", - "react-highlight-words": "^0.17.0" + "react-dom": "^18.2.0", + "react-highlight-words": "^0.20.0" }, "devDependencies": { "@babel/core": "^7.0", "@graphql-codegen/cli": "^2.0.1", "@graphql-codegen/fragment-matcher": "^3.1.0", "@graphql-codegen/import-types-preset": "^2.0.0", - "@graphql-codegen/plugin-helpers": "^2.0.0", + "@graphql-codegen/plugin-helpers": "^3.0.0", "@graphql-codegen/typescript": "^2.0.0", "@graphql-codegen/typescript-operations": "^2.0.1", "@graphql-codegen/typescript-react-apollo": "^3.0.0", "@graphql-codegen/typescript-resolvers": "^2.0.0", "@graphql-tools/load-files": "^6.0", "@graphql-tools/merge": "^8.0.1", - "@graphql-tools/schema": "^8.1.1", - "@types/node": "^16.11.6", + "@graphql-tools/schema": "^9.0.13", + "@types/node": "^18.11.18", "@types/qrcode.react": "^1.0", - "@types/react": "^17.0", - "@types/react-dom": "^17.0", + "@types/react": "^18.0", + "@types/react-dom": "^18.0", "@types/react-highlight-words": "^0.16.3", - "@types/react-test-renderer": "^17.0", - "babel-jest": "^27.0", + "@types/react-test-renderer": "^18.0", + "babel-jest": "^29.0", "deepmerge": "^4.0", - "eslint": "<8.0.0", - "eslint-config-next": "^12.0.2", + "eslint": "^8.0.0", + "eslint-config-next": "^13.1.4", "graphql-let": "^0.18", "graphql-tag": "^2.0", - "jest": "^27.0", - "react-test-renderer": "^17.0", + "jest": "^29.0", + "react-test-renderer": "^18.0", + "ts-node": "^10.9.1", "typescript": "^4.0", - "yaml-loader": "^0.6" + "yaml-loader": "^0.8" }, "browserslist": { "production": [ diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index f26c27a8..de090cc0 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -3,7 +3,6 @@ import { ApolloProvider } from "@apollo/client"; import { useApollo } from "../lib/apollo"; import { CookiesProvider } from "react-cookie"; -import "antd/dist/antd.css"; import "../styles/vars.css"; import "../styles/global.css"; diff --git a/frontend/pages/data/calorimetric.tsx b/frontend/pages/data/calorimetric.tsx index 907d0eb3..d30e7bd4 100644 --- a/frontend/pages/data/calorimetric.tsx +++ b/frontend/pages/data/calorimetric.tsx @@ -90,6 +90,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array() ); + const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -207,8 +208,8 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - message.error( - error.graphQLErrors.map((error) => error.message).join(" ") + messageApi.error( + error.graphQLErrors.map((error) => error.message) ); } // TODO Casting to `CalorimetricData` is wrong and error prone! @@ -229,6 +230,7 @@ function Page() { return ( + {contextHolder} Calorimetric Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/hygrothermal.tsx b/frontend/pages/data/hygrothermal.tsx index 52c8d516..1b06a8b2 100644 --- a/frontend/pages/data/hygrothermal.tsx +++ b/frontend/pages/data/hygrothermal.tsx @@ -78,6 +78,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array() ); + const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -147,8 +148,8 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - message.error( - error.graphQLErrors.map((error) => error.message).join(" ") + messageApi.error( + error.graphQLErrors.map((error) => error.message) ); } // TODO Casting to `HygrothermalData` is wrong and error prone! @@ -169,6 +170,7 @@ function Page() { return ( + {contextHolder} Hygrothermal Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/optical.tsx b/frontend/pages/data/optical.tsx index 26d606ea..a1d71130 100644 --- a/frontend/pages/data/optical.tsx +++ b/frontend/pages/data/optical.tsx @@ -90,6 +90,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array() ); + const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -292,8 +293,8 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - message.error( - error.graphQLErrors.map((error) => error.message).join(" ") + messageApi.error( + error.graphQLErrors.map((error) => error.message) ); } // TODO Casting to `OpticalData` is wrong and error prone! @@ -314,6 +315,7 @@ function Page() { return ( + {contextHolder} Optical Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/photovoltaic.tsx b/frontend/pages/data/photovoltaic.tsx index 81cb0147..9148f392 100644 --- a/frontend/pages/data/photovoltaic.tsx +++ b/frontend/pages/data/photovoltaic.tsx @@ -78,6 +78,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array() ); + const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -147,8 +148,8 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - message.error( - error.graphQLErrors.map((error) => error.message).join(" ") + messageApi.error( + error.graphQLErrors.map((error) => error.message) ); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! @@ -169,6 +170,7 @@ function Page() { return ( + {contextHolder} Photovoltaic Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 96edf6a0..378cf9c2 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3,45 +3,59 @@ "@ampproject/remapping@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" - integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== dependencies: - "@jridgewell/trace-mapping" "^0.3.0" + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" -"@ant-design/colors@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" - integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ== +"@ant-design/colors@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" + integrity sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg== dependencies: "@ctrl/tinycolor" "^3.4.0" +"@ant-design/cssinjs@^1.5.6": + version "1.5.6" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.5.6.tgz#9eb60822a307339d729624533205209c99429062" + integrity sha512-1S7LUPC9BMyQ/CUYgzfePJJwEfsbVHJe3Tpd9zhujTxRM/6LYpN9N4FTaPHVqpnPazm0S2vG0WBkh2T5Erwuug== + dependencies: + "@babel/runtime" "^7.11.1" + "@emotion/hash" "^0.8.0" + "@emotion/unitless" "^0.7.5" + classnames "^2.3.1" + csstype "^3.0.10" + rc-util "^5.27.0" + stylis "^4.0.13" + "@ant-design/icons-svg@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a" integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw== -"@ant-design/icons@^4.7.0": - version "4.7.0" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.7.0.tgz#8c3cbe0a556ba92af5dc7d1e70c0b25b5179af0f" - integrity sha512-aoB4Z7JA431rt6d4u+8xcNPPCrdufSRMUOpxa1ab6mz1JCQZOEVolj2WVs/tDFmN62zzK30mNelEsprLYsSF3g== +"@ant-design/icons@^5.0.0": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.0.1.tgz#febb1fdc5776f58187b2c953ac9a4496069d045b" + integrity sha512-ZyF4ksXCcdtwA/1PLlnFLcF/q8/MhwxXhKHh4oCHDA4Ip+ZzAHoICtyp4wZWfiCVDP0yuz3HsjyvuldHFb3wjA== dependencies: - "@ant-design/colors" "^6.0.0" + "@ant-design/colors" "^7.0.0" "@ant-design/icons-svg" "^4.2.1" "@babel/runtime" "^7.11.2" classnames "^2.2.6" rc-util "^5.9.4" -"@ant-design/react-slick@~0.28.1": - version "0.28.4" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.28.4.tgz#8b296b87ad7c7ae877f2a527b81b7eebd9dd29a9" - integrity sha512-j9eAHTn7GxbXUFNknJoHS2ceAsqrQi2j8XykjZE1IXCD8kJF+t28EvhBLniDpbOsBk/3kjalnhriTfZcjBHNqg== +"@ant-design/react-slick@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.0.tgz#4696eecaa2dea0429e47ae24c267015cfd6df35c" + integrity sha512-OKxZsn8TAf8fYxP79rDXgLs9zvKMTslK6dJ4iLhDXOujUqC5zJPBRszyrcEHXcMPOm1Sgk40JgyF3yiL/Swd7w== dependencies: "@babel/runtime" "^7.10.4" classnames "^2.2.5" json2mq "^0.2.0" - lodash "^4.17.21" - resize-observer-polyfill "^1.5.0" + resize-observer-polyfill "^1.5.1" + throttle-debounce "^5.0.0" "@apollo/client@3.4.*": version "3.4.17" @@ -68,24 +82,47 @@ dependencies: tslib "~2.0.1" -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== +"@ardatan/relay-compiler@12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz#2e4cca43088e807adc63450e8cab037020e91106" + integrity sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q== dependencies: - "@babel/highlight" "^7.10.4" + "@babel/core" "^7.14.0" + "@babel/generator" "^7.14.0" + "@babel/parser" "^7.14.0" + "@babel/runtime" "^7.0.0" + "@babel/traverse" "^7.14.0" + "@babel/types" "^7.0.0" + babel-preset-fbjs "^3.4.0" + chalk "^4.0.0" + fb-watchman "^2.0.0" + fbjs "^3.0.0" + glob "^7.1.1" + immutable "~3.7.6" + invariant "^2.2.4" + nullthrows "^1.1.1" + relay-runtime "12.0.0" + signedsource "^1.0.0" + yargs "^15.3.1" + +"@ardatan/sync-fetch@0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" + integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== + dependencies: + node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.16.4", "@babel/compat-data@^7.17.0": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34" - integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng== +"@babel/compat-data@^7.20.5": + version "7.20.10" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" + integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== "@babel/core@7.15.5": version "7.15.5" @@ -108,198 +145,196 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": - version "7.17.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" - integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== +"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" + integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== dependencies: "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helpers" "^7.17.2" - "@babel/parser" "^7.17.3" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helpers" "^7.20.7" + "@babel/parser" "^7.20.7" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.12" + "@babel/types" "^7.20.7" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.1.2" + json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.17.3", "@babel/generator@^7.7.2": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" - integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== +"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.20.7", "@babel/generator@^7.7.2": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" + integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== dependencies: - "@babel/types" "^7.17.0" + "@babel/types" "^7.20.7" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" - source-map "^0.5.0" -"@babel/helper-annotate-as-pure@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" - integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw== +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" + integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + "@babel/compat-data" "^7.20.5" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.16.7": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz#3778c1ed09a7f3e65e6d6e0f6fbfcc53809d92c9" - integrity sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== - dependencies: - "@babel/helper-get-function-arity" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-get-function-arity@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419" - integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-member-expression-to-functions@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.7.tgz#42b9ca4b2b200123c3b7e726b0ae5153924905b0" - integrity sha512-VtJ/65tYiU/6AbMTDwyoXGPKHgTsfRarivm+YbB5uAzKUyuPjgZSgAFeG87FCigc7KNHu2Pegh1XIT3lXjvz3Q== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.16.7": - version "7.17.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz#3c3b03cc6617e33d68ef5a27a67419ac5199ccd0" - integrity sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.3" - "@babel/types" "^7.17.0" - -"@babel/helper-optimise-call-expression@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2" - integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-create-class-features-plugin@^7.18.6": + version "7.20.12" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" + integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" + integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== + dependencies: + "@babel/types" "^7.20.7" + +"@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" + integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.10" + "@babel/types" "^7.20.7" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" "@babel/helper-plugin-utils@7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.8.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" - integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== - -"@babel/helper-replace-supers@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1" - integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-member-expression-to-functions" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" - -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-skip-transparent-expression-wrappers@^7.16.0": - version "7.16.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" - integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw== - dependencies: - "@babel/types" "^7.16.0" - -"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== - dependencies: - "@babel/types" "^7.16.7" - -"@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== - -"@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== - -"@babel/helpers@^7.15.4", "@babel/helpers@^7.17.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" - integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== - dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.0" - "@babel/types" "^7.17.0" - -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.16.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" - integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== - dependencies: - "@babel/helper-validator-identifier" "^7.16.7" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" + integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" + integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.7" + "@babel/types" "^7.20.7" + +"@babel/helper-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + +"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helpers@^7.15.4", "@babel/helpers@^7.20.7": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" + integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.20.13" + "@babel/types" "^7.20.7" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" @@ -308,29 +343,29 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.17.3": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" - integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" + integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw== "@babel/plugin-proposal-class-properties@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" - integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: - "@babel/helper-create-class-features-plugin" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" - integrity sha512-yuL5iQA/TbZn+RGAfxQXfi7CNLmKi1f8zInn4IgobuCWcAb7i+zj4TYzQ9l8cEzVyJ89PDGuqxK1xZpUDISesw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: - "@babel/compat-data" "^7.17.0" - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.20.7" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -353,12 +388,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" - integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-import-assertions@7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -374,12 +416,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665" - integrity sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -431,194 +473,187 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" - integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" + integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.19.0" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154" - integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" + integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" - integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" - integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" + integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" - integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-optimise-call-expression" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" + integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" - integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" + integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz#c445f75819641788a27a0a3a759d9df911df6abc" - integrity sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" + integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" - integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" + integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-flow" "^7.16.7" + "@babel/helper-plugin-utils" "^7.19.0" + "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" - integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg== + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-function-name@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" - integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== dependencies: - "@babel/helper-compilation-targets" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" - integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" - integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe" - integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA== + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" + integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== dependencies: - "@babel/helper-module-transforms" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-simple-access" "^7.20.2" "@babel/plugin-transform-object-super@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" - integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-replace-supers" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" - integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw== +"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" + integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" - integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" - integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz#eac1565da176ccb1a715dae0b4609858808008c1" - integrity sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ== + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz#f950f0b0c36377503d29a712f16287cedf886cbb" + integrity sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw== dependencies: - "@babel/helper-annotate-as-pure" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/plugin-syntax-jsx" "^7.16.7" - "@babel/types" "^7.17.0" + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.20.7" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a" - integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg== + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-spread@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" - integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg== + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" - integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA== - dependencies: - "@babel/helper-plugin-utils" "^7.16.7" - -"@babel/runtime-corejs3@^7.10.2": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz#fdca2cd05fba63388babe85d349b6801b008fd13" - integrity sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg== + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== dependencies: - core-js-pure "^3.20.2" - regenerator-runtime "^0.13.4" + "@babel/helper-plugin-utils" "^7.18.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4": - version "7.17.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" - integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" + integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== dependencies: - regenerator-runtime "^0.13.4" + regenerator-runtime "^0.13.11" -"@babel/template@^7.15.4", "@babel/template@^7.16.7", "@babel/template@^7.3.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" + integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@babel/traverse@7.15.4": version "7.15.4" @@ -635,28 +670,29 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.7.2": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" - integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.3" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.3" - "@babel/types" "^7.17.0" +"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": + version "7.20.13" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" + integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.13" + "@babel/types" "^7.20.7" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.17.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" - integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" + integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -664,117 +700,118 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@ctrl/tinycolor@^3.4.0": - version "3.4.0" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" - integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== + version "3.5.0" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.5.0.tgz#6e52b3d1c38d13130101771821e09cdd414a16bc" + integrity sha512-tlJpwF40DEQcfR/QF+wNMVyGMaO9FQp6Z1Wahj4Gk3CJQYHwA2xVG7iKDFdW6zuxZY9XWOpGcfNCTsX4McOsOg== -"@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" - integrity sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA== - dependencies: - lodash.get "^4" - make-error "^1" - ts-node "^9" - tslib "^2" +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== +"@eslint/eslintrc@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" + integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.19.0" + ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" + js-yaml "^4.1.0" + minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@graphql-codegen/add@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.1.1.tgz#e161ff1c7cdf74ce20b32f75f640f9592b9a18ca" - integrity sha512-XkVwcqosa0CVBlL1HaQT0gp+EUfhuQE3LzrEpzMQLwchxaj/NPVYtOJL6MUHaYDsHzLqxWrufjfbeB3y2NQgRw== +"@graphql-codegen/add@^3.2.1": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.2.3.tgz#f1ecee085987e7c21841edc4b1fd48877c663e1a" + integrity sha512-sQOnWpMko4JLeykwyjFTxnhqjd/3NOG2OyMuvK76Wnnwh8DRrNf2VEs2kmSvLl7MndMlOj7Kh5U154dVcvhmKQ== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.2" - tslib "~2.3.0" + "@graphql-codegen/plugin-helpers" "^3.1.1" + tslib "~2.4.0" "@graphql-codegen/cli@^2.0.1": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.6.2.tgz#a9aa4656141ee0998cae8c7ad7d0bf9ca8e0c9ae" - integrity sha512-UO75msoVgvLEvfjCezM09cQQqp32+mR8Ma1ACsBpr7nroFvHbgcu2ulx1cMovg4sxDBCsvd9Eq/xOOMpARUxtw== - dependencies: - "@graphql-codegen/core" "2.5.1" - "@graphql-codegen/plugin-helpers" "^2.4.1" - "@graphql-tools/apollo-engine-loader" "^7.0.5" - "@graphql-tools/code-file-loader" "^7.0.6" - "@graphql-tools/git-loader" "^7.0.5" - "@graphql-tools/github-loader" "^7.0.5" - "@graphql-tools/graphql-file-loader" "^7.0.5" - "@graphql-tools/json-file-loader" "^7.1.2" - "@graphql-tools/load" "^7.3.0" - "@graphql-tools/prisma-loader" "^7.0.6" - "@graphql-tools/url-loader" "^7.0.11" - "@graphql-tools/utils" "^8.1.1" - ansi-escapes "^4.3.1" + version "2.16.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.16.4.tgz#c8e6df2dc8cccfd61a088de0ada9a05842ad8ad6" + integrity sha512-MBbdzIIaNZ8BTlFXG00toxU5rIV7Ltf2myaze88HpI5YPVfVJKlfccE6l0/Gv+nLv88CIM/PZrnFLdVtlBmrZw== + dependencies: + "@babel/generator" "^7.18.13" + "@babel/template" "^7.18.10" + "@babel/types" "^7.18.13" + "@graphql-codegen/core" "2.6.8" + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-tools/apollo-engine-loader" "^7.3.6" + "@graphql-tools/code-file-loader" "^7.3.13" + "@graphql-tools/git-loader" "^7.2.13" + "@graphql-tools/github-loader" "^7.3.20" + "@graphql-tools/graphql-file-loader" "^7.5.0" + "@graphql-tools/json-file-loader" "^7.4.1" + "@graphql-tools/load" "7.8.0" + "@graphql-tools/prisma-loader" "^7.2.49" + "@graphql-tools/url-loader" "^7.13.2" + "@graphql-tools/utils" "^9.0.0" + "@whatwg-node/fetch" "^0.6.0" chalk "^4.1.0" - change-case-all "1.0.14" chokidar "^3.5.2" - common-tags "^1.8.0" cosmiconfig "^7.0.0" + cosmiconfig-typescript-loader "4.3.0" debounce "^1.2.0" - dependency-graph "^0.11.0" detect-indent "^6.0.0" - glob "^7.1.6" - globby "^11.0.4" - graphql-config "^4.1.0" + graphql-config "4.4.0" inquirer "^8.0.0" is-glob "^4.0.1" json-to-pretty-yaml "^1.2.2" - latest-version "5.1.0" - listr "^0.14.3" - listr-update-renderer "^0.5.0" + listr2 "^4.0.5" log-symbols "^4.0.0" - minimatch "^4.0.0" - mkdirp "^1.0.4" + shell-quote "^1.7.3" string-env-interpolation "^1.0.1" ts-log "^2.2.3" - tslib "~2.3.0" - valid-url "^1.0.9" - wrap-ansi "^7.0.0" + tslib "^2.4.0" yaml "^1.10.0" yargs "^17.0.0" -"@graphql-codegen/core@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.5.1.tgz#e3d50d3449b8c58b74ea08e97faf656a1b7fc8a1" - integrity sha512-alctBVl2hMnBXDLwkgmnFPrZVIiBDsWJSmxJcM4GKg1PB23+xuov35GE47YAyAhQItE1B1fbYnbb1PtGiDZ4LA== +"@graphql-codegen/core@2.6.8": + version "2.6.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.6.8.tgz#00c4011e3619ddbc6af5e41b2f254d6f6759556e" + integrity sha512-JKllNIipPrheRgl+/Hm/xuWMw9++xNQ12XJR/OHHgFopOg4zmN3TdlRSyYcv/K90hCFkkIwhlHFUQTfKrm8rxQ== dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.1" - "@graphql-tools/schema" "^8.1.2" - "@graphql-tools/utils" "^8.1.1" - tslib "~2.3.0" + "@graphql-codegen/plugin-helpers" "^3.1.1" + "@graphql-tools/schema" "^9.0.0" + "@graphql-tools/utils" "^9.1.1" + tslib "~2.4.0" "@graphql-codegen/fragment-matcher@^3.1.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-3.2.1.tgz#cbaf886d2322cba9ae66e68d7cee512edeaab5b0" - integrity sha512-Hy7tgp5dqDHSUFAfePsK7h5qNk1JWCljpR9gqeY2hnTYzI4YI4jdvH19RiSJ97Px5Ykd15KHzSgj9g0SHm4l8A== + version "3.3.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-3.3.3.tgz#781ed32984eb0aafdedeaf0c22f98fce00755d09" + integrity sha512-lZjarTYQ+w0/TYyoKNFHgIUBI6//rxjo4CwNmOmqGQA0LL+p2nh+/ICJKMFuejPFdK9LI84Y+EEovEFTcDPC+Q== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.2" - tslib "~2.3.0" + "@graphql-codegen/plugin-helpers" "^3.1.1" + tslib "~2.4.0" "@graphql-codegen/import-types-preset@^2.0.0": - version "2.1.12" - resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.1.12.tgz#344baae04ed3e1f20ca44a88a061a264728048a2" - integrity sha512-nFipXzsLJnjZY06PKBovg1hxnueB5n/RSlBPYA29ymahEenNR1vMejbq8/+iuRoxexLuYVSVnJAEcqoBriUKrg== + version "2.2.6" + resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.2.6.tgz#aab292d93f79bffc09bc12a51843c3272b526475" + integrity sha512-Lo2ITOln3UVdyyEPiijj8bVhVg0Ghp/JzHXA2LXxrJVCRbXizQhVC2vjiaWTjMskPt9Zub0yIoce4+RrbsXKcg== dependencies: - "@graphql-codegen/add" "^3.1.1" - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/visitor-plugin-common" "2.7.1" - tslib "~2.3.0" + "@graphql-codegen/add" "^3.2.1" + "@graphql-codegen/plugin-helpers" "^2.7.2" + "@graphql-codegen/visitor-plugin-common" "2.13.1" + tslib "~2.4.0" "@graphql-codegen/plugin-helpers@^1.18.5", "@graphql-codegen/plugin-helpers@^1.18.8": version "1.18.8" @@ -787,48 +824,60 @@ lodash "~4.17.0" tslib "~2.3.0" -"@graphql-codegen/plugin-helpers@^2.0.0", "@graphql-codegen/plugin-helpers@^2.3.2", "@graphql-codegen/plugin-helpers@^2.4.0", "@graphql-codegen/plugin-helpers@^2.4.1": - version "2.4.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.4.2.tgz#e4f6b74dddcf8a9974fef5ce48562ae0980f9fed" - integrity sha512-LJNvwAPv/sKtI3RnRDm+nPD+JeOfOuSOS4FFIpQCMUCyMnFcchV/CPTTv7tT12fLUpEg6XjuFfDBvOwndti30Q== +"@graphql-codegen/plugin-helpers@^2.7.2": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.7.2.tgz#6544f739d725441c826a8af6a49519f588ff9bed" + integrity sha512-kln2AZ12uii6U59OQXdjLk5nOlh1pHis1R98cDZGFnfaiAbX9V3fxcZ1MMJkB7qFUymTALzyjZoXXdyVmPMfRg== dependencies: - "@graphql-tools/utils" "^8.5.2" + "@graphql-tools/utils" "^8.8.0" change-case-all "1.0.14" common-tags "1.8.2" import-from "4.0.0" lodash "~4.17.0" - tslib "~2.3.0" + tslib "~2.4.0" -"@graphql-codegen/schema-ast@^2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-2.4.1.tgz#ad742b53e32f7a2fbff8ea8a91ba7e617e6ef236" - integrity sha512-bIWlKk/ShoVJfghA4Rt1OWnd34/dQmZM/vAe6fu6QKyOh44aAdqPtYQ2dbTyFXoknmu504etKJGEDllYNUJRfg== +"@graphql-codegen/plugin-helpers@^3.0.0", "@graphql-codegen/plugin-helpers@^3.1.1", "@graphql-codegen/plugin-helpers@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz#69a2e91178f478ea6849846ade0a59a844d34389" + integrity sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg== dependencies: - "@graphql-codegen/plugin-helpers" "^2.3.2" - "@graphql-tools/utils" "^8.1.1" - tslib "~2.3.0" + "@graphql-tools/utils" "^9.0.0" + change-case-all "1.0.15" + common-tags "1.8.2" + import-from "4.0.0" + lodash "~4.17.0" + tslib "~2.4.0" + +"@graphql-codegen/schema-ast@^2.6.1": + version "2.6.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-2.6.1.tgz#8ba1b38827c034b51ecd3ce88622c2ae6cd3fe1a" + integrity sha512-5TNW3b1IHJjCh07D2yQNGDQzUpUl2AD+GVe1Dzjqyx/d2Fn0TPMxLsHsKPS4Plg4saO8FK/QO70wLsP7fdbQ1w== + dependencies: + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-tools/utils" "^9.0.0" + tslib "~2.4.0" "@graphql-codegen/typescript-operations@^2.0.1": - version "2.3.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.3.2.tgz#1a94c352b196030d0bf3701ed38343c8619c94e0" - integrity sha512-VLB466ffcT25imuq85ANVqVotVaT4T1gJ6FLJsiJkn5U/iMMZa8CvP+l9oSqDlaBotv38LIZX6pdWGNGAn7kDA== + version "2.5.12" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.12.tgz#36af48b34d70d98a9a2adea1ab79e26fcab23a92" + integrity sha512-/w8IgRIQwmebixf514FOQp2jXOe7vxZbMiSFoQqJgEgzrr42joPsgu4YGU+owv2QPPmu4736OcX8FSavb7SLiA== dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/typescript" "^2.4.5" - "@graphql-codegen/visitor-plugin-common" "2.7.1" + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/typescript" "^2.8.7" + "@graphql-codegen/visitor-plugin-common" "2.13.7" auto-bind "~4.0.0" - tslib "~2.3.0" + tslib "~2.4.0" "@graphql-codegen/typescript-react-apollo@^3.0.0": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.2.8.tgz#776e4f219c9484ff43249569079091fe912bb8d4" - integrity sha512-XmYeBSS3KZF9WePMN5RqYwOHbDybhOxB89C+fvUscFX8MVI5DugMI99qYsdqS8mWQfElRe8ib68zKPaIGTnC0w== + version "3.3.7" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.3.7.tgz#e856caa22c5f7bc9a546c44f54e5f3bd5801ab67" + integrity sha512-9DUiGE8rcwwEkf/S1kpBT/Py/UUs9Qak14bOnTT1JHWs1MWhiDA7vml+A8opU7YFI1EVbSSaE5jjRv11WHoikQ== dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/visitor-plugin-common" "2.7.1" + "@graphql-codegen/plugin-helpers" "^2.7.2" + "@graphql-codegen/visitor-plugin-common" "2.13.1" auto-bind "~4.0.0" change-case-all "1.0.14" - tslib "~2.3.0" + tslib "~2.4.0" "@graphql-codegen/typescript-resolvers@1.19.1": version "1.19.1" @@ -843,16 +892,16 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^2.0.0": - version "2.5.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.5.2.tgz#dc19cc4fd8b7750269651adfa331f9f5f6e33032" - integrity sha512-iYgAttxqE/1TFcKmApwC/VzPFiPa22WYXq6XKQH8eDZUQvG2O4yG+2RfBxfPNGM5DnNkiOGBPuGipTSxV7lmaA== - dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/typescript" "^2.4.5" - "@graphql-codegen/visitor-plugin-common" "2.7.1" - "@graphql-tools/utils" "^8.1.1" + version "2.7.12" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.7.12.tgz#256c52caec517093eec2edfbf9c48206dbe9ba55" + integrity sha512-4Fnfjh4HO6SU8u5phmrY9CJGW1EcRMvaWElydNA9YO2seGCj7nHkGocB24mLlM45CSX5mXuSOgos1nuO5GniSw== + dependencies: + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/typescript" "^2.8.7" + "@graphql-codegen/visitor-plugin-common" "2.13.7" + "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" - tslib "~2.3.0" + tslib "~2.4.0" "@graphql-codegen/typescript@^1.22.0": version "1.23.0" @@ -864,16 +913,16 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.4.5": - version "2.4.5" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.4.5.tgz#80ca7a343511a27cd587c0b220b0a0dd99a4dc4b" - integrity sha512-Ytb8phNHKl/v/wxudsMOAV1dmzIbckWHm2J83PLNOvnu9CGEhgsd67vfe3ZoF95VU2BKSG8BXGa6uL9z2xDmuA== +"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.8.7": + version "2.8.7" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.8.7.tgz#da34261b779a001d7d53b8f454bafc002b06e041" + integrity sha512-Nm5keWqIgg/VL7fivGmglF548tJRP2ttSmfTMuAdY5GNGTJTVZOzNbIOfnbVEDMMWF4V+quUuSyeUQ6zRxtX1w== dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-codegen/schema-ast" "^2.4.1" - "@graphql-codegen/visitor-plugin-common" "2.7.1" + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/schema-ast" "^2.6.1" + "@graphql-codegen/visitor-plugin-common" "2.13.7" auto-bind "~4.0.0" - tslib "~2.3.0" + tslib "~2.4.0" "@graphql-codegen/visitor-plugin-common@1.22.0", "@graphql-codegen/visitor-plugin-common@^1.20.0": version "1.22.0" @@ -891,110 +940,176 @@ parse-filepath "^1.0.2" tslib "~2.3.0" -"@graphql-codegen/visitor-plugin-common@2.7.1": - version "2.7.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.7.1.tgz#bdcded24d1d6f329d32b90704397d6001374b893" - integrity sha512-Ew/+0GwGwL3ZSXS0cYz6lVvE7S5SkbTr7caRpLa4FpAdLGNskCgdA1n6W5tX5c7qsaYstqlxp30hJTfgTsYS6A== +"@graphql-codegen/visitor-plugin-common@2.13.1": + version "2.13.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.1.tgz#2228660f6692bcdb96b1f6d91a0661624266b76b" + integrity sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg== dependencies: - "@graphql-codegen/plugin-helpers" "^2.4.0" - "@graphql-tools/optimize" "^1.0.1" - "@graphql-tools/relay-operation-optimizer" "^6.3.7" - "@graphql-tools/utils" "^8.3.0" + "@graphql-codegen/plugin-helpers" "^2.7.2" + "@graphql-tools/optimize" "^1.3.0" + "@graphql-tools/relay-operation-optimizer" "^6.5.0" + "@graphql-tools/utils" "^8.8.0" auto-bind "~4.0.0" change-case-all "1.0.14" dependency-graph "^0.11.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" - tslib "~2.3.0" + tslib "~2.4.0" -"@graphql-tools/apollo-engine-loader@^7.0.5": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.2.3.tgz#6bebabefa3fd8fb0fc8215a61e53448490b1764c" - integrity sha512-c1AVwAoKf/dSQw6yn/OfwobybplDuAJWLvJS7K7Bm4BiFv0/YXiizPTMsYvxlJYg3uGvmA7fsoeicrzBdlwOpA== +"@graphql-codegen/visitor-plugin-common@2.13.7": + version "2.13.7" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.7.tgz#591e054a970a0d572bdfb765bf948dae21bf8aed" + integrity sha512-xE6iLDhr9sFM1qwCGJcCXRu5MyA0moapG2HVejwyAXXLubYKYwWnoiEigLH2b5iauh6xsl6XP8hh9D1T1dn5Cw== dependencies: - "@graphql-tools/utils" "^8.6.2" - cross-undici-fetch "^0.1.19" - sync-fetch "0.3.1" - tslib "~2.3.0" + "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-tools/optimize" "^1.3.0" + "@graphql-tools/relay-operation-optimizer" "^6.5.0" + "@graphql-tools/utils" "^9.0.0" + auto-bind "~4.0.0" + change-case-all "1.0.15" + dependency-graph "^0.11.0" + graphql-tag "^2.11.0" + parse-filepath "^1.0.2" + tslib "~2.4.0" + +"@graphql-tools/apollo-engine-loader@^7.3.6": + version "7.3.22" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.22.tgz#ace09442dd0aa758a7a42dac3b73252c7935c531" + integrity sha512-4zbL2k7Tcr+qDHBmqKTfrxgOgGkRw0x8NAmrNQVyDYhpP9NiRANmq4DTUgqSPEFiZ6Dx6FYGD4fldRq1JYSYqQ== + dependencies: + "@ardatan/sync-fetch" "0.0.1" + "@graphql-tools/utils" "9.1.4" + "@whatwg-node/fetch" "^0.6.0" + tslib "^2.4.0" + +"@graphql-tools/batch-execute@8.5.15": + version "8.5.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.15.tgz#f61ac71d11e57c9b9f8b8b60fc882e4e9762d182" + integrity sha512-qb12M8XCK6SBJmZDS8Lzd4PVJFsIwNUkYmFuqcTiBqOI/WsoDlQDZI++ghRpGcusLkL9uzcIOTT/61OeHhsaLg== + dependencies: + "@graphql-tools/utils" "9.1.4" + dataloader "2.1.0" + tslib "^2.4.0" + value-or-promise "1.0.12" + +"@graphql-tools/code-file-loader@^7.3.13": + version "7.3.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.16.tgz#58aa85c250175cebe0ea4309214357768d550f93" + integrity sha512-109UFvQjZEntHwjPaHpWvgUudHenGngbXvSImabPc2fdrtgja5KC0h7thCg379Yw6IORHGrF2XbJwS1hAGPPWw== + dependencies: + "@graphql-tools/graphql-tag-pluck" "7.4.3" + "@graphql-tools/utils" "9.1.4" + globby "^11.0.3" + tslib "^2.4.0" + unixify "^1.0.0" -"@graphql-tools/batch-execute@^8.3.2": - version "8.3.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.3.2.tgz#8b5a731d5343f0147734f12d480aafde2a1b6eba" - integrity sha512-ICWqM+MvEkIPHm18Q0cmkvm134zeQMomBKmTRxyxMNhL/ouz6Nqld52/brSlaHnzA3fczupeRJzZ0YatruGBcQ== +"@graphql-tools/delegate@9.0.22": + version "9.0.22" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.22.tgz#12f27ef76c5add456fa8797a496bb7dc82071771" + integrity sha512-dWJGMN8V7KORtbI8eDAjHYTWiMyis/md27M6pPhrlYVlcsDk3U0jbNdgkswBBUEBvqumPRCv8pVOxKcLS4caKA== dependencies: - "@graphql-tools/utils" "^8.6.2" - dataloader "2.0.0" - tslib "~2.3.0" - value-or-promise "1.0.11" + "@graphql-tools/batch-execute" "8.5.15" + "@graphql-tools/executor" "0.0.12" + "@graphql-tools/schema" "9.0.13" + "@graphql-tools/utils" "9.1.4" + dataloader "2.1.0" + tslib "~2.4.0" + value-or-promise "1.0.12" -"@graphql-tools/code-file-loader@^7.0.6": - version "7.2.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.2.4.tgz#f35bf3050b4375ee5c2da0c34a896392cc7bea3f" - integrity sha512-KjIxYKDIbrtRGzeboYC98OnRnCvDVDC3E+suH48J4/KxweSjrG+ZpD++T/A11FdIcFb1Y5OceCw+OwjHI5OoyQ== +"@graphql-tools/executor-graphql-ws@0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.7.tgz#5e7b0e6d02d3b64727bfb37c0f2c1e13badcb829" + integrity sha512-C6EExKoukn4vu3BbvlqsqtC91F4pTLPDZvRceYjpFzTCQSGFSjfrxQGP/haGlemXVRpIDxBy7wpXoQlsF8UmFA== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.6" - "@graphql-tools/utils" "^8.6.2" - globby "^11.0.3" - tslib "~2.3.0" - unixify "^1.0.0" + "@graphql-tools/utils" "9.1.4" + "@repeaterjs/repeater" "3.0.4" + "@types/ws" "^8.0.0" + graphql-ws "5.11.2" + isomorphic-ws "5.0.0" + tslib "^2.4.0" + ws "8.12.0" -"@graphql-tools/delegate@^8.5.1": - version "8.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-8.5.1.tgz#3d146cc3bb74935116d3f4bddb3affdf14a9712d" - integrity sha512-/YPmVxitt57F8sH50pnfXASzOOjEfaUDkX48eF5q6f16+JBncej2zeu+Zm2c68q8MbIxhPlEGfpd0QZeqTvAxw== +"@graphql-tools/executor-http@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.1.tgz#9c4a43811d36d1be2319fd241d5538e2fac45bce" + integrity sha512-bFE6StI7CJEIYGRkAnTYxutSV4OtC1c4MQU3nStOYZZO7KmzIgEQZ4ygPSPrRb+jtRsMCBEqPqlYOD4Rq02aMw== dependencies: - "@graphql-tools/batch-execute" "^8.3.2" - "@graphql-tools/schema" "^8.3.2" - "@graphql-tools/utils" "^8.6.2" - dataloader "2.0.0" - graphql-executor "0.0.18" - tslib "~2.3.0" - value-or-promise "1.0.11" + "@graphql-tools/utils" "9.1.4" + "@repeaterjs/repeater" "3.0.4" + "@whatwg-node/fetch" "0.6.2" + dset "3.1.2" + extract-files "^11.0.0" + meros "1.2.1" + tslib "^2.4.0" + value-or-promise "1.0.12" -"@graphql-tools/git-loader@^7.0.5": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.1.3.tgz#080c57ec2ab83bc0d8d1e3c881c6960b0c7afebd" - integrity sha512-Ya0jRizD6F1hbajk2rwfqJKAp6dQRvzW1gzkOQmlNcQOTtTjWITsFtzk7fS02gZRWkfFBenlTBguGufh91I6bg== +"@graphql-tools/executor-legacy-ws@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.6.tgz#236dd5b3d4d19492978b1458b74928e8c69d16ff" + integrity sha512-L1hRuSvBUCNerYDhfeSZXFeqliDlnNXa3fDHTp7efI3Newpbevqa19Fm0mVzsCL7gqIKOwzrPORwh7kOVE/vew== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.6" - "@graphql-tools/utils" "^8.6.2" + "@graphql-tools/utils" "9.1.4" + "@types/ws" "^8.0.0" + isomorphic-ws "5.0.0" + tslib "^2.4.0" + ws "8.12.0" + +"@graphql-tools/executor@0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.12.tgz#d885c7fa98a8aaeaa771163b71fb98ce9f52f9bd" + integrity sha512-bWpZcYRo81jDoTVONTnxS9dDHhEkNVjxzvFCH4CRpuyzD3uL+5w3MhtxIh24QyWm4LvQ4f+Bz3eMV2xU2I5+FA== + dependencies: + "@graphql-tools/utils" "9.1.4" + "@graphql-typed-document-node/core" "3.1.1" + "@repeaterjs/repeater" "3.0.4" + tslib "^2.4.0" + value-or-promise "1.0.12" + +"@graphql-tools/git-loader@^7.2.13": + version "7.2.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.16.tgz#8c025cad12a623ac91e421eb01380cc177070e7c" + integrity sha512-8DsxYfSouhgKPOBcc7MzuOTM4M/j2UNFn2ehXD0MX9q41t3dKffufJZKsKxE6VyyCUoVYdlRFhUWEyOHPVdcfQ== + dependencies: + "@graphql-tools/graphql-tag-pluck" "7.4.3" + "@graphql-tools/utils" "9.1.4" is-glob "4.0.3" micromatch "^4.0.4" - tslib "~2.3.0" + tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/github-loader@^7.0.5": - version "7.2.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.2.4.tgz#fe5688037015be0f190a1684c953b57279f0fa58" - integrity sha512-QuSN2GWgm/h3lp7o5zpi8TzHnzom4b/f5Zq4Hvprn1OsGaOviHLXQUx6AaWa07cmFvPL0se79R0sEkMZlXlpQQ== +"@graphql-tools/github-loader@^7.3.20": + version "7.3.23" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.23.tgz#9688f4b9cd9596229f0f8c7eed7a8f500806defa" + integrity sha512-oYTZCvW520KNVVonjucDSMhabCFnHwtM1rJbyUkA1JFyzpmmNAAyNMWOOPcU/Q9rTESrsH+Hbja0mfpjpnBKLA== dependencies: - "@graphql-tools/graphql-tag-pluck" "^7.1.6" - "@graphql-tools/utils" "^8.6.2" - cross-undici-fetch "^0.1.19" - sync-fetch "0.3.1" - tslib "~2.3.0" + "@ardatan/sync-fetch" "0.0.1" + "@graphql-tools/graphql-tag-pluck" "7.4.3" + "@graphql-tools/utils" "9.1.4" + "@whatwg-node/fetch" "^0.6.0" + tslib "^2.4.0" -"@graphql-tools/graphql-file-loader@^7.0.5", "@graphql-tools/graphql-file-loader@^7.3.2": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.3.4.tgz#61e3e7e6223a21fbdd987f2abaa6f14104ab7b4a" - integrity sha512-Q0/YtDq0APR6syRclsQMNguWKRlchd8nFTOpLhfc7Xeiy21VhEEi4Ik+quRySfb7ubDfJGhiUq4MQW43FhWJvg== +"@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": + version "7.5.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.14.tgz#6c6527e353cf9adcbda2cdc8a85face03ad8fe04" + integrity sha512-JGer4g57kq4wtsvqv8uZsT4ZG1lLsz1x5yHDfSj2OxyiWw2f1jFkzgby7Ut3H2sseJiQzeeDYZcbm06qgR32pg== dependencies: - "@graphql-tools/import" "^6.6.6" - "@graphql-tools/utils" "^8.6.2" + "@graphql-tools/import" "6.7.15" + "@graphql-tools/utils" "9.1.4" globby "^11.0.3" - tslib "~2.3.0" + tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@^7.1.6": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.1.6.tgz#c78a3f416e06194069609fac6d44c09dd40f6bda" - integrity sha512-VdubvdS8pIrAPVDq6hV7ARXz2Yh8/2153+RO6i+RJOMgyFw8wOW3jRCKE93eN+Hk2pZBC2x3kzdNeUAyVpuslg== +"@graphql-tools/graphql-tag-pluck@7.4.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.4.3.tgz#b07f2263c383d9605d941c836dc01a7bbc6e56a7" + integrity sha512-w+nrJVQw+NTuaZNQG5AwSh4Qe+urP/s4rUz5s1T007rDnv1kvkiX+XHOCnIfJzXOTuvFmG4GGYw/x0CuSRaGZQ== dependencies: "@babel/parser" "^7.16.8" + "@babel/plugin-syntax-import-assertions" "7.20.0" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" - "@graphql-tools/utils" "^8.6.2" - tslib "~2.3.0" + "@graphql-tools/utils" "9.1.4" + tslib "^2.4.0" "@graphql-tools/import@6.4.0": version "6.4.0" @@ -1004,135 +1119,163 @@ resolve-from "5.0.0" tslib "~2.3.0" -"@graphql-tools/import@^6.6.6": - version "6.6.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.6.6.tgz#a4ff216e6b8a49c392bb8a4378d4e9caf2b303d7" - integrity sha512-a0aVajxqu1MsL8EwavA44Osw20lBOIhq8IM2ZIHFPP62cPAcOB26P+Sq57DHMsSyX5YQ0ab9XPM2o4e1dQhs0w== +"@graphql-tools/import@6.7.15": + version "6.7.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.15.tgz#7553e48140797255588b26d423a89aa042196928" + integrity sha512-WNhvauAt2I2iUg+JdQK5oQebKLXqUZWe8naP13K1jOkbTQT7hK3P/4I9AaVmzt0KXRJW5Uow3RgdHZ7eUBKVsA== dependencies: - "@graphql-tools/utils" "8.6.2" + "@graphql-tools/utils" "9.1.4" resolve-from "5.0.0" - tslib "~2.3.0" + tslib "^2.4.0" -"@graphql-tools/json-file-loader@^7.1.2", "@graphql-tools/json-file-loader@^7.3.2": - version "7.3.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.3.4.tgz#41e505f83885f2710ce6781bb150144368ff843a" - integrity sha512-1AROMFh8Lyorf2gTWXgVaUbU3ic84gzAgpRmJCsCla/Nnvn6JiCs4aWHsalk4ZWVXCaK04c8gk8Px1uNQUj02Q== +"@graphql-tools/json-file-loader@^7.3.7", "@graphql-tools/json-file-loader@^7.4.1": + version "7.4.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.15.tgz#ed229d98784350623d2ef32628690db405fa6780" + integrity sha512-pH+hbsDetcEpj+Tmi7ZRUkxzJez2DLdSQuvK5Qi38FX/Nz/5nZKRfW9nqIptGYbuS9+2JPrt9WWNn1aGtegIFQ== dependencies: - "@graphql-tools/utils" "^8.6.2" + "@graphql-tools/utils" "9.1.4" globby "^11.0.3" - tslib "~2.3.0" + tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/load-files@^6.0": - version "6.5.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.5.3.tgz#aa0debfae7fe5e63c4d95e63e4b8adc78bbf53f8" - integrity sha512-hu6hw2fj3ltno7ezbyAQc5VdLCymgx0XIwvk0l9sWAlV2xnaNcX7p/z1qm6U/db2MiCumRBBIm/QuR7Jn71qZg== + version "6.6.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.6.1.tgz#91ce18d910baf8678459486d8cccd474767bec0a" + integrity sha512-nd4GOjdD68bdJkHfRepILb0gGwF63mJI7uD4oJuuf2Kzeq8LorKa6WfyxUhdMuLmZhnx10zdAlWPfwv1NOAL4Q== dependencies: - globby "11.0.4" - tslib "~2.3.0" + globby "11.1.0" + tslib "^2.4.0" unixify "1.0.0" -"@graphql-tools/load@^7.3.0", "@graphql-tools/load@^7.4.1": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.5.2.tgz#0e46129f412bd038ac56996083458c1b8828526f" - integrity sha512-URPqVP77mYxdZxT895DzrWf2C23S3yC/oAmXD4D4YlxR5eVVH/fxu0aZR78WcEKF331fWSiFwWy9j7BZWvkj7g== +"@graphql-tools/load@7.8.0": + version "7.8.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.0.tgz#bd4d2e2a5117de9a60f9691a218217e96afc2ea7" + integrity sha512-l4FGgqMW0VOqo+NMYizwV8Zh+KtvVqOf93uaLo9wJ3sS3y/egPCgxPMDJJ/ufQZG3oZ/0oWeKt68qop3jY0yZg== dependencies: - "@graphql-tools/schema" "8.3.2" - "@graphql-tools/utils" "^8.6.2" + "@graphql-tools/schema" "9.0.4" + "@graphql-tools/utils" "8.12.0" p-limit "3.1.0" - tslib "~2.3.0" + tslib "^2.4.0" -"@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.1", "@graphql-tools/merge@^8.2.3": - version "8.2.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.2.3.tgz#a2861fec230ee7be9dc42d72fed2ac075c31669f" - integrity sha512-XCSmL6/Xg8259OTWNp69B57CPWiVL69kB7pposFrufG/zaAlI9BS68dgzrxmmSqZV5ZHU4r/6Tbf6fwnEJGiSw== +"@graphql-tools/load@^7.5.5": + version "7.8.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.9.tgz#5f4e523095b6154bac43e6a01acb6b043e9afaca" + integrity sha512-/eHRv6OCTI/Ir5XcbtSx0XbW3zOQVscp2MZQFGZKDzqCcGD+NVy4mLCoBwR/OsOUpvWAwMnc+Llb4SDKAYGmjQ== dependencies: - "@graphql-tools/utils" "^8.6.2" - tslib "~2.3.0" + "@graphql-tools/schema" "9.0.13" + "@graphql-tools/utils" "9.1.4" + p-limit "3.1.0" + tslib "^2.4.0" -"@graphql-tools/optimize@^1.0.1": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.2.0.tgz#292d0a269f95d04bc6d822c034569bb7e591fb26" - integrity sha512-l0PTqgHeorQdeOizUor6RB49eOAng9+abSxiC5/aHRo6hMmXVaqv5eqndlmxCpx9BkgNb3URQbK+ZZHVktkP/g== +"@graphql-tools/merge@8.3.15", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": + version "8.3.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.15.tgz#9b24ee5e9c36074684515c7d1587cd3e200c8a8f" + integrity sha512-hYYOlsqkUlL6oOo7zzuk6hIv7xQzy+x21sgK84d5FWaiWYkLYh9As8myuDd9SD5xovWWQ9m/iRhIOVDEMSyEKA== dependencies: - tslib "~2.3.0" + "@graphql-tools/utils" "9.1.4" + tslib "^2.4.0" + +"@graphql-tools/merge@8.3.6": + version "8.3.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.6.tgz#97a936d4c8e8f935e58a514bb516c476437b5b2c" + integrity sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ== + dependencies: + "@graphql-tools/utils" "8.12.0" + tslib "^2.4.0" -"@graphql-tools/prisma-loader@^7.0.6": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.1.2.tgz#a4cb15eacca5e182f36ee0d3a94d76fce002dc86" - integrity sha512-AK/MIEaCDtcV41JTtdTmRBV8I6DM102FWJDbb3rTOVtIYSjU62G23yrPca8aMVcnIneQQNJ7MKYO18agCYXzqw== +"@graphql-tools/optimize@^1.0.1", "@graphql-tools/optimize@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.3.1.tgz#29407991478dbbedc3e7deb8c44f46acb4e9278b" + integrity sha512-5j5CZSRGWVobt4bgRRg7zhjPiSimk+/zIuColih8E8DxuFOaJ+t0qu7eZS5KXWBkjcd4BPNuhUPpNlEmHPqVRQ== dependencies: - "@graphql-tools/url-loader" "^7.7.2" - "@graphql-tools/utils" "^8.6.2" + tslib "^2.4.0" + +"@graphql-tools/prisma-loader@^7.2.49": + version "7.2.54" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.54.tgz#b82d2193c198928f1d914dc088977fd6c502a4c9" + integrity sha512-OoH0JdMQ49IQwiZ8EKOE6sZX4oN5EQwToFnR+ptN5u4ukb4ignyZTrgePFmqOGgj7zyPzisxsWIUXD9szFXXNA== + dependencies: + "@graphql-tools/url-loader" "7.17.3" + "@graphql-tools/utils" "9.1.4" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" - "@types/jsonwebtoken" "^8.5.0" + "@types/jsonwebtoken" "^9.0.0" chalk "^4.1.0" debug "^4.3.1" dotenv "^16.0.0" - graphql-request "^4.0.0" + graphql-request "^5.0.0" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" isomorphic-fetch "^3.0.0" js-yaml "^4.0.0" json-stable-stringify "^1.0.1" - jsonwebtoken "^8.5.1" + jsonwebtoken "^9.0.0" lodash "^4.17.20" - replaceall "^0.1.6" scuid "^1.1.0" - tslib "~2.3.0" + tslib "^2.4.0" yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.3.7": - version "6.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.4.2.tgz#18e20fed783f5de3081ce90d3b4d82047ea8d46b" - integrity sha512-pc/cliYO0veVbMyM5H54lZzQh+9SxnjawqR623rc+jPuY9JUQcuIKkZzM1+E5blbtr4dvh7Bi4uzf3rJ0sxG0Q== - dependencies: - "@graphql-tools/utils" "^8.6.2" - relay-compiler "12.0.0" - tslib "~2.3.0" - -"@graphql-tools/schema@8.3.2", "@graphql-tools/schema@^8.1.1", "@graphql-tools/schema@^8.1.2", "@graphql-tools/schema@^8.3.2": - version "8.3.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-8.3.2.tgz#5b949d7a2cc3936f73507d91cc609996f1266d11" - integrity sha512-77feSmIuHdoxMXRbRyxE8rEziKesd/AcqKV6fmxe7Zt+PgIQITxNDew2XJJg7qFTMNM43W77Ia6njUSBxNOkwg== - dependencies: - "@graphql-tools/merge" "^8.2.3" - "@graphql-tools/utils" "^8.6.2" - tslib "~2.3.0" +"@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.5.0": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.15.tgz#35f9c40ec7b500dacd792592d7007d6ad5c7a10b" + integrity sha512-ILviTglS0eYc4e3rbQ65KlMZ3MWggxer5hro9iDWoN4+amlG3SNo8ejkgZtmI8uQL6Se0NcFt9eASB2SGd64pw== + dependencies: + "@ardatan/relay-compiler" "12.0.0" + "@graphql-tools/utils" "9.1.4" + tslib "^2.4.0" + +"@graphql-tools/schema@9.0.13", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13": + version "9.0.13" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.13.tgz#56b994777df29ac36586a3200fb6397abf7b9d83" + integrity sha512-guRA3fwAtv+M1Kh930P4ydH9aKJTWscIkhVFcWpj/cnjYYxj88jkEJ15ZNiJX/2breNY+sbVgmlgLKb6aXi/Jg== + dependencies: + "@graphql-tools/merge" "8.3.15" + "@graphql-tools/utils" "9.1.4" + tslib "^2.4.0" + value-or-promise "1.0.12" + +"@graphql-tools/schema@9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.4.tgz#1a74608b57abf90fae6fd929d25e5482c57bc05d" + integrity sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ== + dependencies: + "@graphql-tools/merge" "8.3.6" + "@graphql-tools/utils" "8.12.0" + tslib "^2.4.0" value-or-promise "1.0.11" -"@graphql-tools/url-loader@^7.0.11", "@graphql-tools/url-loader@^7.4.2", "@graphql-tools/url-loader@^7.7.2": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.8.0.tgz#f2e6f093dd283734a8d81cc6515898199f897170" - integrity sha512-4j5X40hpYInM5J7KbURSspEJCi6tPqvEE2kyBOyP0C0YIOYtKgUJTAryjbBXqr+HWGyEwJ2zuQ2cQdVMn8l78A== - dependencies: - "@graphql-tools/delegate" "^8.5.1" - "@graphql-tools/utils" "^8.6.2" - "@graphql-tools/wrap" "^8.4.2" - "@n1ru4l/graphql-live-query" "^0.9.0" - "@types/websocket" "^1.0.4" +"@graphql-tools/url-loader@7.17.3", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.3.tgz#5c3270208e5cb039f65d7ef390af93bbc5c76408" + integrity sha512-NY/NQpuf29gjt19XExjRyTj3z44Ohc2OwQZIR/RqHYn+cbdMeXIgJqV5vbPOCN8Umjmm5yVb7kP6oKNGjyeBvw== + dependencies: + "@ardatan/sync-fetch" "0.0.1" + "@graphql-tools/delegate" "9.0.22" + "@graphql-tools/executor-graphql-ws" "0.0.7" + "@graphql-tools/executor-http" "0.1.1" + "@graphql-tools/executor-legacy-ws" "0.0.6" + "@graphql-tools/utils" "9.1.4" + "@graphql-tools/wrap" "9.3.1" "@types/ws" "^8.0.0" - cross-undici-fetch "^0.1.19" - dset "^3.1.0" - extract-files "^11.0.0" - graphql-sse "^1.0.1" - graphql-ws "^5.4.1" - isomorphic-ws "^4.0.1" - meros "^1.1.4" - subscriptions-transport-ws "^0.11.0" - sync-fetch "^0.3.1" - tslib "^2.3.0" - valid-url "^1.0.9" + "@whatwg-node/fetch" "^0.6.0" + isomorphic-ws "5.0.0" + tslib "^2.4.0" value-or-promise "^1.0.11" - ws "^8.3.0" + ws "8.12.0" -"@graphql-tools/utils@8.6.2", "@graphql-tools/utils@^8.1.1", "@graphql-tools/utils@^8.3.0", "@graphql-tools/utils@^8.5.1", "@graphql-tools/utils@^8.5.2", "@graphql-tools/utils@^8.6.2": - version "8.6.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.6.2.tgz#095408135f091aac68fe18a0a21b708e685500da" - integrity sha512-x1DG0cJgpJtImUlNE780B/dfp8pxvVxOD6UeykFH5rHes26S4kGokbgU8F1IgrJ1vAPm/OVBHtd2kicTsPfwdA== +"@graphql-tools/utils@8.12.0": + version "8.12.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.12.0.tgz#243bc4f5fc2edbc9e8fd1038189e57d837cbe31f" + integrity sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw== dependencies: - tslib "~2.3.0" + tslib "^2.4.0" + +"@graphql-tools/utils@9.1.4", "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1": + version "9.1.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.1.4.tgz#2c9e0aefc9655dd73247667befe3c850ec014f3f" + integrity sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg== + dependencies: + tslib "^2.4.0" "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.9.1": version "7.10.0" @@ -1143,41 +1286,48 @@ camel-case "4.1.2" tslib "~2.2.0" -"@graphql-tools/wrap@^8.4.2": - version "8.4.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-8.4.3.tgz#cb7d703c41cebe3c32b999d3a28c17e3eac12783" - integrity sha512-2bd3GtlqV72idEphLTM8SRiSxEDrMPGE556faW8N5Q2qK7qlCEu9AQK0HNKWKdtEPTHqahndZtIPcdpNN6xGig== +"@graphql-tools/utils@^8.8.0": + version "8.13.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.1.tgz#b247607e400365c2cd87ff54654d4ad25a7ac491" + integrity sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw== dependencies: - "@graphql-tools/delegate" "^8.5.1" - "@graphql-tools/schema" "^8.3.2" - "@graphql-tools/utils" "^8.6.2" - tslib "~2.3.0" - value-or-promise "1.0.11" + tslib "^2.4.0" -"@graphql-typed-document-node/core@^3.0.0": +"@graphql-tools/wrap@9.3.1": + version "9.3.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.1.tgz#d0718d87080e7a54655d629a78b08c9d39f314ea" + integrity sha512-uzY1HKc7qMErWL3ybv8bFG3hI1rTJPVYQ8WeJkCF/r/+aHEkUj0Bo2PYZrZTX1UIr3Tb4P5GyhqYBgZOXraZjw== + dependencies: + "@graphql-tools/delegate" "9.0.22" + "@graphql-tools/schema" "9.0.13" + "@graphql-tools/utils" "9.1.4" + tslib "^2.4.0" + value-or-promise "1.0.12" + +"@graphql-typed-document-node/core@3.1.1", "@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: - "@humanwhocodes/object-schema" "^1.2.0" + "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" - minimatch "^3.0.4" + minimatch "^3.0.5" -"@humanwhocodes/object-schema@^1.2.0": +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== -"@iarna/toml@^2.2.5": - version "2.2.5" - resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" - integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== - "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1194,144 +1344,166 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== +"@jest/console@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.3.1.tgz#3e3f876e4e47616ea3b1464b9fbda981872e9583" + integrity sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" slash "^3.0.0" -"@jest/core@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/core@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.3.1.tgz#bff00f413ff0128f4debec1099ba7dcd649774a1" + integrity sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.3.1" + "@jest/reporters" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" + ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" + jest-changed-files "^29.2.0" + jest-config "^29.3.1" + jest-haste-map "^29.3.1" + jest-message-util "^29.3.1" + jest-regex-util "^29.2.0" + jest-resolve "^29.3.1" + jest-resolve-dependencies "^29.3.1" + jest-runner "^29.3.1" + jest-runtime "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" + jest-watcher "^29.3.1" micromatch "^4.0.4" - rimraf "^3.0.0" + pretty-format "^29.3.1" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.3.1.tgz#eb039f726d5fcd14698acd072ac6576d41cfcaa6" + integrity sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "^29.3.1" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/expect-utils@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" + integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + jest-get-type "^29.2.0" + +"@jest/expect@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.3.1.tgz#456385b62894349c1d196f2d183e3716d4c6a6cd" + integrity sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg== + dependencies: + expect "^29.3.1" + jest-snapshot "^29.3.1" + +"@jest/fake-timers@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.3.1.tgz#b140625095b60a44de820876d4c14da1aa963f67" + integrity sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A== + dependencies: + "@jest/types" "^29.3.1" + "@sinonjs/fake-timers" "^9.1.2" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "^29.3.1" + jest-mock "^29.3.1" + jest-util "^29.3.1" -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/globals@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.3.1.tgz#92be078228e82d629df40c3656d45328f134a0c6" + integrity sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/environment" "^29.3.1" + "@jest/expect" "^29.3.1" + "@jest/types" "^29.3.1" + jest-mock "^29.3.1" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.3.1.tgz#9a6d78c109608e677c25ddb34f907b90e07b4310" + integrity sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" + "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" + jest-worker "^29.3.1" slash "^3.0.0" - source-map "^0.6.0" string-length "^4.0.1" - terminal-link "^2.0.0" - v8-to-istanbul "^8.1.0" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.0.0": + version "29.0.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" + integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/source-map@^29.2.0": + version "29.2.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" + integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== dependencies: + "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" - source-map "^0.6.0" -"@jest/test-result@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/test-result@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.3.1.tgz#92cd5099aa94be947560a24610aa76606de78f50" + integrity sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.3.1" + "@jest/types" "^29.3.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-sequencer@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz#fa24b3b050f7a59d48f7ef9e0b782ab65123090d" + integrity sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA== dependencies: - "@jest/test-result" "^27.5.1" + "@jest/test-result" "^29.3.1" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + jest-haste-map "^29.3.1" + slash "^3.0.0" -"@jest/transform@^27.1.0", "@jest/transform@^27.5.1": +"@jest/transform@^27.1.0": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== @@ -1352,6 +1524,27 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/transform@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.3.1.tgz#1e6bd3da4af50b5c82a539b7b1f3770568d6e36d" + integrity sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.3.1" + "@jridgewell/trace-mapping" "^0.3.15" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.3.1" + jest-regex-util "^29.2.0" + jest-util "^29.3.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.1" + "@jest/types@^27.1.0", "@jest/types@^27.5.1": version "27.5.1" resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" @@ -1363,111 +1556,142 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jridgewell/resolve-uri@^3.0.3": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c" - integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew== +"@jest/types@^29.3.1": + version "29.3.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3" + integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA== + dependencies: + "@jest/schemas" "^29.0.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.11" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" - integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.0": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" - integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== dependencies: - "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" -"@n1ru4l/graphql-live-query@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc" - integrity sha512-BTpWy1e+FxN82RnLz4x1+JcEewVdfmUhV1C6/XYD5AjS7PQp9QFF7K8bCD6gzPTr2l+prvqOyVueQhFJxB1vfg== +"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@next-auth/prisma-legacy-adapter@0.1.2": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@next-auth/prisma-legacy-adapter/-/prisma-legacy-adapter-0.1.2.tgz#d19b65696bf16bda5dbc88f85da98aee6f30e2f3" - integrity sha512-QpGcRvrnFERkvVFroqS89qDbnskw9AjhvWxO095u3xl/8QVI++Y+doluQdZWuV6PewjrV7xY+uVUEnRhnGM8yQ== +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: - "@babel/runtime" "^7.14.0" + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" -"@next-auth/typeorm-legacy-adapter@0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@next-auth/typeorm-legacy-adapter/-/typeorm-legacy-adapter-0.1.4.tgz#a250498613669b736d76b36a2a46f7eafc9e6ed5" - integrity sha512-UDnctrPiUU0yaPNeFhD6uw0FAuWGvx6IUVwUYTEzZm26RCEe/BBjkpGLYk43M3rcnkBPfkiljomAs/O7Uzh12w== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== dependencies: - "@babel/runtime" "^7.14.0" - require_optional "^1.0.1" - typeorm "^0.2.30" + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" - integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== +"@next/env@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.4.tgz#ed91156ee48791ad85992d95d8e1f46a740c0d5e" + integrity sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A== -"@next/eslint-plugin-next@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.1.0.tgz#32586a11378b3ffa5a93ac40a3c44ad99d70e95a" - integrity sha512-WFiyvSM2G5cQmh32t/SiQuJ+I2O+FHVlK/RFw5b1565O2kEM/36EXncjt88Pa+X5oSc+1SS+tWxowWJd1lqI+g== +"@next/eslint-plugin-next@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.4.tgz#7dd5025d40b9564fc2cc1ea4aa9661a9552ec875" + integrity sha512-a/T30+7Q1scom5t3L+wEBkYzCa+bhT/3DTxzxlNy4Xckw2InzcckQGeIi/larDgh5r2fSSJswhYAZEcKtuJiig== dependencies: glob "7.1.7" -"@next/swc-android-arm64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39" - integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA== - -"@next/swc-darwin-arm64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135" - integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg== - -"@next/swc-darwin-x64@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd" - integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug== - -"@next/swc-linux-arm-gnueabihf@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7" - integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog== - -"@next/swc-linux-arm64-gnu@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093" - integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q== - -"@next/swc-linux-arm64-musl@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566" - integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA== - -"@next/swc-linux-x64-gnu@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e" - integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A== - -"@next/swc-linux-x64-musl@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31" - integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw== - -"@next/swc-win32-arm64-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283" - integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw== - -"@next/swc-win32-ia32-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1" - integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q== - -"@next/swc-win32-x64-msvc@12.1.0": - version "12.1.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064" - integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg== +"@next/swc-android-arm-eabi@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz#7d258554b0812f693e91d1bfc940d0e386b394f7" + integrity sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA== + +"@next/swc-android-arm64@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz#03572661993ba554c4ac6a2b99c35b5062727489" + integrity sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA== + +"@next/swc-darwin-arm64@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz#10189b4753b43941ffb0f396ee878176c8b4b662" + integrity sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg== + +"@next/swc-darwin-x64@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz#e6574b43699bfe74e38dd620fdaecd7a1676aa0a" + integrity sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA== + +"@next/swc-freebsd-x64@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz#96e77d986bd22b08ce12d3a5fc884494328b94b2" + integrity sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw== + +"@next/swc-linux-arm-gnueabihf@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz#91b035a6c88e8d583f8b9aef465b64d05ecd1f95" + integrity sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw== + +"@next/swc-linux-arm64-gnu@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz#15726af0ce29450ffde593e993b0ec1beba75154" + integrity sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw== + +"@next/swc-linux-arm64-musl@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz#74f449a2bb7b6f6fdaa67df0fa3665ee99a826b8" + integrity sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA== + +"@next/swc-linux-x64-gnu@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz#dfaed86d84f11ce6c14c267ead267eab7cbea66d" + integrity sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA== + +"@next/swc-linux-x64-musl@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz#6f5f184967ba00379ac179546a9575bb7aff9c21" + integrity sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q== + +"@next/swc-win32-arm64-msvc@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz#ccfa61a8ea56b55d7251f32b6c518ed0962d2923" + integrity sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ== + +"@next/swc-win32-ia32-msvc@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz#8d164ce4de9eeaf3400ad4becdd653f68f0d9527" + integrity sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw== + +"@next/swc-win32-x64-msvc@13.1.4": + version "13.1.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz#0eab48201b90e862c76add99c4feff00bfb0fb67" + integrity sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1482,7 +1706,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3": +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1490,71 +1714,162 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@panva/asn1.js@^1.0.0": +"@panva/hkdf@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.2.tgz#bab0f09d09de9fd83628220d496627681bc440d6" + integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA== + +"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" + integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== + dependencies: + asn1js "^3.0.5" + pvtsutils "^1.3.2" + tslib "^2.4.0" + +"@peculiar/json-schema@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== + dependencies: + tslib "^2.0.0" + +"@peculiar/webcrypto@^1.4.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" + integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== + dependencies: + "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/json-schema" "^1.1.12" + pvtsutils "^1.3.2" + tslib "^2.4.1" + webcrypto-core "^1.7.4" + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@rc-component/context@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.3.0.tgz#608ccf0abcbec9406751b17a4b35db08e481c110" + integrity sha512-6QdaCJ7Wn5UZLJs15IEfqy4Ru3OaL5ctqpQYWd5rlfV9wwzrzdt6+kgAQZV/qdB0MUPN4nhyBfRembQCIvBf+w== + dependencies: + "@babel/runtime" "^7.10.1" + rc-util "^5.27.0" + +"@rc-component/mini-decimal@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.0.1.tgz#e5dbc20a6a5b0e234d279bc71ce730ab865d3910" + integrity sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA== + dependencies: + "@babel/runtime" "^7.18.0" + +"@rc-component/mutate-observer@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" - integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.0.0.tgz#ce99af3239ed9c74ee3e7302f1c67098de920b46" + integrity sha512-okqRJSfNisXdI6CUeOLZC5ukBW/8kir2Ii4PJiKpUt+3+uS7dxwJUMxsUZquxA1rQuL8YcEmKVp/TCnR+yUdZA== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" -"@rushstack/eslint-patch@^1.0.8": +"@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.0.tgz#7f698254aadf921e48dda8c0a6b304026b8a9323" - integrity sha512-JLo+Y592QzIE+q7Dl2pMUtt4q8SKYI5jDrZxrozEQxnGVOyYE+GWK9eLkwTaeN9DDctlaRAQ3TBmzZ1qdLE30A== + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.0.tgz#6b94450d2c2b00d50b141bd7a0be23bd96503dbe" + integrity sha512-tbXM9SB1r5FOuZjRCljERFByFiEUcMmCWMXLog/NmgCzlAzreXyf23Vei3ZpSMxSMavzPnhCovfZjZdmxS3d1w== + dependencies: + "@babel/runtime" "^7.18.0" + classnames "^2.3.2" + rc-util "^5.24.4" -"@samverschueren/stream-to-observable@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" - integrity sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ== +"@rc-component/tour@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.1.0.tgz#17b43b5c0bdfb6a8ab3027b1977d960952616d9e" + integrity sha512-Cy45VnNEDq6DLF5eKonIflObDfofbPq7AJpSf18qLN+j9+wW+sNlRv3JnCMDUsCdhSlnM4+yJ1RMokKp9GCpOQ== dependencies: - any-observable "^0.3.0" + "@babel/runtime" "^7.18.0" + "@rc-component/portal" "^1.0.0-9" + classnames "^2.3.2" + rc-trigger "^5.3.4" + rc-util "^5.24.4" + +"@repeaterjs/repeater@3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" + integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== + +"@rushstack/eslint-patch@^1.1.3": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" + integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== "@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + version "1.8.6" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" + integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^8.0.1": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7" - integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg== +"@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== dependencies: "@sinonjs/commons" "^1.7.0" -"@sqltools/formatter@^1.2.2": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20" - integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== +"@swc/helpers@0.4.14": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" + integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== dependencies: - defer-to-connect "^1.0.1" - -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + tslib "^2.4.0" "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.1.18" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" - integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== + version "7.20.0" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" + integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" "@types/babel__generator" "*" "@types/babel__template" "*" "@types/babel__traverse" "*" @@ -1574,10 +1889,10 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": - version "7.14.2" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" - integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" + integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== dependencies: "@babel/types" "^7.3.0" @@ -1586,10 +1901,10 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== -"@types/graceful-fs@^4.1.2": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== +"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" + integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== dependencies: "@types/node" "*" @@ -1626,9 +1941,9 @@ integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== "@types/json-schema@^7.0.6": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/json-stable-stringify@^1.0.32": version "1.0.34" @@ -1638,24 +1953,19 @@ "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/jsonwebtoken@^8.5.0": - version "8.5.8" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44" - integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A== +"@types/jsonwebtoken@^9.0.0": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4" + integrity sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw== dependencies: "@types/node" "*" -"@types/node@*": - version "17.0.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" - integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== - -"@types/node@^16.11.6": - version "16.11.26" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.26.tgz#63d204d136c9916fb4dcd1b50f9740fe86884e47" - integrity sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ== +"@types/node@*", "@types/node@^18.11.18": + version "18.11.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" + integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1663,14 +1973,14 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prettier@^2.1.5": - version "2.4.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" - integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== + version "2.7.2" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" + integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== "@types/prop-types@*": - version "15.7.4" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" - integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== "@types/qrcode.react@^1.0": version "1.0.2" @@ -1679,10 +1989,10 @@ dependencies: "@types/react" "*" -"@types/react-dom@^17.0": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809" - integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ== +"@types/react-dom@^18.0": + version "18.0.10" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" + integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== dependencies: "@types/react" "*" @@ -1693,17 +2003,17 @@ dependencies: "@types/react" "*" -"@types/react-test-renderer@^17.0": - version "17.0.1" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-17.0.1.tgz#3120f7d1c157fba9df0118dae20cb0297ee0e06b" - integrity sha512-3Fi2O6Zzq/f3QR9dRnlnHso9bMl7weKCviFmfF6B4LS1Uat6Hkm15k0ZAQuDz+UBq6B3+g+NM6IT2nr5QgPzCw== +"@types/react-test-renderer@^18.0": + version "18.0.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243" + integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^17.0": - version "17.0.39" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.39.tgz#d0f4cde092502a6db00a1cded6e6bf2abb7633ce" - integrity sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug== +"@types/react@*", "@types/react@^18.0": + version "18.0.27" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71" + integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -1719,17 +2029,10 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== -"@types/websocket@^1.0.4": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c" - integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ== - dependencies: - "@types/node" "*" - "@types/ws@^8.0.0": - version "8.5.2" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.2.tgz#77e0c2e360e9579da930ffcfa53c5975ea3bdd26" - integrity sha512-VXI82ykONr5tacHEojnErTQk+KQSoYbW1NB6iz6wUwrNd+BqfkfggQNoNdCqhJSzbNumShPERbM+Pc5zpfhlbw== + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== dependencies: "@types/node" "*" @@ -1739,9 +2042,16 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^16.0.0": - version "16.0.4" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" - integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + version "16.0.5" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" + integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.20" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.20.tgz#107f0fcc13bd4a524e352b41c49fe88aab5c54d5" + integrity sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A== dependencies: "@types/yargs-parser" "*" @@ -1750,49 +2060,64 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== -"@typescript-eslint/parser@^5.0.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c" - integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg== - dependencies: - "@typescript-eslint/scope-manager" "5.13.0" - "@typescript-eslint/types" "5.13.0" - "@typescript-eslint/typescript-estree" "5.13.0" - debug "^4.3.2" - -"@typescript-eslint/scope-manager@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6" - integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA== - dependencies: - "@typescript-eslint/types" "5.13.0" - "@typescript-eslint/visitor-keys" "5.13.0" - -"@typescript-eslint/types@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5" - integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg== - -"@typescript-eslint/typescript-estree@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141" - integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA== - dependencies: - "@typescript-eslint/types" "5.13.0" - "@typescript-eslint/visitor-keys" "5.13.0" - debug "^4.3.2" - globby "^11.0.4" +"@typescript-eslint/parser@^5.42.0": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.2.tgz#c9edef2a0922d26a37dba03be20c5fff378313b3" + integrity sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw== + dependencies: + "@typescript-eslint/scope-manager" "5.48.2" + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/typescript-estree" "5.48.2" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz#bb7676cb78f1e94921eaab637a4b5d596f838abc" + integrity sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw== + dependencies: + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/visitor-keys" "5.48.2" + +"@typescript-eslint/types@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.2.tgz#635706abb1ec164137f92148f06f794438c97b8e" + integrity sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA== + +"@typescript-eslint/typescript-estree@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz#6e206b462942b32383582a6c9251c05021cc21b0" + integrity sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg== + dependencies: + "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/visitor-keys" "5.48.2" + debug "^4.3.4" + globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd" - integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g== +"@typescript-eslint/visitor-keys@5.48.2": + version "5.48.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz#c247582a0bcce467461d7b696513bf9455000060" + integrity sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ== dependencies: - "@typescript-eslint/types" "5.13.0" - eslint-visitor-keys "^3.0.0" + "@typescript-eslint/types" "5.48.2" + eslint-visitor-keys "^3.3.0" + +"@whatwg-node/fetch@0.6.2", "@whatwg-node/fetch@^0.6.0": + version "0.6.2" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.6.2.tgz#fe4837505f6fc91bcfd6e12cdcec66f4aecfeecc" + integrity sha512-fCUycF1W+bI6XzwJFnbdDuxIldfKM3w8+AzVCLGlucm0D+AQ8ZMm2j84hdcIhfV6ZdE4Y1HFVrHosAxdDZ+nPw== + dependencies: + "@peculiar/webcrypto" "^1.4.0" + abort-controller "^3.0.0" + busboy "^1.6.0" + form-data-encoder "^1.7.1" + formdata-node "^4.3.1" + node-fetch "^2.6.7" + undici "^5.12.0" + urlpattern-polyfill "^6.0.2" + web-streams-polyfill "^3.2.0" "@wry/context@^0.6.0": version "0.6.1" @@ -1801,25 +2126,27 @@ dependencies: tslib "^2.3.0" +"@wry/context@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.0.tgz#be88e22c0ddf62aeb0ae9f95c3d90932c619a5c8" + integrity sha512-LcDAiYWRtwAoSOArfk7cuYvFXytxfVrdX7yxoUmK7pPITLk5jYh2F8knCwS7LjgYL8u1eidPlKKV6Ikqq0ODqQ== + dependencies: + tslib "^2.3.0" + "@wry/equality@^0.5.0": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.2.tgz#72c8a7a7d884dff30b612f4f8464eba26c080e73" - integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA== + version "0.5.3" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.3.tgz#fafebc69561aa2d40340da89fa7dc4b1f6fb7831" + integrity sha512-avR+UXdSrsF2v8vIqIgmeTY0UR91UT+IyablCyKe/uk22uOJ8fusKZnH9JH9e1/EtLeNJBtagNmL3eJdnOV53g== dependencies: tslib "^2.3.0" "@wry/trie@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139" - integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw== + version "0.3.2" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.2.tgz#a06f235dc184bd26396ba456711f69f8c35097e6" + integrity sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ== dependencies: tslib "^2.3.0" -abab@^2.0.3, abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -1827,33 +2154,20 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.3.1: +acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.1.1, acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.2.4: - version "8.7.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" - integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +acorn@^8.4.1, acorn@^8.8.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== agent-base@6: version "6.0.2" @@ -1862,6 +2176,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" @@ -1877,53 +2199,18 @@ ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.10.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" - integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1943,77 +2230,67 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -antd@^4.0: - version "4.18.9" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.18.9.tgz#a0d246786d5076ea7a53b67191cd39295fc2322a" - integrity sha512-MbtFY2J8LvXUnxYH2QehdhP9qMEpHvOp7PmiTIHc7v6aSb+LILCibskRIMGNEKvvhBvsTdq0cnjanR9/IbqEAw== +antd@^5.0: + version "5.1.6" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.1.6.tgz#9ac912279f9f8e571674b3220b668897b4ffb0b3" + integrity sha512-9bn2B4rZ1c7IXtn5U95aNGJXmLOZyL1V5TiGq3pk3S3bGD13BA2eynSI//e3c2FAslZlqQlQEGV+NHms9ygAVA== dependencies: - "@ant-design/colors" "^6.0.0" - "@ant-design/icons" "^4.7.0" - "@ant-design/react-slick" "~0.28.1" - "@babel/runtime" "^7.12.5" + "@ant-design/colors" "^7.0.0" + "@ant-design/cssinjs" "^1.5.6" + "@ant-design/icons" "^5.0.0" + "@ant-design/react-slick" "~1.0.0" + "@babel/runtime" "^7.18.3" "@ctrl/tinycolor" "^3.4.0" + "@rc-component/mutate-observer" "^1.0.0" + "@rc-component/tour" "~1.1.0" classnames "^2.2.6" copy-to-clipboard "^3.2.0" - lodash "^4.17.21" - memoize-one "^6.0.0" - moment "^2.25.3" - rc-cascader "~3.2.1" + dayjs "^1.11.1" + qrcode.react "^3.1.0" + rc-cascader "~3.8.0" rc-checkbox "~2.3.0" - rc-collapse "~3.1.0" - rc-dialog "~8.6.0" - rc-drawer "~4.4.2" - rc-dropdown "~3.2.5" - rc-field-form "~1.23.0" - rc-image "~5.2.5" - rc-input-number "~7.3.0" - rc-mentions "~1.6.1" - rc-menu "~9.2.1" - rc-motion "^2.4.4" - rc-notification "~4.5.7" - rc-pagination "~3.1.9" - rc-picker "~2.5.17" - rc-progress "~3.2.1" + rc-collapse "~3.4.2" + rc-dialog "~9.0.2" + rc-drawer "~6.1.1" + rc-dropdown "~4.0.0" + rc-field-form "~1.27.0" + rc-image "~5.13.0" + rc-input "~0.1.4" + rc-input-number "~7.4.0" + rc-mentions "~1.13.1" + rc-menu "~9.8.2" + rc-motion "^2.6.1" + rc-notification "~5.0.0" + rc-pagination "~3.2.0" + rc-picker "~3.1.1" + rc-progress "~3.4.1" rc-rate "~2.9.0" rc-resize-observer "^1.2.0" - rc-select "~14.0.0-alpha.15" - rc-slider "~9.7.4" - rc-steps "~4.1.0" - rc-switch "~3.2.0" - rc-table "~7.23.0" - rc-tabs "~11.10.0" - rc-textarea "~0.3.0" - rc-tooltip "~5.1.1" - rc-tree "~5.4.3" - rc-tree-select "~5.1.1" - rc-trigger "^5.2.10" + rc-segmented "~2.1.0" + rc-select "~14.2.0" + rc-slider "~10.0.0" + rc-steps "~6.0.0" + rc-switch "~4.0.0" + rc-table "~7.30.2" + rc-tabs "~12.5.1" + rc-textarea "~0.4.5" + rc-tooltip "~5.2.0" + rc-tree "~5.7.0" + rc-tree-select "~5.6.0" + rc-trigger "^5.3.4" rc-upload "~4.3.0" - rc-util "^5.14.0" - scroll-into-view-if-needed "^2.2.25" - -any-observable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" - integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== - -any-promise@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= + rc-util "^5.27.0" + scroll-into-view-if-needed "^3.0.3" + throttle-debounce "^5.0.0" anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" -app-root-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" - integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== - arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2031,23 +2308,22 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== +aria-query@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" + deep-equal "^2.0.5" -array-includes@^3.1.3, array-includes@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" - integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" is-string "^1.0.7" array-tree-filter@^2.1.0: @@ -2060,63 +2336,92 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" - integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446" - integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA== +array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.19.0" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asn1js@^3.0.1, asn1js@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" + integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== + dependencies: + pvtsutils "^1.3.2" + pvutils "^1.1.3" + tslib "^2.4.0" ast-types-flow@^0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-validator@^4.0.2: - version "4.0.7" - resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe" - integrity sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ== +async-validator@^4.1.0: + version "4.2.5" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" + integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -axe-core@^4.3.5: - version "4.4.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.1.tgz#7dbdc25989298f9ad006645cd396782443757413" - integrity sha512-gd1kmb21kwNuWr6BQz8fv6GNECPBnUasepcoLbekws23NVBLODdsClRZ+bQ8+9Uomf3Sm3+Vwn0oYG9NvwnJCw== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== +axe-core@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.2.tgz#6e566ab2a3d29e415f5115bc0fd2597a5eb3e5e3" + integrity sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg== + +axobject-query@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + dependencies: + deep-equal "^2.0.5" babel-jest@27.1.0: version "27.1.0" @@ -2132,27 +2437,19 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^27.0, babel-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== +babel-jest@^29.0, babel-jest@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.3.1.tgz#05c83e0d128cd48c453eea851482a38782249f44" + integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA== dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/transform" "^29.3.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" + babel-preset-jest "^29.2.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" @@ -2174,6 +2471,16 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094" + integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" @@ -2230,7 +2537,7 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.0.6, babel-preset-jest@^27.5.1: +babel-preset-jest@^27.0.6: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== @@ -2238,10 +2545,13 @@ babel-preset-jest@^27.0.6, babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -backo2@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= +babel-preset-jest@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc" + integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA== + dependencies: + babel-plugin-jest-hoist "^29.2.0" + babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: version "1.0.2" @@ -2280,28 +2590,22 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.1, braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserslist@^4.17.5: - version "4.20.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.0.tgz#35951e3541078c125d36df76056e94738a52ebe9" - integrity sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ== +browserslist@^4.21.3: + version "4.21.4" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" + integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== dependencies: - caniuse-lite "^1.0.30001313" - electron-to-chromium "^1.4.76" - escalade "^3.1.1" - node-releases "^2.0.2" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001400" + electron-to-chromium "^1.4.251" + node-releases "^2.0.6" + update-browserslist-db "^1.0.9" bser@2.1.1: version "2.1.1" @@ -2313,14 +2617,14 @@ bser@2.1.1: buffer-equal-constant-time@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0, buffer@^5.7.0: +buffer@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -2328,26 +2632,12 @@ buffer@^5.5.0, buffer@^5.7.0: base64-js "^1.3.1" ieee754 "^1.1.13" -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" + streamsearch "^1.1.0" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" @@ -2360,7 +2650,7 @@ call-bind@^1.0.0, call-bind@^1.0.2: caller@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/caller/-/caller-1.0.1.tgz#b851860f70e195db3d277395aa1a7e23ea30ecf5" - integrity sha1-uFGGD3Dhlds9J3OVqhp+I+ow7PU= + integrity sha512-tfj+ErW/0SbOyfoXriRtdjCMTwrZAvLXj2jHqlh8YCcgoZVzUI22E/JJLxbiuZqDs0Ke7BuRrHTVuxm3EwYbJQ== callsites@^3.0.0: version "3.1.0" @@ -2385,10 +2675,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001313: - version "1.0.30001313" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz#a380b079db91621e1b7120895874e2fd62ed2e2f" - integrity sha512-rI1UN0koZUiKINjysQDuRi2VeSCce3bYJNmDcj3PIKREiAmjakugBul1QSkg/fPrlULYl6oWfGg3PbgOSY9X4Q== +caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406: + version "1.0.30001447" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001447.tgz#ef1f39ae38d839d7176713735a8e467a0a2523bd" + integrity sha512-bdKU1BQDPeEXe9A39xJnGtY0uRq/z5osrnXUw0TcK+EYno45Y+U7QU9HhHEyzvMDffpYadFXi3idnSNkcwLkTw== capital-case@^1.0.4: version "1.0.4" @@ -2399,18 +2689,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.0.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2443,6 +2722,22 @@ change-case-all@1.0.14: upper-case "^2.0.2" upper-case-first "^2.0.2" +change-case-all@1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.15.tgz#de29393167fc101d646cd76b0ef23e27d09756ad" + integrity sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ== + dependencies: + change-case "^4.1.2" + is-lower-case "^2.0.2" + is-upper-case "^2.0.2" + lower-case "^2.0.2" + lower-case-first "^2.0.2" + sponge-case "^1.0.1" + swap-case "^2.0.2" + title-case "^3.0.3" + upper-case "^2.0.2" + upper-case-first "^2.0.2" + change-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" @@ -2487,26 +2782,24 @@ chokidar@^3.5.2: fsevents "~2.3.2" ci-info@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" - integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + version "3.7.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" + integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" - integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== +classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== -cli-cursor@^2.0.0, cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== cli-cursor@^3.1.0: version "3.1.0" @@ -2515,36 +2808,29 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-highlight@^2.1.11: - version "2.1.11" - resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf" - integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg== - dependencies: - chalk "^4.0.0" - highlight.js "^10.7.1" - mz "^2.4.0" - parse5 "^5.1.1" - parse5-htmlparser2-tree-adapter "^6.0.0" - yargs "^16.0.0" - cli-spinners@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" - integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + version "2.7.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" + integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== -cli-truncate@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" - integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ= +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== dependencies: - slice-ansi "0.0.4" - string-width "^1.0.1" + slice-ansi "^3.0.0" + string-width "^4.2.0" cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +client-only@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -2554,36 +2840,24 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - clone@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: version "1.0.1" @@ -2607,13 +2881,18 @@ color-convert@^2.0.1: color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.16: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -2626,20 +2905,20 @@ common-tags@1.8.0: resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== -common-tags@1.8.2, common-tags@^1.8.0: +common-tags@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== -compute-scroll-into-view@^1.0.17: - version "1.0.17" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab" - integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg== +compute-scroll-into-view@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz#2b444b2b9e4724819d2531efacb7ac094155fdf6" + integrity sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== constant-case@^3.0.4: version "3.0.4" @@ -2651,40 +2930,51 @@ constant-case@^3.0.4: upper-case "^2.0.2" convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + copy-to-clipboard@^3.2.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" - integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw== + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: toggle-selection "^1.0.6" -core-js-pure@^3.20.2: - version "3.21.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" - integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== +cosmiconfig-typescript-loader@4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" + integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== -cosmiconfig-toml-loader@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz#0681383651cceff918177debe9084c0d3769509b" - integrity sha512-H/2gurFWVi7xXvCyvsWRLCMekl4tITJcX0QEsDMpzxtuxDyM59xLatYNg4s/k9AA/HdtCYfj2su8mgA0GSDLDA== +cosmiconfig@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" + integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== dependencies: - "@iarna/toml" "^2.2.5" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" -cosmiconfig@7.0.1, cosmiconfig@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" - integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" + integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== dependencies: "@types/parse-json" "^4.0.0" import-fresh "^3.2.1" @@ -2697,7 +2987,7 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@^3.0.6, cross-fetch@^3.1.5: +cross-fetch@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== @@ -2713,93 +3003,38 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cross-undici-fetch@^0.1.19: - version "0.1.25" - resolved "https://registry.yarnpkg.com/cross-undici-fetch/-/cross-undici-fetch-0.1.25.tgz#8c6826dd0ffbb45fcb1a554be5984e0eaef7f3ba" - integrity sha512-KS6hm/VuRO+3jIrg4uidz3mQ8NWvCbiTTOg3yoH30zuGVUvjqZlnXw66h0kuzyfP21hDkrdIbufXCW6BAQdSNw== - dependencies: - abort-controller "^3.0.0" - form-data-encoder "^1.7.1" - formdata-node "^4.3.1" - node-fetch "^2.6.7" - undici "^4.9.3" - web-streams-polyfill "^3.2.0" - -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csstype@^3.0.2: - version "3.0.11" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" - integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== +csstype@^3.0.10, csstype@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" + integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== -damerau-levenshtein@^1.0.7: +damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== - dependencies: - abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" - -dataloader@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.0.0.tgz#41eaf123db115987e21ca93c005cd7753c55fe6f" - integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ== - -date-fns@2.x: - version "2.28.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" - integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== - -date-fns@^1.27.2: - version "1.30.1" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" - integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== +dataloader@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7" + integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== -dayjs@1.x, dayjs@^1.0: - version "1.10.8" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.8.tgz#267df4bc6276fcb33c04a6735287e3f429abec41" - integrity sha512-wbNwDfBHHur9UOzNUjeKUOJ0fCb0a52Wx0xInmQ7Y8FstyajiV1NmK1e00cxsr9YrE9r7yAChE0VvpuY5Rnlow== +dayjs@^1.0, dayjs@^1.11.1: + version "1.11.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -2810,31 +3045,37 @@ debug@^3.2.7: decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decimal.js@^10.2.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== +deep-equal@^2.0.5: + version "2.2.0" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" + integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== + dependencies: + call-bind "^1.0.2" + es-get-iterator "^1.1.2" + get-intrinsic "^1.1.3" + is-arguments "^1.1.1" + is-array-buffer "^3.0.1" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -2845,28 +3086,29 @@ deepmerge@^4.0, deepmerge@^4.2.2: integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== dependencies: clone "^1.0.2" -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== +define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== dependencies: - object-keys "^1.0.12" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== dependency-graph@^0.11.0: version "0.11.0" @@ -2883,10 +3125,10 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== -diff-sequences@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== +diff-sequences@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" + integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== diff@^4.0.1: version "4.0.2" @@ -2915,16 +3157,9 @@ doctrine@^3.0.0: esutils "^2.0.2" dom-align@^1.7.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.2.tgz#0f8164ebd0c9c21b0c790310493cd855892acd4b" - integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg== - -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" + version "1.12.4" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" + integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== dot-case@^3.0.4: version "3.0.4" @@ -2935,24 +3170,14 @@ dot-case@^3.0.4: tslib "^2.0.3" dotenv@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" - integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== - -dotenv@^8.2.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" - integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== - -dset@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.1.tgz#07de5af7a8d03eab337ad1a8ba77fe17bba61a8c" - integrity sha512-hYf+jZNNqJBD2GiMYb+5mqOIX4R4RRHXU3qWMWYN+rqcR2/YpRL2bUHr8C8fU+5DNvqYjJ8YvMGSLuVPWU1cNg== + version "16.0.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" + integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= +dset@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" + integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== ecdsa-sig-formatter@1.0.11: version "1.0.11" @@ -2961,20 +3186,15 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -electron-to-chromium@^1.4.76: - version "1.4.76" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.76.tgz#a0494baedaf51094b1c172999919becd9975a934" - integrity sha512-3Vftv7cenJtQb+k00McEBZ2vVmZ/x+HEF7pcZONZIkOsESqAqVuACmBxMv0JhzX7u0YltU0vSqRqgBSTAhFUjA== - -elegant-spinner@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" - integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= +electron-to-chromium@^1.4.251: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== +emittery@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^8.0.0: version "8.0.0" @@ -2991,19 +3211,13 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enquirer@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" - integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== +enhanced-resolve@^5.10.0: + version "5.12.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" + integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== dependencies: - ansi-colors "^4.1.1" + graceful-fs "^4.2.4" + tapable "^2.2.0" error-ex@^1.3.1: version "1.3.2" @@ -3012,31 +3226,75 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.1: - version "1.19.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" - integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w== +es-abstract@^1.19.0, es-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== dependencies: + available-typed-arrays "^1.0.5" call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" function-bind "^1.1.1" - get-intrinsic "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" has "^1.0.3" - has-symbols "^1.0.2" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.1" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.1" + is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-weakref "^1.0.1" - object-inspect "^1.11.0" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.2" object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-get-iterator@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" @@ -3052,10 +3310,10 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" @@ -3067,208 +3325,199 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== +eslint-config-next@^13.1.4: + version "13.1.4" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.4.tgz#f77d3f62e1b62564bd5c88c27bb5a00eb786c565" + integrity sha512-r7n9V4/kkiDDVFfBwI3tviGUV/jUzGI0lY3JefxceYaU18gdk2kMgNPyhHobowu1+yHZpZi8iEzRtzeTrtGRLg== dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-next@^12.0.2: - version "12.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.1.0.tgz#8ace680dc5207e6ab6c915f3989adec122f582e7" - integrity sha512-tBhuUgoDITcdcM7xFvensi9I5WTI4dnvH4ETGRg1U8ZKpXrZsWQFdOKIDzR3RLP5HR3xXrLviaMM4c3zVoE/pA== - dependencies: - "@next/eslint-plugin-next" "12.1.0" - "@rushstack/eslint-patch" "^1.0.8" - "@typescript-eslint/parser" "^5.0.0" - eslint-import-resolver-node "^0.3.4" - eslint-import-resolver-typescript "^2.4.0" - eslint-plugin-import "^2.25.2" + "@next/eslint-plugin-next" "13.1.4" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.42.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + eslint-plugin-import "^2.26.0" eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.27.0" - eslint-plugin-react-hooks "^4.3.0" + eslint-plugin-react "^7.31.7" + eslint-plugin-react-hooks "^4.5.0" -eslint-import-resolver-node@^0.3.4, eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== +eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== dependencies: debug "^3.2.7" - resolve "^1.20.0" + is-core-module "^2.11.0" + resolve "^1.22.1" -eslint-import-resolver-typescript@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.5.0.tgz#07661966b272d14ba97f597b51e1a588f9722f0a" - integrity sha512-qZ6e5CFr+I7K4VVhQu3M/9xGv9/YmwsEXrsm3nimw8vWaVHRDrQRp26BgCypTxBp3vUp4o5aVEJRiy0F2DFddQ== - dependencies: - debug "^4.3.1" - glob "^7.1.7" - is-glob "^4.0.1" - resolve "^1.20.0" - tsconfig-paths "^3.9.0" +eslint-import-resolver-typescript@^3.5.2: + version "3.5.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" + integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" + is-glob "^4.0.3" + synckit "^0.8.4" -eslint-module-utils@^2.7.2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== +eslint-module-utils@^2.7.4: + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" -eslint-plugin-import@^2.25.2: - version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" - integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== +eslint-plugin-import@^2.26.0: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.2" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" has "^1.0.3" - is-core-module "^2.8.0" + is-core-module "^2.11.0" is-glob "^4.0.3" - minimatch "^3.0.4" - object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.12.0" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" eslint-plugin-jsx-a11y@^6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" - integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== - dependencies: - "@babel/runtime" "^7.16.3" - aria-query "^4.2.2" - array-includes "^3.1.4" + version "6.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" + integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== + dependencies: + "@babel/runtime" "^7.20.7" + aria-query "^5.1.3" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" ast-types-flow "^0.0.7" - axe-core "^4.3.5" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.7" + axe-core "^4.6.2" + axobject-query "^3.1.1" + damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" has "^1.0.3" - jsx-ast-utils "^3.2.1" - language-tags "^1.0.5" - minimatch "^3.0.4" + jsx-ast-utils "^3.3.3" + language-tags "=1.0.5" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + semver "^6.3.0" -eslint-plugin-react-hooks@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" - integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== +eslint-plugin-react-hooks@^4.5.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.27.0: - version "7.29.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.29.3.tgz#f4eab757f2756d25d6d4c2a58a9e20b004791f05" - integrity sha512-MzW6TuCnDOcta67CkpDyRfRsEVx9FNMDV8wZsDqe1luHPdGTrQIUaUXD27Ja3gHsdOIs/cXzNchWGlqm+qRVRg== +eslint-plugin-react@^7.31.7: + version "7.32.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.1.tgz#88cdeb4065da8ca0b64e1274404f53a0f9890200" + integrity sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www== dependencies: - array-includes "^3.1.4" - array.prototype.flatmap "^1.2.5" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.0" - object.values "^1.1.5" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" prop-types "^15.8.1" - resolve "^2.0.0-next.3" + resolve "^2.0.0-next.4" semver "^6.3.0" - string.prototype.matchall "^4.0.6" + string.prototype.matchall "^4.0.8" -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" - estraverse "^4.1.1" + estraverse "^5.2.0" -eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: - eslint-visitor-keys "^1.1.0" - -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.0.0: +eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@<8.0.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== +eslint@^8.0.0: + version "8.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.32.0.tgz#d9690056bb6f1a302bd991e7090f5b68fbaea861" + integrity sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" + "@eslint/eslintrc" "^1.4.1" + "@humanwhocodes/config-array" "^0.11.8" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" - enquirer "^2.3.5" escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" - minimatch "^3.0.4" + minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" + regexpp "^3.2.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^6.0.9" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@^4.0.1: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -3287,11 +3536,6 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" @@ -3307,11 +3551,6 @@ event-target-shim@^5.0.0: resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -eventemitter3@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -3330,17 +3569,18 @@ execa@^5.0.0: exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== +expect@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6" + integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA== dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" + "@jest/expect-utils" "^29.3.1" + jest-get-type "^29.2.0" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" external-editor@^3.0.3: version "3.1.0" @@ -3366,10 +3606,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== +fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3377,27 +3617,27 @@ fast-glob@^3.1.1, fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" @@ -3419,21 +3659,6 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^0.7.30" -figures@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" - integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4= - dependencies: - escape-string-regexp "^1.0.5" - object-assign "^4.1.0" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" @@ -3455,19 +3680,20 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: - locate-path "^5.0.0" + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" path-exists "^4.0.0" flat-cache@^3.0.4: @@ -3479,14 +3705,21 @@ flat-cache@^3.0.4: rimraf "^3.0.2" flatted@^3.1.0: - version "3.2.5" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" - integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" form-data-encoder@^1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.1.tgz#ac80660e4f87ee0d3d3c3638b7da8278ddb8ec96" - integrity sha512-EFRDrsMm/kyqbTQocNvRXMLjc7Es2Vk+IQFx/YW7hkUH1eBl4J1fqiP34l74Yt0pFLCNpc06fkbVk00008mzjg== + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== form-data@^3.0.0: version "3.0.1" @@ -3498,17 +3731,17 @@ form-data@^3.0.0: mime-types "^2.1.12" formdata-node@^4.3.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.3.2.tgz#0262e94931e36db7239c2b08bdb6aaf18ec47d21" - integrity sha512-k7lYJyzDOSL6h917favP8j1L0/wNyylzU+x+1w4p5haGVHNlP58dbpdJhiCUsDbWsa9HwEtLp89obQgXl2e0qg== + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== dependencies: node-domexception "1.0.0" - web-streams-polyfill "4.0.0-beta.1" + web-streams-polyfill "4.0.0-beta.3" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2, fsevents@~2.3.2: version "2.3.2" @@ -3520,15 +3753,20 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" -futoin-hkdf@^1.3.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.5.0.tgz#f10cc4d32f1e26568ded58d5a6535a97aa3a064c" - integrity sha512-4CerDhtTgx4i5PKccQIpEp4T9wqmosPIP9Kep35SdCpYkQeriD3zddUVhrO1Fc4QvGhsAnd2rXyoOr5047mJEg== +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== gensync@1.0.0-beta.2, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" @@ -3540,34 +3778,20 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" + integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== dependencies: function-bind "^1.1.1" has "^1.0.3" - has-symbols "^1.0.1" + has-symbols "^1.0.3" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" @@ -3581,6 +3805,11 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.3.0.tgz#4c26fae115d1050e836aea65d6fe56b507ee249b" + integrity sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ== + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3588,6 +3817,13 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -3600,15 +3836,15 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" @@ -3617,13 +3853,25 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.6.0, globals@^13.9.0: - version "13.12.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb" - integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw== +globals@^13.19.0: + version "13.19.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + globby@11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" @@ -3636,7 +3884,7 @@ globby@11.0.4: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.0.3, globby@^11.0.4: +globby@11.1.0, globby@^11.0.3, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -3648,49 +3896,54 @@ globby@^11.0.3, globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" +globby@^13.1.2: + version "13.1.3" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" + integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -graphql-config@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.1.0.tgz#a3b28d3fb537952ebeb69c75e4430605a10695e3" - integrity sha512-Myqay6pmdcmX3KqoH+bMbeKZ1cTODpHS2CxF1ZzNnfTE+YUpGTcp01bOw6LpzamRb0T/WTYtGFbZeXGo9Hab2Q== - dependencies: - "@endemolshinegroup/cosmiconfig-typescript-loader" "3.0.2" - "@graphql-tools/graphql-file-loader" "^7.3.2" - "@graphql-tools/json-file-loader" "^7.3.2" - "@graphql-tools/load" "^7.4.1" - "@graphql-tools/merge" "^8.2.1" - "@graphql-tools/url-loader" "^7.4.2" - "@graphql-tools/utils" "^8.5.1" - cosmiconfig "7.0.1" - cosmiconfig-toml-loader "1.0.0" - minimatch "3.0.4" +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +graphql-config@4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.4.0.tgz#4b2d34d846bd4b9a40afbadfc5a4426668963c43" + integrity sha512-QUrX7R4htnTBTi83a0IlIilWVfiLEG8ANFlHRcxoZiTvOXTbgan67SUdGe1OlopbDuyNgtcy4ladl3Gvk4C36A== + dependencies: + "@graphql-tools/graphql-file-loader" "^7.3.7" + "@graphql-tools/json-file-loader" "^7.3.7" + "@graphql-tools/load" "^7.5.5" + "@graphql-tools/merge" "^8.2.6" + "@graphql-tools/url-loader" "^7.9.7" + "@graphql-tools/utils" "^9.0.0" + cosmiconfig "8.0.0" + minimatch "4.2.1" string-env-interpolation "1.0.1" - -graphql-executor@0.0.18: - version "0.0.18" - resolved "https://registry.yarnpkg.com/graphql-executor/-/graphql-executor-0.0.18.tgz#6aa4b39e1ca773e159c2a602621e90606df0109a" - integrity sha512-upUSl7tfZCZ5dWG1XkOvpG70Yk3duZKcCoi/uJso4WxJVT6KIrcK4nZ4+2X/hzx46pL8wAukgYHY6iNmocRN+g== + tslib "^2.4.0" graphql-let@^0.18: version "0.18.6" @@ -3717,20 +3970,16 @@ graphql-let@^0.18: "@graphql-codegen/typescript-resolvers" "1.19.1" babel-jest "27.1.0" -graphql-request@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-4.0.0.tgz#5e4361d33df1a95ccd7ad23a8ebb6bbca9d5622f" - integrity sha512-cdqQLCXlBGkaLdkLYRl4LtkwaZU6TfpE7/tnUQFl3wXfUPWN74Ov+Q61VuIh+AltS789YfGB6whghmCmeXLvTw== +graphql-request@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.1.0.tgz#dbc8feee27d21b993cd5da2d3af67821827b240a" + integrity sha512-0OeRVYigVwIiXhNmqnPDt+JhMzsjinxHE7TVy3Lm6jUzav0guVcL0lfSbi6jVTRAxcbwgyr6yrZioSHxf9gHzw== dependencies: - cross-fetch "^3.0.6" + "@graphql-typed-document-node/core" "^3.1.1" + cross-fetch "^3.1.5" extract-files "^9.0.0" form-data "^3.0.0" -graphql-sse@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/graphql-sse/-/graphql-sse-1.0.6.tgz#4f98e0a06f2020542ed054399116108491263224" - integrity sha512-y2mVBN2KwNrzxX2KBncQ6kzc6JWvecxuBernrl0j65hsr6MAS3+Yn8PTFSOgRmtolxugepxveyZVQEuaNEbw3w== - graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.3: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -3738,39 +3987,44 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.3: dependencies: tslib "^2.1.0" -graphql-ws@^5.4.1: - version "5.6.2" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.6.2.tgz#c7e5e382bd80d7fef637ea0b86ef4b1cb3d0b09b" - integrity sha512-TsjovINNEGfv52uKWYSVCOLX9LFe6wAhf9n7hIsV3zjflky1dv/mAP+kjXAXsnzV1jH5Sx0S73CtBFNvxus+SQ== +graphql-ws@5.11.2: + version "5.11.2" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.11.2.tgz#d5e0acae8b4d4a4cf7be410a24135cfcefd7ddc0" + integrity sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w== graphql@^16.0.1: - version "16.3.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05" - integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A== - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" + version "16.6.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" + integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== -has-bigints@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-symbols@^1.0.1, has-symbols@^1.0.2: +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -3802,11 +4056,6 @@ highlight-words-core@^1.2.0: resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa" integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== -highlight.js@^10.7.1: - version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" - integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== - hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -3814,32 +4063,11 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react- dependencies: react-is "^16.7.0" -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -3850,9 +4078,9 @@ http-proxy-agent@^5.0.0: debug "4" https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== dependencies: agent-base "6" debug "4" @@ -3862,32 +4090,27 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@0.4.24, iconv-lite@^0.4.24: +iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -ieee754@^1.1.13, ieee754@^1.2.1: +ieee754@^1.1.13: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - ignore@^5.1.4, ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" - integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks= + integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" @@ -3913,35 +4136,30 @@ import-local@^3.0.2: imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -indent-string@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" - integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - inquirer@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.0.tgz#f44f008dd344bbfc4b30031f45d984e034a3ac3a" - integrity sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ== + version "8.2.5" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" + integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -3953,17 +4171,18 @@ inquirer@^8.0.0: mute-stream "0.0.8" ora "^5.4.1" run-async "^2.4.0" - rxjs "^7.2.0" + rxjs "^7.5.5" string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" + wrap-ansi "^7.0.0" -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== +internal-slot@^1.0.3, internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== dependencies: - get-intrinsic "^1.1.0" + get-intrinsic "^1.1.3" has "^1.0.3" side-channel "^1.0.4" @@ -3982,10 +4201,27 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-bigint@^1.0.1: version "1.0.4" @@ -4009,41 +4245,34 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.2.0, is-core-module@^2.8.0, is-core-module@^2.8.1: - version "2.8.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" - integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== +is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -4074,15 +4303,20 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" -is-negative-zero@^2.0.1: +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== is-number-object@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0" - integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g== + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== dependencies: has-tostringtag "^1.0.0" @@ -4091,22 +4325,10 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-observable@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e" - integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA== - dependencies: - symbol-observable "^1.1.0" - -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-promise@^2.1.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" - integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== is-regex@^1.1.4: version "1.1.4" @@ -4123,15 +4345,17 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-shared-array-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" - integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" is-stream@^2.0.0: version "2.0.1" @@ -4152,10 +4376,21 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== is-unc-path@^1.0.0: version "1.0.0" @@ -4176,22 +4411,47 @@ is-upper-case@^2.0.2: dependencies: tslib "^2.0.3" -is-weakref@^1.0.1: +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + +is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isomorphic-fetch@^3.0.0: version "3.0.0" @@ -4201,10 +4461,10 @@ isomorphic-fetch@^3.0.0: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== +isomorphic-ws@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" @@ -4212,9 +4472,9 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" - integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== + version "5.2.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" + integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== dependencies: "@babel/core" "^7.12.3" "@babel/parser" "^7.14.7" @@ -4241,157 +4501,141 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.4" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" - integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterall@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== +javascript-stringify@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" + integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== +jest-changed-files@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289" + integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA== dependencies: - "@jest/types" "^27.5.1" execa "^5.0.0" - throat "^6.0.1" + p-limit "^3.1.0" -jest-circus@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== +jest-circus@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a" + integrity sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg== dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.3.1" + "@jest/expect" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" - expect "^27.5.1" is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + jest-each "^29.3.1" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-runtime "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" + p-limit "^3.1.0" + pretty-format "^29.3.1" slash "^3.0.0" stack-utils "^2.0.3" - throat "^6.0.1" -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-cli@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.3.1.tgz#e89dff427db3b1df50cea9a393ebd8640790416d" + integrity sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/core" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-config "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" prompts "^2.0.1" - yargs "^16.2.0" + yargs "^17.3.1" -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-config@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.3.1.tgz#0bc3dcb0959ff8662957f1259947aedaefb7f3c6" + integrity sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.3.1" + "@jest/types" "^29.3.1" + babel-jest "^29.3.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" - glob "^7.1.1" + glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-circus "^29.3.1" + jest-environment-node "^29.3.1" + jest-get-type "^29.2.0" + jest-regex-util "^29.2.0" + jest-resolve "^29.3.1" + jest-runner "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^27.5.1" + pretty-format "^29.3.1" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== +jest-diff@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527" + integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw== dependencies: chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + diff-sequences "^29.3.1" + jest-get-type "^29.2.0" + pretty-format "^29.3.1" -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== +jest-docblock@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82" + integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== dependencies: detect-newline "^3.0.0" -jest-each@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e" - integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ== +jest-each@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.3.1.tgz#bc375c8734f1bb96625d83d1ca03ef508379e132" + integrity sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546" - integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + jest-get-type "^29.2.0" + jest-util "^29.3.1" + pretty-format "^29.3.1" + +jest-environment-node@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74" + integrity sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag== + dependencies: + "@jest/environment" "^29.3.1" + "@jest/fake-timers" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" - -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "^29.3.1" + jest-util "^29.3.1" -jest-get-type@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== +jest-get-type@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" + integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== jest-haste-map@^27.5.1: version "27.5.1" @@ -4413,157 +4657,157 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== +jest-haste-map@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.3.1.tgz#af83b4347f1dae5ee8c2fb57368dc0bb3e5af843" + integrity sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A== dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^29.2.0" + jest-util "^29.3.1" + jest-worker "^29.3.1" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" -jest-leak-detector@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8" - integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ== +jest-leak-detector@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz#95336d020170671db0ee166b75cd8ef647265518" + integrity sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA== dependencies: - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-get-type "^29.2.0" + pretty-format "^29.3.1" -jest-matcher-utils@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" - integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== +jest-matcher-utils@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572" + integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ== dependencies: chalk "^4.0.0" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" + jest-diff "^29.3.1" + jest-get-type "^29.2.0" + pretty-format "^29.3.1" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.3.1.tgz#37bc5c468dfe5120712053dd03faf0f053bd6adb" + integrity sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "^29.3.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.3.1.tgz#60287d92e5010979d01f218c6b215b688e0f313e" + integrity sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" "@types/node" "*" + jest-util "^29.3.1" jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + version "1.2.3" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8" - integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg== +jest-regex-util@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" + integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== + +jest-resolve-dependencies@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz#a6a329708a128e68d67c49f38678a4a4a914c3bf" + integrity sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA== dependencies: - "@jest/types" "^27.5.1" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" + jest-regex-util "^29.2.0" + jest-snapshot "^29.3.1" -jest-resolve@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384" - integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== +jest-resolve@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.3.1.tgz#9a4b6b65387a3141e4a40815535c7f196f1a68a7" + integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw== dependencies: - "@jest/types" "^27.5.1" chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" + jest-haste-map "^29.3.1" jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" resolve "^1.20.0" resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5" - integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ== +jest-runner@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.3.1.tgz#a92a879a47dd096fea46bb1517b0a99418ee9e2d" + integrity sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA== dependencies: - "@jest/console" "^27.5.1" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.3.1" + "@jest/environment" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runtime@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af" - integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + jest-docblock "^29.2.0" + jest-environment-node "^29.3.1" + jest-haste-map "^29.3.1" + jest-leak-detector "^29.3.1" + jest-message-util "^29.3.1" + jest-resolve "^29.3.1" + jest-runtime "^29.3.1" + jest-util "^29.3.1" + jest-watcher "^29.3.1" + jest-worker "^29.3.1" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.3.1.tgz#21efccb1a66911d6d8591276a6182f520b86737a" + integrity sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A== + dependencies: + "@jest/environment" "^29.3.1" + "@jest/fake-timers" "^29.3.1" + "@jest/globals" "^29.3.1" + "@jest/source-map" "^29.2.0" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" + "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" - execa "^5.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" + jest-haste-map "^29.3.1" + jest-message-util "^29.3.1" + jest-mock "^29.3.1" + jest-regex-util "^29.2.0" + jest-resolve "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" slash "^3.0.0" strip-bom "^4.0.0" @@ -4575,33 +4819,35 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1" - integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA== +jest-snapshot@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.3.1.tgz#17bcef71a453adc059a18a32ccbd594b8cc4e45e" + integrity sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA== dependencies: - "@babel/core" "^7.7.2" + "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__traverse" "^7.0.4" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" + "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^27.5.1" + expect "^29.3.1" graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" + jest-diff "^29.3.1" + jest-get-type "^29.2.0" + jest-haste-map "^29.3.1" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" + pretty-format "^29.3.1" + semver "^7.3.5" jest-util@^27.5.1: version "27.5.1" @@ -4615,29 +4861,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== +jest-util@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" + integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "^29.3.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.3.1.tgz#d56fefaa2e7d1fde3ecdc973c7f7f8f25eea704a" + integrity sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g== + dependencies: + "@jest/types" "^29.3.1" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^27.5.1" + jest-get-type "^29.2.0" leven "^3.1.0" - pretty-format "^27.5.1" + pretty-format "^29.3.1" -jest-watcher@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2" - integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw== +jest-watcher@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.3.1.tgz#3341547e14fe3c0f79f9c3a4c62dbc3fc977fd4a" + integrity sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg== dependencies: - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" - jest-util "^27.5.1" + emittery "^0.13.1" + jest-util "^29.3.1" string-length "^4.0.1" jest-worker@^27.5.1: @@ -4649,21 +4908,35 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.0: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest-worker@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.3.1.tgz#e9462161017a9bb176380d721cab022661da3d6b" + integrity sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw== dependencies: - "@jest/core" "^27.5.1" - import-local "^3.0.2" - jest-cli "^27.5.1" + "@types/node" "*" + jest-util "^29.3.1" + merge-stream "^2.0.0" + supports-color "^8.0.0" -jose@^1.27.2: - version "1.28.1" - resolved "https://registry.yarnpkg.com/jose/-/jose-1.28.1.tgz#34a0f851a534be59ffab82a6e8845f6874e8c128" - integrity sha512-6JK28rFu5ENp/yxMwM+iN7YeaInnY9B9Bggjkz5fuwLiJhbVrl2O4SJr65bdNBPl9y27fdC3Mymh+FVCvozLIg== +jest@^29.0: + version "29.3.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.3.1.tgz#c130c0d551ae6b5459b8963747fed392ddbde122" + integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA== dependencies: - "@panva/asn1.js" "^1.0.0" + "@jest/core" "^29.3.1" + "@jest/types" "^29.3.1" + import-local "^3.0.2" + jest-cli "^29.3.1" + +jose@^4.10.0, jose@^4.9.3: + version "4.11.2" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.2.tgz#d9699307c02e18ff56825843ba90e2fae9f09e23" + integrity sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A== + +js-sdsl@^4.1.4: + version "4.3.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" + integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -4678,56 +4951,18 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0: +js-yaml@^4.0.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "6.0.1" - saxes "^5.0.1" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -4738,27 +4973,22 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0" + integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g== dependencies: - jsonify "~0.0.0" + jsonify "^0.0.1" json-to-pretty-yaml@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" - integrity sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs= + integrity sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A== dependencies: remedial "^1.0.7" remove-trailing-spaces "^1.0.6" @@ -4766,52 +4996,44 @@ json-to-pretty-yaml@^1.2.2: json2mq@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" - integrity sha1-tje9O6nqvhIsg+lyBIOusQ0skEo= + integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== dependencies: string-convert "^0.2.0" json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json5@^2.1.2, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonify@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" + integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== -jsonwebtoken@^8.5.1: - version "8.5.1" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" - integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== +jsonwebtoken@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" + integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== dependencies: jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" + lodash "^4.17.21" ms "^2.1.1" - semver "^5.6.0" + semver "^7.3.8" -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" - integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" + integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== dependencies: - array-includes "^3.1.3" - object.assign "^4.1.2" + array-includes "^3.1.5" + object.assign "^4.1.3" jwa@^1.4.1: version "1.4.1" @@ -4830,37 +5052,23 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== language-subtag-registry@~0.3.2: - version "0.3.21" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" - integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== -language-tags@^1.0.5: +language-tags@=1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= + integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== dependencies: language-subtag-registry "~0.3.2" -latest-version@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -4874,62 +5082,24 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -listr-silent-renderer@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" - integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4= - -listr-update-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2" - integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA== - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^2.3.0" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" - integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw== - dependencies: - chalk "^2.4.1" - cli-cursor "^2.1.0" - date-fns "^1.27.2" - figures "^2.0.0" - -listr@^0.14.3: - version "0.14.3" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" - integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== - dependencies: - "@samverschueren/stream-to-observable" "^0.3.0" - is-observable "^1.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.5.0" - listr-verbose-renderer "^0.5.0" - p-map "^2.0.0" - rxjs "^6.3.3" +listr2@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" + integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.5" + through "^2.3.8" + wrap-ansi "^7.0.0" loader-utils@2.0.0: version "2.0.0" @@ -4940,22 +5110,14 @@ loader-utils@2.0.0: emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== +loader-utils@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" - json5 "^1.0.1" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" + json5 "^2.1.2" locate-path@^5.0.0: version "5.0.0" @@ -4964,68 +5126,23 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.get@^4: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= - -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= - -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - -lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0, lodash@~4.17.0: +lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -5034,7 +5151,7 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -log-update@4.0.0: +log-update@4.0.0, log-update@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== @@ -5044,15 +5161,6 @@ log-update@4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" -log-update@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= - dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -5074,15 +5182,12 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" lru-cache@^6.0.0: version "6.0.0" @@ -5098,7 +5203,7 @@ make-dir@3.1.0, make-dir@^3.0.0: dependencies: semver "^6.0.0" -make-error@^1, make-error@^1.1.1: +make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -5113,18 +5218,13 @@ makeerror@1.0.12: map-cache@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== memoize-one@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== -memoize-one@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" - integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -5135,86 +5235,59 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meros@^1.1.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.0.tgz#096cdede2eb0b1610b219b1031b935260de1ad08" - integrity sha512-3QRZIS707pZQnijHdhbttXRWwrHhZJ/gzolneoxKVz9N/xmsvY/7Ls8lpnI9gxbgxjcHsAVEW3mgwiZCo6kkJQ== +meros@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.1.tgz#056f7a76e8571d0aaf3c7afcbe7eb6407ff7329e" + integrity sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g== micromatch@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" - integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== dependencies: - braces "^3.0.1" - picomatch "^2.2.3" + braces "^3.0.2" + picomatch "^2.3.1" -mime-db@1.51.0: - version "1.51.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" - integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: - version "2.1.34" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" - integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - mime-db "1.51.0" - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== + mime-db "1.52.0" mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.4, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== - dependencies: - brace-expansion "^1.1.7" - -minimist@1.2.5, minimist@^1.2.0, minimist@^1.2.5: +minimist@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -moment@^2.24.0, moment@^2.25.3: - version "2.29.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== ms@2.1.2: version "2.1.2" @@ -5231,65 +5304,55 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -mz@^2.4.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" - integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== - dependencies: - any-promise "^1.0.0" - object-assign "^4.0.1" - thenify-all "^1.0.0" - -nanoid@^3.1.30: - version "3.3.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" - integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - -next-auth@^3.29.0: - version "3.29.0" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-3.29.0.tgz#60ddbfc7ed8ae7d43ebb02c16dc58eebf5dcb337" - integrity sha512-B//4QTv/1Of0D+roZ82URmI6L2JSbkKgeaKI7Mdrioq8lAzp9ff8NdmouvZL/7zwrPe2cUyM6MLYlasfuI3ZIQ== - dependencies: - "@babel/runtime" "^7.14.0" - "@next-auth/prisma-legacy-adapter" "0.1.2" - "@next-auth/typeorm-legacy-adapter" "0.1.4" - futoin-hkdf "^1.3.2" - jose "^1.27.2" - jsonwebtoken "^8.5.1" - nodemailer "^6.4.16" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +next-auth@^4.18.8: + version "4.18.8" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.18.8.tgz#34012ac107702b27779647e8787715cea948812a" + integrity sha512-USP8ihmvB7iCGtkS0+toe2QPrzdbZfkydQZX56JOI9Ft5n/BardOXh3D4wQ2An+vpq/jDKojGlgfv21wVElW7A== + dependencies: + "@babel/runtime" "^7.16.3" + "@panva/hkdf" "^1.0.1" + cookie "^0.5.0" + jose "^4.9.3" oauth "^0.9.15" - pkce-challenge "^2.1.0" - preact "^10.4.1" - preact-render-to-string "^5.1.14" - querystring "^0.2.0" - -next@^12.0.2: - version "12.1.0" - resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d" - integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q== - dependencies: - "@next/env" "12.1.0" - caniuse-lite "^1.0.30001283" - postcss "8.4.5" - styled-jsx "5.0.0" - use-subscription "1.5.1" + openid-client "^5.1.0" + preact "^10.6.3" + preact-render-to-string "^5.1.19" + uuid "^8.3.2" + +next@^13.1.4: + version "13.1.4" + resolved "https://registry.yarnpkg.com/next/-/next-13.1.4.tgz#fe13ff4a656153b6249c101d79a54569c0cd05e5" + integrity sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog== + dependencies: + "@next/env" "13.1.4" + "@swc/helpers" "0.4.14" + caniuse-lite "^1.0.30001406" + postcss "8.4.14" + styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm64" "12.1.0" - "@next/swc-darwin-arm64" "12.1.0" - "@next/swc-darwin-x64" "12.1.0" - "@next/swc-linux-arm-gnueabihf" "12.1.0" - "@next/swc-linux-arm64-gnu" "12.1.0" - "@next/swc-linux-arm64-musl" "12.1.0" - "@next/swc-linux-x64-gnu" "12.1.0" - "@next/swc-linux-x64-musl" "12.1.0" - "@next/swc-win32-arm64-msvc" "12.1.0" - "@next/swc-win32-ia32-msvc" "12.1.0" - "@next/swc-win32-x64-msvc" "12.1.0" + "@next/swc-android-arm-eabi" "13.1.4" + "@next/swc-android-arm64" "13.1.4" + "@next/swc-darwin-arm64" "13.1.4" + "@next/swc-darwin-x64" "13.1.4" + "@next/swc-freebsd-x64" "13.1.4" + "@next/swc-linux-arm-gnueabihf" "13.1.4" + "@next/swc-linux-arm64-gnu" "13.1.4" + "@next/swc-linux-arm64-musl" "13.1.4" + "@next/swc-linux-x64-gnu" "13.1.4" + "@next/swc-linux-x64-musl" "13.1.4" + "@next/swc-win32-arm64-msvc" "13.1.4" + "@next/swc-win32-ia32-msvc" "13.1.4" + "@next/swc-win32-x64-msvc" "13.1.4" no-case@^3.0.4: version "3.0.4" @@ -5304,32 +5367,34 @@ node-domexception@1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.8" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" + integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== + dependencies: + whatwg-url "^5.0.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - -node-releases@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" - integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -nodemailer@^6.4.16: - version "6.7.2" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.2.tgz#44b2ad5f7ed71b7067f7a21c4fedabaec62b85e0" - integrity sha512-Dz7zVwlef4k5R71fdmxwR8Q39fiboGbu3xgswkzGwczUfjp873rVxt1O46+Fh0j1ORnAC6L9+heI8uUpO6DT7Q== +node-releases@^2.0.6: + version "2.0.8" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" + integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" @@ -5338,11 +5403,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -5355,95 +5415,96 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -nwsapi@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" - integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== - oauth@^0.9.15: version "0.9.15" resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" - integrity sha1-vR/vr2hslrdUda7VGWQS/2DPucE= + integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" - integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== +object-hash@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + +object-inspect@^1.12.2, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" -object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.hasown@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5" - integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg== +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== dependencies: - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +oidc-token-hash@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" + integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== -once@^1.3.0, once@^1.3.1, once@^1.4.0: +once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -5451,25 +5512,32 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optimism@^0.16.1: - version "0.16.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.1.tgz#7c8efc1f3179f18307b887e18c15c5b7133f6e7d" - integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg== +open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== dependencies: - "@wry/context" "^0.6.0" - "@wry/trie" "^0.3.0" + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== +openid-client@^5.1.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.3.2.tgz#fcc2c16f9681fa5f03ee0581b0935f88fc49f11f" + integrity sha512-nXXt+cna0XHOw+WqjMZOmuXw/YZEMwfWD2lD7tCsFtsBjMQGVXA+NZABA3upYBET1suhIsmfd7GnxG4jCAnvYQ== + dependencies: + jose "^4.10.0" + lru-cache "^6.0.0" + object-hash "^2.0.1" + oidc-token-hash "^5.0.1" + +optimism@^0.16.1: + version "0.16.2" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" + integrity sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ== dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" + "@wry/context" "^0.7.0" + "@wry/trie" "^0.3.0" optionator@^0.9.1: version "0.9.1" @@ -5501,27 +5569,15 @@ ora@^5.4.1: os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== -p-limit@3.1.0: +p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5529,13 +5585,6 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -5543,31 +5592,25 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -5586,7 +5629,7 @@ parent-module@^1.0.0: parse-filepath@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= + integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" map-cache "^0.2.0" @@ -5602,23 +5645,6 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -parse5-htmlparser2-tree-adapter@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" - integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== - dependencies: - parse5 "^6.0.1" - -parse5@6.0.1, parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parse5@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" @@ -5635,11 +5661,6 @@ path-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -5648,14 +5669,14 @@ path-exists@^4.0.0: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -5663,12 +5684,12 @@ path-parse@^1.0.6, path-parse@^1.0.7: path-root-regex@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= + integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= + integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" @@ -5682,7 +5703,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -5692,11 +5713,6 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pkce-challenge@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-2.2.0.tgz#02622e0498b82aab248c8c7dbf6507e8bbe20abf" - integrity sha512-Ly0Y0OwhtG2N1ynk5ruqoyJxkrWhAPmvdRk0teiLh9Dp2+J4URKpv1JSKWD0j1Sd+QCeiwO9lTl0EjmrB2jWeA== - pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -5704,60 +5720,45 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -postcss@8.4.5: - version "8.4.5" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" - integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== +postcss@8.4.14: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== dependencies: - nanoid "^3.1.30" + nanoid "^3.3.4" picocolors "^1.0.0" - source-map-js "^1.0.1" + source-map-js "^1.0.2" -preact-render-to-string@^5.1.14: - version "5.1.20" - resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.1.20.tgz#9e61a478e8514e5c4140282c7c43d9747a6d32a9" - integrity sha512-ivh2oOGzth0o7XqbatWUQ81WQGoJwSqDKP5z917SoqTWYCAr7dlBzMv3SAMTAu3Gr5g47BJwrvyO44H2Y10ubg== +preact-render-to-string@^5.1.19: + version "5.2.6" + resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz#0ff0c86cd118d30affb825193f18e92bd59d0604" + integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw== dependencies: pretty-format "^3.8.0" -preact@^10.4.1: - version "10.6.6" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.6.6.tgz#f1899bc8dab7c0788b858481532cb3b5d764a520" - integrity sha512-dgxpTFV2vs4vizwKohYKkk7g7rmp1wOOcfd4Tz3IB3Wi+ivZzsn/SpeKJhRENSE+n8sUfsAl4S3HiCVT923ABw== +preact@^10.6.3: + version "10.11.3" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" + integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -pretty-format@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== +pretty-format@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" + integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg== dependencies: - ansi-regex "^5.0.1" + "@jest/schemas" "^29.0.0" ansi-styles "^5.0.0" - react-is "^17.0.1" + react-is "^18.0.0" pretty-format@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" - integrity sha1-v77VbV6ad2ZF9LH/eqGjrE+jw4U= - -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== promise@^7.1.1: version "7.3.1" @@ -5783,28 +5784,27 @@ prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== +pvtsutils@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" + integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" + tslib "^2.4.0" -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pvutils@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" + integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== -querystring@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" - integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== +qrcode.react@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" + integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== queue-microtask@^1.2.2: version "1.2.3" @@ -5812,27 +5812,26 @@ queue-microtask@^1.2.2: integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== rc-align@^4.0.0: - version "4.0.11" - resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.11.tgz#8198c62db266bc1b8ef05e56c13275bf72628a5e" - integrity sha512-n9mQfIYQbbNTbefyQnRHZPWuTEwG1rY4a9yKlIWHSTbgwI+XUMGRYd0uJ5pE2UbrNX0WvnMBA1zJ3Lrecpra/A== + version "4.0.15" + resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.15.tgz#2bbd665cf85dfd0b0244c5a752b07565e9098577" + integrity sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" dom-align "^1.7.0" - lodash "^4.17.21" - rc-util "^5.3.0" + rc-util "^5.26.0" resize-observer-polyfill "^1.5.1" -rc-cascader@~3.2.1: - version "3.2.7" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.2.7.tgz#74ac3ab9258f930e0c84dfacffd838b122b2cedf" - integrity sha512-M8VtKtifTXXo/qqXj63p12tsMNXm1z45Lytj7tu86L6gxIF8keDPcJ16/ZqrhS5JwlBPfoJNA1VooNl/KId15A== +rc-cascader@~3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.8.0.tgz#5eaca8998b2e3f5692d13f16bfe2346eccc87c6a" + integrity sha512-zCz/NzsNRQ1TIfiR3rQNxjeRvgRHEkNdo0FjHQZ6Ay6n4tdCmMrM7+81ThNaf21JLQ1gS2AUG2t5uikGV78obA== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.0.0-alpha.23" - rc-tree "~5.4.3" + rc-select "~14.2.0" + rc-tree "~5.7.0" rc-util "^5.6.1" rc-checkbox@~2.3.0: @@ -5843,10 +5842,10 @@ rc-checkbox@~2.3.0: "@babel/runtime" "^7.10.1" classnames "^2.2.1" -rc-collapse@~3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.1.2.tgz#76028a811b845d03d9460ccc409c7ea8ad09db14" - integrity sha512-HujcKq7mghk/gVKeI6EjzTbb8e19XUZpakrYazu1MblEZ3Hu3WBMSN4A3QmvbF6n1g7x6lUlZvsHZ5shABWYOQ== +rc-collapse@~3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.4.2.tgz#1310be7ad4cd0dcfc622c45f6c3b5ffdee403ad7" + integrity sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -5854,192 +5853,192 @@ rc-collapse@~3.1.0: rc-util "^5.2.1" shallowequal "^1.1.0" -rc-dialog@~8.6.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.6.0.tgz#3b228dac085de5eed8c6237f31162104687442e7" - integrity sha512-GSbkfqjqxpZC5/zc+8H332+q5l/DKUhpQr0vdX2uDsxo5K0PhvaMEVjyoJUTkZ3+JstEADQji1PVLVb/2bJeOQ== +rc-dialog@~9.0.0, rc-dialog@~9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.0.2.tgz#aadfebdeba145f256c1fac9b9f509f893cdbb5b8" + integrity sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.0.0-8" classnames "^2.2.6" rc-motion "^2.3.0" - rc-util "^5.6.1" - -rc-drawer@~4.4.2: - version "4.4.3" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-4.4.3.tgz#2094937a844e55dc9644236a2d9fba79c344e321" - integrity sha512-FYztwRs3uXnFOIf1hLvFxIQP9MiZJA+0w+Os8dfDh/90X7z/HqP/Yg+noLCIeHEbKln1Tqelv8ymCAN24zPcfQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-util "^5.7.0" + rc-util "^5.21.0" -rc-dropdown@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.3.2.tgz#097c2ec1b6d55c10eeb94dcf6120ba034c7a58e0" - integrity sha512-49GOz42oNvLtYGoJ2X5UWXJFp7aUiSZkj9OcgTV1UpxFZqHQMw+xijkaL5k3XDkMbb92XsuFnFt7IGG3/C0DKw== +rc-drawer@~6.1.1: + version "6.1.2" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.1.2.tgz#032918a21bfa8a7d9e52ada1e7b8ed08c0ae6346" + integrity sha512-mYsTVT8Amy0LRrpVEv7gI1hOjtfMSO/qHAaCDzFx9QBLnms3cAQLJkaxRWM+Eq99oyLhU/JkgoqTg13bc4ogOQ== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/portal" "^1.0.0-6" classnames "^2.2.6" - rc-trigger "^5.0.4" + rc-motion "^2.6.1" + rc-util "^5.21.2" -rc-dropdown@~3.2.5: - version "3.2.5" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-3.2.5.tgz#c211e571d29d15e7f725b5a75fc8c7f371fc3348" - integrity sha512-dVO2eulOSbEf+F4OyhCY5iGiMVhUYY/qeXxL7Ex2jDBt/xc89jU07mNoowV6aWxwVOc70pxEINff0oM2ogjluA== +rc-dropdown@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68" + integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.18.3" classnames "^2.2.6" - rc-trigger "^5.0.4" + rc-trigger "^5.3.1" + rc-util "^5.17.0" -rc-field-form@~1.23.0: - version "1.23.1" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.23.1.tgz#638c11d05d7ed2efdcb862ff3da5fe2a7d199aaa" - integrity sha512-Mun+eaFmX1Pjud9bz0fD0IvxwDfFKWk2Q8tkt4sg4aKR9/FML/rzYC5MjY77p86X45XBurBDUR3gAda+Cg/ULw== +rc-field-form@~1.27.0: + version "1.27.3" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.3.tgz#e5262796b91c80848a42a3e7a669bf459f08d63d" + integrity sha512-HGqxHnmGQgkPApEcikV4qTg3BLPC82uB/cwBDftDt1pYaqitJfSl5TFTTUMKVEJVT5RqJ2Zi68ME1HmIMX2HAw== dependencies: - "@babel/runtime" "^7.8.4" - async-validator "^4.0.2" + "@babel/runtime" "^7.18.0" + async-validator "^4.1.0" rc-util "^5.8.0" -rc-image@~5.2.5: - version "5.2.5" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.2.5.tgz#44e6ffc842626827960e7ab72e1c0d6f3a8ce440" - integrity sha512-qUfZjYIODxO0c8a8P5GeuclYXZjzW4hV/5hyo27XqSFo1DmTCs2HkVeQObkcIk5kNsJtgsj1KoPThVsSc/PXOw== +rc-image@~5.13.0: + version "5.13.0" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.13.0.tgz#1ed9b852a40b5eff34786ba7d2f0e9d26eeab874" + integrity sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg== dependencies: "@babel/runtime" "^7.11.2" + "@rc-component/portal" "^1.0.2" classnames "^2.2.6" - rc-dialog "~8.6.0" + rc-dialog "~9.0.0" + rc-motion "^2.6.2" rc-util "^5.0.6" -rc-input-number@~7.3.0: - version "7.3.4" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.3.4.tgz#674aea98260250287d36e330a7e065b174486e9d" - integrity sha512-W9uqSzuvJUnz8H8vsVY4kx+yK51SsAxNTwr8SNH4G3XqQNocLVmKIibKFRjocnYX1RDHMND9FFbgj2h7E7nvGA== +rc-input-number@~7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.4.0.tgz#b8b4ffa8bbc04198e79ce8b9611756d046d128ec" + integrity sha512-r/Oub/sPYbzqLNUOHnnc9sbCu78a81KX+RCbRwmpvB4W6nptUySbdWS5KHV4Hak5CAE1LAd+wWm5JjvZizG1FA== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" - rc-util "^5.9.8" + rc-util "^5.23.0" -rc-mentions@~1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.6.2.tgz#62ed7cdd8fa86d857c3ce3f9e73438022130815e" - integrity sha512-cntfJkNMq8B910rXuvnsnOV88DfmoUidnQnSIeXzWiYiUX4RL5oWUfSZzs+HAXYRU4SL1l8Mwjx95wHETiZ/fQ== +rc-input@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.1.4.tgz#45cb4ba209ae6cc835a2acb8629d4f8f0cb347e0" + integrity sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA== dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-menu "^9.0.0" - rc-textarea "^0.3.0" - rc-trigger "^5.0.4" - rc-util "^5.0.1" + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-util "^5.18.1" -rc-menu@^9.0.0: - version "9.3.2" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.3.2.tgz#bb842d37ebf71da912bea201cf7ef0a27267ad49" - integrity sha512-h3m45oY1INZyqphGELkdT0uiPnFzxkML8m0VMhJnk2fowtqfiT7F5tJLT3znEVaPIY80vMy1bClCkgq8U91CzQ== +rc-mentions@~1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.13.1.tgz#c884b70e1505a197f1b32a7c6b39090db6992a72" + integrity sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw== dependencies: "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.4.3" - rc-overflow "^1.2.0" - rc-trigger "^5.1.2" - rc-util "^5.12.0" - shallowequal "^1.1.0" + classnames "^2.2.6" + rc-menu "~9.8.0" + rc-textarea "^0.4.0" + rc-trigger "^5.0.4" + rc-util "^5.22.5" -rc-menu@~9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.2.1.tgz#6fbe47f4846363bb81a5a21f0960026c3ada497a" - integrity sha512-UbEtn3rflJ8zS+etYGTVQuzy7Fm+yWXR5c0Rl6ecNTS/dPknRyWAyhJcbeR0Hu1+RdQT+0VCqrUPrgKnm4iY+w== +rc-menu@~9.8.0, rc-menu@~9.8.2: + version "9.8.2" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.8.2.tgz#179df4ea0fc46fffb7a21e18d3cddb901ce35986" + integrity sha512-EahOJVjLuEnJsThoPN+mGnVm431RzVzDLZWHRS/YnXTQULa7OsgdJa/Y7qXxc3Z5sz8mgT6xYtgpmBXLxrZFaQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.4.3" - rc-overflow "^1.2.0" + rc-overflow "^1.2.8" rc-trigger "^5.1.2" - rc-util "^5.12.0" - shallowequal "^1.1.0" + rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4: - version "2.4.5" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.4.5.tgz#b061c50bb29ecd3d735d5f4c40924a3c78226cbd" - integrity sha512-f3uJHR4gcpeZS/s8/nYFSOrXt2Wu/h9GrEcbJmC0qmKrVNgwL1pTgrT5kW7lgG6PFeoL4yHDmpQoEKkrPtKIzQ== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.0, rc-motion@^2.6.1, rc-motion@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.3.tgz#e6d8ca06591c2c1bcd3391a8e7a822ebc4d94e9c" + integrity sha512-xFLkes3/7VL/J+ah9jJruEW/Akbx5F6jVa2wG5o/ApGKQKSOd5FR3rseHLL9+xtJg4PmCwo6/1tqhDO/T+jFHA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.18.1" + rc-util "^5.21.0" -rc-notification@~4.5.7: - version "4.5.7" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.5.7.tgz#265e6e6a0c1a0fac63d6abd4d832eb8ff31522f1" - integrity sha512-zhTGUjBIItbx96SiRu3KVURcLOydLUHZCPpYEn1zvh+re//Tnq/wSxN4FKgp38n4HOgHSVxcLEeSxBMTeBBDdw== +rc-notification@~5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.0.2.tgz#31811ac90e9516d88990fd2a93dbe61cc1712eec" + integrity sha512-74wUFiLlyr6lRGEY1m1BaTiDp+0lIT4FRAblMnh9FApyK2JGdsSLbrQ/1rgM7d2N/IX5UIr8kLLW3TdXxFt/jQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-motion "^2.2.0" - rc-util "^5.0.1" + rc-motion "^2.6.0" + rc-util "^5.20.1" -rc-overflow@^1.0.0, rc-overflow@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.3.tgz#1754216d807f5473304272b0321c3aba7615f47a" - integrity sha512-Bz6dXTn/ww8nmu70tUQfRV0wT3BkfXY6j1lB1O38OVkDPz4xwfAcGK+LJ2zewUR5cTXkJ8hAN7YULohG8z4M7Q== +rc-overflow@^1.0.0, rc-overflow@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.8.tgz#40f140fabc244118543e627cdd1ef750d9481a88" + integrity sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.15.0" + rc-util "^5.19.2" -rc-pagination@~3.1.9: - version "3.1.15" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.15.tgz#e05eddf4c15717a5858290bed0857e27e2f957ff" - integrity sha512-4L3fot8g4E+PjWEgoVGX0noFCg+8ZFZmeLH4vsnZpB3O2T2zThtakjNxG+YvSaYtyMVT4B+GLayjKrKbXQpdAg== +rc-pagination@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.2.0.tgz#4f2fdba9fdac0f48e5c9fb1141973818138af7e1" + integrity sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" -rc-picker@~2.5.17: - version "2.5.19" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.5.19.tgz#73d07546fac3992f0bfabf2789654acada39e46f" - integrity sha512-u6myoCu/qiQ0vLbNzSzNrzTQhs7mldArCpPHrEI6OUiifs+IPXmbesqSm0zilJjfzrZJLgYeyyOMSznSlh0GKA== +rc-picker@~3.1.1: + version "3.1.4" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.1.4.tgz#4806dc36a78424abaee610830777c8e22a23c74c" + integrity sha512-4qANXNc3C02YENNQvun329zf9VLvSQ2W8RkKQRu8k1P+EtSGqe3klcAKCfz/1TuCiDvgRjJlzRmyZAkwvsbI8w== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" - date-fns "2.x" - dayjs "1.x" - moment "^2.24.0" rc-trigger "^5.0.4" rc-util "^5.4.0" shallowequal "^1.1.0" -rc-progress@~3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.2.4.tgz#4036acdae2566438545bc4df2203248babaf7549" - integrity sha512-M9WWutRaoVkPUPIrTpRIDpX0SPSrVHzxHdCRCbeoBFrd9UFWTYNWRlHsruJM5FH1AZI+BwB4wOJUNNylg/uFSw== +rc-progress@~3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.4.1.tgz#a9ffe099e88a4fc03afb09d8603162bf0760d743" + integrity sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-util "^5.16.1" rc-rate@~2.9.0: - version "2.9.1" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.9.1.tgz#e43cb95c4eb90a2c1e0b16ec6614d8c43530a731" - integrity sha512-MmIU7FT8W4LYRRHJD1sgG366qKtSaKb67D0/vVvJYR0lrCuRrCiVQ5qhfT5ghVO4wuVIORGpZs7ZKaYu+KMUzA== + version "2.9.2" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.9.2.tgz#4a58965d1ecf91896ebae01d458b59056df0b4ea" + integrity sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-util "^5.0.1" rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz#9f46052f81cdf03498be35144cb7c53fd282c4c7" - integrity sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ== + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.1.tgz#7f9715b5d1afe126ade3c107aafd2cebf8a57a99" + integrity sha512-g53PnWLeVOmt4XWkt2x+QlIdf/PhJSd7JqHhtMrUY370e7wJ+kxbgXicYqvENUcgFiiOiMCd07YsC2GNsoSbnA== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.20.7" classnames "^2.2.1" - rc-util "^5.15.0" + rc-util "^5.27.0" resize-observer-polyfill "^1.5.1" -rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha.8: - version "14.0.0" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.0.0.tgz#87735dbc548f1cc8e94d579b21682ed2d34f7653" - integrity sha512-DkoWMhyxmrfpc1KJSqPORZdkKevzgOINvjR4WI+dibRe6i6DyqGB4Jk21sencnK9di6dumzOCHf93x9t9+gp3Q== +rc-segmented@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.0.tgz#0e0afe646c1a0e44a0e18785f518c42633ec8efc" + integrity sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw== + dependencies: + "@babel/runtime" "^7.11.1" + classnames "^2.2.1" + rc-motion "^2.4.4" + rc-util "^5.17.0" + +rc-select@~14.2.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.2.0.tgz#60e83a7d5a5dae7fd9e3918d9b209074ea2c92d4" + integrity sha512-tvxHmbAA0EIhBkB7dyaRhcBUIWHocQbUFY/fBlezj2jg5p65a5VQ/UhBg2I9TA1wjpsr5CCx0ruZPkYcUMjDoQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6047,148 +6046,139 @@ rc-select@~14.0.0-alpha.15, rc-select@~14.0.0-alpha.23, rc-select@~14.0.0-alpha. rc-overflow "^1.0.0" rc-trigger "^5.0.4" rc-util "^5.16.1" - rc-virtual-list "^3.2.0" + rc-virtual-list "^3.4.13" -rc-slider@~9.7.4: - version "9.7.5" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-9.7.5.tgz#193141c68e99b1dc3b746daeb6bf852946f5b7f4" - integrity sha512-LV/MWcXFjco1epPbdw1JlLXlTgmWpB9/Y/P2yinf8Pg3wElHxA9uajN21lJiWtZjf5SCUekfSP6QMJfDo4t1hg== +rc-slider@~10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9" + integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-tooltip "^5.0.1" - rc-util "^5.16.1" + rc-util "^5.18.1" shallowequal "^1.1.0" -rc-steps@~4.1.0: - version "4.1.4" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-4.1.4.tgz#0ba82db202d59ca52d0693dc9880dd145b19dc23" - integrity sha512-qoCqKZWSpkh/b03ASGx1WhpKnuZcRWmvuW+ZUu4mvMdfvFzVxblTwUM+9aBd0mlEUFmt6GW8FXhMpHkK3Uzp3w== +rc-steps@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.0.tgz#f7148f8097d5d135f19b96c1b4f4b50ad6093753" + integrity sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ== dependencies: - "@babel/runtime" "^7.10.2" + "@babel/runtime" "^7.16.7" classnames "^2.2.3" - rc-util "^5.0.1" + rc-util "^5.16.1" -rc-switch@~3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-3.2.2.tgz#d001f77f12664d52595b4f6fb425dd9e66fba8e8" - integrity sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A== +rc-switch@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.0.0.tgz#55fbf99fc2d680791175037d379e170ba51fbe78" + integrity sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" rc-util "^5.0.1" -rc-table@~7.23.0: - version "7.23.0" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.23.0.tgz#e5f76998ecf3246147d45ed311417c08886e6507" - integrity sha512-Q1gneB2+lUa8EzCCfbrq+jO1qNSwQv1RUUXKB84W/Stdp4EvGOt2+QqGyfotMNM4JUw0fgGLwY+WjnhUhnLuQQ== +rc-table@~7.30.2: + version "7.30.3" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.30.3.tgz#9333e4d0b106738d88e2e16c8b066dfb29d93114" + integrity sha512-PHe+lZKwPo3qui5j79m54vKu8b4hebk04x+4Hy65NvwUU3+NNFGS5FZpylXQMkueMnE8hgh22ZuScQDkCtzQFQ== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/context" "^1.3.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.14.0" - shallowequal "^1.1.0" + rc-util "^5.27.1" -rc-tabs@~11.10.0: - version "11.10.7" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.10.7.tgz#7d8b5dcc17f1608cf3b9425d80069f1415479335" - integrity sha512-7IKmcU7QU3CdYnJTabeXs2DDeLiXLyALC8fvOtgyWWFXUD47G5vG+4bFO3f9+AI+rcFAPpfwapZbXxgmiRuWYQ== +rc-tabs@~12.5.1: + version "12.5.6" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.5.6.tgz#611728575d5f3ef010a8e5e75d2c1b67bb71e86a" + integrity sha512-aArXHzxK7YICxe+622CZ8FlO5coMi8P7E6tXpseCPKm1gdTjUt0LrQK1/AxcrRXZXG3K4QqhlKmET0+cX5DQaQ== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "^3.2.0" - rc-menu "^9.0.0" + rc-dropdown "~4.0.0" + rc-menu "~9.8.0" + rc-motion "^2.6.2" rc-resize-observer "^1.0.0" - rc-util "^5.5.0" + rc-util "^5.16.0" -rc-textarea@^0.3.0, rc-textarea@~0.3.0: - version "0.3.7" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.3.7.tgz#987142891efdedb774883c07e2f51b318fde5a11" - integrity sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw== +rc-textarea@^0.4.0, rc-textarea@~0.4.5: + version "0.4.7" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.4.7.tgz#627f662d46f99e0059d1c1ebc8db40c65339fe90" + integrity sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.7.0" + rc-util "^5.24.4" shallowequal "^1.1.0" -rc-tooltip@^5.0.1, rc-tooltip@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.1.1.tgz#94178ed162d0252bc4993b725f5dc2ac0fccf154" - integrity sha512-alt8eGMJulio6+4/uDm7nvV+rJq9bsfxFDCI0ljPdbuoygUscbsMYb6EQgwib/uqsXQUvzk+S7A59uYHmEgmDA== +rc-tooltip@~5.2.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.2.2.tgz#e5cafa8ecebf78108936a0bcb93c150fa81ac93b" + integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg== dependencies: "@babel/runtime" "^7.11.2" + classnames "^2.3.1" rc-trigger "^5.0.0" -rc-tree-select@~5.1.1: - version "5.1.4" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.1.4.tgz#3577135399d1f4931b0f4d8245e0845861802e2b" - integrity sha512-sA6vTUQghzbjh3u6YAwJIebKkJEHUWDPFHQpfiPObqsEYqi9TKE1LvWqbJ77NbOlOARZq0KIb7LDGF8X0dikDQ== +rc-tree-select@~5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.6.0.tgz#f34147f4c14341430bcece481804496d0abd3371" + integrity sha512-XG6pu0a9l6+mzhQqUYfR2VIONbe/3LjVc3wKt28k6uBMZsI1j+SSxRyt/7jWRq8Kok8jHJBQASlDg6ehr9Sp0w== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.0.0-alpha.8" - rc-tree "~5.4.3" + rc-select "~14.2.0" + rc-tree "~5.7.0" rc-util "^5.16.1" -rc-tree@~5.4.3: - version "5.4.4" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.4.4.tgz#2ea3663ad3c566aef79a46ba6a1e050d24323e01" - integrity sha512-2qoObRgp31DBXmVzMJmo4qmwP20XEa4hR3imWQtRPcgN3pmljW3WKFmZRrYdOFHz7CyTnRsFZR065bBkIoUpiA== +rc-tree@~5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.7.2.tgz#270ea7d9e1b2e5e81cd3659eba2fbd022a4831f6" + integrity sha512-nmnL6qLnfwVckO5zoqKL2I9UhwDqzyCtjITQCkwhimyz1zfuFkG5ZPIXpzD/Guzso94qQA/QrMsvzic5W6QDjg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.2" + rc-virtual-list "^3.4.8" -rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10: - version "5.2.10" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.2.10.tgz#8a0057a940b1b9027eaa33beec8a6ecd85cce2b1" - integrity sha512-FkUf4H9BOFDaIwu42fvRycXMAvkttph9AlbCZXssZDVzz2L+QZ0ERvfB/4nX3ZFPh1Zd+uVGr1DEDeXxq4J1TA== +rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.3.1, rc-trigger@^5.3.4: + version "5.3.4" + resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.4.tgz#6b4b26e32825677c837d1eb4d7085035eecf9a61" + integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== dependencies: - "@babel/runtime" "^7.11.2" + "@babel/runtime" "^7.18.3" classnames "^2.2.6" rc-align "^4.0.0" rc-motion "^2.0.0" - rc-util "^5.5.0" + rc-util "^5.19.2" rc-upload@~4.3.0: - version "4.3.3" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.3.tgz#e237aa525e5313fa16f4d04d27f53c2f0e157bb8" - integrity sha512-YoJ0phCRenMj1nzwalXzciKZ9/FAaCrFu84dS5pphwucTC8GUWClcDID/WWNGsLFcM97NqIboDqrV82rVRhW/w== + version "4.3.4" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.4.tgz#83ff7d3867631c37adbfd72ea3d1fd7e97ca84af" + integrity sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.18.3" classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.0.7, rc-util@^5.12.0, rc-util@^5.14.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4, rc-util@^5.9.8: - version "5.18.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.18.1.tgz#80bd1450b5254655d2fbea63e3d34f6871e9be79" - integrity sha512-24xaSrMZUEKh1+suDOtJWfPe9E6YrwryViZcoPO0miJTKzP4qhUlV5AAlKQ82AJilz/AOHfi3l6HoX8qa1ye8w== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.27.1, rc-util@^5.4.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: + version "5.27.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.27.1.tgz#d12f02b9577b04299c0f1a235c8acbcf56e2824b" + integrity sha512-PsjHA+f+KBCz+YTZxrl3ukJU5RoNKoe3KSNMh0xGiISbR67NaM9E9BiMjCwxa3AcCUOg/rZ+V0ZKLSimAA+e3w== dependencies: - "@babel/runtime" "^7.12.5" + "@babel/runtime" "^7.18.3" react-is "^16.12.0" - shallowequal "^1.1.0" -rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.2.tgz#1078327aa7230b5e456d679ed2ce99f3c036ebd1" - integrity sha512-OyVrrPvvFcHvV0ssz5EDZ+7Rf5qLat/+mmujjchNw5FfbJWNDwkpQ99EcVE6+FtNRmX9wFa1LGNpZLUTvp/4GQ== +rc-virtual-list@^3.4.13, rc-virtual-list@^3.4.8: + version "3.4.13" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz#20acc934b263abcf7b7c161f50ef82281b2f7e8d" + integrity sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w== dependencies: + "@babel/runtime" "^7.20.0" classnames "^2.2.6" rc-resize-observer "^1.0.0" - rc-util "^5.0.7" - -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" + rc-util "^5.15.0" react-cookie@^4.0.3: version "4.1.1" @@ -6199,19 +6189,18 @@ react-cookie@^4.0.3: hoist-non-react-statics "^3.0.0" universal-cookie "^4.0.0" -react-dom@^17.0: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" - integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "^0.20.2" + scheduler "^0.23.0" -react-highlight-words@^0.17.0: - version "0.17.0" - resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.17.0.tgz#e79a559a2de301548339d7216264d6cd0f1eed6f" - integrity sha512-uX1Qh5IGjnLuJT0Zok234QDwRC8h4hcVMnB99Cb7aquB1NlPPDiWKm0XpSZOTdSactvnClCk8LOmVlP+75dgHA== +react-highlight-words@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c60bfff5d14678c8f0e8fbe4bdcf083e6c70d507" + integrity sha512-asCxy+jCehDVhusNmCBoxDf2mm1AJ//D+EzDx1m5K7EqsMBIHdZ5G4LdwbSEXqZq1Ros0G0UySWmAtntSph7XA== dependencies: highlight-words-core "^1.2.0" memoize-one "^4.0.0" @@ -6222,36 +6211,34 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.1, react-is@^17.0.2: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -react-shallow-renderer@^16.13.1: - version "16.14.1" - resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124" - integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg== +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== dependencies: object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -react-test-renderer@^17.0: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" - integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== +react-test-renderer@^18.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" + integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA== dependencies: - object-assign "^4.1.1" - react-is "^17.0.2" - react-shallow-renderer "^16.13.1" - scheduler "^0.20.2" + react-is "^18.2.0" + react-shallow-renderer "^16.15.0" + scheduler "^0.23.0" -react@^17.0: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" - integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" readable-stream@^3.4.0: version "3.6.0" @@ -6269,66 +6256,25 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -reflect-metadata@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regexp.prototype.flags@^1.3.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" - integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== +regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== dependencies: call-bind "^1.0.2" define-properties "^1.1.3" + functions-have-names "^1.2.2" -regexpp@^3.1.0: +regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== - dependencies: - rc "^1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -relay-compiler@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/relay-compiler/-/relay-compiler-12.0.0.tgz#9f292d483fb871976018704138423a96c8a45439" - integrity sha512-SWqeSQZ+AMU/Cr7iZsHi1e78Z7oh00I5SvR092iCJq79aupqJ6Ds+I1Pz/Vzo5uY5PY0jvC4rBJXzlIN5g9boQ== - dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.4.0" - chalk "^4.0.0" - fb-watchman "^2.0.0" - fbjs "^3.0.0" - glob "^7.1.1" - immutable "~3.7.6" - invariant "^2.2.4" - nullthrows "^1.1.1" - relay-runtime "12.0.0" - signedsource "^1.0.0" - yargs "^15.3.1" - relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -6346,42 +6292,24 @@ remedial@^1.0.7: remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-trailing-spaces@^1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== -replaceall@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/replaceall/-/replaceall-0.1.6.tgz#81d81ac7aeb72d7f5c4942adf2697a3220688d8e" - integrity sha1-gdgax663LX9cSUKt8ml6MiBojY4= - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -require_optional@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e" - integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g== - dependencies: - resolve-from "^2.0.0" - semver "^5.1.0" - -resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: +resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== @@ -6398,52 +6326,33 @@ resolve-from@5.0.0, resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-from@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" - integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c= - resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== -resolve@^1.20.0: - version "1.22.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" - integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== +resolve@^1.20.0, resolve@^1.22.1: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== dependencies: - is-core-module "^2.8.1" + is-core-module "^2.9.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: - version "2.0.0-next.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" - integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" restore-cursor@^3.1.0: version "3.1.0" @@ -6458,7 +6367,12 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^3.0.0, rimraf@^3.0.2: +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== @@ -6477,17 +6391,10 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^6.3.3: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.2.0: - version "7.5.4" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" - integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== +rxjs@^7.5.5: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" @@ -6496,35 +6403,26 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -saxes@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - -scheduler@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" - integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== dependencies: loose-envify "^1.1.0" - object-assign "^4.1.1" schema-utils@3.0.0: version "3.0.0" @@ -6535,32 +6433,27 @@ schema-utils@3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -scroll-into-view-if-needed@^2.2.25: - version "2.2.29" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885" - integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg== +scroll-into-view-if-needed@^3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.4.tgz#4b3101712f0ac8ec982c0a7ab4d071e393241e21" + integrity sha512-s+/F50jwTOUt+u5oEIAzum9MN2lUQNvWBe/zfEsVQcbaERjGkKLq1s+2wCHkahMLC8nMLbzMVKivx9JhunXaZg== dependencies: - compute-scroll-into-view "^1.0.17" + compute-scroll-into-view "^2.0.4" scuid@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== -semver@^5.1.0, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.2, semver@^7.3.5: - version "7.3.5" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" - integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== +semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== dependencies: lru-cache "^6.0.0" @@ -6576,20 +6469,12 @@ sentence-case@^3.0.4: set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -sha.js@^2.4.11: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== shallowequal@^1.1.0: version "1.1.0" @@ -6608,6 +6493,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@^1.7.3: + version "1.7.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" + integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -6617,7 +6507,7 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.2, signal-exit@^3.0.3: +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -6625,7 +6515,7 @@ signal-exit@^3.0.2, signal-exit@^3.0.3: signedsource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" - integrity sha1-HdrOSYF5j5O9gzlzgD2A1S6TrWo= + integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== sisteransi@^1.0.5: version "1.0.5" @@ -6637,10 +6527,19 @@ slash@3.0.0, slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" slice-ansi@^4.0.0: version "4.0.0" @@ -6659,15 +6558,15 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -source-map-js@^1.0.1: +source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@^0.5.17, source-map-support@^0.5.6: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -6675,18 +6574,13 @@ source-map-support@^0.5.17, source-map-support@^0.5.6: source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - sponge-case@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" @@ -6697,19 +6591,31 @@ sponge-case@^1.0.1: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + version "2.0.6" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + string-convert@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" - integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= + integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1" @@ -6724,23 +6630,6 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -6750,35 +6639,37 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa" - integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg== +string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" internal-slot "^1.0.3" - regexp.prototype.flags "^1.3.1" + regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.20.4" -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" + define-properties "^1.1.4" + es-abstract "^1.20.4" string_decoder@^1.1.1: version "1.3.0" @@ -6787,20 +6678,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -6811,7 +6688,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" @@ -6828,31 +6705,17 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -styled-jsx@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77" - integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA== - -subscriptions-transport-ws@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz#baf88f050cba51d52afe781de5e81b3c31f89883" - integrity sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ== +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== dependencies: - backo2 "^1.0.2" - eventemitter3 "^3.1.0" - iterall "^1.2.1" - symbol-observable "^1.0.4" - ws "^5.2.0 || ^6.0.0 || ^7.0.0" + client-only "0.0.1" -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= +stylis@^4.0.13: + version "4.1.3" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" + integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== supports-color@^5.3.0: version "5.5.0" @@ -6861,7 +6724,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -6875,14 +6738,6 @@ supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" - integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== - dependencies: - has-flag "^4.0.0" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -6895,47 +6750,23 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" -symbol-observable@^1.0.4, symbol-observable@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== - symbol-observable@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -sync-fetch@0.3.1, sync-fetch@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.3.1.tgz#62aa82c4b4d43afd6906bfd7b5f92056458509f0" - integrity sha512-xj5qiCDap/03kpci5a+qc5wSJjc8ZSixgG2EUmH1B8Ea2sfWclQA7eH40hiHPCtkCn6MCk4Wb+dqcXdCy2PP3g== - dependencies: - buffer "^5.7.0" - node-fetch "^2.6.1" - -table@^6.0.9: - version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" - integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== +synckit@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.4.tgz#0e6b392b73fafdafcde56692e3352500261d64ec" + integrity sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw== dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" + "@pkgr/utils" "^2.3.1" + tslib "^2.4.0" -terminal-link@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" +tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== test-exclude@^6.0.0: version "6.0.0" @@ -6949,31 +6780,25 @@ test-exclude@^6.0.0: text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -thenify-all@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" - integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY= - dependencies: - thenify ">= 3.1.0 < 4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -"thenify@>= 3.1.0 < 4": - version "3.3.1" - resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" - integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== - dependencies: - any-promise "^1.0.0" - -throat@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== +throttle-debounce@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933" + integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg== -through@^2.3.6: +through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" title-case@^3.0.3: version "3.0.3" @@ -6997,12 +6822,7 @@ tmpl@1.0.5: to-fast-properties@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== to-regex-range@^5.0.1: version "5.0.1" @@ -7014,28 +6834,12 @@ to-regex-range@^5.0.1: toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== ts-invariant@^0.9.0: version "0.9.4" @@ -7045,41 +6849,48 @@ ts-invariant@^0.9.0: tslib "^2.1.0" ts-log@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.4.tgz#d672cf904b33735eaba67a7395c93d45fba475b3" - integrity sha512-DEQrfv6l7IvN2jlzc/VTdZJYsWUnQNCsueYjMkC/iXoEoi5fNan6MjeDqkvhfzbmHgdz9UxDUluX3V5HdjTydQ== - -ts-node@^9: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== - dependencies: + version "2.2.5" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.5.tgz#aef3252f1143d11047e2cb6f7cfaac7408d96623" + integrity sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA== + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" + v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.12.0, tsconfig-paths@^3.9.0: - version "3.13.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.13.0.tgz#f3e9b8f6876698581d94470c03c95b3a48c0e3d7" - integrity sha512-nWuffZppoaYK0vQ1SQmkSsQzJoHA4s6uzdb2waRpD806x9yfq153AdVsWz4je2qZcW+pENrMQXbGQ3sMCkXuhw== +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.1" - minimist "^1.2.0" + minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@~2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@~2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tslib@~2.0.1: version "2.0.3" @@ -7091,6 +6902,11 @@ tslib@~2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== +tslib@~2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7105,13 +6921,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -7127,6 +6936,15 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -7134,58 +6952,37 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typeorm@^0.2.30: - version "0.2.45" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.45.tgz#e5bbb3af822dc4646bad96cfa48cd22fa4687cea" - integrity sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA== - dependencies: - "@sqltools/formatter" "^1.2.2" - app-root-path "^3.0.0" - buffer "^6.0.3" - chalk "^4.1.0" - cli-highlight "^2.1.11" - debug "^4.3.1" - dotenv "^8.2.0" - glob "^7.1.6" - js-yaml "^4.0.0" - mkdirp "^1.0.4" - reflect-metadata "^0.1.13" - sha.js "^2.4.11" - tslib "^2.1.0" - uuid "^8.3.2" - xml2js "^0.4.23" - yargs "^17.0.1" - zen-observable-ts "^1.0.0" - typescript@^4.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4" - integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg== + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== ua-parser-js@^0.7.30: - version "0.7.31" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" - integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ== + version "0.7.33" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" + integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== -unbox-primitive@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" - integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.1" - has-symbols "^1.0.2" + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= + integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undici@^4.9.3: - version "4.15.1" - resolved "https://registry.yarnpkg.com/undici/-/undici-4.15.1.tgz#c2c0e75f232178f0e6781f6b46c81ccc15065f6e" - integrity sha512-h8LJybhMKD09IyQZoQadNtIR/GmugVhTOVREunJrpV6RStriKBFdSVoFzEzTihwXi/27DIBO+Z0OGF+Mzfi0lA== +undici@^5.12.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.16.0.tgz#6b64f9b890de85489ac6332bd45ca67e4f7d9943" + integrity sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ== + dependencies: + busboy "^1.6.0" universal-cookie@^4.0.0: version "4.0.4" @@ -7195,18 +6992,21 @@ universal-cookie@^4.0.0: "@types/cookie" "^0.3.3" cookie "^0.4.0" -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - unixify@1.0.0, unixify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" - integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA= + integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" +update-browserslist-db@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + upper-case-first@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" @@ -7228,69 +7028,48 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -use-subscription@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" - integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== +urlpattern-polyfill@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-6.0.2.tgz#a193fe773459865a2a5c93b246bb794b13d07256" + integrity sha512-5vZjFlH9ofROmuWmXM9yj2wljYKgWstGwe8YTyiqM7hVum/g9LyCizPZtb3UqsuppVwety9QJmfc42VggLpTgg== dependencies: - object-assign "^4.1.1" + braces "^3.0.2" util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-compile-cache@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" - integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== -v8-to-istanbul@^8.1.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" - integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== +v8-to-istanbul@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" + integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== dependencies: + "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" - source-map "^0.7.3" - -valid-url@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" - integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= -value-or-promise@1.0.11, value-or-promise@^1.0.11: +value-or-promise@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== - dependencies: - xml-name-validator "^3.0.0" +value-or-promise@1.0.12, value-or-promise@^1.0.11: + version "1.0.12" + resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" + integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== -walker@^1.0.7: +walker@^1.0.7, walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -7300,69 +7079,49 @@ walker@^1.0.7: wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== dependencies: defaults "^1.0.3" -web-streams-polyfill@4.0.0-beta.1: - version "4.0.0-beta.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" - integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== web-streams-polyfill@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" - integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== + version "3.2.1" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" + integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + +webcrypto-core@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" + integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + dependencies: + "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/json-schema" "^1.1.12" + asn1js "^3.0.1" + pvtsutils "^1.3.2" + tslib "^2.4.0" webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== - -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-fetch@^3.4.1: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^8.0.0, whatwg-url@^8.5.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" - integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== - dependencies: - lodash "^4.7.0" - tr46 "^2.1.0" - webidl-conversions "^6.1.0" - which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" @@ -7374,10 +7133,32 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -7386,19 +7167,11 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -wrap-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" - integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo= - dependencies: - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7420,7 +7193,7 @@ wrap-ansi@^7.0.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^3.0.0: version "3.0.3" @@ -7432,38 +7205,18 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.6: - version "7.5.7" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== - -ws@^8.3.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== - -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== - -xml2js@^0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== +write-file-atomic@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + imurmurhash "^0.1.4" + signal-exit "^3.0.7" -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +ws@8.12.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" + integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== y18n@^4.0.0: version "4.0.3" @@ -7475,6 +7228,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -7485,19 +7243,25 @@ yaml-ast-parser@^0.0.43: resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== -yaml-loader@^0.6: - version "0.6.0" - resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.6.0.tgz#fe1c48b9f4803dace55a59a1474e790ba6ab1b48" - integrity sha512-1bNiLelumURyj+zvVHOv8Y3dpCri0F2S+DCcmps0pA1zWRLjS+FhZQg4o3aUUDYESh73+pKZNI18bj7stpReow== +yaml-loader@^0.8: + version "0.8.0" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.0.tgz#c839325e3fdee082b3768b2a21fe34fde5d96f61" + integrity sha512-LjeKnTzVBKWiQBeE2L9ssl6WprqaUIxCSNs5tle8PaDydgu3wVFXTbMfsvF2MSErpy9TDVa092n4q6adYwJaWg== dependencies: - loader-utils "^1.4.0" - yaml "^1.8.3" + javascript-stringify "^2.0.1" + loader-utils "^2.0.0" + yaml "^2.0.0" -yaml@1.10.2, yaml@^1.10.0, yaml@^1.8.3: +yaml@1.10.2, yaml@^1.10.0: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" + integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" @@ -7506,15 +7270,10 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^15.3.1: version "15.4.1" @@ -7533,31 +7292,18 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.0.0, yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yargs@^17.0.0, yargs@^17.0.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== +yargs@^17.0.0, yargs@^17.3.1: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^21.0.0" + yargs-parser "^21.1.1" yn@3.1.1: version "3.1.1" @@ -7569,7 +7315,7 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zen-observable-ts@^1.0.0, zen-observable-ts@~1.1.0: +zen-observable-ts@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== From cd0aea925d4c8efbe7624e8ba7fca4bbd852122e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Jan 2023 16:01:49 +0100 Subject: [PATCH 011/878] Downgrade ef core design package to version 6 compatible with .NET 6 --- backend/src/Database.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 19563cdb..53c74d55 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -21,7 +21,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 79b0aa43b0cbc568d81aa7c7b34fdcfb6ea7cec6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Jan 2023 14:26:46 +0100 Subject: [PATCH 012/878] Remove unused styles --- frontend/pages/_app.tsx | 3 - frontend/styles/global.css | 193 ------------------------------------- frontend/styles/vars.css | 5 - 3 files changed, 201 deletions(-) delete mode 100644 frontend/styles/global.css delete mode 100644 frontend/styles/vars.css diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index de090cc0..fa2344e8 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -3,9 +3,6 @@ import { ApolloProvider } from "@apollo/client"; import { useApollo } from "../lib/apollo"; import { CookiesProvider } from "react-cookie"; -import "../styles/vars.css"; -import "../styles/global.css"; - export default function App({ Component, pageProps }: AppProps) { const apolloClient = useApollo(pageProps.initialApolloState); diff --git a/frontend/styles/global.css b/frontend/styles/global.css deleted file mode 100644 index 1d2886df..00000000 --- a/frontend/styles/global.css +++ /dev/null @@ -1,193 +0,0 @@ -/* *, -*:before, -*:after { - box-sizing: inherit; -} - -html { - box-sizing: border-box; - font-size: 14px; - -ms-overflow-style: -ms-autohiding-scrollbar; -} - -body { - background-color: var(--backgroundColor); - -ms-text-size-adjust: 100%; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -.m-0 { - margin: 0 !important; -} - -.mt-0, -.my-0 { - margin-top: 0 !important; -} - -.mr-0, -.mx-0 { - margin-right: 0 !important; -} - -.mb-0, -.my-0 { - margin-bottom: 0 !important; -} - -.ml-0, -.mx-0 { - margin-left: 0 !important; -} - -.m-1 { - margin: 0.25rem !important; -} - -.mt-1, -.my-1 { - margin-top: 0.25rem !important; -} - -.mr-1, -.mx-1 { - margin-right: 0.25rem !important; -} - -.mb-1, -.my-1 { - margin-bottom: 0.25rem !important; -} - -.ml-1, -.mx-1 { - margin-left: 0.25rem !important; -} - -.m-2 { - margin: 0.5rem !important; -} - -.mt-2, -.my-2 { - margin-top: 0.5rem !important; -} - -.mr-2, -.mx-2 { - margin-right: 0.5rem !important; -} - -.mb-2, -.my-2 { - margin-bottom: 0.5rem !important; -} - -.ml-2, -.mx-2 { - margin-left: 0.5rem !important; -} - -.m-3 { - margin: 1rem !important; -} - -.mt-3, -.my-3 { - margin-top: 1rem !important; -} - -.mr-3, -.mx-3 { - margin-right: 1rem !important; -} - -.mb-3, -.my-3 { - margin-bottom: 1rem !important; -} - -.ml-3, -.mx-3 { - margin-left: 1rem !important; -} - -.m-4 { - margin: 1.5rem !important; -} - -.mt-4, -.my-4 { - margin-top: 1.5rem !important; -} - -.mr-4, -.mx-4 { - margin-right: 1.5rem !important; -} - -.mb-4, -.my-4 { - margin-bottom: 1.5rem !important; -} - -.ml-4, -.mx-4 { - margin-left: 1.5rem !important; -} - -.m-5 { - margin: 3rem !important; -} - -.mt-5, -.my-5 { - margin-top: 3rem !important; -} - -.mr-5, -.mx-5 { - margin-right: 3rem !important; -} - -.mb-5, -.my-5 { - margin-bottom: 3rem !important; -} - -.ml-5, -.mx-5 { - margin-left: 3rem !important; -} - -.logo { - display: flex; - align-items: center; - margin-right: 1rem; - font-size: 1.25rem; - white-space: nowrap; - color: var(--primaryColor); - justify-content: center; -} - -.logo > svg { - fill: var(--primaryColor); -} - -.text-disabled { - color: var(--textColorSecondary) !important; -} */ diff --git a/frontend/styles/vars.css b/frontend/styles/vars.css deleted file mode 100644 index e6d19fd5..00000000 --- a/frontend/styles/vars.css +++ /dev/null @@ -1,5 +0,0 @@ -/* :root { - --backgroundColor: #f7f7f7; - --primaryColor: #191a1b; - --textColorSecondary: rgba(0, 0, 0, 0.45); -} */ From dd7207d698acad57653edd782997a06af514ab6e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Jan 2023 14:33:52 +0100 Subject: [PATCH 013/878] Update frontend packages (they change rapidly) --- frontend/yarn.lock | 943 +++++++++++++++++++++++---------------------- 1 file changed, 472 insertions(+), 471 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 378cf9c2..04e2f8f6 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -1344,109 +1344,109 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.3.1.tgz#3e3f876e4e47616ea3b1464b9fbda981872e9583" - integrity sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg== +"@jest/console@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.0.tgz#ed2e5bc783791c4be75d0054d1bdf66a46deb163" + integrity sha512-xpXud7e/8zo4syxQlAMDz+EQiFsf8/zXDPslBYm+UaSJ5uGTKQHhbSHfECp7Fw1trQtopjYumeved0n3waijhQ== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.3.1" - jest-util "^29.3.1" + jest-message-util "^29.4.0" + jest-util "^29.4.0" slash "^3.0.0" -"@jest/core@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.3.1.tgz#bff00f413ff0128f4debec1099ba7dcd649774a1" - integrity sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw== - dependencies: - "@jest/console" "^29.3.1" - "@jest/reporters" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" +"@jest/core@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.0.tgz#23cf275372c51d1191a8cd9f36f8d5a47a4e9041" + integrity sha512-E7oCMcENobBFwQXYjnN2IsuUSpRo5jSv7VYk6O9GyQ5kVAfVSS8819I4W5iCCYvqD6+1TzyzLpeEdZEik81kNw== + dependencies: + "@jest/console" "^29.4.0" + "@jest/reporters" "^29.4.0" + "@jest/test-result" "^29.4.0" + "@jest/transform" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.2.0" - jest-config "^29.3.1" - jest-haste-map "^29.3.1" - jest-message-util "^29.3.1" + jest-changed-files "^29.4.0" + jest-config "^29.4.0" + jest-haste-map "^29.4.0" + jest-message-util "^29.4.0" jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-resolve-dependencies "^29.3.1" - jest-runner "^29.3.1" - jest-runtime "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" - jest-watcher "^29.3.1" + jest-resolve "^29.4.0" + jest-resolve-dependencies "^29.4.0" + jest-runner "^29.4.0" + jest-runtime "^29.4.0" + jest-snapshot "^29.4.0" + jest-util "^29.4.0" + jest-validate "^29.4.0" + jest-watcher "^29.4.0" micromatch "^4.0.4" - pretty-format "^29.3.1" + pretty-format "^29.4.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.3.1.tgz#eb039f726d5fcd14698acd072ac6576d41cfcaa6" - integrity sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag== +"@jest/environment@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.0.tgz#b15d7bfc873d6348dfd7323e50083d2c1e067750" + integrity sha512-ocl1VGDcZHfHnYLTqkBY7yXme1bF4x0BevJ9wb6y0sLOSyBCpp8L5fEASChB+wU53WMrIK6kBfGt+ZYoM2kcdw== dependencies: - "@jest/fake-timers" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/fake-timers" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" - jest-mock "^29.3.1" + jest-mock "^29.4.0" -"@jest/expect-utils@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" - integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g== +"@jest/expect-utils@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.0.tgz#97819d0da7027792888d9d2f1a41443be0baef80" + integrity sha512-w/JzTYIqjmPFIM5OOQHF9CawFx2daw1256Nzj4ZqWX96qRKbCq9WYRVqdySBKHHzuvsXLyTDIF6y61FUyrhmwg== dependencies: jest-get-type "^29.2.0" -"@jest/expect@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.3.1.tgz#456385b62894349c1d196f2d183e3716d4c6a6cd" - integrity sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg== +"@jest/expect@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.0.tgz#3d47dbb1c650a0833b8263ca354818ca0665adbe" + integrity sha512-IiDZYQ/Oi94aBT0nKKKRvNsB5JTyHoGb+G3SiGoDxz90JfL7SLx/z5IjB0fzBRzy7aLFQOCbVJlaC2fIgU6Y9Q== dependencies: - expect "^29.3.1" - jest-snapshot "^29.3.1" + expect "^29.4.0" + jest-snapshot "^29.4.0" -"@jest/fake-timers@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.3.1.tgz#b140625095b60a44de820876d4c14da1aa963f67" - integrity sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A== +"@jest/fake-timers@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.0.tgz#9a2409cae63eb1d0122edc21783ef88c67e6face" + integrity sha512-8sitzN2QrhDwEwH3kKcMMgrv/UIkmm9AUgHixmn4L++GQ0CqVTIztm3YmaIQooLmW3O4GhizNTTCyq3iLbWcMw== dependencies: - "@jest/types" "^29.3.1" - "@sinonjs/fake-timers" "^9.1.2" + "@jest/types" "^29.4.0" + "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.3.1" - jest-mock "^29.3.1" - jest-util "^29.3.1" + jest-message-util "^29.4.0" + jest-mock "^29.4.0" + jest-util "^29.4.0" -"@jest/globals@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.3.1.tgz#92be078228e82d629df40c3656d45328f134a0c6" - integrity sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q== +"@jest/globals@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.0.tgz#b3dd853af92bb6b6156e246475f46fb8f3831785" + integrity sha512-Q64ZRgGMVL40RcYTfD2GvyjK7vJLPSIvi8Yp3usGPNPQ3SCW+UCY9KEH6+sVtBo8LzhcjtCXuZEd7avnj/T0mQ== dependencies: - "@jest/environment" "^29.3.1" - "@jest/expect" "^29.3.1" - "@jest/types" "^29.3.1" - jest-mock "^29.3.1" + "@jest/environment" "^29.4.0" + "@jest/expect" "^29.4.0" + "@jest/types" "^29.4.0" + jest-mock "^29.4.0" -"@jest/reporters@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.3.1.tgz#9a6d78c109608e677c25ddb34f907b90e07b4310" - integrity sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA== +"@jest/reporters@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.0.tgz#ec17751cc687fb845fa1978c03fdbc69d34ac462" + integrity sha512-FjJwrD1XOQq/AXKrvnOSf0RgAs6ziUuGKx8+/R53Jscc629JIhg7/m241gf1shUm/fKKxoHd7aCexcg7kxvkWQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/console" "^29.4.0" + "@jest/test-result" "^29.4.0" + "@jest/transform" "^29.4.0" + "@jest/types" "^29.4.0" "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" @@ -1459,20 +1459,20 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - jest-worker "^29.3.1" + jest-message-util "^29.4.0" + jest-util "^29.4.0" + jest-worker "^29.4.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== +"@jest/schemas@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.0.tgz#0d6ad358f295cc1deca0b643e6b4c86ebd539f17" + integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== dependencies: - "@sinclair/typebox" "^0.24.1" + "@sinclair/typebox" "^0.25.16" "@jest/source-map@^29.2.0": version "29.2.0" @@ -1483,24 +1483,24 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.3.1.tgz#92cd5099aa94be947560a24610aa76606de78f50" - integrity sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw== +"@jest/test-result@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.0.tgz#64f7bd518d6d2a6662c40569e208e030a370f351" + integrity sha512-EtRklzjpddZU/aBVxJqqejfzfOcnehmjNXufs6u6qwd05kkhXpAPhZdt8bLlQd7cA2nD+JqZQ5Dx9NX5Jh6mjA== dependencies: - "@jest/console" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/console" "^29.4.0" + "@jest/types" "^29.4.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz#fa24b3b050f7a59d48f7ef9e0b782ab65123090d" - integrity sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA== +"@jest/test-sequencer@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.0.tgz#8041176fccae0f7b86055950461d158833a79b76" + integrity sha512-pEwIgdfvEgF2lBOYX3DVn3SrvsAZ9FXCHw7+C6Qz87HnoDGQwbAselhWLhpgbxDjs6RC9QUJpFnrLmM5uwZV+g== dependencies: - "@jest/test-result" "^29.3.1" + "@jest/test-result" "^29.4.0" graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" + jest-haste-map "^29.4.0" slash "^3.0.0" "@jest/transform@^27.1.0": @@ -1524,26 +1524,26 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.3.1.tgz#1e6bd3da4af50b5c82a539b7b1f3770568d6e36d" - integrity sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug== +"@jest/transform@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.0.tgz#73ddd9bf8599d285af09e7e3fef730c17a2fd965" + integrity sha512-hDjw3jz4GnvbyLMgcFpC9/34QcUhVIzJkBqz7o+3AhgfhGRzGuQppuLf5r/q7lDAAyJ6jzL+SFG7JGsScHOcLQ== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" + jest-haste-map "^29.4.0" jest-regex-util "^29.2.0" - jest-util "^29.3.1" + jest-util "^29.4.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^4.0.1" + write-file-atomic "^5.0.0" "@jest/types@^27.1.0", "@jest/types@^27.5.1": version "27.5.1" @@ -1556,12 +1556,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3" - integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA== +"@jest/types@^29.4.0": + version "29.4.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.0.tgz#569115f2438cacf3cff92521c7d624fbb683de3d" + integrity sha512-1S2Dt5uQp7R0bGY/L2BpuwCSji7v12kY3o8zqwlkbYBmOY956SKk+zOWqmfhHSINegiAVqOXydAYuWpzX6TYsQ== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.4.0" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1616,82 +1616,82 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.4.tgz#ed91156ee48791ad85992d95d8e1f46a740c0d5e" - integrity sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A== +"@next/env@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.5.tgz#72b24d3726a6b752be6b7b9a937f7af31b3ce410" + integrity sha512-0Ry4NhJy6qLbXhvxPRUQ1H6RzgtryGdUto7hfgAK0Iw/bScgeVjwLZdfhm2iT7qsOS32apo9cWzLCxjc6iGPsA== -"@next/eslint-plugin-next@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.4.tgz#7dd5025d40b9564fc2cc1ea4aa9661a9552ec875" - integrity sha512-a/T30+7Q1scom5t3L+wEBkYzCa+bhT/3DTxzxlNy4Xckw2InzcckQGeIi/larDgh5r2fSSJswhYAZEcKtuJiig== +"@next/eslint-plugin-next@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.5.tgz#3eebea0835d2ec549a7b4b8976bf1d7796cca632" + integrity sha512-3kvLTX35bOWOCKU8KY74Ygczc55Qk/kB2TQy0tH7Rti6hzZ6Aij7WQ8zHdIVjmnlD0n/zXWXrIf5y56RKcLQkQ== dependencies: glob "7.1.7" -"@next/swc-android-arm-eabi@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz#7d258554b0812f693e91d1bfc940d0e386b394f7" - integrity sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA== - -"@next/swc-android-arm64@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz#03572661993ba554c4ac6a2b99c35b5062727489" - integrity sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA== - -"@next/swc-darwin-arm64@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz#10189b4753b43941ffb0f396ee878176c8b4b662" - integrity sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg== - -"@next/swc-darwin-x64@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz#e6574b43699bfe74e38dd620fdaecd7a1676aa0a" - integrity sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA== - -"@next/swc-freebsd-x64@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz#96e77d986bd22b08ce12d3a5fc884494328b94b2" - integrity sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw== - -"@next/swc-linux-arm-gnueabihf@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz#91b035a6c88e8d583f8b9aef465b64d05ecd1f95" - integrity sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw== - -"@next/swc-linux-arm64-gnu@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz#15726af0ce29450ffde593e993b0ec1beba75154" - integrity sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw== - -"@next/swc-linux-arm64-musl@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz#74f449a2bb7b6f6fdaa67df0fa3665ee99a826b8" - integrity sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA== - -"@next/swc-linux-x64-gnu@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz#dfaed86d84f11ce6c14c267ead267eab7cbea66d" - integrity sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA== - -"@next/swc-linux-x64-musl@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz#6f5f184967ba00379ac179546a9575bb7aff9c21" - integrity sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q== - -"@next/swc-win32-arm64-msvc@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz#ccfa61a8ea56b55d7251f32b6c518ed0962d2923" - integrity sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ== - -"@next/swc-win32-ia32-msvc@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz#8d164ce4de9eeaf3400ad4becdd653f68f0d9527" - integrity sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw== - -"@next/swc-win32-x64-msvc@13.1.4": - version "13.1.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz#0eab48201b90e862c76add99c4feff00bfb0fb67" - integrity sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g== +"@next/swc-android-arm-eabi@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.5.tgz#0ea6f76754562ab8092efe7e492906790261b7cb" + integrity sha512-QAEf3YM9U0qWVQTxgF3Tsh4OeCN1i9Smsf6cVlwZsPzoLyj2nQ879joCoN+ONqDknkBgG6OG/ajefywL3jw9Cg== + +"@next/swc-android-arm64@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.5.tgz#8d4a91d77811c6c7057a41f4f32ecee1ac963981" + integrity sha512-ZmtGPTghRuT5YKL0nNcC2bBVSiG1O0is16eIZ2rWSP/hRW64ZCcAew6pxw2rihntNp22UfequjSTHd91WE/tyQ== + +"@next/swc-darwin-arm64@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.5.tgz#3f206d22201c6500acce3a9274a807fb07878565" + integrity sha512-aeFXK+M/zmG/CNdMJ0tGNs0MWcLueUe7vZ2V6fa+2yz/ZgYJLI7fEfFvVh1p1yBMzupSbZDowvMuCSFTaeg3MA== + +"@next/swc-darwin-x64@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.5.tgz#3b0775ed190a6b35cd0ba61510dcd133dcb8c4bf" + integrity sha512-6mPX0GNRg8NzjV70at8I8pD9YBnPHDpxJCoMuIqysdTjtQhd09Xk6GUhquNhp1kEJzzVk7OW5l2ch4XIJjtY3A== + +"@next/swc-freebsd-x64@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.5.tgz#a9f2b39434b52867860afbd32638f9742c45c82a" + integrity sha512-nR4a/SNblG0w8hhYRflTZjk4yD99ld18w/FCftw99ziw8sgciBlOXRICJIiRIaMRU8UH7QLSgBOQVnfNcVNKMA== + +"@next/swc-linux-arm-gnueabihf@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.5.tgz#4ae5dcdea5aa5cb6b9ce14bbf86e3afe7dc3df1a" + integrity sha512-EzkltCVKg3gUzamoeKPhGeSgXTTLAhSzc7v/+g1Y+HQa7JKMrlzdRkrJf+H4LJXcz7lnxgNKHGRyZBSXnmJKJw== + +"@next/swc-linux-arm64-gnu@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.5.tgz#92d816a430390642aa1ce1969a255b6a0b2d9833" + integrity sha512-E7HMkdoxStmTUJU4KzBUU4vZ5DHT4Gd327tC3KFZS5lda0NRerJAOCfsRg+fBj22FvCb1UWsX6XI+weL6xhyeQ== + +"@next/swc-linux-arm64-musl@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.5.tgz#b44ba6866802ee0f6d655e7184b7c2f29485219f" + integrity sha512-qlO0Fd3GQwJS6YpbF9NyL5NGHVZ43dKtZDC/jP4vdeMIYDtSu13HcY/nmA1NdW+RpMwDxSCpx4WKsCCEZGIX8Q== + +"@next/swc-linux-x64-gnu@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.5.tgz#f70afe2654d8ca6e488964ed0ebef11f38d56675" + integrity sha512-GftSBFAay2nocGl+KNqFsj6EVSvomaM/bp86hzezbKsTwQmu76PjOCVcejI1gE+4k7f5zPDgCuorF6F04BV0HQ== + +"@next/swc-linux-x64-musl@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.5.tgz#6e4aa45a6ee1cea7e6c3295dfde69250afd4c714" + integrity sha512-UD+3lxU4yuAjd+uBkCDfBpAcbGAVfEcE8mX/efIxUGIImmzN0QzgTHYEpKFnY3Lxu02dIBcwQRT3Q5mfO4obng== + +"@next/swc-win32-arm64-msvc@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.5.tgz#1ef6d453b021b26ed2d0e317c1cb658eb57d66c2" + integrity sha512-uzsvkQY+K3EbL+97IUHPWZPwjsCmCkdH/O5Cf9wCnh0k0gaj7ob1mGKqr1vNNak+9U7HloGwuHcXnZpijWSP7w== + +"@next/swc-win32-ia32-msvc@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.5.tgz#a708ed168a5d83b28519b4fcba396ab329c1a2b0" + integrity sha512-v0NaC1w8mPf620GlJaHBdEm3dm4G4AEQMasDqjzQvo0yCRrvtvzMgCIe8MocBxFHzaF6868NybMqvumxP5YxEg== + +"@next/swc-win32-x64-msvc@13.1.5": + version "13.1.5" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.5.tgz#8c7bef3dc41e624050eecb336cbd944a91fa7198" + integrity sha512-IZHwvd649ccbWyLCfu92IXEpR250NpmBkaRelPV+WVm4jrd62FKRFCNdqdCXq6TrEg9wN8cK4YG8tm44uEZqLA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1812,24 +1812,24 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== -"@sinclair/typebox@^0.24.1": - version "0.24.51" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" - integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== +"@sinclair/typebox@^0.25.16": + version "0.25.21" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" + integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== -"@sinonjs/commons@^1.7.0": - version "1.8.6" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" - integrity sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ== +"@sinonjs/commons@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" + integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== +"@sinonjs/fake-timers@^10.0.2": + version "10.0.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" + integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^2.0.0" "@swc/helpers@0.4.14": version "0.4.14" @@ -2061,47 +2061,47 @@ integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== "@typescript-eslint/parser@^5.42.0": - version "5.48.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.48.2.tgz#c9edef2a0922d26a37dba03be20c5fff378313b3" - integrity sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw== + version "5.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.49.0.tgz#d699734b2f20e16351e117417d34a2bc9d7c4b90" + integrity sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg== dependencies: - "@typescript-eslint/scope-manager" "5.48.2" - "@typescript-eslint/types" "5.48.2" - "@typescript-eslint/typescript-estree" "5.48.2" + "@typescript-eslint/scope-manager" "5.49.0" + "@typescript-eslint/types" "5.49.0" + "@typescript-eslint/typescript-estree" "5.49.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.48.2": - version "5.48.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz#bb7676cb78f1e94921eaab637a4b5d596f838abc" - integrity sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw== +"@typescript-eslint/scope-manager@5.49.0": + version "5.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz#81b5d899cdae446c26ddf18bd47a2f5484a8af3e" + integrity sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ== dependencies: - "@typescript-eslint/types" "5.48.2" - "@typescript-eslint/visitor-keys" "5.48.2" + "@typescript-eslint/types" "5.49.0" + "@typescript-eslint/visitor-keys" "5.49.0" -"@typescript-eslint/types@5.48.2": - version "5.48.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.48.2.tgz#635706abb1ec164137f92148f06f794438c97b8e" - integrity sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA== +"@typescript-eslint/types@5.49.0": + version "5.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.49.0.tgz#ad66766cb36ca1c89fcb6ac8b87ec2e6dac435c3" + integrity sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg== -"@typescript-eslint/typescript-estree@5.48.2": - version "5.48.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz#6e206b462942b32383582a6c9251c05021cc21b0" - integrity sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg== +"@typescript-eslint/typescript-estree@5.49.0": + version "5.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz#ebd6294c0ea97891fce6af536048181e23d729c8" + integrity sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA== dependencies: - "@typescript-eslint/types" "5.48.2" - "@typescript-eslint/visitor-keys" "5.48.2" + "@typescript-eslint/types" "5.49.0" + "@typescript-eslint/visitor-keys" "5.49.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.48.2": - version "5.48.2" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz#c247582a0bcce467461d7b696513bf9455000060" - integrity sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ== +"@typescript-eslint/visitor-keys@5.49.0": + version "5.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz#2561c4da3f235f5c852759bf6c5faec7524f90fe" + integrity sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg== dependencies: - "@typescript-eslint/types" "5.48.2" + "@typescript-eslint/types" "5.49.0" eslint-visitor-keys "^3.3.0" "@whatwg-node/fetch@0.6.2", "@whatwg-node/fetch@^0.6.0": @@ -2165,9 +2165,9 @@ acorn-walk@^8.1.1: integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^8.4.1, acorn@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" - integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + version "8.8.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== agent-base@6: version "6.0.2" @@ -2412,9 +2412,9 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== axe-core@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.2.tgz#6e566ab2a3d29e415f5115bc0fd2597a5eb3e5e3" - integrity sha512-b1WlTV8+XKLj9gZy2DZXgQiyDp9xkkoe2a6U6UbYccScq2wgH/YwCeI2/Jq2mgo0HzQxqJOjWZBLeA/mqsk5Mg== + version "4.6.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" + integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== axobject-query@^3.1.1: version "3.1.1" @@ -2437,15 +2437,15 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^29.0, babel-jest@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.3.1.tgz#05c83e0d128cd48c453eea851482a38782249f44" - integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA== +babel-jest@^29.0, babel-jest@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.0.tgz#20b18b69a321125429ff67a383b94c6bf552d1ec" + integrity sha512-M61cGPg4JBashDvIzKoIV/y95mSF6x3ome7CMEaszUTHD4uo6dtC6Nln+fvRTspYNtwy8lDHl5lmoTBSNY/a+g== dependencies: - "@jest/transform" "^29.3.1" + "@jest/transform" "^29.4.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.2.0" + babel-preset-jest "^29.4.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2471,10 +2471,10 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094" - integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA== +babel-plugin-jest-hoist@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.0.tgz#3fd3dfcedf645932df6d0c9fc3d9a704dd860248" + integrity sha512-a/sZRLQJEmsmejQ2rPEUe35nO1+C9dc9O1gplH1SXmJxveQSRUYdBk8yGZG/VOUuZs1u2aHZJusEGoRMbhhwCg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2545,12 +2545,12 @@ babel-preset-jest@^27.0.6: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc" - integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA== +babel-preset-jest@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.0.tgz#c2b03c548b02dea0a18ae21d5759c136f9251ee4" + integrity sha512-fUB9vZflUSM3dO/6M2TCAepTzvA4VkOvl67PjErcrQMGt9Eve7uazaeyCZ2th3UtI7ljpiBJES0F7A1vBRsLZA== dependencies: - babel-plugin-jest-hoist "^29.2.0" + babel-plugin-jest-hoist "^29.4.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2676,9 +2676,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406: - version "1.0.30001447" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001447.tgz#ef1f39ae38d839d7176713735a8e467a0a2523bd" - integrity sha512-bdKU1BQDPeEXe9A39xJnGtY0uRq/z5osrnXUw0TcK+EYno45Y+U7QU9HhHEyzvMDffpYadFXi3idnSNkcwLkTw== + version "1.0.30001448" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz#ca7550b1587c92a392a2b377cd9c508b3b4395bf" + integrity sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA== capital-case@^1.0.4: version "1.0.4" @@ -3326,11 +3326,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.1.4: - version "13.1.4" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.4.tgz#f77d3f62e1b62564bd5c88c27bb5a00eb786c565" - integrity sha512-r7n9V4/kkiDDVFfBwI3tviGUV/jUzGI0lY3JefxceYaU18gdk2kMgNPyhHobowu1+yHZpZi8iEzRtzeTrtGRLg== + version "13.1.5" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.5.tgz#38daeaa1463197ef5b6cec9136ce6838ea776cf3" + integrity sha512-7FqkjkvGCQfvYUiPTFRiRYPR1uI6Ew+4f4mVp16lLSWcaChtWoZxQCZHM5n0yxzKKVmuEg1aM4uvDQfSXSjTww== dependencies: - "@next/eslint-plugin-next" "13.1.4" + "@next/eslint-plugin-next" "13.1.5" "@rushstack/eslint-patch" "^1.1.3" "@typescript-eslint/parser" "^5.42.0" eslint-import-resolver-node "^0.3.6" @@ -3571,16 +3571,16 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6" - integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA== +expect@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.0.tgz#e2d58a73bf46399deac7db6ec16842827525ce35" + integrity sha512-pzaAwjBgLEVxBh6ZHiqb9Wv3JYuv6m8ntgtY7a48nS+2KbX0EJkPS3FQlKiTZNcqzqJHNyQsfjqN60w1hPUBfQ== dependencies: - "@jest/expect-utils" "^29.3.1" + "@jest/expect-utils" "^29.4.0" jest-get-type "^29.2.0" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-util "^29.3.1" + jest-matcher-utils "^29.4.0" + jest-message-util "^29.4.0" + jest-util "^29.4.0" external-editor@^3.0.3: version "3.1.0" @@ -4513,94 +4513,94 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289" - integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA== +jest-changed-files@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.0.tgz#ac2498bcd394228f7eddcadcf928b3583bf2779d" + integrity sha512-rnI1oPxgFghoz32Y8eZsGJMjW54UlqT17ycQeCEktcxxwqqKdlj9afl8LNeO0Pbu+h2JQHThQP0BzS67eTRx4w== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a" - integrity sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg== +jest-circus@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.0.tgz#08fec87eb15632fba74e93cf069ef85559acd813" + integrity sha512-/pFBaCeLzCavRWyz14JwFgpZgPpEZdS6nPnREhczbHl2wy2UezvYcVp5akVFfUmBaA4ThAUp0I8cpgkbuNOm3g== dependencies: - "@jest/environment" "^29.3.1" - "@jest/expect" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/environment" "^29.4.0" + "@jest/expect" "^29.4.0" + "@jest/test-result" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.3.1" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-runtime "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" + jest-each "^29.4.0" + jest-matcher-utils "^29.4.0" + jest-message-util "^29.4.0" + jest-runtime "^29.4.0" + jest-snapshot "^29.4.0" + jest-util "^29.4.0" p-limit "^3.1.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.3.1.tgz#e89dff427db3b1df50cea9a393ebd8640790416d" - integrity sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ== +jest-cli@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.0.tgz#63f34fc3b6f499a69337b2e5964661d22cead9e0" + integrity sha512-YUkICcxjUd864VOzbfQEi2qd2hIIOd9bRF7LJUNyhWb3Khh3YKrbY0LWwoZZ4WkvukiNdvQu0Z4s6zLsY4hYfg== dependencies: - "@jest/core" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/core" "^29.4.0" + "@jest/test-result" "^29.4.0" + "@jest/types" "^29.4.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" + jest-config "^29.4.0" + jest-util "^29.4.0" + jest-validate "^29.4.0" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.3.1.tgz#0bc3dcb0959ff8662957f1259947aedaefb7f3c6" - integrity sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg== +jest-config@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.0.tgz#08065bcfc7c2ccfd3ca9f8bb412eb366d73fd5a2" + integrity sha512-jtgd72nN4Mob4Oego3N/pLRVfR2ui1hv+yO6xR/SUi5G7NtZ/grr95BJ1qRSDYZshuA0Jw57fnttZHZKb04+CA== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.3.1" - "@jest/types" "^29.3.1" - babel-jest "^29.3.1" + "@jest/test-sequencer" "^29.4.0" + "@jest/types" "^29.4.0" + babel-jest "^29.4.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.3.1" - jest-environment-node "^29.3.1" + jest-circus "^29.4.0" + jest-environment-node "^29.4.0" jest-get-type "^29.2.0" jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-runner "^29.3.1" - jest-util "^29.3.1" - jest-validate "^29.3.1" + jest-resolve "^29.4.0" + jest-runner "^29.4.0" + jest-util "^29.4.0" + jest-validate "^29.4.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527" - integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw== +jest-diff@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.0.tgz#9c75dcef5872c8889bfcb78bc9571a0e4e9bd8f6" + integrity sha512-s8KNvFx8YgdQ4fn2YLDQ7N6kmVOP68dUDVJrCHNsTc3UM5jcmyyFeYKL8EPWBQbJ0o0VvDGbWp8oYQ1nsnqnWw== dependencies: chalk "^4.0.0" diff-sequences "^29.3.1" jest-get-type "^29.2.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" jest-docblock@^29.2.0: version "29.2.0" @@ -4609,28 +4609,28 @@ jest-docblock@^29.2.0: dependencies: detect-newline "^3.0.0" -jest-each@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.3.1.tgz#bc375c8734f1bb96625d83d1ca03ef508379e132" - integrity sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA== +jest-each@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.0.tgz#1b6f018529b26f52851123075df4fa297b5b859b" + integrity sha512-LTOvB8JDVFjrwXItyQiyLuDYy5PMApGLLzbfIYR79QLpeohS0bcS6j2HjlWuRGSM8QQQyp+ico59Blv+Jx3fMw== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" chalk "^4.0.0" jest-get-type "^29.2.0" - jest-util "^29.3.1" - pretty-format "^29.3.1" + jest-util "^29.4.0" + pretty-format "^29.4.0" -jest-environment-node@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74" - integrity sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag== +jest-environment-node@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.0.tgz#b19ae4cbf71199ed077a399de1e2cb0e52dbd936" + integrity sha512-WVveE3fYSH6FhDtZdvXhFKeLsDRItlQgnij+HQv6ZKxTdT1DB5O0sHXKCEC3K5mHraMs1Kzn4ch9jXC7H4L4wA== dependencies: - "@jest/environment" "^29.3.1" - "@jest/fake-timers" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/environment" "^29.4.0" + "@jest/fake-timers" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" - jest-mock "^29.3.1" - jest-util "^29.3.1" + jest-mock "^29.4.0" + jest-util "^29.4.0" jest-get-type@^29.2.0: version "29.2.0" @@ -4657,66 +4657,66 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.3.1.tgz#af83b4347f1dae5ee8c2fb57368dc0bb3e5af843" - integrity sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A== +jest-haste-map@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.0.tgz#139d6bd262f3f74894f23933ee4046af7e770ab0" + integrity sha512-m/pIEfoK0HoJz4c9bkgS5F9CXN2AM22eaSmUcmqTpadRlNVBOJE2CwkgaUzbrNn5MuAqTV1IPVYwWwjHNnk8eA== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.2.0" - jest-util "^29.3.1" - jest-worker "^29.3.1" + jest-util "^29.4.0" + jest-worker "^29.4.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz#95336d020170671db0ee166b75cd8ef647265518" - integrity sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA== +jest-leak-detector@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.0.tgz#5f1079556c244cf6f5f281f5c5134ea2a07ad28e" + integrity sha512-fEGHS6ijzgSv5exABkCecMHNmyHcV52+l39ZsxuwfxmQMp43KBWJn2/Fwg8/l4jTI9uOY9jv8z1dXGgL0PHFjA== dependencies: jest-get-type "^29.2.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" -jest-matcher-utils@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572" - integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ== +jest-matcher-utils@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.0.tgz#c2f804f95152216c8b80afbe73d82ae0ba89f652" + integrity sha512-pU4OjBn96rDdRIaPUImbPiO2ETyRVzkA1EZVu9AxBDv/XPDJ7JWfkb6IiDT5jwgicaPHMrB/fhVa6qjG6potfA== dependencies: chalk "^4.0.0" - jest-diff "^29.3.1" + jest-diff "^29.4.0" jest-get-type "^29.2.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" -jest-message-util@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.3.1.tgz#37bc5c468dfe5120712053dd03faf0f053bd6adb" - integrity sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA== +jest-message-util@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.0.tgz#60d3f3dd6b5ef08ec9b698f434fbbafdb0af761d" + integrity sha512-0FvobqymmhE9pDEifvIcni9GeoKLol8eZspzH5u41g1wxYtLS60a9joT95dzzoCgrKRidNz64eaAXyzaULV8og== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.3.1" + pretty-format "^29.4.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.3.1.tgz#60287d92e5010979d01f218c6b215b688e0f313e" - integrity sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA== +jest-mock@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.0.tgz#efb5d99da5e4548ea02d505c89aafdd06d899cb8" + integrity sha512-+ShT5i+hcu/OFQRV0f/V/YtwpdFcHg64JZ9A8b40JueP+X9HNrZAYGdkupGIzsUK8AucecxCt4wKauMchxubLQ== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@types/node" "*" - jest-util "^29.3.1" + jest-util "^29.4.0" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -4733,81 +4733,82 @@ jest-regex-util@^29.2.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== -jest-resolve-dependencies@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz#a6a329708a128e68d67c49f38678a4a4a914c3bf" - integrity sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA== +jest-resolve-dependencies@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.0.tgz#d8c9dce9ad8b193375b4993c4a659adf2d379b84" + integrity sha512-hxfC84trREyULSj1Cm+fMjnudrrI2dVQ04COjZRcjCZ97boJlPtfJ+qrl/pN7YXS2fnu3wTHEc3LO094pngL6A== dependencies: jest-regex-util "^29.2.0" - jest-snapshot "^29.3.1" + jest-snapshot "^29.4.0" -jest-resolve@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.3.1.tgz#9a4b6b65387a3141e4a40815535c7f196f1a68a7" - integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw== +jest-resolve@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.0.tgz#dbf00b2fb992d37e4954feb7ee6b5391ca8fc6a9" + integrity sha512-g7k7l53T+uC9Dp1mbHyDNkcCt0PMku6Wcfpr1kcMLwOHmM3vucKjSM5+DSa1r4vlDZojh8XH039J3z4FKmtTSw== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" + jest-haste-map "^29.4.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.3.1" - jest-validate "^29.3.1" + jest-util "^29.4.0" + jest-validate "^29.4.0" resolve "^1.20.0" - resolve.exports "^1.1.0" + resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.3.1.tgz#a92a879a47dd096fea46bb1517b0a99418ee9e2d" - integrity sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA== - dependencies: - "@jest/console" "^29.3.1" - "@jest/environment" "^29.3.1" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" +jest-runner@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.0.tgz#9f83f20e9e88d2c5e9b7db1526cef901c1c0fe98" + integrity sha512-4zpcv0NOiJleqT0NAs8YcVbK8MhVRc58CBBn9b0Exc8VPU9GKI+DbzDUZqJYdkJhJSZFy2862l/F6hAqIow1hg== + dependencies: + "@jest/console" "^29.4.0" + "@jest/environment" "^29.4.0" + "@jest/test-result" "^29.4.0" + "@jest/transform" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" jest-docblock "^29.2.0" - jest-environment-node "^29.3.1" - jest-haste-map "^29.3.1" - jest-leak-detector "^29.3.1" - jest-message-util "^29.3.1" - jest-resolve "^29.3.1" - jest-runtime "^29.3.1" - jest-util "^29.3.1" - jest-watcher "^29.3.1" - jest-worker "^29.3.1" + jest-environment-node "^29.4.0" + jest-haste-map "^29.4.0" + jest-leak-detector "^29.4.0" + jest-message-util "^29.4.0" + jest-resolve "^29.4.0" + jest-runtime "^29.4.0" + jest-util "^29.4.0" + jest-watcher "^29.4.0" + jest-worker "^29.4.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.3.1.tgz#21efccb1a66911d6d8591276a6182f520b86737a" - integrity sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A== +jest-runtime@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.0.tgz#d4f3300e65888ea8e045bc13c5a96edd01cf9f49" + integrity sha512-2zumwaGXsIuSF92Ui5Pn5hZV9r7AHMclfBLikrXSq87/lHea9anQ+mC+Cjz/DYTbf/JMjlK1sjZRh8K3yYNvWg== dependencies: - "@jest/environment" "^29.3.1" - "@jest/fake-timers" "^29.3.1" - "@jest/globals" "^29.3.1" + "@jest/environment" "^29.4.0" + "@jest/fake-timers" "^29.4.0" + "@jest/globals" "^29.4.0" "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/test-result" "^29.4.0" + "@jest/transform" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.3.1" - jest-message-util "^29.3.1" - jest-mock "^29.3.1" + jest-haste-map "^29.4.0" + jest-message-util "^29.4.0" + jest-mock "^29.4.0" jest-regex-util "^29.2.0" - jest-resolve "^29.3.1" - jest-snapshot "^29.3.1" - jest-util "^29.3.1" + jest-resolve "^29.4.0" + jest-snapshot "^29.4.0" + jest-util "^29.4.0" + semver "^7.3.5" slash "^3.0.0" strip-bom "^4.0.0" @@ -4819,10 +4820,10 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.3.1.tgz#17bcef71a453adc059a18a32ccbd594b8cc4e45e" - integrity sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA== +jest-snapshot@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.0.tgz#c839f6b84d50b917691ddf6a3cef9f0852b3d0e7" + integrity sha512-UnK3MhdEWrQ2J6MnlKe51tvN5FjRUBQnO4m1LPlDx61or3w9+cP/U0x9eicutgunu/QzE4WC82jj6CiGIAFYzw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" @@ -4830,23 +4831,23 @@ jest-snapshot@^29.3.1: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.3.1" - "@jest/transform" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/expect-utils" "^29.4.0" + "@jest/transform" "^29.4.0" + "@jest/types" "^29.4.0" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.3.1" + expect "^29.4.0" graceful-fs "^4.2.9" - jest-diff "^29.3.1" + jest-diff "^29.4.0" jest-get-type "^29.2.0" - jest-haste-map "^29.3.1" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-util "^29.3.1" + jest-haste-map "^29.4.0" + jest-matcher-utils "^29.4.0" + jest-message-util "^29.4.0" + jest-util "^29.4.0" natural-compare "^1.4.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" semver "^7.3.5" jest-util@^27.5.1: @@ -4861,42 +4862,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" - integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== +jest-util@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.0.tgz#1f3743c3dda843049623501c7e6f8fa5efdc2c2f" + integrity sha512-lCCwlze7UEV8TpR9ArS8w0cTbcMry5tlBkg7QSc5og5kNyV59dnY2aKHu5fY2k5aDJMQpCUGpvL2w6ZU44lveA== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.3.1.tgz#d56fefaa2e7d1fde3ecdc973c7f7f8f25eea704a" - integrity sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g== +jest-validate@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.0.tgz#5090be16531051edc21bc0892553f7f7fab99bf0" + integrity sha512-EXS7u594nX3aAPBnARxBdJ1eZ1cByV6MWrK0Qpt9lt/BcY0p0yYGp/EGJ8GhdLDQh+RFf8qMt2wzbbVzpj5+Vg== dependencies: - "@jest/types" "^29.3.1" + "@jest/types" "^29.4.0" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^29.2.0" leven "^3.1.0" - pretty-format "^29.3.1" + pretty-format "^29.4.0" -jest-watcher@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.3.1.tgz#3341547e14fe3c0f79f9c3a4c62dbc3fc977fd4a" - integrity sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg== +jest-watcher@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.0.tgz#4f51e6fba4341d965279a5a646adde5104d414f0" + integrity sha512-PnnfLygNKelWOJwpAYlcsQjB+OxRRdckD0qiGmYng4Hkz1ZwK3jvCaJJYiywz2msQn4rBNLdriasJtv7YpWHpA== dependencies: - "@jest/test-result" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/test-result" "^29.4.0" + "@jest/types" "^29.4.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.3.1" + jest-util "^29.4.0" string-length "^4.0.1" jest-worker@^27.5.1: @@ -4908,25 +4909,25 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.3.1.tgz#e9462161017a9bb176380d721cab022661da3d6b" - integrity sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw== +jest-worker@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.0.tgz#fbf6d700c3366c555765938da75990c1e7fdcdcd" + integrity sha512-dICMQ+Q4W0QVMsaQzWlA1FVQhKNz7QcDCOGtbk1GCAd0Lai+wdkQvfmQwL4MjGumineh1xz+6M5oMj3rfWS02A== dependencies: "@types/node" "*" - jest-util "^29.3.1" + jest-util "^29.4.0" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.0: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.3.1.tgz#c130c0d551ae6b5459b8963747fed392ddbde122" - integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA== + version "29.4.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.0.tgz#c476b3d0c58af77f276f53f81d4d97e16105ae37" + integrity sha512-Zfd4UzNxPkSoHRBkg225rBjQNa6pVqbh20MGniAzwaOzYLd+pQUcAwH+WPxSXxKFs+QWYfPYIq9hIVSmdVQmPA== dependencies: - "@jest/core" "^29.3.1" - "@jest/types" "^29.3.1" + "@jest/core" "^29.4.0" + "@jest/types" "^29.4.0" import-local "^3.0.2" - jest-cli "^29.3.1" + jest-cli "^29.4.0" jose@^4.10.0, jose@^4.9.3: version "4.11.2" @@ -5315,9 +5316,9 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next-auth@^4.18.8: - version "4.18.8" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.18.8.tgz#34012ac107702b27779647e8787715cea948812a" - integrity sha512-USP8ihmvB7iCGtkS0+toe2QPrzdbZfkydQZX56JOI9Ft5n/BardOXh3D4wQ2An+vpq/jDKojGlgfv21wVElW7A== + version "4.18.10" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.18.10.tgz#8ce63a671e990dbf5e03e8df5a34859e817f4880" + integrity sha512-j2JVyH7byZ2eqXlOqNLpOO7gOlAkTzblxLZNeZihKCeIabYFHNdAJgUvVahhnHs3pZ4xaa3ZqGQHgi1uE97L9Q== dependencies: "@babel/runtime" "^7.16.3" "@panva/hkdf" "^1.0.1" @@ -5330,29 +5331,29 @@ next-auth@^4.18.8: uuid "^8.3.2" next@^13.1.4: - version "13.1.4" - resolved "https://registry.yarnpkg.com/next/-/next-13.1.4.tgz#fe13ff4a656153b6249c101d79a54569c0cd05e5" - integrity sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog== + version "13.1.5" + resolved "https://registry.yarnpkg.com/next/-/next-13.1.5.tgz#d0c230ccecf18414db78d180537d0474c53fdf73" + integrity sha512-rmpYZFCxxWAi2nJCT9sSqMLGC3cu+Pf689hx9clcyP0KbVIhh/7Dus5QcKrVd/PrAd6AjsuogSRR1mCP7BoYRw== dependencies: - "@next/env" "13.1.4" + "@next/env" "13.1.5" "@swc/helpers" "0.4.14" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm-eabi" "13.1.4" - "@next/swc-android-arm64" "13.1.4" - "@next/swc-darwin-arm64" "13.1.4" - "@next/swc-darwin-x64" "13.1.4" - "@next/swc-freebsd-x64" "13.1.4" - "@next/swc-linux-arm-gnueabihf" "13.1.4" - "@next/swc-linux-arm64-gnu" "13.1.4" - "@next/swc-linux-arm64-musl" "13.1.4" - "@next/swc-linux-x64-gnu" "13.1.4" - "@next/swc-linux-x64-musl" "13.1.4" - "@next/swc-win32-arm64-msvc" "13.1.4" - "@next/swc-win32-ia32-msvc" "13.1.4" - "@next/swc-win32-x64-msvc" "13.1.4" + "@next/swc-android-arm-eabi" "13.1.5" + "@next/swc-android-arm64" "13.1.5" + "@next/swc-darwin-arm64" "13.1.5" + "@next/swc-darwin-x64" "13.1.5" + "@next/swc-freebsd-x64" "13.1.5" + "@next/swc-linux-arm-gnueabihf" "13.1.5" + "@next/swc-linux-arm64-gnu" "13.1.5" + "@next/swc-linux-arm64-musl" "13.1.5" + "@next/swc-linux-x64-gnu" "13.1.5" + "@next/swc-linux-x64-musl" "13.1.5" + "@next/swc-win32-arm64-msvc" "13.1.5" + "@next/swc-win32-ia32-msvc" "13.1.5" + "@next/swc-win32-x64-msvc" "13.1.5" no-case@^3.0.4: version "3.0.4" @@ -5746,12 +5747,12 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -pretty-format@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" - integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg== +pretty-format@^29.4.0: + version "29.4.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.0.tgz#766f071bb1c53f1ef8000c105bbeb649e86eb993" + integrity sha512-J+EVUPXIBHCdWAbvGBwXs0mk3ljGppoh/076g1S8qYS8nVG4u/yrhMvyTFHYYYKWnDdgRLExx0vA7pzxVGdlNw== dependencies: - "@jest/schemas" "^29.0.0" + "@jest/schemas" "^29.4.0" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -6331,10 +6332,10 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve.exports@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" - integrity sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ== +resolve.exports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" + integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" @@ -7205,10 +7206,10 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== +write-file-atomic@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" + integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" From 4d4dbd488485a1a6821f52005183fd09ee2dae42 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 14:35:11 +0100 Subject: [PATCH 014/878] Do not override variable `services` --- backend/src/Startup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index fe4957ee..9f5c3cd8 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -119,7 +119,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) ); // Database context as services are used by `Identity`. services.AddDbContext( - (services, options) => + (serviceProvider, options) => { if (!_environment.IsProduction()) { @@ -127,7 +127,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) .EnableSensitiveDataLogging() .EnableDetailedErrors(); } - services + serviceProvider .GetRequiredService>() .CreateDbContext(); }, From 62edf92fef9b26c76be76e9f37eaea01a35dd105 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 15:52:31 +0100 Subject: [PATCH 015/878] Use .NET 7 (and custom version of certificate tool that supports .NET 7) --- .gitmodules | 3 +++ backend/Dockerfile | 31 ++++++++++++++++++++++++------ backend/Dockerfile-bootstrap | 2 +- backend/Dockerfile-production | 4 ++-- backend/dotnet-certificate-tool | 1 + backend/dotnet-tools.json | 8 +------- backend/src/Database.csproj | 16 +++++++-------- backend/test/Database.Tests.csproj | 6 +++--- docker-compose.production.yml | 4 ++-- docker-compose.yml | 4 ++-- 10 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 .gitmodules create mode 160000 backend/dotnet-certificate-tool diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d77f937e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dotnet-certificate-tool"] + path = backend/dotnet-certificate-tool + url = git@github.com:building-envelope-data/dotnet-certificate-tool.git diff --git a/backend/Dockerfile b/backend/Dockerfile index 7a145b1d..c6796f64 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,7 +1,7 @@ # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 ARG GROUP_ID ARG USER_ID @@ -259,12 +259,31 @@ COPY \ --chown=me:us \ ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ ./ssl/ +# Once certificate tool supports .NET 7 simply use: +# ``` +# RUN \ +# dotnet tool run \ +# certificate-tool add \ +# --file ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ +# --store-name Root \ +# --password ${CERTIFICATE_AUTHORITY_PASSWORD} +# ``` +# Instead of: +COPY \ + --chown=me:us \ + ./dotnet-certificate-tool ./dotnet-certificate-tool RUN \ - dotnet tool run \ - certificate-tool add \ - --file ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ - --store-name Root \ - --password ${CERTIFICATE_AUTHORITY_PASSWORD} + dotnet publish \ + --configuration Release \ + --framework net7.0 \ + --output ./dotnet-certificate-tool/release \ + ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ + dotnet tool restore \ + --tool-manifest ./dotnet-tools.json \ + --add-source ./dotnet-certificate-tool/src/nupkg && \ + rm -rf ./dotnet-certificate-tool +# And remove the sub-module as was done before in +# https://github.com/building-envelope-data/metabase/commit/fbdd10f775e2289f9977b6c4a31ee6a5ff0b6ff9 # COPY \ # --chown=me:us \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 91700af0..8aa8f0d2 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,5 +1,5 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 ARG GROUP_ID ARG USER_ID diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 7fd9865c..b147fd86 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -8,7 +8,7 @@ ######### # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 AS build RUN \ apt-get update && \ @@ -36,7 +36,7 @@ RUN \ ########### # https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/aspnet:6.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim-amd64 RUN \ apt-get update && \ diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool new file mode 160000 index 00000000..ffc4650b --- /dev/null +++ b/backend/dotnet-certificate-tool @@ -0,0 +1 @@ +Subproject commit ffc4650b877aaf29e616664cf5b188b07b6e937f diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 91fef8dc..ec3e7e45 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "6.0.11", + "version": "7.0.3", "commands": [ "dotnet-aspnet-codegenerator" ] @@ -67,12 +67,6 @@ "commands": [ "jb" ] - }, - "dotnet-certificate-tool": { - "version": "2.0.7", - "commands": [ - "certificate-tool" - ] } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 53c74d55..f1251739 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,8 +1,8 @@ - net6.0 - 9.0 + net7.0 + 11.0 31666837686487542159 enable @@ -17,15 +17,15 @@ - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -36,14 +36,14 @@ - + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 087ad1ed..08f470b7 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,8 +1,8 @@ - net6.0 - 9.0 + net7.0 + 11.0 false enable true @@ -15,7 +15,7 @@ - + diff --git a/docker-compose.production.yml b/docker-compose.production.yml index f93d4e86..940f0eef 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -3,7 +3,7 @@ version: '3.3' services: database: # https://hub.docker.com/_/postgres restart: always - image: postgres:13.5-bullseye + image: postgres:13.9-bullseye # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -99,7 +99,7 @@ services: nginx: # https://hub.docker.com/_/nginx restart: always - image: nginx:1.21 + image: nginx:1.23 command: [ "nginx", "-g", "daemon off;" diff --git a/docker-compose.yml b/docker-compose.yml index 012f0a5d..4b8e45fe 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.3' services: database: # https://hub.docker.com/_/postgres - image: postgres:13.5-bullseye + image: postgres:13.9-bullseye # ports: # - 5432:5432 # For possible command-line arguments see @@ -146,7 +146,7 @@ services: - backend nginx: # https://hub.docker.com/_/nginx - image: nginx:1.21 + image: nginx:1.23 command: [ "nginx-debug", "-g", "daemon off;" From 54947bcab6a26778673d1811fd937cda101addd7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 16:20:35 +0100 Subject: [PATCH 016/878] Use BuildKit to build Docker images --- Dockerfile-show-build-context | 5 +++ Makefile | 63 ++++++++++++++++++++-------------- backend/Dockerfile | 5 +++ backend/Dockerfile-bootstrap | 5 +++ backend/Dockerfile-production | 5 +++ frontend/Dockerfile | 5 +++ frontend/Dockerfile-production | 5 +++ 7 files changed, 67 insertions(+), 26 deletions(-) diff --git a/Dockerfile-show-build-context b/Dockerfile-show-build-context index 3348ecb9..66f7087a 100644 --- a/Dockerfile-show-build-context +++ b/Dockerfile-show-build-context @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + FROM busybox RUN mkdir /tmp/build/ diff --git a/Makefile b/Makefile index c68ba61c..e35ea60b 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ include .env docker_compose = \ - docker-compose \ + docker compose \ --file docker-compose.yml \ --project-name ${NAME} @@ -16,7 +16,7 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -name : ## Print value of variable `${NAME}` +name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name @@ -24,29 +24,40 @@ name : ## Print value of variable `${NAME}` # Interface with Docker Compose # # ----------------------------- # -build : ## Build images - ${docker_compose} pull - ${docker_compose} build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) +pull : ## Pull images + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull +.PHONY : pull + +# To debug errors during build add `--progress plain \` to get additional +# output. +build : pull ## Build images + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : build -show-backend-build-context : ## Show the build context configured by `./backend/.dockerignore` - docker build \ - --pull \ - --no-cache \ - --file Dockerfile-show-build-context \ - ./backend -.PHONY : show-backend-build-context - -show-frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - docker build \ - --pull \ - --no-cache \ - --file Dockerfile-show-build-context \ - ./frontend -.PHONY : show-frontend-build-context +backend-build-context : ## Show the build context configured by `./backend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --file Dockerfile-show-build-context \ + ./backend +.PHONY : backend-build-context + +frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --file Dockerfile-show-build-context \ + ./frontend +.PHONY : frontend-build-context remove : ## Remove stopped containers ${docker_compose} rm @@ -57,7 +68,7 @@ remove-data : ## Remove data volumes ${NAME}_data .PHONY : remove-data -# TODO `docker-compose up` does not support `--user`, see https://github.com/docker/compose/issues/1532 +# TODO `docker compose up` does not support `--user`, see https://github.com/docker/compose/issues/1532 up : build ## (Re)create, and start containers (after building images if necessary) ${docker_compose} up \ --remove-orphans \ @@ -201,7 +212,7 @@ generate-certificate-authority : ## Generate certificate authority ECDSA private docker run \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.21.1 \ + nginx:1.23 \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ @@ -308,7 +319,7 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig docker run \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.21.1 \ + nginx:1.23 \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ diff --git a/backend/Dockerfile b/backend/Dockerfile index c6796f64..2d904b2d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 8aa8f0d2..1293f129 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,4 +1,9 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk + FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 ARG GROUP_ID diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index b147fd86..3cc5f535 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + # Inspired by # https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # and diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 395680dd..13c2f154 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 51ccd948..d72558cb 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,3 +1,8 @@ +# syntax=docker/dockerfile:1.4 +# The above line fixes the Dockerfile frontend used by BuildKit. For details +# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + # Inspired by https://nextjs.org/docs/deployment#docker-image FROM node:18.13-bullseye-slim AS dependencies From 8d0f92d8986538d5905ef7f264033432119ea523 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 16:38:33 +0100 Subject: [PATCH 017/878] Register enumerations in the database context such that they are picked up by `dotnet ef` --- backend/src/Data/ApplicationDbContext.cs | 8 ++++++++ backend/src/Startup.cs | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 2c0f0652..68926496 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Npgsql; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; namespace Database.Data @@ -12,6 +13,13 @@ public sealed class ApplicationDbContext { static ApplicationDbContext() { + RegisterEnumerations(); + } + + private static void RegisterEnumerations() + { + // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum + NpgsqlConnection.GlobalTypeMapper.MapEnum(); } private static void CreateEnumerations(ModelBuilder builder) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 9f5c3cd8..7e3dd1bf 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -105,7 +105,10 @@ private void ConfigureDatabaseServices(IServiceCollection services) services.AddPooledDbContextFactory(options => { var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); - dataSourceBuilder.MapEnum(); + // Enumerations registered as below are not picked up by + // the tool `dotnet ef` when creating migrations. We thus + // register enumerations in `ApplicationDbContext`. + // dataSourceBuilder.MapEnum(); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName); From 5f7f7b23a6388f2775cc3faf1ab96b37568b6d2d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 16:39:40 +0100 Subject: [PATCH 018/878] Create migration regarding renaming `xmin` to `Version` --- ...0126153325_RenameXminToVersion.Designer.cs | 1052 +++++++++++++++++ .../20230126153325_RenameXminToVersion.cs | 247 ++++ .../ApplicationDbContextModelSnapshot.cs | 283 ++--- 3 files changed, 1448 insertions(+), 134 deletions(-) create mode 100644 backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs create mode 100644 backend/src/Migrations/20230126153325_RenameXminToVersion.cs diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs new file mode 100644 index 00000000..fda98d18 --- /dev/null +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs @@ -0,0 +1,1052 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230126153325_RenameXminToVersion")] + partial class RenameXminToVersion + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs new file mode 100644 index 00000000..561549c7 --- /dev/null +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -0,0 +1,247 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class RenameXminToVersion : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "photovoltaic_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "optical_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "optical_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "hygrothermal_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "calorimetric_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "calorimetric_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "photovoltaic_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "optical_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "optical_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "hygrothermal_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "calorimetric_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "calorimetric_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 355d6176..93aafbe6 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -8,6 +8,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +#nullable disable + namespace Database.Migrations { [DbContext(typeof(ApplicationDbContext))] @@ -18,11 +20,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasPostgresEnum("data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }) - .HasPostgresExtension("pgcrypto") - .HasAnnotation("Relational:MaxIdentifierLength", 63) - .HasAnnotation("ProductVersion", "5.0.10") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("Database.Data.CalorimetricData", b => { @@ -35,7 +38,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -58,18 +61,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + b.Property("Warnings") .IsRequired() .HasColumnType("text[]"); - b.Property("xmin") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); - b.HasKey("Id"); - b.ToTable("calorimetric_data"); + b.ToTable("calorimetric_data", "database"); }); modelBuilder.Entity("Database.Data.GetHttpsResource", b => @@ -105,10 +109,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PhotovoltaicDataId") .HasColumnType("uuid"); - b.Property("xmin") + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); + .HasColumnType("xid") + .HasColumnName("xmin"); b.HasKey("Id"); @@ -122,7 +127,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PhotovoltaicDataId"); - b.ToTable("get_https_resource"); + b.ToTable("get_https_resource", "database"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => @@ -136,7 +141,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -151,18 +156,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + b.Property("Warnings") .IsRequired() .HasColumnType("text[]"); - b.Property("xmin") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); - b.HasKey("Id"); - b.ToTable("hygrothermal_data"); + b.ToTable("hygrothermal_data", "database"); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -180,7 +186,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -215,18 +221,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + b.Property("Warnings") .IsRequired() .HasColumnType("text[]"); - b.Property("xmin") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); - b.HasKey("Id"); - b.ToTable("optical_data"); + b.ToTable("optical_data", "database"); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => @@ -240,7 +247,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("CreatedAt") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -255,18 +262,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + b.Property("Warnings") .IsRequired() .HasColumnType("text[]"); - b.Property("xmin") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid"); - b.HasKey("Id"); - b.ToTable("photovoltaic_data"); + b.ToTable("photovoltaic_data", "database"); }); modelBuilder.Entity("Database.Data.CalorimetricData", b => @@ -281,7 +289,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("CalorimetricDataId"); - b1.ToTable("calorimetric_data"); + b1.ToTable("calorimetric_data", "database"); b1.WithOwner() .HasForeignKey("CalorimetricDataId"); @@ -293,8 +301,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -306,7 +315,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); - b2.ToTable("calorimetric_data_Arguments"); + b2.ToTable("calorimetric_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodCalorimetricDataId"); @@ -319,8 +328,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -328,7 +338,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); - b2.ToTable("calorimetric_data_Sources"); + b2.ToTable("calorimetric_data_Sources", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodCalorimetricDataId"); @@ -339,9 +349,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); @@ -350,14 +358,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("data_kind"); b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); - b3.ToTable("calorimetric_data_Sources"); + b3.ToTable("calorimetric_data_Sources", "database"); b3.WithOwner() .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); @@ -379,8 +387,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -402,11 +411,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b1.HasKey("CalorimetricDataId", "Id"); - b1.ToTable("calorimetric_data_Approvals"); + b1.ToTable("calorimetric_data_Approvals", "database"); b1.WithOwner() .HasForeignKey("CalorimetricDataId"); @@ -447,8 +456,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("DataFormatId") .HasColumnType("uuid"); @@ -459,7 +469,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("GetHttpsResourceId", "Id"); - b1.ToTable("FileMetaInformation"); + b1.ToTable("FileMetaInformation", "database"); b1.WithOwner() .HasForeignKey("GetHttpsResourceId"); @@ -479,7 +489,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("GetHttpsResourceId"); - b1.ToTable("get_https_resource"); + b1.ToTable("get_https_resource", "database"); b1.WithOwner() .HasForeignKey("GetHttpsResourceId"); @@ -491,8 +501,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -504,7 +515,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); - b2.ToTable("get_https_resource_Arguments"); + b2.ToTable("get_https_resource_Arguments", "database"); b2.WithOwner() .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); @@ -540,7 +551,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("HygrothermalDataId"); - b1.ToTable("hygrothermal_data"); + b1.ToTable("hygrothermal_data", "database"); b1.WithOwner() .HasForeignKey("HygrothermalDataId"); @@ -552,8 +563,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -565,7 +577,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); - b2.ToTable("hygrothermal_data_Arguments"); + b2.ToTable("hygrothermal_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodHygrothermalDataId"); @@ -578,8 +590,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -587,7 +600,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); - b2.ToTable("hygrothermal_data_Sources"); + b2.ToTable("hygrothermal_data_Sources", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodHygrothermalDataId"); @@ -598,9 +611,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); @@ -609,14 +620,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("data_kind"); b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); - b3.ToTable("hygrothermal_data_Sources"); + b3.ToTable("hygrothermal_data_Sources", "database"); b3.WithOwner() .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); @@ -638,8 +649,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -661,11 +673,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b1.HasKey("HygrothermalDataId", "Id"); - b1.ToTable("hygrothermal_data_Approvals"); + b1.ToTable("hygrothermal_data_Approvals", "database"); b1.WithOwner() .HasForeignKey("HygrothermalDataId"); @@ -679,6 +691,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Database.Data.OpticalData", b => { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => { b1.Property("OpticalDataId") @@ -689,7 +729,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("OpticalDataId"); - b1.ToTable("optical_data"); + b1.ToTable("optical_data", "database"); b1.WithOwner() .HasForeignKey("OpticalDataId"); @@ -701,8 +741,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -714,7 +755,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodOpticalDataId", "Id"); - b2.ToTable("optical_data_Arguments"); + b2.ToTable("optical_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodOpticalDataId"); @@ -727,8 +768,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -736,7 +778,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodOpticalDataId", "Id"); - b2.ToTable("optical_data_Sources"); + b2.ToTable("optical_data_Sources", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodOpticalDataId"); @@ -747,9 +789,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); @@ -758,14 +798,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("data_kind"); b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); - b3.ToTable("optical_data_Sources"); + b3.ToTable("optical_data_Sources", "database"); b3.WithOwner() .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); @@ -780,33 +820,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); - b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => - { - b1.Property("OpticalDataId") - .HasColumnType("uuid"); - - b1.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); - - b1.Property("AStar") - .HasColumnType("double precision"); - - b1.Property("BStar") - .HasColumnType("double precision"); - - b1.Property("LStar") - .HasColumnType("double precision"); - - b1.HasKey("OpticalDataId", "Id"); - - b1.ToTable("CielabColor"); - - b1.WithOwner() - .HasForeignKey("OpticalDataId"); - }); - b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("OpticalDataId") @@ -814,8 +827,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -837,11 +851,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b1.HasKey("OpticalDataId", "Id"); - b1.ToTable("optical_data_Approvals"); + b1.ToTable("optical_data_Approvals", "database"); b1.WithOwner() .HasForeignKey("OpticalDataId"); @@ -867,7 +881,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.HasKey("PhotovoltaicDataId"); - b1.ToTable("photovoltaic_data"); + b1.ToTable("photovoltaic_data", "database"); b1.WithOwner() .HasForeignKey("PhotovoltaicDataId"); @@ -879,8 +893,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -892,7 +907,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); - b2.ToTable("photovoltaic_data_Arguments"); + b2.ToTable("photovoltaic_data_Arguments", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodPhotovoltaicDataId"); @@ -905,8 +920,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); b2.Property("Name") .IsRequired() @@ -914,7 +930,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); - b2.ToTable("photovoltaic_data_Sources"); + b2.ToTable("photovoltaic_data_Sources", "database"); b2.WithOwner() .HasForeignKey("AppliedMethodPhotovoltaicDataId"); @@ -925,9 +941,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("NamedMethodSourceId") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); b3.Property("DataId") .HasColumnType("uuid"); @@ -936,14 +950,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("data_kind"); b3.Property("DataTimestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") .HasColumnType("uuid"); b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); - b3.ToTable("photovoltaic_data_Sources"); + b3.ToTable("photovoltaic_data_Sources", "database"); b3.WithOwner() .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); @@ -965,8 +979,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); b1.Property("ApproverId") .HasColumnType("uuid"); @@ -988,11 +1003,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b1.Property("Timestamp") - .HasColumnType("timestamp without time zone"); + .HasColumnType("timestamp with time zone"); b1.HasKey("PhotovoltaicDataId", "Id"); - b1.ToTable("photovoltaic_data_Approvals"); + b1.ToTable("photovoltaic_data_Approvals", "database"); b1.WithOwner() .HasForeignKey("PhotovoltaicDataId"); From 76b9af5e9f8bfe93fbaefb9d90839aafe7eb3c21 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 16:42:51 +0100 Subject: [PATCH 019/878] Format code with `dotnet format` --- backend/dotnet-certificate-tool | 2 +- .../20211019125407_InitialCreate.Designer.cs | 2 +- .../20211019125407_InitialCreate.cs | 2 +- ...0126153325_RenameXminToVersion.Designer.cs | 2 +- .../20230126153325_RenameXminToVersion.cs | 2 +- .../ApplicationDbContextModelSnapshot.cs | 2 +- backend/src/Startup.cs | 64 +++++++++---------- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index ffc4650b..934be67b 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit ffc4650b877aaf29e616664cf5b188b07b6e937f +Subproject commit 934be67bb7fa10bcf4fb18c9494c6916417fadba diff --git a/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs b/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs index f1edce5e..66b2cbc9 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20211019125407_InitialCreate.cs b/backend/src/Migrations/20211019125407_InitialCreate.cs index 15bb1ece..658137b0 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json; using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs index fda98d18..a560c1df 100644 --- a/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs index 561549c7..710ca7af 100644 --- a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 93aafbe6..5d355771 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7e3dd1bf..b5929a46 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -213,45 +213,45 @@ public void Configure(IApplicationBuilder app) // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) -{ - context.Response.ContentType = "application/json; charset=utf-8"; + { + context.Response.ContentType = "application/json; charset=utf-8"; - var options = new JsonWriterOptions { Indented = true }; + var options = new JsonWriterOptions { Indented = true }; - using var memoryStream = new MemoryStream(); - using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) - { - jsonWriter.WriteStartObject(); - jsonWriter.WriteString("status", healthReport.Status.ToString()); - jsonWriter.WriteStartObject("results"); + using var memoryStream = new MemoryStream(); + using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) + { + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("status", healthReport.Status.ToString()); + jsonWriter.WriteStartObject("results"); - foreach (var healthReportEntry in healthReport.Entries) - { - jsonWriter.WriteStartObject(healthReportEntry.Key); - jsonWriter.WriteString("status", - healthReportEntry.Value.Status.ToString()); - jsonWriter.WriteString("description", - healthReportEntry.Value.Description); - jsonWriter.WriteStartObject("data"); + foreach (var healthReportEntry in healthReport.Entries) + { + jsonWriter.WriteStartObject(healthReportEntry.Key); + jsonWriter.WriteString("status", + healthReportEntry.Value.Status.ToString()); + jsonWriter.WriteString("description", + healthReportEntry.Value.Description); + jsonWriter.WriteStartObject("data"); - foreach (var item in healthReportEntry.Value.Data) - { - jsonWriter.WritePropertyName(item.Key); + foreach (var item in healthReportEntry.Value.Data) + { + jsonWriter.WritePropertyName(item.Key); - JsonSerializer.Serialize(jsonWriter, item.Value, - item.Value?.GetType() ?? typeof(object)); - } + JsonSerializer.Serialize(jsonWriter, item.Value, + item.Value?.GetType() ?? typeof(object)); + } - jsonWriter.WriteEndObject(); - jsonWriter.WriteEndObject(); - } + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); + } - jsonWriter.WriteEndObject(); - jsonWriter.WriteEndObject(); - } + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); + } - return context.Response.WriteAsync( - Encoding.UTF8.GetString(memoryStream.ToArray())); -} + return context.Response.WriteAsync( + Encoding.UTF8.GetString(memoryStream.ToArray())); + } } } \ No newline at end of file From 668f882ae96f93c5e6affce601f71d5450dbb2c2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 16:48:02 +0100 Subject: [PATCH 020/878] Update .NET version in GitHub workflow --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 1a3d9911..7ff9ef0a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -16,7 +16,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 7.0.x - name: Restore dependencies working-directory: ./backend run: dotnet restore From 9c11fe555ef611bc235be55d08e702ceef2a6f3d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 17:04:26 +0100 Subject: [PATCH 021/878] Do not truncate output --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index e35ea60b..48dd975a 100644 --- a/Makefile +++ b/Makefile @@ -164,6 +164,7 @@ shelld : up ## Enter shell in an existing `database` container (after starting a list : ## List all containers with health status ${docker_compose} ps \ + --no-trunc \ --all .PHONY : list From 4ce5fa2c4dbe67de14f5e1c9be22896921349b50 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 17:07:03 +0100 Subject: [PATCH 022/878] Use BuildKit to build Docker images in production --- Makefile.production | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/Makefile.production b/Makefile.production index 9a292dbb..2c589f4c 100644 --- a/Makefile.production +++ b/Makefile.production @@ -4,7 +4,7 @@ include .env docker_compose = \ - docker-compose \ + docker compose \ --file docker-compose.production.yml \ --project-name ${NAME} @@ -19,18 +19,22 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -name : ## Print value of variable `${NAME}` +name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name pull : ## Pull images - ${docker_compose} pull + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull .PHONY : pull build : pull ## Build images - ${docker_compose} build \ - --pull \ - --no-cache + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull \ + --no-cache .PHONY : build up : ## (Re)create and start containers @@ -66,6 +70,17 @@ shellb : COMMAND = bash shellb : execb ## Enter shell in a running `backend` container (to test database access from within the shell run `apk add postgresql-client` and `psql "host=database port=5432 user=postgres passfile=/run/secrets/postgres_passwords"`) .PHONY : shellb +list : ## List all containers with health status + ${docker_compose} ps \ + --no-trunc \ + --all +.PHONY : list + +list-services : ## List all services specified in the docker-compose file (used by Monit as configured in the `machine` project) + ${docker_compose} config \ + --services +.PHONY : list-services + createdb : ## Create database ${docker_compose} run \ --name create_${NAME}_database \ @@ -89,17 +104,6 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` servic --dbname=xbase .PHONY : psql -list : ## List all containers with health status - ${docker_compose} ps \ - --no-trunc \ - --all -.PHONY : list - -list-services : ## List all services specified in the docker-compose file (used by Monit as configured in the `machine` project) - ${docker_compose} config \ - --services -.PHONY : list-services - # TODO The entrypoint file of the PostgreSQL image uses the file refered to by `POSTGRES_PASSWORD_FILE` and this file needs to be accessible by `other`. Why? We do not want all people to be able to read secrets! postgres_password : ## Generate PostgreSQL password file with nothing but one password in plain text (note that if the data volume already exists, then it either needs to be removed resulting in the loss of all data or the password of the PostgreSQL user needs to be changed manually by executing the SQL query `ALTER USER postgres with password '...'`) mkdir -p ./secrets From ac3467db67cb7971238a655d08edd320c0e5f20d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Jan 2023 17:23:05 +0100 Subject: [PATCH 023/878] Use plain progress to make build context being printed to standard output --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 48dd975a..2e36feee 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,7 @@ backend-build-context : ## Show the build context configured by `./backend/.dock docker build \ --pull \ --no-cache \ + --progress plain \ --file Dockerfile-show-build-context \ ./backend .PHONY : backend-build-context @@ -55,6 +56,7 @@ frontend-build-context : ## Show the build context configured by `./frontend/.do docker build \ --pull \ --no-cache \ + --progress plain \ --file Dockerfile-show-build-context \ ./frontend .PHONY : frontend-build-context From 500baf78b463a0adfc77ed366c31973284194ab0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 15:22:32 +0100 Subject: [PATCH 024/878] Make bash the default shell within Docker containers --- backend/Dockerfile | 2 ++ backend/Dockerfile-production | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 2d904b2d..f2788a86 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -317,4 +317,6 @@ RUN \ ${HOME}/.nuget \ ${HOME}/.dotnet +ENV SHELL=/bin/bash + ENTRYPOINT ["/usr/bin/dumb-init", "--"] diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 3cc5f535..75f81b36 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -63,7 +63,7 @@ COPY \ ./ RUN \ - ln --symbolic Database.dll App.dll + ln --symbolic ./Database.dll ./App.dll # See: https://github.com/dotnet/announcements/issues/20 # Uncomment to enable globalization APIs @@ -71,3 +71,5 @@ RUN \ #RUN apt-get install icu-libs ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 + +ENV SHELL=/bin/bash From ff4ac25669b8d0d21a2170d2c49328d90676ab6b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 16:31:13 +0100 Subject: [PATCH 025/878] Mark constructors and methods that use obsolete values as obsolete --- backend/src/Data/ApplicationDbContext.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 68926496..90ec7de7 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -11,14 +11,22 @@ namespace Database.Data public sealed class ApplicationDbContext : DbContext { + [System.Obsolete] static ApplicationDbContext() { RegisterEnumerations(); } + [System.Obsolete] private static void RegisterEnumerations() { // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum + // Mapping enums like this is marked as obsolete. The preferred way + // is described in + // https://www.npgsql.org/doc/release-notes/7.0.html#managing-type-mappings-at-the-connection-level-is-no-longer-supported + // I tried to go the new way in `Startup#ConfigureDatabaseServices`. + // The problem was though that the tool `dotnet ef` did not pick up + // the registered enumerations. NpgsqlConnection.GlobalTypeMapper.MapEnum(); } From f42dfc5bce9bae2abff05589c41bde674edada11 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 16:31:35 +0100 Subject: [PATCH 026/878] Forward cancellation token --- backend/src/Controllers/FileUploadController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7e31faf7..0debd29d 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -110,7 +110,7 @@ CancellationToken cancellationToken ModelState.AddModelError("AccessToken", $"Access token must come first, got {firstContentDisposition?.Name} instead."); return BadRequest(ModelState); } - var accessToken = await new StreamReader(section.Body).ReadToEndAsync().ConfigureAwait(false); + var accessToken = await new StreamReader(section.Body).ReadToEndAsync(cancellationToken).ConfigureAwait(false); if (accessToken != _accessToken) { ModelState.AddModelError("AccessToken", $"The access token {accessToken} is invalid."); From 38ba3a0d214e1509af7dd5c877f49931c0b80ccd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 16:31:52 +0100 Subject: [PATCH 027/878] Handle null values --- backend/src/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index b5929a46..1c4ce0f1 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -32,7 +32,7 @@ IConfiguration configuration ) { _environment = environment; - _appSettings = configuration.Get(); + _appSettings = configuration.Get() ?? throw new Exception("Failed to get application settings from configuration."); } public void ConfigureServices(IServiceCollection services) From 2e77186ce3309766cdba64d8bbb98368dfe290a7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 16:32:04 +0100 Subject: [PATCH 028/878] Use UTF8 where possible --- backend/src/Utilities/FileHelpers.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index a7d068aa..1a93ac73 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -22,7 +22,7 @@ public static class FileHelpers // and the official specifications for the file types you wish to add. private static readonly Dictionary> _fileSignature = new() { - { ".gif", new List { new byte[] { 0x47, 0x49, 0x46, 0x38 } } }, + { ".gif", new List { "GIF8"u8.ToArray() } }, { ".png", new List { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } }, { ".jpeg", @@ -47,11 +47,11 @@ public static class FileHelpers new List { new byte[] { 0x50, 0x4B, 0x03, 0x04 }, - new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 }, - new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 }, + "PKLITE"u8.ToArray(), + "PKSpX"u8.ToArray(), new byte[] { 0x50, 0x4B, 0x05, 0x06 }, new byte[] { 0x50, 0x4B, 0x07, 0x08 }, - new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 }, + "WinZip"u8.ToArray(), } }, }; From 3b28ab4cad72bb211fb264f9013e4fb7686568d0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 27 Jan 2023 16:42:36 +0100 Subject: [PATCH 029/878] Improve logger usage --- backend/src/Services/EmailSender.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index e6e19d04..f1bfef48 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -30,7 +30,7 @@ public Task SendAsync( string body ) { - _logger.LogDebug($"About to send email to `{to}` with subject `{subject}` and body `{body}`"); + _logger.LogDebug("About to send email to `{To}` with subject `{Subject}` and body `{Body}`", to, subject, body); var message = new MimeMessage(); message.From.Add( new MailboxAddress( From 22130e2ec34a1ecc74b9efb0675ac93a9e6bf948 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 30 Jan 2023 14:10:51 +0100 Subject: [PATCH 030/878] Do not cache node modules in named volumes to make sure that the installed volumes are always up-to-date with the latest ones in the image --- docker-compose.yml | 10 ++++++---- frontend/Dockerfile | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4b8e45fe..b0de8ac7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,11 +81,11 @@ services: - files:/app/src/files - backend_vscode_server_extensions:/home/me/.vscode-server/extensions - backend_vscode_server_insiders_extensions:/home/me/.vscode-server-insiders/extensions - # - backend_node_modules:/app/node_modules # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. + # - /app/node_modules - /app/src/bin - /app/src/obj - /app/test/bin @@ -137,7 +137,11 @@ services: retries: 3 volumes: - ./frontend:/app - - frontend_node_modules:/app/node_modules + # The following items make the engine create anonymous volumes for the + # specified path with the effect that the service does not write + # temporary files into the corresponding directory on the host file + # system that was bound by the `/app` binding. + - /app/node_modules extra_hosts: - ${HOST}:host-gateway environment: @@ -181,7 +185,5 @@ volumes: emails: files: backend_logs: - # backend_node_modules: backend_vscode_server_extensions: backend_vscode_server_insiders_extensions: - frontend_node_modules: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 13c2f154..11bda9e5 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -51,7 +51,7 @@ RUN \ chown \ node:node \ ${HOME}/app && \ - ln -s ${HOME}/app /app + ln --symbolic ${HOME}/app /app ############# # As `node` # @@ -66,6 +66,8 @@ COPY \ COPY \ --chown=node:node \ ./yarn.lock ./ -RUN yarn install --pure-lockfile +RUN \ + yarn install \ + --pure-lockfile ENTRYPOINT ["/usr/bin/dumb-init", "--"] From e17573f6e5aa4337a901ca56280d2fffa0324771 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 30 Jan 2023 14:24:14 +0100 Subject: [PATCH 031/878] Make markdown assign numbers to lists --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index da7feed8..0160ebab 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ If you have a question for which you don't find the answer in this repository, p In another shell -1. Drop into `ash` with the working directory `/app`, which is mounted to the +1. Drop into `bash` with the working directory `/app`, which is mounted to the host's `./backend` directory, inside a fresh Docker container based on `./backend/Dockerfile` by running `make shellb`. If necessary, the Docker image is (re)built automatically, which takes a while the first time. -2. List all backend GNU Make targets by running `make help`. -3. For example, update packages and tools by running `make update`. -4. Drop out of the container by running `exit` or pressing `Ctrl-D`. +1. List all backend GNU Make targets by running `make help`. +1. For example, update packages and tools by running `make update`. +1. Drop out of the container by running `exit` or pressing `Ctrl-D`. The same works for frontend containers by running `make shellf`. From 0795d6490848035f65f3d8b37d0a66ba919aee87 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 31 Jan 2023 14:22:51 +0100 Subject: [PATCH 032/878] Add workflow that gives all new and reopened issues the triage label --- .github/workflows/label-issues.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/label-issues.yml diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yml new file mode 100644 index 00000000..254a712a --- /dev/null +++ b/.github/workflows/label-issues.yml @@ -0,0 +1,23 @@ +# Inspired by https://docs.github.com/en/actions/managing-issues-and-pull-requests/adding-labels-to-issues + +name: Label issues +on: + issues: + types: + - reopened + - opened +jobs: + label_issues: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/github-script@v6 + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["triage"] + }) From ca735c7577b4f51eec801f0ab08b8a54a457b4fa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 31 Jan 2023 16:34:09 +0100 Subject: [PATCH 033/878] Handle next.js WebSocket connections properly --- see https://nextjs.org/docs/upgrading#nextjs-hmr-connection-now-uses-a-websocket and http://nginx.org/en/docs/http/websocket.html --- nginx/templates/development/default.conf.template | 8 ++++++-- nginx/templates/production/default.conf.template | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index 320e8249..87845253 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -84,7 +84,11 @@ server { location / { proxy_pass http://frontend; - limit_req zone=one burst=30 nodelay; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + limit_req zone=one burst=30 nodelay; } location /graphql { @@ -122,7 +126,7 @@ server { proxy_pass http://email; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - limit_req zone=one burst=20 nodelay; + limit_req zone=one burst=20 nodelay; } error_page 503 @maintenance; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index 67662e02..7b22f80c 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -67,7 +67,11 @@ server { location / { proxy_pass http://frontend; - limit_req zone=one burst=30 nodelay; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + limit_req zone=one burst=30 nodelay; } location /graphql { @@ -105,7 +109,7 @@ server { proxy_pass http://email; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - limit_req zone=one burst=20 nodelay; + limit_req zone=one burst=20 nodelay; } error_page 503 @maintenance; From 85760904115f72310ba490437c89b3e7a2b427eb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Feb 2023 15:30:37 +0100 Subject: [PATCH 034/878] Improve store commit GNU Make target --- Makefile.production | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile.production b/Makefile.production index 2c589f4c..bba0f5e8 100644 --- a/Makefile.production +++ b/Makefile.production @@ -256,7 +256,10 @@ rollback : begin-maintenance checkout-target restore deploy-services run-tests e .PHONY : rollback store-commit : ## Store current commit - $(shell git rev-parse --verify HEAD > commit) + git rev-parse \ + --verify \ + HEAD \ + > commit .PHONY : store-commit fetch-all : ## Fetch all From 8bb37519efea0c7c15a28ecff288b625609f1749 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Feb 2023 15:31:24 +0100 Subject: [PATCH 035/878] Remove slash after backup directory to avoid `//` (though they are not necessarily a problem) --- Makefile.production | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.production b/Makefile.production index bba0f5e8..009f2ca9 100644 --- a/Makefile.production +++ b/Makefile.production @@ -144,7 +144,7 @@ docker-stats : ## Show Docker run-time metrics # Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")/ backup` +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` mkdir --parents ${BACKUP_DIRECTORY} ${docker_compose} run \ --name backup_${NAME}_database \ @@ -192,7 +192,7 @@ prune-backups : ## Keep the most recent 7 backups, delete the rest rm --recursive --dir -- .PHONY : prune-backups -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35/ restore` +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore` ${docker_compose} run \ --name restore_${NAME}_database \ --detach \ @@ -246,12 +246,12 @@ end-maintenance : ## End maintenance rm ./nginx/html/maintenance.html .PHONY : end-maintenance -deploy : BACKUP_DIRECTORY = $(shell pwd)/backup/ +deploy : BACKUP_DIRECTORY = $(shell pwd)/backup deploy : begin-maintenance store-commit backup fetch-all checkout-target migrate deploy-services run-tests end-maintenance ## Deploy tag, branch, or commit `${TARGET}`, for example, `make --file Makefile.production TARGET=v1.0.0 deploy` .PHONY : deploy rollback : TARGET = $(shell cat ./commit) -rollback : BACKUP_DIRECTORY = $(shell pwd)/backup/ +rollback : BACKUP_DIRECTORY = $(shell pwd)/backup rollback : begin-maintenance checkout-target restore deploy-services run-tests end-maintenance ## Rollback deployment attempt (use commit hash stored in `./commit` and database backup stored in `./backup/`) .PHONY : rollback From 006f1e776d7cb109d5ecca00d275395c0159f790 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 6 Feb 2023 14:25:30 +0100 Subject: [PATCH 036/878] Create SQL migration script --- .../20230126153325_RenameXminToVersion.cs | 12 ------- backend/src/Migrations/current | 2 +- ..._to_20230126153325_RenameXminToVersion.sql | 31 +++++++++++++++++++ backend/src/Migrations/previous | 2 +- ..._to_20230126153325_RenameXminToVersion.sql | 31 +++++++++++++++++++ 5 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql create mode 100644 backend/src/Migrations/rollback_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs index 710ca7af..01365343 100644 --- a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -11,12 +11,6 @@ public partial class RenameXminToVersion : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - migrationBuilder.AlterColumn( name: "Value_DataTimestamp", schema: "database", @@ -129,12 +123,6 @@ protected override void Up(MigrationBuilder migrationBuilder) /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - migrationBuilder.AlterColumn( name: "Value_DataTimestamp", schema: "database", diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index d51fb881..eda70dee 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20211019125407_InitialCreate +20230126153325_RenameXminToVersion diff --git a/backend/src/Migrations/migrate_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql b/backend/src/Migrations/migrate_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql new file mode 100644 index 00000000..8eecc80b --- /dev/null +++ b/backend/src/Migrations/migrate_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql @@ -0,0 +1,31 @@ +START TRANSACTION; + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230126153325_RenameXminToVersion', '7.0.2'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index 573541ac..d51fb881 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -0 +20211019125407_InitialCreate diff --git a/backend/src/Migrations/rollback_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql b/backend/src/Migrations/rollback_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql new file mode 100644 index 00000000..75882e38 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20211019125407_InitialCreate_to_20230126153325_RenameXminToVersion.sql @@ -0,0 +1,31 @@ +START TRANSACTION; + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp without time zone; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp without time zone; + +ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" TYPE timestamp without time zone; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp without time zone; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp without time zone; + +ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" TYPE timestamp without time zone; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp without time zone; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp without time zone; + +ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" TYPE timestamp without time zone; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp without time zone; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp without time zone; + +ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" TYPE timestamp without time zone; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20230126153325_RenameXminToVersion'; + +COMMIT; + From 90b3094d0f72cc38a93cb766ae4d4e07ac58f00f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 6 Feb 2023 16:04:10 +0100 Subject: [PATCH 037/878] Use system user and group to create backup file archive --- Makefile.production | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile.production b/Makefile.production index 009f2ca9..20bcd260 100644 --- a/Makefile.production +++ b/Makefile.production @@ -166,9 +166,10 @@ backup : ## Backup database and related data to directory with absolute path `${ backend docker run \ --rm \ + --user $(shell id --user):$(shell id --group) \ --volumes-from backup_${NAME}_files \ --volume ${BACKUP_DIRECTORY}:/backup \ - debian:buster-slim \ + debian:bullseye-slim \ tar \ --verbose \ --create \ @@ -215,9 +216,10 @@ restore : ## Restore database and related data from directory with absolute path backend docker run \ --rm \ + --user $(shell id --user):$(shell id --group) \ --volumes-from backup_${NAME}_files \ --volume ${BACKUP_DIRECTORY}:/backup \ - debian:buster-slim \ + debian:bullseye-slim \ bash -cx " \ cd /app/files && \ rm \ From d54546adc59d6eed25e4066ab143e180aeee90f8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 6 Feb 2023 16:24:21 +0100 Subject: [PATCH 038/878] Stay root in production when backing up or restoring --- Makefile.production | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile.production b/Makefile.production index 20bcd260..c15a1da6 100644 --- a/Makefile.production +++ b/Makefile.production @@ -166,7 +166,6 @@ backup : ## Backup database and related data to directory with absolute path `${ backend docker run \ --rm \ - --user $(shell id --user):$(shell id --group) \ --volumes-from backup_${NAME}_files \ --volume ${BACKUP_DIRECTORY}:/backup \ debian:bullseye-slim \ @@ -216,7 +215,6 @@ restore : ## Restore database and related data from directory with absolute path backend docker run \ --rm \ - --user $(shell id --user):$(shell id --group) \ --volumes-from backup_${NAME}_files \ --volume ${BACKUP_DIRECTORY}:/backup \ debian:bullseye-slim \ From 9f28647e5b8341411a90c4b1fe4c6e575596e5b6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Feb 2023 14:52:08 +0100 Subject: [PATCH 039/878] Extend output of health check --- backend/src/Startup.cs | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 1c4ce0f1..eec49943 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -215,16 +215,14 @@ public void Configure(IApplicationBuilder app) private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) { context.Response.ContentType = "application/json; charset=utf-8"; - var options = new JsonWriterOptions { Indented = true }; - using var memoryStream = new MemoryStream(); using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) { jsonWriter.WriteStartObject(); jsonWriter.WriteString("status", healthReport.Status.ToString()); + jsonWriter.WriteString("duration", healthReport.TotalDuration.ToString()); jsonWriter.WriteStartObject("results"); - foreach (var healthReportEntry in healthReport.Entries) { jsonWriter.WriteStartObject(healthReportEntry.Key); @@ -232,24 +230,50 @@ private static Task WriteJsonResponse(HttpContext context, HealthReport healthRe healthReportEntry.Value.Status.ToString()); jsonWriter.WriteString("description", healthReportEntry.Value.Description); + jsonWriter.WriteString("duration", + healthReportEntry.Value.Duration.ToString()); + jsonWriter.WriteStartArray("tags"); + foreach (var tag in healthReportEntry.Value.Tags) + { + jsonWriter.WriteStringValue(tag); + } + jsonWriter.WriteEndArray(); + var exception = healthReportEntry.Value.Exception; + if (exception is not null) + { + jsonWriter.WriteStartObject("exception"); + jsonWriter.WriteString("message", exception.Message); + if (exception.StackTrace is not null) + { + jsonWriter.WriteString("stackTrace", exception.StackTrace); + } + if (exception.InnerException is not null) + { + jsonWriter.WriteString("innerException", exception.InnerException.ToString()); + } + if (exception.Source is not null) + { + jsonWriter.WriteString("source", exception.Source); + } + if (exception.TargetSite is not null) + { + jsonWriter.WriteString("targetSite", exception.TargetSite.ToString()); + } + jsonWriter.WriteEndObject(); + } jsonWriter.WriteStartObject("data"); - foreach (var item in healthReportEntry.Value.Data) { jsonWriter.WritePropertyName(item.Key); - JsonSerializer.Serialize(jsonWriter, item.Value, item.Value?.GetType() ?? typeof(object)); } - jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject(); } - jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject(); } - return context.Response.WriteAsync( Encoding.UTF8.GetString(memoryStream.ToArray())); } From ce71d375fe745a40b475b4ca0eac8689c7fcb656 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Feb 2023 15:44:34 +0100 Subject: [PATCH 040/878] Add packages to print licenses of dependencies --- backend/Makefile | 11 ++ backend/dotnet-tools.json | 6 ++ frontend/Makefile | 9 ++ frontend/package.json | 1 + frontend/yarn.lock | 210 ++++++++++++++++++++++++++++++++++++-- 5 files changed, 231 insertions(+), 6 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index f8b72e91..3c3c4f3c 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -53,6 +53,17 @@ assets : ## Build assets npm run all .PHONY : assets +# For usage information see https://github.com/tomchavakis/nuget-license#usage +# or run `dotnet tool run dotnet-project-licenses -- --help` +licenses : ## Print licenses + dotnet tool run \ + dotnet-project-licenses \ + -i ./Database.sln + # --json + # --allowed-license-types ... + # --forbidden-license-types ... +.PHONY : licenses + # https://github.com/dotnet/diagnostics/blob/master/documentation/dotnet-trace-instructions.md#using-dotnet-trace trace : ## Trace the dotnet process `Database` dotnet tool run \ diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index ec3e7e45..19e84d5d 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -67,6 +67,12 @@ "commands": [ "jb" ] + }, + "dotnet-project-licenses": { + "version": "2.6.0", + "commands": [ + "dotnet-project-licenses" + ] } } } \ No newline at end of file diff --git a/frontend/Makefile b/frontend/Makefile index 25085a01..b6fdedd3 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -29,6 +29,15 @@ lint : ## Lint code next lint .PHONY : lint +licenses : ## Print licenses + yarn run \ + license-checker \ + --unknown \ + --direct \ + --summary + # --failOn + # --onlyAllow + update-schema : ## Update GraphQl schema wget \ --output-document ./type-defs.graphqls \ diff --git a/frontend/package.json b/frontend/package.json index 77ba738c..1fa9835e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -49,6 +49,7 @@ "graphql-let": "^0.18", "graphql-tag": "^2.0", "jest": "^29.0", + "license-checker": "^25.0.1", "react-test-renderer": "^18.0", "ts-node": "^10.9.1", "typescript": "^4.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 04e2f8f6..c73b2c15 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2147,6 +2147,11 @@ dependencies: tslib "^2.3.0" +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2315,6 +2320,11 @@ aria-query@^5.1.3: dependencies: deep-equal "^2.0.5" +array-find-index@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + array-includes@^3.1.5, array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" @@ -2367,7 +2377,7 @@ array.prototype.tosorted@^1.1.1: es-shim-unscopables "^1.0.0" get-intrinsic "^1.1.3" -asap@~2.0.3: +asap@^2.0.0, asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -2689,7 +2699,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@^2.0.0: +chalk@^2.0.0, chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3035,13 +3045,18 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3125,6 +3140,14 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +dezalgo@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + diff-sequences@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" @@ -3919,7 +3942,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -4063,6 +4086,11 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react- dependencies: react-is "^16.7.0" +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -5083,6 +5111,22 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +license-checker@^25.0.1: + version "25.0.1" + resolved "https://registry.yarnpkg.com/license-checker/-/license-checker-25.0.1.tgz#4d14504478a5240a857bb3c21cd0491a00d761fa" + integrity sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g== + dependencies: + chalk "^2.4.1" + debug "^3.1.0" + mkdirp "^0.5.1" + nopt "^4.0.1" + read-installed "~4.0.3" + semver "^5.5.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + spdx-satisfies "^4.0.0" + treeify "^1.1.0" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -5290,6 +5334,13 @@ minimist@^1.2.0, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -5392,6 +5443,24 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -5404,6 +5473,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-normalize-package-bin@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" @@ -5567,11 +5641,24 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-tmpdir@~1.0.2: +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -6241,6 +6328,30 @@ react@^18.2.0: dependencies: loose-envify "^1.1.0" +read-installed@~4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" + integrity sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ== + dependencies: + debuglog "^1.0.1" + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + semver "2 || 3 || 4 || 5" + slide "~1.1.3" + util-extend "^1.0.1" + optionalDependencies: + graceful-fs "^4.1.2" + +read-package-json@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^2.0.0" + npm-normalize-package-bin "^1.0.0" + readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -6250,6 +6361,16 @@ readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readdir-scoped-modules@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -6337,7 +6458,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== -resolve@^1.20.0, resolve@^1.22.1: +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -6446,6 +6567,11 @@ scuid@^1.1.0: resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== +"semver@2 || 3 || 4 || 5", semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -6551,6 +6677,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" +slide@~1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -6582,6 +6713,55 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdx-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7" + integrity sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A== + dependencies: + array-find-index "^1.0.2" + spdx-expression-parse "^3.0.0" + spdx-ranges "^2.0.0" + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.12" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" + integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + +spdx-ranges@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/spdx-ranges/-/spdx-ranges-2.1.1.tgz#87573927ba51e92b3f4550ab60bfc83dd07bac20" + integrity sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA== + +spdx-satisfies@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz#9a09a68d80f5f1a31cfaebb384b0c6009e4969fe" + integrity sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA== + dependencies: + spdx-compare "^1.0.0" + spdx-expression-parse "^3.0.0" + spdx-ranges "^2.0.0" + sponge-case@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" @@ -6842,6 +7022,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + ts-invariant@^0.9.0: version "0.9.4" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.9.4.tgz#42ac6c791aade267dd9dc65276549df5c5d71cac" @@ -7041,6 +7226,11 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util-extend@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" + integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -7060,6 +7250,14 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + value-or-promise@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" From 780e9a1a721c73129f7a2736f395a923c3d98e82 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 14:50:32 +0100 Subject: [PATCH 041/878] Add missing step needed to init, fetch and checkout submodules --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0160ebab..5c72516a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ If you have a question for which you don't find the answer in this repository, p 1. Clone the source code by running `git clone git@github.com:building-envelope-data/database.git` and navigate into the new directory `database` by running `cd database`. +1. Initialize, fetch, and checkout possibly-nested submodules by running + `git submodule update --init --recursive`. An alternative would have been + passing `--recurse-submodules` to `git clone` above. 1. Prepare your environment by running `cp .env.sample .env`, `cp frontend/.env.local.sample frontend/.env.local`, and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. From d7bda95a90e793049567017dd0eb94c8c61c8e77 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 15:25:03 +0100 Subject: [PATCH 042/878] Remove empty backup directories before pruning (such may exist when a backup process failed) --- Makefile.production | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.production b/Makefile.production index c15a1da6..b50fbbfb 100644 --- a/Makefile.production +++ b/Makefile.production @@ -182,6 +182,9 @@ backup : ## Backup database and related data to directory with absolute path `${ # Inspired by https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash/34862475#34862475 prune-backups : ## Keep the most recent 7 backups, delete the rest + find /app/data/backups \ + -type d \ + -execdir rmdir --ignore-fail-on-non-empty '{}' \; cd /app/data/backups && \ ls -t --indicator-style=slash \ | grep '/$$' \ From 6e74f7f64aa42edf4cd164ea150e6e71f261bd1a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 15:41:32 +0100 Subject: [PATCH 043/878] Make targets that start temporary containers more robust by cleaning up before if necessary --- Makefile.production | 74 ++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/Makefile.production b/Makefile.production index b50fbbfb..a824ead9 100644 --- a/Makefile.production +++ b/Makefile.production @@ -81,19 +81,22 @@ list-services : ## List all services specified in the docker-compose file (used --services .PHONY : list-services +createdb : CONTAINER_NAME = create_${NAME}_database createdb : ## Create database + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} run \ - --name create_${NAME}_database \ + --name ${CONTAINER_NAME} \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} create_${NAME}_database) != "healthy" ]; do sleep 1; done + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done docker exec \ - create_${NAME}_database \ + ${CONTAINER_NAME} \ createdb \ --username postgres \ xbase - docker container stop create_${NAME}_database - docker container rm --volumes create_${NAME}_database + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} .PHONY : createdb psql : ## Enter PostgreSQL interactive terminal in the running `database` service @@ -144,29 +147,35 @@ docker-stats : ## Show Docker run-time metrics # Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ +backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database +backup : FILES_CONTAINER_NAME = backup_${NAME}_files backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` mkdir --parents ${BACKUP_DIRECTORY} + -docker container stop ${DATABASE_CONTAINER_NAME} + -docker container rm --volumes ${DATABASE_CONTAINER_NAME} ${docker_compose} run \ - --name backup_${NAME}_database \ + --name ${DATABASE_CONTAINER_NAME} \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} backup_${NAME}_database) != "healthy" ]; do sleep 1; done + while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done docker exec \ - backup_${NAME}_database \ + ${DATABASE_CONTAINER_NAME} \ pg_dumpall \ --clean \ --username=postgres \ | gzip \ > ${BACKUP_DIRECTORY}/${dump_archive_name} - docker container stop backup_${NAME}_database - docker container rm --volumes backup_${NAME}_database + docker container stop ${DATABASE_CONTAINER_NAME} + docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop ${FILES_CONTAINER_NAME} + -docker container rm --volumes ${FILES_CONTAINER_NAME} ${docker_compose} run \ - --name backup_${NAME}_files \ + --name ${FILES_CONTAINER_NAME} \ --detach \ backend docker run \ --rm \ - --volumes-from backup_${NAME}_files \ + --volumes-from ${FILES_CONTAINER_NAME} \ --volume ${BACKUP_DIRECTORY}:/backup \ debian:bullseye-slim \ tar \ @@ -176,8 +185,8 @@ backup : ## Backup database and related data to directory with absolute path `${ --file=/backup/${files_archive_name} \ --directory=/app \ ./files - docker container stop backup_${NAME}_files - docker container rm --volumes backup_${NAME}_files + docker container stop ${FILES_CONTAINER_NAME} + docker container rm --volumes ${FILES_CONTAINER_NAME} .PHONY : backup # Inspired by https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash/34862475#34862475 @@ -195,30 +204,36 @@ prune-backups : ## Keep the most recent 7 backups, delete the rest rm --recursive --dir -- .PHONY : prune-backups +restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database +restore : FILES_CONTAINER_NAME = restore_${NAME}_files restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore` + -docker container stop ${DATABASE_CONTAINER_NAME} + -docker container rm --volumes ${DATABASE_CONTAINER_NAME} ${docker_compose} run \ - --name restore_${NAME}_database \ + --name ${DATABASE_CONTAINER_NAME} \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} restore_${NAME}_database) != "healthy" ]; do sleep 1; done + while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ | docker exec \ --interactive \ - restore_${NAME}_database \ + ${DATABASE_CONTAINER_NAME} \ psql \ --echo-all \ --file=- \ --username=postgres \ --dbname=postgres - docker container stop restore_${NAME}_database - docker container rm --volumes restore_${NAME}_database + docker container stop ${DATABASE_CONTAINER_NAME} + docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop ${FILES_CONTAINER_NAME} + -docker container rm --volumes ${FILES_CONTAINER_NAME} ${docker_compose} run \ - --name backup_${NAME}_files \ + --name ${FILES_CONTAINER_NAME} \ --detach \ backend docker run \ --rm \ - --volumes-from backup_${NAME}_files \ + --volumes-from ${FILES_CONTAINER_NAME} \ --volume ${BACKUP_DIRECTORY}:/backup \ debian:bullseye-slim \ bash -cx " \ @@ -235,8 +250,8 @@ restore : ## Restore database and related data from directory with absolute path --strip-components=2 \ --file=/backup/${files_archive_name} \ " - docker container stop backup_${NAME}_files - docker container rm --volumes backup_${NAME}_files + docker container stop ${FILES_CONTAINER_NAME} + docker container rm --volumes ${FILES_CONTAINER_NAME} .PHONY : restore begin-maintenance : ## Begin maintenance @@ -276,23 +291,26 @@ checkout-target : ## Fetch and checkout `${TARGET}` migrate : FROM = $(file < ./backend/src/Migrations/previous) migrate : TO = $(file < ./backend/src/Migrations/current) +migrate : CONTAINER_NAME = migrate_${NAME}_database migrate : ## Migrate database (note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} run \ - --name migrate_${NAME}_database \ + --name ${CONTAINER_NAME} \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} migrate_${NAME}_database) != "healthy" ]; do sleep 1; done + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done cat ./backend/src/Migrations/migrate_from_${FROM}_to_${TO}.sql \ | docker exec \ --interactive \ - migrate_${NAME}_database \ + ${CONTAINER_NAME} \ psql \ --echo-all \ --file=- \ --username=postgres \ --dbname=xbase - docker container stop migrate_${NAME}_database - docker container rm --volumes migrate_${NAME}_database + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} .PHONY : migrate # Note that NGINX is because of its dependencies taken down and up last and in From 21d30d1bb17a71e6559910d9935cc67abfa3329c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 15:52:03 +0100 Subject: [PATCH 044/878] Remove only empty directories that are exactly one level deep --- Makefile.production | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.production b/Makefile.production index a824ead9..58285ebc 100644 --- a/Makefile.production +++ b/Makefile.production @@ -192,6 +192,8 @@ backup : ## Backup database and related data to directory with absolute path `${ # Inspired by https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash/34862475#34862475 prune-backups : ## Keep the most recent 7 backups, delete the rest find /app/data/backups \ + -mindepth 1 \ + -maxdepth 1 \ -type d \ -execdir rmdir --ignore-fail-on-non-empty '{}' \; cd /app/data/backups && \ From 6c47717ed38504829c0716379504e8a59cc3ef29 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 17:06:58 +0100 Subject: [PATCH 045/878] Put general information on using Docker in production to the beginning of the section --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5c72516a..bc73eb03 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,10 @@ The same works for frontend containers by running `make shellf`. ## Deployment +For information on using Docker in production see +[Configure and troubleshoot the Docker daemon](https://docs.docker.com/config/daemon/) +and the pages following it. + ### Setting up a Debian production machine 1. Use the sibling project [machine](https://github.com/building-envelope-data/machine) and its @@ -112,18 +116,14 @@ The same works for frontend containers by running `make shellf`. figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by - running `cd ./machine && make deploy && cd ..` and test whether everything - works as expected and if that is the case, repeat all stages but this one in - the directory `/app/production` (instead of `/app/staging`). Note that in - the staging environment sent emails can be viewed in the web browser under - `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses in - the variable `RELAY_ALLOWED_EMAILS` in the `.env` file are delivered to the - respective inboxes (the variable's value is a comma separated list of email - addresses). - -For information on using Docker in production see -[Configure and troubleshoot the Docker daemon](https://docs.docker.com/config/daemon/) -and the pages following it. + running `cd /app/machine && make deploy` and test whether everything works + as expected and if that is the case, repeat all stages but this one in the + directory `/app/production` (instead of `/app/staging`). Note that in the + staging environment sent emails can be viewed in the web browser under + `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses + in the variable `RELAY_ALLOWED_EMAILS` in the `.env` file are delivered to + the respective inboxes (the variable's value is a comma separated list of + email addresses). ## Useful Resources From b502cee2208fa8ab9d58c5b73a1bec54e45dd17f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Feb 2023 17:26:20 +0100 Subject: [PATCH 046/878] Move non-project-specific GNU Make targets regarding Docker to the machine project, see https://github.com/building-envelope-data/machine/commit/be9784c5b075692e64b645d7f93815ddd9a9595b --- Makefile.production | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Makefile.production b/Makefile.production index 58285ebc..0269c000 100644 --- a/Makefile.production +++ b/Makefile.production @@ -129,20 +129,6 @@ postgres_passwords : postgres_password ## Generate PostgreSQL passwords file who > ./secrets/postgres_passwords .PHONY : postgres_passwords -# See https://docs.docker.com/config/daemon/#view-stack-traces -daemon-logs : ## View Docker daemon logs - sudo journalctl --unit docker.service -.PHONY : daemon-logs - -reload-daemon : ## Reload Docker daemon - sudo systemctl reload docker -.PHONY : reload-daemon - -# See https://docs.docker.com/config/containers/runmetrics/ -docker-stats : ## Show Docker run-time metrics - docker stats -.PHONY : docker-stats - # Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL # Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes From c741b8e730547b2b107c67461f7037f8a62f650d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Feb 2023 14:41:13 +0100 Subject: [PATCH 047/878] Correct usage of log level overrides -- see https://github.com/serilog/serilog-settings-configuration#minimumlevel-levelswitches-overrides-and-dynamic-reload --- backend/src/appsettings.development.json | 6 ++++-- backend/src/appsettings.production.json | 5 ++--- backend/src/appsettings.test.json | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index d41949da..a2c365de 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -5,8 +5,10 @@ "Serilog": { "MinimumLevel": { "Default": "Debug", - "System": "Debug", - "Microsoft": "Debug" + "Override": { + "System": "Debug", + "Microsoft": "Debug" + } } }, "Database": { diff --git a/backend/src/appsettings.production.json b/backend/src/appsettings.production.json index e331280a..ca27b03a 100644 --- a/backend/src/appsettings.production.json +++ b/backend/src/appsettings.production.json @@ -5,11 +5,10 @@ "Serilog": { "MinimumLevel": { "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information", "Override": { - "Microsoft": "Error", + "Microsoft.Hosting.Lifetime": "Information", "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.DataProtection": "Information", "System": "Error" } } diff --git a/backend/src/appsettings.test.json b/backend/src/appsettings.test.json index 19b6f55c..1defef61 100644 --- a/backend/src/appsettings.test.json +++ b/backend/src/appsettings.test.json @@ -8,6 +8,7 @@ "Override": { "Microsoft": "Error", "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.DataProtection": "Warning", "System": "Error" } } From bbac9c7e84a21dcc67bc5a9446032b862af51b5b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Feb 2023 14:57:26 +0100 Subject: [PATCH 048/878] Persist data protection keys in database --- backend/src/Data/ApplicationDbContext.cs | 4 +- backend/src/Database.csproj | 1 + ...10135413_AddDataProtectionKeys.Designer.cs | 1071 +++++++++++++++++ .../20230210135413_AddDataProtectionKeys.cs | 38 + .../ApplicationDbContextModelSnapshot.cs | 21 +- backend/src/Startup.cs | 4 + 6 files changed, 1137 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs create mode 100644 backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 90ec7de7..36eae441 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Npgsql; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; @@ -9,7 +10,7 @@ namespace Database.Data // [Authentication and authorization for SPAs](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0) // [Customize Identity Model](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.0) public sealed class ApplicationDbContext - : DbContext + : DbContext, IDataProtectionKeyContext { [System.Obsolete] static ApplicationDbContext() @@ -70,6 +71,7 @@ ModelBuilder builder public DbSet HygrothermalData { get; private set; } = default!; public DbSet OpticalData { get; private set; } = default!; public DbSet PhotovoltaicData { get; private set; } = default!; + public DbSet DataProtectionKeys { get; private set; } = default!; public ApplicationDbContext( DbContextOptions options diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index f1251739..d951ee9b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -21,6 +21,7 @@ + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs new file mode 100644 index 00000000..01011be0 --- /dev/null +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs @@ -0,0 +1,1071 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230210135413_AddDataProtectionKeys")] + partial class AddDataProtectionKeys + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs new file mode 100644 index 00000000..76f93ae7 --- /dev/null +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddDataProtectionKeys : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "DataProtectionKeys", + schema: "database", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FriendlyName = table.Column(type: "text", nullable: true), + Xml = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DataProtectionKeys", + schema: "database"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 5d355771..8f4478c0 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; @@ -277,6 +277,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("photovoltaic_data", "database"); }); + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index eec49943..0f2dd4b2 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -7,6 +7,7 @@ using Database.Data.Extensions; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -43,6 +44,9 @@ public void ConfigureServices(IServiceCollection services) ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); ConfigureSessionServices(services); + services + .AddDataProtection() + .PersistKeysToDbContext(); services.AddHttpClient(); services .AddHealthChecks() From 3f935dae23e0d2f6e0abe62332a9a18b359abe7c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 13 Feb 2023 17:57:00 +0100 Subject: [PATCH 049/878] Add SQL scripts to migrate and rollback to `AddDataProtectionKeys` --- backend/src/Migrations/current | 2 +- ...ion_to_20230210135413_AddDataProtectionKeys.sql | 14 ++++++++++++++ backend/src/Migrations/previous | 2 +- ...ion_to_20230210135413_AddDataProtectionKeys.sql | 9 +++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql create mode 100644 backend/src/Migrations/rollback_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index eda70dee..89cc0f5e 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20230126153325_RenameXminToVersion +20230210135413_AddDataProtectionKeys diff --git a/backend/src/Migrations/migrate_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql b/backend/src/Migrations/migrate_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql new file mode 100644 index 00000000..5066a12f --- /dev/null +++ b/backend/src/Migrations/migrate_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql @@ -0,0 +1,14 @@ +START TRANSACTION; + +CREATE TABLE database."DataProtectionKeys" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "FriendlyName" text NULL, + "Xml" text NULL, + CONSTRAINT "PK_DataProtectionKeys" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230210135413_AddDataProtectionKeys', '7.0.2'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index d51fb881..eda70dee 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20211019125407_InitialCreate +20230126153325_RenameXminToVersion diff --git a/backend/src/Migrations/rollback_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql b/backend/src/Migrations/rollback_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql new file mode 100644 index 00000000..41b44215 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20230126153325_RenameXminToVersion_to_20230210135413_AddDataProtectionKeys.sql @@ -0,0 +1,9 @@ +START TRANSACTION; + +DROP TABLE database."DataProtectionKeys"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20230210135413_AddDataProtectionKeys'; + +COMMIT; + From b278b602482dede9e8278128aa24b917a8142ac7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 13 Feb 2023 14:07:53 +0100 Subject: [PATCH 050/878] Upgrade to latest compatible minor versions --- frontend/yarn.lock | 1846 ++++++++++++++++++++++---------------------- 1 file changed, 910 insertions(+), 936 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c73b2c15..49832ca7 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -105,7 +105,7 @@ signedsource "^1.0.0" yargs "^15.3.1" -"@ardatan/sync-fetch@0.0.1": +"@ardatan/sync-fetch@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== @@ -120,9 +120,9 @@ "@babel/highlight" "^7.18.6" "@babel/compat-data@^7.20.5": - version "7.20.10" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec" - integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg== + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" + integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== "@babel/core@7.15.5": version "7.15.5" @@ -167,9 +167,9 @@ semver "^6.3.0" "@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.20.7", "@babel/generator@^7.7.2": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a" - integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw== + version "7.20.14" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" + integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== dependencies: "@babel/types" "^7.20.7" "@jridgewell/gen-mapping" "^0.3.2" @@ -344,9 +344,9 @@ integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== "@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.13.tgz#ddf1eb5a813588d2fb1692b70c6fce75b945c088" - integrity sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw== + version "7.20.15" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" + integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -494,9 +494,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz#9f5a3424bd112a3f32fe0cf9364fbb155cff262a" - integrity sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw== + version "7.20.15" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz#3e1b2aa9cbbe1eb8d644c823141a9c5c2a22392d" + integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -746,14 +746,14 @@ tslib "~2.4.0" "@graphql-codegen/cli@^2.0.1": - version "2.16.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.16.4.tgz#c8e6df2dc8cccfd61a088de0ada9a05842ad8ad6" - integrity sha512-MBbdzIIaNZ8BTlFXG00toxU5rIV7Ltf2myaze88HpI5YPVfVJKlfccE6l0/Gv+nLv88CIM/PZrnFLdVtlBmrZw== + version "2.16.5" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.16.5.tgz#b3b5eeec357af01c1cb72f6a4ea96e52bd49e662" + integrity sha512-XYPIp+q7fB0xAGSAoRykiTe4oY80VU+z+dw5nuv4mLY0+pv7+pa2C6Nwhdw7a65lXOhFviBApWCCZeqd54SMnA== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/core" "2.6.8" + "@graphql-codegen/core" "^2.6.8" "@graphql-codegen/plugin-helpers" "^3.1.2" "@graphql-tools/apollo-engine-loader" "^7.3.6" "@graphql-tools/code-file-loader" "^7.3.13" @@ -761,7 +761,7 @@ "@graphql-tools/github-loader" "^7.3.20" "@graphql-tools/graphql-file-loader" "^7.5.0" "@graphql-tools/json-file-loader" "^7.4.1" - "@graphql-tools/load" "7.8.0" + "@graphql-tools/load" "^7.8.0" "@graphql-tools/prisma-loader" "^7.2.49" "@graphql-tools/url-loader" "^7.13.2" "@graphql-tools/utils" "^9.0.0" @@ -769,10 +769,10 @@ chalk "^4.1.0" chokidar "^3.5.2" cosmiconfig "^7.0.0" - cosmiconfig-typescript-loader "4.3.0" + cosmiconfig-typescript-loader "^4.3.0" debounce "^1.2.0" detect-indent "^6.0.0" - graphql-config "4.4.0" + graphql-config "^4.4.0" inquirer "^8.0.0" is-glob "^4.0.1" json-to-pretty-yaml "^1.2.2" @@ -781,11 +781,12 @@ shell-quote "^1.7.3" string-env-interpolation "^1.0.1" ts-log "^2.2.3" + ts-node "^10.9.1" tslib "^2.4.0" yaml "^1.10.0" yargs "^17.0.0" -"@graphql-codegen/core@2.6.8": +"@graphql-codegen/core@^2.6.8": version "2.6.8" resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.6.8.tgz#00c4011e3619ddbc6af5e41b2f254d6f6759556e" integrity sha512-JKllNIipPrheRgl+/Hm/xuWMw9++xNQ12XJR/OHHgFopOg4zmN3TdlRSyYcv/K90hCFkkIwhlHFUQTfKrm8rxQ== @@ -858,13 +859,13 @@ tslib "~2.4.0" "@graphql-codegen/typescript-operations@^2.0.1": - version "2.5.12" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.12.tgz#36af48b34d70d98a9a2adea1ab79e26fcab23a92" - integrity sha512-/w8IgRIQwmebixf514FOQp2jXOe7vxZbMiSFoQqJgEgzrr42joPsgu4YGU+owv2QPPmu4736OcX8FSavb7SLiA== + version "2.5.13" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.13.tgz#f286c37f9c023356aacaa983ebd32e9e021a05ca" + integrity sha512-3vfR6Rx6iZU0JRt29GBkFlrSNTM6t+MSLF86ChvL4d/Jfo/JYAGuB3zNzPhirHYzJPCvLOAx2gy9ID1ltrpYiw== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-codegen/typescript" "^2.8.7" - "@graphql-codegen/visitor-plugin-common" "2.13.7" + "@graphql-codegen/typescript" "^2.8.8" + "@graphql-codegen/visitor-plugin-common" "2.13.8" auto-bind "~4.0.0" tslib "~2.4.0" @@ -892,13 +893,13 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^2.0.0": - version "2.7.12" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.7.12.tgz#256c52caec517093eec2edfbf9c48206dbe9ba55" - integrity sha512-4Fnfjh4HO6SU8u5phmrY9CJGW1EcRMvaWElydNA9YO2seGCj7nHkGocB24mLlM45CSX5mXuSOgos1nuO5GniSw== + version "2.7.13" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.7.13.tgz#169ba75bf686a96c2222ac8c5141e75ab6b4cb20" + integrity sha512-ZIXiUw86ctNYQe41RAwadhSMe0koNvomxNRK+ZYlWQn1gplpc3jcdgI+BrdJbBWK/6VBYKZzrayJHr1VsHfYJQ== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-codegen/typescript" "^2.8.7" - "@graphql-codegen/visitor-plugin-common" "2.13.7" + "@graphql-codegen/typescript" "^2.8.8" + "@graphql-codegen/visitor-plugin-common" "2.13.8" "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" tslib "~2.4.0" @@ -913,14 +914,14 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.8.7": - version "2.8.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.8.7.tgz#da34261b779a001d7d53b8f454bafc002b06e041" - integrity sha512-Nm5keWqIgg/VL7fivGmglF548tJRP2ttSmfTMuAdY5GNGTJTVZOzNbIOfnbVEDMMWF4V+quUuSyeUQ6zRxtX1w== +"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.8.8": + version "2.8.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.8.8.tgz#8c3b9153e334db43c65f8f31ced69b4c60d14861" + integrity sha512-A0oUi3Oy6+DormOlrTC4orxT9OBZkIglhbJBcDmk34jAKKUgesukXRd4yOhmTrnbchpXz2T8IAOFB3FWIaK4Rw== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.2" "@graphql-codegen/schema-ast" "^2.6.1" - "@graphql-codegen/visitor-plugin-common" "2.13.7" + "@graphql-codegen/visitor-plugin-common" "2.13.8" auto-bind "~4.0.0" tslib "~2.4.0" @@ -956,10 +957,10 @@ parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@2.13.7": - version "2.13.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.7.tgz#591e054a970a0d572bdfb765bf948dae21bf8aed" - integrity sha512-xE6iLDhr9sFM1qwCGJcCXRu5MyA0moapG2HVejwyAXXLubYKYwWnoiEigLH2b5iauh6xsl6XP8hh9D1T1dn5Cw== +"@graphql-codegen/visitor-plugin-common@2.13.8": + version "2.13.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz#09bc6317b227e5a278f394f4cef0d6c2d1910597" + integrity sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.2" "@graphql-tools/optimize" "^1.3.0" @@ -973,142 +974,142 @@ tslib "~2.4.0" "@graphql-tools/apollo-engine-loader@^7.3.6": - version "7.3.22" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.22.tgz#ace09442dd0aa758a7a42dac3b73252c7935c531" - integrity sha512-4zbL2k7Tcr+qDHBmqKTfrxgOgGkRw0x8NAmrNQVyDYhpP9NiRANmq4DTUgqSPEFiZ6Dx6FYGD4fldRq1JYSYqQ== + version "7.3.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.26.tgz#91e54460d5579933e42a2010b8688c3459c245d8" + integrity sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ== dependencies: - "@ardatan/sync-fetch" "0.0.1" - "@graphql-tools/utils" "9.1.4" - "@whatwg-node/fetch" "^0.6.0" + "@ardatan/sync-fetch" "^0.0.1" + "@graphql-tools/utils" "^9.2.1" + "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@8.5.15": - version "8.5.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.15.tgz#f61ac71d11e57c9b9f8b8b60fc882e4e9762d182" - integrity sha512-qb12M8XCK6SBJmZDS8Lzd4PVJFsIwNUkYmFuqcTiBqOI/WsoDlQDZI++ghRpGcusLkL9uzcIOTT/61OeHhsaLg== +"@graphql-tools/batch-execute@8.5.17": + version "8.5.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.17.tgz#1d3294becfd2c3c6bddfe56ff3507ce89ca236a2" + integrity sha512-ma6zlFIBG8VuqSwE8jhYhMbaFsJ1YdVsnpFmbQ0O/qJTmlgdAWCyAZTJH0aZ24fqNFfj/vW/Qtpqn7gRcF8QOw== dependencies: - "@graphql-tools/utils" "9.1.4" - dataloader "2.1.0" + "@graphql-tools/utils" "9.2.1" + dataloader "2.2.1" tslib "^2.4.0" value-or-promise "1.0.12" "@graphql-tools/code-file-loader@^7.3.13": - version "7.3.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.16.tgz#58aa85c250175cebe0ea4309214357768d550f93" - integrity sha512-109UFvQjZEntHwjPaHpWvgUudHenGngbXvSImabPc2fdrtgja5KC0h7thCg379Yw6IORHGrF2XbJwS1hAGPPWw== + version "7.3.20" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.20.tgz#6f523bcb5b5bcdbcede4b5894cbcb1890132b002" + integrity sha512-htwylU+/if5j5rgrd/i2xgM22cWC2RGgUGO7K+nxZU+l7iCimJUdDQnqCW9G3eVHbLpVOhyza9bBUNMPzh3sxg== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.4.3" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/graphql-tag-pluck" "7.4.6" + "@graphql-tools/utils" "9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@9.0.22": - version "9.0.22" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.22.tgz#12f27ef76c5add456fa8797a496bb7dc82071771" - integrity sha512-dWJGMN8V7KORtbI8eDAjHYTWiMyis/md27M6pPhrlYVlcsDk3U0jbNdgkswBBUEBvqumPRCv8pVOxKcLS4caKA== - dependencies: - "@graphql-tools/batch-execute" "8.5.15" - "@graphql-tools/executor" "0.0.12" - "@graphql-tools/schema" "9.0.13" - "@graphql-tools/utils" "9.1.4" - dataloader "2.1.0" - tslib "~2.4.0" +"@graphql-tools/delegate@9.0.26", "@graphql-tools/delegate@^9.0.26": + version "9.0.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.26.tgz#b931607bf11db26da307d12cc2a97eec82a677f1" + integrity sha512-RPcjH+NnK3e4e9/6CwKbyv9DtVa+ojiwvsbW9Q6zMXRdlP0zazsQOe5+ktL3yE+d3zlzGAasp0WAiSLUS5vFRw== + dependencies: + "@graphql-tools/batch-execute" "8.5.17" + "@graphql-tools/executor" "0.0.14" + "@graphql-tools/schema" "9.0.16" + "@graphql-tools/utils" "9.2.1" + dataloader "2.2.1" + tslib "~2.5.0" value-or-promise "1.0.12" -"@graphql-tools/executor-graphql-ws@0.0.7": - version "0.0.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.7.tgz#5e7b0e6d02d3b64727bfb37c0f2c1e13badcb829" - integrity sha512-C6EExKoukn4vu3BbvlqsqtC91F4pTLPDZvRceYjpFzTCQSGFSjfrxQGP/haGlemXVRpIDxBy7wpXoQlsF8UmFA== +"@graphql-tools/executor-graphql-ws@^0.0.10": + version "0.0.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.10.tgz#c4775ac3730a0017392373f84aa21bfc4de4495b" + integrity sha512-5SxFvupyWe6+Egq8Zws0+mJZMKV18rTAwxHwhrx+KhRfGpilqkqS4I+qwVL94LNktWL2uy95cU5b5CQFyVaVEg== dependencies: - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" "@repeaterjs/repeater" "3.0.4" "@types/ws" "^8.0.0" - graphql-ws "5.11.2" + graphql-ws "5.11.3" isomorphic-ws "5.0.0" tslib "^2.4.0" ws "8.12.0" -"@graphql-tools/executor-http@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.1.tgz#9c4a43811d36d1be2319fd241d5538e2fac45bce" - integrity sha512-bFE6StI7CJEIYGRkAnTYxutSV4OtC1c4MQU3nStOYZZO7KmzIgEQZ4ygPSPrRb+jtRsMCBEqPqlYOD4Rq02aMw== +"@graphql-tools/executor-http@^0.1.7": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.8.tgz#129fc3095370ecd39cad68103d97ad8aa767e334" + integrity sha512-L3tldMlL760Bkdl6zdearvxqCA8cRmTWvd8SyZ0i/EpBWFkU1XcUF2/wqNt7URdNAE6+pO6wCgI4BEsKOgL7Qg== dependencies: - "@graphql-tools/utils" "9.1.4" - "@repeaterjs/repeater" "3.0.4" - "@whatwg-node/fetch" "0.6.2" - dset "3.1.2" + "@graphql-tools/utils" "^9.2.1" + "@repeaterjs/repeater" "^3.0.4" + "@whatwg-node/fetch" "^0.8.1" + dset "^3.1.2" extract-files "^11.0.0" - meros "1.2.1" + meros "^1.2.1" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" -"@graphql-tools/executor-legacy-ws@0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.6.tgz#236dd5b3d4d19492978b1458b74928e8c69d16ff" - integrity sha512-L1hRuSvBUCNerYDhfeSZXFeqliDlnNXa3fDHTp7efI3Newpbevqa19Fm0mVzsCL7gqIKOwzrPORwh7kOVE/vew== +"@graphql-tools/executor-legacy-ws@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.8.tgz#3387f50eafacfb05ac578baf46c0ea0fe059494e" + integrity sha512-NZfBijmr774rCO60cRTqbf2otRjn32sVikq6PdT+0vZfhVwX7wydNMdyfJZQ3WGuTyab5hrqOWD+UU8VcIbAeg== dependencies: - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" "@types/ws" "^8.0.0" isomorphic-ws "5.0.0" tslib "^2.4.0" ws "8.12.0" -"@graphql-tools/executor@0.0.12": - version "0.0.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.12.tgz#d885c7fa98a8aaeaa771163b71fb98ce9f52f9bd" - integrity sha512-bWpZcYRo81jDoTVONTnxS9dDHhEkNVjxzvFCH4CRpuyzD3uL+5w3MhtxIh24QyWm4LvQ4f+Bz3eMV2xU2I5+FA== +"@graphql-tools/executor@0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.14.tgz#7c6073d75c77dd6e7fab0c835761ed09c85a3bc6" + integrity sha512-YiBbN9NT0FgqPJ35+Eg0ty1s5scOZTgiPf+6hLVJBd5zHEURwojEMCTKJ9e0RNZHETp2lN+YaTFGTSoRk0t4Sw== dependencies: - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" "@graphql-typed-document-node/core" "3.1.1" "@repeaterjs/repeater" "3.0.4" tslib "^2.4.0" value-or-promise "1.0.12" "@graphql-tools/git-loader@^7.2.13": - version "7.2.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.16.tgz#8c025cad12a623ac91e421eb01380cc177070e7c" - integrity sha512-8DsxYfSouhgKPOBcc7MzuOTM4M/j2UNFn2ehXD0MX9q41t3dKffufJZKsKxE6VyyCUoVYdlRFhUWEyOHPVdcfQ== + version "7.2.19" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.19.tgz#42b12ef34b9e848d885a0ba22d987d798755aacc" + integrity sha512-F94PqVdBXbOETg7x081rJec+2egi/4TgXQWlvHdQ8jjrNd+C/EU+Dxq0ccmfnhUKdcVyKJpMpLUIUyY7uwX6gw== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.4.3" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/graphql-tag-pluck" "7.4.6" + "@graphql-tools/utils" "9.2.1" is-glob "4.0.3" micromatch "^4.0.4" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/github-loader@^7.3.20": - version "7.3.23" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.23.tgz#9688f4b9cd9596229f0f8c7eed7a8f500806defa" - integrity sha512-oYTZCvW520KNVVonjucDSMhabCFnHwtM1rJbyUkA1JFyzpmmNAAyNMWOOPcU/Q9rTESrsH+Hbja0mfpjpnBKLA== - dependencies: - "@ardatan/sync-fetch" "0.0.1" - "@graphql-tools/graphql-tag-pluck" "7.4.3" - "@graphql-tools/utils" "9.1.4" - "@whatwg-node/fetch" "^0.6.0" + version "7.3.27" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.27.tgz#77a2fbaeb7bf5f8edc4a865252ecb527a5399e01" + integrity sha512-fFFC35qenyhjb8pfcYXKknAt0CXP5CkQYtLfJXgTXSgBjIsfAVMrqxQ/Y0ejeM19XNF/C3VWJ7rE308yOX6ywA== + dependencies: + "@ardatan/sync-fetch" "^0.0.1" + "@graphql-tools/graphql-tag-pluck" "^7.4.6" + "@graphql-tools/utils" "^9.2.1" + "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" "@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": - version "7.5.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.14.tgz#6c6527e353cf9adcbda2cdc8a85face03ad8fe04" - integrity sha512-JGer4g57kq4wtsvqv8uZsT4ZG1lLsz1x5yHDfSj2OxyiWw2f1jFkzgby7Ut3H2sseJiQzeeDYZcbm06qgR32pg== + version "7.5.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.16.tgz#d954b25ee14c6421ddcef43f4320a82e9800cb23" + integrity sha512-lK1N3Y2I634FS12nd4bu7oAJbai3bUc28yeX+boT+C83KTO4ujGHm+6hPC8X/FRGwhKOnZBxUM7I5nvb3HiUxw== dependencies: - "@graphql-tools/import" "6.7.15" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/import" "6.7.17" + "@graphql-tools/utils" "9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@7.4.3": - version "7.4.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.4.3.tgz#b07f2263c383d9605d941c836dc01a7bbc6e56a7" - integrity sha512-w+nrJVQw+NTuaZNQG5AwSh4Qe+urP/s4rUz5s1T007rDnv1kvkiX+XHOCnIfJzXOTuvFmG4GGYw/x0CuSRaGZQ== +"@graphql-tools/graphql-tag-pluck@7.4.6", "@graphql-tools/graphql-tag-pluck@^7.4.6": + version "7.4.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.4.6.tgz#57fe6f37d0c7c5f8bf3dfbec58f70ec693e70b0a" + integrity sha512-KPlkrC+WtJAg/Sv93rPiDHZDsgQDIZEy9ViHqz80KdRvq0aeQN9TGp26mQCyD7zo1Ib2paT16IVwTNQf02yxpQ== dependencies: "@babel/parser" "^7.16.8" "@babel/plugin-syntax-import-assertions" "7.20.0" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" "@graphql-tools/import@6.4.0": @@ -1119,21 +1120,21 @@ resolve-from "5.0.0" tslib "~2.3.0" -"@graphql-tools/import@6.7.15": - version "6.7.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.15.tgz#7553e48140797255588b26d423a89aa042196928" - integrity sha512-WNhvauAt2I2iUg+JdQK5oQebKLXqUZWe8naP13K1jOkbTQT7hK3P/4I9AaVmzt0KXRJW5Uow3RgdHZ7eUBKVsA== +"@graphql-tools/import@6.7.17": + version "6.7.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.17.tgz#ab51ed08bcbf757f952abf3f40793ce3db42d4a3" + integrity sha512-bn9SgrECXq3WIasgNP7ful/uON51wBajPXtxdY+z/ce7jLWaFE6lzwTDB/GAgiZ+jo7nb0ravlxteSAz2qZmuA== dependencies: - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" resolve-from "5.0.0" tslib "^2.4.0" "@graphql-tools/json-file-loader@^7.3.7", "@graphql-tools/json-file-loader@^7.4.1": - version "7.4.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.15.tgz#ed229d98784350623d2ef32628690db405fa6780" - integrity sha512-pH+hbsDetcEpj+Tmi7ZRUkxzJez2DLdSQuvK5Qi38FX/Nz/5nZKRfW9nqIptGYbuS9+2JPrt9WWNn1aGtegIFQ== + version "7.4.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.17.tgz#3f08e74ab1a3534c02dc97875acc7f15aa460011" + integrity sha512-KOSTP43nwjPfXgas90rLHAFgbcSep4nmiYyR9xRVz4ZAmw8VYHcKhOLTSGylCAzi7KUfyBXajoW+6Z7dQwdn3g== dependencies: - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" @@ -1147,40 +1148,22 @@ tslib "^2.4.0" unixify "1.0.0" -"@graphql-tools/load@7.8.0": - version "7.8.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.0.tgz#bd4d2e2a5117de9a60f9691a218217e96afc2ea7" - integrity sha512-l4FGgqMW0VOqo+NMYizwV8Zh+KtvVqOf93uaLo9wJ3sS3y/egPCgxPMDJJ/ufQZG3oZ/0oWeKt68qop3jY0yZg== - dependencies: - "@graphql-tools/schema" "9.0.4" - "@graphql-tools/utils" "8.12.0" - p-limit "3.1.0" - tslib "^2.4.0" - -"@graphql-tools/load@^7.5.5": - version "7.8.9" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.9.tgz#5f4e523095b6154bac43e6a01acb6b043e9afaca" - integrity sha512-/eHRv6OCTI/Ir5XcbtSx0XbW3zOQVscp2MZQFGZKDzqCcGD+NVy4mLCoBwR/OsOUpvWAwMnc+Llb4SDKAYGmjQ== +"@graphql-tools/load@^7.5.5", "@graphql-tools/load@^7.8.0": + version "7.8.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.12.tgz#6457fe6ec8cd2e2b5ca0d2752464bc937d186cca" + integrity sha512-JwxgNS2c6i6oIdKttcbXns/lpKiyN7c6/MkkrJ9x2QE9rXk5HOhSJxRvPmOueCuAin1542xUrcDRGBXJ7thSig== dependencies: - "@graphql-tools/schema" "9.0.13" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/schema" "9.0.16" + "@graphql-tools/utils" "9.2.1" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@8.3.15", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": - version "8.3.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.15.tgz#9b24ee5e9c36074684515c7d1587cd3e200c8a8f" - integrity sha512-hYYOlsqkUlL6oOo7zzuk6hIv7xQzy+x21sgK84d5FWaiWYkLYh9As8myuDd9SD5xovWWQ9m/iRhIOVDEMSyEKA== - dependencies: - "@graphql-tools/utils" "9.1.4" - tslib "^2.4.0" - -"@graphql-tools/merge@8.3.6": - version "8.3.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.6.tgz#97a936d4c8e8f935e58a514bb516c476437b5b2c" - integrity sha512-uUBokxXi89bj08P+iCvQk3Vew4vcfL5ZM6NTylWi8PIpoq4r5nJ625bRuN8h2uubEdRiH8ntN9M4xkd/j7AybQ== +"@graphql-tools/merge@8.3.18", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": + version "8.3.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.18.tgz#bfbb517c68598a885809f16ce5c3bb1ebb8f04a2" + integrity sha512-R8nBglvRWPAyLpZL/f3lxsY7wjnAeE0l056zHhcO/CgpvK76KYUt9oEkR05i8Hmt8DLRycBN0FiotJ0yDQWTVA== dependencies: - "@graphql-tools/utils" "8.12.0" + "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" "@graphql-tools/optimize@^1.0.1", "@graphql-tools/optimize@^1.3.0": @@ -1191,12 +1174,12 @@ tslib "^2.4.0" "@graphql-tools/prisma-loader@^7.2.49": - version "7.2.54" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.54.tgz#b82d2193c198928f1d914dc088977fd6c502a4c9" - integrity sha512-OoH0JdMQ49IQwiZ8EKOE6sZX4oN5EQwToFnR+ptN5u4ukb4ignyZTrgePFmqOGgj7zyPzisxsWIUXD9szFXXNA== + version "7.2.63" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.63.tgz#7793997f57fc3c773dfe4b2343ffbf22a322d127" + integrity sha512-k4SLQDA2Kfag3axw3YINbjJWixzv9l32neW5sBAVU8YeDjumWASSTEKjnGhLaniOJQjqyvKc8Jt5Jo0ptjvRxA== dependencies: - "@graphql-tools/url-loader" "7.17.3" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/url-loader" "7.17.12" + "@graphql-tools/utils" "9.2.1" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" "@types/jsonwebtoken" "^9.0.0" @@ -1216,65 +1199,49 @@ yaml-ast-parser "^0.0.43" "@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.5.0": - version "6.5.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.15.tgz#35f9c40ec7b500dacd792592d7007d6ad5c7a10b" - integrity sha512-ILviTglS0eYc4e3rbQ65KlMZ3MWggxer5hro9iDWoN4+amlG3SNo8ejkgZtmI8uQL6Se0NcFt9eASB2SGd64pw== + version "6.5.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.17.tgz#4e4e2675d696a2a31f106b09ed436c43f7976f37" + integrity sha512-hHPEX6ccRF3+9kfVz0A3In//Dej7QrHOLGZEokBmPDMDqn9CS7qUjpjyGzclbOX0tRBtLfuFUZ68ABSac3P1nA== dependencies: "@ardatan/relay-compiler" "12.0.0" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@9.0.13", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13": - version "9.0.13" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.13.tgz#56b994777df29ac36586a3200fb6397abf7b9d83" - integrity sha512-guRA3fwAtv+M1Kh930P4ydH9aKJTWscIkhVFcWpj/cnjYYxj88jkEJ15ZNiJX/2breNY+sbVgmlgLKb6aXi/Jg== +"@graphql-tools/schema@9.0.16", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13": + version "9.0.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.16.tgz#7d340d69e6094dc01a2b9e625c7bb4fff89ea521" + integrity sha512-kF+tbYPPf/6K2aHG3e1SWIbapDLQaqnIHVRG6ow3onkFoowwtKszvUyOASL6Krcv2x9bIMvd1UkvRf9OaoROQQ== dependencies: - "@graphql-tools/merge" "8.3.15" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/merge" "8.3.18" + "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-tools/schema@9.0.4": - version "9.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.4.tgz#1a74608b57abf90fae6fd929d25e5482c57bc05d" - integrity sha512-B/b8ukjs18fq+/s7p97P8L1VMrwapYc3N2KvdG/uNThSazRRn8GsBK0Nr+FH+mVKiUfb4Dno79e3SumZVoHuOQ== - dependencies: - "@graphql-tools/merge" "8.3.6" - "@graphql-tools/utils" "8.12.0" - tslib "^2.4.0" - value-or-promise "1.0.11" - -"@graphql-tools/url-loader@7.17.3", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": - version "7.17.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.3.tgz#5c3270208e5cb039f65d7ef390af93bbc5c76408" - integrity sha512-NY/NQpuf29gjt19XExjRyTj3z44Ohc2OwQZIR/RqHYn+cbdMeXIgJqV5vbPOCN8Umjmm5yVb7kP6oKNGjyeBvw== - dependencies: - "@ardatan/sync-fetch" "0.0.1" - "@graphql-tools/delegate" "9.0.22" - "@graphql-tools/executor-graphql-ws" "0.0.7" - "@graphql-tools/executor-http" "0.1.1" - "@graphql-tools/executor-legacy-ws" "0.0.6" - "@graphql-tools/utils" "9.1.4" - "@graphql-tools/wrap" "9.3.1" +"@graphql-tools/url-loader@7.17.12", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": + version "7.17.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.12.tgz#e3d2a34fcbae6edf5bb70287af25a5133531171f" + integrity sha512-XIGWnXLoPjZzYTkeM24oUFs//buROz8Q1bgiqNf604JjmHJ6UzpOHOVskMN2lvfufqghhGDiK9v/oVhlVb8B0Q== + dependencies: + "@ardatan/sync-fetch" "^0.0.1" + "@graphql-tools/delegate" "^9.0.26" + "@graphql-tools/executor-graphql-ws" "^0.0.10" + "@graphql-tools/executor-http" "^0.1.7" + "@graphql-tools/executor-legacy-ws" "^0.0.8" + "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/wrap" "^9.3.5" "@types/ws" "^8.0.0" - "@whatwg-node/fetch" "^0.6.0" - isomorphic-ws "5.0.0" + "@whatwg-node/fetch" "^0.8.0" + isomorphic-ws "^5.0.0" tslib "^2.4.0" value-or-promise "^1.0.11" - ws "8.12.0" - -"@graphql-tools/utils@8.12.0": - version "8.12.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.12.0.tgz#243bc4f5fc2edbc9e8fd1038189e57d837cbe31f" - integrity sha512-TeO+MJWGXjUTS52qfK4R8HiPoF/R7X+qmgtOYd8DTH0l6b+5Y/tlg5aGeUJefqImRq7nvi93Ms40k/Uz4D5CWw== - dependencies: - tslib "^2.4.0" + ws "^8.12.0" -"@graphql-tools/utils@9.1.4", "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1": - version "9.1.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.1.4.tgz#2c9e0aefc9655dd73247667befe3c850ec014f3f" - integrity sha512-hgIeLt95h9nQgQuzbbdhuZmh+8WV7RZ/6GbTj6t3IU4Zd2zs9yYJ2jgW/krO587GMOY8zCwrjNOMzD40u3l7Vg== +"@graphql-tools/utils@9.2.1", "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" + integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== dependencies: + "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.9.1": @@ -1293,14 +1260,14 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/wrap@9.3.1": - version "9.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.1.tgz#d0718d87080e7a54655d629a78b08c9d39f314ea" - integrity sha512-uzY1HKc7qMErWL3ybv8bFG3hI1rTJPVYQ8WeJkCF/r/+aHEkUj0Bo2PYZrZTX1UIr3Tb4P5GyhqYBgZOXraZjw== +"@graphql-tools/wrap@^9.3.5": + version "9.3.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.5.tgz#6c48c0189fecaf5199052b98fd99d7b1c48e8b68" + integrity sha512-D3jR6/ZDWa6bw4hc1odHKLIFLxjgXlL8FSkkNlViAcRgRaqUVgFQsk+dThdWkqKP6+uij4lBG+pd/XZfrI1zeQ== dependencies: - "@graphql-tools/delegate" "9.0.22" - "@graphql-tools/schema" "9.0.13" - "@graphql-tools/utils" "9.1.4" + "@graphql-tools/delegate" "9.0.26" + "@graphql-tools/schema" "9.0.16" + "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" value-or-promise "1.0.12" @@ -1344,109 +1311,109 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.0.tgz#ed2e5bc783791c4be75d0054d1bdf66a46deb163" - integrity sha512-xpXud7e/8zo4syxQlAMDz+EQiFsf8/zXDPslBYm+UaSJ5uGTKQHhbSHfECp7Fw1trQtopjYumeved0n3waijhQ== +"@jest/console@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.2.tgz#f78374905c2454764152904a344a2d5226b0ef09" + integrity sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.4.0" - jest-util "^29.4.0" + jest-message-util "^29.4.2" + jest-util "^29.4.2" slash "^3.0.0" -"@jest/core@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.0.tgz#23cf275372c51d1191a8cd9f36f8d5a47a4e9041" - integrity sha512-E7oCMcENobBFwQXYjnN2IsuUSpRo5jSv7VYk6O9GyQ5kVAfVSS8819I4W5iCCYvqD6+1TzyzLpeEdZEik81kNw== +"@jest/core@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.2.tgz#6e999b67bdc2df9d96ba9b142465bda71ee472c2" + integrity sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ== dependencies: - "@jest/console" "^29.4.0" - "@jest/reporters" "^29.4.0" - "@jest/test-result" "^29.4.0" - "@jest/transform" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/console" "^29.4.2" + "@jest/reporters" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.4.0" - jest-config "^29.4.0" - jest-haste-map "^29.4.0" - jest-message-util "^29.4.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.4.0" - jest-resolve-dependencies "^29.4.0" - jest-runner "^29.4.0" - jest-runtime "^29.4.0" - jest-snapshot "^29.4.0" - jest-util "^29.4.0" - jest-validate "^29.4.0" - jest-watcher "^29.4.0" + jest-changed-files "^29.4.2" + jest-config "^29.4.2" + jest-haste-map "^29.4.2" + jest-message-util "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-resolve-dependencies "^29.4.2" + jest-runner "^29.4.2" + jest-runtime "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" + jest-watcher "^29.4.2" micromatch "^4.0.4" - pretty-format "^29.4.0" + pretty-format "^29.4.2" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.0.tgz#b15d7bfc873d6348dfd7323e50083d2c1e067750" - integrity sha512-ocl1VGDcZHfHnYLTqkBY7yXme1bF4x0BevJ9wb6y0sLOSyBCpp8L5fEASChB+wU53WMrIK6kBfGt+ZYoM2kcdw== +"@jest/environment@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.2.tgz#ee92c316ee2fbdf0bcd9d2db0ef42d64fea26b56" + integrity sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ== dependencies: - "@jest/fake-timers" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/fake-timers" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" - jest-mock "^29.4.0" + jest-mock "^29.4.2" -"@jest/expect-utils@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.0.tgz#97819d0da7027792888d9d2f1a41443be0baef80" - integrity sha512-w/JzTYIqjmPFIM5OOQHF9CawFx2daw1256Nzj4ZqWX96qRKbCq9WYRVqdySBKHHzuvsXLyTDIF6y61FUyrhmwg== +"@jest/expect-utils@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.2.tgz#cd0065dfdd8e8a182aa350cc121db97b5eed7b3f" + integrity sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA== dependencies: - jest-get-type "^29.2.0" + jest-get-type "^29.4.2" -"@jest/expect@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.0.tgz#3d47dbb1c650a0833b8263ca354818ca0665adbe" - integrity sha512-IiDZYQ/Oi94aBT0nKKKRvNsB5JTyHoGb+G3SiGoDxz90JfL7SLx/z5IjB0fzBRzy7aLFQOCbVJlaC2fIgU6Y9Q== +"@jest/expect@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.2.tgz#2d4a6a41b29380957c5094de19259f87f194578b" + integrity sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ== dependencies: - expect "^29.4.0" - jest-snapshot "^29.4.0" + expect "^29.4.2" + jest-snapshot "^29.4.2" -"@jest/fake-timers@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.0.tgz#9a2409cae63eb1d0122edc21783ef88c67e6face" - integrity sha512-8sitzN2QrhDwEwH3kKcMMgrv/UIkmm9AUgHixmn4L++GQ0CqVTIztm3YmaIQooLmW3O4GhizNTTCyq3iLbWcMw== +"@jest/fake-timers@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.2.tgz#af43ee1a5720b987d0348f80df98f2cb17d45cd0" + integrity sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.4.0" - jest-mock "^29.4.0" - jest-util "^29.4.0" + jest-message-util "^29.4.2" + jest-mock "^29.4.2" + jest-util "^29.4.2" -"@jest/globals@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.0.tgz#b3dd853af92bb6b6156e246475f46fb8f3831785" - integrity sha512-Q64ZRgGMVL40RcYTfD2GvyjK7vJLPSIvi8Yp3usGPNPQ3SCW+UCY9KEH6+sVtBo8LzhcjtCXuZEd7avnj/T0mQ== +"@jest/globals@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.2.tgz#73f85f5db0e17642258b25fd0b9fc89ddedb50eb" + integrity sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg== dependencies: - "@jest/environment" "^29.4.0" - "@jest/expect" "^29.4.0" - "@jest/types" "^29.4.0" - jest-mock "^29.4.0" + "@jest/environment" "^29.4.2" + "@jest/expect" "^29.4.2" + "@jest/types" "^29.4.2" + jest-mock "^29.4.2" -"@jest/reporters@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.0.tgz#ec17751cc687fb845fa1978c03fdbc69d34ac462" - integrity sha512-FjJwrD1XOQq/AXKrvnOSf0RgAs6ziUuGKx8+/R53Jscc629JIhg7/m241gf1shUm/fKKxoHd7aCexcg7kxvkWQ== +"@jest/reporters@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.2.tgz#6abfa923941daae0acc76a18830ee9e79a22042d" + integrity sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.4.0" - "@jest/test-result" "^29.4.0" - "@jest/transform" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/console" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" @@ -1459,48 +1426,48 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.4.0" - jest-util "^29.4.0" - jest-worker "^29.4.0" + jest-message-util "^29.4.2" + jest-util "^29.4.2" + jest-worker "^29.4.2" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.0.tgz#0d6ad358f295cc1deca0b643e6b4c86ebd539f17" - integrity sha512-0E01f/gOZeNTG76i5eWWSupvSHaIINrTie7vCyjiYFKgzNdyEGd12BUv4oNBFHOqlHDbtoJi3HrQ38KCC90NsQ== +"@jest/schemas@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" + integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== dependencies: "@sinclair/typebox" "^0.25.16" -"@jest/source-map@^29.2.0": - version "29.2.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" - integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== +"@jest/source-map@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.2.tgz#f9815d59e25cd3d6828e41489cd239271018d153" + integrity sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q== dependencies: "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.0.tgz#64f7bd518d6d2a6662c40569e208e030a370f351" - integrity sha512-EtRklzjpddZU/aBVxJqqejfzfOcnehmjNXufs6u6qwd05kkhXpAPhZdt8bLlQd7cA2nD+JqZQ5Dx9NX5Jh6mjA== +"@jest/test-result@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.2.tgz#34b0ba069f2e3072261e4884c8fb6bd15ed6fb8d" + integrity sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA== dependencies: - "@jest/console" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/console" "^29.4.2" + "@jest/types" "^29.4.2" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.0.tgz#8041176fccae0f7b86055950461d158833a79b76" - integrity sha512-pEwIgdfvEgF2lBOYX3DVn3SrvsAZ9FXCHw7+C6Qz87HnoDGQwbAselhWLhpgbxDjs6RC9QUJpFnrLmM5uwZV+g== +"@jest/test-sequencer@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz#8b48e5bc4af80b42edacaf2a733d4f295edf28fb" + integrity sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg== dependencies: - "@jest/test-result" "^29.4.0" + "@jest/test-result" "^29.4.2" graceful-fs "^4.2.9" - jest-haste-map "^29.4.0" + jest-haste-map "^29.4.2" slash "^3.0.0" "@jest/transform@^27.1.0": @@ -1524,26 +1491,26 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.0.tgz#73ddd9bf8599d285af09e7e3fef730c17a2fd965" - integrity sha512-hDjw3jz4GnvbyLMgcFpC9/34QcUhVIzJkBqz7o+3AhgfhGRzGuQppuLf5r/q7lDAAyJ6jzL+SFG7JGsScHOcLQ== +"@jest/transform@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.2.tgz#b24b72dbab4c8675433a80e222d6a8ef4656fb81" + integrity sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.0" - jest-regex-util "^29.2.0" - jest-util "^29.4.0" + jest-haste-map "^29.4.2" + jest-regex-util "^29.4.2" + jest-util "^29.4.2" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" - write-file-atomic "^5.0.0" + write-file-atomic "^4.0.2" "@jest/types@^27.1.0", "@jest/types@^27.5.1": version "27.5.1" @@ -1556,12 +1523,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.4.0": - version "29.4.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.0.tgz#569115f2438cacf3cff92521c7d624fbb683de3d" - integrity sha512-1S2Dt5uQp7R0bGY/L2BpuwCSji7v12kY3o8zqwlkbYBmOY956SKk+zOWqmfhHSINegiAVqOXydAYuWpzX6TYsQ== +"@jest/types@^29.4.2": + version "29.4.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" + integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== dependencies: - "@jest/schemas" "^29.4.0" + "@jest/schemas" "^29.4.2" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1616,82 +1583,82 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.5.tgz#72b24d3726a6b752be6b7b9a937f7af31b3ce410" - integrity sha512-0Ry4NhJy6qLbXhvxPRUQ1H6RzgtryGdUto7hfgAK0Iw/bScgeVjwLZdfhm2iT7qsOS32apo9cWzLCxjc6iGPsA== +"@next/env@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.6.tgz#c4925609f16142ded1a5cb833359ab17359b7a93" + integrity sha512-s+W9Fdqh5MFk6ECrbnVmmAOwxKQuhGMT7xXHrkYIBMBcTiOqNWhv5KbJIboKR5STXxNXl32hllnvKaffzFaWQg== -"@next/eslint-plugin-next@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.5.tgz#3eebea0835d2ec549a7b4b8976bf1d7796cca632" - integrity sha512-3kvLTX35bOWOCKU8KY74Ygczc55Qk/kB2TQy0tH7Rti6hzZ6Aij7WQ8zHdIVjmnlD0n/zXWXrIf5y56RKcLQkQ== +"@next/eslint-plugin-next@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.6.tgz#ad8be22dd3d8aee9a9bd9a2507e2c55a2f7ebdd9" + integrity sha512-o7cauUYsXjzSJkay8wKjpKJf2uLzlggCsGUkPu3lP09Pv97jYlekTC20KJrjQKmSv5DXV0R/uks2ZXhqjNkqAw== dependencies: glob "7.1.7" -"@next/swc-android-arm-eabi@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.5.tgz#0ea6f76754562ab8092efe7e492906790261b7cb" - integrity sha512-QAEf3YM9U0qWVQTxgF3Tsh4OeCN1i9Smsf6cVlwZsPzoLyj2nQ879joCoN+ONqDknkBgG6OG/ajefywL3jw9Cg== - -"@next/swc-android-arm64@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.5.tgz#8d4a91d77811c6c7057a41f4f32ecee1ac963981" - integrity sha512-ZmtGPTghRuT5YKL0nNcC2bBVSiG1O0is16eIZ2rWSP/hRW64ZCcAew6pxw2rihntNp22UfequjSTHd91WE/tyQ== - -"@next/swc-darwin-arm64@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.5.tgz#3f206d22201c6500acce3a9274a807fb07878565" - integrity sha512-aeFXK+M/zmG/CNdMJ0tGNs0MWcLueUe7vZ2V6fa+2yz/ZgYJLI7fEfFvVh1p1yBMzupSbZDowvMuCSFTaeg3MA== - -"@next/swc-darwin-x64@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.5.tgz#3b0775ed190a6b35cd0ba61510dcd133dcb8c4bf" - integrity sha512-6mPX0GNRg8NzjV70at8I8pD9YBnPHDpxJCoMuIqysdTjtQhd09Xk6GUhquNhp1kEJzzVk7OW5l2ch4XIJjtY3A== - -"@next/swc-freebsd-x64@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.5.tgz#a9f2b39434b52867860afbd32638f9742c45c82a" - integrity sha512-nR4a/SNblG0w8hhYRflTZjk4yD99ld18w/FCftw99ziw8sgciBlOXRICJIiRIaMRU8UH7QLSgBOQVnfNcVNKMA== - -"@next/swc-linux-arm-gnueabihf@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.5.tgz#4ae5dcdea5aa5cb6b9ce14bbf86e3afe7dc3df1a" - integrity sha512-EzkltCVKg3gUzamoeKPhGeSgXTTLAhSzc7v/+g1Y+HQa7JKMrlzdRkrJf+H4LJXcz7lnxgNKHGRyZBSXnmJKJw== - -"@next/swc-linux-arm64-gnu@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.5.tgz#92d816a430390642aa1ce1969a255b6a0b2d9833" - integrity sha512-E7HMkdoxStmTUJU4KzBUU4vZ5DHT4Gd327tC3KFZS5lda0NRerJAOCfsRg+fBj22FvCb1UWsX6XI+weL6xhyeQ== - -"@next/swc-linux-arm64-musl@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.5.tgz#b44ba6866802ee0f6d655e7184b7c2f29485219f" - integrity sha512-qlO0Fd3GQwJS6YpbF9NyL5NGHVZ43dKtZDC/jP4vdeMIYDtSu13HcY/nmA1NdW+RpMwDxSCpx4WKsCCEZGIX8Q== - -"@next/swc-linux-x64-gnu@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.5.tgz#f70afe2654d8ca6e488964ed0ebef11f38d56675" - integrity sha512-GftSBFAay2nocGl+KNqFsj6EVSvomaM/bp86hzezbKsTwQmu76PjOCVcejI1gE+4k7f5zPDgCuorF6F04BV0HQ== - -"@next/swc-linux-x64-musl@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.5.tgz#6e4aa45a6ee1cea7e6c3295dfde69250afd4c714" - integrity sha512-UD+3lxU4yuAjd+uBkCDfBpAcbGAVfEcE8mX/efIxUGIImmzN0QzgTHYEpKFnY3Lxu02dIBcwQRT3Q5mfO4obng== - -"@next/swc-win32-arm64-msvc@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.5.tgz#1ef6d453b021b26ed2d0e317c1cb658eb57d66c2" - integrity sha512-uzsvkQY+K3EbL+97IUHPWZPwjsCmCkdH/O5Cf9wCnh0k0gaj7ob1mGKqr1vNNak+9U7HloGwuHcXnZpijWSP7w== - -"@next/swc-win32-ia32-msvc@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.5.tgz#a708ed168a5d83b28519b4fcba396ab329c1a2b0" - integrity sha512-v0NaC1w8mPf620GlJaHBdEm3dm4G4AEQMasDqjzQvo0yCRrvtvzMgCIe8MocBxFHzaF6868NybMqvumxP5YxEg== - -"@next/swc-win32-x64-msvc@13.1.5": - version "13.1.5" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.5.tgz#8c7bef3dc41e624050eecb336cbd944a91fa7198" - integrity sha512-IZHwvd649ccbWyLCfu92IXEpR250NpmBkaRelPV+WVm4jrd62FKRFCNdqdCXq6TrEg9wN8cK4YG8tm44uEZqLA== +"@next/swc-android-arm-eabi@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.6.tgz#d766dfc10e27814d947b20f052067c239913dbcc" + integrity sha512-F3/6Z8LH/pGlPzR1AcjPFxx35mPqjE5xZcf+IL+KgbW9tMkp7CYi1y7qKrEWU7W4AumxX/8OINnDQWLiwLasLQ== + +"@next/swc-android-arm64@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.6.tgz#f37a98d5f18927d8c9970d750d516ac779465176" + integrity sha512-cMwQjnB8vrYkWyK/H0Rf2c2pKIH4RGjpKUDvbjVAit6SbwPDpmaijLio0LWFV3/tOnY6kvzbL62lndVA0mkYpw== + +"@next/swc-darwin-arm64@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.6.tgz#ec1b90fd9bf809d8b81004c5182e254dced4ad96" + integrity sha512-KKRQH4DDE4kONXCvFMNBZGDb499Hs+xcFAwvj+rfSUssIDrZOlyfJNy55rH5t2Qxed1e4K80KEJgsxKQN1/fyw== + +"@next/swc-darwin-x64@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.6.tgz#e869ac75d16995eee733a7d1550322d9051c1eb4" + integrity sha512-/uOky5PaZDoaU99ohjtNcDTJ6ks/gZ5ykTQDvNZDjIoCxFe3+t06bxsTPY6tAO6uEAw5f6vVFX5H5KLwhrkZCA== + +"@next/swc-freebsd-x64@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.6.tgz#84a7b2e423a2904afc2edca21c2f1ba6b53fa4c1" + integrity sha512-qaEALZeV7to6weSXk3Br80wtFQ7cFTpos/q+m9XVRFggu+8Ib895XhMWdJBzew6aaOcMvYR6KQ6JmHA2/eMzWw== + +"@next/swc-linux-arm-gnueabihf@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.6.tgz#980eed1f655ff8a72187d8a6ef9e73ac39d20d23" + integrity sha512-OybkbC58A1wJ+JrJSOjGDvZzrVEQA4sprJejGqMwiZyLqhr9Eo8FXF0y6HL+m1CPCpPhXEHz/2xKoYsl16kNqw== + +"@next/swc-linux-arm64-gnu@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.6.tgz#87a71db21cded3f7c63d1d19079845c59813c53d" + integrity sha512-yCH+yDr7/4FDuWv6+GiYrPI9kcTAO3y48UmaIbrKy8ZJpi7RehJe3vIBRUmLrLaNDH3rY1rwoHi471NvR5J5NQ== + +"@next/swc-linux-arm64-musl@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.6.tgz#c5aac8619331b9fd030603bbe2b36052011e11de" + integrity sha512-ECagB8LGX25P9Mrmlc7Q/TQBb9rGScxHbv/kLqqIWs2fIXy6Y/EiBBiM72NTwuXUFCNrWR4sjUPSooVBJJ3ESQ== + +"@next/swc-linux-x64-gnu@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.6.tgz#9513d36d540bbfea575576746736054c31aacdea" + integrity sha512-GT5w2mruk90V/I5g6ScuueE7fqj/d8Bui2qxdw6lFxmuTgMeol5rnzAv4uAoVQgClOUO/MULilzlODg9Ib3Y4Q== + +"@next/swc-linux-x64-musl@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.6.tgz#d61fc6884899f5957251f4ce3f522e34a2c479b7" + integrity sha512-keFD6KvwOPzmat4TCnlnuxJCQepPN+8j3Nw876FtULxo8005Y9Ghcl7ACcR8GoiKoddAq8gxNBrpjoxjQRHeAQ== + +"@next/swc-win32-arm64-msvc@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.6.tgz#fac2077a8ae9768e31444c9ae90807e64117cda7" + integrity sha512-OwertslIiGQluFvHyRDzBCIB07qJjqabAmINlXUYt7/sY7Q7QPE8xVi5beBxX/rxTGPIbtyIe3faBE6Z2KywhQ== + +"@next/swc-win32-ia32-msvc@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.6.tgz#498bc11c91b4c482a625bf4b978f98ae91111e46" + integrity sha512-g8zowiuP8FxUR9zslPmlju7qYbs2XBtTLVSxVikPtUDQedhcls39uKYLvOOd1JZg0ehyhopobRoH1q+MHlIN/w== + +"@next/swc-win32-x64-msvc@13.1.6": + version "13.1.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.6.tgz#17ed919c723426b7d0ce1cd73d40ce3dcd342089" + integrity sha512-Ls2OL9hi3YlJKGNdKv8k3X/lLgc3VmLG3a/DeTkAd+lAituJp8ZHmRmm9f9SL84fT3CotlzcgbdaCDfFwFA6bA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1791,10 +1758,10 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/tour@~1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.1.0.tgz#17b43b5c0bdfb6a8ab3027b1977d960952616d9e" - integrity sha512-Cy45VnNEDq6DLF5eKonIflObDfofbPq7AJpSf18qLN+j9+wW+sNlRv3JnCMDUsCdhSlnM4+yJ1RMokKp9GCpOQ== +"@rc-component/tour@~1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.6.0.tgz#ef3888c8ccc5d1a2e465d7d2615abd1d2dd51e27" + integrity sha512-b/s7LCb7bW4wxpWfZyNpl7khHUzSyObSlsLaIScRGd+W/v1wFVk8F7gRytl/z8ik9ZSXbLWx9EvexIuHoO/RcQ== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/portal" "^1.0.0-9" @@ -1802,7 +1769,7 @@ rc-trigger "^5.3.4" rc-util "^5.24.4" -"@repeaterjs/repeater@3.0.4": +"@repeaterjs/repeater@3.0.4", "@repeaterjs/repeater@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== @@ -1963,9 +1930,9 @@ "@types/node" "*" "@types/node@*", "@types/node@^18.11.18": - version "18.11.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f" - integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA== + version "18.13.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" + integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2011,9 +1978,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^18.0": - version "18.0.27" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.27.tgz#d9425abe187a00f8a5ec182b010d4fd9da703b71" - integrity sha512-3vtRKHgVxu3Jp9t718R9BuzoD4NcQ8YJ5XRzsSKxNDiDonD2MXIT1TmSkenxuCycZJoQT5d2vE8LwWJxBC1gmA== + version "18.0.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" + integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2049,9 +2016,9 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.20" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.20.tgz#107f0fcc13bd4a524e352b41c49fe88aab5c54d5" - integrity sha512-eknWrTHofQuPk2iuqDm1waA7V6xPlbgBoaaXEgYkClhLOnB0TtbW+srJaOToAgawPxPlHQzwypFA2bhZaUGP5A== + version "17.0.22" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" + integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== dependencies: "@types/yargs-parser" "*" @@ -2061,63 +2028,95 @@ integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== "@typescript-eslint/parser@^5.42.0": - version "5.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.49.0.tgz#d699734b2f20e16351e117417d34a2bc9d7c4b90" - integrity sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg== + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.51.0.tgz#2d74626652096d966ef107f44b9479f02f51f271" + integrity sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA== dependencies: - "@typescript-eslint/scope-manager" "5.49.0" - "@typescript-eslint/types" "5.49.0" - "@typescript-eslint/typescript-estree" "5.49.0" + "@typescript-eslint/scope-manager" "5.51.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/typescript-estree" "5.51.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.49.0": - version "5.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz#81b5d899cdae446c26ddf18bd47a2f5484a8af3e" - integrity sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ== +"@typescript-eslint/scope-manager@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz#ad3e3c2ecf762d9a4196c0fbfe19b142ac498990" + integrity sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ== dependencies: - "@typescript-eslint/types" "5.49.0" - "@typescript-eslint/visitor-keys" "5.49.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" -"@typescript-eslint/types@5.49.0": - version "5.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.49.0.tgz#ad66766cb36ca1c89fcb6ac8b87ec2e6dac435c3" - integrity sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg== +"@typescript-eslint/types@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.51.0.tgz#e7c1622f46c7eea7e12bbf1edfb496d4dec37c90" + integrity sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw== -"@typescript-eslint/typescript-estree@5.49.0": - version "5.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz#ebd6294c0ea97891fce6af536048181e23d729c8" - integrity sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA== +"@typescript-eslint/typescript-estree@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz#0ec8170d7247a892c2b21845b06c11eb0718f8de" + integrity sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA== dependencies: - "@typescript-eslint/types" "5.49.0" - "@typescript-eslint/visitor-keys" "5.49.0" + "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/visitor-keys" "5.51.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.49.0": - version "5.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz#2561c4da3f235f5c852759bf6c5faec7524f90fe" - integrity sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg== +"@typescript-eslint/visitor-keys@5.51.0": + version "5.51.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz#c0147dd9a36c0de758aaebd5b48cae1ec59eba87" + integrity sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ== dependencies: - "@typescript-eslint/types" "5.49.0" + "@typescript-eslint/types" "5.51.0" eslint-visitor-keys "^3.3.0" -"@whatwg-node/fetch@0.6.2", "@whatwg-node/fetch@^0.6.0": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.6.2.tgz#fe4837505f6fc91bcfd6e12cdcec66f4aecfeecc" - integrity sha512-fCUycF1W+bI6XzwJFnbdDuxIldfKM3w8+AzVCLGlucm0D+AQ8ZMm2j84hdcIhfV6ZdE4Y1HFVrHosAxdDZ+nPw== +"@whatwg-node/events@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.2.tgz#7b7107268d2982fc7b7aff5ee6803c64018f84dd" + integrity sha512-WKj/lI4QjnLuPrim0cfO7i+HsDSXHxNv1y0CrJhdntuO3hxWZmnXCwNDnwOvry11OjRin6cgWNF+j/9Pn8TN4w== + +"@whatwg-node/fetch@^0.6.0": + version "0.6.9" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.6.9.tgz#6cc694cc0378e27b8dfed427c5bf633eda6972b9" + integrity sha512-JfrBCJdMu9n9OARc0e/hPHcD98/8Nz1CKSdGYDg6VbObDkV/Ys30xe5i/wPOatYbxuvatj1kfWeHf7iNX3i17w== + dependencies: + "@peculiar/webcrypto" "^1.4.0" + "@whatwg-node/node-fetch" "^0.0.5" + busboy "^1.6.0" + urlpattern-polyfill "^6.0.2" + web-streams-polyfill "^3.2.1" + +"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.1.tgz#ee3c94746132f217e17f78f9e073bb342043d630" + integrity sha512-Fkd1qQHK2tAWxKlC85h9L86Lgbq3BzxMnHSnTsnzNZMMzn6Xi+HlN8/LJ90LxorhSqD54td+Q864LgwUaYDj1Q== dependencies: "@peculiar/webcrypto" "^1.4.0" - abort-controller "^3.0.0" + "@whatwg-node/node-fetch" "^0.3.0" busboy "^1.6.0" - form-data-encoder "^1.7.1" - formdata-node "^4.3.1" - node-fetch "^2.6.7" - undici "^5.12.0" urlpattern-polyfill "^6.0.2" - web-streams-polyfill "^3.2.0" + web-streams-polyfill "^3.2.1" + +"@whatwg-node/node-fetch@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.0.5.tgz#bebf18891088e5e2fc449dea8d1bc94af5ec38df" + integrity sha512-hbccmaSZaItdsRuBKBEEhLoO+5oXJPxiyd0kG2xXd0Dh3Rt+vZn4pADHxuSiSHLd9CM+S2z4+IxlEGbWUgiz9g== + dependencies: + "@whatwg-node/events" "^0.0.2" + busboy "^1.6.0" + tslib "^2.3.1" + +"@whatwg-node/node-fetch@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.0.tgz#7c7e90d03fa09d0ddebff29add6f16d923327d58" + integrity sha512-mPM8WnuHiI/3kFxDeE0SQQXAElbz4onqmm64fEGCwYEcBes2UsvIDI8HwQIqaXCH42A9ajJUPv4WsYoN/9oG6w== + dependencies: + "@whatwg-node/events" "^0.0.2" + busboy "^1.6.0" + fast-querystring "^1.1.1" + fast-url-parser "^1.1.3" + tslib "^2.3.1" "@wry/context@^0.6.0": version "0.6.1" @@ -2152,13 +2151,6 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -2236,9 +2228,9 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== antd@^5.0: - version "5.1.6" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.1.6.tgz#9ac912279f9f8e571674b3220b668897b4ffb0b3" - integrity sha512-9bn2B4rZ1c7IXtn5U95aNGJXmLOZyL1V5TiGq3pk3S3bGD13BA2eynSI//e3c2FAslZlqQlQEGV+NHms9ygAVA== + version "5.2.1" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.2.1.tgz#96ee27776bf920fc89a508d3db4da6554b396973" + integrity sha512-yuTeYEhfvsEjHdPwhb47GXBk9EHZ/pHDaUgJMXHhOUbaWPWnTliHtQwgHYMJT9rEZobi3O0YRP2wnYWVtbDGsw== dependencies: "@ant-design/colors" "^7.0.0" "@ant-design/cssinjs" "^1.5.6" @@ -2247,22 +2239,22 @@ antd@^5.0: "@babel/runtime" "^7.18.3" "@ctrl/tinycolor" "^3.4.0" "@rc-component/mutate-observer" "^1.0.0" - "@rc-component/tour" "~1.1.0" + "@rc-component/tour" "~1.6.0" classnames "^2.2.6" copy-to-clipboard "^3.2.0" dayjs "^1.11.1" qrcode.react "^3.1.0" rc-cascader "~3.8.0" rc-checkbox "~2.3.0" - rc-collapse "~3.4.2" + rc-collapse "~3.5.2" rc-dialog "~9.0.2" rc-drawer "~6.1.1" rc-dropdown "~4.0.0" rc-field-form "~1.27.0" rc-image "~5.13.0" - rc-input "~0.1.4" + rc-input "~0.2.1" rc-input-number "~7.4.0" - rc-mentions "~1.13.1" + rc-mentions "~2.0.0" rc-menu "~9.8.2" rc-motion "^2.6.1" rc-notification "~5.0.0" @@ -2271,15 +2263,15 @@ antd@^5.0: rc-progress "~3.4.1" rc-rate "~2.9.0" rc-resize-observer "^1.2.0" - rc-segmented "~2.1.0" + rc-segmented "~2.1.2" rc-select "~14.2.0" - rc-slider "~10.0.0" + rc-slider "~10.1.0" rc-steps "~6.0.0" rc-switch "~4.0.0" rc-table "~7.30.2" - rc-tabs "~12.5.1" - rc-textarea "~0.4.5" - rc-tooltip "~5.2.0" + rc-tabs "~12.5.6" + rc-textarea "~1.0.0" + rc-tooltip "~5.3.1" rc-tree "~5.7.0" rc-tree-select "~5.6.0" rc-trigger "^5.3.4" @@ -2447,15 +2439,15 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^29.0, babel-jest@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.0.tgz#20b18b69a321125429ff67a383b94c6bf552d1ec" - integrity sha512-M61cGPg4JBashDvIzKoIV/y95mSF6x3ome7CMEaszUTHD4uo6dtC6Nln+fvRTspYNtwy8lDHl5lmoTBSNY/a+g== +babel-jest@^29.0, babel-jest@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.2.tgz#b17b9f64be288040877cbe2649f91ac3b63b2ba6" + integrity sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ== dependencies: - "@jest/transform" "^29.4.0" + "@jest/transform" "^29.4.2" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.4.0" + babel-preset-jest "^29.4.2" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2481,10 +2473,10 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.0.tgz#3fd3dfcedf645932df6d0c9fc3d9a704dd860248" - integrity sha512-a/sZRLQJEmsmejQ2rPEUe35nO1+C9dc9O1gplH1SXmJxveQSRUYdBk8yGZG/VOUuZs1u2aHZJusEGoRMbhhwCg== +babel-plugin-jest-hoist@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz#22aa43e255230f02371ffef1cac7eedef58f60bc" + integrity sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2555,12 +2547,12 @@ babel-preset-jest@^27.0.6: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.0.tgz#c2b03c548b02dea0a18ae21d5759c136f9251ee4" - integrity sha512-fUB9vZflUSM3dO/6M2TCAepTzvA4VkOvl67PjErcrQMGt9Eve7uazaeyCZ2th3UtI7ljpiBJES0F7A1vBRsLZA== +babel-preset-jest@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz#f0b20c6a79a9f155515e72a2d4f537fe002a4e38" + integrity sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ== dependencies: - babel-plugin-jest-hoist "^29.4.0" + babel-plugin-jest-hoist "^29.4.2" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2608,14 +2600,14 @@ braces@^3.0.2, braces@~3.0.2: fill-range "^7.0.1" browserslist@^4.21.3: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" bser@2.1.1: version "2.1.1" @@ -2685,10 +2677,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406: - version "1.0.30001448" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001448.tgz#ca7550b1587c92a392a2b377cd9c508b3b4395bf" - integrity sha512-tq2YI+MJnooG96XpbTRYkBxLxklZPOdLmNIOdIhvf7SNJan6u5vCKum8iT7ZfCt70m1GPkuC7P3TtX6UuhupuA== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449: + version "1.0.30001451" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" + integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== capital-case@^1.0.4: version "1.0.4" @@ -2792,9 +2784,9 @@ chokidar@^3.5.2: fsevents "~2.3.2" ci-info@^3.2.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" - integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -2966,7 +2958,7 @@ copy-to-clipboard@^3.2.0: dependencies: toggle-selection "^1.0.6" -cosmiconfig-typescript-loader@4.3.0: +cosmiconfig-typescript-loader@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== @@ -3023,10 +3015,10 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -dataloader@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.1.0.tgz#c69c538235e85e7ac6c6c444bae8ecabf5de9df7" - integrity sha512-qTcEYLen3r7ojZNgVUaRggOI+KM7jrKxXeSHhogh/TWxYMeONEMqY+hmkobiYQozsGIyg9OYVzO4ZIfoB4I0pQ== +dataloader@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.1.tgz#f07ab712514313a34b1507a308dbb7dc14bac715" + integrity sha512-Zn+tVZo1RKu120rgoe0JsRk56UiKdefPSH47QROJsMHrX8/S9UJvi5A/A6+Sbuk6rE88z5JoM/wIJ09Z7BTfYA== dayjs@^1.0, dayjs@^1.11.1: version "1.11.7" @@ -3096,9 +3088,9 @@ deep-is@^0.1.3: integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.0, deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + version "4.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== defaults@^1.0.3: version "1.0.4" @@ -3113,9 +3105,9 @@ define-lazy-prop@^2.0.0: integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== dependencies: has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -3148,10 +3140,10 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" - integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== +diff-sequences@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda" + integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw== diff@^4.0.1: version "4.0.2" @@ -3197,7 +3189,7 @@ dotenv@^16.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== -dset@3.1.2: +dset@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== @@ -3209,10 +3201,10 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -electron-to-chromium@^1.4.251: - version "1.4.284" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" - integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== +electron-to-chromium@^1.4.284: + version "1.4.295" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz#911d5df67542bf7554336142eb302c5ec90bba66" + integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== emittery@^0.13.1: version "0.13.1" @@ -3349,11 +3341,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.1.4: - version "13.1.5" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.5.tgz#38daeaa1463197ef5b6cec9136ce6838ea776cf3" - integrity sha512-7FqkjkvGCQfvYUiPTFRiRYPR1uI6Ew+4f4mVp16lLSWcaChtWoZxQCZHM5n0yxzKKVmuEg1aM4uvDQfSXSjTww== + version "13.1.6" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.6.tgz#ab6894fe5b80080f1e9b9306d1c4b0003230620e" + integrity sha512-0cg7h5wztg/SoLAlxljZ0ZPUQ7i6QKqRiP4M2+MgTZtxWwNKb2JSwNc18nJ6/kXBI6xYvPraTbQSIhAuVw6czw== dependencies: - "@next/eslint-plugin-next" "13.1.5" + "@next/eslint-plugin-next" "13.1.6" "@rushstack/eslint-patch" "^1.1.3" "@typescript-eslint/parser" "^5.42.0" eslint-import-resolver-node "^0.3.6" @@ -3441,9 +3433,9 @@ eslint-plugin-react-hooks@^4.5.0: integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== eslint-plugin-react@^7.31.7: - version "7.32.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.1.tgz#88cdeb4065da8ca0b64e1274404f53a0f9890200" - integrity sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www== + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== dependencies: array-includes "^3.1.6" array.prototype.flatmap "^1.3.1" @@ -3487,9 +3479,9 @@ eslint-visitor-keys@^3.3.0: integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== eslint@^8.0.0: - version "8.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.32.0.tgz#d9690056bb6f1a302bd991e7090f5b68fbaea861" - integrity sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ== + version "8.34.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.34.0.tgz#fe0ab0ef478104c1f9ebc5537e303d25a8fb22d6" + integrity sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg== dependencies: "@eslint/eslintrc" "^1.4.1" "@humanwhocodes/config-array" "^0.11.8" @@ -3569,11 +3561,6 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -3594,16 +3581,16 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.0.tgz#e2d58a73bf46399deac7db6ec16842827525ce35" - integrity sha512-pzaAwjBgLEVxBh6ZHiqb9Wv3JYuv6m8ntgtY7a48nS+2KbX0EJkPS3FQlKiTZNcqzqJHNyQsfjqN60w1hPUBfQ== +expect@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492" + integrity sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ== dependencies: - "@jest/expect-utils" "^29.4.0" - jest-get-type "^29.2.0" - jest-matcher-utils "^29.4.0" - jest-message-util "^29.4.0" - jest-util "^29.4.0" + "@jest/expect-utils" "^29.4.2" + jest-get-type "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-util "^29.4.2" external-editor@^3.0.3: version "3.1.0" @@ -3624,6 +3611,11 @@ extract-files@^9.0.0: resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== +fast-decode-uri-component@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -3650,6 +3642,20 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-querystring@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.1.tgz#f4c56ef56b1a954880cfd8c01b83f9e1a3d3fda2" + integrity sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q== + dependencies: + fast-decode-uri-component "^1.0.1" + +fast-url-parser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + fastq@^1.6.0: version "1.15.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" @@ -3739,11 +3745,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -form-data-encoder@^1.7.1: - version "1.7.2" - resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" - integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== - form-data@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" @@ -3753,14 +3754,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -formdata-node@^4.3.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" - integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== - dependencies: - node-domexception "1.0.0" - web-streams-polyfill "4.0.0-beta.3" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3801,7 +3794,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== @@ -3829,9 +3822,9 @@ get-symbol-description@^1.0.0: get-intrinsic "^1.1.1" get-tsconfig@^4.2.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.3.0.tgz#4c26fae115d1050e836aea65d6fe56b507ee249b" - integrity sha512-YCcF28IqSay3fqpIu5y3Krg/utCBHBeoflkZyHj/QcqI2nrLPC3ZegS9CmIo+hJb8K7aiGsuUl7PwWVjNG2HQQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" + integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" @@ -3877,9 +3870,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: - version "13.19.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" - integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== dependencies: type-fest "^0.20.2" @@ -3952,10 +3945,10 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -graphql-config@4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.4.0.tgz#4b2d34d846bd4b9a40afbadfc5a4426668963c43" - integrity sha512-QUrX7R4htnTBTi83a0IlIilWVfiLEG8ANFlHRcxoZiTvOXTbgan67SUdGe1OlopbDuyNgtcy4ladl3Gvk4C36A== +graphql-config@^4.4.0: + version "4.4.1" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.4.1.tgz#2b1b5215b38911c0b15ff9b2e878101c984802d6" + integrity sha512-B8wlvfBHZ5WnI4IiuQZRqql6s+CKz7S+xpUeTb28Z8nRBi8tH9ChEBgT5FnTyE05PUhHlrS2jK9ICJ4YBl9OtQ== dependencies: "@graphql-tools/graphql-file-loader" "^7.3.7" "@graphql-tools/json-file-loader" "^7.3.7" @@ -4010,10 +4003,10 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.3: dependencies: tslib "^2.1.0" -graphql-ws@5.11.2: - version "5.11.2" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.11.2.tgz#d5e0acae8b4d4a4cf7be410a24135cfcefd7ddc0" - integrity sha512-4EiZ3/UXYcjm+xFGP544/yW1+DVI8ZpKASFbzrV5EDTFWJp0ZvLl4Dy2fSZAzz9imKp5pZMIcjB0x/H69Pv/6w== +graphql-ws@5.11.3: + version "5.11.3" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.11.3.tgz#eaf8e6baf669d167975cff13ad86abca4ecfe82f" + integrity sha512-fU8zwSgAX2noXAsuFiCZ8BtXeXZOzXyK5u1LloCdacsVth4skdBMPO74EG51lBoWSIZ8beUocdpV8+cQHBODnQ== graphql@^16.0.1: version "16.6.0" @@ -4206,11 +4199,11 @@ inquirer@^8.0.0: wrap-ansi "^7.0.0" internal-slot@^1.0.3, internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== dependencies: - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" has "^1.0.3" side-channel "^1.0.4" @@ -4489,7 +4482,7 @@ isomorphic-fetch@^3.0.0: node-fetch "^2.6.1" whatwg-fetch "^3.4.1" -isomorphic-ws@5.0.0: +isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== @@ -4541,129 +4534,129 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.0.tgz#ac2498bcd394228f7eddcadcf928b3583bf2779d" - integrity sha512-rnI1oPxgFghoz32Y8eZsGJMjW54UlqT17ycQeCEktcxxwqqKdlj9afl8LNeO0Pbu+h2JQHThQP0BzS67eTRx4w== +jest-changed-files@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.2.tgz#bee1fafc8b620d6251423d1978a0080546bc4376" + integrity sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.0.tgz#08fec87eb15632fba74e93cf069ef85559acd813" - integrity sha512-/pFBaCeLzCavRWyz14JwFgpZgPpEZdS6nPnREhczbHl2wy2UezvYcVp5akVFfUmBaA4ThAUp0I8cpgkbuNOm3g== +jest-circus@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.2.tgz#2d00c04baefd0ee2a277014cd494d4b5970663ed" + integrity sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg== dependencies: - "@jest/environment" "^29.4.0" - "@jest/expect" "^29.4.0" - "@jest/test-result" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/environment" "^29.4.2" + "@jest/expect" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.4.0" - jest-matcher-utils "^29.4.0" - jest-message-util "^29.4.0" - jest-runtime "^29.4.0" - jest-snapshot "^29.4.0" - jest-util "^29.4.0" + jest-each "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-runtime "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" p-limit "^3.1.0" - pretty-format "^29.4.0" + pretty-format "^29.4.2" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.0.tgz#63f34fc3b6f499a69337b2e5964661d22cead9e0" - integrity sha512-YUkICcxjUd864VOzbfQEi2qd2hIIOd9bRF7LJUNyhWb3Khh3YKrbY0LWwoZZ4WkvukiNdvQu0Z4s6zLsY4hYfg== +jest-cli@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.2.tgz#94a2f913a0a7a49d11bee98ad88bf48baae941f4" + integrity sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q== dependencies: - "@jest/core" "^29.4.0" - "@jest/test-result" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/core" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.4.0" - jest-util "^29.4.0" - jest-validate "^29.4.0" + jest-config "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.0.tgz#08065bcfc7c2ccfd3ca9f8bb412eb366d73fd5a2" - integrity sha512-jtgd72nN4Mob4Oego3N/pLRVfR2ui1hv+yO6xR/SUi5G7NtZ/grr95BJ1qRSDYZshuA0Jw57fnttZHZKb04+CA== +jest-config@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.2.tgz#15386dd9ed2f7059516915515f786b8836a98f07" + integrity sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.4.0" - "@jest/types" "^29.4.0" - babel-jest "^29.4.0" + "@jest/test-sequencer" "^29.4.2" + "@jest/types" "^29.4.2" + babel-jest "^29.4.2" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.4.0" - jest-environment-node "^29.4.0" - jest-get-type "^29.2.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.4.0" - jest-runner "^29.4.0" - jest-util "^29.4.0" - jest-validate "^29.4.0" + jest-circus "^29.4.2" + jest-environment-node "^29.4.2" + jest-get-type "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-runner "^29.4.2" + jest-util "^29.4.2" + jest-validate "^29.4.2" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.4.0" + pretty-format "^29.4.2" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.0.tgz#9c75dcef5872c8889bfcb78bc9571a0e4e9bd8f6" - integrity sha512-s8KNvFx8YgdQ4fn2YLDQ7N6kmVOP68dUDVJrCHNsTc3UM5jcmyyFeYKL8EPWBQbJ0o0VvDGbWp8oYQ1nsnqnWw== +jest-diff@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.2.tgz#b88502d5dc02d97f6512d73c37da8b36f49b4871" + integrity sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g== dependencies: chalk "^4.0.0" - diff-sequences "^29.3.1" - jest-get-type "^29.2.0" - pretty-format "^29.4.0" + diff-sequences "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" -jest-docblock@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82" - integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== +jest-docblock@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.2.tgz#c78a95eedf9a24c0a6cc16cf2abdc4b8b0f2531b" + integrity sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag== dependencies: detect-newline "^3.0.0" -jest-each@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.0.tgz#1b6f018529b26f52851123075df4fa297b5b859b" - integrity sha512-LTOvB8JDVFjrwXItyQiyLuDYy5PMApGLLzbfIYR79QLpeohS0bcS6j2HjlWuRGSM8QQQyp+ico59Blv+Jx3fMw== +jest-each@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.2.tgz#e1347aff1303f4c35470827a62c029d389c5d44a" + integrity sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" chalk "^4.0.0" - jest-get-type "^29.2.0" - jest-util "^29.4.0" - pretty-format "^29.4.0" - -jest-environment-node@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.0.tgz#b19ae4cbf71199ed077a399de1e2cb0e52dbd936" - integrity sha512-WVveE3fYSH6FhDtZdvXhFKeLsDRItlQgnij+HQv6ZKxTdT1DB5O0sHXKCEC3K5mHraMs1Kzn4ch9jXC7H4L4wA== - dependencies: - "@jest/environment" "^29.4.0" - "@jest/fake-timers" "^29.4.0" - "@jest/types" "^29.4.0" + jest-get-type "^29.4.2" + jest-util "^29.4.2" + pretty-format "^29.4.2" + +jest-environment-node@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.2.tgz#0eab835b41e25fd0c1a72f62665fc8db08762ad2" + integrity sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/fake-timers" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" - jest-mock "^29.4.0" - jest-util "^29.4.0" + jest-mock "^29.4.2" + jest-util "^29.4.2" -jest-get-type@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" - integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== +jest-get-type@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe" + integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg== jest-haste-map@^27.5.1: version "27.5.1" @@ -4685,66 +4678,66 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.0.tgz#139d6bd262f3f74894f23933ee4046af7e770ab0" - integrity sha512-m/pIEfoK0HoJz4c9bkgS5F9CXN2AM22eaSmUcmqTpadRlNVBOJE2CwkgaUzbrNn5MuAqTV1IPVYwWwjHNnk8eA== +jest-haste-map@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.2.tgz#9112df3f5121e643f1b2dcbaa86ab11b0b90b49a" + integrity sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.2.0" - jest-util "^29.4.0" - jest-worker "^29.4.0" + jest-regex-util "^29.4.2" + jest-util "^29.4.2" + jest-worker "^29.4.2" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.0.tgz#5f1079556c244cf6f5f281f5c5134ea2a07ad28e" - integrity sha512-fEGHS6ijzgSv5exABkCecMHNmyHcV52+l39ZsxuwfxmQMp43KBWJn2/Fwg8/l4jTI9uOY9jv8z1dXGgL0PHFjA== +jest-leak-detector@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz#8f05c6680e0cb46a1d577c0d3da9793bed3ea97b" + integrity sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA== dependencies: - jest-get-type "^29.2.0" - pretty-format "^29.4.0" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" -jest-matcher-utils@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.0.tgz#c2f804f95152216c8b80afbe73d82ae0ba89f652" - integrity sha512-pU4OjBn96rDdRIaPUImbPiO2ETyRVzkA1EZVu9AxBDv/XPDJ7JWfkb6IiDT5jwgicaPHMrB/fhVa6qjG6potfA== +jest-matcher-utils@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz#08d0bf5abf242e3834bec92c7ef5071732839e85" + integrity sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg== dependencies: chalk "^4.0.0" - jest-diff "^29.4.0" - jest-get-type "^29.2.0" - pretty-format "^29.4.0" + jest-diff "^29.4.2" + jest-get-type "^29.4.2" + pretty-format "^29.4.2" -jest-message-util@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.0.tgz#60d3f3dd6b5ef08ec9b698f434fbbafdb0af761d" - integrity sha512-0FvobqymmhE9pDEifvIcni9GeoKLol8eZspzH5u41g1wxYtLS60a9joT95dzzoCgrKRidNz64eaAXyzaULV8og== +jest-message-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" + integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.4.0" + pretty-format "^29.4.2" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.0.tgz#efb5d99da5e4548ea02d505c89aafdd06d899cb8" - integrity sha512-+ShT5i+hcu/OFQRV0f/V/YtwpdFcHg64JZ9A8b40JueP+X9HNrZAYGdkupGIzsUK8AucecxCt4wKauMchxubLQ== +jest-mock@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8" + integrity sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@types/node" "*" - jest-util "^29.4.0" + jest-util "^29.4.2" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -4756,86 +4749,86 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" - integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== +jest-regex-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.2.tgz#19187cca35d301f8126cf7a021dd4dcb7b58a1ca" + integrity sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig== -jest-resolve-dependencies@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.0.tgz#d8c9dce9ad8b193375b4993c4a659adf2d379b84" - integrity sha512-hxfC84trREyULSj1Cm+fMjnudrrI2dVQ04COjZRcjCZ97boJlPtfJ+qrl/pN7YXS2fnu3wTHEc3LO094pngL6A== +jest-resolve-dependencies@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz#6359db606f5967b68ca8bbe9dbc07a4306c12bf7" + integrity sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg== dependencies: - jest-regex-util "^29.2.0" - jest-snapshot "^29.4.0" + jest-regex-util "^29.4.2" + jest-snapshot "^29.4.2" -jest-resolve@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.0.tgz#dbf00b2fb992d37e4954feb7ee6b5391ca8fc6a9" - integrity sha512-g7k7l53T+uC9Dp1mbHyDNkcCt0PMku6Wcfpr1kcMLwOHmM3vucKjSM5+DSa1r4vlDZojh8XH039J3z4FKmtTSw== +jest-resolve@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.2.tgz#8831f449671d08d161fe493003f61dc9b55b808e" + integrity sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.0" + jest-haste-map "^29.4.2" jest-pnp-resolver "^1.2.2" - jest-util "^29.4.0" - jest-validate "^29.4.0" + jest-util "^29.4.2" + jest-validate "^29.4.2" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.0.tgz#9f83f20e9e88d2c5e9b7db1526cef901c1c0fe98" - integrity sha512-4zpcv0NOiJleqT0NAs8YcVbK8MhVRc58CBBn9b0Exc8VPU9GKI+DbzDUZqJYdkJhJSZFy2862l/F6hAqIow1hg== +jest-runner@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.2.tgz#2bcecf72303369df4ef1e6e983c22a89870d5125" + integrity sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg== dependencies: - "@jest/console" "^29.4.0" - "@jest/environment" "^29.4.0" - "@jest/test-result" "^29.4.0" - "@jest/transform" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/console" "^29.4.2" + "@jest/environment" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^29.2.0" - jest-environment-node "^29.4.0" - jest-haste-map "^29.4.0" - jest-leak-detector "^29.4.0" - jest-message-util "^29.4.0" - jest-resolve "^29.4.0" - jest-runtime "^29.4.0" - jest-util "^29.4.0" - jest-watcher "^29.4.0" - jest-worker "^29.4.0" + jest-docblock "^29.4.2" + jest-environment-node "^29.4.2" + jest-haste-map "^29.4.2" + jest-leak-detector "^29.4.2" + jest-message-util "^29.4.2" + jest-resolve "^29.4.2" + jest-runtime "^29.4.2" + jest-util "^29.4.2" + jest-watcher "^29.4.2" + jest-worker "^29.4.2" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.0.tgz#d4f3300e65888ea8e045bc13c5a96edd01cf9f49" - integrity sha512-2zumwaGXsIuSF92Ui5Pn5hZV9r7AHMclfBLikrXSq87/lHea9anQ+mC+Cjz/DYTbf/JMjlK1sjZRh8K3yYNvWg== - dependencies: - "@jest/environment" "^29.4.0" - "@jest/fake-timers" "^29.4.0" - "@jest/globals" "^29.4.0" - "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.4.0" - "@jest/transform" "^29.4.0" - "@jest/types" "^29.4.0" +jest-runtime@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.2.tgz#d86b764c5b95d76cb26ed1f32644e99de5d5c134" + integrity sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw== + dependencies: + "@jest/environment" "^29.4.2" + "@jest/fake-timers" "^29.4.2" + "@jest/globals" "^29.4.2" + "@jest/source-map" "^29.4.2" + "@jest/test-result" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.4.0" - jest-message-util "^29.4.0" - jest-mock "^29.4.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.4.0" - jest-snapshot "^29.4.0" - jest-util "^29.4.0" + jest-haste-map "^29.4.2" + jest-message-util "^29.4.2" + jest-mock "^29.4.2" + jest-regex-util "^29.4.2" + jest-resolve "^29.4.2" + jest-snapshot "^29.4.2" + jest-util "^29.4.2" semver "^7.3.5" slash "^3.0.0" strip-bom "^4.0.0" @@ -4848,10 +4841,10 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.0.tgz#c839f6b84d50b917691ddf6a3cef9f0852b3d0e7" - integrity sha512-UnK3MhdEWrQ2J6MnlKe51tvN5FjRUBQnO4m1LPlDx61or3w9+cP/U0x9eicutgunu/QzE4WC82jj6CiGIAFYzw== +jest-snapshot@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.2.tgz#ba1fb9abb279fd2c85109ff1757bc56b503bbb3a" + integrity sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" @@ -4859,23 +4852,23 @@ jest-snapshot@^29.4.0: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.4.0" - "@jest/transform" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/expect-utils" "^29.4.2" + "@jest/transform" "^29.4.2" + "@jest/types" "^29.4.2" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.4.0" + expect "^29.4.2" graceful-fs "^4.2.9" - jest-diff "^29.4.0" - jest-get-type "^29.2.0" - jest-haste-map "^29.4.0" - jest-matcher-utils "^29.4.0" - jest-message-util "^29.4.0" - jest-util "^29.4.0" + jest-diff "^29.4.2" + jest-get-type "^29.4.2" + jest-haste-map "^29.4.2" + jest-matcher-utils "^29.4.2" + jest-message-util "^29.4.2" + jest-util "^29.4.2" natural-compare "^1.4.0" - pretty-format "^29.4.0" + pretty-format "^29.4.2" semver "^7.3.5" jest-util@^27.5.1: @@ -4890,42 +4883,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.0.tgz#1f3743c3dda843049623501c7e6f8fa5efdc2c2f" - integrity sha512-lCCwlze7UEV8TpR9ArS8w0cTbcMry5tlBkg7QSc5og5kNyV59dnY2aKHu5fY2k5aDJMQpCUGpvL2w6ZU44lveA== +jest-util@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" + integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.0.tgz#5090be16531051edc21bc0892553f7f7fab99bf0" - integrity sha512-EXS7u594nX3aAPBnARxBdJ1eZ1cByV6MWrK0Qpt9lt/BcY0p0yYGp/EGJ8GhdLDQh+RFf8qMt2wzbbVzpj5+Vg== +jest-validate@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.2.tgz#3b3f8c4910ab9a3442d2512e2175df6b3f77b915" + integrity sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q== dependencies: - "@jest/types" "^29.4.0" + "@jest/types" "^29.4.2" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^29.2.0" + jest-get-type "^29.4.2" leven "^3.1.0" - pretty-format "^29.4.0" + pretty-format "^29.4.2" -jest-watcher@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.0.tgz#4f51e6fba4341d965279a5a646adde5104d414f0" - integrity sha512-PnnfLygNKelWOJwpAYlcsQjB+OxRRdckD0qiGmYng4Hkz1ZwK3jvCaJJYiywz2msQn4rBNLdriasJtv7YpWHpA== +jest-watcher@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.2.tgz#09c0f4c9a9c7c0807fcefb1445b821c6f7953b7c" + integrity sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w== dependencies: - "@jest/test-result" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/test-result" "^29.4.2" + "@jest/types" "^29.4.2" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.4.0" + jest-util "^29.4.2" string-length "^4.0.1" jest-worker@^27.5.1: @@ -4937,30 +4930,30 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.0.tgz#fbf6d700c3366c555765938da75990c1e7fdcdcd" - integrity sha512-dICMQ+Q4W0QVMsaQzWlA1FVQhKNz7QcDCOGtbk1GCAd0Lai+wdkQvfmQwL4MjGumineh1xz+6M5oMj3rfWS02A== +jest-worker@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.2.tgz#d9b2c3bafc69311d84d94e7fb45677fc8976296f" + integrity sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw== dependencies: "@types/node" "*" - jest-util "^29.4.0" + jest-util "^29.4.2" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.0.tgz#c476b3d0c58af77f276f53f81d4d97e16105ae37" - integrity sha512-Zfd4UzNxPkSoHRBkg225rBjQNa6pVqbh20MGniAzwaOzYLd+pQUcAwH+WPxSXxKFs+QWYfPYIq9hIVSmdVQmPA== + version "29.4.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.2.tgz#4c2127d03a71dc187f386156ef155dbf323fb7be" + integrity sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg== dependencies: - "@jest/core" "^29.4.0" - "@jest/types" "^29.4.0" + "@jest/core" "^29.4.2" + "@jest/types" "^29.4.2" import-local "^3.0.2" - jest-cli "^29.4.0" + jest-cli "^29.4.2" jose@^4.10.0, jose@^4.9.3: - version "4.11.2" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.2.tgz#d9699307c02e18ff56825843ba90e2fae9f09e23" - integrity sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A== + version "4.11.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.4.tgz#e04d4a393ac017d0450fa0a38e2f2382cd73f71e" + integrity sha512-94FdcR8felat4vaTJyL/WVdtlWLlsnLMZP8v+A0Vru18K3bQ22vn7TtpVh3JlgBFNIlYOUlGqwp/MjRPOnIyCQ== js-sdsl@^4.1.4: version "4.3.0" @@ -5280,7 +5273,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meros@1.2.1: +meros@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.1.tgz#056f7a76e8571d0aaf3c7afcbe7eb6407ff7329e" integrity sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g== @@ -5330,9 +5323,9 @@ minimist@1.2.5: integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== minimist@^1.2.0, minimist@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" - integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== mkdirp@^0.5.1: version "0.5.6" @@ -5367,9 +5360,9 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next-auth@^4.18.8: - version "4.18.10" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.18.10.tgz#8ce63a671e990dbf5e03e8df5a34859e817f4880" - integrity sha512-j2JVyH7byZ2eqXlOqNLpOO7gOlAkTzblxLZNeZihKCeIabYFHNdAJgUvVahhnHs3pZ4xaa3ZqGQHgi1uE97L9Q== + version "4.19.2" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.19.2.tgz#8dbdc4886d4f006e7c67c77ab56e0aec5693064d" + integrity sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ== dependencies: "@babel/runtime" "^7.16.3" "@panva/hkdf" "^1.0.1" @@ -5382,29 +5375,29 @@ next-auth@^4.18.8: uuid "^8.3.2" next@^13.1.4: - version "13.1.5" - resolved "https://registry.yarnpkg.com/next/-/next-13.1.5.tgz#d0c230ccecf18414db78d180537d0474c53fdf73" - integrity sha512-rmpYZFCxxWAi2nJCT9sSqMLGC3cu+Pf689hx9clcyP0KbVIhh/7Dus5QcKrVd/PrAd6AjsuogSRR1mCP7BoYRw== + version "13.1.6" + resolved "https://registry.yarnpkg.com/next/-/next-13.1.6.tgz#054babe20b601f21f682f197063c9b0b32f1a27c" + integrity sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw== dependencies: - "@next/env" "13.1.5" + "@next/env" "13.1.6" "@swc/helpers" "0.4.14" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm-eabi" "13.1.5" - "@next/swc-android-arm64" "13.1.5" - "@next/swc-darwin-arm64" "13.1.5" - "@next/swc-darwin-x64" "13.1.5" - "@next/swc-freebsd-x64" "13.1.5" - "@next/swc-linux-arm-gnueabihf" "13.1.5" - "@next/swc-linux-arm64-gnu" "13.1.5" - "@next/swc-linux-arm64-musl" "13.1.5" - "@next/swc-linux-x64-gnu" "13.1.5" - "@next/swc-linux-x64-musl" "13.1.5" - "@next/swc-win32-arm64-msvc" "13.1.5" - "@next/swc-win32-ia32-msvc" "13.1.5" - "@next/swc-win32-x64-msvc" "13.1.5" + "@next/swc-android-arm-eabi" "13.1.6" + "@next/swc-android-arm64" "13.1.6" + "@next/swc-darwin-arm64" "13.1.6" + "@next/swc-darwin-x64" "13.1.6" + "@next/swc-freebsd-x64" "13.1.6" + "@next/swc-linux-arm-gnueabihf" "13.1.6" + "@next/swc-linux-arm64-gnu" "13.1.6" + "@next/swc-linux-arm64-musl" "13.1.6" + "@next/swc-linux-x64-gnu" "13.1.6" + "@next/swc-linux-x64-musl" "13.1.6" + "@next/swc-win32-arm64-msvc" "13.1.6" + "@next/swc-win32-ia32-msvc" "13.1.6" + "@next/swc-win32-x64-msvc" "13.1.6" no-case@^3.0.4: version "3.0.4" @@ -5414,11 +5407,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-domexception@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -5426,10 +5414,10 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.6.1, node-fetch@^2.6.7: - version "2.6.8" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" - integrity sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg== +node-fetch@^2.6.1: + version "2.6.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" + integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== dependencies: whatwg-url "^5.0.0" @@ -5438,10 +5426,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.6: - version "2.0.8" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.8.tgz#0f349cdc8fcfa39a92ac0be9bc48b7706292b9ae" - integrity sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A== +node-releases@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" + integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== nopt@^4.0.1: version "4.0.3" @@ -5588,18 +5576,18 @@ onetime@^5.1.0, onetime@^5.1.2: mimic-fn "^2.1.0" open@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" - integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + version "8.4.1" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.1.tgz#2ab3754c07f5d1f99a7a8d6a82737c95e3101cff" + integrity sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg== dependencies: define-lazy-prop "^2.0.0" is-docker "^2.1.1" is-wsl "^2.2.0" openid-client@^5.1.0: - version "5.3.2" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.3.2.tgz#fcc2c16f9681fa5f03ee0581b0935f88fc49f11f" - integrity sha512-nXXt+cna0XHOw+WqjMZOmuXw/YZEMwfWD2lD7tCsFtsBjMQGVXA+NZABA3upYBET1suhIsmfd7GnxG4jCAnvYQ== + version "5.4.0" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.0.tgz#77f1cda14e2911446f16ea3f455fc7c405103eac" + integrity sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ== dependencies: jose "^4.10.0" lru-cache "^6.0.0" @@ -5825,21 +5813,21 @@ preact-render-to-string@^5.1.19: pretty-format "^3.8.0" preact@^10.6.3: - version "10.11.3" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" - integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== + version "10.12.1" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.12.1.tgz#8f9cb5442f560e532729b7d23d42fd1161354a21" + integrity sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg== prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -pretty-format@^29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.0.tgz#766f071bb1c53f1ef8000c105bbeb649e86eb993" - integrity sha512-J+EVUPXIBHCdWAbvGBwXs0mk3ljGppoh/076g1S8qYS8nVG4u/yrhMvyTFHYYYKWnDdgRLExx0vA7pzxVGdlNw== +pretty-format@^29.4.2: + version "29.4.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" + integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== dependencies: - "@jest/schemas" "^29.4.0" + "@jest/schemas" "^29.4.2" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -5872,6 +5860,11 @@ prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + punycode@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" @@ -5930,16 +5923,15 @@ rc-checkbox@~2.3.0: "@babel/runtime" "^7.10.1" classnames "^2.2.1" -rc-collapse@~3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.4.2.tgz#1310be7ad4cd0dcfc622c45f6c3b5ffdee403ad7" - integrity sha512-jpTwLgJzkhAgp2Wpi3xmbTbbYExg6fkptL67Uu5LCRVEj6wqmy0DHTjjeynsjOLsppHGHu41t1ELntZ0lEvS/Q== +rc-collapse@~3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.5.2.tgz#abb7d144ad55bd9cbd201fa95bc5b271da2aa7c3" + integrity sha512-/TNiT3DW1t3sUCiVD/DPUYooJZ3BLA93/2rZsB3eM2bGJCCla2X9D2E4tgm7LGMQGy5Atb2lMUn2FQuvQNvavQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.3.4" - rc-util "^5.2.1" - shallowequal "^1.1.0" + rc-util "^5.27.0" rc-dialog@~9.0.0, rc-dialog@~9.0.2: version "9.0.2" @@ -5974,9 +5966,9 @@ rc-dropdown@~4.0.0: rc-util "^5.17.0" rc-field-form@~1.27.0: - version "1.27.3" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.3.tgz#e5262796b91c80848a42a3e7a669bf459f08d63d" - integrity sha512-HGqxHnmGQgkPApEcikV4qTg3BLPC82uB/cwBDftDt1pYaqitJfSl5TFTTUMKVEJVT5RqJ2Zi68ME1HmIMX2HAw== + version "1.27.4" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.4.tgz#53600714af5b28c226c70d34867a8c52ccd64d44" + integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== dependencies: "@babel/runtime" "^7.18.0" async-validator "^4.1.0" @@ -6004,24 +5996,25 @@ rc-input-number@~7.4.0: classnames "^2.2.5" rc-util "^5.23.0" -rc-input@~0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.1.4.tgz#45cb4ba209ae6cc835a2acb8629d4f8f0cb347e0" - integrity sha512-FqDdNz+fV2dKNgfXzcSLKvC+jEs1709t7nD+WdfjrdSaOcefpgc7BUJYadc3usaING+b7ediMTfKxuJBsEFbXA== +rc-input@^0.2.1, rc-input@^0.2.2, rc-input@~0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.2.2.tgz#de1c3c0e090d15777a6b1a3fd4a9566e25b3fbee" + integrity sha512-xgkVcFgtRO0Hl9hmvslZhObNyxbSpTmy3nR1Tk4XrjjZ9lFJ7GcJBy6ss30Pdb0oX36cHzLN8I7VCjBGeRNB9A== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.13.1.tgz#c884b70e1505a197f1b32a7c6b39090db6992a72" - integrity sha512-FCkaWw6JQygtOz0+Vxz/M/NWqrWHB9LwqlY2RtcuFqWJNFK9njijOOzTSsBGANliGufVUzx/xuPHmZPBV0+Hgw== +rc-mentions@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.0.0.tgz#688846d698943340334657d96c1448d8b57e5666" + integrity sha512-58NSeM6R5MrgYAhR2TH27JgAN7ivp3iBTmty3q6gvrrGHelPMdGxpJ5aH7AIlodCrPWLAm1lT4XoiuI4s9snXA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" + rc-input "^0.2.2" rc-menu "~9.8.0" - rc-textarea "^0.4.0" + rc-textarea "^1.0.0" rc-trigger "^5.0.4" rc-util "^5.22.5" @@ -6075,15 +6068,14 @@ rc-pagination@~3.2.0: classnames "^2.2.1" rc-picker@~3.1.1: - version "3.1.4" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.1.4.tgz#4806dc36a78424abaee610830777c8e22a23c74c" - integrity sha512-4qANXNc3C02YENNQvun329zf9VLvSQ2W8RkKQRu8k1P+EtSGqe3klcAKCfz/1TuCiDvgRjJlzRmyZAkwvsbI8w== + version "3.1.5" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.1.5.tgz#49953655a92cda3dee59678f4cbcdee61f5b0f0c" + integrity sha512-Hh3ml+u+5mxLfl4ahVWlRGiX5+0EJrALR6tSW9yP0eea+6j+YjvjfetbvuVidViMDMweZa38dr8HTfAFLG6GFw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" rc-trigger "^5.0.4" - rc-util "^5.4.0" - shallowequal "^1.1.0" + rc-util "^5.27.0" rc-progress@~3.4.1: version "3.4.1" @@ -6104,19 +6096,19 @@ rc-rate@~2.9.0: rc-util "^5.0.1" rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.1.tgz#7f9715b5d1afe126ade3c107aafd2cebf8a57a99" - integrity sha512-g53PnWLeVOmt4XWkt2x+QlIdf/PhJSd7JqHhtMrUY370e7wJ+kxbgXicYqvENUcgFiiOiMCd07YsC2GNsoSbnA== + version "1.3.1" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.3.1.tgz#b61b9f27048001243617b81f95e53d7d7d7a6a3d" + integrity sha512-iFUdt3NNhflbY3mwySv5CA1TC06zdJ+pfo0oc27xpf4PIOvfZwZGtD9Kz41wGYqC4SLio93RVAirSSpYlV/uYg== dependencies: "@babel/runtime" "^7.20.7" classnames "^2.2.1" rc-util "^5.27.0" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.0.tgz#0e0afe646c1a0e44a0e18785f518c42633ec8efc" - integrity sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw== +rc-segmented@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.2.tgz#14c9077a1dae9c2ccb2ef5fbc5662c1c48c7ce8e" + integrity sha512-qGo1bCr83ESXpXVOCXjFe1QJlCAQXyi9KCiy8eX3rIMYlTeJr/ftySIaTnYsitL18SvWf5ZEHsfqIWoX0EMfFQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" @@ -6136,15 +6128,14 @@ rc-select@~14.2.0: rc-util "^5.16.1" rc-virtual-list "^3.4.13" -rc-slider@~10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9" - integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q== +rc-slider@~10.1.0: + version "10.1.1" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.1.1.tgz#5e82036e60b61021aba3ea0e353744dd7c74e104" + integrity sha512-gn8oXazZISEhnmRinI89Z/JD/joAaM35jp+gDtIVSTD/JJMCCBqThqLk1SVJmvtfeiEF/kKaFY0+qt4SDHFUDw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-util "^5.18.1" - shallowequal "^1.1.0" + rc-util "^5.27.0" rc-steps@~6.0.0: version "6.0.0" @@ -6175,7 +6166,7 @@ rc-table@~7.30.2: rc-resize-observer "^1.1.0" rc-util "^5.27.1" -rc-tabs@~12.5.1: +rc-tabs@~12.5.6: version "12.5.6" resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.5.6.tgz#611728575d5f3ef010a8e5e75d2c1b67bb71e86a" integrity sha512-aArXHzxK7YICxe+622CZ8FlO5coMi8P7E6tXpseCPKm1gdTjUt0LrQK1/AxcrRXZXG3K4QqhlKmET0+cX5DQaQ== @@ -6188,25 +6179,25 @@ rc-tabs@~12.5.1: rc-resize-observer "^1.0.0" rc-util "^5.16.0" -rc-textarea@^0.4.0, rc-textarea@~0.4.5: - version "0.4.7" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.4.7.tgz#627f662d46f99e0059d1c1ebc8db40c65339fe90" - integrity sha512-IQPd1CDI3mnMlkFyzt2O4gQ2lxUsnBAeJEoZGJnkkXgORNqyM9qovdrCj9NzcRfpHgLdzaEbU3AmobNFGUznwQ== +rc-textarea@^1.0.0, rc-textarea@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.0.1.tgz#44b1e09eda37a7af19cf884251e530cc7b03050b" + integrity sha512-dtIm96apjJpCUcCeTtbnLGJaVlqbOqVgN0P9z+bqMSi7rcV5QVeUtBnG+jQTGk/uD183Z7jbhc8Dx7G3luDCwg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" + rc-input "^0.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.24.4" - shallowequal "^1.1.0" + rc-util "^5.27.0" -rc-tooltip@~5.2.0: - version "5.2.2" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.2.2.tgz#e5cafa8ecebf78108936a0bcb93c150fa81ac93b" - integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg== +rc-tooltip@~5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.3.1.tgz#3dde4e1865f79cd23f202bba4e585c2a1173024b" + integrity sha512-e6H0dMD38EPaSPD2XC8dRfct27VvT2TkPdoBSuNl3RRZ5tspiY/c5xYEmGC0IrABvMBgque4Mr2SMZuliCvoiQ== dependencies: "@babel/runtime" "^7.11.2" classnames "^2.3.1" - rc-trigger "^5.0.0" + rc-trigger "^5.3.1" rc-tree-select@~5.6.0: version "5.6.0" @@ -6230,7 +6221,7 @@ rc-tree@~5.7.0: rc-util "^5.16.1" rc-virtual-list "^3.4.8" -rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.3.1, rc-trigger@^5.3.4: +rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.3.1, rc-trigger@^5.3.4: version "5.3.4" resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.4.tgz#6b4b26e32825677c837d1eb4d7085035eecf9a61" integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== @@ -6250,10 +6241,10 @@ rc-upload@~4.3.0: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.27.1, rc-util@^5.4.0, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: - version "5.27.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.27.1.tgz#d12f02b9577b04299c0f1a235c8acbcf56e2824b" - integrity sha512-PsjHA+f+KBCz+YTZxrl3ukJU5RoNKoe3KSNMh0xGiISbR67NaM9E9BiMjCwxa3AcCUOg/rZ+V0ZKLSimAA+e3w== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.27.1, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: + version "5.27.2" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.27.2.tgz#472a7bab26a62856c2c016d18dc6356e46d01012" + integrity sha512-8XHRbeJOWlTR2Hk1K2xLaPOf7lZu+3taskAGuqOPccA676vB3ygrz3ZgdrA3wml40CzR9RlIEHDWwI7FZT3wBQ== dependencies: "@babel/runtime" "^7.18.3" react-is "^16.12.0" @@ -6603,11 +6594,6 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -shallowequal@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8" - integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ== - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6621,9 +6607,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.7.3: - version "1.7.4" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.4.tgz#33fe15dee71ab2a81fcbd3a52106c5cfb9fb75d8" - integrity sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" + integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== side-channel@^1.0.4: version "1.0.4" @@ -6937,12 +6923,12 @@ symbol-observable@^4.0.0: integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== synckit@^0.8.4: - version "0.8.4" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.4.tgz#0e6b392b73fafdafcde56692e3352500261d64ec" - integrity sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw== + version "0.8.5" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== dependencies: "@pkgr/utils" "^2.3.1" - tslib "^2.4.0" + tslib "^2.5.0" tapable@^2.2.0: version "2.2.1" @@ -7073,10 +7059,10 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.4.1, tslib@~2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0, tslib@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== tslib@~2.0.1: version "2.0.3" @@ -7093,6 +7079,11 @@ tslib@~2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@~2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -7139,9 +7130,9 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^4.0: - version "4.9.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" - integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== ua-parser-js@^0.7.30: version "0.7.33" @@ -7163,13 +7154,6 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undici@^5.12.0: - version "5.16.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.16.0.tgz#6b64f9b890de85489ac6332bd45ca67e4f7d9943" - integrity sha512-KWBOXNv6VX+oJQhchXieUznEmnJMqgXMbs0xxH2t8q/FUAWSJvOSr/rMaZKnX5RIVq7JDn0JbP4BOnKG2SGXLQ== - dependencies: - busboy "^1.6.0" - universal-cookie@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" @@ -7185,7 +7169,7 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.0.9: +update-browserslist-db@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== @@ -7258,12 +7242,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -value-or-promise@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" - integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== - -value-or-promise@1.0.12, value-or-promise@^1.0.11: +value-or-promise@1.0.12, value-or-promise@^1.0.11, value-or-promise@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== @@ -7282,20 +7261,15 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" -web-streams-polyfill@4.0.0-beta.3: - version "4.0.0-beta.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" - integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== - -web-streams-polyfill@^3.2.0: +web-streams-polyfill@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== webcrypto-core@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.5.tgz#c02104c953ca7107557f9c165d194c6316587ca4" - integrity sha512-gaExY2/3EHQlRNNNVSrbG2Cg94Rutl7fAaKILS1w8ZDhGxdFOaw6EbCfHIxPy9vt/xwp5o0VQAx9aySPF6hU1A== + version "1.7.6" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" + integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== dependencies: "@peculiar/asn1-schema" "^2.1.6" "@peculiar/json-schema" "^1.1.12" @@ -7404,15 +7378,15 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write-file-atomic@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0" - integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w== +write-file-atomic@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.12.0: +ws@8.12.0, ws@^8.12.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== From f1010243003bc9dbffcc8b569e0a6acc5af4db08 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 13 Feb 2023 14:37:40 +0100 Subject: [PATCH 051/878] Upgrade frontend packages to latest version --- frontend/package.json | 14 ++-- frontend/yarn.lock | 169 ++++++++++++++++++++++-------------------- 2 files changed, 94 insertions(+), 89 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 1fa9835e..0c72a21d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,7 @@ "start": "next start -p 8080" }, "dependencies": { - "@apollo/client": "3.4.*", + "@apollo/client": "^3.7.0", "antd": "^5.0", "dayjs": "^1.0", "graphql": "^16.0.1", @@ -25,14 +25,14 @@ }, "devDependencies": { "@babel/core": "^7.0", - "@graphql-codegen/cli": "^2.0.1", - "@graphql-codegen/fragment-matcher": "^3.1.0", + "@graphql-codegen/cli": "^3.0.0", + "@graphql-codegen/fragment-matcher": "^4.0.0", "@graphql-codegen/import-types-preset": "^2.0.0", - "@graphql-codegen/plugin-helpers": "^3.0.0", - "@graphql-codegen/typescript": "^2.0.0", - "@graphql-codegen/typescript-operations": "^2.0.1", + "@graphql-codegen/plugin-helpers": "^4.0.0", + "@graphql-codegen/typescript": "^3.0.0", + "@graphql-codegen/typescript-operations": "^3.0.0", "@graphql-codegen/typescript-react-apollo": "^3.0.0", - "@graphql-codegen/typescript-resolvers": "^2.0.0", + "@graphql-codegen/typescript-resolvers": "^3.0.0", "@graphql-tools/load-files": "^6.0", "@graphql-tools/merge": "^8.0.1", "@graphql-tools/schema": "^9.0.13", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 49832ca7..0b53e0d0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -57,23 +57,24 @@ resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" -"@apollo/client@3.4.*": - version "3.4.17" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.4.17.tgz#4972e19a49809e16d17c5adc67f45623a6dac135" - integrity sha512-MDt2rwMX1GqodiVEKJqmDmAz8xr0qJmq5PdWeIt0yDaT4GOkKYWZiWkyfhfv3raTk8PyJvbsNG9q2CqmUrlGfg== +"@apollo/client@^3.7.0": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.7.tgz#5eb6e8af24fb809c97c8f66c3faf9524f83c412b" + integrity sha512-Rp/pCWuJSjLN7Xl5Qi2NoeURmZYEU/TIUz0n/LOwEo1tGdU2W7/fGVZ8+5um58JeVYq4UoTGBKFxSVeG4s411A== dependencies: - "@graphql-typed-document-node/core" "^3.0.0" - "@wry/context" "^0.6.0" + "@graphql-typed-document-node/core" "^3.1.1" + "@wry/context" "^0.7.0" "@wry/equality" "^0.5.0" "@wry/trie" "^0.3.0" - graphql-tag "^2.12.3" + graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" optimism "^0.16.1" prop-types "^15.7.2" + response-iterator "^0.2.6" symbol-observable "^4.0.0" - ts-invariant "^0.9.0" + ts-invariant "^0.10.3" tslib "^2.3.0" - zen-observable-ts "~1.1.0" + zen-observable-ts "^1.2.5" "@ardatan/aggregate-error@0.0.6": version "0.0.6" @@ -745,18 +746,18 @@ "@graphql-codegen/plugin-helpers" "^3.1.1" tslib "~2.4.0" -"@graphql-codegen/cli@^2.0.1": - version "2.16.5" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-2.16.5.tgz#b3b5eeec357af01c1cb72f6a4ea96e52bd49e662" - integrity sha512-XYPIp+q7fB0xAGSAoRykiTe4oY80VU+z+dw5nuv4mLY0+pv7+pa2C6Nwhdw7a65lXOhFviBApWCCZeqd54SMnA== +"@graphql-codegen/cli@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.0.0.tgz#eb367adfe51349e4822518183fc7ea7c06aea11b" + integrity sha512-16nuFabHCfPQ/d+v52OvR1ueL8eiJvS/nRuvuEV8d9T1fkborHKRw4lhyKVebu9izFBs6G0CvVCLhgVzQwHSLw== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/core" "^2.6.8" - "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/core" "^3.0.0" + "@graphql-codegen/plugin-helpers" "^4.0.0" "@graphql-tools/apollo-engine-loader" "^7.3.6" - "@graphql-tools/code-file-loader" "^7.3.13" + "@graphql-tools/code-file-loader" "^7.3.17" "@graphql-tools/git-loader" "^7.2.13" "@graphql-tools/github-loader" "^7.3.20" "@graphql-tools/graphql-file-loader" "^7.5.0" @@ -786,22 +787,22 @@ yaml "^1.10.0" yargs "^17.0.0" -"@graphql-codegen/core@^2.6.8": - version "2.6.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-2.6.8.tgz#00c4011e3619ddbc6af5e41b2f254d6f6759556e" - integrity sha512-JKllNIipPrheRgl+/Hm/xuWMw9++xNQ12XJR/OHHgFopOg4zmN3TdlRSyYcv/K90hCFkkIwhlHFUQTfKrm8rxQ== +"@graphql-codegen/core@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-3.0.0.tgz#ff6d643bb9d80c54ddd9ee5c68194888c971ae45" + integrity sha512-WUfAUTmUcgeHPR7F5ZQqaBqJLJb5+3Lvp6v9SrnupKOFed+Q3u8CvZL6sPTvDpqqW8Ucjy59DEZqumPLp99pdQ== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.1" + "@graphql-codegen/plugin-helpers" "^4.0.0" "@graphql-tools/schema" "^9.0.0" "@graphql-tools/utils" "^9.1.1" tslib "~2.4.0" -"@graphql-codegen/fragment-matcher@^3.1.0": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-3.3.3.tgz#781ed32984eb0aafdedeaf0c22f98fce00755d09" - integrity sha512-lZjarTYQ+w0/TYyoKNFHgIUBI6//rxjo4CwNmOmqGQA0LL+p2nh+/ICJKMFuejPFdK9LI84Y+EEovEFTcDPC+Q== +"@graphql-codegen/fragment-matcher@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-4.0.0.tgz#2908d0107d5ef23e8931cf5760bd42e9a007cdf1" + integrity sha512-pkVrFjosh/uPY3x9208AyoCVYUDoMvkcLb2ytMCS0KbMVjRFFpnsIa38HPVGIzlGkwZBEwh5wTH4sJ4FiFpGBw== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.1" + "@graphql-codegen/plugin-helpers" "^4.0.0" tslib "~2.4.0" "@graphql-codegen/import-types-preset@^2.0.0": @@ -837,7 +838,7 @@ lodash "~4.17.0" tslib "~2.4.0" -"@graphql-codegen/plugin-helpers@^3.0.0", "@graphql-codegen/plugin-helpers@^3.1.1", "@graphql-codegen/plugin-helpers@^3.1.2": +"@graphql-codegen/plugin-helpers@^3.1.1": version "3.1.2" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz#69a2e91178f478ea6849846ade0a59a844d34389" integrity sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg== @@ -849,23 +850,35 @@ lodash "~4.17.0" tslib "~2.4.0" -"@graphql-codegen/schema-ast@^2.6.1": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-2.6.1.tgz#8ba1b38827c034b51ecd3ce88622c2ae6cd3fe1a" - integrity sha512-5TNW3b1IHJjCh07D2yQNGDQzUpUl2AD+GVe1Dzjqyx/d2Fn0TPMxLsHsKPS4Plg4saO8FK/QO70wLsP7fdbQ1w== +"@graphql-codegen/plugin-helpers@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.0.0.tgz#9c10e4700dc6efe657781dff47347d0c99674061" + integrity sha512-vgNGTanT36hC4RAC/LAThMEjDvnu3WCyx6MtKZcPUtfCWFxbUAr88+OarGl1LAEiOef0agIREC7tIBXCqjKkJA== + dependencies: + "@graphql-tools/utils" "^9.0.0" + change-case-all "1.0.15" + common-tags "1.8.2" + import-from "4.0.0" + lodash "~4.17.0" + tslib "~2.4.0" + +"@graphql-codegen/schema-ast@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-3.0.0.tgz#3311a58184f885853d33d8272c527ff5bdf3023b" + integrity sha512-5gC8nNk/bxufS2LBSpaPExRgn6eNo8LQdtwDtwfM9XGEzt/F6rIBQoyOmqqwkiBmgu1PHHH8kLZMBYvYB1x5DA== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/plugin-helpers" "^4.0.0" "@graphql-tools/utils" "^9.0.0" tslib "~2.4.0" -"@graphql-codegen/typescript-operations@^2.0.1": - version "2.5.13" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-2.5.13.tgz#f286c37f9c023356aacaa983ebd32e9e021a05ca" - integrity sha512-3vfR6Rx6iZU0JRt29GBkFlrSNTM6t+MSLF86ChvL4d/Jfo/JYAGuB3zNzPhirHYzJPCvLOAx2gy9ID1ltrpYiw== +"@graphql-codegen/typescript-operations@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.0.tgz#e67c9dedb739a20f8a1d2c9a9631701c269de0c1" + integrity sha512-t+Lk+lxkUFDh6F0t8CErowOccP3bZwxhl66qmEeBcOrC7jQrSCnRZoFvOXhFKFBJe/y4DIJiizgSr34AqjiJIQ== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-codegen/typescript" "^2.8.8" - "@graphql-codegen/visitor-plugin-common" "2.13.8" + "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/typescript" "^3.0.0" + "@graphql-codegen/visitor-plugin-common" "3.0.0" auto-bind "~4.0.0" tslib "~2.4.0" @@ -892,14 +905,14 @@ auto-bind "~4.0.0" tslib "~2.2.0" -"@graphql-codegen/typescript-resolvers@^2.0.0": - version "2.7.13" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-2.7.13.tgz#169ba75bf686a96c2222ac8c5141e75ab6b4cb20" - integrity sha512-ZIXiUw86ctNYQe41RAwadhSMe0koNvomxNRK+ZYlWQn1gplpc3jcdgI+BrdJbBWK/6VBYKZzrayJHr1VsHfYJQ== +"@graphql-codegen/typescript-resolvers@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.0.0.tgz#69856b9eb003660b7c7551727a63764b8a6c89ee" + integrity sha512-yfRNJF9ZUoiLSPhaBEpQ9E4ymExYzbsKgjU2qJ/0c8l0lViA+o4ALkJMTwb7yU/yTayQtQWgxL5cdDY2I12GpQ== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-codegen/typescript" "^2.8.8" - "@graphql-codegen/visitor-plugin-common" "2.13.8" + "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/typescript" "^3.0.0" + "@graphql-codegen/visitor-plugin-common" "3.0.0" "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" tslib "~2.4.0" @@ -914,14 +927,14 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^2.0.0", "@graphql-codegen/typescript@^2.8.8": - version "2.8.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-2.8.8.tgz#8c3b9153e334db43c65f8f31ced69b4c60d14861" - integrity sha512-A0oUi3Oy6+DormOlrTC4orxT9OBZkIglhbJBcDmk34jAKKUgesukXRd4yOhmTrnbchpXz2T8IAOFB3FWIaK4Rw== +"@graphql-codegen/typescript@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.0.tgz#473dde1646540039bca5db4b6daf174d13af0ce3" + integrity sha512-FQWyuIUy1y+fxb9+EZfvdBHBQpYExlIBHV5sg2WGNCsyVyCqBTl0mO8icyOtsQPVg6YFMFe8JJO69vQbwHma5w== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-codegen/schema-ast" "^2.6.1" - "@graphql-codegen/visitor-plugin-common" "2.13.8" + "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/schema-ast" "^3.0.0" + "@graphql-codegen/visitor-plugin-common" "3.0.0" auto-bind "~4.0.0" tslib "~2.4.0" @@ -957,12 +970,12 @@ parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@2.13.8": - version "2.13.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz#09bc6317b227e5a278f394f4cef0d6c2d1910597" - integrity sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ== +"@graphql-codegen/visitor-plugin-common@3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.0.0.tgz#527185eb3b1b06739702084bc6263713e167a166" + integrity sha512-ZoNlCmmkGClB137SpJT9og/nkihLN7Z4Ynl9Ir3OlbDuI20dbpyXsclpr9QGLcxEcfQeVfhGw9CooW7wZJJ8LA== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" + "@graphql-codegen/plugin-helpers" "^4.0.0" "@graphql-tools/optimize" "^1.3.0" "@graphql-tools/relay-operation-optimizer" "^6.5.0" "@graphql-tools/utils" "^9.0.0" @@ -993,7 +1006,7 @@ tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-tools/code-file-loader@^7.3.13": +"@graphql-tools/code-file-loader@^7.3.17": version "7.3.20" resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.20.tgz#6f523bcb5b5bcdbcede4b5894cbcb1890132b002" integrity sha512-htwylU+/if5j5rgrd/i2xgM22cWC2RGgUGO7K+nxZU+l7iCimJUdDQnqCW9G3eVHbLpVOhyza9bBUNMPzh3sxg== @@ -1271,7 +1284,7 @@ tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-typed-document-node/core@3.1.1", "@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.1": +"@graphql-typed-document-node/core@3.1.1", "@graphql-typed-document-node/core@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== @@ -2022,11 +2035,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/zen-observable@0.8.3": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3" - integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw== - "@typescript-eslint/parser@^5.42.0": version "5.51.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.51.0.tgz#2d74626652096d966ef107f44b9479f02f51f271" @@ -2118,13 +2126,6 @@ fast-url-parser "^1.1.3" tslib "^2.3.1" -"@wry/context@^0.6.0": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2" - integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw== - dependencies: - tslib "^2.3.0" - "@wry/context@^0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.0.tgz#be88e22c0ddf62aeb0ae9f95c3d90932c619a5c8" @@ -3996,7 +3997,7 @@ graphql-request@^5.0.0: extract-files "^9.0.0" form-data "^3.0.0" -graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.3: +graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== @@ -6467,6 +6468,11 @@ resolve@^2.0.0-next.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +response-iterator@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" + integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== + restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -7013,10 +7019,10 @@ treeify@^1.1.0: resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== -ts-invariant@^0.9.0: - version "0.9.4" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.9.4.tgz#42ac6c791aade267dd9dc65276549df5c5d71cac" - integrity sha512-63jtX/ZSwnUNi/WhXjnK8kz4cHHpYS60AnmA6ixz17l7E12a5puCWFlNpkne5Rl0J8TBPVHpGjsj4fxs8ObVLQ== +ts-invariant@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" + integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== dependencies: tslib "^2.1.0" @@ -7488,12 +7494,11 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zen-observable-ts@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83" - integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA== +zen-observable-ts@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== dependencies: - "@types/zen-observable" "0.8.3" zen-observable "0.8.15" zen-observable@0.8.15: From 4f2be3697ec96bb6d71b34d7fe9ccaa07079413c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 13 Feb 2023 14:41:46 +0100 Subject: [PATCH 052/878] Do not install frontend packages before entering shell (because the packages are always up-to-date as they are contained in the image which is rebuilt each time the lockfile changes) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2e36feee..d53a49f1 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ runb : CONTAINER = backend runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container (after starting all containers if necessary) .PHONY : runb -shellf : COMMAND = bash -c "make install && exec bash" +shellf : COMMAND = bash shellf : execf ## Enter shell in an existing `frontend` container (after starting all containers if necessary) .PHONY : shellf From 089d90950031835b10d7d7b185f86e1199983cd9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 13 Feb 2023 14:56:25 +0100 Subject: [PATCH 053/878] Add some text to the home site (to solve some layout issues) --- frontend/pages/index.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index e94406c5..d44d3be0 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,7 +1,16 @@ import Layout from "../components/Layout"; +import { Typography } from "antd"; +import Link from "next/link"; +import paths from "../paths"; const Index = () => { - return ; + return +
+ + solarbuildingenvelopes.com is an example of a database in the network Building Envelope Data. It provides access to some data of the TestLab Solar Façades which is an accredited laboratory at Fraunhofer Institute for Solar Energy Systems (ISE). The database is a live instance of the reference implementation. + +
+
; }; export default Index; From bba425d97caff2a21beb8a013f20f37648c15a79 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Feb 2023 14:19:25 +0100 Subject: [PATCH 054/878] Assume yes when upgrading packages with APT --- backend/Dockerfile-production | 4 ++-- frontend/Dockerfile-production | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 75f81b36..992d7be1 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -17,7 +17,7 @@ FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 AS build RUN \ apt-get update && \ - apt-get upgrade && \ + apt-get upgrade --assume-yes && \ rm \ --recursive \ --force \ @@ -45,7 +45,7 @@ FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim-amd64 RUN \ apt-get update && \ - apt-get upgrade && \ + apt-get upgrade --assume-yes && \ apt-get install \ --yes \ --no-install-recommends \ diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index d72558cb..20167c7d 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -8,7 +8,7 @@ FROM node:18.13-bullseye-slim AS dependencies RUN \ apt-get update && \ - apt-get upgrade && \ + apt-get upgrade --assume-yes && \ rm \ --recursive \ --force \ @@ -20,7 +20,7 @@ RUN yarn install --pure-lockfile FROM node:18.13-bullseye-slim AS build RUN \ apt-get update && \ - apt-get upgrade && \ + apt-get upgrade --assume-yes && \ rm \ --recursive \ --force \ @@ -41,9 +41,9 @@ COPY --from=build /app/package.json ./ COPY --from=build /app/public ./public RUN \ apt-get update && \ - apt-get upgrade && \ + apt-get upgrade --assume-yes && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ wget && \ rm \ From a091c4a9d05cef63adc179c8559a2af5264b3aea Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Feb 2023 12:30:30 +0100 Subject: [PATCH 055/878] Prefer `--assume-yes` over `--yes` --- backend/Dockerfile | 4 ++-- backend/Dockerfile-production | 2 +- frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index f2788a86..97bf3ff2 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -40,7 +40,7 @@ RUN \ RUN \ apt-get update && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ curl \ dumb-init \ @@ -59,7 +59,7 @@ RUN \ RUN \ apt-get update && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ bash \ jq \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 75f81b36..fbc9cf57 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -47,7 +47,7 @@ RUN \ apt-get update && \ apt-get upgrade && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ curl && \ rm \ diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 11bda9e5..27659414 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -27,7 +27,7 @@ ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME RUN \ apt-get update && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ ca-certificates \ dumb-init \ diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index d72558cb..b554611c 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -43,7 +43,7 @@ RUN \ apt-get update && \ apt-get upgrade && \ apt-get install \ - --yes \ + --assume-yes \ --no-install-recommends \ wget && \ rm \ From 4cbdc4128a4f4d04b86b9096ffe5812630d5f338 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Feb 2023 10:17:06 +0100 Subject: [PATCH 056/878] Name `Index` page component but `Page` --- frontend/pages/index.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index d44d3be0..850730f4 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -3,14 +3,16 @@ import { Typography } from "antd"; import Link from "next/link"; import paths from "../paths"; -const Index = () => { - return +function Page() { + return ( +
solarbuildingenvelopes.com is an example of a database in the network Building Envelope Data. It provides access to some data of the TestLab Solar Façades which is an accredited laboratory at Fraunhofer Institute for Solar Energy Systems (ISE). The database is a live instance of the reference implementation.
-
; -}; +
+ ); +} -export default Index; +export default Page; From 847e58d33ed34d9bcc84e3ea257d73277caa5a07 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Feb 2023 10:26:54 +0100 Subject: [PATCH 057/878] Install prettier, add GNU Make target `format` and format code with `make format` --- frontend/.graphql-let.yml | 4 +- frontend/.prettierignore | 3 + frontend/Makefile | 5 + frontend/codegen.yml | 4 +- frontend/jest.config.js | 18 +-- frontend/lib/apollo.ts | 8 +- frontend/lib/form.ts | 81 ++++++------ frontend/next.config.js | 18 +-- frontend/package.json | 1 + frontend/pages/api/auth/[...nextauth].ts | 5 +- frontend/pages/data/calorimetric.tsx | 18 ++- frontend/pages/data/hygrothermal.tsx | 18 ++- frontend/pages/data/index.tsx | 14 +- frontend/pages/data/optical.tsx | 18 ++- frontend/pages/data/photovoltaic.tsx | 18 ++- frontend/pages/index.tsx | 21 ++- frontend/public/site.webmanifest | 20 ++- frontend/test/index.test.tsx | 34 ++--- frontend/tsconfig.json | 14 +- frontend/type-defs.graphqls | 160 ++++++++++++++++++++--- frontend/yarn.lock | 5 + 21 files changed, 316 insertions(+), 171 deletions(-) create mode 100644 frontend/.prettierignore diff --git a/frontend/.graphql-let.yml b/frontend/.graphql-let.yml index c820c15c..1d11a6a5 100644 --- a/frontend/.graphql-let.yml +++ b/frontend/.graphql-let.yml @@ -1,5 +1,5 @@ -schema: '**/*.graphqls' -documents: '**/*.graphql' +schema: "**/*.graphqls" +documents: "**/*.graphql" plugins: - typescript-operations - typescript-react-apollo diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 00000000..7157d53b --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,3 @@ +/node_modules +/.next +/__generated__ diff --git a/frontend/Makefile b/frontend/Makefile index b6fdedd3..1aa5c50d 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -29,6 +29,11 @@ lint : ## Lint code next lint .PHONY : lint +format : ## Format the code + yarn run \ + prettier --write . +.PHONY : format + licenses : ## Print licenses yarn run \ license-checker \ diff --git a/frontend/codegen.yml b/frontend/codegen.yml index cb204fcf..d1ff6d03 100644 --- a/frontend/codegen.yml +++ b/frontend/codegen.yml @@ -4,8 +4,8 @@ generates: # https://www.graphql-code-generator.com/docs/plugins/fragment-matcher __generated__/fragmentMatcherTypes.ts: plugins: - - 'fragment-matcher' + - "fragment-matcher" config: - module: 'es2015' + module: "es2015" apolloClientVersion: 3 useExplicitTyping: false diff --git a/frontend/jest.config.js b/frontend/jest.config.js index 1046252b..c7c929e1 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -1,13 +1,13 @@ module.exports = { - roots: [''], - moduleFileExtensions: ['ts', 'tsx', 'js', 'json', 'jsx'], - testPathIgnorePatterns: ['[/\\\\](node_modules|.next)[/\\\\]'], - transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$'], + roots: [""], + moduleFileExtensions: ["ts", "tsx", "js", "json", "jsx"], + testPathIgnorePatterns: ["[/\\\\](node_modules|.next)[/\\\\]"], + transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$"], transform: { - '^.+\\.(ts|tsx)$': 'babel-jest', - '\\.graphql$': [ - 'graphql-let/jestTransformer', - { subsequentTransformer: 'babel-jest' }, + "^.+\\.(ts|tsx)$": "babel-jest", + "\\.graphql$": [ + "graphql-let/jestTransformer", + { subsequentTransformer: "babel-jest" }, ], }, -} +}; diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 2a93e398..06191ee8 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -9,7 +9,7 @@ import { } from "@apollo/client"; import merge from "deepmerge"; import { message } from "antd"; -import fragmentMatcherTypes from '../__generated__/fragmentMatcherTypes'; // generated by `fragment-matcher` plugin +import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin let apolloClient: ApolloClient | undefined; @@ -35,7 +35,7 @@ function createIsomorphLink(context: ResolverContext = {}) { useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", headers: { - accept: 'application/json', + accept: "application/json", cookie: context.req ? context.req.headers.cookie : null, }, }); @@ -45,7 +45,9 @@ function createApolloClient(context?: ResolverContext) { return new ApolloClient({ ssrMode: typeof window === "undefined", link: createIsomorphLink(context), - cache: new InMemoryCache({ possibleTypes: fragmentMatcherTypes.possibleTypes }), + cache: new InMemoryCache({ + possibleTypes: fragmentMatcherTypes.possibleTypes, + }), }); } diff --git a/frontend/lib/form.ts b/frontend/lib/form.ts index f56746f0..44367b40 100644 --- a/frontend/lib/form.ts +++ b/frontend/lib/form.ts @@ -1,49 +1,42 @@ -import { GraphQLError } from 'graphql' -import { FormInstance } from 'antd' -import { Dispatch, SetStateAction } from 'react' +import { GraphQLError } from "graphql"; +import { FormInstance } from "antd"; +import { Dispatch, SetStateAction } from "react"; export function handleFormErrors( - graphQlErrors: readonly GraphQLError[] | undefined, - userErrors: { code: string, message: string, path: string[] }[] | undefined, - setGlobalErrorMessages: Dispatch>, - form: FormInstance + graphQlErrors: readonly GraphQLError[] | undefined, + userErrors: { code: string; message: string; path: string[] }[] | undefined, + setGlobalErrorMessages: Dispatch>, + form: FormInstance ) { - const globalErrorMessages = new Array() - if (graphQlErrors) { - // TODO Is this how we want to handle GraphQl errors? - globalErrorMessages.push( - ...graphQlErrors.map(e => e.message) - ) + const globalErrorMessages = new Array(); + if (graphQlErrors) { + // TODO Is this how we want to handle GraphQl errors? + globalErrorMessages.push(...graphQlErrors.map((e) => e.message)); + } + if (userErrors) { + const errorPathToMessage = userErrors.reduce((a, x) => { + // We use strings as keys instead of path arrays because the + // latter are compared by reference. + const pathAsString = x.path.join("."); + if (!a.has(pathAsString)) { + a.set(pathAsString, [x.path, []]); + } + a.get(pathAsString)?.[1]?.push(x.message); + return a; + }, new Map()); + console.log(errorPathToMessage); + for (let [, [path, messages]] of errorPathToMessage) { + if (path.length === 1) { + globalErrorMessages.push(...messages); + } else { + form.setFields([ + { + name: path.slice(1), + errors: messages, + }, + ]); + } } - if (userErrors) { - const errorPathToMessage = - userErrors.reduce( - (a, x) => { - // We use strings as keys instead of path arrays because the - // latter are compared by reference. - const pathAsString = x.path.join('.'); - if (!a.has(pathAsString)) { - a.set(pathAsString, [x.path, []]) - } - a.get(pathAsString)?.[1]?.push(x.message) - return a - }, - new Map() - ) - console.log(errorPathToMessage) - for (let [, [path, messages]] of errorPathToMessage) { - if (path.length === 1) { - globalErrorMessages.push(...messages) - } - else { - form.setFields([ - { - name: path.slice(1), - errors: messages, - } - ]) - } - } - } - setGlobalErrorMessages(globalErrorMessages) + } + setGlobalErrorMessages(globalErrorMessages); } diff --git a/frontend/next.config.js b/frontend/next.config.js index c6d3173c..d2690548 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -3,21 +3,21 @@ module.exports = { config.module.rules.push({ test: /\.graphql$/, exclude: /node_modules/, - use: [options.defaultLoaders.babel, { loader: 'graphql-let/loader' }], - }) + use: [options.defaultLoaders.babel, { loader: "graphql-let/loader" }], + }); config.module.rules.push({ test: /\.graphqls$/, exclude: /node_modules/, - use: ['graphql-let/schema/loader'], - }) + use: ["graphql-let/schema/loader"], + }); config.module.rules.push({ test: /\.ya?ml$/, - type: 'json', - use: 'yaml-loader', - }) + type: "json", + use: "yaml-loader", + }); - return config + return config; }, -} +}; diff --git a/frontend/package.json b/frontend/package.json index 0c72a21d..0fdfd75b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -50,6 +50,7 @@ "graphql-tag": "^2.0", "jest": "^29.0", "license-checker": "^25.0.1", + "prettier": "^2.8.4", "react-test-renderer": "^18.0", "ts-node": "^10.9.1", "typescript": "^4.0", diff --git a/frontend/pages/api/auth/[...nextauth].ts b/frontend/pages/api/auth/[...nextauth].ts index 1f2d4e97..ceb3bb3c 100644 --- a/frontend/pages/api/auth/[...nextauth].ts +++ b/frontend/pages/api/auth/[...nextauth].ts @@ -147,8 +147,7 @@ const options = { name: "Metabase", type: "oauth", version: "2.0", - scope: - "openid email profile roles api:read api:write offline_access", + scope: "openid email profile roles api:read api:write offline_access", params: { grant_type: "authorization_code" }, authorizationParams: {}, @@ -267,7 +266,7 @@ const options = { */ async session( _session: { - user: { name?: string; email?: string; }; + user: { name?: string; email?: string }; accessToken?: string; expires: number; }, diff --git a/frontend/pages/data/calorimetric.tsx b/frontend/pages/data/calorimetric.tsx index d30e7bd4..837183de 100644 --- a/frontend/pages/data/calorimetric.tsx +++ b/frontend/pages/data/calorimetric.tsx @@ -208,9 +208,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error( - error.graphQLErrors.map((error) => error.message) - ); + messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `CalorimetricData` is wrong and error prone! setData((data?.allCalorimetricData?.nodes || []) as CalorimetricData[]); @@ -258,28 +256,28 @@ function Page() { loading={filtering} columns={[ { - ...getUuidColumnProps( + ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), (_uuid) => "/" // TODO Link somewhere useful! ), }, { - ...getNameColumnProps(onFilterTextChange, (x) => + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getDescriptionColumnProps( + ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getTimestampColumnProps(), + ...getTimestampColumnProps<(typeof data)[0]>(), }, { - ...getComponentUuidColumnProps( + ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), @@ -295,13 +293,13 @@ function Page() { // ), // }, { - ...getAppliedMethodColumnProps( + ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getResourceTreeColumnProps( + ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), diff --git a/frontend/pages/data/hygrothermal.tsx b/frontend/pages/data/hygrothermal.tsx index 1b06a8b2..807dd006 100644 --- a/frontend/pages/data/hygrothermal.tsx +++ b/frontend/pages/data/hygrothermal.tsx @@ -148,9 +148,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error( - error.graphQLErrors.map((error) => error.message) - ); + messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `HygrothermalData` is wrong and error prone! setData((data?.allHygrothermalData?.nodes || []) as HygrothermalData[]); @@ -196,28 +194,28 @@ function Page() { loading={filtering} columns={[ { - ...getUuidColumnProps( + ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), (_uuid) => "/" // TODO Link somewhere useful! ), }, { - ...getNameColumnProps(onFilterTextChange, (x) => + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getDescriptionColumnProps( + ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getTimestampColumnProps(), + ...getTimestampColumnProps<(typeof data)[0]>(), }, { - ...getComponentUuidColumnProps( + ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), @@ -233,13 +231,13 @@ function Page() { // ), // }, { - ...getAppliedMethodColumnProps( + ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getResourceTreeColumnProps( + ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), diff --git a/frontend/pages/data/index.tsx b/frontend/pages/data/index.tsx index f8411dd2..22a0fdb0 100644 --- a/frontend/pages/data/index.tsx +++ b/frontend/pages/data/index.tsx @@ -194,28 +194,28 @@ function Page() { loading={filtering} columns={[ { - ...getUuidColumnProps( + ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), (_uuid) => "/" // TODO Link somewhere useful! ), }, { - ...getNameColumnProps(onFilterTextChange, (x) => + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getDescriptionColumnProps( + ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getTimestampColumnProps(), + ...getTimestampColumnProps<(typeof data)[0]>(), }, { - ...getComponentUuidColumnProps( + ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), @@ -231,13 +231,13 @@ function Page() { // ), // }, { - ...getAppliedMethodColumnProps( + ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getResourceTreeColumnProps( + ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), diff --git a/frontend/pages/data/optical.tsx b/frontend/pages/data/optical.tsx index a1d71130..0215596d 100644 --- a/frontend/pages/data/optical.tsx +++ b/frontend/pages/data/optical.tsx @@ -293,9 +293,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error( - error.graphQLErrors.map((error) => error.message) - ); + messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `OpticalData` is wrong and error prone! setData((data?.allOpticalData?.nodes || []) as OpticalData[]); @@ -361,28 +359,28 @@ function Page() { loading={filtering} columns={[ { - ...getUuidColumnProps( + ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), (_uuid) => "/" // TODO Link somewhere useful! ), }, { - ...getNameColumnProps(onFilterTextChange, (x) => + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getDescriptionColumnProps( + ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getTimestampColumnProps(), + ...getTimestampColumnProps<(typeof data)[0]>(), }, { - ...getComponentUuidColumnProps( + ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), @@ -398,13 +396,13 @@ function Page() { // ), // }, { - ...getAppliedMethodColumnProps( + ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getResourceTreeColumnProps( + ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), diff --git a/frontend/pages/data/photovoltaic.tsx b/frontend/pages/data/photovoltaic.tsx index 9148f392..25717211 100644 --- a/frontend/pages/data/photovoltaic.tsx +++ b/frontend/pages/data/photovoltaic.tsx @@ -148,9 +148,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error( - error.graphQLErrors.map((error) => error.message) - ); + messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! setData((data?.allPhotovoltaicData?.nodes || []) as PhotovoltaicData[]); @@ -196,28 +194,28 @@ function Page() { loading={filtering} columns={[ { - ...getUuidColumnProps( + ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), (_uuid) => "/" // TODO Link somewhere useful! ), }, { - ...getNameColumnProps(onFilterTextChange, (x) => + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getDescriptionColumnProps( + ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getTimestampColumnProps(), + ...getTimestampColumnProps<(typeof data)[0]>(), }, { - ...getComponentUuidColumnProps( + ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), @@ -233,13 +231,13 @@ function Page() { // ), // }, { - ...getAppliedMethodColumnProps( + ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), }, { - ...getResourceTreeColumnProps( + ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x) ), diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 850730f4..f24a3413 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -8,8 +8,25 @@ function Page() {
- solarbuildingenvelopes.com is an example of a database in the network Building Envelope Data. It provides access to some data of the TestLab Solar Façades which is an accredited laboratory at Fraunhofer Institute for Solar Energy Systems (ISE). The database is a live instance of the reference implementation. - + solarbuildingenvelopes.com is an + example of a database in the network{" "} + + Building Envelope Data + + . It provides access to some data of the{" "} + + TestLab Solar Façades + {" "} + which is an accredited laboratory at{" "} + + Fraunhofer Institute for Solar Energy Systems (ISE) + + . The database is a live instance of the{" "} + + reference implementation + + . +
); diff --git a/frontend/public/site.webmanifest b/frontend/public/site.webmanifest index a82c958f..52a10c61 100644 --- a/frontend/public/site.webmanifest +++ b/frontend/public/site.webmanifest @@ -1 +1,19 @@ -{"name":"Database","short_name":"D","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{ + "name": "Database", + "short_name": "D", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/frontend/test/index.test.tsx b/frontend/test/index.test.tsx index 6bb35f7a..f200c774 100644 --- a/frontend/test/index.test.tsx +++ b/frontend/test/index.test.tsx @@ -1,10 +1,10 @@ -import { InMemoryCache, gql } from '@apollo/client' -import React from 'react' -import Index from '../pages' -import renderer from 'react-test-renderer' -import { MockedProvider } from '@apollo/client/testing' +import { InMemoryCache, gql } from "@apollo/client"; +import React from "react"; +import Index from "../pages"; +import renderer from "react-test-renderer"; +import { MockedProvider } from "@apollo/client/testing"; -const cache = new InMemoryCache() +const cache = new InMemoryCache(); cache.writeQuery({ query: gql` query Viewer { @@ -17,21 +17,21 @@ cache.writeQuery({ `, data: { viewer: { - __typename: 'User', - id: 'Baa', - name: 'Baa', - status: 'Healthy', + __typename: "User", + id: "Baa", + name: "Baa", + status: "Healthy", }, }, -}) +}); -describe('Index', () => { - it('renders the html we want', async () => { +describe("Index", () => { + it("renders the html we want", async () => { const component = renderer.create( - ) - expect(component.toJSON()).toMatchSnapshot() - }) -}) + ); + expect(component.toJSON()).toMatchSnapshot(); + }); +}); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 33a86b8e..2128691b 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -6,10 +6,7 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "jsx": "preserve", - "lib": [ - "dom", - "es2017" - ], + "lib": ["dom", "es2017"], "module": "esnext", "moduleResolution": "node", "noEmit": true, @@ -22,11 +19,6 @@ "target": "esnext", "incremental": true }, - "exclude": [ - "node_modules" - ], - "include": [ - "**/*.ts", - "**/*.tsx" - ] + "exclude": ["node_modules"], + "include": ["**/*.ts", "**/*.tsx"] } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index abd605fd..e88ea089 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -263,11 +263,19 @@ type HygrothermalDataEdge { } type Mutation { - createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! - createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! - createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! + createCalorimetricData( + input: CreateCalorimetricDataInput! + ): CreateCalorimetricDataPayload! + createGetHttpsResource( + input: CreateGetHttpsResourceInput! + ): CreateGetHttpsResourcePayload! + createHygrothermalData( + input: CreateHygrothermalDataInput! + ): CreateHygrothermalDataPayload! createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! - createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! + createPhotovoltaicData( + input: CreatePhotovoltaicDataInput! + ): CreatePhotovoltaicDataPayload! } type NamedMethodArgument { @@ -377,22 +385,110 @@ type PhotovoltaicDataEdge { } type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] timestamp: DateTime where: CalorimetricDataPropositionInput): CalorimetricDataConnection - allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] timestamp: DateTime where: DataPropositionInput): DataConnection - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] timestamp: DateTime where: HygrothermalDataPropositionInput): HygrothermalDataConnection - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] timestamp: DateTime where: OpticalDataPropositionInput): OpticalDataConnection - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection - calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData - data(id: Uuid! locale: Locale timestamp: DateTime): Data + allCalorimetricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [CalorimetricDataSortInput!] + timestamp: DateTime + where: CalorimetricDataPropositionInput + ): CalorimetricDataConnection + allData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [IDataSortInput!] + timestamp: DateTime + where: DataPropositionInput + ): DataConnection + allHygrothermalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [HygrothermalDataSortInput!] + timestamp: DateTime + where: HygrothermalDataPropositionInput + ): HygrothermalDataConnection + allOpticalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [OpticalDataSortInput!] + timestamp: DateTime + where: OpticalDataPropositionInput + ): OpticalDataConnection + allPhotovoltaicData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [PhotovoltaicDataSortInput!] + timestamp: DateTime + where: PhotovoltaicDataPropositionInput + ): PhotovoltaicDataConnection + calorimetricData( + id: Uuid! + locale: Locale + timestamp: DateTime + ): CalorimetricData + data(id: Uuid!, locale: Locale, timestamp: DateTime): Data getHttpsResource(id: Uuid!): GetHttpsResource - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput): GetHttpsResourceConnection - hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData + getHttpsResources( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): GetHttpsResourceConnection + hygrothermalData( + id: Uuid! + locale: Locale + timestamp: DateTime + ): HygrothermalData "Fetches an object given its ID." node("ID of the object." id: ID!): Node "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! - opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData - photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData + opticalData(id: Uuid!, locale: Locale, timestamp: DateTime): OpticalData + photovoltaicData( + id: Uuid! + locale: Locale + timestamp: DateTime + ): PhotovoltaicData } type ToTreeVertexAppliedConversionMethod { @@ -962,25 +1058,47 @@ enum SortEnumType { DESC } -directive @authorize("Defines when when the resolver shall be executed.By default the resolver is executed after the policy has determined that the current user is allowed to access the field." apply: ApplyPolicy! = BEFORE_RESOLVER "The name of the authorization policy that determines access to the annotated resource." policy: String "Roles that are allowed to access the annotated resource." roles: [String!]) repeatable on SCHEMA | OBJECT | FIELD_DEFINITION +directive @authorize( + "Defines when when the resolver shall be executed.By default the resolver is executed after the policy has determined that the current user is allowed to access the field." + apply: ApplyPolicy! = BEFORE_RESOLVER + "The name of the authorization policy that determines access to the annotated resource." + policy: String + "Roles that are allowed to access the annotated resource." + roles: [String!] +) repeatable on SCHEMA | OBJECT | FIELD_DEFINITION "The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." -directive @defer("Deferred when true." if: Boolean "If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @defer( + "Deferred when true." + if: Boolean + "If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." + label: String +) on FRAGMENT_SPREAD | INLINE_FRAGMENT "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR +directive @specifiedBy( + "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." + url: String! +) on SCALAR "The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." -directive @stream("Streamed when true." if: Boolean "The initial elements that shall be send down to the consumer." initialCount: Int! = 0 "If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." label: String) on FIELD +directive @stream( + "Streamed when true." + if: Boolean + "The initial elements that shall be send down to the consumer." + initialCount: Int! = 0 + "If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." + label: String +) on FIELD scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") +scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") "BCP 47 compliant Language Tag string" scalar Locale scalar Url -scalar Uuid \ No newline at end of file +scalar Uuid diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 0b53e0d0..980fecf7 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5823,6 +5823,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prettier@^2.8.4: + version "2.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" + integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== + pretty-format@^29.4.2: version "29.4.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" From fce59dd4ad7ae707c14598ced94bad2715133775 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Feb 2023 11:04:14 +0100 Subject: [PATCH 058/878] Fix headers values (property value must not be null) --- frontend/lib/apollo.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 06191ee8..087519fa 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -34,10 +34,12 @@ function createIsomorphLink(context: ResolverContext = {}) { : `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/`, useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", - headers: { - accept: "application/json", - cookie: context.req ? context.req.headers.cookie : null, - }, + headers: context.req?.headers?.cookie + ? { + accept: "application/json", + cookie: context.req.headers.cookie, + } + : { accept: "application/json" }, }); } From ac845da11336be2a55b0b5d937b85b20264c027c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Feb 2023 11:08:27 +0100 Subject: [PATCH 059/878] Upgrade frontend packages to latest patch versions --- frontend/yarn.lock | 965 ++++++++++++++++++++++----------------------- 1 file changed, 482 insertions(+), 483 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 980fecf7..75cf1f19 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -58,9 +58,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^3.7.0": - version "3.7.7" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.7.tgz#5eb6e8af24fb809c97c8f66c3faf9524f83c412b" - integrity sha512-Rp/pCWuJSjLN7Xl5Qi2NoeURmZYEU/TIUz0n/LOwEo1tGdU2W7/fGVZ8+5um58JeVYq4UoTGBKFxSVeG4s411A== + version "3.7.8" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.8.tgz#e1c8dfd02cbbe1baf9b18fa86918904efd9cc580" + integrity sha512-o1NxF4ytET2w9HSVMLwYUEEdv6H3XPpbh9M+ABVGnUVT0s6T9pgqRtYO4pFP1TmeDmb1pbRfVhFwh3gC167j5Q== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" @@ -709,9 +709,9 @@ "@jridgewell/trace-mapping" "0.3.9" "@ctrl/tinycolor@^3.4.0": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.5.0.tgz#6e52b3d1c38d13130101771821e09cdd414a16bc" - integrity sha512-tlJpwF40DEQcfR/QF+wNMVyGMaO9FQp6Z1Wahj4Gk3CJQYHwA2xVG7iKDFdW6zuxZY9XWOpGcfNCTsX4McOsOg== + version "3.6.0" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz#53fa5fe9c34faee89469e48f91d51a3766108bc8" + integrity sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ== "@emotion/hash@^0.8.0": version "0.8.0" @@ -996,44 +996,44 @@ "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@8.5.17": - version "8.5.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.17.tgz#1d3294becfd2c3c6bddfe56ff3507ce89ca236a2" - integrity sha512-ma6zlFIBG8VuqSwE8jhYhMbaFsJ1YdVsnpFmbQ0O/qJTmlgdAWCyAZTJH0aZ24fqNFfj/vW/Qtpqn7gRcF8QOw== +"@graphql-tools/batch-execute@8.5.18": + version "8.5.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.18.tgz#2f0e91cc12e8eed32f14bc814f27c6a498b75e17" + integrity sha512-mNv5bpZMLLwhkmPA6+RP81A6u3KF4CSKLf3VX9hbomOkQR4db8pNs8BOvpZU54wKsUzMzdlws/2g/Dabyb2Vsg== dependencies: "@graphql-tools/utils" "9.2.1" - dataloader "2.2.1" + dataloader "2.2.2" tslib "^2.4.0" value-or-promise "1.0.12" "@graphql-tools/code-file-loader@^7.3.17": - version "7.3.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.20.tgz#6f523bcb5b5bcdbcede4b5894cbcb1890132b002" - integrity sha512-htwylU+/if5j5rgrd/i2xgM22cWC2RGgUGO7K+nxZU+l7iCimJUdDQnqCW9G3eVHbLpVOhyza9bBUNMPzh3sxg== + version "7.3.21" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.21.tgz#3eed4ff4610cf0a6f4b1be17d0bce1eec9359479" + integrity sha512-dj+OLnz1b8SYkXcuiy0CUQ25DWnOEyandDlOcdBqU3WVwh5EEVbn0oXUYm90fDlq2/uut00OrtC5Wpyhi3tAvA== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.4.6" + "@graphql-tools/graphql-tag-pluck" "7.5.0" "@graphql-tools/utils" "9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@9.0.26", "@graphql-tools/delegate@^9.0.26": - version "9.0.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.26.tgz#b931607bf11db26da307d12cc2a97eec82a677f1" - integrity sha512-RPcjH+NnK3e4e9/6CwKbyv9DtVa+ojiwvsbW9Q6zMXRdlP0zazsQOe5+ktL3yE+d3zlzGAasp0WAiSLUS5vFRw== +"@graphql-tools/delegate@9.0.27", "@graphql-tools/delegate@^9.0.27": + version "9.0.27" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.27.tgz#e500554bace46cc7ededd48a0c28079f747c9f49" + integrity sha512-goYewiPls/RDXiRTl1S2tRPlsyDQCxlDWqd0uEIzQZ6aWSyiutfwQnTzdbZPXK0qOblEVMIqFhSGrB6fp0OkBA== dependencies: - "@graphql-tools/batch-execute" "8.5.17" + "@graphql-tools/batch-execute" "8.5.18" "@graphql-tools/executor" "0.0.14" "@graphql-tools/schema" "9.0.16" "@graphql-tools/utils" "9.2.1" - dataloader "2.2.1" + dataloader "2.2.2" tslib "~2.5.0" value-or-promise "1.0.12" -"@graphql-tools/executor-graphql-ws@^0.0.10": - version "0.0.10" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.10.tgz#c4775ac3730a0017392373f84aa21bfc4de4495b" - integrity sha512-5SxFvupyWe6+Egq8Zws0+mJZMKV18rTAwxHwhrx+KhRfGpilqkqS4I+qwVL94LNktWL2uy95cU5b5CQFyVaVEg== +"@graphql-tools/executor-graphql-ws@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.11.tgz#c6536aa862f76a9c7ac83e7e07fe8d5119e6de38" + integrity sha512-muRj6j897ks2iKqe3HchWFFzd+jFInSRuLPvHJ7e4WPrejFvaZx3BQ9gndfJvVkfYUZIFm13stCGXaJJTbVM0Q== dependencies: "@graphql-tools/utils" "9.2.1" "@repeaterjs/repeater" "3.0.4" @@ -1041,12 +1041,12 @@ graphql-ws "5.11.3" isomorphic-ws "5.0.0" tslib "^2.4.0" - ws "8.12.0" + ws "8.12.1" "@graphql-tools/executor-http@^0.1.7": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.8.tgz#129fc3095370ecd39cad68103d97ad8aa767e334" - integrity sha512-L3tldMlL760Bkdl6zdearvxqCA8cRmTWvd8SyZ0i/EpBWFkU1XcUF2/wqNt7URdNAE6+pO6wCgI4BEsKOgL7Qg== + version "0.1.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.9.tgz#ddd74ef376b4a2ed59c622acbcca068890854a30" + integrity sha512-tNzMt5qc1ptlHKfpSv9wVBVKCZ7gks6Yb/JcYJluxZIT4qRV+TtOFjpptfBU63usgrGVOVcGjzWc/mt7KhmmpQ== dependencies: "@graphql-tools/utils" "^9.2.1" "@repeaterjs/repeater" "^3.0.4" @@ -1057,16 +1057,16 @@ tslib "^2.4.0" value-or-promise "^1.0.12" -"@graphql-tools/executor-legacy-ws@^0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.8.tgz#3387f50eafacfb05ac578baf46c0ea0fe059494e" - integrity sha512-NZfBijmr774rCO60cRTqbf2otRjn32sVikq6PdT+0vZfhVwX7wydNMdyfJZQ3WGuTyab5hrqOWD+UU8VcIbAeg== +"@graphql-tools/executor-legacy-ws@^0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.9.tgz#1ff517998f750af2be9c1dae8924665a136e4986" + integrity sha512-L7oDv7R5yoXzMH+KLKDB2WHVijfVW4dB2H+Ae1RdW3MFvwbYjhnIB6QzHqKEqksjp/FndtxZkbuTIuAOsYGTYw== dependencies: "@graphql-tools/utils" "9.2.1" "@types/ws" "^8.0.0" isomorphic-ws "5.0.0" tslib "^2.4.0" - ws "8.12.0" + ws "8.12.1" "@graphql-tools/executor@0.0.14": version "0.0.14" @@ -1080,11 +1080,11 @@ value-or-promise "1.0.12" "@graphql-tools/git-loader@^7.2.13": - version "7.2.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.19.tgz#42b12ef34b9e848d885a0ba22d987d798755aacc" - integrity sha512-F94PqVdBXbOETg7x081rJec+2egi/4TgXQWlvHdQ8jjrNd+C/EU+Dxq0ccmfnhUKdcVyKJpMpLUIUyY7uwX6gw== + version "7.2.20" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.20.tgz#b17917c89be961c272bfbf205dcf32287247494b" + integrity sha512-D/3uwTzlXxG50HI8BEixqirT4xiUp6AesTdfotRXAs2d4CT9wC6yuIWOHkSBqgI1cwKWZb6KXZr467YPS5ob1w== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.4.6" + "@graphql-tools/graphql-tag-pluck" "7.5.0" "@graphql-tools/utils" "9.2.1" is-glob "4.0.3" micromatch "^4.0.4" @@ -1113,10 +1113,10 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@7.4.6", "@graphql-tools/graphql-tag-pluck@^7.4.6": - version "7.4.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.4.6.tgz#57fe6f37d0c7c5f8bf3dfbec58f70ec693e70b0a" - integrity sha512-KPlkrC+WtJAg/Sv93rPiDHZDsgQDIZEy9ViHqz80KdRvq0aeQN9TGp26mQCyD7zo1Ib2paT16IVwTNQf02yxpQ== +"@graphql-tools/graphql-tag-pluck@7.5.0", "@graphql-tools/graphql-tag-pluck@^7.4.6": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.0.tgz#be99bc6b5e8331a2379ab4585d71b057eb981497" + integrity sha512-76SYzhSlH50ZWkhWH6OI94qrxa8Ww1ZeOU04MdtpSeQZVT2rjGWeTb3xM3kjTVWQJsr/YJBhDeNPGlwNUWfX4Q== dependencies: "@babel/parser" "^7.16.8" "@babel/plugin-syntax-import-assertions" "7.20.0" @@ -1187,11 +1187,11 @@ tslib "^2.4.0" "@graphql-tools/prisma-loader@^7.2.49": - version "7.2.63" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.63.tgz#7793997f57fc3c773dfe4b2343ffbf22a322d127" - integrity sha512-k4SLQDA2Kfag3axw3YINbjJWixzv9l32neW5sBAVU8YeDjumWASSTEKjnGhLaniOJQjqyvKc8Jt5Jo0ptjvRxA== + version "7.2.64" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.64.tgz#e9fc85054b15a22a16c8e69ad4f9543da30c0164" + integrity sha512-W8GfzfBKiBSIEgw+/nJk6zUlF6k/jterlNoFhM27mBsbeMtWxKnm1+gEU6KA0N1PNEdq2RIa2W4AfVfVBl2GgQ== dependencies: - "@graphql-tools/url-loader" "7.17.12" + "@graphql-tools/url-loader" "7.17.13" "@graphql-tools/utils" "9.2.1" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" @@ -1230,18 +1230,18 @@ tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-tools/url-loader@7.17.12", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.12.tgz#e3d2a34fcbae6edf5bb70287af25a5133531171f" - integrity sha512-XIGWnXLoPjZzYTkeM24oUFs//buROz8Q1bgiqNf604JjmHJ6UzpOHOVskMN2lvfufqghhGDiK9v/oVhlVb8B0Q== +"@graphql-tools/url-loader@7.17.13", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": + version "7.17.13" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.13.tgz#d4ee8193792ab1c42db2fbdf5f6ca75fa819ac40" + integrity sha512-FEmbvw68kxeZLn4VYGAl+NuBPk09ZnxymjW07A6mCtiDayFgYfHdWeRzXn/iM5PzsEuCD73R1sExtNQ/ISiajg== dependencies: "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/delegate" "^9.0.26" - "@graphql-tools/executor-graphql-ws" "^0.0.10" + "@graphql-tools/delegate" "^9.0.27" + "@graphql-tools/executor-graphql-ws" "^0.0.11" "@graphql-tools/executor-http" "^0.1.7" - "@graphql-tools/executor-legacy-ws" "^0.0.8" + "@graphql-tools/executor-legacy-ws" "^0.0.9" "@graphql-tools/utils" "^9.2.1" - "@graphql-tools/wrap" "^9.3.5" + "@graphql-tools/wrap" "^9.3.6" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.8.0" isomorphic-ws "^5.0.0" @@ -1273,12 +1273,12 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/wrap@^9.3.5": - version "9.3.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.5.tgz#6c48c0189fecaf5199052b98fd99d7b1c48e8b68" - integrity sha512-D3jR6/ZDWa6bw4hc1odHKLIFLxjgXlL8FSkkNlViAcRgRaqUVgFQsk+dThdWkqKP6+uij4lBG+pd/XZfrI1zeQ== +"@graphql-tools/wrap@^9.3.6": + version "9.3.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.6.tgz#23beaf9c3713160adda511c6a498d1c7077c2848" + integrity sha512-HtQIYoPz48bzpMYZzoeMmzIIYuVxcaUuLD7dH7GtIhwe2f4hpPDE+JLUPxpYiaXdY10l7kP9wycK+FtRfCsFlw== dependencies: - "@graphql-tools/delegate" "9.0.26" + "@graphql-tools/delegate" "9.0.27" "@graphql-tools/schema" "9.0.16" "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" @@ -1324,109 +1324,109 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.2.tgz#f78374905c2454764152904a344a2d5226b0ef09" - integrity sha512-0I/rEJwMpV9iwi9cDEnT71a5nNGK9lj8Z4+1pRAU2x/thVXCDnaTGrvxyK+cAqZTFVFCiR+hfVrP4l2m+dCmQg== +"@jest/console@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.3.tgz#1f25a99f7f860e4c46423b5b1038262466fadde1" + integrity sha512-W/o/34+wQuXlgqlPYTansOSiBnuxrTv61dEVkA6HNmpcgHLUjfaUbdqt6oVvOzaawwo9IdW9QOtMgQ1ScSZC4A== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.4.2" - jest-util "^29.4.2" + jest-message-util "^29.4.3" + jest-util "^29.4.3" slash "^3.0.0" -"@jest/core@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.2.tgz#6e999b67bdc2df9d96ba9b142465bda71ee472c2" - integrity sha512-KGuoQah0P3vGNlaS/l9/wQENZGNKGoWb+OPxh3gz+YzG7/XExvYu34MzikRndQCdM2S0tzExN4+FL37i6gZmCQ== +"@jest/core@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.3.tgz#829dd65bffdb490de5b0f69e97de8e3b5eadd94b" + integrity sha512-56QvBq60fS4SPZCuM7T+7scNrkGIe7Mr6PVIXUpu48ouvRaWOFqRPV91eifvFM0ay2HmfswXiGf97NGUN5KofQ== dependencies: - "@jest/console" "^29.4.2" - "@jest/reporters" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/console" "^29.4.3" + "@jest/reporters" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/transform" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.4.2" - jest-config "^29.4.2" - jest-haste-map "^29.4.2" - jest-message-util "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-resolve-dependencies "^29.4.2" - jest-runner "^29.4.2" - jest-runtime "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" - jest-watcher "^29.4.2" + jest-changed-files "^29.4.3" + jest-config "^29.4.3" + jest-haste-map "^29.4.3" + jest-message-util "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.4.3" + jest-resolve-dependencies "^29.4.3" + jest-runner "^29.4.3" + jest-runtime "^29.4.3" + jest-snapshot "^29.4.3" + jest-util "^29.4.3" + jest-validate "^29.4.3" + jest-watcher "^29.4.3" micromatch "^4.0.4" - pretty-format "^29.4.2" + pretty-format "^29.4.3" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.2.tgz#ee92c316ee2fbdf0bcd9d2db0ef42d64fea26b56" - integrity sha512-JKs3VUtse0vQfCaFGJRX1bir9yBdtasxziSyu+pIiEllAQOe4oQhdCYIf3+Lx+nGglFktSKToBnRJfD5QKp+NQ== +"@jest/environment@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.3.tgz#9fe2f3169c3b33815dc4bd3960a064a83eba6548" + integrity sha512-dq5S6408IxIa+lr54zeqce+QgI+CJT4nmmA+1yzFgtcsGK8c/EyiUb9XQOgz3BMKrRDfKseeOaxj2eO8LlD3lA== dependencies: - "@jest/fake-timers" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/fake-timers" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" - jest-mock "^29.4.2" + jest-mock "^29.4.3" -"@jest/expect-utils@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.2.tgz#cd0065dfdd8e8a182aa350cc121db97b5eed7b3f" - integrity sha512-Dd3ilDJpBnqa0GiPN7QrudVs0cczMMHtehSo2CSTjm3zdHx0RcpmhFNVEltuEFeqfLIyWKFI224FsMSQ/nsJQA== +"@jest/expect-utils@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.3.tgz#95ce4df62952f071bcd618225ac7c47eaa81431e" + integrity sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ== dependencies: - jest-get-type "^29.4.2" + jest-get-type "^29.4.3" -"@jest/expect@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.2.tgz#2d4a6a41b29380957c5094de19259f87f194578b" - integrity sha512-NUAeZVApzyaeLjfWIV/64zXjA2SS+NuUPHpAlO7IwVMGd5Vf9szTl9KEDlxY3B4liwLO31os88tYNHl6cpjtKQ== +"@jest/expect@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.3.tgz#d31a28492e45a6bcd0f204a81f783fe717045c6e" + integrity sha512-iktRU/YsxEtumI9zsPctYUk7ptpC+AVLLk1Ax3AsA4g1C+8OOnKDkIQBDHtD5hA/+VtgMd5AWI5gNlcAlt2vxQ== dependencies: - expect "^29.4.2" - jest-snapshot "^29.4.2" + expect "^29.4.3" + jest-snapshot "^29.4.3" -"@jest/fake-timers@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.2.tgz#af43ee1a5720b987d0348f80df98f2cb17d45cd0" - integrity sha512-Ny1u0Wg6kCsHFWq7A/rW/tMhIedq2siiyHyLpHCmIhP7WmcAmd2cx95P+0xtTZlj5ZbJxIRQi4OPydZZUoiSQQ== +"@jest/fake-timers@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.3.tgz#31e982638c60fa657d310d4b9d24e023064027b0" + integrity sha512-4Hote2MGcCTWSD2gwl0dwbCpBRHhE6olYEuTj8FMowdg3oQWNKr2YuxenPQYZ7+PfqPY1k98wKDU4Z+Hvd4Tiw== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.4.2" - jest-mock "^29.4.2" - jest-util "^29.4.2" + jest-message-util "^29.4.3" + jest-mock "^29.4.3" + jest-util "^29.4.3" -"@jest/globals@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.2.tgz#73f85f5db0e17642258b25fd0b9fc89ddedb50eb" - integrity sha512-zCk70YGPzKnz/I9BNFDPlK+EuJLk21ur/NozVh6JVM86/YYZtZHqxFFQ62O9MWq7uf3vIZnvNA0BzzrtxD9iyg== +"@jest/globals@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.3.tgz#63a2c4200d11bc6d46f12bbe25b07f771fce9279" + integrity sha512-8BQ/5EzfOLG7AaMcDh7yFCbfRLtsc+09E1RQmRBI4D6QQk4m6NSK/MXo+3bJrBN0yU8A2/VIcqhvsOLFmziioA== dependencies: - "@jest/environment" "^29.4.2" - "@jest/expect" "^29.4.2" - "@jest/types" "^29.4.2" - jest-mock "^29.4.2" + "@jest/environment" "^29.4.3" + "@jest/expect" "^29.4.3" + "@jest/types" "^29.4.3" + jest-mock "^29.4.3" -"@jest/reporters@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.2.tgz#6abfa923941daae0acc76a18830ee9e79a22042d" - integrity sha512-10yw6YQe75zCgYcXgEND9kw3UZZH5tJeLzWv4vTk/2mrS1aY50A37F+XT2hPO5OqQFFnUWizXD8k1BMiATNfUw== +"@jest/reporters@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.3.tgz#0a68a0c0f20554760cc2e5443177a0018969e353" + integrity sha512-sr2I7BmOjJhyqj9ANC6CTLsL4emMoka7HkQpcoMRlhCbQJjz2zsRzw0BDPiPyEFDXAbxKgGFYuQZiSJ1Y6YoTg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/console" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/transform" "^29.4.3" + "@jest/types" "^29.4.3" "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" @@ -1439,48 +1439,48 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.4.2" - jest-util "^29.4.2" - jest-worker "^29.4.2" + jest-message-util "^29.4.3" + jest-util "^29.4.3" + jest-worker "^29.4.3" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.2.tgz#cf7cfe97c5649f518452b176c47ed07486270fc1" - integrity sha512-ZrGzGfh31NtdVH8tn0mgJw4khQuNHiKqdzJAFbCaERbyCP9tHlxWuL/mnMu8P7e/+k4puWjI1NOzi/sFsjce/g== +"@jest/schemas@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" + integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== dependencies: "@sinclair/typebox" "^0.25.16" -"@jest/source-map@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.2.tgz#f9815d59e25cd3d6828e41489cd239271018d153" - integrity sha512-tIoqV5ZNgYI9XCKXMqbYe5JbumcvyTgNN+V5QW4My033lanijvCD0D4PI9tBw4pRTqWOc00/7X3KVvUh+qnF4Q== +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== dependencies: "@jridgewell/trace-mapping" "^0.3.15" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.2.tgz#34b0ba069f2e3072261e4884c8fb6bd15ed6fb8d" - integrity sha512-HZsC3shhiHVvMtP+i55MGR5bPcc3obCFbA5bzIOb8pCjwBZf11cZliJncCgaVUbC5yoQNuGqCkC0Q3t6EItxZA== +"@jest/test-result@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.3.tgz#e13d973d16c8c7cc0c597082d5f3b9e7f796ccb8" + integrity sha512-Oi4u9NfBolMq9MASPwuWTlC5WvmNRwI4S8YrQg5R5Gi47DYlBe3sh7ILTqi/LGrK1XUE4XY9KZcQJTH1WJCLLA== dependencies: - "@jest/console" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/console" "^29.4.3" + "@jest/types" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.2.tgz#8b48e5bc4af80b42edacaf2a733d4f295edf28fb" - integrity sha512-9Z2cVsD6CcObIVrWigHp2McRJhvCxL27xHtrZFgNC1RwnoSpDx6fZo8QYjJmziFlW9/hr78/3sxF54S8B6v8rg== +"@jest/test-sequencer@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.3.tgz#0862e876a22993385a0f3e7ea1cc126f208a2898" + integrity sha512-yi/t2nES4GB4G0mjLc0RInCq/cNr9dNwJxcGg8sslajua5Kb4kmozAc+qPLzplhBgfw1vLItbjyHzUN92UXicw== dependencies: - "@jest/test-result" "^29.4.2" + "@jest/test-result" "^29.4.3" graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" + jest-haste-map "^29.4.3" slash "^3.0.0" "@jest/transform@^27.1.0": @@ -1504,22 +1504,22 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.2.tgz#b24b72dbab4c8675433a80e222d6a8ef4656fb81" - integrity sha512-kf1v5iTJHn7p9RbOsBuc/lcwyPtJaZJt5885C98omWz79NIeD3PfoiiaPSu7JyCyFzNOIzKhmMhQLUhlTL9BvQ== +"@jest/transform@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.3.tgz#f7d17eac9cb5bb2e1222ea199c7c7e0835e0c037" + integrity sha512-8u0+fBGWolDshsFgPQJESkDa72da/EVwvL+II0trN2DR66wMwiQ9/CihaGfHdlLGFzbBZwMykFtxuwFdZqlKwg== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - jest-regex-util "^29.4.2" - jest-util "^29.4.2" + jest-haste-map "^29.4.3" + jest-regex-util "^29.4.3" + jest-util "^29.4.3" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -1536,12 +1536,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.4.2": - version "29.4.2" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.2.tgz#8f724a414b1246b2bfd56ca5225d9e1f39540d82" - integrity sha512-CKlngyGP0fwlgC1BRUtPZSiWLBhyS9dKwKmyGxk8Z6M82LBEGB2aLQSg+U1MyLsU+M7UjnlLllBM2BLWKVm/Uw== +"@jest/types@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.3.tgz#9069145f4ef09adf10cec1b2901b2d390031431f" + integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA== dependencies: - "@jest/schemas" "^29.4.2" + "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1793,9 +1793,9 @@ integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== "@sinclair/typebox@^0.25.16": - version "0.25.21" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272" - integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g== + version "0.25.22" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.22.tgz#2808d895e9c2722b20a622a9c8cb332f6720eb4a" + integrity sha512-6U6r2L7rnM7EG8G1tWzIjdB3QlsHF4slgcqXNN/SF0xJOAr0nDmT2GedlkyO3mrv8mDTJ24UuOMWR3diBrCvQQ== "@sinonjs/commons@^2.0.0": version "2.0.0" @@ -1970,9 +1970,9 @@ "@types/react" "*" "@types/react-dom@^18.0": - version "18.0.10" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.10.tgz#3b66dec56aa0f16a6cc26da9e9ca96c35c0b4352" - integrity sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg== + version "18.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" + integrity sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw== dependencies: "@types/react" "*" @@ -2036,47 +2036,47 @@ "@types/yargs-parser" "*" "@typescript-eslint/parser@^5.42.0": - version "5.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.51.0.tgz#2d74626652096d966ef107f44b9479f02f51f271" - integrity sha512-fEV0R9gGmfpDeRzJXn+fGQKcl0inIeYobmmUWijZh9zA7bxJ8clPhV9up2ZQzATxAiFAECqPQyMDB4o4B81AaA== + version "5.52.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.52.0.tgz#73c136df6c0133f1d7870de7131ccf356f5be5a4" + integrity sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA== dependencies: - "@typescript-eslint/scope-manager" "5.51.0" - "@typescript-eslint/types" "5.51.0" - "@typescript-eslint/typescript-estree" "5.51.0" + "@typescript-eslint/scope-manager" "5.52.0" + "@typescript-eslint/types" "5.52.0" + "@typescript-eslint/typescript-estree" "5.52.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.51.0": - version "5.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.51.0.tgz#ad3e3c2ecf762d9a4196c0fbfe19b142ac498990" - integrity sha512-gNpxRdlx5qw3yaHA0SFuTjW4rxeYhpHxt491PEcKF8Z6zpq0kMhe0Tolxt0qjlojS+/wArSDlj/LtE69xUJphQ== +"@typescript-eslint/scope-manager@5.52.0": + version "5.52.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz#a993d89a0556ea16811db48eabd7c5b72dcb83d1" + integrity sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw== dependencies: - "@typescript-eslint/types" "5.51.0" - "@typescript-eslint/visitor-keys" "5.51.0" + "@typescript-eslint/types" "5.52.0" + "@typescript-eslint/visitor-keys" "5.52.0" -"@typescript-eslint/types@5.51.0": - version "5.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.51.0.tgz#e7c1622f46c7eea7e12bbf1edfb496d4dec37c90" - integrity sha512-SqOn0ANn/v6hFn0kjvLwiDi4AzR++CBZz0NV5AnusT2/3y32jdc0G4woXPWHCumWtUXZKPAS27/9vziSsC9jnw== +"@typescript-eslint/types@5.52.0": + version "5.52.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.52.0.tgz#19e9abc6afb5bd37a1a9bea877a1a836c0b3241b" + integrity sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ== -"@typescript-eslint/typescript-estree@5.51.0": - version "5.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.51.0.tgz#0ec8170d7247a892c2b21845b06c11eb0718f8de" - integrity sha512-TSkNupHvNRkoH9FMA3w7TazVFcBPveAAmb7Sz+kArY6sLT86PA5Vx80cKlYmd8m3Ha2SwofM1KwraF24lM9FvA== +"@typescript-eslint/typescript-estree@5.52.0": + version "5.52.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz#6408cb3c2ccc01c03c278cb201cf07e73347dfca" + integrity sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ== dependencies: - "@typescript-eslint/types" "5.51.0" - "@typescript-eslint/visitor-keys" "5.51.0" + "@typescript-eslint/types" "5.52.0" + "@typescript-eslint/visitor-keys" "5.52.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.51.0": - version "5.51.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.51.0.tgz#c0147dd9a36c0de758aaebd5b48cae1ec59eba87" - integrity sha512-Oh2+eTdjHjOFjKA27sxESlA87YPSOJafGCR0md5oeMdh1ZcCfAGCIOL216uTBAkAIptvLIfKQhl7lHxMJet4GQ== +"@typescript-eslint/visitor-keys@5.52.0": + version "5.52.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz#e38c971259f44f80cfe49d97dbffa38e3e75030f" + integrity sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA== dependencies: - "@typescript-eslint/types" "5.51.0" + "@typescript-eslint/types" "5.52.0" eslint-visitor-keys "^3.3.0" "@whatwg-node/events@^0.0.2": @@ -2440,15 +2440,15 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^29.0, babel-jest@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.2.tgz#b17b9f64be288040877cbe2649f91ac3b63b2ba6" - integrity sha512-vcghSqhtowXPG84posYkkkzcZsdayFkubUgbE3/1tuGbX7AQtwCkkNA/wIbB0BMjuCPoqTkiDyKN7Ty7d3uwNQ== +babel-jest@^29.0, babel-jest@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.3.tgz#478b84d430972b277ad67dd631be94abea676792" + integrity sha512-o45Wyn32svZE+LnMVWv/Z4x0SwtLbh4FyGcYtR20kIWd+rdrDZ9Fzq8Ml3MYLD+mZvEdzCjZsCnYZ2jpJyQ+Nw== dependencies: - "@jest/transform" "^29.4.2" + "@jest/transform" "^29.4.3" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.4.2" + babel-preset-jest "^29.4.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2474,10 +2474,10 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.2.tgz#22aa43e255230f02371ffef1cac7eedef58f60bc" - integrity sha512-5HZRCfMeWypFEonRbEkwWXtNS1sQK159LhRVyRuLzyfVBxDy/34Tr/rg4YVi0SScSJ4fqeaR/OIeceJ/LaQ0pQ== +babel-plugin-jest-hoist@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.3.tgz#ad1dfb5d31940957e00410ef7d9b2aa94b216101" + integrity sha512-mB6q2q3oahKphy5V7CpnNqZOCkxxZ9aokf1eh82Dy3jQmg4xvM1tGrh5y6BQUJh4a3Pj9+eLfwvAZ7VNKg7H8Q== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2548,12 +2548,12 @@ babel-preset-jest@^27.0.6: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.2.tgz#f0b20c6a79a9f155515e72a2d4f537fe002a4e38" - integrity sha512-ecWdaLY/8JyfUDr0oELBMpj3R5I1L6ZqG+kRJmwqfHtLWuPrJStR0LUkvUhfykJWTsXXMnohsayN/twltBbDrQ== +babel-preset-jest@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.3.tgz#bb926b66ae253b69c6e3ef87511b8bb5c53c5b52" + integrity sha512-gWx6COtSuma6n9bw+8/F+2PCXrIgxV/D1TJFnp6OyBK2cxPWg0K9p/sriNYeifKjpUkMViWQ09DSWtzJQRETsw== dependencies: - babel-plugin-jest-hoist "^29.4.2" + babel-plugin-jest-hoist "^29.4.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2679,9 +2679,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449: - version "1.0.30001451" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001451.tgz#2e197c698fc1373d63e1406d6607ea4617c613f1" - integrity sha512-XY7UbUpGRatZzoRft//5xOa69/1iGJRBlrieH6QYrkKLIFn3m7OVEJ81dSrKoy2BnKsdbX5cLrOispZNYo9v2w== + version "1.0.30001453" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001453.tgz#6d3a1501622bf424a3cee5ad9550e640b0de3de8" + integrity sha512-R9o/uySW38VViaTrOtwfbFEiBFUh7ST3uIG4OEymIG3/uKdHDO4xk/FaqfUw0d+irSUyFPy3dZszf9VvSTPnsA== capital-case@^1.0.4: version "1.0.4" @@ -2913,10 +2913,10 @@ common-tags@1.8.2: resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== -compute-scroll-into-view@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-2.0.4.tgz#2b444b2b9e4724819d2531efacb7ac094155fdf6" - integrity sha512-y/ZA3BGnxoM/QHHQ2Uy49CLtnWPbt4tTPpEEZiEmmiWBFKjej7nEyH8Ryz54jH0MLXflUYA3Er2zUxPSJu5R+g== +compute-scroll-into-view@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.0.0.tgz#95d2f2f4653e7edda74dd1e38edaaa897918e0f0" + integrity sha512-Yk1An4qzo5++Cu6peT9PsmRKIU8tALpmdoE09n//AfGQFcPfx21/tMGMsmKYmLJWaBJrGOJ5Jz5hoU+7cZZUWQ== concat-map@0.0.1: version "0.0.1" @@ -3016,10 +3016,10 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -dataloader@2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.1.tgz#f07ab712514313a34b1507a308dbb7dc14bac715" - integrity sha512-Zn+tVZo1RKu120rgoe0JsRk56UiKdefPSH47QROJsMHrX8/S9UJvi5A/A6+Sbuk6rE88z5JoM/wIJ09Z7BTfYA== +dataloader@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" + integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== dayjs@^1.0, dayjs@^1.11.1: version "1.11.7" @@ -3141,10 +3141,10 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.2.tgz#711fe6bd8a5869fe2539cee4a5152425ff671fda" - integrity sha512-R6P0Y6PrsH3n4hUXxL3nns0rbRk6Q33js3ygJBeEpbzLzgcNuJ61+u0RXasFpTKISw99TxUzFnumSnRLsjhLaw== +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== diff@^4.0.1: version "4.0.2" @@ -3203,9 +3203,9 @@ ecdsa-sig-formatter@1.0.11: safe-buffer "^5.0.1" electron-to-chromium@^1.4.284: - version "1.4.295" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.295.tgz#911d5df67542bf7554336142eb302c5ec90bba66" - integrity sha512-lEO94zqf1bDA3aepxwnWoHUjA8sZ+2owgcSZjYQy0+uOSEclJX0VieZC+r+wLpSxUHRd6gG32znTWmr+5iGzFw== + version "1.4.299" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.299.tgz#faa2069cd4879a73e540e533178db5c618768d41" + integrity sha512-lQ7ijJghH6pCGbfWXr6EY+KYCMaRSjgsY925r1p/TlpSfVM1VjHTcn1gAc15VM4uwti283X6QtjPTXdpoSGiZQ== emittery@^0.13.1: version "0.13.1" @@ -3539,9 +3539,9 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + version "1.4.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.1.tgz#ddb8e1e2666750113b78c15f59e977564f52b116" + integrity sha512-3ZggxvMv5EEY1ssUVyHSVt0oPreyBfbUi1XikJVfjFiBeBDLdrb0IWoDiEwqT/2sUQi0TGaWtFhOGDD8RTpXgQ== dependencies: estraverse "^5.1.0" @@ -3582,16 +3582,16 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.2.tgz#2ae34eb88de797c64a1541ad0f1e2ea8a7a7b492" - integrity sha512-+JHYg9O3hd3RlICG90OPVjRkPBoiUH7PxvDVMnRiaq1g6JUgZStX514erMl0v2Dc5SkfVbm7ztqbd6qHHPn+mQ== +expect@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.3.tgz#5e47757316df744fe3b8926c3ae8a3ebdafff7fe" + integrity sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg== dependencies: - "@jest/expect-utils" "^29.4.2" - jest-get-type "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-util "^29.4.2" + "@jest/expect-utils" "^29.4.3" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.4.3" + jest-message-util "^29.4.3" + jest-util "^29.4.3" external-editor@^3.0.3: version "3.1.0" @@ -4535,129 +4535,129 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.2.tgz#bee1fafc8b620d6251423d1978a0080546bc4376" - integrity sha512-Qdd+AXdqD16PQa+VsWJpxR3kN0JyOCX1iugQfx5nUgAsI4gwsKviXkpclxOK9ZnwaY2IQVHz+771eAvqeOlfuw== +jest-changed-files@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.3.tgz#7961fe32536b9b6d5c28dfa0abcfab31abcf50a7" + integrity sha512-Vn5cLuWuwmi2GNNbokPOEcvrXGSGrqVnPEZV7rC6P7ck07Dyw9RFnvWglnupSh+hGys0ajGtw/bc2ZgweljQoQ== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.2.tgz#2d00c04baefd0ee2a277014cd494d4b5970663ed" - integrity sha512-wW3ztp6a2P5c1yOc1Cfrt5ozJ7neWmqeXm/4SYiqcSriyisgq63bwFj1NuRdSR5iqS0CMEYwSZd89ZA47W9zUg== +jest-circus@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.3.tgz#fff7be1cf5f06224dd36a857d52a9efeb005ba04" + integrity sha512-Vw/bVvcexmdJ7MLmgdT3ZjkJ3LKu8IlpefYokxiqoZy6OCQ2VAm6Vk3t/qHiAGUXbdbJKJWnc8gH3ypTbB/OBw== dependencies: - "@jest/environment" "^29.4.2" - "@jest/expect" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/environment" "^29.4.3" + "@jest/expect" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-runtime "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" + jest-each "^29.4.3" + jest-matcher-utils "^29.4.3" + jest-message-util "^29.4.3" + jest-runtime "^29.4.3" + jest-snapshot "^29.4.3" + jest-util "^29.4.3" p-limit "^3.1.0" - pretty-format "^29.4.2" + pretty-format "^29.4.3" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.2.tgz#94a2f913a0a7a49d11bee98ad88bf48baae941f4" - integrity sha512-b+eGUtXq/K2v7SH3QcJvFvaUaCDS1/YAZBYz0m28Q/Ppyr+1qNaHmVYikOrbHVbZqYQs2IeI3p76uy6BWbXq8Q== +jest-cli@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.3.tgz#fe31fdd0c90c765f392b8b7c97e4845071cd2163" + integrity sha512-PiiAPuFNfWWolCE6t3ZrDXQc6OsAuM3/tVW0u27UWc1KE+n/HSn5dSE6B2juqN7WP+PP0jAcnKtGmI4u8GMYCg== dependencies: - "@jest/core" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/core" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/types" "^29.4.3" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" + jest-config "^29.4.3" + jest-util "^29.4.3" + jest-validate "^29.4.3" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.2.tgz#15386dd9ed2f7059516915515f786b8836a98f07" - integrity sha512-919CtnXic52YM0zW4C1QxjG6aNueX1kBGthuMtvFtRTAxhKfJmiXC9qwHmi6o2josjbDz8QlWyY55F1SIVmCWA== +jest-config@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.3.tgz#fca9cdfe6298ae6d04beef1624064d455347c978" + integrity sha512-eCIpqhGnIjdUCXGtLhz4gdDoxKSWXKjzNcc5r+0S1GKOp2fwOipx5mRcwa9GB/ArsxJ1jlj2lmlD9bZAsBxaWQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.4.2" - "@jest/types" "^29.4.2" - babel-jest "^29.4.2" + "@jest/test-sequencer" "^29.4.3" + "@jest/types" "^29.4.3" + babel-jest "^29.4.3" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.4.2" - jest-environment-node "^29.4.2" - jest-get-type "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-runner "^29.4.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" + jest-circus "^29.4.3" + jest-environment-node "^29.4.3" + jest-get-type "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.4.3" + jest-runner "^29.4.3" + jest-util "^29.4.3" + jest-validate "^29.4.3" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.4.2" + pretty-format "^29.4.3" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.2.tgz#b88502d5dc02d97f6512d73c37da8b36f49b4871" - integrity sha512-EK8DSajVtnjx9sa1BkjZq3mqChm2Cd8rIzdXkQMA8e0wuXq53ypz6s5o5V8HRZkoEt2ywJ3eeNWFKWeYr8HK4g== +jest-diff@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.3.tgz#42f4eb34d0bf8c0fb08b0501069b87e8e84df347" + integrity sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA== dependencies: chalk "^4.0.0" - diff-sequences "^29.4.2" - jest-get-type "^29.4.2" - pretty-format "^29.4.2" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.4.3" -jest-docblock@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.2.tgz#c78a95eedf9a24c0a6cc16cf2abdc4b8b0f2531b" - integrity sha512-dV2JdahgClL34Y5vLrAHde3nF3yo2jKRH+GIYJuCpfqwEJZcikzeafVTGAjbOfKPG17ez9iWXwUYp7yefeCRag== +jest-docblock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" + integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== dependencies: detect-newline "^3.0.0" -jest-each@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.2.tgz#e1347aff1303f4c35470827a62c029d389c5d44a" - integrity sha512-trvKZb0JYiCndc55V1Yh0Luqi7AsAdDWpV+mKT/5vkpnnFQfuQACV72IoRV161aAr6kAVIBpmYzwhBzm34vQkA== +jest-each@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.3.tgz#a434c199a2f6151c5e3dc80b2d54586bdaa72819" + integrity sha512-1ElHNAnKcbJb/b+L+7j0/w7bDvljw4gTv1wL9fYOczeJrbTbkMGQ5iQPFJ3eFQH19VPTx1IyfePdqSpePKss7Q== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" chalk "^4.0.0" - jest-get-type "^29.4.2" - jest-util "^29.4.2" - pretty-format "^29.4.2" - -jest-environment-node@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.2.tgz#0eab835b41e25fd0c1a72f62665fc8db08762ad2" - integrity sha512-MLPrqUcOnNBc8zTOfqBbxtoa8/Ee8tZ7UFW7hRDQSUT+NGsvS96wlbHGTf+EFAT9KC3VNb7fWEM6oyvmxtE/9w== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/fake-timers" "^29.4.2" - "@jest/types" "^29.4.2" + jest-get-type "^29.4.3" + jest-util "^29.4.3" + pretty-format "^29.4.3" + +jest-environment-node@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.3.tgz#579c4132af478befc1889ddc43c2413a9cdbe014" + integrity sha512-gAiEnSKF104fsGDXNkwk49jD/0N0Bqu2K9+aMQXA6avzsA9H3Fiv1PW2D+gzbOSR705bWd2wJZRFEFpV0tXISg== + dependencies: + "@jest/environment" "^29.4.3" + "@jest/fake-timers" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" - jest-mock "^29.4.2" - jest-util "^29.4.2" + jest-mock "^29.4.3" + jest-util "^29.4.3" -jest-get-type@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.2.tgz#7cb63f154bca8d8f57364d01614477d466fa43fe" - integrity sha512-vERN30V5i2N6lqlFu4ljdTqQAgrkTFMC9xaIIfOPYBw04pufjXRty5RuXBiB1d72tGbURa/UgoiHB90ruOSivg== +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== jest-haste-map@^27.5.1: version "27.5.1" @@ -4679,66 +4679,66 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.2.tgz#9112df3f5121e643f1b2dcbaa86ab11b0b90b49a" - integrity sha512-WkUgo26LN5UHPknkezrBzr7lUtV1OpGsp+NfXbBwHztsFruS3gz+AMTTBcEklvi8uPzpISzYjdKXYZQJXBnfvw== +jest-haste-map@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.3.tgz#085a44283269e7ace0645c63a57af0d2af6942e2" + integrity sha512-eZIgAS8tvm5IZMtKlR8Y+feEOMfo2pSQkmNbufdbMzMSn9nitgGxF1waM/+LbryO3OkMcKS98SUb+j/cQxp/vQ== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.4.2" - jest-util "^29.4.2" - jest-worker "^29.4.2" + jest-regex-util "^29.4.3" + jest-util "^29.4.3" + jest-worker "^29.4.3" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.2.tgz#8f05c6680e0cb46a1d577c0d3da9793bed3ea97b" - integrity sha512-Wa62HuRJmWXtX9F00nUpWlrbaH5axeYCdyRsOs/+Rb1Vb6+qWTlB5rKwCCRKtorM7owNwKsyJ8NRDUcZ8ghYUA== +jest-leak-detector@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.3.tgz#2b35191d6b35aa0256e63a9b79b0f949249cf23a" + integrity sha512-9yw4VC1v2NspMMeV3daQ1yXPNxMgCzwq9BocCwYrRgXe4uaEJPAN0ZK37nFBhcy3cUwEVstFecFLaTHpF7NiGA== dependencies: - jest-get-type "^29.4.2" - pretty-format "^29.4.2" + jest-get-type "^29.4.3" + pretty-format "^29.4.3" -jest-matcher-utils@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.2.tgz#08d0bf5abf242e3834bec92c7ef5071732839e85" - integrity sha512-EZaAQy2je6Uqkrm6frnxBIdaWtSYFoR8SVb2sNLAtldswlR/29JAgx+hy67llT3+hXBaLB0zAm5UfeqerioZyg== +jest-matcher-utils@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz#ea68ebc0568aebea4c4213b99f169ff786df96a0" + integrity sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg== dependencies: chalk "^4.0.0" - jest-diff "^29.4.2" - jest-get-type "^29.4.2" - pretty-format "^29.4.2" + jest-diff "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.4.3" -jest-message-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.2.tgz#309a2924eae6ca67cf7f25781a2af1902deee717" - integrity sha512-SElcuN4s6PNKpOEtTInjOAA8QvItu0iugkXqhYyguRvQoXapg5gN+9RQxLAkakChZA7Y26j6yUCsFWN+hlKD6g== +jest-message-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.3.tgz#65b5280c0fdc9419503b49d4f48d4999d481cb5b" + integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.4.2" + pretty-format "^29.4.3" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.2.tgz#e1054be66fb3e975d26d4528fcde6979e4759de8" - integrity sha512-x1FSd4Gvx2yIahdaIKoBjwji6XpboDunSJ95RpntGrYulI1ByuYQCKN/P7hvk09JB74IonU3IPLdkutEWYt++g== +jest-mock@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.3.tgz#23d84a20a74cdfff0510fdbeefb841ed57b0fe7e" + integrity sha512-LjFgMg+xed9BdkPMyIJh+r3KeHt1klXPJYBULXVVAkbTaaKjPX1o1uVCAZADMEp/kOxGTwy/Ot8XbvgItOrHEg== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@types/node" "*" - jest-util "^29.4.2" + jest-util "^29.4.3" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -4750,87 +4750,86 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.2.tgz#19187cca35d301f8126cf7a021dd4dcb7b58a1ca" - integrity sha512-XYZXOqUl1y31H6VLMrrUL1ZhXuiymLKPz0BO1kEeR5xER9Tv86RZrjTm74g5l9bPJQXA/hyLdaVPN/sdqfteig== +jest-regex-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" + integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.2.tgz#6359db606f5967b68ca8bbe9dbc07a4306c12bf7" - integrity sha512-6pL4ptFw62rjdrPk7rRpzJYgcRqRZNsZTF1VxVTZMishbO6ObyWvX57yHOaNGgKoADtAHRFYdHQUEvYMJATbDg== +jest-resolve-dependencies@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.3.tgz#9ad7f23839a6d88cef91416bda9393a6e9fd1da5" + integrity sha512-uvKMZAQ3nmXLH7O8WAOhS5l0iWyT3WmnJBdmIHiV5tBbdaDZ1wqtNX04FONGoaFvSOSHBJxnwAVnSn1WHdGVaw== dependencies: - jest-regex-util "^29.4.2" - jest-snapshot "^29.4.2" + jest-regex-util "^29.4.3" + jest-snapshot "^29.4.3" -jest-resolve@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.2.tgz#8831f449671d08d161fe493003f61dc9b55b808e" - integrity sha512-RtKWW0mbR3I4UdkOrW7552IFGLYQ5AF9YrzD0FnIOkDu0rAMlA5/Y1+r7lhCAP4nXSBTaE7ueeqj6IOwZpgoqw== +jest-resolve@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.3.tgz#3c5b5c984fa8a763edf9b3639700e1c7900538e2" + integrity sha512-GPokE1tzguRyT7dkxBim4wSx6E45S3bOQ7ZdKEG+Qj0Oac9+6AwJPCk0TZh5Vu0xzeX4afpb+eDmgbmZFFwpOw== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" + jest-haste-map "^29.4.3" jest-pnp-resolver "^1.2.2" - jest-util "^29.4.2" - jest-validate "^29.4.2" + jest-util "^29.4.3" + jest-validate "^29.4.3" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.2.tgz#2bcecf72303369df4ef1e6e983c22a89870d5125" - integrity sha512-wqwt0drm7JGjwdH+x1XgAl+TFPH7poowMguPQINYxaukCqlczAcNLJiK+OLxUxQAEWMdy+e6nHZlFHO5s7EuRg== +jest-runner@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.3.tgz#68dc82c68645eda12bea42b5beece6527d7c1e5e" + integrity sha512-GWPTEiGmtHZv1KKeWlTX9SIFuK19uLXlRQU43ceOQ2hIfA5yPEJC7AMkvFKpdCHx6pNEdOD+2+8zbniEi3v3gA== dependencies: - "@jest/console" "^29.4.2" - "@jest/environment" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/console" "^29.4.3" + "@jest/environment" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/transform" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^29.4.2" - jest-environment-node "^29.4.2" - jest-haste-map "^29.4.2" - jest-leak-detector "^29.4.2" - jest-message-util "^29.4.2" - jest-resolve "^29.4.2" - jest-runtime "^29.4.2" - jest-util "^29.4.2" - jest-watcher "^29.4.2" - jest-worker "^29.4.2" + jest-docblock "^29.4.3" + jest-environment-node "^29.4.3" + jest-haste-map "^29.4.3" + jest-leak-detector "^29.4.3" + jest-message-util "^29.4.3" + jest-resolve "^29.4.3" + jest-runtime "^29.4.3" + jest-util "^29.4.3" + jest-watcher "^29.4.3" + jest-worker "^29.4.3" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.2.tgz#d86b764c5b95d76cb26ed1f32644e99de5d5c134" - integrity sha512-3fque9vtpLzGuxT9eZqhxi+9EylKK/ESfhClv4P7Y9sqJPs58LjVhTt8jaMp/pRO38agll1CkSu9z9ieTQeRrw== - dependencies: - "@jest/environment" "^29.4.2" - "@jest/fake-timers" "^29.4.2" - "@jest/globals" "^29.4.2" - "@jest/source-map" "^29.4.2" - "@jest/test-result" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" +jest-runtime@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.3.tgz#f25db9874dcf35a3ab27fdaabca426666cc745bf" + integrity sha512-F5bHvxSH+LvLV24vVB3L8K467dt3y3dio6V3W89dUz9nzvTpqd/HcT9zfYKL2aZPvD63vQFgLvaUX/UpUhrP6Q== + dependencies: + "@jest/environment" "^29.4.3" + "@jest/fake-timers" "^29.4.3" + "@jest/globals" "^29.4.3" + "@jest/source-map" "^29.4.3" + "@jest/test-result" "^29.4.3" + "@jest/transform" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.4.2" - jest-message-util "^29.4.2" - jest-mock "^29.4.2" - jest-regex-util "^29.4.2" - jest-resolve "^29.4.2" - jest-snapshot "^29.4.2" - jest-util "^29.4.2" - semver "^7.3.5" + jest-haste-map "^29.4.3" + jest-message-util "^29.4.3" + jest-mock "^29.4.3" + jest-regex-util "^29.4.3" + jest-resolve "^29.4.3" + jest-snapshot "^29.4.3" + jest-util "^29.4.3" slash "^3.0.0" strip-bom "^4.0.0" @@ -4842,10 +4841,10 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.2.tgz#ba1fb9abb279fd2c85109ff1757bc56b503bbb3a" - integrity sha512-PdfubrSNN5KwroyMH158R23tWcAXJyx4pvSvWls1dHoLCaUhGul9rsL3uVjtqzRpkxlkMavQjGuWG1newPgmkw== +jest-snapshot@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.3.tgz#183d309371450d9c4a3de7567ed2151eb0e91145" + integrity sha512-NGlsqL0jLPDW91dz304QTM/SNO99lpcSYYAjNiX0Ou+sSGgkanKBcSjCfp/pqmiiO1nQaOyLp6XQddAzRcx3Xw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" @@ -4853,23 +4852,23 @@ jest-snapshot@^29.4.2: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.4.2" - "@jest/transform" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/expect-utils" "^29.4.3" + "@jest/transform" "^29.4.3" + "@jest/types" "^29.4.3" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.4.2" + expect "^29.4.3" graceful-fs "^4.2.9" - jest-diff "^29.4.2" - jest-get-type "^29.4.2" - jest-haste-map "^29.4.2" - jest-matcher-utils "^29.4.2" - jest-message-util "^29.4.2" - jest-util "^29.4.2" + jest-diff "^29.4.3" + jest-get-type "^29.4.3" + jest-haste-map "^29.4.3" + jest-matcher-utils "^29.4.3" + jest-message-util "^29.4.3" + jest-util "^29.4.3" natural-compare "^1.4.0" - pretty-format "^29.4.2" + pretty-format "^29.4.3" semver "^7.3.5" jest-util@^27.5.1: @@ -4884,42 +4883,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.2.tgz#3db8580b295df453a97de4a1b42dd2578dabd2c2" - integrity sha512-wKnm6XpJgzMUSRFB7YF48CuwdzuDIHenVuoIb1PLuJ6F+uErZsuDkU+EiExkChf6473XcawBrSfDSnXl+/YG4g== +jest-util@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.3.tgz#851a148e23fc2b633c55f6dad2e45d7f4579f496" + integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.2.tgz#3b3f8c4910ab9a3442d2512e2175df6b3f77b915" - integrity sha512-tto7YKGPJyFbhcKhIDFq8B5od+eVWD/ySZ9Tvcp/NGCvYA4RQbuzhbwYWtIjMT5W5zA2W0eBJwu4HVw34d5G6Q== +jest-validate@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.3.tgz#a13849dec4f9e95446a7080ad5758f58fa88642f" + integrity sha512-J3u5v7aPQoXPzaar6GndAVhdQcZr/3osWSgTeKg5v574I9ybX/dTyH0AJFb5XgXIB7faVhf+rS7t4p3lL9qFaw== dependencies: - "@jest/types" "^29.4.2" + "@jest/types" "^29.4.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^29.4.2" + jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^29.4.2" + pretty-format "^29.4.3" -jest-watcher@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.2.tgz#09c0f4c9a9c7c0807fcefb1445b821c6f7953b7c" - integrity sha512-onddLujSoGiMJt+tKutehIidABa175i/Ays+QvKxCqBwp7fvxP3ZhKsrIdOodt71dKxqk4sc0LN41mWLGIK44w== +jest-watcher@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.3.tgz#e503baa774f0c2f8f3c8db98a22ebf885f19c384" + integrity sha512-zwlXH3DN3iksoIZNk73etl1HzKyi5FuQdYLnkQKm5BW4n8HpoG59xSwpVdFrnh60iRRaRBGw0gcymIxjJENPcA== dependencies: - "@jest/test-result" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/test-result" "^29.4.3" + "@jest/types" "^29.4.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.4.2" + jest-util "^29.4.3" string-length "^4.0.1" jest-worker@^27.5.1: @@ -4931,30 +4930,30 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.2.tgz#d9b2c3bafc69311d84d94e7fb45677fc8976296f" - integrity sha512-VIuZA2hZmFyRbchsUCHEehoSf2HEl0YVF8SDJqtPnKorAaBuh42V8QsLnde0XP5F6TyCynGPEGgBOn3Fc+wZGw== +jest-worker@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.3.tgz#9a4023e1ea1d306034237c7133d7da4240e8934e" + integrity sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA== dependencies: "@types/node" "*" - jest-util "^29.4.2" + jest-util "^29.4.3" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.0: - version "29.4.2" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.2.tgz#4c2127d03a71dc187f386156ef155dbf323fb7be" - integrity sha512-+5hLd260vNIHu+7ZgMIooSpKl7Jp5pHKb51e73AJU3owd5dEo/RfVwHbA/na3C/eozrt3hJOLGf96c7EWwIAzg== + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.3.tgz#1b8be541666c6feb99990fd98adac4737e6e6386" + integrity sha512-XvK65feuEFGZT8OO0fB/QAQS+LGHvQpaadkH5p47/j3Ocqq3xf2pK9R+G0GzgfuhXVxEv76qCOOcMb5efLk6PA== dependencies: - "@jest/core" "^29.4.2" - "@jest/types" "^29.4.2" + "@jest/core" "^29.4.3" + "@jest/types" "^29.4.3" import-local "^3.0.2" - jest-cli "^29.4.2" + jest-cli "^29.4.3" jose@^4.10.0, jose@^4.9.3: - version "4.11.4" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.4.tgz#e04d4a393ac017d0450fa0a38e2f2382cd73f71e" - integrity sha512-94FdcR8felat4vaTJyL/WVdtlWLlsnLMZP8v+A0Vru18K3bQ22vn7TtpVh3JlgBFNIlYOUlGqwp/MjRPOnIyCQ== + version "4.12.0" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.12.0.tgz#7f00cd2f82499b91623cd413b7b5287fd52651ed" + integrity sha512-wW1u3cK81b+SFcHjGC8zw87yuyUweEFe0UJirrXEw1NasW00eF7sZjeG3SLBGz001ozxQ46Y9sofDvhBmWFtXQ== js-sdsl@^4.1.4: version "4.3.0" @@ -5828,12 +5827,12 @@ prettier@^2.8.4: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== -pretty-format@^29.4.2: - version "29.4.2" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.2.tgz#64bf5ccc0d718c03027d94ac957bdd32b3fb2401" - integrity sha512-qKlHR8yFVCbcEWba0H0TOC8dnLlO4vPlyEjRPw31FZ2Rupy9nLa8ZLbYny8gWEl8CkEhJqAE6IzdNELTBVcBEg== +pretty-format@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c" + integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== dependencies: - "@jest/schemas" "^29.4.2" + "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -5951,9 +5950,9 @@ rc-dialog@~9.0.0, rc-dialog@~9.0.2: rc-util "^5.21.0" rc-drawer@~6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.1.2.tgz#032918a21bfa8a7d9e52ada1e7b8ed08c0ae6346" - integrity sha512-mYsTVT8Amy0LRrpVEv7gI1hOjtfMSO/qHAaCDzFx9QBLnms3cAQLJkaxRWM+Eq99oyLhU/JkgoqTg13bc4ogOQ== + version "6.1.3" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.1.3.tgz#4b2277db09f059be7144dc82d5afede9c2ab2191" + integrity sha512-AvHisO90A+xMLMKBw2zs89HxjWxusM2BUABlgK60RhweIHF8W/wk0hSOrxBlUXoA9r1F+10na3g6GZ97y1qDZA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/portal" "^1.0.0-6" @@ -6162,9 +6161,9 @@ rc-switch@~4.0.0: rc-util "^5.0.1" rc-table@~7.30.2: - version "7.30.3" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.30.3.tgz#9333e4d0b106738d88e2e16c8b066dfb29d93114" - integrity sha512-PHe+lZKwPo3qui5j79m54vKu8b4hebk04x+4Hy65NvwUU3+NNFGS5FZpylXQMkueMnE8hgh22ZuScQDkCtzQFQ== + version "7.30.4" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.30.4.tgz#08da9ed849fa700a15a87d097c3a65f5622ca875" + integrity sha512-NXfitP7CEXAnnuxdEYZNwEMyWmWOSsv9WgiP2yI10pyy0BwomFn4aa8nX/9x832edLwuIlrYuL6ojfOK60An2w== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/context" "^1.3.0" @@ -6558,11 +6557,11 @@ schema-utils@3.0.0: ajv-keywords "^3.5.2" scroll-into-view-if-needed@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.4.tgz#4b3101712f0ac8ec982c0a7ab4d071e393241e21" - integrity sha512-s+/F50jwTOUt+u5oEIAzum9MN2lUQNvWBe/zfEsVQcbaERjGkKLq1s+2wCHkahMLC8nMLbzMVKivx9JhunXaZg== + version "3.0.6" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.6.tgz#2c803a509c1036bc4a9c009fecc5c145f87e47cf" + integrity sha512-x+CW0kOzlFNOnseF0DBr0AJ5m+TgGmSOdEZwyiZW0gV87XBvxQKw5A8DvFFgabznA68XqLgVX+PwPX8OzsFvRA== dependencies: - compute-scroll-into-view "^2.0.4" + compute-scroll-into-view "^3.0.0" scuid@^1.1.0: version "1.1.0" @@ -7237,9 +7236,9 @@ v8-compile-cache-lib@^3.0.1: integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== + version "9.1.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" + integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" @@ -7397,10 +7396,10 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.12.0, ws@^8.12.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8" - integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig== +ws@8.12.1, ws@^8.12.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" + integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== y18n@^4.0.0: version "4.0.3" From ec7e74aa0ee90ee08c263e7adfd162dd099f0920 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Feb 2023 15:39:01 +0100 Subject: [PATCH 060/878] Upgrade dependencies and tooling --- backend/dotnet-tools.json | 16 ++++++++-------- backend/src/Database.csproj | 26 +++++++++++++------------- backend/test/Database.Tests.csproj | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 19e84d5d..48884595 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,25 +3,25 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "7.0.3", + "version": "7.0.4", "commands": [ "dotnet-aspnet-codegenerator" ] }, "dotnet-counters": { - "version": "6.0.351802", + "version": "7.0.410101", "commands": [ "dotnet-counters" ] }, "dotnet-dump": { - "version": "6.0.351802", + "version": "7.0.410101", "commands": [ "dotnet-dump" ] }, "dotnet-ef": { - "version": "7.0.2", + "version": "7.0.3", "commands": [ "dotnet-ef" ] @@ -39,19 +39,19 @@ ] }, "dotnet-trace": { - "version": "6.0.351802", + "version": "7.0.410101", "commands": [ "dotnet-trace" ] }, "dotnet-gcdump": { - "version": "6.0.351802", + "version": "7.0.410101", "commands": [ "dotnet-gcdump" ] }, "dotnet-sos": { - "version": "6.0.351802", + "version": "7.0.410101", "commands": [ "dotnet-sos" ] @@ -63,7 +63,7 @@ ] }, "jetbrains.resharper.globaltools": { - "version": "2022.3.1", + "version": "2022.3.2", "commands": [ "jb" ] diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index d951ee9b..de39a514 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -19,32 +19,32 @@ - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - - - - + + + + + + + - +
diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 08f470b7..7437ef71 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -12,10 +12,10 @@ - + - + From 653856c02e8a586dd326367ccc80ce1efd87d6c2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Feb 2023 15:44:57 +0100 Subject: [PATCH 061/878] Use generic attributes for `GraphQLType` --- .../src/Configuration/GraphQlConfiguration.cs | 3 +- .../GraphQl/DataX/GetHttpsResourceTreeRoot.cs | 2 +- backend/src/GraphQl/LocaleType.cs | 2 +- .../src/GraphQl/NamedMethodArgumentInput.cs | 7 +- .../src/GraphQl/NamedMethodArgumentType.cs | 180 +----------------- 5 files changed, 13 insertions(+), 181 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index e47d13ae..38faaac8 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -96,6 +96,7 @@ IWebHostEnvironment environment // Scalar Types .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type .AddType(new UrlType("Url")) + .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar .AddType(new GraphQl.LocaleType()) // Object Types .AddType() @@ -144,7 +145,7 @@ protected override void Configure(IFilterConventionDescriptor descriptor) // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? // For the base implementation see https://github.com/ChilliCream/hotchocolate/blob/f0dff93a14cb7ddecc7b3a0530a687a5bc4bad71/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 - public override NameString GetTypeName(Type runtimeType) + public override string GetTypeName(Type runtimeType) { var nameString = base.GetTypeName(runtimeType); if (!nameString.HasValue) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs index c69122f3..81abe03c 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.DataX { public sealed class GetHttpsResourceTreeRoot : IGetHttpsResourceTreeVertex { - [GraphQLType(typeof(NonNullType))] + [GraphQLType>] public string VertexId { get => Data.GetHttpsResource.ConstructVertexId(Value.Id); diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs index d8162800..b73d4b1f 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/LocaleType.cs @@ -43,7 +43,7 @@ public LocaleType() /// Initializes a new instance of the class. /// public LocaleType( - NameString name, + string name, string? description = null, BindingBehavior bind = BindingBehavior.Explicit) : base( diff --git a/backend/src/GraphQl/NamedMethodArgumentInput.cs b/backend/src/GraphQl/NamedMethodArgumentInput.cs index 9c322b7f..a3ae1af7 100644 --- a/backend/src/GraphQl/NamedMethodArgumentInput.cs +++ b/backend/src/GraphQl/NamedMethodArgumentInput.cs @@ -1,10 +1,9 @@ -// using HotChocolate.Types; -// using HotChocolate; +using System.Text.Json; namespace Database.GraphQl { public record NamedMethodArgumentInput( - string Name - // TODO [GraphQLType(nameof(NonNullType))] object Value + string Name, + JsonElement Value ); } \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentType.cs b/backend/src/GraphQl/NamedMethodArgumentType.cs index fd6f80f0..01097866 100644 --- a/backend/src/GraphQl/NamedMethodArgumentType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentType.cs @@ -9,184 +9,16 @@ protected override void Configure( IObjectTypeDescriptor descriptor ) { - // TODO Convert value to "nested collections", that is, some - // hierarchy of `IReadOnlyDictionary` and - // `IReadOnlyList`. See also - // https://github.com/ChilliCream/hotchocolate/issues/3661 and - // https://github.com/ChilliCream/hotchocolate/pull/3713 - // The latter contains code of how to do such a conversion. + // Note that in the GraphQL configuration we map the scalar "Any" + // to `JsonType`. descriptor .Field(x => x.Value) - .Type>() + .Type>() .Resolve(context => - "TODO" - // context.Parent() - // .Value + context.Parent() + .Value + .RootElement ); } - - // TODO Below is some code I wrote to convet `JsonValue` to "nested collections". It is based on Newtonsoft's JSON representation though. - // private static object? ConvertJsonValueToNestedCollections(JsonValue jsonValue) - // { - // return jsonValue.Type switch - // { - // JsonValueType.Array => - // jsonValue.Array - // .Select(ConvertJsonValueToNestedCollections) - // .ToList().AsReadOnly(), - // JsonValueType.Boolean => jsonValue.Boolean, - // JsonValueType.Null => null, - // JsonValueType.Number => jsonValue.Number, - // JsonValueType.Object => - // new ReadOnlyDictionary( - // jsonValue.Object.ToDictionary( - // pair => pair.Key, - // pair => ConvertJsonValueToNestedCollections(pair.Value) - // ) - // ), - // JsonValueType.String => jsonValue.String, - // // God-damned C# does not have switch expression exhaustiveness for - // // enums as mentioned for example on https://github.com/dotnet/csharplang/issues/2266 - // _ => throw new Exception($"The JSON value `{jsonValue}` of type `{jsonValue.Type}` fell through") - // }; - // } - - // private static Result ConvertNestedCollectionsToJsonValue( - // object? jsonValue, - // IReadOnlyList? path = null - // ) - // { - // return jsonValue switch - // { - // IList list => - // list.Select((v, index) => - // ConvertNestedCollectionsToJsonValue( - // v, - // path?.Append(index).ToList().AsReadOnly() - // ) - // ) - // .Combine() - // .Map(jsonValues => - // new JsonValue( - // new JsonArray(jsonValues) - // ) - // ), - // bool boolean => Result.Success(new JsonValue(boolean)), - // null => Result.Success(JsonValue.Null), - // // For the list of implicit conversions to `double` see - // // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/numeric-conversions#implicit-numeric-conversions - // // and for the explicit ones see - // // https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/numeric-conversions#explicit-numeric-conversions - // sbyte number => Result.Success(new JsonValue((double)number)), - // byte number => Result.Success(new JsonValue((double)number)), - // short number => Result.Success(new JsonValue((double)number)), - // ushort number => Result.Success(new JsonValue((double)number)), - // int number => Result.Success(new JsonValue((double)number)), - // uint number => Result.Success(new JsonValue((double)number)), - // long number => Result.Success(new JsonValue((double)number)), - // ulong number => Result.Success(new JsonValue((double)number)), - // float number => Result.Success(new JsonValue((double)number)), - // double number => Result.Success(new JsonValue(number)), - // decimal number => Result.Success(new JsonValue((double)number)), - // IDictionary dictionary => - // dictionary.Select(pair => - // ConvertNestedCollectionsToJsonValue( - // pair.Value, - // path?.Append(pair.Key).ToList().AsReadOnly() - // ) - // .Map(jsonValue => - // new KeyValuePair(pair.Key, jsonValue) - // ) - // ) - // .Combine() - // .Map(stringJsonValuePairs => - // new JsonValue( - // new JsonObject( - // stringJsonValuePairs.ToDictionary( - // pair => pair.Key, - // pair => (JsonValue?)pair.Value - // ) - // ) - // ) - // ), - // string @string => Result.Success(new JsonValue(@string)), - // // God-damned C# does not have switch expression exhaustiveness for - // // enums as mentioned for example on https://github.com/dotnet/csharplang/issues/2266 - // _ => Result.Failure( - // Errors.One( - // message: $"The JSON value `{jsonValue}` of type `{jsonValue.GetType()}` fell through", - // code: ErrorCodes.InvalidValue, - // path: path - // ) - // ) - // }; - // } - - // private static Result ConvertJsonElementToJsonValue( - // JsonElement jsonElement, - // IReadOnlyList? path = null - // ) - // { - // return jsonElement.ValueKind switch - // { - // JsonValueKind.Array => - // jsonElement.EnumerateArray() - // .Select((v, index) => - // ConvertJsonElementToJsonValue( - // v, - // path?.Append(index).ToList().AsReadOnly() - // ) - // ) - // .Combine() - // .Map(jsonValues => - // new JsonValue( - // new JsonArray(jsonValues) - // ) - // ), - // JsonValueKind.False => Result.Success(new JsonValue(false)), - // JsonValueKind.Null => Result.Success(JsonValue.Null), - // JsonValueKind.Number => Result.Success(new JsonValue(jsonElement.GetDouble())), - // JsonValueKind.Object => - // jsonElement.EnumerateObject() - // .Select(jsonProperty => - // ConvertJsonElementToJsonValue( - // jsonProperty.Value, - // path?.Append(jsonProperty.Name).ToList().AsReadOnly() - // ) - // .Map(jsonValue => - // new KeyValuePair(jsonProperty.Name, jsonValue) - // ) - // ) - // .Combine() - // .Map(stringJsonValuePairs => - // new JsonValue( - // new JsonObject( - // stringJsonValuePairs.ToDictionary( - // pair => pair.Key, - // pair => (JsonValue?)pair.Value - // ) - // ) - // ) - // ), - // JsonValueKind.String => Result.Success(new JsonValue(jsonElement.GetString())), - // JsonValueKind.True => Result.Success(new JsonValue(true)), - // JsonValueKind.Undefined => Result.Failure( - // Errors.One( - // message: $"The JSON element `{jsonElement}` is of kind `{JsonValueKind.Undefined}`", - // code: ErrorCodes.InvalidValue, - // path: path - // ) - // ), - // // God-damned C# does not have switch expression exhaustiveness for - // // enums as mentioned for example on https://github.com/dotnet/csharplang/issues/2266 - // _ => Result.Failure( - // Errors.One( - // message: $"The JSON element `{jsonElement}` of kind `{jsonElement.ValueKind}` fell through", - // code: ErrorCodes.InvalidValue, - // path: path - // ) - // ) - // }; - // } } } From 98f58fc468c08814aa7dbc7a5517f9b4343ddeb6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Feb 2023 16:06:45 +0100 Subject: [PATCH 062/878] Install strawberry shake tool --- backend/dotnet-tools.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 48884595..36aa29cb 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -73,6 +73,12 @@ "commands": [ "dotnet-project-licenses" ] + }, + "strawberryshake.tools": { + "version": "13.0.2", + "commands": [ + "dotnet-graphql" + ] } } } \ No newline at end of file From 4adeed0452e1f8bb3c6f5d61a857abbb3f7003ea Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Feb 2023 16:53:38 +0100 Subject: [PATCH 063/878] Update Hot Chocolate usage as detailed on https://chillicream.com/docs/hotchocolate/v13/migrating/migrate-from-12-to-13 --- .../src/Configuration/GraphQlConfiguration.cs | 8 +- .../CalorimetricDataMutations.cs | 2 +- .../CalorimetricDataQueries.cs | 6 +- .../CreateCalorimetricDataInput.cs | 2 +- backend/src/GraphQl/Connection.cs | 2 +- backend/src/GraphQl/DataX/DataQueries.cs | 6 +- .../GetHttpsResourceTreeNonRootVertex.cs | 4 +- .../DataX/IGetHttpsResourceTreeVertex.cs | 2 +- backend/src/GraphQl/Edge.cs | 2 +- backend/src/GraphQl/ForkingConnection.cs | 4 +- .../GetHttpsResourceMutations.cs | 2 +- .../GetHttpsResourceQueries.cs | 2 +- .../CreateHygrothermalDataInput.cs | 2 +- .../HygrothermalDataMutations.cs | 2 +- .../HygrothermalDataQueries.cs | 6 +- .../GraphQl/LoggingDiagnosticEventListener.cs | 4 +- .../OpticalDataX/CreateOpticalDataInput.cs | 2 +- .../OpticalDataX/OpticalDataMutations.cs | 2 +- .../OpticalDataX/OpticalDataQueries.cs | 6 +- .../CreatePhotovoltaicDataInput.cs | 2 +- .../PhotovoltaicDataMutations.cs | 2 +- .../PhotovoltaicDataQueries.cs | 6 +- backend/src/Program.cs | 4 +- frontend/type-defs.graphqls | 200 ++++-------------- 24 files changed, 78 insertions(+), 202 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 38faaac8..46f3c898 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -24,6 +24,8 @@ IWebHostEnvironment environment ) { services.AddGraphQLServer() + // Database + .RegisterDbContext(DbContextKind.Pooled) // Types .AddType() // Extensions @@ -148,10 +150,6 @@ protected override void Configure(IFilterConventionDescriptor descriptor) public override string GetTypeName(Type runtimeType) { var nameString = base.GetTypeName(runtimeType); - if (!nameString.HasValue) - { - return nameString; - } return new Regex(@"IData").Replace( new Regex(@"Double").Replace( @@ -162,7 +160,7 @@ public override string GetTypeName(Type runtimeType) new Regex(@"^Comparable").Replace( new Regex(@"FilterInput$").Replace( new Regex(@"OperationFilterInput").Replace( - nameString.Value, + nameString, "PropositionInput" ), "PropositionInput" diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index b4ef2d4e..b1fc6d50 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -20,7 +20,7 @@ public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, - [ScopedService] Data.ApplicationDbContext context, + Data.ApplicationDbContext context, [Service] AppSettings appSettings, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 6c5be9e4..cf0e9d21 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -19,8 +19,8 @@ public sealed class CalorimetricDataQueries [UseSorting] public IQueryable GetAllCalorimetricData( DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context + [GraphQLType] string? locale, + Data.ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. @@ -30,7 +30,7 @@ [ScopedService] Data.ApplicationDbContext context public Task GetCalorimetricDataAsync( Guid id, DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, + [GraphQLType] string? locale, CalorimetricDataByIdDataLoader byId, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index d155ce75..ca76e285 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -8,7 +8,7 @@ namespace Database.GraphQl.CalorimetricDataX public record CreateCalorimetricDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? - [GraphQLType(typeof(NonNullType))] string Locale, + [GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index e7bd4848..256f9551 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -25,7 +25,7 @@ Func createEdge } public async Task> GetEdgesAsync( - [DataLoader] TAssociationsByAssociateIdDataLoader dataLoader, + TAssociationsByAssociateIdDataLoader dataLoader, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 9b49bfae..92e3fe04 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -24,8 +24,8 @@ public sealed class DataQueries [UseSorting] public IEnumerable GetAllData( DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context + [GraphQLType] string? locale, + Data.ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. @@ -44,7 +44,7 @@ [ScopedService] Data.ApplicationDbContext context public async Task GetDataAsync( Guid id, DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, + [GraphQLType] string? locale, CalorimetricDataByIdDataLoader calorimetricDataById, HygrothermalDataByIdDataLoader hygrothermalDataById, OpticalDataByIdDataLoader opticalDataById, diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs index 7a02277e..a337b9a4 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -7,7 +7,7 @@ namespace Database.GraphQl.DataX public sealed class GetHttpsResourceTreeNonRootVertex : IGetHttpsResourceTreeVertex { - [GraphQLType(typeof(NonNullType))] + [GraphQLType>] public string VertexId { get => Data.GetHttpsResource.ConstructVertexId(Value.Id); @@ -15,7 +15,7 @@ public string VertexId public Data.GetHttpsResource Value { get; } - [GraphQLType(typeof(NonNullType))] + [GraphQLType>] public string ParentId { get => Data.GetHttpsResource.ConstructVertexId( diff --git a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs index 774fbe8a..21985d52 100644 --- a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs +++ b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs @@ -6,7 +6,7 @@ namespace Database.GraphQl.DataX [InterfaceType("GetHttpsResourceTreeVertex")] public interface IGetHttpsResourceTreeVertex { - [GraphQLType(typeof(IdType))] + [GraphQLType] string VertexId { get; } Data.GetHttpsResource Value { get; } diff --git a/backend/src/GraphQl/Edge.cs b/backend/src/GraphQl/Edge.cs index 17486d82..a7622ba5 100644 --- a/backend/src/GraphQl/Edge.cs +++ b/backend/src/GraphQl/Edge.cs @@ -19,7 +19,7 @@ Guid nodeId } public async Task GetNodeAsync( - [DataLoader] TNodeByIdDataLoader byId, + TNodeByIdDataLoader byId, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index 44298992..c787d2fc 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -29,8 +29,8 @@ Func createEdge } public Task> GetEdgesAsync( - [DataLoader] TSomeAssociationsByAssociateIdDataLoader someDataLoader, - [DataLoader] TOtherAssociationsByAssociateIdDataLoader otherDataLoader, + TSomeAssociationsByAssociateIdDataLoader someDataLoader, + TOtherAssociationsByAssociateIdDataLoader otherDataLoader, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 6f74ab54..49dfb7ce 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -20,7 +20,7 @@ public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, - [ScopedService] Data.ApplicationDbContext context, + Data.ApplicationDbContext context, [Service] AppSettings appSettings, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 605502b7..27bb6cf3 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -17,7 +17,7 @@ public sealed class GetHttpsResourceQueries [UseFiltering] [UseSorting] public IQueryable GetGetHttpsResources( - [ScopedService] Data.ApplicationDbContext context + Data.ApplicationDbContext context ) { return context.GetHttpsResources; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 8c0a1cde..a50c4a2f 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -8,7 +8,7 @@ namespace Database.GraphQl.HygrothermalDataX public record CreateHygrothermalDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? - [GraphQLType(typeof(NonNullType))] string Locale, + [GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index fdf2fcff..c07db715 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -20,7 +20,7 @@ public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, - [ScopedService] Data.ApplicationDbContext context, + Data.ApplicationDbContext context, [Service] AppSettings appSettings, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 00be0ec6..1b60e2ff 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -19,8 +19,8 @@ public sealed class HygrothermalDataQueries [UseSorting] public IQueryable GetAllHygrothermalData( DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context + [GraphQLType] string? locale, + Data.ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. @@ -30,7 +30,7 @@ [ScopedService] Data.ApplicationDbContext context public Task GetHygrothermalDataAsync( Guid id, DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, + [GraphQLType] string? locale, HygrothermalDataByIdDataLoader byId, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index f62253f3..cbb8622f 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -52,12 +52,12 @@ IError error public override void SubscriptionEventError(SubscriptionEventContext context, Exception exception) { - _logger.LogError(exception, $"During execution of the subscription operation {context.Subscription.Operation.Print()} the exception {exception} occurred."); + _logger.LogError(exception, $"During execution of the subscription operation {context.Subscription.Operation} the exception {exception} occurred."); } public override void SubscriptionTransportError(ISubscription subscription, Exception exception) { - _logger.LogError(exception, $"During execution of the subscription operation {subscription.Operation.Print()} the exception {exception} occurred."); + _logger.LogError(exception, $"During execution of the subscription operation {subscription.Operation} the exception {exception} occurred."); } public override void SyntaxError( diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index 37519525..c0525343 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -8,7 +8,7 @@ namespace Database.GraphQl.OpticalDataX public record CreateOpticalDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? - [GraphQLType(typeof(NonNullType))] string Locale, + [GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index a4d97a4e..f76b3b8b 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -20,7 +20,7 @@ public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, - [ScopedService] Data.ApplicationDbContext context, + Data.ApplicationDbContext context, [Service] AppSettings appSettings, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 3264abb1..57f2bd83 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -19,8 +19,8 @@ public sealed class OpticalDataQueries [UseSorting] public IQueryable GetAllOpticalData( DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context + [GraphQLType] string? locale, + Data.ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. @@ -30,7 +30,7 @@ [ScopedService] Data.ApplicationDbContext context public Task GetOpticalDataAsync( Guid id, DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, + [GraphQLType] string? locale, OpticalDataByIdDataLoader byId, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index 6ff46c5d..8ae7499f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -8,7 +8,7 @@ namespace Database.GraphQl.PhotovoltaicDataX public record CreatePhotovoltaicDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? - [GraphQLType(typeof(NonNullType))] string Locale, + [GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index a048f9ce..5d23b45e 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -20,7 +20,7 @@ public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, - [ScopedService] Data.ApplicationDbContext context, + Data.ApplicationDbContext context, [Service] AppSettings appSettings, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index b2f1bcec..f7e2f2ff 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -19,8 +19,8 @@ public sealed class PhotovoltaicDataQueries [UseSorting] public IQueryable GetAllPhotovoltaicData( DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, - [ScopedService] Data.ApplicationDbContext context + [GraphQLType] string? locale, + Data.ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. @@ -30,7 +30,7 @@ [ScopedService] Data.ApplicationDbContext context public Task GetPhotovoltaicDataAsync( Guid id, DateTime? timestamp, - [GraphQLType(typeof(LocaleType))] string? locale, + [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, CancellationToken cancellationToken ) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 4e79c8ec..a947b679 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -88,9 +88,7 @@ IServiceProvider services { try { - using var dbContext = - services.GetRequiredService>() - .CreateDbContext(); + using var dbContext = services.GetRequiredService(); if (dbContext.Database.EnsureCreated()) { await Data.DbSeeder.DoAsync(services).ConfigureAwait(false); diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index e88ea089..34391ff1 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -12,8 +12,8 @@ interface Data { description: String locale: Locale! name: String - resourceTree: GetHttpsResourceTree! resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! timestamp: DateTime! uuid: Uuid! warnings: [String!]! @@ -41,11 +41,11 @@ type CalorimetricData implements Node & Data { id: ID! locale: Locale! name: String - resourceTree: GetHttpsResourceTree! resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! timestamp: DateTime! - uValues: [Float!]! uuid: Uuid! + uValues: [Float!]! warnings: [String!]! } @@ -57,6 +57,7 @@ type CalorimetricDataConnection { nodes: [CalorimetricData!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -135,10 +136,10 @@ type CreatePhotovoltaicDataPayload { } type CrossDatabaseDataReference { + databaseId: Uuid! dataId: Uuid! dataKind: DataKind! dataTimestamp: DateTime! - databaseId: Uuid! } type DataApproval { @@ -158,6 +159,7 @@ type DataConnection { nodes: [Data!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -171,7 +173,6 @@ type DataEdge { type FileMetaInformation { dataFormatId: Uuid! - formatId: Uuid! path: [String!]! } @@ -182,7 +183,6 @@ type GetHttpsResource implements Node { data: Data dataFormatId: Uuid! description: String! - formatId: Uuid! hashValue: String! id: ID! locator: Url! @@ -198,6 +198,7 @@ type GetHttpsResourceConnection { nodes: [GetHttpsResource!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -236,8 +237,8 @@ type HygrothermalData implements Node & Data { id: ID! locale: Locale! name: String - resourceTree: GetHttpsResourceTree! resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! timestamp: DateTime! uuid: Uuid! warnings: [String!]! @@ -251,6 +252,7 @@ type HygrothermalDataConnection { nodes: [HygrothermalData!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -263,19 +265,11 @@ type HygrothermalDataEdge { } type Mutation { - createCalorimetricData( - input: CreateCalorimetricDataInput! - ): CreateCalorimetricDataPayload! - createGetHttpsResource( - input: CreateGetHttpsResourceInput! - ): CreateGetHttpsResourcePayload! - createHygrothermalData( - input: CreateHygrothermalDataInput! - ): CreateHygrothermalDataPayload! + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! - createPhotovoltaicData( - input: CreatePhotovoltaicDataInput! - ): CreatePhotovoltaicDataPayload! + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! } type NamedMethodArgument { @@ -310,8 +304,8 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resourceTree: GetHttpsResourceTree! resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! timestamp: DateTime! uuid: Uuid! warnings: [String!]! @@ -325,6 +319,7 @@ type OpticalDataConnection { nodes: [OpticalData!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -358,8 +353,8 @@ type PhotovoltaicData implements Node & Data { id: ID! locale: Locale! name: String - resourceTree: GetHttpsResourceTree! resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! timestamp: DateTime! uuid: Uuid! warnings: [String!]! @@ -373,6 +368,7 @@ type PhotovoltaicDataConnection { nodes: [PhotovoltaicData!] "Information to aid in pagination." pageInfo: PageInfo! + "Identifies the total count of items in the connection." totalCount: Int! } @@ -385,110 +381,22 @@ type PhotovoltaicDataEdge { } type Query { - allCalorimetricData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [CalorimetricDataSortInput!] - timestamp: DateTime - where: CalorimetricDataPropositionInput - ): CalorimetricDataConnection - allData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [IDataSortInput!] - timestamp: DateTime - where: DataPropositionInput - ): DataConnection - allHygrothermalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [HygrothermalDataSortInput!] - timestamp: DateTime - where: HygrothermalDataPropositionInput - ): HygrothermalDataConnection - allOpticalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [OpticalDataSortInput!] - timestamp: DateTime - where: OpticalDataPropositionInput - ): OpticalDataConnection - allPhotovoltaicData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [PhotovoltaicDataSortInput!] - timestamp: DateTime - where: PhotovoltaicDataPropositionInput - ): PhotovoltaicDataConnection - calorimetricData( - id: Uuid! - locale: Locale - timestamp: DateTime - ): CalorimetricData - data(id: Uuid!, locale: Locale, timestamp: DateTime): Data + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] timestamp: DateTime where: CalorimetricDataPropositionInput): CalorimetricDataConnection + allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] timestamp: DateTime where: DataPropositionInput): DataConnection + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] timestamp: DateTime where: HygrothermalDataPropositionInput): HygrothermalDataConnection + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] timestamp: DateTime where: OpticalDataPropositionInput): OpticalDataConnection + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection + calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData + data(id: Uuid! locale: Locale timestamp: DateTime): Data getHttpsResource(id: Uuid!): GetHttpsResource - getHttpsResources( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - order: [GetHttpsResourceSortInput!] - where: GetHttpsResourcePropositionInput - ): GetHttpsResourceConnection - hygrothermalData( - id: Uuid! - locale: Locale - timestamp: DateTime - ): HygrothermalData + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput): GetHttpsResourceConnection + hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData "Fetches an object given its ID." node("ID of the object." id: ID!): Node "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! - opticalData(id: Uuid!, locale: Locale, timestamp: DateTime): OpticalData - photovoltaicData( - id: Uuid! - locale: Locale - timestamp: DateTime - ): PhotovoltaicData + opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData + photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData } type ToTreeVertexAppliedConversionMethod { @@ -542,7 +450,7 @@ input CalorimetricDataSortInput { createdAt: SortEnumType appliedMethod: AppliedMethodSortInput id: SortEnumType - xmin: SortEnumType + version: SortEnumType } input CielabColorInput { @@ -637,10 +545,10 @@ input CreatePhotovoltaicDataInput { } input CrossDatabaseDataReferenceInput { + databaseId: Uuid! dataId: Uuid! dataKind: DataKind! dataTimestamp: DateTime! - databaseId: Uuid! } input CrossDatabaseDataReferencePropositionInput { @@ -717,7 +625,6 @@ input FileMetaInformationPropositionInput { or: [FileMetaInformationPropositionInput!] path: StringsPropositionInput dataFormatId: UuidPropositionInput - formatId: UuidPropositionInput } input FloatPropositionInput { @@ -758,7 +665,6 @@ input GetHttpsResourceSortInput { description: SortEnumType hashValue: SortEnumType dataFormatId: SortEnumType - formatId: SortEnumType dataId: SortEnumType data: IDataSortInput calorimetricDataId: SortEnumType @@ -773,7 +679,7 @@ input GetHttpsResourceSortInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput parent: GetHttpsResourceSortInput id: SortEnumType - xmin: SortEnumType + version: SortEnumType } input HygrothermalDataPropositionInput { @@ -801,7 +707,7 @@ input HygrothermalDataSortInput { createdAt: SortEnumType appliedMethod: AppliedMethodSortInput id: SortEnumType - xmin: SortEnumType + version: SortEnumType } input IDataSortInput { @@ -858,6 +764,7 @@ input ListNamedMethodArgumentFilterTypePropositionInput { input NamedMethodArgumentInput { name: String! + value: Any! } input NamedMethodArgumentPropositionInput { @@ -923,7 +830,7 @@ input OpticalDataSortInput { createdAt: SortEnumType appliedMethod: AppliedMethodSortInput id: SortEnumType - xmin: SortEnumType + version: SortEnumType } input PhotovoltaicDataPropositionInput { @@ -951,7 +858,7 @@ input PhotovoltaicDataSortInput { createdAt: SortEnumType appliedMethod: AppliedMethodSortInput id: SortEnumType - xmin: SortEnumType + version: SortEnumType } input RootGetHttpsResourceInput { @@ -1019,6 +926,7 @@ input UuidPropositionInput { enum ApplyPolicy { BEFORE_RESOLVER AFTER_RESOLVER + VALIDATION } enum CreateCalorimetricDataErrorCode { @@ -1058,47 +966,19 @@ enum SortEnumType { DESC } -directive @authorize( - "Defines when when the resolver shall be executed.By default the resolver is executed after the policy has determined that the current user is allowed to access the field." - apply: ApplyPolicy! = BEFORE_RESOLVER - "The name of the authorization policy that determines access to the annotated resource." - policy: String - "Roles that are allowed to access the annotated resource." - roles: [String!] -) repeatable on SCHEMA | OBJECT | FIELD_DEFINITION - -"The `@defer` directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`." -directive @defer( - "Deferred when true." - if: Boolean - "If this argument label has a value other than null, it will be passed on to the result of this defer directive. This label is intended to give client applications a way to identify to which fragment a deferred result belongs to." - label: String -) on FRAGMENT_SPREAD | INLINE_FRAGMENT +directive @authorize("Defines when when the authorize directive shall be applied.By default the authorize directives are applied during the validation phase." apply: ApplyPolicy! = VALIDATION "The name of the authorization policy that determines access to the annotated resource." policy: String "Roles that are allowed to access the annotated resource." roles: [String!]) repeatable on OBJECT | FIELD_DEFINITION "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy( - "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." - url: String! -) on SCALAR - -"The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`." -directive @stream( - "Streamed when true." - if: Boolean - "The initial elements that shall be send down to the consumer." - initialCount: Int! = 0 - "If this argument label has a value other than null, it will be passed on to the result of this stream directive. This label is intended to give client applications a way to identify to which fragment a streamed result belongs to." - label: String -) on FIELD +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "BCP 47 compliant Language Tag string" scalar Locale scalar Url -scalar Uuid +scalar Uuid \ No newline at end of file From 997c2dcbcdb40932c178c362838ea45b09ccbea7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Feb 2023 17:39:15 +0100 Subject: [PATCH 064/878] Reference documentation on service registration --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 46f3c898..211dde66 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -24,7 +24,7 @@ IWebHostEnvironment environment ) { services.AddGraphQLServer() - // Database + // Services https://chillicream.com/docs/hotchocolate/v13/server/dependency-injection#registerservice .RegisterDbContext(DbContextKind.Pooled) // Types .AddType() From ff1c5d0557e0719ab0564b41e01a155b3271d759 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Feb 2023 17:56:50 +0100 Subject: [PATCH 065/878] Use database context factory wherever possible --- backend/src/Program.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index a947b679..4e79c8ec 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -88,7 +88,9 @@ IServiceProvider services { try { - using var dbContext = services.GetRequiredService(); + using var dbContext = + services.GetRequiredService>() + .CreateDbContext(); if (dbContext.Database.EnsureCreated()) { await Data.DbSeeder.DoAsync(services).ConfigureAwait(false); From 48909670971ad587cc2d1f010b6f133b608a9e93 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Mar 2023 17:09:34 +0100 Subject: [PATCH 066/878] Update backend dependencies and tools --- backend/dotnet-tools.json | 4 ++-- backend/src/Database.csproj | 12 ++++++------ backend/test/Database.Tests.csproj | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 36aa29cb..3e3fa9cb 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -69,13 +69,13 @@ ] }, "dotnet-project-licenses": { - "version": "2.6.0", + "version": "2.7.0", "commands": [ "dotnet-project-licenses" ] }, "strawberryshake.tools": { - "version": "13.0.2", + "version": "13.0.5", "commands": [ "dotnet-graphql" ] diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index de39a514..238e8a88 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -19,7 +19,7 @@ - + @@ -32,11 +32,11 @@ - - - - - + + + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 7437ef71..f7596ad4 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -11,12 +11,12 @@ - + - + - + From 982e2c39410b2b170fddbd4fa60167b1401032f4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Mar 2023 17:26:32 +0100 Subject: [PATCH 067/878] Remove unnecessary attribute of `UseDbContext` --- .../src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs | 1 - backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs | 1 - backend/src/GraphQl/DataX/DataQueries.cs | 1 - .../src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs | 1 - backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs | 1 - .../src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs | 1 - backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs | 1 - backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs | 1 - backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 1 - .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs | 1 - backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 1 - 11 files changed, 11 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index b1fc6d50..dec3666e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -13,7 +13,6 @@ namespace Database.GraphQl.CalorimetricDataX [ExtendObjectType(nameof(Mutation))] public sealed class CalorimetricDataMutations { - [UseDbContext(typeof(Data.ApplicationDbContext))] // [UseUserManager] // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateCalorimetricDataAsync( diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index cf0e9d21..bff93e9b 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -12,7 +12,6 @@ namespace Database.GraphQl.CalorimetricDataX [ExtendObjectType(nameof(Query))] public sealed class CalorimetricDataQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 92e3fe04..f40ecf57 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -17,7 +17,6 @@ namespace Database.GraphQl.DataX [ExtendObjectType(nameof(Query))] public sealed class DataQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] // TODO Filtering does not work with unions. diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 49dfb7ce..72fffc71 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -13,7 +13,6 @@ namespace Database.GraphQl.GetHttpsResources [ExtendObjectType(nameof(Mutation))] public sealed class GetHttpsResourceMutations { - [UseDbContext(typeof(Data.ApplicationDbContext))] // [UseUserManager] // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGetHttpsResourceAsync( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 27bb6cf3..aadcb61b 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -11,7 +11,6 @@ namespace Database.GraphQl.GetHttpsResources [ExtendObjectType(nameof(Query))] public sealed class GetHttpsResourceQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index c07db715..df0f1e12 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -13,7 +13,6 @@ namespace Database.GraphQl.HygrothermalDataX [ExtendObjectType(nameof(Mutation))] public sealed class HygrothermalDataMutations { - [UseDbContext(typeof(Data.ApplicationDbContext))] // [UseUserManager] // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateHygrothermalDataAsync( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 1b60e2ff..51897701 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -12,7 +12,6 @@ namespace Database.GraphQl.HygrothermalDataX [ExtendObjectType(nameof(Query))] public sealed class HygrothermalDataQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index f76b3b8b..12413a6f 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -13,7 +13,6 @@ namespace Database.GraphQl.OpticalDataX [ExtendObjectType(nameof(Mutation))] public sealed class OpticalDataMutations { - [UseDbContext(typeof(Data.ApplicationDbContext))] // [UseUserManager] // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateOpticalDataAsync( diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 57f2bd83..8850d795 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -12,7 +12,6 @@ namespace Database.GraphQl.OpticalDataX [ExtendObjectType(nameof(Query))] public sealed class OpticalDataQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 5d23b45e..62ddd73c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -13,7 +13,6 @@ namespace Database.GraphQl.PhotovoltaicDataX [ExtendObjectType(nameof(Mutation))] public sealed class PhotovoltaicDataMutations { - [UseDbContext(typeof(Data.ApplicationDbContext))] // [UseUserManager] // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreatePhotovoltaicDataAsync( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index f7e2f2ff..0a248d50 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -12,7 +12,6 @@ namespace Database.GraphQl.PhotovoltaicDataX [ExtendObjectType(nameof(Query))] public sealed class PhotovoltaicDataQueries { - [UseDbContext(typeof(Data.ApplicationDbContext))] [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] From f1f7a73f3001bc89d429b103b360ccd36ea32e0d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Apr 2023 17:16:33 +0200 Subject: [PATCH 068/878] Enable automatic persisted queries as elaborated on in https://chillicream.com/docs/hotchocolate/v13/performance/automatic-persisted-queries and https://www.apollographql.com/docs/apollo-server/performance/apq/ --- .../src/Configuration/GraphQlConfiguration.cs | 11 ++-- backend/src/Database.csproj | 1 + frontend/lib/apollo.ts | 51 +++++++++++-------- frontend/package.json | 1 + frontend/yarn.lock | 5 ++ 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 211dde66..bb2bdbf3 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -23,8 +23,11 @@ public static void ConfigureServices( IWebHostEnvironment environment ) { - services.AddGraphQLServer() - // Services https://chillicream.com/docs/hotchocolate/v13/server/dependency-injection#registerservice + services + .AddMemoryCache() + .AddSha256DocumentHashProvider(HotChocolate.Language.HashFormat.Hex) + .AddGraphQLServer() + // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContext(DbContextKind.Pooled) // Types .AddType() @@ -120,7 +123,9 @@ IWebHostEnvironment environment // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. InferConnectionNameFromField = false } - ); + ) + .UseAutomaticPersistedQueryPipeline() + .AddInMemoryQueryStorage(); } } diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 238e8a88..56a23ee5 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -19,6 +19,7 @@ + diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 087519fa..b1da91ae 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -7,8 +7,10 @@ import { InMemoryCache, NormalizedCacheObject, } from "@apollo/client"; +import { createPersistedQueryLink } from "@apollo/client/link/persisted-queries"; import merge from "deepmerge"; import { message } from "antd"; +import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin let apolloClient: ApolloClient | undefined; @@ -19,28 +21,33 @@ export type ResolverContext = { }; function createIsomorphLink(context: ResolverContext = {}) { - return createHttpLink({ - uri: - // In the case `typeof window === "undefined"`, that is on the server - // side, because we require HTTPS for the JWT bearer token, we cannot use - // "http://backend:8080/graphql/" to make local requests. When the reverse - // proxy NGINX serves the certificate like in development we could use - // "https://nginx:443/graphql/" to make local requests, which this does - // however not work in production. Thus, on the server side we also use - // `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` as on the client - // side. - typeof window === "undefined" - ? `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` - : `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/`, - useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors - credentials: "same-origin", - headers: context.req?.headers?.cookie - ? { - accept: "application/json", - cookie: context.req.headers.cookie, - } - : { accept: "application/json" }, - }); + return createPersistedQueryLink({ + sha256, + useGETForHashedQueries: false, + }).concat( + createHttpLink({ + uri: + // In the case `typeof window === "undefined"`, that is on the server + // side, because we require HTTPS for the JWT bearer token, we cannot use + // "http://backend:8080/graphql/" to make local requests. When the reverse + // proxy NGINX serves the certificate like in development we could use + // "https://nginx:443/graphql/" to make local requests, which this does + // however not work in production. Thus, on the server side we also use + // `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` as on the client + // side. + typeof window === "undefined" + ? `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` + : `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/`, + useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors + credentials: "same-origin", + headers: context.req?.headers?.cookie + ? { + accept: "application/json", + cookie: context.req.headers.cookie, + } + : { accept: "application/json" }, + }) + ); } function createApolloClient(context?: ResolverContext) { diff --git a/frontend/package.json b/frontend/package.json index 0fdfd75b..7337f13b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,7 @@ "dependencies": { "@apollo/client": "^3.7.0", "antd": "^5.0", + "crypto-hash": "^2.0.1", "dayjs": "^1.0", "graphql": "^16.0.1", "next": "^13.1.4", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 75cf1f19..ca93ef97 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3006,6 +3006,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-hash@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-2.0.1.tgz#46c3732e65a078ea06b8b4ae686db41216f81213" + integrity sha512-t4mkp7Vh6MuCZRBf0XLzBOfhkH3nW6YEAotMDSjshVQ1GffCMGdPLSr7pKH0rdXY02jTjAZ7QW2apD0buaZXcQ== + csstype@^3.0.10, csstype@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" From 1b9baf5e37d45dcc5ad4d2f0983c14ede0ef41a0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Apr 2023 18:08:34 +0200 Subject: [PATCH 069/878] Upgrade backend dependencies and tools --- backend/dotnet-tools.json | 8 ++++---- backend/src/Database.csproj | 12 ++++++------ backend/test/Database.Tests.csproj | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 3e3fa9cb..5b38f0fe 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "7.0.4", + "version": "7.0.6", "commands": [ "dotnet-aspnet-codegenerator" ] @@ -21,7 +21,7 @@ ] }, "dotnet-ef": { - "version": "7.0.3", + "version": "7.0.5", "commands": [ "dotnet-ef" ] @@ -63,13 +63,13 @@ ] }, "jetbrains.resharper.globaltools": { - "version": "2022.3.2", + "version": "2023.1.0", "commands": [ "jb" ] }, "dotnet-project-licenses": { - "version": "2.7.0", + "version": "2.7.1", "commands": [ "dotnet-project-licenses" ] diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 56a23ee5..258131e1 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -21,13 +21,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -38,14 +38,14 @@ - + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index f7596ad4..1f03fe57 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -11,11 +11,11 @@ - + - + From 77f1559eb33c0efed41d3d458b149fb9b16354d7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Apr 2023 18:08:45 +0200 Subject: [PATCH 070/878] Upgrade typescript from version 4 to 5 --- frontend/package.json | 2 +- frontend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 7337f13b..e19ccc7d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -54,7 +54,7 @@ "prettier": "^2.8.4", "react-test-renderer": "^18.0", "ts-node": "^10.9.1", - "typescript": "^4.0", + "typescript": "^5.0.4", "yaml-loader": "^0.8" }, "browserslist": { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ca93ef97..ebd054c3 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -7144,10 +7144,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.0: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== ua-parser-js@^0.7.30: version "0.7.33" From c77563cdb1adcd805c43f869070e8cce2884c174 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Apr 2023 18:11:31 +0200 Subject: [PATCH 071/878] Upgrade frontend packages to latest patch versions --- frontend/yarn.lock | 2062 +++++++++++++++++++++++--------------------- 1 file changed, 1060 insertions(+), 1002 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ebd054c3..da193c4e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,12 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== +"@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== dependencies: - "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" "@ant-design/colors@^7.0.0": @@ -58,9 +58,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^3.7.0": - version "3.7.8" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.8.tgz#e1c8dfd02cbbe1baf9b18fa86918904efd9cc580" - integrity sha512-o1NxF4ytET2w9HSVMLwYUEEdv6H3XPpbh9M+ABVGnUVT0s6T9pgqRtYO4pFP1TmeDmb1pbRfVhFwh3gC167j5Q== + version "3.7.11" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.11.tgz#fbe9ac3cd508123aacae370dfb3d268fd74f3a44" + integrity sha512-uLg2KtxoAyj9ta7abLxXx8cGRM7HypCkXVmxtL7Ko//N5g37aoJ3ca7VYoFCMUFO1BXBulj+yKVl0U3+ILj5AQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" @@ -68,7 +68,7 @@ "@wry/trie" "^0.3.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.16.1" + optimism "^0.16.2" prop-types "^15.7.2" response-iterator "^0.2.6" symbol-observable "^4.0.0" @@ -113,17 +113,24 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/code-frame@^7.14.5": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.20.5": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.14.tgz#4106fc8b755f3e3ee0a0a7c27dde5de1d2b2baf8" - integrity sha512-0YpKHD6ImkWMEINCyDAD0HLLUH/lPCefG8ld9it8DJB2wnApraKuhgYTvTY1z7UFIfBTGy5LwncZ+5HWWGbhFw== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@7.15.5": version "7.15.5" @@ -146,7 +153,28 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": +"@babel/core@^7.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/core@^7.1.0": version "7.20.12" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== @@ -167,7 +195,17 @@ json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.20.7", "@babel/generator@^7.7.2": +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.20.7", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + dependencies: + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/generator@^7.15.4": version "7.20.14" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== @@ -183,7 +221,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": +"@babel/helper-compilation-targets@^7.15.4": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== @@ -194,15 +232,26 @@ lru-cache "^5.1.1" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== + dependencies: + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz#4349b928e79be05ed2d1643b20b99bb87c503819" - integrity sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" + integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-member-expression-to-functions" "^7.20.7" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-replace-supers" "^7.20.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" @@ -213,7 +262,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0": +"@babel/helper-function-name@^7.15.4": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== @@ -221,6 +270,14 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + "@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" @@ -228,21 +285,21 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz#a6f26e919582275a93c3aa6594756d71b0bb7f05" - integrity sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw== +"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" + integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== dependencies: - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.20.11": +"@babel/helper-module-transforms@^7.15.4": version "7.20.11" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== @@ -256,6 +313,20 @@ "@babel/traverse" "^7.20.10" "@babel/types" "^7.20.7" +"@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" + integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.2" + "@babel/types" "^7.21.2" + "@babel/helper-optimise-call-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" @@ -316,12 +387,12 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== +"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== -"@babel/helpers@^7.15.4", "@babel/helpers@^7.20.7": +"@babel/helpers@^7.15.4": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== @@ -330,6 +401,15 @@ "@babel/traverse" "^7.20.13" "@babel/types" "^7.20.7" +"@babel/helpers@^7.20.7", "@babel/helpers@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" + integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.0" + "@babel/types" "^7.21.0" + "@babel/highlight@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" @@ -344,7 +424,12 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== + +"@babel/parser@^7.15.4", "@babel/parser@^7.15.5": version "7.20.15" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== @@ -390,11 +475,11 @@ "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" - integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" + integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-import-assertions@7.20.0": version "7.20.0" @@ -418,11 +503,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" + integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -474,11 +559,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.20.7" @@ -495,21 +580,21 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.15.tgz#3e1b2aa9cbbe1eb8d644c823141a9c5c2a22392d" - integrity sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-classes@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz#f438216f094f6bb31dc266ebfab8ff05aecad073" - integrity sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-compilation-targets" "^7.20.7" "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" + "@babel/helper-function-name" "^7.21.0" "@babel/helper-optimise-call-expression" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-replace-supers" "^7.20.7" @@ -525,26 +610,26 @@ "@babel/template" "^7.20.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz#8bda578f71620c7de7c93af590154ba331415454" - integrity sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.19.0.tgz#e9e8606633287488216028719638cbbb2f2dde8f" - integrity sha512-sgeMlNaQVbCSpgLSKP4ZZKfsJVnFnNQlUSk6gPYzR/q7tzCgQF2t8RBKAP6cKJeZdveei7Q7Jm527xepI8lNLg== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" + integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-flow" "^7.18.6" "@babel/plugin-transform-for-of@^7.0.0": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" - integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" + integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-function-name@^7.0.0": version "7.18.9" @@ -570,11 +655,11 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz#8cb23010869bf7669fd4b3098598b6b2be6dc607" - integrity sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw== + version "7.21.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" + integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== dependencies: - "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-module-transforms" "^7.21.2" "@babel/helper-plugin-utils" "^7.20.2" "@babel/helper-simple-access" "^7.20.2" @@ -587,9 +672,9 @@ "@babel/helper-replace-supers" "^7.18.6" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz#0ee349e9d1bc96e78e3b37a7af423a4078a7083f" - integrity sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA== + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -608,15 +693,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.20.13.tgz#f950f0b0c36377503d29a712f16287cedf886cbb" - integrity sha512-MmTZx/bkUrfJhhYAYt3Urjm+h8DQGrPrnKQ94jLo7NLuOU+T89a7IByhKmrb8SKhrIYIQ0FN0CHMbnFRen4qNw== + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" + integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== dependencies: "@babel/helper-annotate-as-pure" "^7.18.6" "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.20.7" + "@babel/types" "^7.21.0" "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.18.6" @@ -640,7 +725,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== @@ -671,7 +763,23 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.15.4": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== @@ -687,7 +795,16 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@babel/types@^7.15.4": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== @@ -723,14 +840,26 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint/eslintrc@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" - integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.4.0" + espree "^9.5.1" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -738,6 +867,11 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@eslint/js@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" + integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== + "@graphql-codegen/add@^3.2.1": version "3.2.3" resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.2.3.tgz#f1ecee085987e7c21841edc4b1fd48877c663e1a" @@ -747,15 +881,15 @@ tslib "~2.4.0" "@graphql-codegen/cli@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.0.0.tgz#eb367adfe51349e4822518183fc7ea7c06aea11b" - integrity sha512-16nuFabHCfPQ/d+v52OvR1ueL8eiJvS/nRuvuEV8d9T1fkborHKRw4lhyKVebu9izFBs6G0CvVCLhgVzQwHSLw== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.3.0.tgz#12755b6b20b5a24eca7514a6fa00507b61406bef" + integrity sha512-KRQoAG0stor/ztMsBxHqxnwQzcM1DEZigwzah1LFej8bh6TT1WpUt37zt6PH0EdlMpelmeN3keY4Pq6GMd4ADw== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/core" "^3.0.0" - "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/core" "^3.1.0" + "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-tools/apollo-engine-loader" "^7.3.6" "@graphql-tools/code-file-loader" "^7.3.17" "@graphql-tools/git-loader" "^7.2.13" @@ -766,44 +900,44 @@ "@graphql-tools/prisma-loader" "^7.2.49" "@graphql-tools/url-loader" "^7.13.2" "@graphql-tools/utils" "^9.0.0" - "@whatwg-node/fetch" "^0.6.0" + "@parcel/watcher" "^2.1.0" + "@whatwg-node/fetch" "^0.8.0" chalk "^4.1.0" - chokidar "^3.5.2" cosmiconfig "^7.0.0" - cosmiconfig-typescript-loader "^4.3.0" debounce "^1.2.0" detect-indent "^6.0.0" - graphql-config "^4.4.0" + graphql-config "^4.5.0" inquirer "^8.0.0" is-glob "^4.0.1" + jiti "^1.17.1" json-to-pretty-yaml "^1.2.2" listr2 "^4.0.5" log-symbols "^4.0.0" + micromatch "^4.0.5" shell-quote "^1.7.3" string-env-interpolation "^1.0.1" ts-log "^2.2.3" - ts-node "^10.9.1" tslib "^2.4.0" yaml "^1.10.0" yargs "^17.0.0" -"@graphql-codegen/core@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-3.0.0.tgz#ff6d643bb9d80c54ddd9ee5c68194888c971ae45" - integrity sha512-WUfAUTmUcgeHPR7F5ZQqaBqJLJb5+3Lvp6v9SrnupKOFed+Q3u8CvZL6sPTvDpqqW8Ucjy59DEZqumPLp99pdQ== +"@graphql-codegen/core@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-3.1.0.tgz#ad859d52d509a4eb2ebe5aabba6543a628fb181b" + integrity sha512-DH1/yaR7oJE6/B+c6ZF2Tbdh7LixF1K8L+8BoSubjNyQ8pNwR4a70mvc1sv6H7qgp6y1bPQ9tKE+aazRRshysw== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-tools/schema" "^9.0.0" "@graphql-tools/utils" "^9.1.1" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-codegen/fragment-matcher@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-4.0.0.tgz#2908d0107d5ef23e8931cf5760bd42e9a007cdf1" - integrity sha512-pkVrFjosh/uPY3x9208AyoCVYUDoMvkcLb2ytMCS0KbMVjRFFpnsIa38HPVGIzlGkwZBEwh5wTH4sJ4FiFpGBw== + version "4.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-4.0.1.tgz#56f53759f1c1428e60eca18bdbda071fd8b858f5" + integrity sha512-m1jjCBppB+QXupYu8eVM1BZM36APPArHEVB8R0v29Vj2Kz/35+pe682PyQaf7iyxTkemW759h2KIvWFtXdLCJQ== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" - tslib "~2.4.0" + "@graphql-codegen/plugin-helpers" "^4.1.0" + tslib "~2.5.0" "@graphql-codegen/import-types-preset@^2.0.0": version "2.2.6" @@ -850,37 +984,37 @@ lodash "~4.17.0" tslib "~2.4.0" -"@graphql-codegen/plugin-helpers@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.0.0.tgz#9c10e4700dc6efe657781dff47347d0c99674061" - integrity sha512-vgNGTanT36hC4RAC/LAThMEjDvnu3WCyx6MtKZcPUtfCWFxbUAr88+OarGl1LAEiOef0agIREC7tIBXCqjKkJA== +"@graphql-codegen/plugin-helpers@^4.0.0", "@graphql-codegen/plugin-helpers@^4.1.0", "@graphql-codegen/plugin-helpers@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz#8324914d0f99162a223cfa01796cdd6be972d2ae" + integrity sha512-THFTCfg+46PXlXobYJ/OoCX6pzjI+9woQqCjdyKtgoI0tn3Xq2HUUCiidndxUpEYVrXb5pRiRXb7b/ZbMQqD0A== dependencies: "@graphql-tools/utils" "^9.0.0" change-case-all "1.0.15" common-tags "1.8.2" import-from "4.0.0" lodash "~4.17.0" - tslib "~2.4.0" + tslib "~2.5.0" -"@graphql-codegen/schema-ast@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-3.0.0.tgz#3311a58184f885853d33d8272c527ff5bdf3023b" - integrity sha512-5gC8nNk/bxufS2LBSpaPExRgn6eNo8LQdtwDtwfM9XGEzt/F6rIBQoyOmqqwkiBmgu1PHHH8kLZMBYvYB1x5DA== +"@graphql-codegen/schema-ast@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-3.0.1.tgz#37b458bb57b95715a9eb4259341c856dae2a461d" + integrity sha512-rTKTi4XiW4QFZnrEqetpiYEWVsOFNoiR/v3rY9mFSttXFbIwNXPme32EspTiGWmEEdHY8UuTDtZN3vEcs/31zw== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/plugin-helpers" "^4.1.0" "@graphql-tools/utils" "^9.0.0" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-codegen/typescript-operations@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.0.tgz#e67c9dedb739a20f8a1d2c9a9631701c269de0c1" - integrity sha512-t+Lk+lxkUFDh6F0t8CErowOccP3bZwxhl66qmEeBcOrC7jQrSCnRZoFvOXhFKFBJe/y4DIJiizgSr34AqjiJIQ== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.3.tgz#35b2dabd7d7d243ad7be7edccc605826aa305a9d" + integrity sha512-RtMRFpZ8nyXxwurdxklWVIHX9U6/fGWYWEpvarNIIci17BKajZPzdBPKP2GKhUEWzZVGNESqH3RX38jt0onLqQ== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" - "@graphql-codegen/typescript" "^3.0.0" - "@graphql-codegen/visitor-plugin-common" "3.0.0" + "@graphql-codegen/plugin-helpers" "^4.2.0" + "@graphql-codegen/typescript" "^3.0.3" + "@graphql-codegen/visitor-plugin-common" "3.1.0" auto-bind "~4.0.0" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-codegen/typescript-react-apollo@^3.0.0": version "3.3.7" @@ -906,16 +1040,16 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.0.0.tgz#69856b9eb003660b7c7551727a63764b8a6c89ee" - integrity sha512-yfRNJF9ZUoiLSPhaBEpQ9E4ymExYzbsKgjU2qJ/0c8l0lViA+o4ALkJMTwb7yU/yTayQtQWgxL5cdDY2I12GpQ== + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.2.0.tgz#d4e7892208d9774059610753003c6a5d4c543b01" + integrity sha512-119zDHVb/DYuPW76VYRwYAVRFABHpMN0OZgXe/g2PIHlb/Vlfof4TLjrKkRM7iBFbZPWGL92jqiI8hWFHhpqdg== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" - "@graphql-codegen/typescript" "^3.0.0" - "@graphql-codegen/visitor-plugin-common" "3.0.0" + "@graphql-codegen/plugin-helpers" "^4.2.0" + "@graphql-codegen/typescript" "^3.0.3" + "@graphql-codegen/visitor-plugin-common" "3.1.0" "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-codegen/typescript@^1.22.0": version "1.23.0" @@ -927,16 +1061,16 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.0.tgz#473dde1646540039bca5db4b6daf174d13af0ce3" - integrity sha512-FQWyuIUy1y+fxb9+EZfvdBHBQpYExlIBHV5sg2WGNCsyVyCqBTl0mO8icyOtsQPVg6YFMFe8JJO69vQbwHma5w== +"@graphql-codegen/typescript@^3.0.0", "@graphql-codegen/typescript@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.3.tgz#3732e2072ba27970006db14b22d412ac4c943628" + integrity sha512-oVV4/RPs3Lmr2XqywlNDmLhNBCIhefA8rYcuxoWcnd/WRc6GNRm1b2NOIzDhhF4xriVluMyk3QQVRepkdj9enw== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" - "@graphql-codegen/schema-ast" "^3.0.0" - "@graphql-codegen/visitor-plugin-common" "3.0.0" + "@graphql-codegen/plugin-helpers" "^4.2.0" + "@graphql-codegen/schema-ast" "^3.0.1" + "@graphql-codegen/visitor-plugin-common" "3.1.0" auto-bind "~4.0.0" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-codegen/visitor-plugin-common@1.22.0", "@graphql-codegen/visitor-plugin-common@^1.20.0": version "1.22.0" @@ -970,12 +1104,12 @@ parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.0.0.tgz#527185eb3b1b06739702084bc6263713e167a166" - integrity sha512-ZoNlCmmkGClB137SpJT9og/nkihLN7Z4Ynl9Ir3OlbDuI20dbpyXsclpr9QGLcxEcfQeVfhGw9CooW7wZJJ8LA== +"@graphql-codegen/visitor-plugin-common@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.0.tgz#93172d64440b103edfe8b63ea3ac8842a5277a54" + integrity sha512-S4BO/xP38eoEDULwbem5QeV7yWA8DpbcSGkvwrnMfVQW08nL6uHliCSV8hiuAi7Mhx7snVir4Iumk8uIDRVu6Q== dependencies: - "@graphql-codegen/plugin-helpers" "^4.0.0" + "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-tools/optimize" "^1.3.0" "@graphql-tools/relay-operation-optimizer" "^6.5.0" "@graphql-tools/utils" "^9.0.0" @@ -984,7 +1118,7 @@ dependency-graph "^0.11.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" - tslib "~2.4.0" + tslib "~2.5.0" "@graphql-tools/apollo-engine-loader@^7.3.6": version "7.3.26" @@ -996,7 +1130,7 @@ "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@8.5.18": +"@graphql-tools/batch-execute@^8.5.18": version "8.5.18" resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.18.tgz#2f0e91cc12e8eed32f14bc814f27c6a498b75e17" integrity sha512-mNv5bpZMLLwhkmPA6+RP81A6u3KF4CSKLf3VX9hbomOkQR4db8pNs8BOvpZU54wKsUzMzdlws/2g/Dabyb2Vsg== @@ -1017,33 +1151,33 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@9.0.27", "@graphql-tools/delegate@^9.0.27": - version "9.0.27" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.27.tgz#e500554bace46cc7ededd48a0c28079f747c9f49" - integrity sha512-goYewiPls/RDXiRTl1S2tRPlsyDQCxlDWqd0uEIzQZ6aWSyiutfwQnTzdbZPXK0qOblEVMIqFhSGrB6fp0OkBA== +"@graphql-tools/delegate@9.0.30", "@graphql-tools/delegate@^9.0.30": + version "9.0.30" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.30.tgz#d91b0b61b91491e6434b1738ed831e89a4e228be" + integrity sha512-Q4xmpmFcPPrcrR6hZRmoinq9vPR38w26YKu8otKC6TgGpxxk7IBNe6bnvB9h0l05bs8nhDcYZBiLerqtYUFy8Q== dependencies: - "@graphql-tools/batch-execute" "8.5.18" - "@graphql-tools/executor" "0.0.14" - "@graphql-tools/schema" "9.0.16" - "@graphql-tools/utils" "9.2.1" - dataloader "2.2.2" - tslib "~2.5.0" - value-or-promise "1.0.12" + "@graphql-tools/batch-execute" "^8.5.18" + "@graphql-tools/executor" "^0.0.16" + "@graphql-tools/schema" "^9.0.16" + "@graphql-tools/utils" "^9.2.1" + dataloader "^2.2.2" + tslib "^2.5.0" + value-or-promise "^1.0.12" -"@graphql-tools/executor-graphql-ws@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.11.tgz#c6536aa862f76a9c7ac83e7e07fe8d5119e6de38" - integrity sha512-muRj6j897ks2iKqe3HchWFFzd+jFInSRuLPvHJ7e4WPrejFvaZx3BQ9gndfJvVkfYUZIFm13stCGXaJJTbVM0Q== +"@graphql-tools/executor-graphql-ws@^0.0.13": + version "0.0.13" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.13.tgz#61deec5e87580147890b8d1a02817360e4466794" + integrity sha512-BCFTyXdNtX4BIMjVmctehTXJVdjknMx1d8uNuhENKc8x/hFWUBT2a2/vC/ewLzQ2aEdXMhru7ubYopp671/qUQ== dependencies: "@graphql-tools/utils" "9.2.1" "@repeaterjs/repeater" "3.0.4" "@types/ws" "^8.0.0" - graphql-ws "5.11.3" + graphql-ws "5.12.1" isomorphic-ws "5.0.0" tslib "^2.4.0" - ws "8.12.1" + ws "8.13.0" -"@graphql-tools/executor-http@^0.1.7": +"@graphql-tools/executor-http@^0.1.7", "@graphql-tools/executor-http@^0.1.9": version "0.1.9" resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.9.tgz#ddd74ef376b4a2ed59c622acbcca068890854a30" integrity sha512-tNzMt5qc1ptlHKfpSv9wVBVKCZ7gks6Yb/JcYJluxZIT4qRV+TtOFjpptfBU63usgrGVOVcGjzWc/mt7KhmmpQ== @@ -1057,24 +1191,24 @@ tslib "^2.4.0" value-or-promise "^1.0.12" -"@graphql-tools/executor-legacy-ws@^0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.9.tgz#1ff517998f750af2be9c1dae8924665a136e4986" - integrity sha512-L7oDv7R5yoXzMH+KLKDB2WHVijfVW4dB2H+Ae1RdW3MFvwbYjhnIB6QzHqKEqksjp/FndtxZkbuTIuAOsYGTYw== +"@graphql-tools/executor-legacy-ws@^0.0.10": + version "0.0.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.10.tgz#9a75f46901db1e0406c110bac8340ba5d8d04189" + integrity sha512-DLd4t7LgPFkTIuVAgks6nj9kQCNmBIGg7enoAbkgcExW9xXm7dTB/QfHdu5/6EqFPbayt2G7NHWxY6IFTVkbkA== dependencies: "@graphql-tools/utils" "9.2.1" "@types/ws" "^8.0.0" isomorphic-ws "5.0.0" tslib "^2.4.0" - ws "8.12.1" + ws "8.13.0" -"@graphql-tools/executor@0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.14.tgz#7c6073d75c77dd6e7fab0c835761ed09c85a3bc6" - integrity sha512-YiBbN9NT0FgqPJ35+Eg0ty1s5scOZTgiPf+6hLVJBd5zHEURwojEMCTKJ9e0RNZHETp2lN+YaTFGTSoRk0t4Sw== +"@graphql-tools/executor@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.16.tgz#6c7cfbc04c05149b8dd6630f9debeb0293e806d5" + integrity sha512-TGhvJPNaZbvhurLQtYBfedvNRuokvoPRC3vCjeHCe9AOUfb8Vn7Jvrpnys5R/wdCnXKsOpCm9QZt9OqNMfkByQ== dependencies: "@graphql-tools/utils" "9.2.1" - "@graphql-typed-document-node/core" "3.1.1" + "@graphql-typed-document-node/core" "3.2.0" "@repeaterjs/repeater" "3.0.4" tslib "^2.4.0" value-or-promise "1.0.12" @@ -1092,15 +1226,17 @@ unixify "^1.0.0" "@graphql-tools/github-loader@^7.3.20": - version "7.3.27" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.27.tgz#77a2fbaeb7bf5f8edc4a865252ecb527a5399e01" - integrity sha512-fFFC35qenyhjb8pfcYXKknAt0CXP5CkQYtLfJXgTXSgBjIsfAVMrqxQ/Y0ejeM19XNF/C3VWJ7rE308yOX6ywA== + version "7.3.28" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.28.tgz#a7166b136e8442bd8b3ab943ad3b66c84bcabfcf" + integrity sha512-OK92Lf9pmxPQvjUNv05b3tnVhw0JRfPqOf15jZjyQ8BfdEUrJoP32b4dRQQem/wyRL24KY4wOfArJNqzpsbwCA== dependencies: "@ardatan/sync-fetch" "^0.0.1" + "@graphql-tools/executor-http" "^0.1.9" "@graphql-tools/graphql-tag-pluck" "^7.4.6" "@graphql-tools/utils" "^9.2.1" "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" + value-or-promise "^1.0.12" "@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": version "7.5.16" @@ -1162,19 +1298,19 @@ unixify "1.0.0" "@graphql-tools/load@^7.5.5", "@graphql-tools/load@^7.8.0": - version "7.8.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.12.tgz#6457fe6ec8cd2e2b5ca0d2752464bc937d186cca" - integrity sha512-JwxgNS2c6i6oIdKttcbXns/lpKiyN7c6/MkkrJ9x2QE9rXk5HOhSJxRvPmOueCuAin1542xUrcDRGBXJ7thSig== + version "7.8.13" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.13.tgz#a813bfc8195d27f465739c15fb1672a6def6e9ee" + integrity sha512-c97/GuUl81Wpa38cx3E6nMz8gUrvVcFokoPfDOaA5uTWSTXA1UxaF4KrvM9P5rNFaKVAtF9f6nMIusRE5B0mag== dependencies: - "@graphql-tools/schema" "9.0.16" + "@graphql-tools/schema" "9.0.17" "@graphql-tools/utils" "9.2.1" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@8.3.18", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": - version "8.3.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.18.tgz#bfbb517c68598a885809f16ce5c3bb1ebb8f04a2" - integrity sha512-R8nBglvRWPAyLpZL/f3lxsY7wjnAeE0l056zHhcO/CgpvK76KYUt9oEkR05i8Hmt8DLRycBN0FiotJ0yDQWTVA== +"@graphql-tools/merge@8.4.0", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.0.tgz#47fbe5c4b6764276dc35bd19c4e7d3c46d3dc0fc" + integrity sha512-3XYCWe0d3I4F1azNj1CdShlbHfTIfiDgj00R9uvFH8tHKh7i1IWN3F7QQYovcHKhayaR6zPok3YYMESYQcBoaA== dependencies: "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" @@ -1187,25 +1323,24 @@ tslib "^2.4.0" "@graphql-tools/prisma-loader@^7.2.49": - version "7.2.64" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.64.tgz#e9fc85054b15a22a16c8e69ad4f9543da30c0164" - integrity sha512-W8GfzfBKiBSIEgw+/nJk6zUlF6k/jterlNoFhM27mBsbeMtWxKnm1+gEU6KA0N1PNEdq2RIa2W4AfVfVBl2GgQ== + version "7.2.69" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.69.tgz#cc823092650e4b2b790b106322c92d81bb2eb3e2" + integrity sha512-kSJ018huCzO0AMHAUK/OVSddGOWJxKtE6HXFyTPhJAnXdKEqOSuivaAXZt2msDVA8sL8x/qe0Fb5PTtP8Mhk1Q== dependencies: - "@graphql-tools/url-loader" "7.17.13" + "@graphql-tools/url-loader" "7.17.17" "@graphql-tools/utils" "9.2.1" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" - "@types/jsonwebtoken" "^9.0.0" + "@whatwg-node/fetch" "^0.8.2" chalk "^4.1.0" debug "^4.3.1" dotenv "^16.0.0" graphql-request "^5.0.0" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" - isomorphic-fetch "^3.0.0" + jose "^4.11.4" js-yaml "^4.0.0" json-stable-stringify "^1.0.1" - jsonwebtoken "^9.0.0" lodash "^4.17.20" scuid "^1.1.0" tslib "^2.4.0" @@ -1220,28 +1355,28 @@ "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@9.0.16", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13": - version "9.0.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.16.tgz#7d340d69e6094dc01a2b9e625c7bb4fff89ea521" - integrity sha512-kF+tbYPPf/6K2aHG3e1SWIbapDLQaqnIHVRG6ow3onkFoowwtKszvUyOASL6Krcv2x9bIMvd1UkvRf9OaoROQQ== +"@graphql-tools/schema@9.0.17", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13", "@graphql-tools/schema@^9.0.16": + version "9.0.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.17.tgz#d731e9899465f88d5b9bf69e607ec465bb88b062" + integrity sha512-HVLq0ecbkuXhJlpZ50IHP5nlISqH2GbNgjBJhhRzHeXhfwlUOT4ISXGquWTmuq61K0xSaO0aCjMpxe4QYbKTng== dependencies: - "@graphql-tools/merge" "8.3.18" + "@graphql-tools/merge" "8.4.0" "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-tools/url-loader@7.17.13", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": - version "7.17.13" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.13.tgz#d4ee8193792ab1c42db2fbdf5f6ca75fa819ac40" - integrity sha512-FEmbvw68kxeZLn4VYGAl+NuBPk09ZnxymjW07A6mCtiDayFgYfHdWeRzXn/iM5PzsEuCD73R1sExtNQ/ISiajg== +"@graphql-tools/url-loader@7.17.17", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": + version "7.17.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.17.tgz#a26238fa80a788fc2a981cefcec2fa6c14643208" + integrity sha512-zT0GskawhfVzu3LjP5iXi+chyrUYvnv6B8RbJbnkiFHdSLsgIkxEyKm4tQUMN1Rm1l3i5nmeMtSzpEI9jTYLGw== dependencies: "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/delegate" "^9.0.27" - "@graphql-tools/executor-graphql-ws" "^0.0.11" + "@graphql-tools/delegate" "^9.0.30" + "@graphql-tools/executor-graphql-ws" "^0.0.13" "@graphql-tools/executor-http" "^0.1.7" - "@graphql-tools/executor-legacy-ws" "^0.0.9" + "@graphql-tools/executor-legacy-ws" "^0.0.10" "@graphql-tools/utils" "^9.2.1" - "@graphql-tools/wrap" "^9.3.6" + "@graphql-tools/wrap" "^9.4.1" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.8.0" isomorphic-ws "^5.0.0" @@ -1273,21 +1408,21 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/wrap@^9.3.6": - version "9.3.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.3.6.tgz#23beaf9c3713160adda511c6a498d1c7077c2848" - integrity sha512-HtQIYoPz48bzpMYZzoeMmzIIYuVxcaUuLD7dH7GtIhwe2f4hpPDE+JLUPxpYiaXdY10l7kP9wycK+FtRfCsFlw== +"@graphql-tools/wrap@^9.4.1": + version "9.4.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.1.tgz#c8297d96967d9f1c6176d3d71b56780ff9a1244c" + integrity sha512-uMT6A8CKQhtKyzSI7BfU5YSG3dMW+3TuMN9W9N3wG+8E2m6Jb1twp8bAb7Ho9cvT8/tiLhl/xNkdMt17p+JCTQ== dependencies: - "@graphql-tools/delegate" "9.0.27" - "@graphql-tools/schema" "9.0.16" + "@graphql-tools/delegate" "9.0.30" + "@graphql-tools/schema" "9.0.17" "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" value-or-promise "1.0.12" -"@graphql-typed-document-node/core@3.1.1", "@graphql-typed-document-node/core@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052" - integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg== +"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== "@humanwhocodes/config-array@^0.11.8": version "0.11.8" @@ -1324,109 +1459,109 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.4.3.tgz#1f25a99f7f860e4c46423b5b1038262466fadde1" - integrity sha512-W/o/34+wQuXlgqlPYTansOSiBnuxrTv61dEVkA6HNmpcgHLUjfaUbdqt6oVvOzaawwo9IdW9QOtMgQ1ScSZC4A== +"@jest/console@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" + integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.4.3" - jest-util "^29.4.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" -"@jest/core@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.4.3.tgz#829dd65bffdb490de5b0f69e97de8e3b5eadd94b" - integrity sha512-56QvBq60fS4SPZCuM7T+7scNrkGIe7Mr6PVIXUpu48ouvRaWOFqRPV91eifvFM0ay2HmfswXiGf97NGUN5KofQ== - dependencies: - "@jest/console" "^29.4.3" - "@jest/reporters" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/transform" "^29.4.3" - "@jest/types" "^29.4.3" +"@jest/core@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" + integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/reporters" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.4.3" - jest-config "^29.4.3" - jest-haste-map "^29.4.3" - jest-message-util "^29.4.3" + jest-changed-files "^29.5.0" + jest-config "^29.5.0" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" jest-regex-util "^29.4.3" - jest-resolve "^29.4.3" - jest-resolve-dependencies "^29.4.3" - jest-runner "^29.4.3" - jest-runtime "^29.4.3" - jest-snapshot "^29.4.3" - jest-util "^29.4.3" - jest-validate "^29.4.3" - jest-watcher "^29.4.3" + jest-resolve "^29.5.0" + jest-resolve-dependencies "^29.5.0" + jest-runner "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" + jest-watcher "^29.5.0" micromatch "^4.0.4" - pretty-format "^29.4.3" + pretty-format "^29.5.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.4.3.tgz#9fe2f3169c3b33815dc4bd3960a064a83eba6548" - integrity sha512-dq5S6408IxIa+lr54zeqce+QgI+CJT4nmmA+1yzFgtcsGK8c/EyiUb9XQOgz3BMKrRDfKseeOaxj2eO8LlD3lA== +"@jest/environment@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" + integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== dependencies: - "@jest/fake-timers" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^29.4.3" + jest-mock "^29.5.0" -"@jest/expect-utils@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.4.3.tgz#95ce4df62952f071bcd618225ac7c47eaa81431e" - integrity sha512-/6JWbkxHOP8EoS8jeeTd9dTfc9Uawi+43oLKHfp6zzux3U2hqOOVnV3ai4RpDYHOccL6g+5nrxpoc8DmJxtXVQ== +"@jest/expect-utils@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" + integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== dependencies: jest-get-type "^29.4.3" -"@jest/expect@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.4.3.tgz#d31a28492e45a6bcd0f204a81f783fe717045c6e" - integrity sha512-iktRU/YsxEtumI9zsPctYUk7ptpC+AVLLk1Ax3AsA4g1C+8OOnKDkIQBDHtD5hA/+VtgMd5AWI5gNlcAlt2vxQ== +"@jest/expect@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" + integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== dependencies: - expect "^29.4.3" - jest-snapshot "^29.4.3" + expect "^29.5.0" + jest-snapshot "^29.5.0" -"@jest/fake-timers@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.4.3.tgz#31e982638c60fa657d310d4b9d24e023064027b0" - integrity sha512-4Hote2MGcCTWSD2gwl0dwbCpBRHhE6olYEuTj8FMowdg3oQWNKr2YuxenPQYZ7+PfqPY1k98wKDU4Z+Hvd4Tiw== +"@jest/fake-timers@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" + integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.4.3" - jest-mock "^29.4.3" - jest-util "^29.4.3" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" + jest-util "^29.5.0" -"@jest/globals@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.4.3.tgz#63a2c4200d11bc6d46f12bbe25b07f771fce9279" - integrity sha512-8BQ/5EzfOLG7AaMcDh7yFCbfRLtsc+09E1RQmRBI4D6QQk4m6NSK/MXo+3bJrBN0yU8A2/VIcqhvsOLFmziioA== +"@jest/globals@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" + integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== dependencies: - "@jest/environment" "^29.4.3" - "@jest/expect" "^29.4.3" - "@jest/types" "^29.4.3" - jest-mock "^29.4.3" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/types" "^29.5.0" + jest-mock "^29.5.0" -"@jest/reporters@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.4.3.tgz#0a68a0c0f20554760cc2e5443177a0018969e353" - integrity sha512-sr2I7BmOjJhyqj9ANC6CTLsL4emMoka7HkQpcoMRlhCbQJjz2zsRzw0BDPiPyEFDXAbxKgGFYuQZiSJ1Y6YoTg== +"@jest/reporters@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" + integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/transform" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/console" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" @@ -1439,9 +1574,9 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.4.3" - jest-util "^29.4.3" - jest-worker "^29.4.3" + jest-message-util "^29.5.0" + jest-util "^29.5.0" + jest-worker "^29.5.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" @@ -1463,24 +1598,24 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.4.3.tgz#e13d973d16c8c7cc0c597082d5f3b9e7f796ccb8" - integrity sha512-Oi4u9NfBolMq9MASPwuWTlC5WvmNRwI4S8YrQg5R5Gi47DYlBe3sh7ILTqi/LGrK1XUE4XY9KZcQJTH1WJCLLA== +"@jest/test-result@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" + integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== dependencies: - "@jest/console" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/console" "^29.5.0" + "@jest/types" "^29.5.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.4.3.tgz#0862e876a22993385a0f3e7ea1cc126f208a2898" - integrity sha512-yi/t2nES4GB4G0mjLc0RInCq/cNr9dNwJxcGg8sslajua5Kb4kmozAc+qPLzplhBgfw1vLItbjyHzUN92UXicw== +"@jest/test-sequencer@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" + integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== dependencies: - "@jest/test-result" "^29.4.3" + "@jest/test-result" "^29.5.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.3" + jest-haste-map "^29.5.0" slash "^3.0.0" "@jest/transform@^27.1.0": @@ -1504,22 +1639,22 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.4.3.tgz#f7d17eac9cb5bb2e1222ea199c7c7e0835e0c037" - integrity sha512-8u0+fBGWolDshsFgPQJESkDa72da/EVwvL+II0trN2DR66wMwiQ9/CihaGfHdlLGFzbBZwMykFtxuwFdZqlKwg== +"@jest/transform@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" + integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.3" + jest-haste-map "^29.5.0" jest-regex-util "^29.4.3" - jest-util "^29.4.3" + jest-util "^29.5.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -1536,10 +1671,10 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.4.3.tgz#9069145f4ef09adf10cec1b2901b2d390031431f" - integrity sha512-bPYfw8V65v17m2Od1cv44FH+SiKW7w2Xu7trhcdTLUmSv85rfKsP+qXSjO4KGJr4dtPSzl/gvslZBXctf1qGEA== +"@jest/types@^29.5.0": + version "29.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" + integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== dependencies: "@jest/schemas" "^29.4.3" "@types/istanbul-lib-coverage" "^2.0.0" @@ -1548,18 +1683,10 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== dependencies: "@jridgewell/set-array" "^1.0.1" "@jridgewell/sourcemap-codec" "^1.4.10" @@ -1570,16 +1697,21 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": +"@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@1.4.14": version "1.4.14" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + "@jridgewell/trace-mapping@0.3.9": version "0.3.9" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" @@ -1588,90 +1720,70 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== dependencies: "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.6.tgz#c4925609f16142ded1a5cb833359ab17359b7a93" - integrity sha512-s+W9Fdqh5MFk6ECrbnVmmAOwxKQuhGMT7xXHrkYIBMBcTiOqNWhv5KbJIboKR5STXxNXl32hllnvKaffzFaWQg== +"@next/env@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" + integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== -"@next/eslint-plugin-next@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.6.tgz#ad8be22dd3d8aee9a9bd9a2507e2c55a2f7ebdd9" - integrity sha512-o7cauUYsXjzSJkay8wKjpKJf2uLzlggCsGUkPu3lP09Pv97jYlekTC20KJrjQKmSv5DXV0R/uks2ZXhqjNkqAw== +"@next/eslint-plugin-next@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.3.0.tgz#3a4742b0817575cc0dd4d152cb10363584c215ac" + integrity sha512-wuGN5qSEjSgcq9fVkH0Y/qIPFjnZtW3ZPwfjJOn7l/rrf6y8J24h/lo61kwqunTyzZJm/ETGfGVU9PUs8cnzEA== dependencies: glob "7.1.7" -"@next/swc-android-arm-eabi@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.6.tgz#d766dfc10e27814d947b20f052067c239913dbcc" - integrity sha512-F3/6Z8LH/pGlPzR1AcjPFxx35mPqjE5xZcf+IL+KgbW9tMkp7CYi1y7qKrEWU7W4AumxX/8OINnDQWLiwLasLQ== - -"@next/swc-android-arm64@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.6.tgz#f37a98d5f18927d8c9970d750d516ac779465176" - integrity sha512-cMwQjnB8vrYkWyK/H0Rf2c2pKIH4RGjpKUDvbjVAit6SbwPDpmaijLio0LWFV3/tOnY6kvzbL62lndVA0mkYpw== - -"@next/swc-darwin-arm64@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.6.tgz#ec1b90fd9bf809d8b81004c5182e254dced4ad96" - integrity sha512-KKRQH4DDE4kONXCvFMNBZGDb499Hs+xcFAwvj+rfSUssIDrZOlyfJNy55rH5t2Qxed1e4K80KEJgsxKQN1/fyw== - -"@next/swc-darwin-x64@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.6.tgz#e869ac75d16995eee733a7d1550322d9051c1eb4" - integrity sha512-/uOky5PaZDoaU99ohjtNcDTJ6ks/gZ5ykTQDvNZDjIoCxFe3+t06bxsTPY6tAO6uEAw5f6vVFX5H5KLwhrkZCA== - -"@next/swc-freebsd-x64@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.6.tgz#84a7b2e423a2904afc2edca21c2f1ba6b53fa4c1" - integrity sha512-qaEALZeV7to6weSXk3Br80wtFQ7cFTpos/q+m9XVRFggu+8Ib895XhMWdJBzew6aaOcMvYR6KQ6JmHA2/eMzWw== - -"@next/swc-linux-arm-gnueabihf@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.6.tgz#980eed1f655ff8a72187d8a6ef9e73ac39d20d23" - integrity sha512-OybkbC58A1wJ+JrJSOjGDvZzrVEQA4sprJejGqMwiZyLqhr9Eo8FXF0y6HL+m1CPCpPhXEHz/2xKoYsl16kNqw== - -"@next/swc-linux-arm64-gnu@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.6.tgz#87a71db21cded3f7c63d1d19079845c59813c53d" - integrity sha512-yCH+yDr7/4FDuWv6+GiYrPI9kcTAO3y48UmaIbrKy8ZJpi7RehJe3vIBRUmLrLaNDH3rY1rwoHi471NvR5J5NQ== - -"@next/swc-linux-arm64-musl@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.6.tgz#c5aac8619331b9fd030603bbe2b36052011e11de" - integrity sha512-ECagB8LGX25P9Mrmlc7Q/TQBb9rGScxHbv/kLqqIWs2fIXy6Y/EiBBiM72NTwuXUFCNrWR4sjUPSooVBJJ3ESQ== - -"@next/swc-linux-x64-gnu@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.6.tgz#9513d36d540bbfea575576746736054c31aacdea" - integrity sha512-GT5w2mruk90V/I5g6ScuueE7fqj/d8Bui2qxdw6lFxmuTgMeol5rnzAv4uAoVQgClOUO/MULilzlODg9Ib3Y4Q== - -"@next/swc-linux-x64-musl@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.6.tgz#d61fc6884899f5957251f4ce3f522e34a2c479b7" - integrity sha512-keFD6KvwOPzmat4TCnlnuxJCQepPN+8j3Nw876FtULxo8005Y9Ghcl7ACcR8GoiKoddAq8gxNBrpjoxjQRHeAQ== - -"@next/swc-win32-arm64-msvc@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.6.tgz#fac2077a8ae9768e31444c9ae90807e64117cda7" - integrity sha512-OwertslIiGQluFvHyRDzBCIB07qJjqabAmINlXUYt7/sY7Q7QPE8xVi5beBxX/rxTGPIbtyIe3faBE6Z2KywhQ== - -"@next/swc-win32-ia32-msvc@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.6.tgz#498bc11c91b4c482a625bf4b978f98ae91111e46" - integrity sha512-g8zowiuP8FxUR9zslPmlju7qYbs2XBtTLVSxVikPtUDQedhcls39uKYLvOOd1JZg0ehyhopobRoH1q+MHlIN/w== - -"@next/swc-win32-x64-msvc@13.1.6": - version "13.1.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.6.tgz#17ed919c723426b7d0ce1cd73d40ce3dcd342089" - integrity sha512-Ls2OL9hi3YlJKGNdKv8k3X/lLgc3VmLG3a/DeTkAd+lAituJp8ZHmRmm9f9SL84fT3CotlzcgbdaCDfFwFA6bA== +"@next/swc-darwin-arm64@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" + integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== + +"@next/swc-darwin-x64@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" + integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== + +"@next/swc-linux-arm64-gnu@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" + integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== + +"@next/swc-linux-arm64-musl@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" + integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== + +"@next/swc-linux-x64-gnu@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" + integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== + +"@next/swc-linux-x64-musl@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" + integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== + +"@next/swc-win32-arm64-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" + integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== + +"@next/swc-win32-ia32-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" + integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== + +"@next/swc-win32-x64-msvc@13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" + integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1694,15 +1806,25 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@panva/hkdf@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.2.tgz#bab0f09d09de9fd83628220d496627681bc440d6" - integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA== +"@panva/hkdf@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.4.tgz#4e02bb248402ff6c5c024e23a68438e2b0e69d67" + integrity sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ== -"@peculiar/asn1-schema@^2.1.6", "@peculiar/asn1-schema@^2.3.0": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.3.tgz#21418e1f3819e0b353ceff0c2dad8ccb61acd777" - integrity sha512-6GptMYDMyWBHTUKndHaDsRZUO/XMSgIns2krxcm2L7SEExRHwawFvSwNBhqNPR9HJwv3MruAiF1bhN0we6j6GQ== +"@parcel/watcher@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" + integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw== + dependencies: + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^3.2.1" + node-gyp-build "^4.3.0" + +"@peculiar/asn1-schema@^2.3.6": + version "2.3.6" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" + integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.2" @@ -1716,15 +1838,15 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.4.0": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz#821493bd5ad0f05939bd5f53b28536f68158360a" - integrity sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw== + version "1.4.3" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" + integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== dependencies: - "@peculiar/asn1-schema" "^2.3.0" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.2" - tslib "^2.4.1" - webcrypto-core "^1.7.4" + tslib "^2.5.0" + webcrypto-core "^1.7.7" "@pkgr/utils@^2.3.1": version "2.3.1" @@ -1793,9 +1915,9 @@ integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== "@sinclair/typebox@^0.25.16": - version "0.25.22" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.22.tgz#2808d895e9c2722b20a622a9c8cb332f6720eb4a" - integrity sha512-6U6r2L7rnM7EG8G1tWzIjdB3QlsHF4slgcqXNN/SF0xJOAr0nDmT2GedlkyO3mrv8mDTJ24UuOMWR3diBrCvQQ== + version "0.25.24" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" + integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== "@sinonjs/commons@^2.0.0": version "2.0.0" @@ -1935,17 +2057,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/jsonwebtoken@^9.0.0": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4" - integrity sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw== - dependencies: - "@types/node" "*" - "@types/node@*", "@types/node@^18.11.18": - version "18.13.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" - integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1990,7 +2105,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0": +"@types/react@*": version "18.0.28" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== @@ -1999,10 +2114,19 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^18.0": + version "18.0.35" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741" + integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== "@types/stack-utils@^2.0.0": version "2.0.1" @@ -2029,54 +2153,54 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/parser@^5.42.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.52.0.tgz#73c136df6c0133f1d7870de7131ccf356f5be5a4" - integrity sha512-e2KiLQOZRo4Y0D/b+3y08i3jsekoSkOYStROYmPUnGMEoA0h+k2qOH5H6tcjIc68WDvGwH+PaOrP1XRzLJ6QlA== + version "5.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6" + integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ== dependencies: - "@typescript-eslint/scope-manager" "5.52.0" - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/typescript-estree" "5.52.0" + "@typescript-eslint/scope-manager" "5.58.0" + "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/typescript-estree" "5.58.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.52.0.tgz#a993d89a0556ea16811db48eabd7c5b72dcb83d1" - integrity sha512-AR7sxxfBKiNV0FWBSARxM8DmNxrwgnYMPwmpkC1Pl1n+eT8/I2NAUPuwDy/FmDcC6F8pBfmOcaxcxRHspgOBMw== +"@typescript-eslint/scope-manager@5.58.0": + version "5.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8" + integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA== dependencies: - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/visitor-keys" "5.52.0" + "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/visitor-keys" "5.58.0" -"@typescript-eslint/types@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.52.0.tgz#19e9abc6afb5bd37a1a9bea877a1a836c0b3241b" - integrity sha512-oV7XU4CHYfBhk78fS7tkum+/Dpgsfi91IIDy7fjCyq2k6KB63M6gMC0YIvy+iABzmXThCRI6xpCEyVObBdWSDQ== +"@typescript-eslint/types@5.58.0": + version "5.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8" + integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g== -"@typescript-eslint/typescript-estree@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.52.0.tgz#6408cb3c2ccc01c03c278cb201cf07e73347dfca" - integrity sha512-WeWnjanyEwt6+fVrSR0MYgEpUAuROxuAH516WPjUblIrClzYJj0kBbjdnbQXLpgAN8qbEuGywiQsXUVDiAoEuQ== +"@typescript-eslint/typescript-estree@5.58.0": + version "5.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61" + integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q== dependencies: - "@typescript-eslint/types" "5.52.0" - "@typescript-eslint/visitor-keys" "5.52.0" + "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/visitor-keys" "5.58.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.52.0": - version "5.52.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.52.0.tgz#e38c971259f44f80cfe49d97dbffa38e3e75030f" - integrity sha512-qMwpw6SU5VHCPr99y274xhbm+PRViK/NATY6qzt+Et7+mThGuFSl/ompj2/hrBlRP/kq+BFdgagnOSgw9TB0eA== +"@typescript-eslint/visitor-keys@5.58.0": + version "5.58.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4" + integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA== dependencies: - "@typescript-eslint/types" "5.52.0" + "@typescript-eslint/types" "5.58.0" eslint-visitor-keys "^3.3.0" "@whatwg-node/events@^0.0.2": @@ -2084,41 +2208,21 @@ resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.2.tgz#7b7107268d2982fc7b7aff5ee6803c64018f84dd" integrity sha512-WKj/lI4QjnLuPrim0cfO7i+HsDSXHxNv1y0CrJhdntuO3hxWZmnXCwNDnwOvry11OjRin6cgWNF+j/9Pn8TN4w== -"@whatwg-node/fetch@^0.6.0": - version "0.6.9" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.6.9.tgz#6cc694cc0378e27b8dfed427c5bf633eda6972b9" - integrity sha512-JfrBCJdMu9n9OARc0e/hPHcD98/8Nz1CKSdGYDg6VbObDkV/Ys30xe5i/wPOatYbxuvatj1kfWeHf7iNX3i17w== - dependencies: - "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.0.5" - busboy "^1.6.0" - urlpattern-polyfill "^6.0.2" - web-streams-polyfill "^3.2.1" - -"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.1.tgz#ee3c94746132f217e17f78f9e073bb342043d630" - integrity sha512-Fkd1qQHK2tAWxKlC85h9L86Lgbq3BzxMnHSnTsnzNZMMzn6Xi+HlN8/LJ90LxorhSqD54td+Q864LgwUaYDj1Q== +"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1", "@whatwg-node/fetch@^0.8.2": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.4.tgz#ae1c306d1e4f5ba5cf9badf070de259f04c2cda8" + integrity sha512-xK0NGWt49P+JmsdfN+8zmHzZoscENrV0KL1SyyncvWkc6vbFmSqGSpvItEBuhj1PAfTGFEUpyiRMCsut2hLy/Q== dependencies: "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.0" + "@whatwg-node/node-fetch" "^0.3.3" busboy "^1.6.0" urlpattern-polyfill "^6.0.2" web-streams-polyfill "^3.2.1" -"@whatwg-node/node-fetch@^0.0.5": - version "0.0.5" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.0.5.tgz#bebf18891088e5e2fc449dea8d1bc94af5ec38df" - integrity sha512-hbccmaSZaItdsRuBKBEEhLoO+5oXJPxiyd0kG2xXd0Dh3Rt+vZn4pADHxuSiSHLd9CM+S2z4+IxlEGbWUgiz9g== - dependencies: - "@whatwg-node/events" "^0.0.2" - busboy "^1.6.0" - tslib "^2.3.1" - -"@whatwg-node/node-fetch@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.0.tgz#7c7e90d03fa09d0ddebff29add6f16d923327d58" - integrity sha512-mPM8WnuHiI/3kFxDeE0SQQXAElbz4onqmm64fEGCwYEcBes2UsvIDI8HwQIqaXCH42A9ajJUPv4WsYoN/9oG6w== +"@whatwg-node/node-fetch@^0.3.3": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.4.tgz#4beb88579c53ebd870e28d0f2f0376191b9fd6c3" + integrity sha512-gP1MN6DiHVbhkLWH1eCELhE2ZtLRxb+HRKu4eYze1Tijxz0uT1T2kk3lseZp94txzxCfbxGFU0jsWkxNdH3EXA== dependencies: "@whatwg-node/events" "^0.0.2" busboy "^1.6.0" @@ -2281,7 +2385,7 @@ antd@^5.0: scroll-into-view-if-needed "^3.0.3" throttle-debounce "^5.0.0" -anymatch@^3.0.3, anymatch@~3.1.2: +anymatch@^3.0.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2313,6 +2417,14 @@ aria-query@^5.1.3: dependencies: deep-equal "^2.0.5" +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + array-find-index@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2440,15 +2552,15 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^29.0, babel-jest@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.4.3.tgz#478b84d430972b277ad67dd631be94abea676792" - integrity sha512-o45Wyn32svZE+LnMVWv/Z4x0SwtLbh4FyGcYtR20kIWd+rdrDZ9Fzq8Ml3MYLD+mZvEdzCjZsCnYZ2jpJyQ+Nw== +babel-jest@^29.0, babel-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" + integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== dependencies: - "@jest/transform" "^29.4.3" + "@jest/transform" "^29.5.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.4.3" + babel-preset-jest "^29.5.0" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2474,10 +2586,10 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.4.3.tgz#ad1dfb5d31940957e00410ef7d9b2aa94b216101" - integrity sha512-mB6q2q3oahKphy5V7CpnNqZOCkxxZ9aokf1eh82Dy3jQmg4xvM1tGrh5y6BQUJh4a3Pj9+eLfwvAZ7VNKg7H8Q== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2548,12 +2660,12 @@ babel-preset-jest@^27.0.6: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.4.3.tgz#bb926b66ae253b69c6e3ef87511b8bb5c53c5b52" - integrity sha512-gWx6COtSuma6n9bw+8/F+2PCXrIgxV/D1TJFnp6OyBK2cxPWg0K9p/sriNYeifKjpUkMViWQ09DSWtzJQRETsw== +babel-preset-jest@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" + integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== dependencies: - babel-plugin-jest-hoist "^29.4.3" + babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2571,11 +2683,6 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -2593,7 +2700,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -2617,11 +2724,6 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -2635,7 +2737,7 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -busboy@^1.6.0: +busboy@1.6.0, busboy@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== @@ -2679,9 +2781,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449: - version "1.0.30001453" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001453.tgz#6d3a1501622bf424a3cee5ad9550e640b0de3de8" - integrity sha512-R9o/uySW38VViaTrOtwfbFEiBFUh7ST3uIG4OEymIG3/uKdHDO4xk/FaqfUw0d+irSUyFPy3dZszf9VvSTPnsA== + version "1.0.30001478" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" + integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== capital-case@^1.0.4: version "1.0.4" @@ -2769,21 +2871,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chokidar@^3.5.2: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - ci-info@^3.2.0: version "3.8.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" @@ -2812,9 +2899,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.5.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== + version "2.8.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" + integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== cli-truncate@^2.1.0: version "2.1.0" @@ -2959,11 +3046,6 @@ copy-to-clipboard@^3.2.0: dependencies: toggle-selection "^1.0.6" -cosmiconfig-typescript-loader@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz#c4259ce474c9df0f32274ed162c0447c951ef073" - integrity sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q== - cosmiconfig@8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" @@ -3011,17 +3093,22 @@ crypto-hash@^2.0.1: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-2.0.1.tgz#46c3732e65a078ea06b8b4ae686db41216f81213" integrity sha512-t4mkp7Vh6MuCZRBf0XLzBOfhkH3nW6YEAotMDSjshVQ1GffCMGdPLSr7pKH0rdXY02jTjAZ7QW2apD0buaZXcQ== -csstype@^3.0.10, csstype@^3.0.2: +csstype@^3.0.10: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== +csstype@^3.0.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -dataloader@2.2.2: +dataloader@2.2.2, dataloader@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== @@ -3094,9 +3181,9 @@ deep-is@^0.1.3: integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.0, deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" @@ -3200,17 +3287,10 @@ dset@^3.1.2: resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - electron-to-chromium@^1.4.284: - version "1.4.299" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.299.tgz#faa2069cd4879a73e540e533178db5c618768d41" - integrity sha512-lQ7ijJghH6pCGbfWXr6EY+KYCMaRSjgsY925r1p/TlpSfVM1VjHTcn1gAc15VM4uwti283X6QtjPTXdpoSGiZQ== + version "1.4.359" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz#5c4d13cb08032469fcd6bd36457915caa211356b" + integrity sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw== emittery@^0.13.1: version "0.13.1" @@ -3232,7 +3312,7 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enhanced-resolve@^5.10.0: +enhanced-resolve@^5.12.0: version "5.12.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== @@ -3248,17 +3328,17 @@ error-ex@^1.3.1: is-arrayish "^0.2.1" es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== dependencies: + array-buffer-byte-length "^1.0.0" available-typed-arrays "^1.0.5" call-bind "^1.0.2" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function-bind "^1.1.1" function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" @@ -3266,8 +3346,8 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" is-callable "^1.2.7" is-negative-zero "^2.0.2" is-regex "^1.1.4" @@ -3275,11 +3355,12 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: is-string "^1.0.7" is-typed-array "^1.1.10" is-weakref "^1.0.2" - object-inspect "^1.12.2" + object-inspect "^1.12.3" object-keys "^1.1.1" object.assign "^4.1.4" regexp.prototype.flags "^1.4.3" safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" string.prototype.trimend "^1.0.6" string.prototype.trimstart "^1.0.6" typed-array-length "^1.0.4" @@ -3347,11 +3428,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.1.4: - version "13.1.6" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.6.tgz#ab6894fe5b80080f1e9b9306d1c4b0003230620e" - integrity sha512-0cg7h5wztg/SoLAlxljZ0ZPUQ7i6QKqRiP4M2+MgTZtxWwNKb2JSwNc18nJ6/kXBI6xYvPraTbQSIhAuVw6czw== + version "13.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.3.0.tgz#c302fbecfe2b976ea306f7622af637ef9d9e3802" + integrity sha512-6YEwmFBX0VjBd3ODGW9df0Is0FLaRFdMN8eAahQG9CN6LjQ28J8AFr19ngxqMSg7Qv6Uca/3VeeBosJh1bzu0w== dependencies: - "@next/eslint-plugin-next" "13.1.6" + "@next/eslint-plugin-next" "13.3.0" "@rushstack/eslint-patch" "^1.1.3" "@typescript-eslint/parser" "^5.42.0" eslint-import-resolver-node "^0.3.6" @@ -3371,17 +3452,18 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.7: resolve "^1.22.1" eslint-import-resolver-typescript@^3.5.2: - version "3.5.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.3.tgz#db5ed9e906651b7a59dd84870aaef0e78c663a05" - integrity sha512-njRcKYBc3isE42LaTcJNVANR3R99H9bAxBDMNDr2W7yq5gYPxbU3MkdhsQukxZ/Xg9C2vcyLlDsbKfRDg0QvCQ== + version "3.5.5" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" + integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== dependencies: debug "^4.3.4" - enhanced-resolve "^5.10.0" - get-tsconfig "^4.2.0" - globby "^13.1.2" - is-core-module "^2.10.0" + enhanced-resolve "^5.12.0" + eslint-module-utils "^2.7.4" + get-tsconfig "^4.5.0" + globby "^13.1.3" + is-core-module "^2.11.0" is-glob "^4.0.3" - synckit "^0.8.4" + synckit "^0.8.5" eslint-module-utils@^2.7.4: version "2.7.4" @@ -3467,29 +3549,20 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== eslint@^8.0.0: - version "8.34.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.34.0.tgz#fe0ab0ef478104c1f9ebc5537e303d25a8fb22d6" - integrity sha512-1Z8iFsucw+7kSqXNZVslXS8Ioa4u2KM7GPwuKtkTFAqZ/cHMcEaR+1+Br0wLlot49cNxIiZk5wp8EAbPcYZxTg== - dependencies: - "@eslint/eslintrc" "^1.4.1" + version "8.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" + integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.38.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -3500,10 +3573,9 @@ eslint@^8.0.0: doctrine "^3.0.0" escape-string-regexp "^4.0.0" eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" + esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" @@ -3524,29 +3596,28 @@ eslint@^8.0.0: minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.1" - regexpp "^3.2.0" strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" - integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.0" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.1.tgz#ddb8e1e2666750113b78c15f59e977564f52b116" - integrity sha512-3ZggxvMv5EEY1ssUVyHSVt0oPreyBfbUi1XikJVfjFiBeBDLdrb0IWoDiEwqT/2sUQi0TGaWtFhOGDD8RTpXgQ== +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== dependencies: estraverse "^5.1.0" @@ -3587,16 +3658,16 @@ exit@^0.1.2: resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.4.3.tgz#5e47757316df744fe3b8926c3ae8a3ebdafff7fe" - integrity sha512-uC05+Q7eXECFpgDrHdXA4k2rpMyStAYPItEDLyQDo5Ta7fVkJnNA/4zh/OIVkVVNZ1oOK1PipQoyNjuZ6sz6Dg== +expect@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" + integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== dependencies: - "@jest/expect-utils" "^29.4.3" + "@jest/expect-utils" "^29.5.0" jest-get-type "^29.4.3" - jest-matcher-utils "^29.4.3" - jest-message-util "^29.4.3" - jest-util "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" external-editor@^3.0.3: version "3.1.0" @@ -3765,7 +3836,7 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -3827,12 +3898,12 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-tsconfig@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.4.0.tgz#64eee64596668a81b8fce18403f94f245ee0d4e5" - integrity sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ== +get-tsconfig@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" + integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== -glob-parent@^5.1.2, glob-parent@~5.1.2: +glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -3918,10 +3989,10 @@ globby@11.1.0, globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.2: - version "13.1.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.3.tgz#f62baf5720bcb2c1330c8d4ef222ee12318563ff" - integrity sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw== +globby@^13.1.3: + version "13.1.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" + integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== dependencies: dir-glob "^3.0.1" fast-glob "^3.2.11" @@ -3941,20 +4012,25 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +graceful-fs@^4.2.4, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + grapheme-splitter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== -graphql-config@^4.4.0: - version "4.4.1" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.4.1.tgz#2b1b5215b38911c0b15ff9b2e878101c984802d6" - integrity sha512-B8wlvfBHZ5WnI4IiuQZRqql6s+CKz7S+xpUeTb28Z8nRBi8tH9ChEBgT5FnTyE05PUhHlrS2jK9ICJ4YBl9OtQ== +graphql-config@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.5.0.tgz#257c2338950b8dce295a27f75c5f6c39f8f777b2" + integrity sha512-x6D0/cftpLUJ0Ch1e5sj1TZn6Wcxx4oMfmhaG9shM0DKajA9iR+j1z86GSTQ19fShbGvrSSvbIQsHku6aQ6BBw== dependencies: "@graphql-tools/graphql-file-loader" "^7.3.7" "@graphql-tools/json-file-loader" "^7.3.7" @@ -3963,7 +4039,8 @@ graphql-config@^4.4.0: "@graphql-tools/url-loader" "^7.9.7" "@graphql-tools/utils" "^9.0.0" cosmiconfig "8.0.0" - minimatch "4.2.1" + jiti "1.17.1" + minimatch "4.2.3" string-env-interpolation "1.0.1" tslib "^2.4.0" @@ -3993,9 +4070,9 @@ graphql-let@^0.18: babel-jest "27.1.0" graphql-request@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.1.0.tgz#dbc8feee27d21b993cd5da2d3af67821827b240a" - integrity sha512-0OeRVYigVwIiXhNmqnPDt+JhMzsjinxHE7TVy3Lm6jUzav0guVcL0lfSbi6jVTRAxcbwgyr6yrZioSHxf9gHzw== + version "5.2.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.2.0.tgz#a05fb54a517d91bb2d7aefa17ade4523dc5ebdca" + integrity sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" cross-fetch "^3.1.5" @@ -4009,10 +4086,10 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: dependencies: tslib "^2.1.0" -graphql-ws@5.11.3: - version "5.11.3" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.11.3.tgz#eaf8e6baf669d167975cff13ad86abca4ecfe82f" - integrity sha512-fU8zwSgAX2noXAsuFiCZ8BtXeXZOzXyK5u1LloCdacsVth4skdBMPO74EG51lBoWSIZ8beUocdpV8+cQHBODnQ== +graphql-ws@5.12.1: + version "5.12.1" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.12.1.tgz#c62d5ac54dbd409cc6520b0b39de374b3d59d0dd" + integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== graphql@^16.0.1: version "16.6.0" @@ -4204,7 +4281,7 @@ inquirer@^8.0.0: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.3, internal-slot@^1.0.4: +internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -4236,13 +4313,13 @@ is-arguments@^1.1.1: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.0" is-typed-array "^1.1.10" is-arrayish@^0.2.1: @@ -4257,13 +4334,6 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -4277,10 +4347,10 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.10.0, is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" - integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" + integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== dependencies: has "^1.0.3" @@ -4311,7 +4381,7 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4480,14 +4550,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isomorphic-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" - integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== - dependencies: - node-fetch "^2.6.1" - whatwg-fetch "^3.4.1" - isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" @@ -4540,94 +4602,95 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.4.3.tgz#7961fe32536b9b6d5c28dfa0abcfab31abcf50a7" - integrity sha512-Vn5cLuWuwmi2GNNbokPOEcvrXGSGrqVnPEZV7rC6P7ck07Dyw9RFnvWglnupSh+hGys0ajGtw/bc2ZgweljQoQ== +jest-changed-files@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" + integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== dependencies: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.4.3.tgz#fff7be1cf5f06224dd36a857d52a9efeb005ba04" - integrity sha512-Vw/bVvcexmdJ7MLmgdT3ZjkJ3LKu8IlpefYokxiqoZy6OCQ2VAm6Vk3t/qHiAGUXbdbJKJWnc8gH3ypTbB/OBw== +jest-circus@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" + integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== dependencies: - "@jest/environment" "^29.4.3" - "@jest/expect" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/environment" "^29.5.0" + "@jest/expect" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.4.3" - jest-matcher-utils "^29.4.3" - jest-message-util "^29.4.3" - jest-runtime "^29.4.3" - jest-snapshot "^29.4.3" - jest-util "^29.4.3" + jest-each "^29.5.0" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-runtime "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" p-limit "^3.1.0" - pretty-format "^29.4.3" + pretty-format "^29.5.0" + pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.4.3.tgz#fe31fdd0c90c765f392b8b7c97e4845071cd2163" - integrity sha512-PiiAPuFNfWWolCE6t3ZrDXQc6OsAuM3/tVW0u27UWc1KE+n/HSn5dSE6B2juqN7WP+PP0jAcnKtGmI4u8GMYCg== +jest-cli@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" + integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== dependencies: - "@jest/core" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/core" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.4.3" - jest-util "^29.4.3" - jest-validate "^29.4.3" + jest-config "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.4.3.tgz#fca9cdfe6298ae6d04beef1624064d455347c978" - integrity sha512-eCIpqhGnIjdUCXGtLhz4gdDoxKSWXKjzNcc5r+0S1GKOp2fwOipx5mRcwa9GB/ArsxJ1jlj2lmlD9bZAsBxaWQ== +jest-config@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" + integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.4.3" - "@jest/types" "^29.4.3" - babel-jest "^29.4.3" + "@jest/test-sequencer" "^29.5.0" + "@jest/types" "^29.5.0" + babel-jest "^29.5.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.4.3" - jest-environment-node "^29.4.3" + jest-circus "^29.5.0" + jest-environment-node "^29.5.0" jest-get-type "^29.4.3" jest-regex-util "^29.4.3" - jest-resolve "^29.4.3" - jest-runner "^29.4.3" - jest-util "^29.4.3" - jest-validate "^29.4.3" + jest-resolve "^29.5.0" + jest-runner "^29.5.0" + jest-util "^29.5.0" + jest-validate "^29.5.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.4.3" + pretty-format "^29.5.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.4.3.tgz#42f4eb34d0bf8c0fb08b0501069b87e8e84df347" - integrity sha512-YB+ocenx7FZ3T5O9lMVMeLYV4265socJKtkwgk/6YUz/VsEzYDkiMuMhWzZmxm3wDRQvayJu/PjkjjSkjoHsCA== +jest-diff@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" + integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== dependencies: chalk "^4.0.0" diff-sequences "^29.4.3" jest-get-type "^29.4.3" - pretty-format "^29.4.3" + pretty-format "^29.5.0" jest-docblock@^29.4.3: version "29.4.3" @@ -4636,28 +4699,28 @@ jest-docblock@^29.4.3: dependencies: detect-newline "^3.0.0" -jest-each@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.4.3.tgz#a434c199a2f6151c5e3dc80b2d54586bdaa72819" - integrity sha512-1ElHNAnKcbJb/b+L+7j0/w7bDvljw4gTv1wL9fYOczeJrbTbkMGQ5iQPFJ3eFQH19VPTx1IyfePdqSpePKss7Q== +jest-each@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" + integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" chalk "^4.0.0" jest-get-type "^29.4.3" - jest-util "^29.4.3" - pretty-format "^29.4.3" + jest-util "^29.5.0" + pretty-format "^29.5.0" -jest-environment-node@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.4.3.tgz#579c4132af478befc1889ddc43c2413a9cdbe014" - integrity sha512-gAiEnSKF104fsGDXNkwk49jD/0N0Bqu2K9+aMQXA6avzsA9H3Fiv1PW2D+gzbOSR705bWd2wJZRFEFpV0tXISg== +jest-environment-node@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" + integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== dependencies: - "@jest/environment" "^29.4.3" - "@jest/fake-timers" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-mock "^29.4.3" - jest-util "^29.4.3" + jest-mock "^29.5.0" + jest-util "^29.5.0" jest-get-type@^29.4.3: version "29.4.3" @@ -4684,66 +4747,66 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.4.3.tgz#085a44283269e7ace0645c63a57af0d2af6942e2" - integrity sha512-eZIgAS8tvm5IZMtKlR8Y+feEOMfo2pSQkmNbufdbMzMSn9nitgGxF1waM/+LbryO3OkMcKS98SUb+j/cQxp/vQ== +jest-haste-map@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" + integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.4.3" - jest-util "^29.4.3" - jest-worker "^29.4.3" + jest-util "^29.5.0" + jest-worker "^29.5.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.4.3.tgz#2b35191d6b35aa0256e63a9b79b0f949249cf23a" - integrity sha512-9yw4VC1v2NspMMeV3daQ1yXPNxMgCzwq9BocCwYrRgXe4uaEJPAN0ZK37nFBhcy3cUwEVstFecFLaTHpF7NiGA== +jest-leak-detector@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" + integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== dependencies: jest-get-type "^29.4.3" - pretty-format "^29.4.3" + pretty-format "^29.5.0" -jest-matcher-utils@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.4.3.tgz#ea68ebc0568aebea4c4213b99f169ff786df96a0" - integrity sha512-TTciiXEONycZ03h6R6pYiZlSkvYgT0l8aa49z/DLSGYjex4orMUcafuLXYyyEDWB1RKglq00jzwY00Ei7yFNVg== +jest-matcher-utils@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" + integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== dependencies: chalk "^4.0.0" - jest-diff "^29.4.3" + jest-diff "^29.5.0" jest-get-type "^29.4.3" - pretty-format "^29.4.3" + pretty-format "^29.5.0" -jest-message-util@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.4.3.tgz#65b5280c0fdc9419503b49d4f48d4999d481cb5b" - integrity sha512-1Y8Zd4ZCN7o/QnWdMmT76If8LuDv23Z1DRovBj/vcSFNlGCJGoO8D1nJDw1AdyAGUk0myDLFGN5RbNeJyCRGCw== +jest-message-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" + integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.4.3" + pretty-format "^29.5.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.4.3.tgz#23d84a20a74cdfff0510fdbeefb841ed57b0fe7e" - integrity sha512-LjFgMg+xed9BdkPMyIJh+r3KeHt1klXPJYBULXVVAkbTaaKjPX1o1uVCAZADMEp/kOxGTwy/Ot8XbvgItOrHEg== +jest-mock@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" + integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@types/node" "*" - jest-util "^29.4.3" + jest-util "^29.5.0" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -4760,81 +4823,81 @@ jest-regex-util@^29.4.3: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.4.3.tgz#9ad7f23839a6d88cef91416bda9393a6e9fd1da5" - integrity sha512-uvKMZAQ3nmXLH7O8WAOhS5l0iWyT3WmnJBdmIHiV5tBbdaDZ1wqtNX04FONGoaFvSOSHBJxnwAVnSn1WHdGVaw== +jest-resolve-dependencies@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" + integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== dependencies: jest-regex-util "^29.4.3" - jest-snapshot "^29.4.3" + jest-snapshot "^29.5.0" -jest-resolve@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.4.3.tgz#3c5b5c984fa8a763edf9b3639700e1c7900538e2" - integrity sha512-GPokE1tzguRyT7dkxBim4wSx6E45S3bOQ7ZdKEG+Qj0Oac9+6AwJPCk0TZh5Vu0xzeX4afpb+eDmgbmZFFwpOw== +jest-resolve@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" + integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.4.3" + jest-haste-map "^29.5.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.4.3" - jest-validate "^29.4.3" + jest-util "^29.5.0" + jest-validate "^29.5.0" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.4.3.tgz#68dc82c68645eda12bea42b5beece6527d7c1e5e" - integrity sha512-GWPTEiGmtHZv1KKeWlTX9SIFuK19uLXlRQU43ceOQ2hIfA5yPEJC7AMkvFKpdCHx6pNEdOD+2+8zbniEi3v3gA== - dependencies: - "@jest/console" "^29.4.3" - "@jest/environment" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/transform" "^29.4.3" - "@jest/types" "^29.4.3" +jest-runner@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" + integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== + dependencies: + "@jest/console" "^29.5.0" + "@jest/environment" "^29.5.0" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" jest-docblock "^29.4.3" - jest-environment-node "^29.4.3" - jest-haste-map "^29.4.3" - jest-leak-detector "^29.4.3" - jest-message-util "^29.4.3" - jest-resolve "^29.4.3" - jest-runtime "^29.4.3" - jest-util "^29.4.3" - jest-watcher "^29.4.3" - jest-worker "^29.4.3" + jest-environment-node "^29.5.0" + jest-haste-map "^29.5.0" + jest-leak-detector "^29.5.0" + jest-message-util "^29.5.0" + jest-resolve "^29.5.0" + jest-runtime "^29.5.0" + jest-util "^29.5.0" + jest-watcher "^29.5.0" + jest-worker "^29.5.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.4.3.tgz#f25db9874dcf35a3ab27fdaabca426666cc745bf" - integrity sha512-F5bHvxSH+LvLV24vVB3L8K467dt3y3dio6V3W89dUz9nzvTpqd/HcT9zfYKL2aZPvD63vQFgLvaUX/UpUhrP6Q== +jest-runtime@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" + integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== dependencies: - "@jest/environment" "^29.4.3" - "@jest/fake-timers" "^29.4.3" - "@jest/globals" "^29.4.3" + "@jest/environment" "^29.5.0" + "@jest/fake-timers" "^29.5.0" + "@jest/globals" "^29.5.0" "@jest/source-map" "^29.4.3" - "@jest/test-result" "^29.4.3" - "@jest/transform" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.4.3" - jest-message-util "^29.4.3" - jest-mock "^29.4.3" + jest-haste-map "^29.5.0" + jest-message-util "^29.5.0" + jest-mock "^29.5.0" jest-regex-util "^29.4.3" - jest-resolve "^29.4.3" - jest-snapshot "^29.4.3" - jest-util "^29.4.3" + jest-resolve "^29.5.0" + jest-snapshot "^29.5.0" + jest-util "^29.5.0" slash "^3.0.0" strip-bom "^4.0.0" @@ -4846,10 +4909,10 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.4.3.tgz#183d309371450d9c4a3de7567ed2151eb0e91145" - integrity sha512-NGlsqL0jLPDW91dz304QTM/SNO99lpcSYYAjNiX0Ou+sSGgkanKBcSjCfp/pqmiiO1nQaOyLp6XQddAzRcx3Xw== +jest-snapshot@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" + integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" @@ -4857,23 +4920,22 @@ jest-snapshot@^29.4.3: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.4.3" - "@jest/transform" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/expect-utils" "^29.5.0" + "@jest/transform" "^29.5.0" + "@jest/types" "^29.5.0" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.4.3" + expect "^29.5.0" graceful-fs "^4.2.9" - jest-diff "^29.4.3" + jest-diff "^29.5.0" jest-get-type "^29.4.3" - jest-haste-map "^29.4.3" - jest-matcher-utils "^29.4.3" - jest-message-util "^29.4.3" - jest-util "^29.4.3" + jest-matcher-utils "^29.5.0" + jest-message-util "^29.5.0" + jest-util "^29.5.0" natural-compare "^1.4.0" - pretty-format "^29.4.3" + pretty-format "^29.5.0" semver "^7.3.5" jest-util@^27.5.1: @@ -4888,42 +4950,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.4.3.tgz#851a148e23fc2b633c55f6dad2e45d7f4579f496" - integrity sha512-ToSGORAz4SSSoqxDSylWX8JzkOQR7zoBtNRsA7e+1WUX5F8jrOwaNpuh1YfJHJKDHXLHmObv5eOjejUd+/Ws+Q== +jest-util@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" + integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.4.3.tgz#a13849dec4f9e95446a7080ad5758f58fa88642f" - integrity sha512-J3u5v7aPQoXPzaar6GndAVhdQcZr/3osWSgTeKg5v574I9ybX/dTyH0AJFb5XgXIB7faVhf+rS7t4p3lL9qFaw== +jest-validate@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" + integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== dependencies: - "@jest/types" "^29.4.3" + "@jest/types" "^29.5.0" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^29.4.3" leven "^3.1.0" - pretty-format "^29.4.3" + pretty-format "^29.5.0" -jest-watcher@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.4.3.tgz#e503baa774f0c2f8f3c8db98a22ebf885f19c384" - integrity sha512-zwlXH3DN3iksoIZNk73etl1HzKyi5FuQdYLnkQKm5BW4n8HpoG59xSwpVdFrnh60iRRaRBGw0gcymIxjJENPcA== +jest-watcher@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" + integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== dependencies: - "@jest/test-result" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/test-result" "^29.5.0" + "@jest/types" "^29.5.0" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.4.3" + jest-util "^29.5.0" string-length "^4.0.1" jest-worker@^27.5.1: @@ -4935,35 +4997,45 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.4.3.tgz#9a4023e1ea1d306034237c7133d7da4240e8934e" - integrity sha512-GLHN/GTAAMEy5BFdvpUfzr9Dr80zQqBrh0fz1mtRMe05hqP45+HfQltu7oTBfduD0UeZs09d+maFtFYAXFWvAA== +jest-worker@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" + integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== dependencies: "@types/node" "*" - jest-util "^29.4.3" + jest-util "^29.5.0" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.0: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.4.3.tgz#1b8be541666c6feb99990fd98adac4737e6e6386" - integrity sha512-XvK65feuEFGZT8OO0fB/QAQS+LGHvQpaadkH5p47/j3Ocqq3xf2pK9R+G0GzgfuhXVxEv76qCOOcMb5efLk6PA== + version "29.5.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" + integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== dependencies: - "@jest/core" "^29.4.3" - "@jest/types" "^29.4.3" + "@jest/core" "^29.5.0" + "@jest/types" "^29.5.0" import-local "^3.0.2" - jest-cli "^29.4.3" + jest-cli "^29.5.0" + +jiti@1.17.1: + version "1.17.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.17.1.tgz#264daa43ee89a03e8be28c3d712ccc4eb9f1e8ed" + integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== -jose@^4.10.0, jose@^4.9.3: - version "4.12.0" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.12.0.tgz#7f00cd2f82499b91623cd413b7b5287fd52651ed" - integrity sha512-wW1u3cK81b+SFcHjGC8zw87yuyUweEFe0UJirrXEw1NasW00eF7sZjeG3SLBGz001ozxQ46Y9sofDvhBmWFtXQ== +jiti@^1.17.1: + version "1.18.2" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" + integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== + +jose@^4.10.0, jose@^4.11.4: + version "4.13.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.13.1.tgz#449111bb5ab171db85c03f1bd2cb1647ca06db1c" + integrity sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ== js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5027,7 +5099,7 @@ json2mq@^0.2.0: dependencies: string-convert "^0.2.0" -json5@^1.0.1: +json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== @@ -5044,16 +5116,6 @@ jsonify@^0.0.1: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== -jsonwebtoken@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d" - integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw== - dependencies: - jws "^3.2.2" - lodash "^4.17.21" - ms "^2.1.1" - semver "^7.3.8" - "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" @@ -5062,23 +5124,6 @@ jsonwebtoken@^9.0.0: array-includes "^3.1.5" object.assign "^4.1.3" -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -5283,7 +5328,7 @@ meros@^1.2.1: resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.1.tgz#056f7a76e8571d0aaf3c7afcbe7eb6407ff7329e" integrity sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g== -micromatch@^4.0.4: +micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== @@ -5308,10 +5353,10 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== +minimatch@4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" + integrity sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng== dependencies: brace-expansion "^1.1.7" @@ -5355,9 +5400,9 @@ mute-stream@0.0.8: integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== natural-compare@^1.4.0: version "1.4.0" @@ -5365,44 +5410,41 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next-auth@^4.18.8: - version "4.19.2" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.19.2.tgz#8dbdc4886d4f006e7c67c77ab56e0aec5693064d" - integrity sha512-6V2YG3IJQVhgCAH7mvT3yopTW92gMdUrcwGX7NQ0dCreT/+axGua/JmVdarjec0C/oJukKpIYRgjMlV+L5ZQOQ== + version "4.22.0" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.22.0.tgz#325982b8efaaa55f505ee104a382c3118fec9d96" + integrity sha512-08+kjnDoE7aQ52O996x6cwA3ffc2CbHIkrCgLYhbE+aDIJBKI0oA9UbIEIe19/+ODYJgpAHHOtJx4izmsgaVag== dependencies: - "@babel/runtime" "^7.16.3" - "@panva/hkdf" "^1.0.1" + "@babel/runtime" "^7.20.13" + "@panva/hkdf" "^1.0.2" cookie "^0.5.0" - jose "^4.9.3" + jose "^4.11.4" oauth "^0.9.15" - openid-client "^5.1.0" + openid-client "^5.4.0" preact "^10.6.3" preact-render-to-string "^5.1.19" uuid "^8.3.2" next@^13.1.4: - version "13.1.6" - resolved "https://registry.yarnpkg.com/next/-/next-13.1.6.tgz#054babe20b601f21f682f197063c9b0b32f1a27c" - integrity sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw== + version "13.3.0" + resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" + integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== dependencies: - "@next/env" "13.1.6" + "@next/env" "13.3.0" "@swc/helpers" "0.4.14" + busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" optionalDependencies: - "@next/swc-android-arm-eabi" "13.1.6" - "@next/swc-android-arm64" "13.1.6" - "@next/swc-darwin-arm64" "13.1.6" - "@next/swc-darwin-x64" "13.1.6" - "@next/swc-freebsd-x64" "13.1.6" - "@next/swc-linux-arm-gnueabihf" "13.1.6" - "@next/swc-linux-arm64-gnu" "13.1.6" - "@next/swc-linux-arm64-musl" "13.1.6" - "@next/swc-linux-x64-gnu" "13.1.6" - "@next/swc-linux-x64-musl" "13.1.6" - "@next/swc-win32-arm64-msvc" "13.1.6" - "@next/swc-win32-ia32-msvc" "13.1.6" - "@next/swc-win32-x64-msvc" "13.1.6" + "@next/swc-darwin-arm64" "13.3.0" + "@next/swc-darwin-x64" "13.3.0" + "@next/swc-linux-arm64-gnu" "13.3.0" + "@next/swc-linux-arm64-musl" "13.3.0" + "@next/swc-linux-x64-gnu" "13.3.0" + "@next/swc-linux-x64-musl" "13.3.0" + "@next/swc-win32-arm64-msvc" "13.3.0" + "@next/swc-win32-ia32-msvc" "13.3.0" + "@next/swc-win32-x64-msvc" "13.3.0" no-case@^3.0.4: version "3.0.4" @@ -5412,6 +5454,11 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-addon-api@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -5426,6 +5473,11 @@ node-fetch@^2.6.1: dependencies: whatwg-url "^5.0.0" +node-gyp-build@^4.3.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -5461,7 +5513,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0, normalize-path@~3.0.0: +normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -5498,7 +5550,7 @@ object-hash@^2.0.1: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== -object-inspect@^1.12.2, object-inspect@^1.9.0: +object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== @@ -5562,9 +5614,9 @@ object.values@^1.1.6: es-abstract "^1.20.4" oidc-token-hash@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" - integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== + version "5.0.2" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.2.tgz#f9ca7f7f1f92d721a2973e66b7430cb52a486648" + integrity sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ== once@^1.3.0: version "1.4.0" @@ -5581,15 +5633,15 @@ onetime@^5.1.0, onetime@^5.1.2: mimic-fn "^2.1.0" open@^8.4.0: - version "8.4.1" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.1.tgz#2ab3754c07f5d1f99a7a8d6a82737c95e3101cff" - integrity sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg== + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== dependencies: define-lazy-prop "^2.0.0" is-docker "^2.1.1" is-wsl "^2.2.0" -openid-client@^5.1.0: +openid-client@^5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.0.tgz#77f1cda14e2911446f16ea3f455fc7c405103eac" integrity sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ== @@ -5599,7 +5651,7 @@ openid-client@^5.1.0: object-hash "^2.0.1" oidc-token-hash "^5.0.1" -optimism@^0.16.1: +optimism@^0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" integrity sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ== @@ -5784,7 +5836,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -5818,9 +5870,9 @@ preact-render-to-string@^5.1.19: pretty-format "^3.8.0" preact@^10.6.3: - version "10.12.1" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.12.1.tgz#8f9cb5442f560e532729b7d23d42fd1161354a21" - integrity sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg== + version "10.13.2" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.13.2.tgz#2c40c73d57248b57234c4ae6cd9ab9d8186ebc0a" + integrity sha512-q44QFLhOhty2Bd0Y46fnYW0gD/cbVM9dUVtNTDKPcdXSMA7jfY+Jpd6rk3GB0lcQss0z5s/6CmVP0Z/hV+g6pw== prelude-ls@^1.2.1: version "1.2.1" @@ -5828,14 +5880,14 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^2.8.4: - version "2.8.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" - integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== -pretty-format@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.4.3.tgz#25500ada21a53c9e8423205cf0337056b201244c" - integrity sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA== +pretty-format@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" + integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== dependencies: "@jest/schemas" "^29.4.3" ansi-styles "^5.0.0" @@ -5880,6 +5932,11 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +pure-rand@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" + integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + pvtsutils@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" @@ -6354,9 +6411,9 @@ read-package-json@^2.0.0: npm-normalize-package-bin "^1.0.0" readable-stream@^3.4.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -6372,13 +6429,6 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" @@ -6393,11 +6443,6 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -6455,11 +6500,11 @@ resolve-from@^4.0.0: integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve.exports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" - integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" + integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.1: +resolve@^1.10.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -6468,6 +6513,15 @@ resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.20.0, resolve@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^2.0.0-next.4: version "2.0.0-next.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" @@ -6526,7 +6580,7 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6583,10 +6637,10 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: - version "7.3.8" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" - integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== +semver@^7.3.5, semver@^7.3.7: + version "7.4.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" + integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== dependencies: lru-cache "^6.0.0" @@ -6622,9 +6676,9 @@ shebang-regex@^3.0.0: integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@^1.7.3: - version "1.8.0" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.0.tgz#20d078d0eaf71d54f43bd2ba14a1b5b9bfa5c8ba" - integrity sha512-QHsz8GgQIGKlRi24yFc6a6lN69Idnx634w49ay6+jA5yFh7a1UY+4Rp6HPx/L/1zcEDPEij8cIsiqR6bQsE5VQ== + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== side-channel@^1.0.4: version "1.0.4" @@ -6835,6 +6889,15 @@ string.prototype.matchall@^4.0.8: regexp.prototype.flags "^1.4.3" side-channel "^1.0.4" +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -6937,7 +7000,7 @@ symbol-observable@^4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -synckit@^0.8.4: +synckit@^0.8.5: version "0.8.5" resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== @@ -7060,12 +7123,12 @@ ts-node@^10.9.1: yn "3.1.1" tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== dependencies: "@types/json5" "^0.0.29" - json5 "^1.0.1" + json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" @@ -7074,7 +7137,7 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.5.0, tslib@~2.5.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== @@ -7150,9 +7213,9 @@ typescript@^5.0.4: integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== ua-parser-js@^0.7.30: - version "0.7.33" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.33.tgz#1d04acb4ccef9293df6f70f2c3d22f3030d8b532" - integrity sha512-s8ax/CeZdK9R/56Sui0WM6y9OFREJarMRHqLB2EwkovemBxNQ+Bqu8GAsUnVcXKgphb++ghr/B2BZx4mahujPw== + version "0.7.35" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307" + integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g== unbox-primitive@^1.0.2: version "1.0.2" @@ -7281,12 +7344,12 @@ web-streams-polyfill@^3.2.1: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== -webcrypto-core@^1.7.4: - version "1.7.6" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.6.tgz#e32c4a12a13de4251f8f9ef336a6cba7cdec9b55" - integrity sha512-TBPiewB4Buw+HI3EQW+Bexm19/W4cP/qZG/02QJCXN+iN+T5sl074vZ3rJcle/ZtDBQSgjkbsQO/1eFcxnSBUA== +webcrypto-core@^1.7.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" + integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== dependencies: - "@peculiar/asn1-schema" "^2.1.6" + "@peculiar/asn1-schema" "^2.3.6" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" pvtsutils "^1.3.2" @@ -7297,11 +7360,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -whatwg-fetch@^3.4.1: - version "3.6.2" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" - integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -7401,10 +7459,10 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.12.1, ws@^8.12.0: - version "8.12.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" - integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== +ws@8.13.0, ws@^8.12.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== y18n@^4.0.0: version "4.0.3" @@ -7481,9 +7539,9 @@ yargs@^15.3.1: yargs-parser "^18.1.2" yargs@^17.0.0, yargs@^17.3.1: - version "17.6.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" - integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + version "17.7.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" + integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== dependencies: cliui "^8.0.1" escalade "^3.1.1" From 6e1a5a484b2054e0eca168fe58b4860caef8d872 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Apr 2023 14:52:03 +0200 Subject: [PATCH 072/878] Add page stubs for individual data entries (related to #10) --- .../data/calorimetric/CalorimetricData.tsx | 71 ++++++++++ .../data/hygrothermal/HygrothermalData.tsx | 71 ++++++++++ .../components/data/optical/OpticalData.tsx | 71 ++++++++++ .../data/photovoltaic/PhotovoltaicData.tsx | 71 ++++++++++ frontend/package.json | 1 + frontend/pages/data/calorimetric/[uuid].tsx | 22 +++ .../index.tsx} | 17 +-- frontend/pages/data/hygrothermal/[uuid].tsx | 22 +++ .../index.tsx} | 15 +- frontend/pages/data/optical/[uuid].tsx | 22 +++ .../data/{optical.tsx => optical/index.tsx} | 17 +-- frontend/pages/data/photovoltaic/[uuid].tsx | 22 +++ .../index.tsx} | 15 +- frontend/paths.ts | 12 ++ frontend/queries/data.graphql | 36 +++-- frontend/yarn.lock | 129 +++++++++++++++++- 16 files changed, 572 insertions(+), 42 deletions(-) create mode 100644 frontend/components/data/calorimetric/CalorimetricData.tsx create mode 100644 frontend/components/data/hygrothermal/HygrothermalData.tsx create mode 100644 frontend/components/data/optical/OpticalData.tsx create mode 100644 frontend/components/data/photovoltaic/PhotovoltaicData.tsx create mode 100644 frontend/pages/data/calorimetric/[uuid].tsx rename frontend/pages/data/{calorimetric.tsx => calorimetric/index.tsx} (95%) create mode 100644 frontend/pages/data/hygrothermal/[uuid].tsx rename frontend/pages/data/{hygrothermal.tsx => hygrothermal/index.tsx} (94%) create mode 100644 frontend/pages/data/optical/[uuid].tsx rename frontend/pages/data/{optical.tsx => optical/index.tsx} (97%) create mode 100644 frontend/pages/data/photovoltaic/[uuid].tsx rename frontend/pages/data/{photovoltaic.tsx => photovoltaic/index.tsx} (94%) diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx new file mode 100644 index 00000000..048b57df --- /dev/null +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -0,0 +1,71 @@ +import { Scalars } from "../../../__generated__/__types__"; +import { useCalorimetricDataQuery } from "../../../queries/data.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import Link from "next/link"; +import paths from "../../../paths"; +import { messageApolloError } from "../../../lib/apollo"; + +export type CalorimetricDataProps = { + calorimetricDataId: Scalars["Uuid"]; +}; + +export default function CalorimetricData({ calorimetricDataId }: CalorimetricDataProps) { + const { loading, error, data } = useCalorimetricDataQuery({ + variables: { + uuid: calorimetricDataId, + }, + }); + const calorimetricData = data?.calorimetricData; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!calorimetricData) { + return ( + + ); + } + + return ( + , + // ]} + backIcon={false} + > + + {calorimetricData.uuid} + + + {calorimetricData.componentId} + + + + + {calorimetricData.creatorId} + + + + + ); +} diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx new file mode 100644 index 00000000..6342c8e5 --- /dev/null +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -0,0 +1,71 @@ +import { Scalars } from "../../../__generated__/__types__"; +import { useHygrothermalDataQuery } from "../../../queries/data.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import Link from "next/link"; +import paths from "../../../paths"; +import { messageApolloError } from "../../../lib/apollo"; + +export type HygrothermalDataProps = { + hygrothermalDataId: Scalars["Uuid"]; +}; + +export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDataProps) { + const { loading, error, data } = useHygrothermalDataQuery({ + variables: { + uuid: hygrothermalDataId, + }, + }); + const hygrothermalData = data?.hygrothermalData; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!hygrothermalData) { + return ( + + ); + } + + return ( + , + // ]} + backIcon={false} + > + + {hygrothermalData.uuid} + + + {hygrothermalData.componentId} + + + + + {hygrothermalData.creatorId} + + + + + ); +} diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx new file mode 100644 index 00000000..7cb5feee --- /dev/null +++ b/frontend/components/data/optical/OpticalData.tsx @@ -0,0 +1,71 @@ +import { Scalars } from "../../../__generated__/__types__"; +import { useOpticalDataQuery } from "../../../queries/data.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import Link from "next/link"; +import paths from "../../../paths"; +import { messageApolloError } from "../../../lib/apollo"; + +export type OpticalDataProps = { + opticalDataId: Scalars["Uuid"]; +}; + +export default function OpticalData({ opticalDataId }: OpticalDataProps) { + const { loading, error, data } = useOpticalDataQuery({ + variables: { + uuid: opticalDataId, + }, + }); + const opticalData = data?.opticalData; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!opticalData) { + return ( + + ); + } + + return ( + , + // ]} + backIcon={false} + > + + {opticalData.uuid} + + + {opticalData.componentId} + + + + + {opticalData.creatorId} + + + + + ); +} diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx new file mode 100644 index 00000000..48bdb0da --- /dev/null +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -0,0 +1,71 @@ +import { Scalars } from "../../../__generated__/__types__"; +import { usePhotovoltaicDataQuery } from "../../../queries/data.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import Link from "next/link"; +import paths from "../../../paths"; +import { messageApolloError } from "../../../lib/apollo"; + +export type PhotovoltaicDataProps = { + photovoltaicDataId: Scalars["Uuid"]; +}; + +export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDataProps) { + const { loading, error, data } = usePhotovoltaicDataQuery({ + variables: { + uuid: photovoltaicDataId, + }, + }); + const photovoltaicData = data?.photovoltaicData; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!photovoltaicData) { + return ( + + ); + } + + return ( + , + // ]} + backIcon={false} + > + + {photovoltaicData.uuid} + + + {photovoltaicData.componentId} + + + + + {photovoltaicData.creatorId} + + + + + ); +} diff --git a/frontend/package.json b/frontend/package.json index e19ccc7d..0576c476 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,6 +12,7 @@ "start": "next start -p 8080" }, "dependencies": { + "@ant-design/pro-layout": "^7.10.3", "@apollo/client": "^3.7.0", "antd": "^5.0", "crypto-hash": "^2.0.1", diff --git a/frontend/pages/data/calorimetric/[uuid].tsx b/frontend/pages/data/calorimetric/[uuid].tsx new file mode 100644 index 00000000..23cbdc45 --- /dev/null +++ b/frontend/pages/data/calorimetric/[uuid].tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import CalorimetricData from "../../../components/data/calorimetric/CalorimetricData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} + +export default Page; diff --git a/frontend/pages/data/calorimetric.tsx b/frontend/pages/data/calorimetric/index.tsx similarity index 95% rename from frontend/pages/data/calorimetric.tsx rename to frontend/pages/data/calorimetric/index.tsx index 837183de..4531a8f4 100644 --- a/frontend/pages/data/calorimetric.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -1,4 +1,4 @@ -import Layout from "../../components/Layout"; +import Layout from "../../../components/Layout"; import { Table, message, @@ -8,14 +8,14 @@ import { Typography, Descriptions, } from "antd"; -import { useAllCalorimetricDataQuery } from "../../queries/data.graphql"; +import { useAllCalorimetricDataQuery } from "../../../queries/data.graphql"; import { CalorimetricData, Scalars, CalorimetricDataPropositionInput, -} from "../../__generated__/__types__"; +} from "../../../__generated__/__types__"; import { useState } from "react"; -import { setMapValue } from "../../lib/freeTextFilter"; +import { setMapValue } from "../../../lib/freeTextFilter"; import { getAppliedMethodColumnProps, getComponentUuidColumnProps, @@ -24,15 +24,16 @@ import { getResourceTreeColumnProps, getTimestampColumnProps, getUuidColumnProps, -} from "../../lib/table"; +} from "../../../lib/table"; import { FloatPropositionComparator, FloatPropositionFormList, -} from "../../components/FloatPropositionFormList"; +} from "../../../components/FloatPropositionFormList"; import { UuidPropositionComparator, UuidPropositionFormList, -} from "../../components/UuidPropositionFormList"; +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; const layout = { labelCol: { span: 8 }, @@ -259,7 +260,7 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - (_uuid) => "/" // TODO Link somewhere useful! + paths.calorimetricDatum ), }, { diff --git a/frontend/pages/data/hygrothermal/[uuid].tsx b/frontend/pages/data/hygrothermal/[uuid].tsx new file mode 100644 index 00000000..753c898c --- /dev/null +++ b/frontend/pages/data/hygrothermal/[uuid].tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import HygrothermalData from "../../../components/data/hygrothermal/HygrothermalData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} + +export default Page; diff --git a/frontend/pages/data/hygrothermal.tsx b/frontend/pages/data/hygrothermal/index.tsx similarity index 94% rename from frontend/pages/data/hygrothermal.tsx rename to frontend/pages/data/hygrothermal/index.tsx index 807dd006..23fe5bb5 100644 --- a/frontend/pages/data/hygrothermal.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -1,13 +1,13 @@ -import Layout from "../../components/Layout"; +import Layout from "../../../components/Layout"; import { Table, message, Form, Button, Alert, Typography } from "antd"; -import { useAllHygrothermalDataQuery } from "../../queries/data.graphql"; +import { useAllHygrothermalDataQuery } from "../../../queries/data.graphql"; import { HygrothermalData, Scalars, HygrothermalDataPropositionInput, -} from "../../__generated__/__types__"; +} from "../../../__generated__/__types__"; import { useState } from "react"; -import { setMapValue } from "../../lib/freeTextFilter"; +import { setMapValue } from "../../../lib/freeTextFilter"; import { getAppliedMethodColumnProps, getComponentUuidColumnProps, @@ -16,11 +16,12 @@ import { getResourceTreeColumnProps, getTimestampColumnProps, getUuidColumnProps, -} from "../../lib/table"; +} from "../../../lib/table"; import { UuidPropositionComparator, UuidPropositionFormList, -} from "../../components/UuidPropositionFormList"; +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; const layout = { labelCol: { span: 8 }, @@ -197,7 +198,7 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - (_uuid) => "/" // TODO Link somewhere useful! + paths.hygrothermalDatum ), }, { diff --git a/frontend/pages/data/optical/[uuid].tsx b/frontend/pages/data/optical/[uuid].tsx new file mode 100644 index 00000000..d6129cb9 --- /dev/null +++ b/frontend/pages/data/optical/[uuid].tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import OpticalData from "../../../components/data/optical/OpticalData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} + +export default Page; diff --git a/frontend/pages/data/optical.tsx b/frontend/pages/data/optical/index.tsx similarity index 97% rename from frontend/pages/data/optical.tsx rename to frontend/pages/data/optical/index.tsx index 0215596d..4571597b 100644 --- a/frontend/pages/data/optical.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -1,4 +1,4 @@ -import Layout from "../../components/Layout"; +import Layout from "../../../components/Layout"; import { Table, message, @@ -8,14 +8,14 @@ import { Typography, Descriptions, } from "antd"; -import { useAllOpticalDataQuery } from "../../queries/data.graphql"; +import { useAllOpticalDataQuery } from "../../../queries/data.graphql"; import { OpticalData, Scalars, OpticalDataPropositionInput, -} from "../../__generated__/__types__"; +} from "../../../__generated__/__types__"; import { useState } from "react"; -import { setMapValue } from "../../lib/freeTextFilter"; +import { setMapValue } from "../../../lib/freeTextFilter"; import { getAppliedMethodColumnProps, getComponentUuidColumnProps, @@ -24,15 +24,16 @@ import { getResourceTreeColumnProps, getTimestampColumnProps, getUuidColumnProps, -} from "../../lib/table"; +} from "../../../lib/table"; import { FloatPropositionComparator, FloatPropositionFormList, -} from "../../components/FloatPropositionFormList"; +} from "../../../components/FloatPropositionFormList"; import { UuidPropositionComparator, UuidPropositionFormList, -} from "../../components/UuidPropositionFormList"; +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; const layout = { labelCol: { span: 8 }, @@ -362,7 +363,7 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - (_uuid) => "/" // TODO Link somewhere useful! + paths.opticalDatum ), }, { diff --git a/frontend/pages/data/photovoltaic/[uuid].tsx b/frontend/pages/data/photovoltaic/[uuid].tsx new file mode 100644 index 00000000..708e4e3e --- /dev/null +++ b/frontend/pages/data/photovoltaic/[uuid].tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import PhotovoltaicData from "../../../components/data/photovoltaic/PhotovoltaicData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} + +export default Page; diff --git a/frontend/pages/data/photovoltaic.tsx b/frontend/pages/data/photovoltaic/index.tsx similarity index 94% rename from frontend/pages/data/photovoltaic.tsx rename to frontend/pages/data/photovoltaic/index.tsx index 25717211..4001ee3e 100644 --- a/frontend/pages/data/photovoltaic.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -1,13 +1,13 @@ -import Layout from "../../components/Layout"; +import Layout from "../../../components/Layout"; import { Table, message, Form, Button, Alert, Typography } from "antd"; -import { useAllPhotovoltaicDataQuery } from "../../queries/data.graphql"; +import { useAllPhotovoltaicDataQuery } from "../../../queries/data.graphql"; import { PhotovoltaicData, Scalars, PhotovoltaicDataPropositionInput, -} from "../../__generated__/__types__"; +} from "../../../__generated__/__types__"; import { useState } from "react"; -import { setMapValue } from "../../lib/freeTextFilter"; +import { setMapValue } from "../../../lib/freeTextFilter"; import { getAppliedMethodColumnProps, getComponentUuidColumnProps, @@ -16,11 +16,12 @@ import { getResourceTreeColumnProps, getTimestampColumnProps, getUuidColumnProps, -} from "../../lib/table"; +} from "../../../lib/table"; import { UuidPropositionComparator, UuidPropositionFormList, -} from "../../components/UuidPropositionFormList"; +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; const layout = { labelCol: { span: 8 }, @@ -197,7 +198,7 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - (_uuid) => "/" // TODO Link somewhere useful! + paths.photovoltaicDatum ), }, { diff --git a/frontend/paths.ts b/frontend/paths.ts index 29ca08f5..d3fe1983 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -4,9 +4,21 @@ export default { dataProtectionInformation: "/data-protection-information", data: "/data", calorimetricData: "/data/calorimetric", + calorimetricDatum(uuid: string) { + return `/data/calorimetric/${encodeURIComponent(uuid)}`; + }, hygrothermalData: "/data/hygrothermal", + hygrothermalDatum(uuid: string) { + return `/data/hygrothermal/${encodeURIComponent(uuid)}`; + }, opticalData: "/data/optical", + opticalDatum(uuid: string) { + return `/data/optical/${encodeURIComponent(uuid)}`; + }, photovoltaicData: "/data/photovoltaic", + photovoltaicDatum(uuid: string) { + return `/data/photovoltaic/${encodeURIComponent(uuid)}`; + }, createData: "/data/create", uploadFile: "/upload-file", metabase: { diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 55d29ba7..33b75bb1 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -74,6 +74,12 @@ fragment DataPartial on Data { warnings } +query Data($uuid: Uuid!) { + data(id: $uuid) { + ...DataPartial + } +} + query AllData($where: DataPropositionInput) { allData(where: $where) { nodes { @@ -92,18 +98,12 @@ fragment CalorimetricDataPartial on CalorimetricData { uValues } -query AllCalorimetricData($where: CalorimetricDataPropositionInput) { - allCalorimetricData(where: $where) { - nodes { - ...CalorimetricDataPartial - } +query CalorimetricData($uuid: Uuid!) { + calorimetricData(id: $uuid) { + ...CalorimetricDataPartial } } -# query HasCalorimetricData($where: DataPropositionInput!) { -# hasCalorimetricData(where: $where) -# } - query AllCalorimetricData($where: CalorimetricDataPropositionInput) { allCalorimetricData(where: $where) { nodes { @@ -120,6 +120,12 @@ fragment HygrothermalDataPartial on HygrothermalData { ...DataPartial } +query HygrothermalData($uuid: Uuid!) { + hygrothermalData(id: $uuid) { + ...HygrothermalDataPartial + } +} + query AllHygrothermalData($where: HygrothermalDataPropositionInput) { allHygrothermalData(where: $where) { nodes { @@ -141,6 +147,12 @@ fragment OpticalDataPartial on OpticalData { nearnormalHemisphericalVisibleTransmittances } +query OpticalData($uuid: Uuid!) { + opticalData(id: $uuid) { + ...OpticalDataPartial + } +} + query AllOpticalData($where: OpticalDataPropositionInput) { allOpticalData(where: $where) { nodes { @@ -157,6 +169,12 @@ fragment PhotovoltaicDataPartial on PhotovoltaicData { ...DataPartial } +query PhotovoltaicData($uuid: Uuid!) { + photovoltaicData(id: $uuid) { + ...PhotovoltaicDataPartial + } +} + query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { allPhotovoltaicData(where: $where) { nodes { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index da193c4e..f348657e 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -10,6 +10,13 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ant-design/colors@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" + integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ== + dependencies: + "@ctrl/tinycolor" "^3.4.0" + "@ant-design/colors@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" @@ -35,6 +42,17 @@ resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a" integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw== +"@ant-design/icons@^4.3.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.8.0.tgz#3084e2bb494cac3dad6c0392f77c1efc90ee1fa4" + integrity sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg== + dependencies: + "@ant-design/colors" "^6.0.0" + "@ant-design/icons-svg" "^4.2.1" + "@babel/runtime" "^7.11.2" + classnames "^2.2.6" + rc-util "^5.9.4" + "@ant-design/icons@^5.0.0": version "5.0.1" resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.0.1.tgz#febb1fdc5776f58187b2c953ac9a4496069d045b" @@ -46,6 +64,53 @@ classnames "^2.2.6" rc-util "^5.9.4" +"@ant-design/pro-layout@^7.10.3": + version "7.10.3" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.10.3.tgz#742a29d41e29dd42f7247d6188424be5dcde53f9" + integrity sha512-r8S+KqtyKFmt9eG5/G7KS2O8oIpmxZA3/WIYGMbSOyD4YkwmK1MF9KCjUErdHxQxIS9IL8Fc6V23/8KVAZmZyQ== + dependencies: + "@ant-design/icons" "^5.0.0" + "@ant-design/pro-provider" "^2.7.0" + "@ant-design/pro-utils" "^2.7.2" + "@babel/runtime" "^7.18.0" + "@umijs/route-utils" "^4.0.0" + "@umijs/use-params" "^1.0.9" + classnames "^2.2.6" + lodash.merge "^4.6.2" + omit.js "^2.0.2" + path-to-regexp "2.4.0" + rc-resize-observer "^1.1.0" + rc-util "^5.0.6" + swr "^2.0.0" + use-json-comparison "^1.0.3" + use-media-antd-query "^1.1.0" + warning "^4.0.3" + +"@ant-design/pro-provider@^2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.7.0.tgz#3d9b708c1d6753c53c96c348ca3e68963eb629ee" + integrity sha512-5iKr0rIGekmOH7t5C6k0AsNp8CROroTzewjnaYLF3gt+kdz4+q8IQ5rmQG78e0npcJp6yQLJvIxevuJl5kFNfw== + dependencies: + "@ant-design/cssinjs" "^1.5.6" + "@babel/runtime" "^7.18.0" + "@ctrl/tinycolor" "^3.4.0" + rc-util "^5.0.1" + swr "^2.0.0" + +"@ant-design/pro-utils@^2.7.2": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.7.2.tgz#42435147b037e5c2a13f404fd02bd9e10f9a974e" + integrity sha512-1nzw9x4Hy/LXPUGWvCaFQd1g5SkX86yDp9KFZ3m4gvVREo4/7QTp0iyZasEWweaND3JNLcaXXU8sLfMiMgHIiw== + dependencies: + "@ant-design/icons" "^4.3.0" + "@ant-design/pro-provider" "^2.7.0" + "@babel/runtime" "^7.18.0" + classnames "^2.2.6" + dayjs "^1.11.4" + rc-util "^5.0.6" + react-sortable-hoc "^2.0.0" + swr "^2.0.0" + "@ant-design/react-slick@~1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.0.tgz#4696eecaa2dea0429e47ae24c267015cfd6df35c" @@ -725,7 +790,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== @@ -2203,6 +2268,16 @@ "@typescript-eslint/types" "5.58.0" eslint-visitor-keys "^3.3.0" +"@umijs/route-utils@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-4.0.1.tgz#156df5b3f2328059722d3ee7dd8f65e18c3cde8b" + integrity sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ== + +"@umijs/use-params@^1.0.9": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" + integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== + "@whatwg-node/events@^0.0.2": version "0.0.2" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.2.tgz#7b7107268d2982fc7b7aff5ee6803c64018f84dd" @@ -3113,7 +3188,7 @@ dataloader@2.2.2, dataloader@^2.2.2: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== -dayjs@^1.0, dayjs@^1.11.1: +dayjs@^1.0, dayjs@^1.11.1, dayjs@^1.11.4: version "1.11.7" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== @@ -5618,6 +5693,11 @@ oidc-token-hash@^5.0.1: resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.2.tgz#f9ca7f7f1f92d721a2973e66b7430cb52a486648" integrity sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ== +omit.js@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f" + integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5826,6 +5906,11 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-to-regexp@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" + integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -5913,7 +5998,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6370,6 +6455,15 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" +react-sortable-hoc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" + integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg== + dependencies: + "@babel/runtime" "^7.2.0" + invariant "^2.2.4" + prop-types "^15.5.7" + react-test-renderer@^18.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" @@ -6995,6 +7089,13 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" +swr@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.3.tgz#ae3608d4dc19f75121e0b51d1982735fbf02f52e" + integrity sha512-g3ApxIM4Fjbd6vvEAlW60hJlKcYxHb+wtehogTygrh6Jsw7wNagv9m4Oj5Gq6zvvZw0tcyhVGL9L0oISvl3sUw== + dependencies: + use-sync-external-store "^1.2.0" + symbol-observable@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" @@ -7283,6 +7384,21 @@ urlpattern-polyfill@^6.0.2: dependencies: braces "^3.0.2" +use-json-comparison@^1.0.3: + version "1.0.6" + resolved "https://registry.yarnpkg.com/use-json-comparison/-/use-json-comparison-1.0.6.tgz#a012bbc258ce745db1f56745dc653f575226cb21" + integrity sha512-xPadt5yMRbEmVfOSGFSMqjjICrq7nLbfSH3rYIXsrtcuFX7PmbYDN/ku8ObBn3v8o/yZelO1OxUS5+5TI3+fUw== + +use-media-antd-query@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/use-media-antd-query/-/use-media-antd-query-1.1.0.tgz#f083ad7e292c1c0261b6bbfaac0edc3e0920d85d" + integrity sha512-B6kKZwNV4R+l4Rl11sWO7HqOay9alzs1Vp1b4YJqjz33YxbltBCZtt/yxXxkXN9rc1S7OeEL/GbwC30Wmqhw6Q== + +use-sync-external-store@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -7332,6 +7448,13 @@ walker@^1.0.7, walker@^1.0.8: dependencies: makeerror "1.0.12" +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" From 02686110ec589e4e2ef0e06730951bfb31a89655 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Apr 2023 16:08:51 +0200 Subject: [PATCH 073/878] Make the metabase accessible under its URL --- .env.sample | 3 ++- docker-compose.yml | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.sample b/.env.sample index c94f4e4f..69762e13 100644 --- a/.env.sample +++ b/.env.sample @@ -4,7 +4,8 @@ HOST=local.solarbuildingenvelopes.com HTTP_PORT=5050 HTTPS_PORT=5051 -METABASE_HOST=local.buildingenvelopedata.org:4041 +METABASE_HOST=local.buildingenvelopedata.org +METABASE_HTTPS_PORT=4041 ACCESS_TOKEN=secret OPEN_ID_CONNECT_CLIENT_SECRET=secret diff --git a/docker-compose.yml b/docker-compose.yml index b0de8ac7..64f2ffe4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,7 +101,7 @@ services: - ASPNETCORE_URLS=http://0.0.0.0:8080 - ASPNETCORE_ENVIRONMENT=development - XBASE_Host=https://${HOST}:${HTTPS_PORT} - - XBASE_MetabaseHost=https://${METABASE_HOST} + - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} @@ -112,8 +112,7 @@ services: # - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx extra_hosts: - ${HOST}:host-gateway - # - ${ISE_HOST}:host-gateway - # - ${LBNL_HOST}:host-gateway + - ${METABASE_HOST}:host-gateway depends_on: - database - email From b49eada672be337d0940afdfcdb334c608e098d8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Apr 2023 16:11:15 +0200 Subject: [PATCH 074/878] Configure schema stitching (we cannot use it though as it does not support custom scalars yet as mentioned on https://github.com/ChilliCream/graphql-platform/issues/3760#issuecomment-1169759515) --- .../src/Configuration/GraphQlConfiguration.cs | 173 +++++++++++------- backend/src/Database.csproj | 1 + backend/src/Startup.cs | 2 +- 3 files changed, 106 insertions(+), 70 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index bb2bdbf3..9a55bef6 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -13,24 +13,48 @@ using System.Text.RegularExpressions; using Microsoft.AspNetCore.Authentication.Cookies; using Database.GraphQl.Filters; +using System.Net.Http; namespace Database.Configuration { public static class GraphQlConfiguration { + public static class WellKnownSchemaNames + { + public const string Metabase = "metabase"; + } + public static void ConfigureServices( IServiceCollection services, - IWebHostEnvironment environment + IWebHostEnvironment environment, + AppSettings appSettings ) { + // Stitching Services + // https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching + var httpClientBuilder = services.AddHttpClient( + WellKnownSchemaNames.Metabase, + _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") + ); + if (!environment.IsProduction()) + { + httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler + { + // ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } + ); + } + // Automatic-Persisted-Queries Services services .AddMemoryCache() - .AddSha256DocumentHashProvider(HotChocolate.Language.HashFormat.Hex) + .AddSha256DocumentHashProvider(HotChocolate.Language.HashFormat.Hex); + // GraphQL Server + services .AddGraphQLServer() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContext(DbContextKind.Pooled) - // Types - .AddType() // Extensions .AddProjections() .AddFiltering() @@ -60,72 +84,83 @@ IWebHostEnvironment environment /* options.QueryCacheSize = ...; */ /* options.UseComplexityMultipliers = ...; */ } + ) + // TODO Configure `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. But how? + // Subscriptions + /* .AddInMemorySubscriptions() */ + // TODO Persisted queries + /* .AddFileSystemQueryStorage("./persisted_queries") */ + /* .UsePersistedQueryPipeline(); */ + .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => + { + // HotChocolate uses the default cookie authentication + // scheme `IdentityConstants.ApplicationScheme` by + // default. + var authenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + if (authenticateResult.Succeeded && authenticateResult.Principal is not null) + { + httpContext.User = authenticateResult.Principal; + } + }) + .AddDiagnosticEventListener(_ => + new GraphQl.LoggingDiagnosticEventListener( + _.GetApplicationService>() ) - // TODO Configure `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. But how? - // Subscriptions - /* .AddInMemorySubscriptions() */ - // TODO Persisted queries - /* .AddFileSystemQueryStorage("./persisted_queries") */ - /* .UsePersistedQueryPipeline(); */ - .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => - { - // HotChocolate uses the default cookie authentication - // scheme `IdentityConstants.ApplicationScheme` by - // default. - var authenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); - if (authenticateResult.Succeeded && authenticateResult.Principal is not null) - { - httpContext.User = authenticateResult.Principal; - } - }) - .AddDiagnosticEventListener(_ => - new GraphQl.LoggingDiagnosticEventListener( - _.GetApplicationService>() - ) - ) - .AddQueryType(d => d.Name(nameof(GraphQl.Query))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddMutationType(d => d.Name(nameof(GraphQl.Mutation))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ - /* .AddType() */ - // Scalar Types - .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type - .AddType(new UrlType("Url")) - .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar - .AddType(new GraphQl.LocaleType()) - // Object Types - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - // Data Loaders - /* .AddDataLoader() */ - // Paging - .SetPagingOptions( - new PagingOptions - { - MaxPageSize = int.MaxValue, - DefaultPageSize = int.MaxValue, - IncludeTotalCount = true, - // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. - InferConnectionNameFromField = false - } - ) - .UseAutomaticPersistedQueryPipeline() - .AddInMemoryQueryStorage(); + ) + // Scalar Types + .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type + .AddType(new UrlType("Url")) + .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar + .AddType(new GraphQl.LocaleType()) + // Query Types + .AddQueryType(d => d.Name(nameof(GraphQl.Query))) + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + // Mutation Types + .AddMutationType(d => d.Name(nameof(GraphQl.Mutation))) + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ + /* .AddType() */ + // Object Types + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + // Data Loaders + /* .AddDataLoader() */ + // Paging + .SetPagingOptions( + new PagingOptions + { + MaxPageSize = int.MaxValue, + DefaultPageSize = int.MaxValue, + IncludeTotalCount = true, + // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. + InferConnectionNameFromField = false + } + ) + // Stitching + // .AddRemoteSchema(WellKnownSchemaNames.Metabase, ignoreRootTypes: true) + // .AddTypeExtensionsFromString(@" + // extend type OpticalData { + // component: Component @delegate(schema: 'Metabase', path: 'component(uuid: $fields:ComponentId)') + // } + // ") + // Automatic Peristed Queries + .UseAutomaticPersistedQueryPipeline() + .AddInMemoryQueryStorage(); } } diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 258131e1..5373178e 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -20,6 +20,7 @@ + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 0f2dd4b2..7e044973 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -39,7 +39,7 @@ IConfiguration configuration public void ConfigureServices(IServiceCollection services) { AuthConfiguration.ConfigureServices(services, _appSettings); - GraphQlConfiguration.ConfigureServices(services, _environment); + GraphQlConfiguration.ConfigureServices(services, _environment, _appSettings); ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); From 5dd0a94bcfbd3de69a3b29f0a07be406617fbf83 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 19 Apr 2023 13:11:55 +0200 Subject: [PATCH 075/878] Add GraphQL query `verificationCode` (it is used by the metabase to assert that the user who added a database has control over its GraphQL endpoint) --- .env.production.sample | 1 + .env.sample | 1 + backend/src/AppSettings.cs | 3 +++ .../src/Configuration/GraphQlConfiguration.cs | 1 + backend/src/GraphQl/VerificationCodeQueries.cs | 16 ++++++++++++++++ docker-compose.production.yml | 1 + docker-compose.yml | 1 + frontend/type-defs.graphqls | 1 + 8 files changed, 25 insertions(+) create mode 100644 backend/src/GraphQl/VerificationCodeQueries.cs diff --git a/.env.production.sample b/.env.production.sample index 8ba75e77..3cd2f4e4 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -7,6 +7,7 @@ METABASE_HOST=www.buildingenvelopedata.org ACCESS_TOKEN= OPEN_ID_CONNECT_CLIENT_SECRET= +VERIFICATION_CODE= SMTP_HOST=mail-mtaKA26.fraunhofer.de SMTP_PORT=25 diff --git a/.env.sample b/.env.sample index 69762e13..ca58ec11 100644 --- a/.env.sample +++ b/.env.sample @@ -9,6 +9,7 @@ METABASE_HTTPS_PORT=4041 ACCESS_TOKEN=secret OPEN_ID_CONNECT_CLIENT_SECRET=secret +VERIFICATION_CODE=secret CERTIFICATE_AUTHORITY_HOST=ca.org CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 9defb15b..7c680415 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -13,6 +13,9 @@ public sealed class AppSettings public string AccessToken { get; set; } = ""; + public string VerificationCode { get; set; } + = ""; + public LoggingSettings Logging { get; set; } = new LoggingSettings(); diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 9a55bef6..bbf761fa 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -114,6 +114,7 @@ AppSettings appSettings .AddType(new GraphQl.LocaleType()) // Query Types .AddQueryType(d => d.Name(nameof(GraphQl.Query))) + .AddType() .AddType() .AddType() .AddType() diff --git a/backend/src/GraphQl/VerificationCodeQueries.cs b/backend/src/GraphQl/VerificationCodeQueries.cs new file mode 100644 index 00000000..258479ae --- /dev/null +++ b/backend/src/GraphQl/VerificationCodeQueries.cs @@ -0,0 +1,16 @@ +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl +{ + [ExtendObjectType(nameof(Query))] + public sealed class VerificationCodeQueries + { + public string GetVerificationCode( + [Service] AppSettings appSettings + ) + { + return appSettings.VerificationCode; + } + } +} \ No newline at end of file diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 940f0eef..6991a95b 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -73,6 +73,7 @@ services: - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_VerificationCode=${VERIFICATION_CODE} volumes: - backend_logs:/app/logs - files:/app/files diff --git a/docker-compose.yml b/docker-compose.yml index 64f2ffe4..7f0c4737 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -106,6 +106,7 @@ services: - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_VerificationCode=${VERIFICATION_CODE} - DOTNET_USE_POLLING_FILE_WATCHER=1 # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose # - ASPNETCORE_Kestrel__Certificates__Default__Password=${SSL_CERTIFICATE_PASSWORD} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 34391ff1..d6b30456 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -397,6 +397,7 @@ type Query { nodes("The list of node IDs." ids: [ID!]!): [Node]! opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData + verificationCode: String! } type ToTreeVertexAppliedConversionMethod { From 48da888bb281d978c252afa471dd6f687428d170 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 19 Apr 2023 15:03:25 +0200 Subject: [PATCH 076/878] Describe the TestLabDB solarbuildingenvelopes.com on its home page --- closes #42 --- frontend/pages/index.tsx | 146 ++++++++++++++++++++++++++++++++--- frontend/public/overview.png | Bin 0 -> 79720 bytes 2 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 frontend/public/overview.png diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index f24a3413..8d88a145 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -2,31 +2,155 @@ import Layout from "../components/Layout"; import { Typography } from "antd"; import Link from "next/link"; import paths from "../paths"; +import Image from "next/image"; +import overviewImage from "../public/overview.png"; function Page() { return (
- solarbuildingenvelopes.com is an - example of a database in the network{" "} - - Building Envelope Data - - . It provides access to some data of the{" "} + solarbuildingenvelopes.com is the + website of the product data server of{" "} - TestLab Solar Façades + TestLab Solar Facades {" "} - which is an accredited laboratory at{" "} + at{" "} - Fraunhofer Institute for Solar Energy Systems (ISE) + Fraunhofer Institute for Solar Energy Systems ISE + + . The product data server is part of the Product Data Network{" "} + + buildingenvelopedata.org + + . The product data server is an instance of the{" "} + + reference implementation of a database - . The database is a live instance of the{" "} + . The{" "} - reference implementation + reference implementation of a database + {" "} + is open-source with a permissive license, so that everyone can easily + create their own product data server as part of the Product Data + Network{" "} + + buildingenvelopedata.org . + + This website is the frontend of the product data server of{" "} + + TestLab Solar Facades + + . You can use this website to search this product data server for{" "} + optical data,{" "} + calorimetric data and{" "} + photovoltaic data. If you + would like to search the entire Product Data Network{" "} + + buildingenvelopedata.org + + , you can search there for example for{" "} + + optical data{" "} + + . You will find there also an overview about all building envelope{" "} + + {" "} + components + + ,{" "} + + institutions + + ,{" "} + + data formats + + ,{" "} + + methods + {" "} + and{" "} + + databases + {" "} + of the Product Data Network{" "} + + buildingenvelopedata.org + + . + + + This website is completely based on the + + {" "} + GraphQL endpoint + + . The{" "} + + GraphQL endpoint + {" "} + is the Application Programming Interface (API) to the backend of the + product data server. If you like programming, you can use the endpoint + to automate the interaction with this product data server. You find + the{" "} + + specification of the API on GitHub + {" "} + which is especially helpful if you develop software to plan buildings. + The{" "} + + GraphQL endpoint of the metabase + {" "} + is then a convenient way to query for data, components, institutions, + databases, data formats and methods. + + + The data format{" "} + + BED-JSON + {" "} + is a general format for optical, calorimetric and photovoltaic data + sets. It is defined by the{" "} + + JSON Schemas of the API specification + + . Other{" "} + + data formats + {" "} + are available, too. + + Schematic depiction of how users like architects, planners, or engineers can use the metabase to find products and data in and across databases.
); diff --git a/frontend/public/overview.png b/frontend/public/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..a7a3741039274701b7031a9f3941296800ab8635 GIT binary patch literal 79720 zcmeFYhgVZw7dMCn3yPr91QD@;R8fk6fPjF~r4vYKqJ%0n5}Fl!s3Ovv^cq5MF+oK@ zKw2oFg-9sKd_2&&I;S!v5&t z0}~dOW2P)Dhlfra1HQ?Y{BiJ)L%t?D8Y~sP7ngyXqtDb0)LB@n;!f>3umbldpFgzr zWntkxd+<5bbz@VFg@sx0=z+S~3&<+|_{(cLfeq_=hmStgfQ?UNR`dszX^EV<_Wcz* zpYOGauR6!Z_-@&My(Wx=ZfORlPLcd4N;_^pBzov~Rt>0R4gLH?)oP9Zm-hW; zh+h^%B|bKrHbdgGzLOJE;^!1`!v;@5zG$=`+qLF zx%dZe^wM?~ysdfPW@zv}_uR1V<}(|=nkPB_E9{5H!y$u{0&c05_q-aWu4*t5xH?<7 zhki-LpxgB?_2waVszzl4{Z>kG!-_hki&x1({uAhS5TZdw%g=V$Lv*9cFPDtL(~Mn& zw!i@^(_E{YYQs*_ULn4AIEdRTeugA(PcEv0PE6z2SVr-fCid@@)5+4wE zZ|03J$O!&(yUQ}UU26doB@z5LRywNVV{XD~i>XsCyh>hJ&oS333xL{0#o{uB_e;u4 z4;GQhn>qb3h$XOM?x1(c*@CH;*qeUQ0y1we{C7cn!IeZbzBoQ_Dm@SK%@&!zHu7Si z0$8yJZSm1klU_G&%0$ce&lOvK@G^7KRikH8tMJvsj)-H!DJE(nO%BDLlI+41vge1G zn-t=cx_*iQ2ls#9opPBHITc{GUf#775OZ5M+ls zc1*6=&jeX)>9SO?qPlcPNw7$xd%UFL&QklQfSAX-rzvKg+#9=P%n`-q+BgauQ@u27 z5uNp>8Pbq|8>HrMnySHe4JSefW$XeL;D&6cpNke@iI+;V6I#;YSDzGxU-_4~e5DqD zwgpK1(kt9Eie@=q(rj;LI@L|xAi=9@-Sk~>4^6#}mbx~8+lmED$1}S<>39RiqPo-CuFmvF_4Q z6QSPH?C>{mO?5PHG`~@4cbR68zbRo&RKRrTgBiF&`xT{FsQWT|nT#`HqNMZ^)pbH( z>lto14!NIs>k$FfG1fm)N~BT?O)rud{e>8FAla3oCxTOl=(*o?7ZkKB|RW znGke#>T>NEk|tUD2fdLk*8ULMkmvRQm(Sn5f-^!{A&T_;@pTv9@UL&FETc)`|CH%S zYZCr77wz}auVH9_2k7lvC72=tgpUHbNQwO8+1k?t?Xkw!$z4t`ZLC$PInn&h_87q;OK$rYQUm zoVsoW6>im#yj}HEx3g|i$i@Sn#SOdOqC;=AV)#i5-cP8=PSTB~ZRsYyXo`fQ2y5sJ@r(Y)ckHpAsTsV0P$fC*!# zNB)96df19X1*O@u6&1K{nW2_e!`6?^|FdRa-yW^93yowl@!-B5}tU^k{xOvohzUS65aq5RytbyMx1cBy$Txbr%TEzO1~ zhp#K)V<{$JJ9g!=9GQPb_Xfa!%PNQ);#P&irDC7v% z++EKVdM(Ha!bo$a3O*%aVE;4t_8LRtFK})uF*iwKU(K5;B9~lQQT!KHVva<5vR|W2 z-DES*vJ~Lnx-jmE3g-g$0L2~AU>6&v-SdN#s+?cR9w@nj0bdWEBD*@bOd%OWzcVZB zsL^gAl$&`r>-{V0@GJYnL1`{b zz`Nl}r|j&cb=08$Dq-cT749!YbEFly;d`TL-0R_78%^Dj5)+i^5RM!m0Rl{%&0Ki& ziret9SIq(JVwt(7UhJ7T_z^>u5m1vIvln-6oa}n_fe2+vuS|=fyjqw%TO4XMP8jZm z%K3pUkskjCAsxQT#pa6N7dLnbaXDcncGp4TWtcrm6zu8da9sc~fJ7FM)y*32k<7lU|ON3PsgSw^iEsB57ieqbWK z6FHD__`f=FJ{L3%s04hT0vC&>%!Osy<$)X!+N|b=W#=H=Lkw(yITS)Zl-D<~Xx?BA zp6aez+A$xq^VsRfEHZjl7E2(H2>ajRZ#wrX z*K*oa9jV4eMe>w$Sj!BW9(ZCugOkz3vsiGqftE%s8dI{Pd}GEjLSDt~f2;6Q$V^X1 zJsp>LdDdk1Jq>^RZHHA_a9vwR#wOv^H%4hvc>3}vmWm8{US!3;uVzCv2ymjA+3li| zoo7dDAF*gwR|MlMH}G22^f}bm=XH9>Bv|Meme?mMa%A>REf9mhbQoWO(Fm0qvzwBt z{JHc;60NKjm0w2pAy31Qm5n}$jaLvsRc-H78?&u5%ywpkczGGB^k_|x5UV{Of5$~% zGb@Qpzw?5k@b^O7JlXoHYhT2-T~CsiZfGH4NnN6h`Kqnt@fHeZ({YP1$nN}n8ky;f zGX$Pygr|^q&v#?-U8-+_dHJGs;p{>Jeu}AEY^P8ErC95E4N=Ev4ygH@YBc`x)e^je9Y^?B^jcfyL%eefs!Sc1r7HbkC0dK= zZPlsr9Qm^W#QgpM2|vxic>IU`F4b8zNp`|j_E#*Qjjr~>)DXci)pqdm?4d1(t6iPj zf%%r7jq=R_uALr^*VDO_qhWty|CDOuDW_#iE_vT?x1|aXKi#q%g)c@}?fvK~wWCah ze4E*v;_wo?T&(#uM7D<~?F;4tCZ7+Vr>s<8yQ5Sn});?8F&M?HP=6udTvpv#~1j@~iD$^t1D4aVAO@qJq+rJ>=T-Z%#8N zUl6C(q*srv*KLR5Q?fGa8?LPSt~RuS?dj|4dejC3)Un_86B2zWuWZ_yPy+d|KrYT_ zqYr0m7>h2lv|;7|+>*qK0o)4j4r*D=$Z~}7D+Dk(?>ALKnV3noH_X}YnRwy10$v0R zSaUy0e9?`?X*>h*;CPC7TR#8xu zUM@l~ziuc_ko-i(IMoHia8j^fxAQDNuv&fdbCg0{Xm@A6Ro;AlIe z#S6ckQ6p{kM$NyH&6si2Yt5GIo_V?)@>Rd4c7raENbc?F0oOzXQ_VKOpXcu@(tb~6 zS-%NZo)0y=PH+QAQ?=Pc))D0Sq zMtGQrY?avZ7#UQtXhcI!tg+T}EDoLe+*1@~`yz|q4jaV6N8vj4ElertxR`TdC|8AI zyYKpaJq2Y259NNJr2R{&QqsODO`XmDw-G`8y7|Zj@`aEfIRAL zbz(moVT{W#Q_`vFzku&>lJHc1efSx;h6R+gR;bFwL=;W9vGzxWY%w8RZLzhgw+0=f zVby*)BHfnJtR4!nEdkE6%K>*5-R0PAZt2>BK@ z`L52C>;JJ_neB$KUi85621m#{&b{lZJYfyKi@kw12CXa}pColIK8A>YT03dce@}qPtP<`kYC2fALUmgun zlA52p8fU9?MWVUI)6y_@swLg$_R~Xu&Anz3o1nNoW{S;Qy!|8l03&~^4Nc)P0Qt9; z+_us@Hntzy(u}m&`6BV-58>X;C~NLKR8he@ZuXnsJo|%1A=&Ue_qiCS;irs+QFfA@ ze1u%_DiXf~xv24?a0H5A(=YO*t<5;DA9qS4?|*Ja&Y@DcZ=x~171TG_G9iM`?Pcye z5q~}6<59p(KNZaM5X>27}$?}T;zD!cND9O^c?2^PXH zp6P;JrAZZzCvkq)BZ0DOeKy$549|Q@kWv1RYGA6j;%YP&_C5P8UestjTfZ*;1^<&L z&gbG-sK>x_b>W?a+P*-hq!jzN<<0>68=ss5%j5RlnjOmX-*jt=?mx}Q`bxAlSqIfE$jn0$p-R7X>Ps)tl}dsV<`yK?huc1yXaXQ1PJ0!cSA z+YGv}_{RxVyg4sk7`FEKU1VrV2ojXv{y}M3Pd*O}43t9GNfvmcrkE4hHE!CjSHbKI z^nCw^j~?c&kKTK>H4domkfkLgRv!^ruIgYWEP7!^HwbNaAH!9~>CWA1VL{KA>(+yg z7vSzfm;I-e1iLz^z}c>9fjpuP_+1In)6>%_{;>S|MyTX|>wf5s2UM`o9@L^zsX>t| zmbRUylfM|I7Hn7`y0GK|}U48nbc%rqq*q*>(*rYIU{n zdhGK%JtKq9iIh7ee$}?nBN)eJW^2;Eoo&+oR8lyv0%@WlEak9XjZqa9Tu6kRb*gdP zp0A%8#)>64mf8{V4_-em~w2@$i`aW2TX9qfCW&e*0l<`TIS_32@KO_3_WyE&$0d4w`U{pYl&7l>@ z6fe`AwTNsbymBups@(WRYTNuanPHSo<+NN3<+@BT$$zi`WUi8d3?t}jh?SKk`=_QrXRt3HLQU1Y%tlFaNsVeBSV`!@0QQ=XGQF-r>I}lC!xr za?;y;CH$OTKy~97`O^#j<8cjq(U|$vdJ;8R_+EQG^8{*aQq3q*(o(bQxJ0JW z+}p$;>YB=rZHA&!Y_L90GT^@wEe;*4NFs+$R48uXr#_G!=PV>ek~IyG$-G+YEE6%0 z43Udt>+*3O`yU}PviZYuR}U=wR<1<1(Nv?LKSbxc3cWtja{0}TMb`tO$rZl z#u3_*j+H3{(DSkkQCcXfO@8{LB9ZO1mh(`&*F?{}H#GUi$1K@e!nZiHI6QWpgVg>} zWaF0cO+v^S+FG-18!MGc307!-QB-@8?|oZa8{Wy-Ff^Xj^&~j~%|wmwJ>iXq$UMv+ zes%fa41VX5k~~*m2bFMt?5#AOcboYBw26;I5PR61ZRM~M#UmsUye=<hdc_qkWJ6d>VQMT1d8#cv_z$_t z->{k*+Tp3)HyG62R-ur9aQ=q&4h$U3|CNA!Y|W!ku40UDwJ{bJROT7CaZy;N7wXu^zLOa$3BdlMo z7{3eWHko5i;Xw=Tc!3=(A=F!DKk&p_Pdy@Au7Q9t*^cDgi_4fDLOec6u@Jp40`7NBfSb4}b9;!xI|)dltN7df&ot}hPBK^NY0M~Fvt8}$^=q)Jn8?gl zg@x~R`{V0bPmo4?=jnVLX**c#PzPz(#OM=uj40f-x1ynDl|R_ankVk=u1m{3HMOvq z`FUq>7{AYbXd|xkR$bNzp)kE0Fm*znVP3zT*17bGvTOSW?}^dXY#f*f;jg{bjG&zM zGEYzE!%zb0E~f5^U=VYomTyT%nzj}f9#3|)K`)>jsw48vF!Vz1<-}lKw?SRxHXT`; zLxhHzb#-b@>6f;pEQBivFtp3}BnZFF^GJk<*SunEua5a9F3p;f;JZz2`_o^xi>80~zOTAl zbylFoe=c6z4FZbOB5kuih0of1JLt4L^4qam;T>6GQJASU?%7SYWc$m|yqxLoqg7{8?UFm>}z|{Ri`?0MBF( z?hUZtuqM19=!LV*s{a~yo#uZlU)Arz(J(@GId@TCN4C|YtB;26%JzU z{f%*c_f<`Kr8tkj^jh7p&STN>^2n)7Wms=fSLTSRN@7S&<7mVm!v0d8`N`YR`rY3I z3*{UBDr$W*e#%qQjmBR_llMKNqN1GP9Q}_Ub9IdRZ+hw>dCie!pThh8450fZo7{-) zy%yv}D2g&#nr_z8*w{y#$*fmP#%&w0tG1kQe(?YlmfPsOC>bL!p0z1s?eehBSM!gW z?^MWl&uiHicv{I)l9D8SuOn$CdG=}kP-$8&jnSta2~_X@3oE;5hW zR&Gj}IwBiL*>Nw#^_OJq6|+l;gtchGGk^Q|Jk~=RE}JLLHf89;+teSHUDW{rm~o9r z`;Lyq673)E7Z&$AFTHO0$x6MqI9V65bu`FmYYfO0f`YfC2`yg7w>NJGG4!VTnA)Ha z$G4noCd3UeyNXtU@Wm_MG7_;hzj;Z+^7*(+2Z$3GZ%;~gjeA{hRzWJ#9{QEiZrvtE8R+j+Ko z!K?I!qKb6;A8Rrr>bps!7F_yi3S*zpqKBbV=TNNCHc~9ew~vWoH}Nfd9=lTH4c7fz8WcD=L27V=k739!Hf*nEH+vdYi!4FL&7;V5(=V%M(eyI!OG&E7bV%50GRbgRwxo7 zUz#Nf2JC=WI;l@;q5(G%x<(*y@g5g~C_-B=j?!9U88<>6E!u7l_~~{m)0=)Ahb7@3 zhW^B)2f4+j)?WA(E4eLD5lo$2p~x%%sDZv7U>$uWm6IAMnPhEdkxo3hH#Y;qAtr+Q zt$H5THesLvA2!xVNQ02}sM(O4B7Ik|E5|@OAcpBdx}74bx?5!BpbdUb<@HHp&HfFU z&)BT*x~nQv-9x7lX-iWr)qln^p3oyKE~7j^Ugz8)X?EBr5!8U3bsR*+|*;I8HcP18>~U%2F?aLlZ|_ZDi(sFFCn#SQ&dAwZ|c129aU0X_Wou`RfV z=eEhoX_@-$MdX+E*6T96mifm%KdNf$rBo#a2)RS4C8}IVYR;Hmu_7%r_DIL*9vb`n z+2TZyDyEU|(r4|$T1kCHly;Uo?rZ)6N~jppy-m&_Pu)N$oRoL_siGGYHtL0OTi%VD zL)AE&bax8>vV(!TIxQ~UZOpj1H&#}Fd#b4Tw&Nq@*%7FJYy5#IY}Fj@oeg=;KcU7u zxr#(xemhfj{?C)0h57k+isHqL&93jyHlbNUR@0jeSa^bCdOK;@R5z`5V*iqW#!d+D zWEp~US5UdWdxh1QPVP=8*qz8`kDBJFjTtGtCBC}w=Tg8C9}uaz5$h>x7r(H`7d0iC z=~DdW3J^pzV_u}NQL3No`{RU% zVuC~%4oizzg%JawHYp~9nPmAwNdj1=#gu5(348VrBecN)xxG7kLlljf`Mfn=nxC1p zwOcw2K!7N^B9`cP6%ot*m;Z(q_H-xlFB#yQ00M`Ms{F&k{SxcyWMqU?CPPxPe)M(t zyhCrxElyrDj7={&wJXp40Ko#lKWTnEi{jpE4Tb;OjB2^rFA z_-xWPn)zftCV4?dbiX%AFwG+UsEy{;y?XouM2b$^KK+~!!*oz7p)@s?5@IF6>%4wK zD3W>7nXU_>2h}5}SM;c-5DiXg`e7IweH%=W&<1|Yd6}ZVtzx7FmkeI#@aXEala6%! zh{V6??u_43ih5Y)6n7BX->|c8wy{>9-_rf2m9UXHk8bHpIdgI-QA`Z%a<2u%XF6@_ zd+uT&;FI!)Z{X!stN|E{UGup#onP1{b~@c&w8L_4rFF7^imk`MsWFiN2cF_iiEXEX$XP#D=KhK((!U)4_f4L>Gxw#o-ahKM$uc0?4kq?~=<+iv{uZIx(K;m=7 z=e0UpoCq2?DUxw3)@iQ7-1qzUTnSHm1x{rB;iY$VhnKjw84ynJi&qQz56RzTQwe_T z^ekov21a?}5<*UjMN4Phd1UmvuBuIXCnUgtdC#(cWqc$og3~i&B$X%a8pYVMm(b;T zjgaGKLnmRwvL7c+d%1&E1|m*sW0co@FlE|_XJ{2_EvKhxzP`RVee$=rX=_^`A?@N+ zh*3t@7O33BaC`sVrvwoJsQ=7gmAf|D(8G4!tNwM@zV0YrD>r``p2yHV=T6P{(KF)j z(M1-V$^9+M>;;8_@)9@FsHY8;qPfP zBUOS5bI*B2`JSTI`h9Vnbfc4*U=S!RNo1q6{$i8g+*#pY%vT)y>2(Atif#YoX=E1G zDf_I5yGJ@X$uxs}tQ(ezvOHLE6R+*E=UEht!%Q@E@CiUe^fS&Z`|Eaf2H-u(#)etr zTjPj~)x0o~`MF10pv)@=_|z?oqhFZgyN`Ol(+^l>c_Vp^b_Q3)t%Tjjjx%FycZTf9 zQE?NwRax(Y1ncGbWAZeXv(D;;h1T_ZFfkHG#Xlv@S3$x0jaPUY>WK7mSN{5R1V=kx zoPL@98xM>qVC~lS-Nt6H3(p@80vXVEjb(IDV40;MMfc;zd6vr?n zy~U_t>_Qi;sLs-y8ruA`!{Lm#l4;vM8w3zsh5UJofA3K~ zu2U0~sr&aN5ZR8`+&?j3Nk34|oGAb%C=ZGaI{HSpWoq~(ukdxZ6^npWKjUnoiUv0} zHq5)LyJ1?Vd^z8Ke*|1aXyh1aY6YKemb2^NR)&f)NS0ssu#NgPuggmd(53E}{959kVvW6VuI575&Tf z{Y!3y`B&3u*`(*9m+YeoH%`haPvwP(?q5AhO}xU>w;no}1067%K*0uux6}PMIa@6q z3nFyy2rIID6aGr&SsE$csoFc&2;)aO(YoI830M z%}!zcTW0#HD``%`N2&TYvIzWJ0QN;uKX3+cL3i>>RC?v*C#a-gKXN&bs-)S@o|>c{ z(c$%Ik7@sT5REtAN%M89YR|+7{=GCHF)xDM>ZOn7W!wvzpX08xnX?J6m1#X*eOPACwoLc$MOW>jCt8M8c2)SL#1P>pb9Alf z`)BUG<7G(m$F#>_9rcTPdbDN9s#ba({2N67^sWt8s7iEd0IlLGArsC|#J_v938te% z6=-WaO4D!8*MlzUDF(I!0_%Zw+uFwiVUyZDM7WFwDAVAtg!)*pG^KLi3-kZnzl^LD zphx7lr8oj1t8Xs~W`UtU)pC#C*Mf5Ezg+k+_bm)X+see~(20&OUP-1E6b(MLi1tpt zu?cw`$hlXL6r26g;#-O^ArlOUn&mvoh~}w|Q7)9dCb;Y`?=LE1x1usX=luYb3I4mZ zu1$GA%~9N4HrvJ0IfG+Af5s~7cPjVFVG^4D5<}NLwB;DY*oSgQEAmd-jZ$H>S7hnE zA3!!x)Rq#;ukkPe5t@@0OKR8))|-MoBH9OkAon&t#Rynzi=Il$P-|DyEd#!TJi9nQ z_m4U#)BEqD=X`zVUvIYc`MkQCwBMC0ddV|NzG396#Cw^wlLH|Qpq;I63}a-G`K>}( z!|HR}n@fzRPn0)h`~;n}bEfV%9`!5OvS)r>O%>=~IzR`Ivv{%5DE zp7mvw<@?_Xca-qt%|%@f^V%l+3dA>=JRRa8GeSs8jm1f^%lE$knkK9tm{#n?d4~K@bN(jwA6F@$NScls9Jsvwlh3e(rVn4S;0m zU+3@DK+Ev&@5$M5ET=UNX~9}*FFO6~n9-_}-Ru4MPsg*ddf8uX`+lo$S{i{$)}W93 zEg_2qRKOb~E#@ceS;7Xs$b7JjLBCBGZ1DocvXA6sM32=XAcpmgpSF}PJuE9eK$~7p z_h+!F?g$Bqx-_10jBYT9l6ezAo(f6fXp1OQxEcTsHh1bTG{2>tl$^X6{5%`vU1KaC zA^Y|8M4j8>7ceh<(P+RfMxdfoaZqgZa!-@T6`Ir;mwD9w)!{Av*K{`8T6Y1XRp}ws7}kXg|9+h6 zw3uPkEpsCyiUU#TAjIe(%}XK*QoQ-sh%t43dx%{7y+1JzFi~edkw?N8ebp9Djeex%EU2$0fLs2#&e_|iJK|p5&^IAvu1XF847(L)C z4aE!iUy^--U~v&UL&ZQKs~ceddn$eFq*JOqMTHtgGeAa58FaL-p|;hCvl`KfnCdT; z`pGo!D!^!C{IVyq!}Y@EXB=lMpf@ku1p7%N3!Q}aidLyi5L8k7ADpSseD62O(*d=K0l3^tIaKvI^5Cfy58<~&(5smZ+do@5cg>Bq z^V{w>Yfl<&$T@od6SWqHb#$);puKIz-pYOcfMhN$Z+*~d*)jhOn}CpvDyGsu`zB-n zrF4w$|3<4&eizJZ-mD$$r`Cge|84_MFKFWv?&`EM*yO*7$go{ALh_m#UQ)q|S!e=o zq?|kErd5ta_k?A3{~yfGTlBBd>SAohDWtV`<^w7@XnVCu7m)e6C1D$=DVci! zMO)m!6-4fFz%<>N<>46yIwide-|-*M^6=Ii;6!Ody@_sPNy%(5Ro3XjS=eX)Z?W2VXUKl zi|n;Y4u8aIobg^LDEh20K`X9;KaalB*9u$7IYHRn$3hXyJ$N_ZJ3{3j$35)!*{AsNyaUhdeRky)#ZB}=m`C(V6VRNO?zq*5;JJsmysH2`TlpF+ z&{qr*muiINYmMF)Zj4w~9t+##!?%og+ko_5C2A}~Yz;b8cW^~od}oa?QJ2Q*)_<+k zInvf92CqcFPfW-*Wt@>ioZV0YBJ9a+mfQMnNBPtxvmT?MTec zJFbC&v_F;NlVfzC`JtIyaykCgc?-^vU=X{Nx@PR#-_m8a;)IEs4lko98I2h2*%j}b zQ|&>_i$amo=7W_XI!yPnTiUYYkpp<&Hp)4?Wui!a8{bDf3MRw@z&<}=195z_9)O{p zMg`rYtyKz&iUeWB;TF;&bZi*8adsviDii)P2vmN+DT!i*LO(Z_{^pf;xmiNP)VUW? zZQ28&mQ?3FGwQ4MPZwrkwDh}V$kJ>IknbwTFs z_8l9j%(3WTUSj+P-V4y5ZyR78jIsn`YvmErD&Z-deg8hZ&ZjzY;vrJ=KBx^2*sfo% z54zdcJaU9FrDXHH#5V2EGOPmaY#AMaWC-yMy@{L0jMm&zITj6I&Jw({iM^7O(r9)o z2l5m;Vl<{RZ0>at1N}vUadpt~^y6snPES5N_eBO*)El5MMz550Cm5XQ0}#G5xir5F zm{9TLeTV~6#$xNG+$Mh1215*)#VmV#W$S{SvfEqS4^}v)2b6i=EC5;zfSc$8?3G(+ z@730LhFw1s3W`4DQaF5R(*i4|1c06?tZp9qXW5Aq#i$iJH?DzCj^Ff_&p{`}FCFG5 z&dI*jV|!TccRd#0axPe0RvBdDA_P>eV`oMkuST@Crua^&l$rb)GU!$t?>>D~?wUoV znk<|ts!UL;Z@iXGzo^JQ(b88hyQfJHojT_`b(b0A6{}|IHy&UO&BTuPWaV2NiaO?8X`a?I;Df-Cjw!jc+e_V#?P)NF2%s z4kQ>K!N0mE9Iik-xl_yMHuMYT6h~VFd^Y^N#yZOe{+nB&<-v|Qd0dx=TLvp~GzT5a zf%1MU5WC+ZjZCCm;om50W^eo0=DOyR^Uo|1ZL>vygS(YWDhO_Vc~i1|^~DNZE}?vK zXO{Vb0(WzFr&gMV=yjQ{tqJ>?4R|Ca10^PyplU}4;S~g7Y7+zgL(nikNKn(I2Lw$N z$%QFvXM~)%AhtM(&^S$~eyv{fvOju=G)9~8&5^i-scf{L|MgVel-tEdLiD+#xy8>f z1z@Aah1bvCuhjs`TwKd{n9?>$QVZ6o=xb?G19 zQ?N?n!`Y}Jfrr$83JMCYVu3@_q{7=a@F4*z=9~Dw59A7tW$spNA%G!+k7LEc-_^3& zkFx_^q$>&7vKiEQ^L|*`56q?U?0^_P(g;{`{P8?mH4nc_=~qR{zqR%c@vux}Qo>&a z40P8!b1q5^E>fxyp6p_b3^ln@zr1e1-QY-=Q7$c^Am)$ ztCaT<5?0yRXjRy@1T;FfJ~&`B(8Hel&tlocWAbamQX=IXX}bguFVj*+%O9OHCdYWP za}E2OuiFV7M%cLEL;2m{{}>XAVsmgIgL2r|?Yit)Q@>^@pt-)t3Q$K*VOh`VJbDTD ziIv1kCZemx?9i!lYe3Fjr+!=%eUUYvG+75onJG{}td-qE@5bb1E*dps@tY1*MIuO2>yf#($+v9ATsVcKd#1>Cnoes1aNocw=JCo zj-mbEV_^FNHk1PSN?d2(!hKvuOW@ZTu9^$zPYER&6b9-lK@R0rV+pMFfzJ=77{Q1%-5hak{W>p3x$?uk?o-9%} zGQvqMNWUp`Fnya8QB?Qx)hIfM>LAQhbg6M9@)Qqm6du^UCMGK0MdinD*=P4Rm1=^i zR~nWqh&N*sA0qiq7Fg=#4f||peM+EffZ84&*hiEDF#oj?49s6_74$5)bnJV?|z|j)cZDtRnrz|>}^E3auA4>kB{GUBrcdYPs6Ee7bwV!ne|t2 zK)LfY4n!$@IHh`oWHu~5hPn{oq|4aZB9P2*kGJ4=EXX&jr_vjTd%+<~h*0<&mz0W? zpddVMC8(7B1|_@{A~Gv?O3}am-Er-A9Q_E9#HEPAmiC2)Q6&i~+7&tgb#$awdNGrV_iGIanv=FuGJBbZOG> z_IqDT=Dm0I!yb(lNnwZH`8(=%sQ_+w3{*tbB zV!9p5ZU1@eNeZ`o7+;s|Nn)R!C~rjqj}!YB7|=C5adAoyIy#eF;nz^F6@4-$M>Ntg z0_^o{Db20G3AP=aK}h7uivrrrqP@KUS{Kj8sqOP8;r9RBhf@|MB|eb1U;EtF_6QA~ z!V)8WHUd8-SOV!rF#w+Gqtt=Hm>}`u@96W03B5O3DoT`N@$ftiWNi;zItG$EUXxqe zY^b(at?Ko#h{UlnS#l_EO5jFBZ-i=*>8v@!7I}STv2aScfWJAZIfSa2ck&HbG_>IG z{WIh!k%=oeO52TB$Lrjt*s|0JVQ5wu=!CCMQQK0&jm!q-M3`#&*~wGx10y5+Uw8TQV;X&jQxbPaTjcJ=&Ue&z7qLb6@*J4q#f} zWg?3JvDYPG#pmaS<1J)dfrMwU0`gBw5-`c~b}llY^-jsF&zLM#BX(($7>P)1un{S< z{DUkEon$kgBi^Yihd}M@!uo;Xy70{1?TEm&+HxCB0|uIrMGR1qi(K*-G%eCj4DDTR zz8Dr<-!ov;=ibROF;qFQ_Cj^kpHUHr63*D+O|SyxN)#MbonRX&He7f+qCcAMVlBx zfD~Ea)q(RK7C1R#Vhu*xl51mj|Cr$ySmQ(akM1IVBdPhz@)D`Q;D~h0O;^q(b9BT* zh83{;+j1}K+{{XZIzaVk9;Nnx7_l-z`u)3$o=&OEKrt5Ct2^1&hhBQC%9wd4Gi!o- zN`5bEtcM$Qx9v$A2aO zqlSrO(hV78SSu6ALf0$5dySh3qzF5x5C`((|l!~~dM)qspFo(tG z%m?YJyBsgb90pOZa!!70!KB4Xg4J$HYVgjXMo;AUNn3m4)Obezro@PHw7(USX|vQ^>Tp3p!6-zchpV+M^XPgHTXns*kA4}-F1a~7x>MVk18?d zJcV$bxogNhT-raLS2%^^B-MNkr}7NmaG(2;7CB+=l3>l?R=taaEbH*SYq$8C&d&9KxdW^D*_p;)P#5M-l;%klw9OA z;>nJ+UDe+1y)b{$m_IDs*E5CVguy$YZkV+dnWHy9mrYD7`;_37!js326_Y>wH$k;D zL&}+@V#a%P(1$dc2<^DZTc2BY$%ZjD}5p|t^Vx$--mv8QV6mXr^pCeaYwJ0DB?rBQa zDN^xAG@h=kg%-}6x@=q!Nre{#sgx$}0MlqA$Z&o|E*cOP>OXE(SIf@vSC)IozB}pf z>Idb`>_r{Kyxjv8G6i@zNrSH9cL0nn5HgC55>8C?P&gJB7AjJCj%ZG!>6YAgeZLLv z+br|BaVE76Bzg9YG@0%EHmZ3YQa8=V7cQCPNw7BP5N=#7a9+C7-q_K5fhMKhMV}$K zE>jAiXPbe*{p!%*e@ygWoAC4-oZob)vENJj5Q5-oL;1{tdMZhazLR+z&E>)K8d<>rDbWm?`&+wgxm6_a1}? z5E)leSEsLY(}x2~Bc>e#l)&Xg@{pW?nMdbABAg{8o zV`49MmyBw)e$`ZJw^hvW34vPq58O?nb0DIYkFRky_Px%y9Lz*cFyeO>^q@9!UjdN# z0-){0zZ*ud?1tGriTVi(0^FeJV?&5Cu<$6483-Rr`0&t^S}&*`e^KCsp+o% z0tHMs_9?1;|BjtoiOFIZ$-ntGQZsMhZWk-HEf_*E%i><}O6NIpqz*`*%!T%t7K7fv zY)`$5n;Uz1crfqPC$x&7AUQPX(z<$3W^jX!2bQRqZ+W_Nfip5-ey*&ZC+}eS_Xme| z7e_u#Wd?2Y-g9dF#g-fPk>~J{^G@E%s1Yf5@H3tQ6gDV~3Vc9!-T_xZO2 zAW{T_c(4cMRR-e}VR5y5DnNl{ zKon?WrHg|f!AXKZ;HQ32@l4lnxhKtgkAgeS?5aJ%_r9sA$u0a%yE{yX0F)S~q1@$f z3Z{#YcN?bA*Ch`hd+OleAoYNn%8dj4VR}TpIEOR>SX*yMTZ%Aj zVB}PuJPu&M!JXn$4Rps=C;Iqg11X67K^ezI>7cbxwOOdFe@u+;ZMUNzfSP`7=4yM~ z0obe7Lv>nbWPxjM=zDTf?J z5y~ONj5$n68AdrIW7Y{*B$Z=M%V7><<}`AcRdR@&<}l_|PBV*z&6?SKU+a2*zPH== zFZlLD-EJdZkLTfWxZm%O$7`xfUCLNXedlnuUYNt7aRLtLh2${({|v_dhGV|e=a)~M zo(c)5?l2KhyqW zQ=rm+rrf0|sxf)Ndi5ts+~Jy_iuNmYHF+k{%<*6qTy1oK}Cu3v%Q3xE%y zei+uqTDITyCO}nj(UPzQXhf0D4NXto4T_d+OS{>NXa6DXI|!Fy0!5pc7)ZG*Nb~cw zLH*AXveRLZ`m>*pY9#uGr*Bn`S#15?=)yDO%wxl)c+>Z5J8OqC4xhGak$f;oe!set zf|<+XuL0jI62^yoB#i*<9&f{bCJVdN6_=;COFHTOzQeq;!@l?L+w+!#$L1PSJ-&p+ zK4QlaeN&TAjX_m&m6OJkPRB_5_QPfTz^Msd*MOe8cZ!*S&bv3D2c0dsPq`XW#1{IIw?`)G8FB><=D8^@6HRW^y0Qtn5A%r;A;3tiw}<6A|1m@6Pu1 z!`w&#j$*{noVwqncK6L+ItKIrg**d1&9rse5YVVu!GA6B2Y!B>Qr0ZLIhBH~CZ}g+ zG9#M*vf>-4sTcL5$SFGzWt%28JFsANQ zLO>0Onbv$daXnthE&C_jsc~uUYBVh#Z^th=E^A99cSUf|pX@Uqkh&dS z(?~?=vZ^vW*2Yj_pKnKFTP4*|6ChM8Xbp=l2-r7f*?PpiH;JtdCc77KNkTh*1ezaVeCz%9EPk-+R{^-FZ}kDI^s^$sq6(HL z8l|nK{ETa(vC;BlI?JwOceIxa{B8qk@nfLYNyv9uQ=)e`K(Tu3g*~k}ynKuLQIT-9 zCBERheXA#>vatkJgqj*0U6&?icA#kHh|KOYU8_d2Va~wdfIuxd3v9@X1V9qA^Csi_ z<s-3vC`_JLhIs z%%KWTe~T+z@}7Jiw&Cj_1_qY(erSV;)_XO|m)&N@&hBX4+rc$ub`@4F2i_O^@JeZ09*++$$4A6XCQa`1e5OW@EMK*ukC)ys zYB-Kpff5eQ5dpB?5$Lf?dMKkZT~Rq428xW z^I098<^u_U!9drp{`2_!P)-i}{G&hhUy(P;d44a1?QL?dDmi#xr2(4@RM`9_TwqY`CGKkb6GJy?*)l zOYZaA>IoBNYVz%cX*8&t+h%w2K4X!WqjVvXAp`o*u>1XjJIG3PRAI0|V8-cc@rpPn zVZ&M>YL*aojX?BC8JL$g$<&m;;qYod;oI%f{a=-XZIUF>2 zP~@%ZbN&iz;!DZ5l05+ilps7EH8ZRAeEPCnfw5TTgsJ}8?vn^C_AB%J zKY7#XkJ-Wn~} z&BXOn>O1dM=%027q>GZTM>&Yf?Ne4| zj~>1bZZ$jhYXCs#a(`V91!`!3E1%D5Op`f2b%zP_zu&*Pxp@mZo|@_h!Uu|XqN)5u zV_;kdeC71m@B~K>-@A5qVQp!Owr=IWk~$0(LtfI~dAehI`W75MdlDRRSmPc|VcsMm2OaU6~j zX-XmkQ~B^&a6*wEpAkHn-#bM2Gp;cf@k_LWkM;rQ^tOw~a8_+X5_E>pn4~`OLpg5x z`Z`N-j+*8A9=1Cn&6x)3ri`CbUdq17V0yqWD8RG#Z)yxgoUKGG?3qAU^fnQ&V;V)h zzjBQnF!)|S?Dw{7w7Qxjp7We^vQaDLdgCSs51JU}j)PEcM;z`40UCd;q$t2Hd!Qfb!YXIQ)M$yJFadSyfbnwj=-JNNK5oT}2`x0(! zU>&$oO8a;w(Zl|r?f}2+5ie9mi%6=OP|a2adRtk2og0x$v83|t} zYM;zMW-%^Pi|-oHA=DtEKGzm~7i6Ru&~}ZbX%N@1@tw{tgnG6mr~!8w3FB`PR}8K4 zX-?W`kntQrgP8UOrpXk=6|ut&gHjE=ny);dlchJh=c@z;q7gTph~O#9DwW=7lZbXQ zXUEc}qZ(BM`}zKv{Sq1IX}1-bvH3(ty@K9r%##t){X!oyMc5CL`|n!$!Nv@%o+@6K zQifG&1-VDmLmyY`wT=92euz~!?xu9ht0M&Kz*YP1_x7X*%LNM6|3uluqt>27(HRK7 z%*I~Vrs-g!M#|0s%68P{FQM)=L$w_`8^P`8jee$wQ#aoqIW&0npDHKW%(Q&pWe`%ov zqWbu3DD6(^PnY83>*MU?e#swmLpg`H-`T@Boc<3WWUXNT%w?ZsLcH8Yyfo@;q(vfj ze=WK8 z^~TRN-GD~)Q!K&#b;?DjLjU1rxS|(3ZPdtEi?r|Si#=U1mh2OQfpxCi7%Q{D?1S}x zG3#U#>^(_lUhB>VR|ny1#L@35FA)q1_3Q4%vgOXLiLTiF@|BQo$X_Ni-3)9c;Aw3Z zvv2V?U$Fl=cL7*v?vgQpQOX_2I2eHzBHP)=&mH*hxI71NR6W(NDEFlP7`sg`WPM=7 z`d9Z0&)NcAbAMht_KP}JD;yIFE(8BG=W>uUb=)7L)$^b5>l)_hdc>N%X!sDr+jCwN zA8r6bYjr6%ANY4emOR%+gNr}&a_=Oc@4T5Gyu75@z*S`jlJ2a;%5{P8le%MdeCLKb z)L?Vll%>Xw3trAVzfOp8`s%2F#{^Ox>gJ^@FIt}~qXmUA8lrCi*bKn?+K>8+f10X% z@_khHkjv$j?+i}P-=U8F7pV`QkXRR-z8M;{-7AsVM=-qj30u4QH~&SiK0W86F63;1 z4f_gzh-SQWcP8w|skSuneg7!zNb=6a<|>_WcR~KTW}mEkmB9VX2d2d|Qy;=MYk-MD z$|$D+-!($&sJ=0Gk~BOS+{Iqc@xiE(Fto*#>74hX2nql5oK=tz*JR6Mo}e&6_C_vW zV7&}&^hA9yHIjD4_6JJZZwa$rz0$b;7f zInBQ^NZje+d#9FNaTS1%taREh5FDm&VT6lhPJ{YZp~}OWs28nNpaUUUX9!$M>A+0V z$kUbUf63^7_y`<=KSVQykgbX9X8b`*!g4o_TC+Rd%&!~9@?r#Z^GXFI zG|drGYg|_o`USS_zYGYg)>WzpX;J}aMV=Ow;vxH4f?TIdLv|o~t%}dM(k-|5Fqa+; z(S#L;FY5uSSEWw3NB7N!t6h3@Y3h4d9gyyITl1EC3_b z6UZ5xGSx4CpR(7L+b0izKU8mDpzDL8ah*q0#hNFF8%KtXrzD=By(>|y$vc;W!4cd5 z{VPnMG5=!3R(Nf;)en}umJtH#Exr`HUIhLbVr#9jW8o1H6J)Ipu)s9HWnM=z{z;t- zxCLKtlnd$-M$srg1B=)sEYTd|T2oG<%dWd9VG$x^ZJ=iJA6i!=#hyxUn+9b9??=bFRRe zu%q}Gu=vu>z%oo^W z0BZzBV-B-6WFf;Q1bi#ZWv~vMctL&g?uviB0ko{J*+#&<3NUZ5A&Cd9nkU&RPC2(U z6xe_C+FK|LRd`R?x5f!jO{AJhi4hmd*I=zYI^Dwq zkR4=>txEbU+A@+0AG`GV34Ix%&O9dU3_7&jOV}_v9R6m#m!bwZAV9AT6c*<^?${&B z>=uZ`1uQU(c&d6~xcK~P+d;bQ^8P0gc{2al?)^xflt@;rLc?wo-0z5!Yg4o*H4IxN zvVr+wic_SB_M42k$9-3#Y9|tX_)fnfk;w4q{hvZz`Si230zt8WZ?m3*-kZB}MZp#- zm3{=UD#*wGfi`r6Bh+eKhW(WvQHp(R_~%j@(GU;C=9*?OM>fy_Tcp%nlK!Yaf3*ny zX`*)wt*G^9x!~pBzFq7;@G4nGf&5jVNd{<73Q<#7Ka&2?eU;i}Fh?-c9N!#T|K2jO z3QScvI!5kNTjVj5I$)ak-(l`)QRUTADVZVXu8UYdxb>I*l5KZyCny7oov}OOUF7N~ zW215dLlQ^`?#&f6Fjj!NRXuUw{Cdv3;a(_JBAjcjA)U-kXe6qnjO)hdEgi0cRH7c1 z%IIj6EQh^VzM+t~G!q;e33vDOwRxyNeBfz;*VklV=EUE3sGH~!PE?LND~*u>IT2Hi zZd~t`&Ruy{vnb)sSoVQ4&PTAqUV_#~9w*{+q7F1;d;hI`?|=*!j}T}e6qoAE;lvb= z(7pPy3P~^3fnh*RK)w`);+4u<3TZxC%ebYnsL0Z-pehI9M%XMNT~?IkKKdS>ZWd6` z_tSOATvDA$1!%H^OUU9EWt!giD@PE~HERc*9`Wm{)P!vknbN=xH9FnnCt$I>Z1|82 z(BpAe93*2+&a3no=-S~eKIu2QP9EIIYGW0qL7ah+50W>(w*f(bxSg&zdf)I``|HGO z?P*Tpg|edSSFZQ6q46`N&GrY&>X!dziu)S(Y!}ZJK)~g@Mco1T1|Uqg;yf!uwfz_D zM(!HsgL#7w;YheR$EY&2oF(|Au1?DFWUQ~~$AqY*EsRibA3n=p5^fBwe1(x-G9p(Y z){>&o^Wmq@zj)sh(evlOY~j5Hi`eg`Yn8>hg&U)KG}L^DoI_TyF7Zir4Ext@<~0}e zf1}RgYoUK@75d+aZn#^hgmN6E+$V)nUY*!VS9zI}>EUOAA$yAyy$jFthws0)U3?!B zi3dMom;q&3JT}@m0K12KUOQ_cqb^OlvaiA&F)$6}DMOiaYnu92)!$`->j0GY^@7uG zH&k*|-iR%N!KwMAl>yu)?<|0<9~z&$i!qT&?6(5n)^UVNo&5LoRNA z+^&V-EY-?`pHJu^V;C^TD2awb2MP~NT*sCAH$rIsI z7h8>(p8$LUa2_cyPbnNoP)?Ui+2rJ-<_WB$?!v|4S7cemA))={c=&H3;P6XdjpW71 zQ$t5j1olg!oUK|cLXBypyQXpul%z`1$Sv{4-jRrMN9qEbW;Y_7pc&C6R2O{DyMbJ4 zR1{)gTC|&;QoFNFbVyR#k`D}6l>%&F5&K=pAhx}u0sAE=Gilsazv(729IP)3NSG1B zndF$hU)lftG~65Vx>k{iDjNGlb^s8i0*#mtt$>yU2`N&_ki14s0AZN&vyv7uT-B}Z zV)Zc?5X0kYgUs0@I8v2~uV+R_fS#lZ{DB#u1BE6DkjH?1>+} zEIM0qe{UeHfqXAUIU9ZFH(Q}0ouv}~<3fg4O#};lg4CpKT!e>i^6}0B^Qv_05f_h!{osT?o-kDq@uK40RSo2Lz%2oKPw4z zbBIn#blXAe%r70V%(ZioYQWiY5?YF@7fSw)DHXV+uVKDt`YT{VJ6}AII=jRk`)QB) z=}~#}K%eW!AM^MpMO*ifB}z_q0wr+92!Ckiu1}WVCf=xheMN?W%~^POxkh&9-<^cu2Sh;F-V5;iv&{nC_rhr#G{o5;kq*Qp2w5%N?UMx(J765+{Tru9CH+jM=_X~?@ zKAb*1KF)C!w++i^J-{A(5ySZz)o8HV0f?l1cU5whvD|Q2WD}+cz45vY5h(i)DR5kg z6uEz?XdS{w@I$3VC!95~f*pEP@}Iw`k0`3)o{F~sfHkGY)R|8Q6UxH#??>gt=@WB0qst&nw~F>iK`s+dzi!yN>ND_ z4zI6e!qL6M!lD_w=$^6v!kcJgZZSTM5(THgubiDX*61#BJYuVc`t`GM>r#KM=j>Vl z)%FzqFecEDq3`zDN8c#@)LyrAb9(#}Xlaf7YMny`Of&T($7zW3F*PdZR@(H?#F__Q zW4d#MqBQUNbdPdc)+#kHv=c@LbOEo2#ZUjvXg%-Yf^!$9O6_f5M-9Vly)27zKLeH? z7YV-WwvXrI_w$gd)=_;DNTo3{+8519?s;4LwFq2V`;h#zx8xDGkWITZdUwm!kDq|9 z+%hcw+300)#$OTq){D=mA=geVKkbnTyjQ;BqX2=*q$7b#k>`Il?pB>U3++ecyQ&LN zgOpBcvXiUSkp$@g8oTBxrAxD2>t$7F)NQ7lFMP2a@Ew&?&kbls?!F9`(h}XfKB!t9 z4fDOI*R?M2!=u6iiCiK6m1EW_eM7Xo#V~UT~OX8EMBJj;y;=RV#O7Y(WJu_wRxXi=xl4bWEa@nhwMs!#Q z^9`mXlITF2S_^v>$VwCkfts?~yZ)ZSQr$YNJ$L3>>9z|`fg&ckdy;P&#sa230?cU@ z+Q6#1&JO8_?fvxWQz!)q&QvEEdcfO;c~3}1bUhoVeAMpTUoUDA@b%~Gl*v8G$C)s?@Sj%p8r-g_h z7ad7-M)5k5!3lM7K11*tY-iB$Pe zR#Ee-IGHRAV_8yQ&n^1TDCW%=+~|Y zI7mv~)#P3;F!jg;bbTF5g`DX+Q9dN9iWL`<8b6$*7<#6jMs~pBw}59q1ccy4PcJV9kwLE7WDznO#u8`OCo=Sto8kA=0Gt^Us|AHK{<-t*D*dlgD|X8))D z+RXtKwU}?s;DfF1l)PrZlAd*8V8K#kn?za{ykCMQy=2SWlRvEooQ|2!9U~$Pe?Lgu z4^H<=)j3%;4pHqLkv*?y*@Y~;PeaHPhIDS68W8-LtChMyOB*(- zPYm5q*mlP{k;fd5+a&fok40L@o%NC8F@u**twO&3Zo0iBtyShkynGAPk2t;o53n9B zu{7>L0vMc0xqy@Sp#4i$s)$31m0LgFA3bNK1~P0++~km+cEW%$)5BH6Dync}fBw`l zG=Niyn4k~{hI@N|E~54VF+ths!+?V}x%T}hi5Tgcv<4&fe9d9R#9aWshqS(w0ft#` zMKuD&CeZJ5FMz(%qY%0@JQZy)!c4U4>ZNR~A2~Ea#*mtt&fU#qI(bgy2`agZdq%4f z##HtnpmUP{hbCGbNP7sRiF{u9Ol|4qRy}TJt#Au>zKyaJz0M-IZYGhU&{De!bIlKG z%IZ=97zxgjht`2B3t}>-@6eoR{;t4G9^Gec$~0Zc1i`+qT2{7Oy_8S?tWwEkbOO-X zWi&@%71Ie;R%-@`;LWgWme+ z0|2h^6+TCZ0`Zvg>grP3Xd;62=>Gtw3jNSA(7f)i z$+PTp`y|3x)mxJ}vK-$ok^D&&P{nFC7*{@-XO`XLiN`BKkF7Wgh`;hAGHW+;p|(sW z3SY-9RO|9{`Tkz7rwqX*IUOe5f&TH;kfM3cB`cU(%VxI>j;y>GLdFfk%Ts^=kqsbe zVVo!{jA8|Os>&|#ulQ+$^~|4_`P>V{mpSP}5tOlrs<6P|-Bm?!#U)mv`h)}H>DvpR zH#rthyJ3kHo5T(tlP?nQy_VhfA3EEYx$${zgGEHfn}xapFTKh9)Rr8La&m5RYzfzr zO8`oZ_--W4Jh~HTj~0z_aQZh?^vW0}gYhn40@E1O15Yxp*}jT3`!g^NaFoA6^Hh(m zD*t^-)02oj(|zn!s87&_X3G4WYW=HPUD29@$K4QSD2t^TTf-qaJf$i15!9SXhib{i*X#g^P;j|iB0PTcL*$CrYL>=T}&h9AZ zv)bil5Yo+=QCYA9HU0RpA7{$WafQPURSAJExdDODnkT2{C3AmOw4xWUJ&b^NPh?i_ zm#eE-BB}qe^}43}sSmmah8G00sKP=svA=*5W?u`W55ZO!GIZyHSn*p6gQh?s;e{aC zMt{p;WUVpgw8tt>9IV9``DLb_9!u5x9}sKL_toHPpEW$EzOM(CCL0bt3UQTwdTDF44IZl5wsX##!gHFuo_#M?ovL<+En5WRD%Z$B*XmOA(_cz zqG?R#7uZ&H4!Lba8!Zq6FgZXp34pdI91dE@!5Wx9Fe~pxX5%88#+m`mH3Euk+~pS#3aElAfo&JhR(8wH=-xGC&zj? zL=b{%G^3$@))$pya1Dcz%q~U9f7tdrb0=zrs9GIe)Glm$nWTo$9!L=InZS5iFF3sq z@I9hQ&%=C(t5DT7DjXo0;PMJ^1$-%BDA3rQOH@Y}8&PYwhKBFTOby-jQ>@W|-U*zd z(Imv|kNPn30XP-KUvE+6F%M8*k#mNjWR%dCP}c=BuAtRE{_mE3=y1N-*BRc5#uoXO zy;>k62m)h}OOn&;?v22BGu2B`rNn^oq*S6e^XM4OVb6gT)R&Eo6~jiY#-LXF05MF0be%+U!W1Ci00x)as631s6jnQM83=>0Hf(F_ z^frJ4w?Haf!T|B}$`>hlX|=fNvl)#iNm2V*ht`QIcgK~MjHTfu+HmdR_(VT z1cloonNsRH%NN7HwDgp8#Z;otOdAc3x~l=yeRVFvja;YZBT>4fVJBeNTM~KuQ?uXN zmEB8YwLk$9kfLQX*;rT8aP|;7%fq(|;E|Y&zg<}f2KFV?YiOD2I3nJ60dPy#D9%gn z0||15Cil`@8U!&hKP)vAZ^Pk?-BzWr>QDj@L&A{tNr=*f1JdtsQ=3C!-7wP;3|VM_ zEI9!vBozM>4ZPmJX?KlAn4rpT1MF3JhK(cwmf2K)jmldQ*|K;UY z6?(loM8E`S)q0T%XHgpf;8V!7sWfbw*b6|?D7xo==A-Z7+(50<39^B{2LjoX*%T<@ z;X|bABTW$?)H&3oVN_zNXcezmRl|#cLcvWRDaGEi%W6#^d|6G01N=H6?X?ZrPwe~}I!TK2x*t8-sN9p(|>^x;PC zju(o+!d})AHgeafs$Fdp$vS>;;#&{I=tOpKJNLYk;iFt2Y5m{JV@?oK4V)9HcS;e}Cq-Y9(QhvN>A}b)}4oUn`0!Jr4%(-dY1kO1o+vyc}GdHB6 zd;5pBhQih*_2jzdk?CHwWOIV|HqXd2*(w^O9C4l!U~G(~OetjFnW%sj(|8s%@?^A1 ze9(^W3+j%2s3&ISv*7QLl&1Ra*FM^)^fivT)L12!0!q*!_J)TfcEGJZbAUloPc{$q zzq~`GBFZvsiEz381>X5#8hUJ)1a3APw6OXrbJwxzMGXl?}5?j~QlTQbF z-q)5C%g=4Qu6-|R;CNA2m)hXZJ-`#~O;9n1dEIX?JNvK4trfF)2(&oXGGK4NjaR@) zd+%4%d*e5oZ+ve+Rs>A$5=|@zIFrq+FiT?DDd(v!#c+}kDk@0tTm2H+Av<2 z+kRD<)2sg*S5?wk8`>G0tOITvdMeqeRBGE!HRV;U+F^CEUUPM9l^q-TN8fhV;ow%@ zSLfX8{fnD!5SUT6(nF%x{+-B`zY_YoT*tR~+9Bm4?)H&WC=cQ%NUlk>L>JD#TS~Eh zS{HI&dG$bZT!8JL<+?RXn|d2xH+kXc)ufFfiD@@Dwy$z-=8s`Z-8a5_zf!T~bvJLA z5z1YpvAA0hOnr`;SGZ#jpY!F( z9WBU`1&liz=ZmsCqMbYhC;1X z!Yj*I%csbDS9!3yownF(;GJ}yb23bGQ2pK`%2SFtvGPWE($OalP(KUCHKy`XzYlQi zAHAm7+$4#WW?F+}3M4YG6<8{Qg=Yr4PU`wBv_W2DItflYOF!hO`mYV}o|YX9IMnd5 z2a~UKf-~Bn+$+=8aIKgBmg}A}%6qu#u<3ZT_o#adk2Xj&DEE5L+zFu;%jt)7CpP5zGCIxo!g$XiiO4YDdcF4Mb_7`otq<_1e#?dNy-M;f_H!_pgS&|1&623u|EW z5Tj_>SMe6vU{pkmTGO3#(E~BUETsRO4NqeikSY69pobwU@;J8?!d;Hek&|-@omR=l zsu=qnmUV45;O(lgPJj=iJak(N@;t0%m_xDTd^EyJg3eUXNC}kc7@+Nq{YoCb3)pvg zlayn~^F-*!JSBH+jZbBebGZI%DGm~#l2Z9Q*%}jKsv$3f#4A#$f4Cvi-&{+TH(Pu4ga?mI@FVzL zl#WiuboP=4cWSpg1pN4FDy{-zt_7VaQu&HwSfp&H!2%YJ@$}_@(;Y155NJ8)9e(yS zn62WDKQ%>(NPd7@YxEhbS30uR=$Y&o<6xRsZj*AKGX_%e^>j4K-5H4$$HoVMO28h2 z%_Hr;V z*=?!ltJO_vOFrvU@EOWz0O!9e*LBCJ1Sag5qc*gKl1i0v3b+`>)+ zV|ZCdN{2*fHZ>t43P*;%&5Dh(50THfwg0uTh0Mr?zgbR}A*4`wbzp1A@~%|$Yt@_* zPd^lWV>!el)KsseWi25yD>cYgOE5ntaT{2HT^Tt|R8FS6E?+^v#C=xc4#Huq$CgX4 zc^#1o2P)P2{u}%kE3e31E?A~Lc?icIBI1}%E%mcGw;P)Ii1&jVNX#Ul1~j~lsU`G& zVTg4&_Gd&ml~mpuSG;KV)?B7*@gp|IGNreHv)94J#ybe~qhR&T>JDV&&8#!sL$RwA zZG6zzcjaJhOW#Hn1N)u8d$T``^mS*etl~@$EPU|;d3PUX45;@;-#isdCf7EVO~S*HxZc(U_4QV{P|r&U1|`0dRwRAAcj(G1hb9xMGy8tNkRrl=OVB1 zQrN~4g;+GXy%Vx}E=nbEp`kncDmfu}@$st&heaBsH6-BK;Qnmf?V^?Y4JD7&(+t@1 z2452PB5|;U=x`%hcPi;?d)`!}`Mx&{rWeIYE@@E5w(;C<-E$Onz)g5& zp;3nfI;sj~ccTu0jr?3`cYqJvr#Aep_X(WlS0Bsai#T=Hq@HXj40CXP5c256Q8)V? z`7!ZBf%ToLzK*kD@=xEe{sc#LP{;p?SzSz#`?eN(edZWoa@(7LZ$TfmV{@Jj+o(l5 zzqnBz>9nxN8vIDv@w+SjnbYj|x2yLq#2LcOIXmwi&h1>@)I*xsVzMKo&IEZAW4yZf zIT6ycmLLVMIwhPo+hO@~VdrJa^bh;!nDV4?=eH4WO5T`KApr%0oLoOU8R>@8r66hQ z0bEwO#|&?y2X4oIqlz0kahb1=0R6pI|Dmlfu%H~xG3nu`qJjr{!bYYJq!32+;O{qwm8#4E4!ilx%m_9sy# z_Sa3j!Cm_|@Q!c2l0lywCx?jTy^v8k!1lwg4Gc;WWhibB=tz+~bfRxt(p6pWhFTfi zNr%?J|7~dJ^ti)2=w(oGZl=Cj&VpetA@^5$tGFywXcd1zeRaQX+<5*!5TWwJRk^n+ zd8Jg;vUV!ntyAg^4>`aoDu!kHJ$Ji_XXhj6liP8{{dZL-;IJT#3d2~+d#Ue9iMwWO zQdkX9TF-8Xj11&)-Kbt29{tNZN@{v^f=fi{A*%4^Q zH9rIlw3{6s?rg-lF(5oM0pDU@!LNt)k@l<#J(06JAGqtP49p%) zi^F#m;2mbLauaM?&4pi#uW0%qqC+{eu zG^E%ix)`&_?_zIzo*|ORa}Ghw1Al=Qb+a~+$l|{XW8pNZIqjFAOd^bC;a*J(1I+cj zwQ$GUcC7S6w&aw`zokzsrOt}QSfv~p(wqe!YszrDoTXYLgZtb5RM7HGJN$^#?9RWy z@6V7_ak>*v_aO|>FO4psqb4!q2Xdj?a#a78(XYKO^F7_Io(st_P=?jjQ`1z$Ls7Fk z&9rqER$my*B?40efX;m2WRIEJ8FJ|`aFufx_P$2e62#8 zWG+N~U2B}(=_inNqKCa@cLpd}m4AbrkZA$$!<5TshY1RCYGhiZ8nm2K(>!UBW1z?8 zzl#lK8{80h=pxS$JQ%u;{L(3t6w%H;dY-qvPGS=G0VmtRajcAy_59@I`98TPC^}X} z$NcSlvJm_@&klPtpwF%~_qcSL1O7*k%=5T${w2*~OV)#2%lb{d@KyHaAMZ_CShIq! z{PMV%NJHdtX#3TLdv-R)So`OKzC*)d zT}x>_W4otw?;$^~U&xLCy?fW@$Fc6`q(`*YO3o3HyG^L&$mZfA#gvJe6UMtC-DB)* z8=70X3Vl(3K*;1KOzjpOY&DtBq17HJ%8e~2vr{b>#9H#>DmM=jVl*>mEw>E+b}V;u zoD0gqn7)pUzA?LJVC@-2LTcBAj<}!Kb64g#dU2bh^>5sA2RnD$9(|Lvmd524U ztHUZ*zJiel7|BU!-%xVykZIS@m<$<%oxcb#b(USe-OS8@$e12MgKbf;ayWx(JMR|cfs z1f+&|o2_zE+xD@_Uozp# z`=WbFdl=AplLe(36zY0ZMicaYd7qI=(5bt`?I3AZ{<}B64_Cv65+en3ldg-XcdWCz zdvV9FP7&m0*x9PA!O*@y+h^BvW6Tg5+|C;s(Tkj9!l|W96TbdSkZhl#cGT=K1&&}b z?1b*-9MfN&3~xvs^ufp{hDw##D{<#ty8>SgLrh=R5_auKcSD|MWDID(V_uf>YPjAY z`t#*iLsBE#ochJwP&yd3+wa!+BX_MKGda!HEFe>*SdVmp0MWS#-RW0#-Q z+yu=j;9FPAOi7+25bQnR7#wvkkAJ&AtPohG^Gw=}6PPGfHi^k+aqCp11Bk`(keKeG7tD!$QPxSa}t>Tv=*mw4~CRtk+2h>0aQP@l;4-5k;FO zj5?Z~{&UlCjwM-%*U%3V!9VA;9p6)JLWuVW^2*@Og`Gz~crL&{8|oDWoaH<_bJ{eA zYASQI{<5j9PifGF-u!occFG$|Wlryyi~+ajIkA!Q=Chrl(JG(Wqd-QAtN6ly8;j{Y zP_4Qe`A^)n$nAQV*lTZk>w3^f&uhSJ>vf%SG)}`T{1}yeXG7F|BkDD6LCx&&h8Pa7 z{!b0*$3-~@X%HkNK}j38$hXI0RqEI5!hHKmdsul>&mT}t>)v-vJIIDdrH$Ja`8{x> zhkc|mxG;u$cvkr_cjJ0{y!%4km6j0(bbu8jCkzREEIVPPyyPff5@Mtj4tTec@!T13 zb1Ul)!1BVE0XZQv1(qppBaq_l7BT^bg1viVa;(2HtZe_Vz16BctN|&YA1ge#fd`-R zF5eBF%`H4w|M*J6srv7?)l>3G=H>SwXEgfm((C1MGAs%0?LJd$uT0_$EjrGq*aw|x zDRZ2j>s&K*O9t>3;hLL5_(-v#nn&}UokKG!y@!$r@#Cc`z1NZnyTI@Lw%C6(uBS~k zCKO4GFK(X24R$AlEq-T%;#JjXDAt5Y%P>Pp*&1gXnQNGiPO@@Ksm_#K=Csp2NH7d!1b6R9n%J_g~i(p~1bnh!wDo25F%{P1I zayRgW$H3)hkvAsjmcI;H`)wPgG05y$)>Y~wITP^dub`ruCVjw-#|Iq9s*4|wzb?~_ zbL(l5!+iqjwk`7KkL7%MH}x0lZl~Aay}T)o$Glg6XhGGhqi51nSq<)iKr24z;ki=N zJh;y_6kY$&V(t#nrPiMKhcRDFBf)e(A}J|)ijO$`4jH@{SNMy{cFlmnjv`B&Xq6i9 z%~X0ou~U<3koWtxfnJ%}`t8ktlJDtmgfX}83+U8p1y}+UNam+vyX#C(A(Y{CTEvhEm@VP_nMBZ2p$wFb%gE< zjY>%k*~iBQm$lW8TUky;Hz#dozGIbbyt=OOI-;E+pj{Wf43-+J*9wk5b#hupXTv+_uAah_QUb}{#j>MZAAF!6>*KWpHpAVx)4 zDzjdH9>Pz;6hO{Mf92=I)^r|ts9Jp0AYElSG+@=vp`L1bbn+~kTTf4pi9IqKmJ?wy zS0BB~#VO5kcsQ{k;q1;jL$`^V^<=vv*Xft(<}x|@%Qrho3n2kR-@w^ZW^i|@yp1T_ zT(^e_)#5sV$Nwr-+|V}I5;JKdZ_Btg)z}=2CAsbNJ>(l{zd_}en7_TBk!VJ(lzUA? z52131xlKqc%Z+Zq$XEmR_tD$DP8c}E5u-KST^IATFG1yzu(%4WJH0RLbzqWIS7hYYukiB?wODt{zd{zA57vi8 z-x{VXd;t}T^2<1Px}}t>K3eXE1zk(`)7-HiJ2?W}X8zLL&BfKQ^oUDyyj!bbY2>>5 z1v}NXFU`(SLUpbW_T_hmWn0W8Y`hHi%8co7ghH(niwwzD%R>dM>tj7+MSTs!Gg|t- z>3u}tb)e({%f^Xji1xoKOC?2bqBJb@6ysOr-8F{z|12f0N4O2}-uH!f(f!^akBXqt z9j`GRINS2#PVtEsJxFiqK4~oQj=A}e0{QD=B!~Zprtc1<`v3k{N<}x7ktp6Od*@zc z#*L7@cO=M@qOJszu$lUabM5#Jm-0y z&$AwnxoSdhwqS+VdH>RKQ+^Lao;txU@8-v)wWhOl3FC<>5(N*`MpZdiHPQI)tgKx= z#weqbKltPp`rDR@z2*ss$AxFB4{-AUQAH!0@z8Q$@UY54EeUmfMP;n--#nu35DFnT zhhpAGbmB6Q8{kH`$~?UtL|6-622%K&OTQ0zFU>5Mzl1uI+* z)OO%k;GKh9C;NRuD3)M$AAM^l#S3~?1sQ9Rw}?*JczknjVb0UTJXh^Q3;YwK{){fS z(N^I($O93qmlZT^u2ml+2a>~VVZFyaparOEeBATSCgc6GeWmRnC$qcrem8F&H!fz7 z;lq@&)>)VWKFMZ#f{YkiGcLp3A*$yrLz|icEDw&2o731=`jn1`W+z`BtV~VGQBOEn zK{-z%&X>sox6}uUg^|w`>qsxw89Yo*1*@Me>e}xWHa5W;Xtt#bA(Qd*`I{^OBSI0J z{(2buQel$<71#GH$C)a+3CrHh`xV}ILsgokoVmPPD(>J-bK2VqIC&Cvk}G6C%KIb> zhWj?uNr*72ern1e@6uqQtn|7!q1STiOFYbv(p68c5VOmU`-7cN!Q;t0ekMZl-s}T) zdOU$AsO{TJ?7dsxjPRaf=nL<>!cwX+Yu0m)nTVi|@Hp*eilU3Asf)T%**y$*=8R4p z-bL8(Zd7kmFD}^ID?o_R*=pr{vz|>@?wY{qm3}4;Kx%a#!yJyDK&G7bi7CgWOX{{F zKDmW_R>?4<{*J&$5B=&?5~E8g{m^T&k@O%tqmp&gS#-G<OJHX?(getQCt4iN8d311yn zSA|TfpBmQY3*9l2$fXy;+@u)k)AEGwc+I73-ip1Uuu`(7n%syzGM?3#VIhCW0cso@ z#qDut0#bxkt8Pie0?rI^nICMGxWk7}Mz=$q1vWQkxigq&%$6*AbbD+1B%GQC*Y%ID zcroS$9_?ok@q+;7UjFDucK^ziahdF?s_ox(Q$ma!pYTiIoxXM;ibQH=o&rL;-21x= zHw{beEo*dyyqw>{+F_F>H;?2l7ZG#l_%jn9n-_dpz7_Lo0TRjnbNkz81bdKUy`r|O<4^A5U!(QTR|P{^0#x<^UFD3)Gc3oA`)BvPRH=Kd!ZJU~rC^k>E6G@v zjPT{(4CGrbp&5SA>CIeRlb^f8BON`*J-4~v8)xSdibX9bQw$7dR47*19}lxWEyU=1 zfL3%l@94A@9%s(ddjze?(or_`%^zgBLXsBuyzOV;^M3u50&g?m)*7Cy+~p?(lrYX= zLb9ATB3T7JmgLK%xMBz&8ySMc4nR44o*_QROY&sd7iN}JS!oq6<^A-g%aODeiMD`oO=Y3p3Lzm_A+;CNin64Ic4xJlHltg*-~`k*#sr?yKwBdOwRS zlXtUH%nx%}?}RqbZGA-Cc5bf^Y^+prxkoseTedS;Jeo&}g3^rup%_Ky_WedP(_!eX zyh)3FSwq@z__aA8f*#T~SmO?As&BaeHJw^gD7U&7g2&AHbMEwk9s_;p(Z>iLWJOQsB<#HkKgE0ap#Eq znp!Ed>G_>DVJ~w*ynaq!Q?kjBf&QefQ|-MV9ZpS6bo2}3av?8|tB~;7Abhhoz$4r8 zpHSLX-^=}mIs9NS|7N4})2(&$!N{N?rU1o6c`jX;TRRj7_$jZu#+=jcw**J4xC9Zi z%UXv;$GxDCgLj833Q73L$uc^SaklHXeOWf*k?5#e(4?C6Vf_j6x8b8DqXn&9ak3}o zFdgQs(^dIyXz)QFLOQ44`L2NVJW@AP8jzJnf$;K7y++aG-Xf{~CPDj}__EQN?5Vp+ zQbu-FsC>cK&gkd|m`j_Ny6-Cl=4iq&=Z&>?3fyK4(vk9hgGJRpOCS3E^oLnK6_#rWK)JmE1|$&kw$YXEJ!FjwX}a$l69g0r?lY#Sve#=Q zS7=@!3uM#J#pML)TXcK-^KX`0MUSt4i2P_UJ5>izCOy;J!uUiVJ*aC_FD)NFnVq~& zZydZ3gm258q?K*8VHZhqbj8VY#j|r%ia?H>4g)w*o3k4W$O8FTOU2f}K#Ki9 zz`uDiYlma5lh!~vs-o??+RT$V@`=rP@bR=F)v-?RRVUFK zL8#K#feOXaMms~*oA2FS{`NV}wdRdKxySVw_MDygc{>0&xmU>E+W~dgbPl(IVi<%+ zR}lH_ZAlA$9p0oxIsM!$xauOC6g*{0o-1ZYTfvBEh?&^nC%*qY)4!Y3DiCxd?BI%; zhJ$5LnJg~`ZO*Yujhoo`C-jz(IkKzeFe_iC&*{>(tcBMFq`}z!4;;GPIaZ!#W#Q-Y zG6hq;8R;nUdghmk3^66aV$5}IZ&g@!=qYOYsfK8J7vssI;ZQ+13;A|7>9#7xE=xJG zaqQ>;R=7;ciGNe?X>`LHHWbh@5(2q0i%bcOtve&Qd$FS}58vnLn?949)qYsEg;`ms z^Cm80fM@2{!pMjn5o!JslaKxHU(`jy-j$g-rSF#aNl`o?wsTX%m0ZU=I~|8*yG7FK zu&Z5%865Fysv!9Tt)Rw6L}nO_l#JBq+WN96H;ib*WfYLg$0ap}c>sY!eF>m)RHz|F znSxZ74uuEhX5FJz(@u5{7A9EmKvoxvaS4$SnhE?k`v6 zLnOsucJf|b&Qmn%!hr|}@z-wV9sG|3j!VqgBs>V5MyqyMfD#LNhDQA%SD}wXo*k^^ zTgc<@>|WQu8&#diky_t#o}fD$WPI~II1OIn9~`(RWjA=bMg&@%k#XV&y2Z!)U9=$e4M~hh> zg7a)y3kZgD0LhhIO4|}fb94M*Zl{-V=fb6R7HqL8^x>+Vf%}NwEO!&#;J{}?r^AB` z`CBO}hZ|}P5H=iP`l*PGT(V} zN#2%?Osi2{fH1BZyRY}@nF~I34nP5MvF=!Zxr>Xu#gmy&lev*V+3Idk$mlChQSCUD zFt$#!wgjLMv#=;BmuU&%oC8V}*6!PZ!m^}*CZ8T^qJ!-ry}IXMKM7PLWO*=$B@i8`KXs<`Q=4(T4|fj?A0;@G3p*oe;4=%;3vu~Yo`*NKSq zS!BFC@e@10Pvd5}OOvDL8F6^B^;p0*sdyavVrnH$W2OyaED+X*lkAKCYr(udzGb0yAKJWT$BB@(Mjxg(4|(8tnJ=!VxkR$I_|cf;6O zHjjkqA)>Q%Y4Uc#oF-2^QL3Wf7-dB(^y>F95$smM87E|KiYdDF-ImBGu89N$xGVBc zW7$P~#>FaS`lrvc%5ojlz*h<$zlARacB`x{*Z93t6x+Sr30x+V()H37?tn{bFEFiu zhtKYtRdTH;K0$jIuIaU9qX|d}u9(hBmtTa1B^%iI0)iU4^QOW6ZX0FNAJ+XT<4fOu zo{YReo0p1u+dYg!J-?OzrTn#W7mFfbj24CC36V?&m^-@8_Gyc7lewnOrMka4tsscZ z_|P5czJi>makK#_rijT;98Sdd+3nsn{8&a)kSy_GZtD$5;A|7TWTO694YsaNi+%Sv zPWbc_fZNjKhMpOKuS}9ewmB(k{dI(Zfn$SJ=iIGgQu_GeBgP!(EB|GBXS(9@;U30} zD~X&g15yyT-*6!PL>nEvRp|~X)CO1KDIj;u1hYs(FcP<1>2SmX2ff^{}KYn7E zf^^>^H#6jZL1bj7EPL)J4yGtKo-*D{~kbu}MM5Zn$vyW|7r``odvuW74C*+sZvPXSwq;Hbx zo@Tzhp2Au`zhxLB-xKSsYEPe+EGV*-NJ&GyzVJ|dY1=juu584?VlCm`$zzG8=<-$W>k1Pu!XMr`*#J;B|Q#c;5^ij^S+rPITEdW}3R2F52 z`!3i2zL0l`I$!Mq?td>^7n{>*W}Dz#%Ll5m49eDk4j1O1#g$y?t7`{=>vm(|>%cNH z#f~|h*h&D8wXf|(8@JiaWnRFisfeFpB|pe@s?kE*K(f99T{uoD5)iAUDQOYYyWO85 z4OD|P%$>MOOO7u6`3-rFChs*&1%j+9^?=+WBX6a!0(O|TxNDJ|Tc2mVQ5W;@@o|#LyjD25Y9Y zW>u!0BF{Y1)Gx2aV;6s*#fGN*2a#iro3-x#-AH)Z;*u;Xr}^^$C9^Hl#DfKUVV^oSxsN!XJ7zw-4}jfK3su9 zzPPWeB?ugx9wK&bH$cNjIqfmhAz@p0=LKLdP%iE7@)&Z1J*H7@N#VeH>)I~ke&H8TbBF;IVk2A)IzL8)R7 ztJ+v+6tRsEhIyRPqKD*&6EE{#11$Moy-pES*d$H#U(*3Ei0PTl)#y96)$D&%7Wyos$< zfixK>qUipit90MOfnQ83riay1#0N)(0;3v=?d4gHfdjUIdi}xW%emp67_@?-BYN3* z>uljLi+Cxpg|!3X(RG)zF9@}tN{9lj0hqunbc1-^j{E446S#C0M}kd7=g0uFZycjd z9yjfeLUwjn=?`}^GrViQHX+u&sJK|IleqLN8kW?nyZp>{Ws{&D7RtNWYOG*J zOgzsf4R<-X&022)@tJ@Mhk8D;r*BPW*S2x}5b_xEmrwi(cy>Hp1UqLyQ5%#G-A?;C z`@6JQK_-=!Adrq3)#CG|_*7NBmYnwGt`Yxk1;zFVnnzm-r;1x;&@x5#D{He~A&q!kn= z!1lOv4YHe5stsDhJSYr}^MS-1n$M#0A57YNA{cB5ZYE+0FX9sjB!zRkV%!H}mKj_( znv>_ARjPw{6~fs~w>gg~KdhNGcYV2zmb(a~HbqCzw$|EvUz)9FK*A;o0s;Fk%%y8_3@K&irLJ%oi=r^Y*Sz z$X$u~@Wl~e9a%*+Dx>rDPZ$RZT9M8jQYWA08BOFv@$5WqG`^U!Z&@;EL?g8N%u&(mgvY@7Q z$Fk?oXF-aW^#b0DunZh76<`JCtbLcQ)w zpNBqI4Mr*JR{4KDuUq%*uCo&@WPK^^IVVjlE#R-NpkN=iTq(kHXD-Gj)D-M`4e8n7 zYiut*(gFhjC&1Wc&E+4by0AX8{_oeC>vneA(j;3x#U1YD9@j^f#Yn7fHhTJT9d|)} zNL4FY*2h|)V~4Dwj%6k1!(GI-0d)^Q)IwB^@~H1-5z0=3EP z=cVZ51V+>k6asM(rOFf<|Jw87g6t-%?yQvkU7y?7PxRvb#N1zp(;onRST?)eCcfQt zSokUVahy;!U`Ypab{0VXLH)@+%m|u{R%6ljSqqaJYn$$fQ-kjQYh7&(6?9#FzF`v+ zSW(>P|94#6K39Gc=<+JyW6^zPe;2sL;&P%Zbj=nI40Um1mNdso8WBnFHrcQ`;d_2}@(!`#Z~ch>yLg9RHoE?_e2 zUweG)r3r9DyGpb4Z2F>nQgb_uKlD*tAodZ_w?I=B)!AGCxW2fT#i}T%X7fuM{8_d< zYO5Lvu-8JvUcA`BBFTb_&~;(gmFp-()ITJ#lH8s=_tQ7SuZ)6`tEl(*5?dW|5(aRK zd~>hrDXF4vbS#5HYJrWwARiM88J9y3gI}^Wg4v=wA6bWxs`Pm7iVFho1)@*M9KE~t zc8}EyHeQO9epw>bTOSts(JLfM_)-Z;Zj2w2K?2;&N*fLvGM01i0n8d9wbQFT`PZL> zs7|)c>plOGHH%XcE%~2Ac0OV5$u+?B56KI$Bco7f+0GmDVh!|%QE*KO;zcz80Vb9} zE8=BSL=WS=-6}qN;RX0qLv(nle&C2djqiMlX77Wwu#k9ofDyZa=+=+SzvDk|6I|?b z0f@5SC&yVWJOLq)_-Nj|EK8J}?Ek*0Rj6{WvC3tKYj*+T{zcq%)s>S3 zfYs4-Yf?$5l@=uMmD4xzCll4APn;tmAsB{LO!aAG-oI67(P&2> zK8umpsJ){!*B*{x`$*Q4S^Hwkf9o=U^aJQ|512VAm6*%DZ1+}Kzi|Y{F9sQR-ZQav z`#Vmr&(xoes|=^1${pTbzYK5>vwgG|0iULniUG)u zw9AA{wLI8TFOfxJITlGy>=#ow>YFO0ti6ql%?YXPB@KqTok!%^?*lCwlgaFsQfk3 zDkq1-H5qY!A(EaM@Eo3%Rs7Mnfw|pRy~l=cPsUvS=+UafmSdCS@;~3W~O-1^ws`>I5+N{WUG>94Aq%v$rv+Fj7<-)?!S17&iACq zGQG9O`G5*Ph(5K{t)`)S4Oo7{JX2SbA$)|!i~;bKzw~sOn-)Xx7fxkk<{Nmb=-%DB zfh1%tL4KKT^NrPWZKM6u8Cr^cg}YYErmeyd^P zg_F9l<(d`(&m}4%a{I$8k2}trL2@4%Ic20Mt(TtK0HAVDsMy6cDc7aT^)NN?0=+Y# z$^&{{`%!Dd7JYh=lpKlkVcDGL(x6(y3BkFXPG`24by(M`+UNKpIE|)$Fvs2ouD0H~ zszG!RYC<2BFFamQ2jT$|svLpXp%su|fi;n+x z1l=}QtQEs5tBqYr?ils{wTLg5@uY%ayCkWnr^y8Ouf<3lk-vLJ2>l&DR<)q!FKs)4 z0sc12z~MWl7Yp8Kc$eroM^W?~n7pt3(*i7liQ69Xbp^AV#`jL- zt+&Jk`ebck4C9l#wib#aHLcusV)x$#og5E(^WuD`!`W=o5c`$z{5`QYHjXrZ5~~NS zgc_mPj?QWrddHt_F%{v2{dgX2#?aH@jg2VfF@AX4^-S@3fJ{D~?D1i~>QBu@9HDCS zS-Vj=<0ZzFz1V9k-QpE4}oB? z{bl3N)uYJttRUN`7IBRIPgiq8Qim!A`ARo_9PTeWFMcI^MqIUF$_-JEZRyGMo^<;M z(@LP$>}a&EO&BSVRCHJ8qiN$Vly;sI%EB+&p8OyeUhjH2 zd2%cWO*^L)NL<@&vMbWDVz01=ySuQBhG;F379##MU3-Iw3liI{SzAy80HRZ7(LK~< zRpys2vFj=_#w5&yvHDeH-#>#hz~@O@E*|LP`6#c>9w^(>52-Xk>S{1Z`#U7SKOz+e z^=#4(V}R>o2kaN+y^4o!JDLjZ0KjQKPDc=@v;P3q2Bl+HB4Z>JO}8pX8dtJ(RpXQt zYuDre6laa)mZ{^PigL&>-bs%ujvv`$(nuQX`N%($dPyy`F*z5vw!%0psE_sUdwM)? z1aq*!McJOjXC~f#9!}AU2|R9Se-o{}9o@>j4Q&!3Yo~^IpkQZwa_s)t!1_yLp}M?U z;Z#(p4DXQT+@^*h=yb!?i9_};8`c)fN z@Q(eng2&OjClTDGi*Y$NoQ@G!QB4i<2i!P#RVDGQ#6ivH*2rVfl~=+|mFM|J1S2bl zGR~do$&vhge;h}&F#MGZsJV@=`Mal6V_*Lt*Hem&W#am`wS&bh6Drve0Z!GeA+M8B(5ucJpX|*QELu1a)q7%LLB4L>A z87*eYTL2(z8?G93@x+mvIbOXeu8LSRbH19CHsBBf4Ld%L@Ls)A-8nRrmZ;jsVn5EJ zGE+KvLyVp~aH#PHK$a|V%dA2lfQZ{>u{;HEq2*RqEG;alzFFKIEPzA05$;jyo&k`| z_jb~{Ih`I4w@Qn6VfYl%`+y+OEKcyJ_uZ0U%a0txg&tJn#mA7X)4VY|sl8w!ev45S*p-O_f?gAMy_!!+!S0!C zn@aYtPoREt_oGyxh@~_;Aguo?qRPK|Jx7TTl3jUckE`&fVu_?@;I{4;`w>$6`*$m+ z8QzA2G3HyJMJLJ!IUO3t&)0{s$&3mUza#Jj2iaqt2fCSahSV~=ob zs|wZpeQR`C)Hyk7(Czu#lbk>+S+%ueR{%R8$jrjQyrRntz}$*5%A^En;#-a+gWlRD7t$pF$nf@*VME_DNbgrkk&IQ z_VidX^qgSG54X-a>fKyC0$j6JCERn@xndmi=M1^SZ>wHB893x%Ak(^; zwT^gB$bH>pLY?K91j??>-(2_@!(Hz;(q%|W`E&ep#oi~Y#=hwh$@2gOA^xsWx$DF~ zS5g9@nt%6oP#qeb+35U1NCn)T2up%FzW$BTysr?HBGg8-KVK2o%KVmHXS4i6rySek z;2o#8dZ6>>w>y`=vBpZ`g>*>79U$c1A>Np;nT0V_fCB$aTUT{ zg$lj00Bp^>{-&3Y8r>~jlzbO?1}c*k16QAK59ghKbPXgzS(tt_sZd_7#<`D z{9PLJc;p%H=d9Zy4*ETJJ?yCJs>DP?6q1Yc4ZY78=nA^pfant{ltKJ1>ivFYNES%B ztyEvieZWnEJxp|#U5wg%2+(Wz%7QC%z8_m+gne$)NQ$-YTPxN+oo`LL+C10$0HXK| zDJ(3U#J!hjDuvx2_-nD(Q%Rr46-Q0anK3C~umHkXgog1~*90$QXd3Jw7()Ei=uI&J z2oS zFk{9wq3dlQ&A2*V&Rp;?)6T)%rYCYb1PgN8*&UN;#Z-0{__}HZq_$Y#s8mlZ96;ub zj&o&LElBa-KC{VyZg0Y=hft}~|4oZNYl3nay)y2x6{DT~ZL=MaW$u*UAYVA zSH(=n(Hwnzrh$i6o&8D^MA)PW3zc@ga~KEf{+}60Y9#x?b97nc>#ky>fxf1^F!ok9 zQ72j4kP4}Y{h`FZ&sLVugt6Vv^}xg@xEA1H&i3Z2%G9%s0V{KuCkpp!;*>O*`CY?n zm~u**xYv5F{e@rNx6-v5c*RW>^`CP&YUfy)uUs$As0QCD$z+L*um?J#rA#MgA!2k)cXgo+R3lr*H2 zU_EXtRO|vUiTIe2vP7Ci#ThS4 zdD|cUsXVW?jsdb=@PqH@!qNlL#eVVApHan6$#0^H7q~i-N}#)a(hWszAA-5;8~HV^ zpMnMzj13~t4WXALTE|;I&aaE>RPSLeUs+YLkG#;45~>!yLecT`v^W14k2Uc~s(-FI zE~nc3s)~hi2{TLIk))VFbt)r12albOOjd4K%l~eP5*5z^D^|?6_a76ldVoiIr4s}! z!;oAXO^%Rz%u&7jIh+~fj+?uyL>y84DGhIda2MAuG_WMNYX1;x{2g0?S42FvtGE$~ zj}qvytZ`p$CE`Ykl4Je)uA#T2c+%L>oz(jzV!@W| zjd~z(b$#R2%?KnZ#d6j$wA$ijhwXYc-;)5ODmS9~3E73^<=d*?nI#ZX2|SNqRCi!| zXpkJ2O$R%zQJ&)1o8jbjPdM)x&q`8Pbh)qE7QH;kjCFKN=wKFMWTFYnDHTNnVkcal z`kKd9?V(S&! z2W0MkP7UT*53E_dclx6_kURB$Q&PcBlWv*M4{^OMyFNXeBhK?up)0>hjEMrOc&sDa zRk_immdDMGEjwPvCzJfz9n2J&i=S$oyW=@F2W!Q0?->%aC#3?WplzTQjTk9?)BM^= z_h)@|Efkk@@y%;lj&2!Sumfr3v7n_}X_eZ4_xghP&_GjXc~tL9Gzypx-#%403~NAQ z?bH+Z(cFz-u6L_ha1Mp;^YaWHN&7xn22gqd!q0{x`C=?OW}yzaRBaQ1{NP$ja&j`0 zR;rSc+keuS2StKsi|x9}kG3PTSkjw%L?$Pn%J~9|y%hP%0J{61W=6Qi`|_zK_xn7P zkt&CmI7X6Z`x(48URsi;`kyB~*J=02mla55=HoOftN#og=rh&6M8EQM`R7Wd2k14+ zr`tw;O(E7C*6}hD=!3RHqu*wMTx4>7&{7(h=l8Vtpr;Qh@Qrq5yv$UNm)%XkV%*O5 zWNymbU>EE>g~oNfT8M$)Dtz5XQUWa@_m%HE03(>Wx{rz+v)~1`uO`MKbY&0q?xrYu zZ?~Vz^Z0h1-#KlkGco#tx#f|$d74;(=eYFyZ&{Emu9xRrE55N=TKN??YC4~+B^iF5 zbD>m%H1i5Jm3apq`PWuU;Sy9l%Xk&+!%FSOmVvRZQ0;8GeE+}xJmyJm zb_rX*^X(Xqa09olj92VGyzkKHBpSEwt!97ZMp$dAzj59N4_;snT^fVM{?1JP%{XSW zA?(_7I!dr28E$?iJHIS=Jjnv>5cRxn#$%i9CML+eJ1=x6 z^lXAJJ_+VJ%kg<&XSU4yQ1ec21KGwm(?c!)4`8UyMdi(OWk3Dk-fl zpV_rKe(}Hc-dkA#Czm&qm9=xdx^wn=ZFYx>_F<29t?$yf2z z{ZKsyy1pvb$mywjjVF3^l2QL`i^o`X=?1j|@;#QoF`O0#joX}3`ZGDF!t~qKU!WWH zQZ#bQ`aMm$`#r-KS?~u=i_i%^3ha+f4=NAcuq_=s3H{#s+va0es=!u7tca-5bsim5 zZEb6d-}ph9xn&@>{CCV?7-p1H<_$vaL`FUD)?Z4o&HCuu07E2?;WYB7-sYO;oqHLU zP+;xTpL=BblYW_b+*e)5^)`C;-z|$`fi$?uuS;Q4P_KZgSDyH+lr z<$?(lKW$j0;C?-%ciOmqjzpGSv- zHhrVsi2u;5Y1F=dx3o=0VhQk;^qS!CvFbQk&-)HZ;mZDTEWSzYYzwkO)-|<$nrxbZ zkU@pUp_x|xVQPveU5iQ4C82N2YA>Wu;>xj`0UkYEyS}R)_bx>N(8VXXuYhy$VQ!r? zahV}qcURei=a3u3!rmD(t5~i9W;T3Lfk-0+qK9MPxn|y}kd}~o-*};})pF^Y*LTg* zCw?nDF+F%5EwB}TPlHfmqHCD`Tr=a`#mGRy?o@sKB&#Rwq5M6=B=smJw%kXVlzWEL z%S;17ZxM-=amvH)dU#fzv}rriw5tLv){9yz6o(SWxH(Pcm_)(XWSg^2hsJ^E(_$ z{{_!Yq3h_uARmJwauGS)G3q`&bO{0#(5Bp^b30AnPTJ2)MIy}%b!7oNe~}_Ff7!L? z#?ZF9z)CV~-y1;th3T`<4z(!gw$(W6hkK;7PziwCrNEEFf7j zdcTgtJRhR9D%j09{f6!ncXbiw=4R}EpDE{ua_T&Jz7@$NFfVA|ExEHs!Y{BeReb&G zRs5x;?!?@i^mJ@_&aVKl@3H66?ETH?&%|s_DQwXhoCejkAFA~83iPRs4?HIV=Tj~0 zS97)i`PZRw<^E}s%!6yge2tOKaLlicRQ&{j>!c$8Lw~|awZ>gQ0^y4|<)d$+NB4T^ zAx79UsoWM)k4Kz}(Xme8NvnH?Qdz;eAD)lPvv)p;dc`x|+{7g2_v5Pacehcy*!>m$ z4_-Kdo-NMp>^;+t!?#!) zJ;#C+Y_>YAotmpa{EZ?DNhANBPw2(m9b5PW<`R9n6sG;hqjOa<5hznQ(TrYzKp4 z47+~uI^WgGxUz~y^~6C_{l{$uLPhn?*8U*h#toc4k&+OVs6 zaw!pfVAJ-tEbI;tGsstIHK#`$_8s?snGSL?7e=+IqFpg_QvohnvZx)j>@ZIZRVo@C z-;Fyrrot&CuZ_z$5LUJ`PVYFB`gb_2#U+kg@1}kY0Dnr%N}R!o5}{`YFppMgt%*P* z)7)jyx$eeX2E&Hu?qx|wM}hY2HD5wyGkbgDM0&Tp^VnQVwGc}sMS}xi&HJk4gbSYb zfm(-`yBIYCiIfZ{rm!6W?WkCBl?^@P<~ysm4fsl-4L#*=5uxE^cpEsPv@Aj6WC4gk z%!2hU*YD<{vKXm0A`Tf>8fwY6-V6B;YxfkYH!*fzs#U>i@7Ko@$FPn}Y1WRU9<&`M z@Q(g&sM^tzfo@_wH`mQhh7?JMRMZPC&n2{t00viB-eQOy{Qipq&m@ja-^pr_#2ikJ zCQ{exavviqpg%0hO#K@M5jb~7w=&A z>wv{zNYqk zap@e9&pTTU2#=so_DPa1f7-Se{i3e(P634&4%H6f;2K)*_R(o)yE10`XzyKss;R;P zq?Li(rn)?(fSOUsxr(9D1`7r4TwcRz?sB4@VzF`%aOCps3yaBY((Of&u^(NJ%Vi^z z_Vjaj$Fr_qCPx`OJ~T1l(J@*a(G1`7j>mt|N%?-U%ZY2$G*B!M_tWkRa%17f&VPhD z7xR>D_9U%qO<8*lRZm6AIACs9SISmp1p!Ve!&LoV7C<}(72F+gNMu&?L$sO!W#ld{COuPj$DCyT{vBi1aANUf@%M|vpZ91KCw*0KBjMmzv zK3_`QA(`iMO`C)@aP*L#Q<2O@Uw5=_q!a^BsHDro&$npitxyX}E>E_sAud9(0*$L5 zGYv}U@^XY@+72CcI+*QyTz5Ri@TdP85+8yfKMN&9Z}FjkGYQEwcqr`~% zM$7I}Y5}~nfi}x*wFf=uoK`)OZT=)vnyadJsqI(rt$3sg!FOm~9vrQ6p(AOD@X~O~ zyBOVA9^AP5C(>$<)J6}=Z3UvsA)i=*o@GX9mvq|JSzYYV8AJ3`Lo8%%1D_eQ94En8 z@-G=nV^LcZBVBiquXy;XV!XGq{nG4W+}3*B3CoQX;RJz!T<-j%wF?wj?(2|aB8Q&B z@Xvsfq+_>F>fO{bsKNmkd6WITr%{>#YH z#6E1M$TzptSnVNkYqi;S9$rNMgG)E+tLuCHUcK%Bu9yJLG$(&qYt#9Y)Yy7-L*t?f z?)*{}op7j4Se}=KU0plPf`t4T1KX4U=q-Oops-31IuC}>)u9Y z^p`8L&qqoVh9BdCh)n9kMsWk+Bi$<PL&QN)pvJyEIYe=13#!6}!}k=H@wEo-NCF5Y6U ztm_8k^Rs131J<_3>Umo4kmxOML1#9>W6|%6+X#a!58!5hsb2PpG&wRYODb@8_gr<( z&)iWMbg`YTpMV*pOsiyl_x22vm~X@WOD%_9vUr7_3hMQq;y9OV8;}_zX>BVX?=!W4 z)%ReTFEHmy5J*H%Ed={{V-yw z_K!sjUDf{N2BQD;C7Vg2Jh5%YN)!yh=6 zW`Oe-%;-6V$jO{}bM7euIDSFzj@Q&eZm+GJr7^i_53_gk>~U#+g}()M*Z`Iz+jqNVn41vy+Cg$?>P3`ajhVl`aPOFt!5g9t_a<8{M$f6<@V^nOQosznl*l} z`#)%zXH{73;lCo$&w@X{A|p2mYd;t2{`hf+-W_2Ups`SzWu|`g{$q$DdoY z)#P+lZx# zspE1a!iwg*x>o9`^N3}3d5h5av-$GxzW`>Pu9Q0&LM=f6U1u-2%~81taW?TcsC5w<+jj+1CQRVvmDO5w z!h;L%K6BENA7n3oMxFEXeht58h(y#goYoCoHNMR4mx5d-K>F9MPuPL3^w7dD4jY~2 zU&bu9JixE)apRIcIaMu6`;JA?PhurVPg(x`d^a}_-88okkrvv)imZ~=y2Q_u?i1uw zqRvXZn@Z4%^DgtA%Hr0}d8bH`>R%8#Quu5m092K1v0sJ1#eA6`&HiziSE&w>7Zm>p z;ql74q=s+jpIAuXJ5|RH<)_ z9@B{mpUD6J`-ItLa;jGx18K1kCFH_CoW2vI)tp;+#%%ae^EHtJ%DQ@9F@}?0(K-9v z+#MypwD_f4H~x>k_Y7+?>%vBH#u?inDhLh=Sg6vwQdNr5K}w{H5a}RYLUSx2L_|aH z(jkEaX`u!c0RbUO3q3<9fkZ$EA+*4^(Q(H2I^Vx@exCgC@&cahXYW<-b+5H|f}(f7 zxYWvWi|z9$%tO|h9$o+ao;#dsv;8pA1KG(Ei{P)b-1EsE%ZVEJ7biptYfFV=?2BW* z9S`S&S;OFmM|K07uBpn(?Y5j}YtQ5TJ4c;9N_@=IlwIZwo@vc#Gc-u>}+ux0G-jb9ovbWX7#@*{)N+Hb`&|8ImD9o6=hOh6j z^*bVdZAmP13U0Gbww;Vxv6_xv+vl;b&^dgy`pmvV3_5f88h`7M<*s(vM}XU{WALoX?A(Bj``*?At_Tg??w-Lt}f z`DPstCmrlV3O`aa4*X*2U4XJtB!NKY|!+v-v==A`*i|#2TId#`VwEsdB zkP#1&wA*7Ts}o>B-6JHC!4dUh62rhX>r0Y)bjqECS!`^#{of=g)OF|lQE_`)jBmiG z=q6)6*&+~=VLU!);(=9YC;}k9(w*kRj#<-L@b>(Yx*!vdj|cVuE|yzdtbXQ%ugG(6 zdFStlDmGHOL)BM@&$Dh+xpXA7*MHS$5V5Q^j|!xLi8+MW-7{PJOJMo?Sfr%Wyx)&} z!E{!v!!XXx;~Z8?olO;e^DaFydrxJl*-!8JG`*@Tw%&Hjz;0KJEA3;rXpGPW$SjaSI3&H}Km5t_jENHE$w2=axLboqI>)^ZN=@SbFT`d#6$Git&iL!!Qb#8lgVerI1 zKs({+cJ{r=)6OT!wV&?kSihWo@s+&s2bXp5olUJ9g~B?8WOH%}e`+LlM0Yh*?M-U~ zI8%$9oJL;K9Z~gWKsYhx7?urD4f!-e(i-I?!+mz499ULC&~&A=4Ft#a_A$l%co(N}0o>?feDYamI{ z(Y8fKslTX@>8jYpzjA3?8Ss-_)^Gomu%ledzP!89=e!(b`)zm5SBNQtTkNwt{AhaE z#sU#!>-C}Lh~3X^9J^LYv#+G@-o`*3dqWsqCzpBy2S^v@?hnq!-D{6{MKKZocG9=K z<|7{R&5@cULi5n?*d(h?3YJ2CZR92e*tw+;-MqKG2b=&byB@5u8rlI>u6_TM;94}$ZtGgLv`V8#X7M4krgGVZuFxu}g= zc1KjuH0$qoY@gRIa?vteZUyG>3N0JkS(kC0V!5q7y&<0TOk`q=I{fP*6=EA&E>?Ep zEb-p*sQGKpOvCUH291w9J}h6Xd!IX8v{FnIHBenOk=Sr;27Z3m8_P5;t1g0|IS-?KTpbsvhR-mnuUN=sr+s_beQdQ-Z^ZHb6s}g z-?i^XzIZ>=v7g1MW5l}Y(2^{{*5om~kzd1l)=n08n2`yGk>5;n>0_O0W^&B=kk`tK z?qkwu|EIdI0{R$yfI6Jrv4e<+=Nwn*|-sE!l6FLx%8>t z4ij`IiyT*8vz|1_fT5xjQJuZp)uN0yO(Ae=fFA@Ebso+070ULosLC8|caIf8o8Bpi z7$c=GUTQw|#l3-&v*{9MGQDK1iVFC?dzZOqsq+%sPJiRxTDLrOyh~-TqEZ=f6?cny$6$SvV?YMIlLkisB9d-YTjg+JbX7o$}~9-4T{Tj_CUt;zz$~?OU(MLMY@7+f~8LdN?h#GUY60;6-mjwQx zo`^y2{`%eKY5eK9b~@hP-E+CxPLj}!PdmY#?zxqv+PYw7OP2hp8HPT!qb-m5Hm_C#|*R3fq~9d$lI+ z`Z!i#Pnm}10cDgp%BdAzJxctoZQ6bi!YrKuPRw?WR5pSh(Go>(A2Cuq`q2Hz}Gq7C-Dq!sUX?GA@Eu5Z6gIqMe*`gY6EW>b@Ng} zGD6KQL1_RGjVj$jO!6786`HHWy3L`fOzrAMkb~;QcG%x7+bD#=Z&}-XsoB+Y{HRe@ z9W>lP$nfda*$@%Rbn?o}j_u$bk+e6P=~lGefYb&AS9fWn4dYCk#%hS*7Hyo@=h7bP z-fSe6H-*Z5S}HcZ!4h&oZ543K6N=lY#Y#6`P<7>KOW7D{BDLh07hbe&7ZiZUXeMd8A+>p#~D)Hdls`{GlE8&-< z(`bNdB*HBzWyjcb3LLWUel8U<^&b!Y#WN|=&=e!WN*(NXKg{x=&e3z3WQRXfr4TW- z!=$S|kmpYUUSw+~W>=x@i@lXqy;gPo$wchn#4r!J89Bv_+uB%lqjMv#TXII&^DMOv ze7EGE5IYC#{q7boD|vjhn5v2VJ^jfcs(E{I{nr5HuGZ4j8K)R@a&sW}MR(4~0MzNu zE<70wg#+K8$Umb4iby~!lMCl}+?Ki9GRj9sNaIY0`W1$gWnpvJ)X}v6Y$+OqhvZn>-5?edb*)pf9S-W=f=S>O%sL2=ZYI!N{X^JBP(_amf>*j-Wi zZSa3q$m}H4(q$rHS0rZID>y5=Vy}wmCjU)~PMMk-J?zfl*#3hn9L;55taha3TK7#E zEkcXYSOgB<*5Oj5&pw8N#KROfoZH!Es!6|qQz^(g+vX(sx@P^s)@Cq+?k<5FHp=Hn zuA^@%-`|g2#2_~e?CcC=)OD^WL)UID5L31X1}fxBmCLOenaC_Ou9aZJZ2p^*s$!Q* zL&kj5mIuklqj4pakX{|3OcI6us}fs~n!V5H#48%=pRGIe3D9Ly zNn|OLVsdAnZU0eEX#usDIrm?}yHiraE8|7#XLq<&x>~huAE`?CH-gMgyiW-P@fo?o zJ^fOaS}&U=br9+1@F*FqXAoVx<9=o!z=jWW2 zdmNu#q>*EHh?_F+C86*T26Y*d=Go$rP=H-3>DDduuvj{?*AL20WY-2kzkle!A(jW? zT|%a%;yYnb=**DYvT>W!lF#TFu`bfpf!q?QHkB*w(uM1BZl8^n*xYFmLaE=)Xq$_h z6Y=SWj2t8?yHBfq>_gV^^p|`2Dz}|M`}osf#{_@Wn1z(eL1B#OA1f;cmH7fJ0e4qv=< zKu$<^)G4m#k<%U#5@ZraEukfo=KHUJlq$}qS>Cz+L)3w4uvpz%;$(lbVD24 zp3J(M3a4V4gAeaAT*f}(+7%3CS}(Vykl1yByX-Tt0N^2>hb-t$Gy2oC<5uqQnhMwj`L*!pFcCFZSK@ z=JJue02pu4v!Is7QlrL1Lr5Aq$?OU1pNPK`sZ$*it3gJzzzsXT&4$T%KDFiJf>S~~ zFq0O(>rL_jHq6s+2-l+{2251VLYoHspth3do~7y!d+D#`REPn;4a;^LHmluaK1C?B zP4fk)do4-wJo{$@E2o}I*e$-pd*)suK8YMj@xOLOhuh%!Qk?7pf4jDTX~KuE9jx6t zvhz88Rn3JP-i1p1hgfcpuVT%j<4$tn2vK%6#=l&RP+wKlT~^^z8~&S}kI!c^{)EGc zu-=^JEv3kvo^?m}MacoJUG1*{{lxPF)_%DgesxVpvd4za)Kc&0@9?q`&Bb{;Ptn+z z3(tDI-E5e=u3O{X`_~vhPOc136x`g^QbtAzM*XHhrbDIjPiY0T4hUUG9 zJe+C~cUXRV?$oA|IILmNUb5*o6&<(xrc+iR@8CxK#v*0u_J9$+ugT+Y_TO=g_w1mk z`4I7ku7dFE1@>dy6qAVSEt7IWJPsH(`s03|7CS!fUvAQmgoLTQd7{o05#A+HlK=$M z#8H2eb72ni=SNRQf*t;1M-Ej{9&IQ|}07>bS@o94H z!PDM?stE~^{3efHtd*U+QYbB!1S7cY>Z<9d!QjRF47MP5Zp50$=eo--P}x|luU`6G zXT&M&^LPBg@LWHEQ=t`CqYnt2O?diMC%QanHeVz9Bu}0vhV6-Zayg-0F7ofOn=2~V zK~AEWo?jk_0J zX)=OJI5CqHA7uz$2!%spVdZlwFjc=i>%iANn2X;P?d~&fZ<0Ur_4~-6-EeJE(G%yti-wu_n=XLvPg#qbs;b+-{EDCU=KOVCKI=V0uH@I{q`_`uy zFE+=^h<~Z=n-o$ayfE8gOy_IpoaE7%3<+~llg@s++@!_<#CvV?6Hz;pm_(&Z3q1#B zFW4~#e=D7sKXHa~C>JxBCi<*{m<l^s@ru3%wL3+%mp#_J$_=!bx$t1e* zoolM5Rq9~eE9RL_sjSlY`I9z-=`6i#lD?B6^TMl~m63m>YkTvsxajHFWp97l z*4O0(FN$JhBMwh$k}2jz`yYp6w({T02`OQy^c=fKNWCGdMvLDDY_CSUyh6T?i4(uG zXK2CXi&XMPW~WTEJ?I7SRo{JGJ$u4&<#c5!4gzXoyT0ndFT-$R$(3%)nt=Hf(7xjl zvAf}jYIQHh4RTWc@-2ZE)hVg#(h0tqaXqMij%)TYOvfcF{@x7^c_&v~q7U)Cd4={? zKd%3DUQLd}AMu8T`o1nQ0yzoKRtUy9PKgtr?teCXx!0!q9^h1xLYCkQ#y9Z?N zUf4>D&{?^JS4QEFo0A3q1Pa2$_3bH0+J#QSXCXB?lLmDzBK47`?8rzC|NGwFUh$aR z`%roN=&Z^M#m``oAOiD?ZzO%G#CypTbx*f(NDhRL@2HpWW=|D6u4y(GPD zjWGI|@|lE>2h{7<{QQ!olR&zfqmE>28b2Hn;tZ{rG>`~gX)*HO5$D=rbmX@%Hb1@I z2K0EgCm4HhEaX>yheE<$oNX%vcd0K9a_SalG>0&}3=AV4SJ*7@tdv#A5sHIuRNuFK zCLJ+E9Y%Fktk+c6)IdHiju%P^ZlzI@$s2JN-#V!Qw<$0GVH-Ry#L}g7!v|(LC+aeWL^G#{=|6(>qt1E`%CIgALEVbx=qNf;l2^ zw*N>A&a&-T~4S&IYiYDWUF7?e7*>~)2`S0 zrg_ikI#!VTyI=Q4K8FAduR42Yr^4O`?r#779(|)FyeTGPAn>2Szx>HY{?$urxQVpz z$Rks^;`CnQimRjUXF}z_#yU2EE%sd%to1H7^DQpBsv{`ytZjbymshGpQPP@3=({&U zYVV?Z2GR|iFMxmPA=h;d?S{B;=MZC+w85@6dxJeE814Bau+z-vtK{n~qMs0g$xC?a zn;9XUB9;rE@|hiTw07=zd+Vgu>cy=H1;KIqGLU&TqpF8T$9&N8g|%ognmr*LHQ=5B zbHh26U53yvc70j?vHr6Ok3W^)$_|`7eKhfzv4+Fwq1k~Ob6CiuLP2%j0T_T$l&$>^ zu506K+6l&XZrf%TO4NgzqR}~T+jw^V7^1A!EqClw|6DhFaz*EHrH%0GSMN)W6ogGm z_G@1H96Yq;j@YwBOTT4re+~F^kg5@VH}iCp+5>}Jof!b^l?I!Z%12a2M9}nDnU7Fz z4=`afy9>|L|H;ab`<^E`Q$RE+Y4> z@hx8cbk)Hj$Qjac&US< ziFJwS1^M;MtH*JCoqDwM>zg7ViKx^(v@iRNaqg8vzX}`;J@FR2-Tp^L3c%!UwEpi4+LsWWXlT<~dV6$T3dSoQ0tRMmY z9oxA5R^;ugNMqJ+c7;NUGb_9=;lZcTMO+YkR>4Kq=uL44n33{#T#jcNrB@E1Wp;qh zQkr}sPWLESFSRmNe7d)gJ-}LpX$N?%Zn8)vKa*zE)Le4W-$`r= z30f%S#1Ni`ZPoC6qdwv&qj%>8i%#YMsKzvzdje;T4F} za7|ms--_{+84AO9psy7Yb4O9fV=uyIk$MsNYi}s)5p^Z!CAP3S6l& z$aDaFLo*lR#k{P2X-4l%^BTBI2WamJEda#jy*PAA?eZ~0{~al=dMtNRHvDspOEb;>8`6Pz>8|EVq>cIkbH2-QnJe1$8)K8OGM| z8AzFR%PGGM0DRbN_jY#HfT(~+OVka#hgNWi5)TtB4e!Z_t|#8LgVtNzNjI8I`!8t54qBn#!(lb=K~sSoe>|U+j_RFIKx7Y&HHy_# zyZp-Tgzl+53Ff}|`qdUPB-eP)NhO>Cd+KW7kA9!79=QY}9U+{AXGqvHxe_TBz%m96 zC#T$L*~Yoo4jpmqh5T9RX>d&dPcsGa8>mf)+SYa1Y%oqa@>(WAEl&Qa15m#4wPp|f z<;4my92h#qY!>g5VV<5nHlRUYj^+p&%l4Na{N(WMEm$sm)}+DG?KQ-nuui${Cw$e9 zI3;Fk^x)o586Zp7(EKosT~D~%se-KprNnWhMzDxO!Sn;}z)Fh(D@*y;H6Bxr5?Ha#kWsM{#t<=m z*Q(E`D#fUN=D;56w&6@~f{Q+r&aP6}LLspqjL~CZW)q$a^%2|%xy~!wzL&AMo?S6G zVGpeoQ?{eu>%s&D)ZU<9c=;`jW)ZyFq(gzl7XQUWQk(^@Y)f$mfR-VpucJF&c1IEuxW%!Aa#5xEm^=LW=)q%(AbKdh<#w5M=QV@4u z)(Oqr3v<*Jfe~RT1m@innD=p%7#x#aQ1^CZdkI*_dg)d|h$>2lLbL-zU<7q4+QW9; zy_n6G)@SbdG*!>CyQT#qZ3lW`ki{wH%7|BWP}fxb_@W(yu0To6ovL+=80TTQDup-m zwknoZGkhCv^D2BhAt@7XFTIXO<&gIQ5@g%h5B#w*sZr@*B|^1@uR zOr7WCnc@AU&9~+IS2H0!IC&7Q)-XkDJt7 z*7vb|jL&U{Y?>L%4C)15s~ zEL`u5%&6XoN*7Eg_>65HXqduTwFl8_;npAF;9_dt5v^h@5=gG3r#T1PMgvaztRFNg5jWKU05Yc`!d>mo$S0#k(=aqgjz*FVE$|9jV*1 zWE8X-MC-;FtkU&P>#?}eExouZZ(@yOg_!l=@_3x`rrtNV&7zPr%Nbk+huiiGD3Lx% zS^0oK)FK*$4-{CKaI^*nkmhPY0Ge#B@en|&#Yxqz?8ux3J4^BJ;+=BD$anb{F76Xl z4=&&iz=h({f?Jv42>#u2xc$KNs*g)ue4-jtc7LC$OptjK1H zG?G?Zs9PKQ8X$8&|JJcUC(E_4!Py97j@C+Bl}f=ZsD5}-bqVaP4BNlYx}}n7NS@u> z@L>)s`;*^rn!TO;|35&QE;)YmeAdBh#WCM<@Q%qfd}9`q;`n1{uBbYG#HRv6i8vg7 z@XqIJc%0g=1MT0X0$^R$ofTuy(vADWl?S@A&OQC@kC0DczpEAEuhC!sc*5UOjvoQL z@{V=dqy4Xo9Xyr|+!s0q+P_N%ctp#JihAoU)*yeUU;Ub~hp&2+6^9Jc{{$T>Nk9Kq z{{x$J{nxV>vD-Jfi07V)b0pW3w~*_>L0yvk-@!=mg~jH{`abIROG)Yzpa*2dE&WE zkeHkAAG-NWTAiHb_r2r?e*PfvM&a$xFZSAmzkQxPb~?<-+Oan%d++n+#{b7Z?sWPx z-r(+>CZR5`qtuM`BlobQT2rp_e=j4hMQv zW1d|9j%b4COPxT7Oi=?H;`I9#A(dj6i#2uA1iA8EH{rUs&9u0omNgmBaYpUJE;Kke z`1S>y^!$z(wQ)2tqWw~iV=X44*&X5wtL}QD{Xb94Gp*q7LhMT7;fCj3G6 zmX@vqzw#S2#QBW!eWa~E7+rH6AX%Zu@b$h*Z3aqNI6nEYKthr0O0>$-j!X6E6RrP! zW(Uny!63f5i?$jQo_nYIdS|+FCaB)hpNE%B_V>LPQW{$W&kZfH3V3`rmDCxC@muEM zO0IsUm-txZFppkz%Jgn;6|HXo&2{2`4zwz{P%j9){()Y(I>V0WaNi4IIVZ%-Us_a@ z`jN$`c7+S5{T=c;2te`<+n=pJA0=47v|T>BljV6$)ntsY)v&&FHkfA4pe=zZeXsJ6 z8e$y($oZfuro?F@3skM4;4N`7N^fw3u4^K2T1lH4!Jo%qvkJRg19A-|OVQ%PFlx_hs za`?yAW%HMV&n`P)M~S zdEMw+3>s%_#q-bC@1lEX(+`vfHBfil^)IHL@9OGG9VH&{SBr70t#CF+asBc?+um<& z)#;sBnCaJB6;g&c#dOjoLoJgstxjIPX5#Oa%-=CT5LYc%WuY$8yP;q0VvX>(FLP)f zdp%(+ZsmBhBcUlI9<3agTwB{1p@wSm*l`FBevSCwH}K6Dqwbic3G#JSOlu49*x00# zJG_frM@nMOye$i<5M>Bp$GPI2=Dh%3wdWaO0@8?lTErq1Zqq1b(6DPFpo^PW2=q2R zsm`xoSnH^QwNgRw>-5}f566yz-gR{eCA_7TRVV8FkspuN`Hus-;e{^G98%6VXYEsJj^c zVZ=#yGZGA-RQ7KemmPhK0u1X-^$!Efd5b9rHra_m7+>OU)~t8Q(bvW|{8ypqa`-I=Sb{(WdXtb=4mLCz=8FvGn4J(aBDA z34A;TiH$mOCU}Aw6WKdb?M~=Hemq`3Uck2y*pMAKic`ibV=` zTjF*F6VhJ0e7cCmt@dYZ^|a0FSibJ|o9Sn)fcf3?411pomeWqUX@TKNT^3OvkuWcR zP~e+hRt{PmGe2pm(QyJY+lt;6VDfiTK3Vg1(0Ve*D2|&26xUm2z)t%nK?}BCqn$sj z@ekKshmtg(qx!mSzUcH-DH^(r?(4{TqVbSI5D*Nu*ZnVIbbF+E~Bv_qa5JG|#pHzuVf{@JUb9s$?1?P%%I=cuC%(0?jfKnR{W9Y=^*4l=vt3~-eA z13(e2(hAkbL5B!1uorX?vK3+*?csPxdfbHTF3X4N{^tW%XWWt1V5IAuIWk%pkgj?n^3l}43Yty>fn+sCw zPJ=;&ta)I1=l_kHPIYh^6`n~Xk8|C%T=Aq+iV-K5tnKV@aBIkD1Amv2`fO)-XDcg{ zS1fw8kXGe|O*hkO;}kRnOv`Gk#xepm%p0*9FXA=6i{j)vA!yoLwe`;JHA0CRyO3!p z6c7LlOe!ESsEIb&g&kYt0ZO0>g!uX3&ErMV4rn#$gf5*B&;qVR5nRL4SvD~tS)DXG zYl8k5e9>Cqro@fDRY=gnNOk4K3JJB;TxO9ggLPp4SIHZQ#nEvFp>~F=eu`LmG5p4N z?{!Dc2(FB8uU87y2%r)qHy%gIDQ|rl$nQ$J_LGW~rRW3OTpt3=e`m#X{Y4R9jkLh* zk^TlJ;MRohT9Su@vusK=A4dM7hggg#0_NnD?CZ;#Ad@wEe&+eW{w`6Q*B4+P^_5d^ zUhcHb7Ap3_g)eev5vk%ll&h8IKgEbnC;?OOZZrv8uThLk@A|+5Z`=qNPj<-srF{Lk zuXkD?xCwK$(=A2ZlV$|2l&e;~)m9ytf>|6LBS4%iH!otpm;(3b>ww@_zj?Aaq78(b ziuyX5SIyL&E+9m-KCAZe>@t~}oo>SOwAHd zX^gE6CqK`GOmUD7b~NmnXapMOP<&k%;V%gU)rZ_O5e>fcNW946Iu?{Aed%F-i67Qs zD%bTA^f$Wrb-a@5GRuHpuC&{*ffP2W#OTgjC}@&ipw7S6!*}w^i6J_2j2N5 zble^FxWyoNBJ3~e1~~Eqw=O89k?X**p5Yf(n@u8m?p5NDLuO9kmE!gcZbd1rWpV3z z1HcOEwl?F?b0$pj%}_v2WD7zQ1L@3sWg@;*R;>)a2abGG^;W&NzfU6K0CWGfl@Vaum9el~ zT3)|TKuGEHl6p?9l`<8ho~7%Xnc>U^NP#PP!hBg{Y$b}jFB`AbbX|~#N&Up!;@2@m z9<3|**|Mm`i?AtOH0Tp5a`pYoNXiN27w)8f_t|Ci#VYNH=ZM7vHg(kM*ezH*yeL2^ zv+ANuz`z(kxgHiV(Yjl|o(q?(gJyTrrtQZzv(cyRM7!!4`iSY~@yih|y*bSK`)eyJ zz7{-QT^D|skB`YjA0MfMw_CccWdyn^rYcKmy&xc_u#32kgn1YB)x}#k<=MNH@#}=Q zCaN#pBNc?awqEO69^>z`0I&rAa6mm=7KuR-$r~#o!CBUj>ACQZY8zYc!G%CZJx0ht zsfw7X++{nE!})J)cpFz`P&hWMb!#zGg7Zm_yh@Pm5&cgp0;auks=(}dcT`MMR0t}3 z(Xf<8O`X5Mse|l)BozpM8hKvPw(jCGBD8#TVZu?=sQmfcAGYXrmI|O}-Uu&&YiY

Hi z_uz+Mv-SFBDsAWy>N-kB!_}gf^FXd4XW+fisV3hA(l?XI=ZphryJkCc2fcIuY~q99 z6#*`X3w5M(5}b!R5+*^X0M4PgwyMe>4)}XI75-R|5b$@^6Cf=x z+w8(|^}hY9<}VHQc%i)mGXaY>623m5g_Wk#(3wfB8~0lKfE?m>9ohI3vM2+YRX%-6 zX+pqqa06a=h)F*A!xesnZ`B_>{UI#$ed~G4_f9bHJ47-1V8PhW~`b+70)9b?WQQ}vtrCG=#|ySA0l zx?3Ec@gmlLExO;wfY~roP#QwtWQE@#C_9N?nNiCZcdC$DgEjC<;vZMXS1jgin2h0p z_%5>Tv2tQOk%mIeO_U;jEd;+2;6!%H>szKtj7u9R+nZ#LZz_VJNDC=$@#eu<+od%n zh|}IiAf%~ZVau@aZ_r~H^)2!hy80S48_u($aeUnsJx$1q-SR*3wv+=QwvP|(r{6qu zdf1+H$3*O=jKHETkR0ugdNR-ym8Gmia>oFUp`8{;tZ8fvjdG*w&-nw67`;J?4F01K0&k$AX*6tx6yFR^4fcH1-Q%o)n1KY%)8+v*ivra39LD z-G7*LDYl*WPrQv8EijS4NXa6vD+=1ke82=MjC1M~egPR56_s^5hJ1GzU16mtYr_*k zy~k6zgaTGoov(QhLO8kDzQ8O9Gm)A3ZGcolrNfA*3E{;QK8Q>~eGQqa0_IZ$`kQ+! z{_tUMN=*^`U!IVJGU7r|Ds8Rj&!#>!z-l2xM*FI^A}`3T53L(S;&;Sc2F7YM@HH@p54dQwJ-NXRhto1g30rP8g-(wh^eaa=q?Drm{P2}#dL_9NIM>@3LaNFh^FI|lObMm%+k zSFz<8LjO=#wSnf`wttoqvHbcA$`|V8Iqtd50Z+^lQ{F69X}PXCx}L^8!~JBt38LDv z&{B`(F_ajK!Z8GwL!$-Bgv$P^>_T^yn%61*a)5YN2}$Y{q{U;=33#eO+$dJ8Qve+bEeVEnIbUgqx6;c{C^VQ| z5>o;Iy_`Tm+siRBcCv)or3q$t(Y~y>9W3*s&W3bUEaKgpYD5le)$IGM{v5RwfHrg{ z2T!bq{H(2qPrr|nua9CKcf0MCCy^>sioH7a3LVyesB690{S3qZ371^bgd-xA_`z0lG||IeS-O>OnH0)z5HD&Q=Iw8f59-=^JFs=Y=B0eoO@4gx5_vG2T(d#?y)D@` zR0cpC<-ilOBat45{xxK%ge%LjZ7tmM^t<6hAI1tz)V;n~Owr4+#nozF`WU_6A5%{n z=p-9wTq<_gPp*mnKNNUtLZJ$)zp!UBW{$Tw<2A zLhU>;?eAae58t;;;%Nr{6D4PhGXh}O(8A*8IfmcVF{|Kkcme+~^N~dO?O&%3G>k8L z%0=m#$BO@p)ZBfEIEQ;%YjC0JaV)_T!R6zKg;`V`$A_6>a1ZMx=P zTuCVR`)ruXJH|_?I)~#f3!jtf29s|1Mi;F~&O&GEyWw3`!x5qCr2N~Bc8A()@PnE! z_1}VVI^V(Y2bLTPnpmDrVJ{cn!LGAjU%dK6uabV>so$?z8$NUlnse#UF~vf?f4=Br zT*$S4f#0+X+QNAB2#Q~9`#ByhF3H7<_>hnb%;a@TlycPihFkpVsX$zSYV>MlXY9@0 zfW5&Xa(udbu{XTk!k%w|vMFjg*i^Kapo04(@P5?J9h)p6wyc-;pDHICnm7GqTeV}~ zIXEuYN|z>UjSY<_aaXl0RAmp9(+x=H^1%!m&lA7d-9mfWFU!01{aPHoXx-SXAU(IK zFPnM-%_lG^uq2(?C6o3=11Dw-3Modq_&!-NqXPenP zOJaI$s3LRSLG-+1nPfND8Sbb}x&|ReSX=2fHzP?l|7{vOBk7c4BrBVOp(B>d3qp9I z<`EXJ5Ig^4J=D72&(Z6jhUo9j0&~toh~$l3@E%&5vKg%*qkJF1n8igOVJ93lfS7>3 z6$H8Ct(a+kHo(oIws!QiUh*NP5S7%krm+_Td(@=AGr|P(=pgUril` zzpsqeFWcG|KnPVe&`C7OYIwP~q;W5XiP|~PAao_Iut~*UtyMgv5iXjTe~-TV2tXWr zfs2uo@Fb!z6H<%8Ogvt9=Zcsj!F5^YZ2(gJCbJ!5Zk~DMUtQDtoz+3k@DDfsxYG?( zZYH(r&lph~bzP076UQcN+-%~ibvW)j)YZcb@APoU&qZy0vf*z*$T(~O+W31$M1tnc zbm!Y69SJYg6a#b;uXel-0EWuvos(`7ln4=@0L^=9UG+;P2m!u9`~cE?53G()cE_E- zVw@_^10dlGWtmScheQe0LHK7aBJ1<)r-uJ63RLoY9J5%8^l6;X8hT}u{NhFJYipgq z6iE9uZ65{H+I_ye;uC%PJ@tgj_oGESs2SR^raKoJhVqPNxZet|L;&G;OL(u5&fl*s zXsX!(e9ESn{%;)#v3;xv5!d=a+)FuwiToMhIKHL#7I27GN5Y;j?pp&1SOu(Pm0GRj zF+8(KhFH1NkRLz(z(^ig9#`Z?y$(6!gZZm)|JAXfvgz*fi9^d@4P)v(NivU4{BD;q zWXu+)6t7=+Lq!uZ`aX`!zTJT!rG~#s&<;4%Whj$+g4m_NuqTnLO30tJg-kW+Y*=sF zSnLzv&HhB_2UjKu=`dE<4KFzaRo@=~z%B!XWd=pOGQiN2^W^-cTP??g?!&m5+D!_97!JVtyRjnpQOej9o^@pfv0DG+>Jt4(kMu4|akA6xZ z=L8XZ_0WTQ+L^(>`I04--AAE$}cO%zVWPi#uN-)uOmTS^@J`NQI-Q;kK`SJ~AnXHJMOy5@eOv$4x; z;ucj-wje~76}am=?SmqicRVpBwh#5&`z}9W3=-7EicTezrT~5}wc6fx_v_}SvmDOC zO5r3JS!9^*fa|UDbF~D3s<@pzyd%M|DDSyQ1jymKjuOgf6=Ge60jM}ia+<+UFA+(h zEBt~wr!reI#(+Q=Vzh40DMT(3>gCbriK4F1r~|4;=6Zf;(c{!)(y16gB1SpV+O98iTIdUBAO z6G+9a8ZV~F&IE%4j^C@5z#2z0y8_ZA3+$Js840!}F(2jrjgNnGsJKc*{T0$gYWs5k zQsMok=fi?IuQugb7p9-g1e_5F8j%}9eSD=E^9&(;u&b4o7SKO+u93^&_!7$V+PiG~ z`$t>2nbk|{cCE)x&u~XSKhC)8cGxz%+}SX`w#O5cU~(OeNV5G;ff!oufjze{Sehn; zpo;EsuA6~a=tQdrYGE1JIa&vYXL?~ryUjMx-R$bmlavj82$9^d8QMQEQ<(d~EW2ww zSQWNcS1=E7{ug5PP@sQ3c=};f(=ySbky!f~R{}!9L8OLCX>up(c$2|}fX2)sr4opC zhDvWmO_Ptd4#(peQsjo9lDa*<-u)QcL9(CF$1+2Ws z83?9b^w9CBSstCfp7i7-L_^)3>kK`<*+JE@lx^eSu;$dy7{3)X@EC=t32CNP58IEU zyk*93`apWmsJo_3D0THKL*;qLnhpkLTYCSt4BF=Oh99d&|udxLSpq zT*Dfu+$at$n0;c|oCm3po0&vZg_vlc+(P&5j3fYhuTksM*6WnI03OH4v(D_)|A9n+#zFwb z#0FeS?G27Yk+b#+FegR9K!lQ*veyc!2o;Q#N<1hD0!(m0Owa2J!&nC$#F$pi>_ID= zYbo&jz*fF70zAaB^*bth%+cuVpyl7Rk^*)K;vt6S9ajGs^0`7R5x$p#gGJG3PI6Ht61z*C%lu3JcaAaOjDl^oL zXrg35I|j5qhdpeNl$8!T{L?$s+}Xo{?5aa&fo??vs`Ji}=FYCtj_2#Lr+GlI1Hm_Au0@wk$AhArNzh?kj_yBxNIuJh zm!Pp9z{tco_M;LKLCc<8$zD!CUUiZRB+(Qz(Azdf@CM}>Lf!o)Fhh1C1DY9PuEiFO z5FylJ7BA5QhTgB&pr*j#n8zh7s)PIRh;Pj!LUzUO9{70!Zpc8b5&U?G7?0|6_V$XR z6=Gpf^8zc&qzrF;yN0qPXG?}6P?UUE@}w0}9owzi?C{yB(BD1=aJcSMmx;I1kqfGNy<%za5ID34Ci7NWXUz|xIZ-~r( zaKKsQEtc(EDwR*B!ClqEu_C`f7T7u<@&w$^z108l=-5O-GLJm6!Iv&JjO z{BUfvfv{^e6+~IaU*?VVr%RORHm8__VSNT`8uQate=~`~z|G#pb(hwqGau4R> zFCHTQ2?byrJp>^6ze@xJp<8nQZ+@Io1+1%IUspt(Fr1w7f4aM}sHT!`ZQuUesX;{$ zH4$+H2SgAO1_`txAVNSygvg+X$QUrjFa!d2s~~eLh>VGX24o0f3`7Pc4AQ6p0to_v zHW30uh%rEbFx(SplwSAozT9=s1FV%(=bT-8SMAzW_0_jK4CsyM>>&I|s_J9&;0xvE z+1l`v#CPHIZ2=I;5epF*SVS1RlrHmc@J|XkJ64l^EM$mN;!$)<$flB|JVP^eFOGWU z^o*}Rdi1DjN=eY;qv268O}MJ*ilgEPApP1{0aWSVNGT2TMm#5gsJ;C$EMzzsv_&V? zWx=A0XhvrCw?4KhdG6OsYj>Q-IwO+Bx&QOo;Ny#X#dA*bhSu=*UboFX`1wL_#(v;L zg%+l_%f#P0xNYLy&w225Ddp(3bw>?7qPuu9X|n;jHcKIPxKpcQa2Jpb~^PDbMx ze;Y*d_1Sz~N6G|=H1`yzv}zX=FkVUo01u7TG_lRN-C^FcFy9+bir!qZAI`918-`v? z>=q1fSN?$fVemE}z*7VLXkh??>BmaOa*h5Mx6N%&yf_r2Tm(S#Ru4dHCoHSXq;0fM zYmqUo_XEu5nWLV1t+47%3g<-)e!i!|RX< zdnzs^!9qTx$4|E;8PKg?8ZNQ*NZrLQ>7EtHHdn_OxnJv)@8ltS!aS#UR_&tluPfeU zlXRus2iy1x8}e5dWh!dO^PFziV|HwAQSX;d|4YX7T8T`xBjNt2+X}q!^<`$y$+V8@ zH_44TamNfTfU1#P(ihPz&-V?3`R%R8HQa@%LFux;2Kw4ZiV||4*6p3xUU`|g2@u13Yv=M53Xvo(+_ z0P@QZ18lSs5eS6dDa;6c5mg!1l;UyGWJLvQh`@Go`p1sy8kN*$)oR$ zJP-k)c1wxqrDd<`1N^BweVFeoN_s@u+8*{MQ0I|R0AQf)S__0J2*fh~ptNYprqbpw zI?TQuDz=wsLq;~+KHes&8|`G#yJl4E#jh@{`gZp4Hftva-`F)KrU2+=CeTb3TME-@ z2h#?lj-86KlcxJ#y6%hsoQ)EV)GR@3M|GWAt2404mP0LIj=aUzK0C7F^gkj1nJ|;fr{pS}PR>*xes70Jp1)~#{YJOitJ3@d zJ6C7tzTpp_w&(BrUOLg;Lh_~D*rV#u*o@TOT{Hn2|L4(w-$#QgYf0lZ9K@>XwE)1Q z8%#xk?zP>IN~WLWy?tlH=FC!y{8?42Y^4IgQ4(Vhe*lhp7`JS2oI}}NA9C;^0Pqim zCPm(eQ{JktyT*R>w?G@Gq&Uc?FP>Q{82;_SEEV1MuQvi2p9c(Xy)EE8ayedu0g6pv zYaG)%oVK!F0VKlqKWwE!Q)uLKpWiEPTn9|d0Bk_2ZStVH+xvAKtfON!X_XD+ntM7z z`=@3%Y#=1PMb4g}_?gwcwd`^AT`182B7Pc=Qs{fK;)fx~hRhxY(o5Ya>CfTjpWI3Y za--iBWWC*8L^Z|K_2GT30pGK76(p3;9PjDq+i~y!2^a!i963=Ms~L#F#L1u10gfM^ zUe#c6mIwn?d|*R2rxlTW%--b2C4djio>jEVCOpt;mFk56)K<$8Q2oB0uF>JBe2?H- z1^>uV5bFvrNqV$dnhs^@x+C1-b_pO^{wyyS`ACjwSaalrsNfiWH=zA|-u1DT)$dGl z58Lb(!>>Pcul^W-TfRPo0F2-io@px`r+!o?HV06$a(g~a@yQgn#ujLQ)|g`!lCiJ5 zZd(oFIl|Si>@JypCG7x!+X0TmSrtUTGw8Cz#M5cXQ{}75X{I2XcRFV9<+lK751Ojq z6=p`Z@M@HrJ|73L92?z4bSkEKa+%^yTC3Q%fP!@@Dol%Pu9$x?l?f7`b`>ye9-cfq3l@sHxp~eI#%BKcPqS(ig@{gC%* zX4awc8-H;w6M`JzhDlRTlkH80tlE!`6VdDGEsYozh~!6iy?WFlhT~z0sD4hKIm(o8 z`W)_s6bfuQTp_XV2gW)6PE~c8Ip;Ih-g)o6xuRS&-%>Wi4G8b5_Ox!gVgw7D8U z_VNo533qomaZkg}F~K^Ox+jEfSHk9ZnX&cQ!&&2?SP*M?HxPk*9j#b-><;4Q6hJ^s zb{E#(G~99xot4(PfLljV>T_W)G1E%Rkw0$QZBWSuh8gaa3Gbo-(zL9@?*XaivWpw_h-&@ke6Lpe)MCfju(q09zwhH= z%0X!)o9uog_HaSDUNP#K56Pz}uibA@?E#8PLuAoi5=TT5!W|<4p6U`l(8BQaZFj%Hr`;S z`a(k^&RGVCvA7O`QZ=j<4HvuBaYXOn%Ki{0%=%@&G`ol~ z;mIj;fH-bPGnU}KH?`wCbe~T&rIH`En5FBCd$bfBv`u!(jZ7d3E_o$gBMq zAxbtpp=rbw0`QwOTQ~vp!9P$1Vua$O!?+b=lhuV9CqCW0;;l_kWmpko_l=Kf3F01M zt?E78E`j%{YN~Yrr>&!@2M)9-#CmU~m#jbr)~NjE-yw<6f>0Y}Te#4_kr4=BAAyDH z2Osnp>Djc^kj~u{qcHG_incSRU^m`_tqbjxeYpLK5$L7ny90RlsKmntA0GCH@=;P129`zx!q%vtw# z{8YYaYeig)>pa(IE6E2;i_aHH=JA5(0iPJ^~si2-3~ z$uIbUFa%fJL`{xpy*Qt*1Y~v&O+Hw=jyu=C9@z==?;(&VhVq|+@40j6c-EAbZf?e+ z0f{F#d$zYgoEi_mM))+@Def=8SuLvydZybUYi)cA$aoHGl5Xnw(#MmrKyx=t1J!hP zZ-v%tn>Yz7eDD1(&QB>sjr8zMT-lK!c(M;nnatUno1ox|cE|b0`A#N;vbtaDX;d zGk>Kp#DzuHU$KpR5GB48mj-v*eyQm>Q$qRt+J=IK&i z`MeDCar);0xzx{EW5;@U%K_>At++ZBE@|08V@49M559o)(pzAWxP8D#OJhxCol-oS86T$KtcQnNXY`Ys~-$?j$ ziU7Gn2@Xp>xWF#!*SC`3SvdUK`y~6IcsSt~C7nLf%iX9dXjGQQTAZSL2fa*<^Sui9 zHf(|S0(?>u`V_f3BcxTk)s>(HS1S&w2nB94u^8qs?Qh9j(~z@U5rPE3pqd% zkGLY7n&INK!NudS&6f>_(A$h(FXXxiMI+(72A-}p zy+k`xME1m*<^ynrWT)vGW|x;_XKd?ziHl(1p_c(8W>#-`P*0&&elm#z z!#n?$gT|M6b;p1&D(LrVetg_mnV79 z_K8?45xyDICEyYHg}C8L*Wml3_Bmr78tOc^t>I=wq2=*amX#%Y{|!>{&GoXaB|R&A z*?vSpr(YZLK=uM_Jrt-;{n<&+txVK;y{IDNbe}Cy-A%o9xy46(En^dp3n$NiM1dVu|%oVa|}P%yA>uVou(NQ+li^-dcXmrW^!tEa~+M;D=- zvu{oByi_)0d2etVLwm#n!9n|h;bPQfu~W-KvSoUR`~cd7QM&aBGEa0!GSM@ql-i_K zlS`0a@)LIe%FNn$#@CL{HG^IR*Wj8$eapT*ohn|oIV8CP__saPDtF_Rpc=A1!?8s) zGG9^WK*h)}2kwq4)D`!20!%@n$Vk}9PY^p9zI*9z*VA4w-Z0>Y;lrWpYUB(@dAWs9 z`t?vl8s0g|C=r`HYUwk8U7($S!**W#1jW!jgK89z_*Q9WGUQcAHO|L3OO$wlPqW5r zEqdu!ok;L~ry}P6E%VD5a8!@>7W3}29i6$Ojk4F^vX0ND3kOkPSB}Vs@m3W@vq>cP zk#WteXD>f}h$(tgDHjc<^z;jWo}mjDDV}I zDVbTTC)u_sox1X6DR-dW^nrM(|2RB$gVAYNo&qOGLwCp=&81eFj5hR zr!|PP#?ablL+97RHdEEl9f}z2HO;gkm>mB{MlSucZu^Zoq*OYg9;L<%PQ=h@E_$=I z$AH{gfeao*opn)AA&CJ;(B$r zr(fEiB1PET*%*hlLd~G|E2t}98gOl@s-QjkFYl{hu}cVJhvMPd?MOoQDGr}?yZM&I4~zGrnJLsPxl0zFUm#HF_)!rz_lba za_f~kB08R6a-Wn5V)2KJJw;#QCBFB2wP6BAPhh$TL>v!x5QJgE0JOeE=m##dJ)H^L z%u4tsIoG_If}E{x8cZ8y2ZR^9(wW-$5w9<{%vV>OX3loYC7{mTMn%^VR{;q;{KCR@kVL9qTpp5P(#vEHYmTy3G4xrf0ig zo6jZRbMLfu?Y`H6#Isk~u$e8;K$7giqHj|1_%+FZwzvzm(egHMf_;~k8eqUmC&t1x z8op5KT&KP}S6xJVK>gq6``R2?J5;#Z;|<1t#`U(HD3mra*6oFUK(Y(0JqkM43O$CO zqKc48V@G#&oYhqGI`6ThJUd{%()C$(#1sH20*bYX!1)q#>g^V4Ce)*UNbOFy*Ry1uI zd$xr>`1C)&`SwLrRPIG)+@qI)-c*w_YLx+4hWO))`Vn=r`CiP0dQsKIe=(ww+Pp-l@xx%jTtwdpMYCMm4c22$MGN zzDb)~!sWW|vhm=#lcn&}dJ8qrJ!h-)@UB3cYaUFS+76!bg>zF~p3|4l_`mLKlqK`HOq+E91(AVa)xtLzt+l zQ}vW~RhZg^IT;Erer*&Xl1euTU0erqqeh~>!EHSH=+`UZw$8*VL1pvr{l5OM7FC33 zoeiaPY8Fm)lP)wbHX6o23GSZ?FS?z{H~e{(#e3=D=^j2H`b5@BLnNyEdG4I5kn9mU zlD*5Kz(dVQ&qV6=KR{P6^1kZ!&Em z6_mU894{X?tPg}u-szQZY{YQPhJmZhh1;2!0-{bjH^NGZq4QwGC#a^}YQyvCey;(c zUcCPE)T!-$M&2FB9hqj8ixu?tJ;G9ixT*~z(|YK(QGLmgY*7~^ruE2Z1J5?_i~c7l zoG#H87fPH0@z{RufE$){+qcV%ZqI-*itBOZnsjGOduVlj^LpI)~+|@B3bLD_M~1UB&^BM(1zx(CCJ^aPN!_EFFjgqs47~fTcgsH9V^611t*`?F{%=TFr(g+$5m&h< z{#0?EhXd(lKj6&f8+!~L6T^t^`QiU~45Iq(nG!{-sbjPlq>b$JG1yItqAWN#|-Xup5=A&8%`qY^|h z-&Jqi4#=Tuu)F?xPux|&(+xy`Dw8^+6P^(wAxCDAUB4+_K|{`YDPw4PGFct~Ii}=+ zI0r++%Xc|qHd0tH8)t(tW25F~8_(47_qz1#4_bXJZO{)|Lgl_pD5A z7={BlAir024xyR7DGgdmz25&gAfRq!E7Z6BRwdt8h1IktBi1J#2DyA+dR`MBF}pD` zsYAT13E=NdAiF6~p`&_<RGucN>apl3#L9Db zjvd5X)*=r*tap;;7hk%rwlXxFF)fcy39YstxUll;>-zHog@bl3g40otbc3^84qaYQ nRX>}xa{K@F|L*I(74wTt2H^R%KlLwBSD2evogtmR`p16*FgB1o literal 0 HcmV?d00001 From 51d34951cd6fce9205716a5ad3225373370ef2b0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Apr 2023 14:21:58 +0200 Subject: [PATCH 077/878] Add some changes to the changelog --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfe997cc..10b9bbbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- -- -- +- Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) +- Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) +- Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) - - - @@ -38,8 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) -- -- +- Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) +- Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) - - - From 60a8dff3c564f85918ae9c8dace01dd1912cf272 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Apr 2023 17:44:35 +0200 Subject: [PATCH 078/878] Use OpenIddict Client to login through Metabase OpenId Connect --- .env.production.sample | 3 + .env.sample | 3 + Makefile | 28 +- backend/create-certificates.csx | 44 + backend/src/AppSettings.cs | 12 + .../src/Configuration/AuthConfiguration.cs | 168 ++- .../Controllers/AuthenticationControllers.cs | 200 ++- backend/src/Database.csproj | 33 +- .../20230420143838_OpenIddict.Designer.cs | 1305 +++++++++++++++++ .../Migrations/20230420143838_OpenIddict.cs | 180 +++ .../ApplicationDbContextModelSnapshot.cs | 236 ++- backend/src/Startup.cs | 5 +- backend/test/Database.Tests.csproj | 12 +- docker-compose.production.yml | 2 + docker-compose.yml | 2 + .../development/default.conf.template | 5 + .../production/default.conf.template | 5 + 17 files changed, 2181 insertions(+), 62 deletions(-) create mode 100644 backend/create-certificates.csx create mode 100644 backend/src/Migrations/20230420143838_OpenIddict.Designer.cs create mode 100644 backend/src/Migrations/20230420143838_OpenIddict.cs diff --git a/.env.production.sample b/.env.production.sample index 3cd2f4e4..5cd1498b 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -9,6 +9,9 @@ ACCESS_TOKEN= OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= +JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= +JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= + SMTP_HOST=mail-mtaKA26.fraunhofer.de SMTP_PORT=25 RELAY_SMTP_HOST= diff --git a/.env.sample b/.env.sample index ca58ec11..ce7166c2 100644 --- a/.env.sample +++ b/.env.sample @@ -20,5 +20,8 @@ SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password SSL_CERTIFICATE_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy +JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password +JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password + SMTP_HOST=email SMTP_PORT=25 diff --git a/Makefile b/Makefile index d53a49f1..039550a8 100644 --- a/Makefile +++ b/Makefile @@ -148,7 +148,7 @@ shelln : up ## Enter shell in an existing `nginx` container (after starting all ${docker_compose} exec \ nginx \ bash -.PHONY : shelld +.PHONY : shelln psql : ## Enter PostgreSQL interactive terminal in the running `database` container ${docker_compose} exec \ @@ -199,6 +199,30 @@ prepare-release : ## Prepare release # Generate Certificates # # --------------------- # +# TODO Pass passwords in a more secure way! +jwt-certificates : ## Create JWT encryption and signing certificates if necessary + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + --tag ${NAME}_bootstrap \ + --file ./backend/Dockerfile-bootstrap \ + ./backend + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -cx " \ + dotnet-script \ + create-certificates.csx \ + -- \ + ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ + ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ + " +.PHONY : jwt-certificates + # For an introduction to how HTTPS works see https://howhttps.works ssl : ## Generate and trust certificate authority, and generate SSL certificates make generate-certificate-authority @@ -213,6 +237,7 @@ ssl : ## Generate and trust certificate authority, and generate SSL certificates generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate mkdir --parents ./ssl/ docker run \ + --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ nginx:1.23 \ @@ -320,6 +345,7 @@ trust-certificate-authority : ## Trust the authority's SSL certificate generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate signed by our certificate authority mkdir --parents ./ssl/ docker run \ + --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ nginx:1.23 \ diff --git a/backend/create-certificates.csx b/backend/create-certificates.csx new file mode 100644 index 00000000..dee68b2e --- /dev/null +++ b/backend/create-certificates.csx @@ -0,0 +1,44 @@ +using System.Linq; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +// TODO Is there a better way to manage these keys? Is there gonna be a problem in two-years time when the keys become invalid? +// Inspired by https://github.com/openiddict/openiddict-core/blob/78901e3e7e3ee47cf7846a71f758dc9ca110b1a2/src/OpenIddict.Server/OpenIddictServerBuilder.cs#L661-L679 +// and https://github.com/openiddict/openiddict-core/blob/78901e3e7e3ee47cf7846a71f758dc9ca110b1a2/src/OpenIddict.Server/OpenIddictServerBuilder.cs#L661-L679 +foreach (var (fileName, name, flags, password) in new[] { + ("jwt-encryption-certificate.pfx", "Encryption", X509KeyUsageFlags.KeyEncipherment, Args[0]), + ("jwt-signing-certificate.pfx", "Signing", X509KeyUsageFlags.DigitalSignature, Args[1]) +}) +{ + var path = Path.Join("src", fileName); + var certificate = + File.Exists(path) + ? new X509Certificate2(path, password) + : null; + if (certificate is null || certificate.NotAfter <= DateTime.Now) + { + var subject = new X500DistinguishedName($"CN=Database OpenId Connect Server {name} Certificate"); + // certificates.LastOrDefault(certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now); + // TODO Is RSA sufficiently secure? Or should we use ECDSA? + using (var algorithm = RSA.Create(keySizeInBits: 2048)) + { + var request = new CertificateRequest( + subject, + algorithm, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1 + ); + request.CertificateExtensions.Add( + new X509KeyUsageExtension(flags, critical: true) + ); + certificate = request.CreateSelfSigned( + notBefore: DateTimeOffset.UtcNow, + notAfter: DateTimeOffset.UtcNow.AddYears(2) + ); + File.WriteAllBytes( + path, + certificate.Export(X509ContentType.Pfx, password) + ); + } + } +} \ No newline at end of file diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 7c680415..0fed9093 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -25,6 +25,18 @@ public sealed class LoggingSettings = false; } + public JsonWebTokenSettings JsonWebToken { get; set; } + = new JsonWebTokenSettings(); + + public sealed class JsonWebTokenSettings + { + public string EncryptionCertificatePassword { get; set; } + = ""; + + public string SigningCertificatePassword { get; set; } + = ""; + } + public EmailSettings Email { get; set; } = new EmailSettings(); diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 86df2e05..cf159617 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,20 +1,59 @@ using System; -using System.IdentityModel.Tokens.Jwt; -using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; -using Microsoft.IdentityModel.Protocols.OpenIdConnect; -using System.Collections.Generic; +using Microsoft.AspNetCore.Hosting; +using OpenIddict.Client; +using Microsoft.Extensions.Hosting; +using Quartz; +using Database.Data; +using System.Security.Cryptography.X509Certificates; +using static OpenIddict.Abstractions.OpenIddictConstants; +using System.Reflection; +using System.IO; namespace Database.Configuration { + // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Startup.cs + // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs public abstract class AuthConfiguration { public static void ConfigureServices( IServiceCollection services, + IWebHostEnvironment environment, AppSettings appSettings ) + { + var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); + var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); + ConfigureAuthenticiationAndAuthorizationServices(services); + ConfigureTaskScheduling(services, environment); + ConfigureOpenIddictServices(services, appSettings, encryptionCertificate, signingCertificate); + } + + private static X509Certificate2 LoadCertificate( + string fileName, + string password + ) + { + if (string.IsNullOrEmpty(password)) + { + throw new Exception($"Empty password for certificate {fileName}."); + } + var stream = + Assembly.GetExecutingAssembly().GetManifestResourceStream($"Database.{fileName}") + ?? throw new Exception($"Missing certificate {fileName}."); + using var buffer = new MemoryStream(); + stream.CopyTo(buffer); + return new X509Certificate2( + buffer.ToArray(), + password, + X509KeyStorageFlags.EphemeralKeySet + ); + } + + private static void ConfigureAuthenticiationAndAuthorizationServices( + IServiceCollection services + ) { // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs services @@ -25,43 +64,110 @@ AppSettings appSettings .AddCookie(_ => { _.AccessDeniedPath = "/unauthorized"; - _.LoginPath = "/users/login"; - _.LogoutPath = "/users/logout"; + _.LoginPath = "/connect/login"; + _.LogoutPath = "/connect/logout"; _.ReturnUrlParameter = "returnTo"; _.ExpireTimeSpan = TimeSpan.FromMinutes(50); _.SlidingExpiration = false; + }); + } + + private static void ConfigureTaskScheduling( + IServiceCollection services, + IWebHostEnvironment environment + ) + { + // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks + // (like pruning orphaned authorizations/tokens from the database) at regular intervals. + // For configuring Quartz see + // https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/hosted-services-integration.html + services.AddQuartz(_ => + { + _.SchedulerId = "database"; + _.SchedulerName = "Database"; + _.UseMicrosoftDependencyInjectionJobFactory(); + _.UseSimpleTypeLoader(); + _.UseInMemoryStore(); + _.UseDefaultThreadPool(_ => + _.MaxConcurrency = 10 + ); + if (environment.IsEnvironment("test")) + { + // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 + _.SchedulerName = Guid.NewGuid().ToString(); + } + }); + // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. + services.AddQuartzHostedService(_ => + _.WaitForJobsToComplete = true + ); + } + + private static void ConfigureOpenIddictServices( + IServiceCollection services, + AppSettings appSettings, + X509Certificate2 encryptionCertificate, + X509Certificate2 signingCertificate + ) + { + services.AddOpenIddict() + // Register the OpenIddict core components. + .AddCore(_ => + { + // Configure OpenIddict to use the Entity Framework Core stores and models. + // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. + _.UseEntityFrameworkCore() + .UseDbContext(); + // Enable Quartz.NET integration. + _.UseQuartz(); }) - .AddOpenIdConnect(_ => + .AddClient(_ => { - // Note: these settings must match the application details - // inserted in the database at the server level. - _.ClientId = "testlab-solar-facades"; - _.ClientSecret = appSettings.OpenIdConnectClientSecret; + _.AllowAuthorizationCodeFlow(); - _.RequireHttpsMetadata = false; - _.GetClaimsFromUserInfoEndpoint = true; - _.SaveTokens = true; + // Register the signing and encryption credentials. + // See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 + _.AddEncryptionCertificate(encryptionCertificate) + .AddSigningCertificate(signingCertificate); - // Use the authorization code flow. - _.ResponseType = OpenIdConnectResponseType.Code; - _.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet; + // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. + var aspNetCoreBuilder = + _.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough() + .EnablePostLogoutRedirectionEndpointPassthrough(); + // TODO aspNetCoreBuilder.DisableTransportSecurityRequirement(); - // Note: setting the Authority allows the OIDC client middleware to automatically - // retrieve the identity provider's configuration and spare you from setting - // the different endpoints URIs or the token validation parameters explicitly. - _.Authority = $"https://{appSettings.MetabaseHost}"; + // Register the System.Net.Http integration and use the identity of the current + // assembly as a more specific user agent, which can be useful when dealing with + // providers that use the user agent as a way to throttle requests (e.g Reddit). + _.UseSystemNetHttp() + .SetProductInformation(typeof(Startup).Assembly); - _.Scope.Add("email"); - _.Scope.Add("roles"); + // Add a client registration matching the client application definition in the server project. + _.AddRegistration( + new OpenIddictClientRegistration + { + Issuer = new Uri(appSettings.MetabaseHost, UriKind.Absolute), - _.SecurityTokenValidator = new JwtSecurityTokenHandler - { - // Disable the built-in JWT claims mapping feature. - InboundClaimTypeMap = new Dictionary() - }; + // Note: these settings must match the application details + // inserted in the database at the server level. + ClientId = "testlab-solar-facades", + ClientSecret = appSettings.OpenIdConnectClientSecret, + + Scopes = { + Scopes.Email, + Scopes.Roles + }, - _.TokenValidationParameters.NameClaimType = "name"; - _.TokenValidationParameters.RoleClaimType = "role"; + // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint + // URI per provider, unless all the registered providers support returning a special "iss" + // parameter containing their URL as part of authorization responses. For more information, + // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + RedirectUri = new Uri("connect/callback/login/local", UriKind.Relative), + PostLogoutRedirectUri = new Uri("connect/callback/logout/local", UriKind.Relative) + } + ); }); } } diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index b559e793..f0443158 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -1,24 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.Mvc; +using OpenIddict.Client.AspNetCore; +using static OpenIddict.Abstractions.OpenIddictConstants; namespace Database.Controllers { + // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs + // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs public class AuthenticationController : Controller { - [HttpGet("~/users/login")] - public ActionResult LogIn() + private readonly string _issuer; + + public AuthenticationController(AppSettings appSettings) + { + _issuer = appSettings.MetabaseHost; + } + + [HttpGet("~/connect/login")] + public ActionResult LogIn(string? returnUrl) + { + // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. + return Challenge( + new AuthenticationProperties( + new Dictionary + { + // Note: when only one client is registered in the client options, + // setting the issuer property is not required and can be omitted. + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer + } + ) + { + // Only allow local return URLs to prevent open redirect attacks. + RedirectUri = SanitizeReturnUrl(returnUrl) + }, + OpenIddictClientAspNetCoreDefaults.AuthenticationScheme + ); + } + + [HttpPost("~/connect/logout"), ValidateAntiForgeryToken] + public async Task LogOut(string? returnUrl) + { + // Retrieve the identity stored in the local authentication cookie. If it's not available, + // this indicate that the user is already logged out locally (or has not logged in yet). + var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + if (result is not { Succeeded: true }) + { + // Only allow local return URLs to prevent open redirect attacks. + return Redirect(SanitizeReturnUrl(returnUrl)); + } + // Remove the local authentication cookie before triggering a redirection to the remote server. + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. + return SignOut( + new AuthenticationProperties( + new Dictionary + { + // Note: when only one client is registered in the client options, + // setting the issuer property is not required and can be omitted. + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer, + // While not required, the specification encourages sending an id_token_hint + // parameter containing an identity token returned by the server for this user. + [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = + result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken) + } + ) + { + // Only allow local return URLs to prevent open redirect attacks. + RedirectUri = SanitizeReturnUrl(returnUrl) + }, + OpenIddictClientAspNetCoreDefaults.AuthenticationScheme + ); + } + + // Note: this controller uses the same callback action for all providers + // but for users who prefer using a different action per provider, + // the following action can be split into separate actions. + [HttpGet("~/connect/callback/login/{provider}"), HttpPost("~/connect/callback/login/{provider}"), IgnoreAntiforgeryToken] + public async Task LogInCallback() + { + // Retrieve the authorization data validated by OpenIddict as part of the callback handling. + var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); + // Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons: + // + // * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable + // for applications that don't need a long-term access to the user's resources or don't want to store + // access/refresh tokens in a database or in an authentication cookie (which has security implications). + // It is also suitable for applications that don't need to authenticate users but only need to perform + // action(s) on their behalf by making API calls using the access token returned by the remote server. + // + // * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an + // authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET + // Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens. + // + // Note: in this case, it's recommended to use column encryption to protect the tokens in the database. + // + // * Storing the external claims/tokens in an authentication cookie, which doesn't require having + // a user database but may be affected by the cookie size limits enforced by most browser vendors + // (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies). + // + // Note: this is the approach used here, but the external claims are first filtered to only persist + // a few claims like the user identifier. The same approach is used to store the access/refresh tokens. + // + // Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint, + // result.Principal.Identity will represent an unauthenticated identity and won't contain any claim. + // + // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the + // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but + // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls. + if (result.Principal?.Identity is not ClaimsIdentity { IsAuthenticated: true }) + { + throw new InvalidOperationException("The external authorization data cannot be used for authentication."); + } + // Build an identity based on the external claims and that will be used to create the authentication cookie. + // + // By default, all claims extracted during the authorization dance are available. The claims collection stored + // in the cookie can be filtered out or mapped to different names depending on the claim name or its issuer. + var claims = new List( + result.Principal.Claims + .Select(claim => claim switch + { + // Map the standard "sub" and custom "id" claims to ClaimTypes.NameIdentifier, which is + // the default claim type used by .NET and is required by the antiforgery components. + { Type: Claims.Subject } + => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer), + // Map the standard "name" claim to ClaimTypes.Name. + { Type: Claims.Name } + => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer), + _ => claim + }) + .Where(claim => claim switch + { + // Preserve the basic claims that are necessary for the application to work correctly. + { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true, + // Don't preserve the other claims. + _ => false + })); + var identity = new ClaimsIdentity( + claims, + authenticationType: CookieAuthenticationDefaults.AuthenticationScheme, + nameType: ClaimTypes.Name, + roleType: ClaimTypes.Role + ); + AuthenticationProperties properties; + if (result.Properties is null) + { + properties = new AuthenticationProperties(); + } + else + { + // Build the authentication properties based on the properties that were added when the challenge was triggered. + properties = new AuthenticationProperties(result.Properties.Items); + // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. + // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. + properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch + { + // Preserve the access, identity and refresh tokens returned in the token response, if available. + { + Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or + OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken + } => true, + // Ignore the other tokens. + _ => false + })); + } + // Ask the cookie authentication handler to return a new cookie and redirect + // the user agent to the return URL stored in the authentication properties. + return SignIn( + new ClaimsPrincipal(identity), + properties, + CookieAuthenticationDefaults.AuthenticationScheme + ); + } + + // Note: this controller uses the same callback action for all providers + // but for users who prefer using a different action per provider, + // the following action can be split into separate actions. + [HttpGet("~/connect/callback/logout/{provider}"), HttpPost("~/connect/callback/logout/{provider}"), IgnoreAntiforgeryToken] + public async Task LogOutCallback() { - return Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectDefaults.AuthenticationScheme); + // Retrieve the data stored by OpenIddict in the state token created when the logout was triggered. + var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); + // In this sample, the local authentication cookie is always removed before the user agent is redirected + // to the authorization server. Applications that prefer delaying the removal of the local cookie can + // remove the corresponding code from the logout action and remove the authentication cookie in this action. + return Redirect( + SanitizeReturnUrl(result.Properties?.RedirectUri) + ); } - [HttpGet("~/users/logout"), HttpPost("~/users/logout")] - public ActionResult LogOut() + private string SanitizeReturnUrl(string? returnUrl) { - // is redirected from the identity provider after a successful authorization flow and - // to redirect the user agent to the identity provider to sign out. - return SignOut(CookieAuthenticationDefaults.AuthenticationScheme, OpenIdConnectDefaults.AuthenticationScheme); + return + returnUrl is not null && Url.IsLocalUrl(returnUrl) + ? returnUrl + : "/"; } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 5373178e..2dd7453a 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -19,34 +19,43 @@ + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs b/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs new file mode 100644 index 00000000..9e0bbdcc --- /dev/null +++ b/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs @@ -0,0 +1,1305 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230420143838_OpenIddict")] + partial class OpenIddict + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs new file mode 100644 index 00000000..caa7451b --- /dev/null +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -0,0 +1,180 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class OpenIddict : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "OpenIddictApplications", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ClientId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + ClientSecret = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + ConsentType = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + DisplayName = table.Column(type: "text", nullable: true), + DisplayNames = table.Column(type: "text", nullable: true), + Permissions = table.Column(type: "text", nullable: true), + PostLogoutRedirectUris = table.Column(type: "text", nullable: true), + Properties = table.Column(type: "text", nullable: true), + RedirectUris = table.Column(type: "text", nullable: true), + Requirements = table.Column(type: "text", nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictScopes", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Description = table.Column(type: "text", nullable: true), + Descriptions = table.Column(type: "text", nullable: true), + DisplayName = table.Column(type: "text", nullable: true), + DisplayNames = table.Column(type: "text", nullable: true), + Name = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Properties = table.Column(type: "text", nullable: true), + Resources = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + Properties = table.Column(type: "text", nullable: true), + Scopes = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~", + column: x => x.ApplicationId, + principalSchema: "database", + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: true), + AuthorizationId = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + ExpirationDate = table.Column(type: "timestamp with time zone", nullable: true), + Payload = table.Column(type: "text", nullable: true), + Properties = table.Column(type: "text", nullable: true), + RedemptionDate = table.Column(type: "timestamp with time zone", nullable: true), + ReferenceId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalSchema: "database", + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalSchema: "database", + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictApplications_ClientId", + schema: "database", + table: "OpenIddictApplications", + column: "ClientId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + schema: "database", + table: "OpenIddictAuthorizations", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictScopes_Name", + schema: "database", + table: "OpenIddictScopes", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + schema: "database", + table: "OpenIddictTokens", + columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + schema: "database", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + schema: "database", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OpenIddictScopes", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictApplications", + schema: "database"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 8f4478c0..294d441a 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -20,7 +20,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "7.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); @@ -296,6 +296,204 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("DataProtectionKeys", "database"); }); + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => @@ -1038,6 +1236,30 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Approvals"); }); + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Navigation("Resources"); @@ -1062,6 +1284,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("Resources"); }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); #pragma warning restore 612, 618 } } diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7e044973..8e6ecbd8 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -38,7 +38,7 @@ IConfiguration configuration public void ConfigureServices(IServiceCollection services) { - AuthConfiguration.ConfigureServices(services, _appSettings); + AuthConfiguration.ConfigureServices(services, _environment, _appSettings); GraphQlConfiguration.ConfigureServices(services, _environment, _appSettings); ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); @@ -115,7 +115,8 @@ private void ConfigureDatabaseServices(IServiceCollection services) // dataSourceBuilder.MapEnum(); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) - .UseSchemaName(_appSettings.Database.SchemaName); + .UseSchemaName(_appSettings.Database.SchemaName) + .UseOpenIddict(); if (!_environment.IsProduction()) { options diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 1f03fe57..6f324dad 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -11,19 +11,19 @@ - - + + - - - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 6991a95b..bc19acc3 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -69,6 +69,8 @@ services: - ASPNETCORE_ENVIRONMENT=production - XBASE_Host=https://${HOST} - XBASE_MetabaseHost=https://${METABASE_HOST} + - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} + - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} diff --git a/docker-compose.yml b/docker-compose.yml index 7f0c4737..af138dc9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -102,6 +102,8 @@ services: - ASPNETCORE_ENVIRONMENT=development - XBASE_Host=https://${HOST}:${HTTPS_PORT} - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} + - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} + - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index 87845253..c3c0a0d4 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -96,6 +96,11 @@ server { limit_req zone=one burst=10 nodelay; } + location /connect { + proxy_pass http://backend; + limit_req zone=one burst=10 nodelay; + } + location /api { proxy_pass http://backend; limit_req zone=one burst=10 nodelay; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index 7b22f80c..f0eb4186 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -79,6 +79,11 @@ server { limit_req zone=one burst=10 nodelay; } + location /connect { + proxy_pass http://backend; + limit_req zone=one burst=10 nodelay; + } + location /api { proxy_pass http://backend; limit_req zone=one burst=10 nodelay; From 32dc9f6b6b935fa78b227eb7a5925ae0cb525325 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 21 Apr 2023 14:33:57 +0200 Subject: [PATCH 079/878] Install certificate authority using dotnet certificate tool --- backend/Dockerfile | 42 +++++++++++++-------------------- backend/dotnet-certificate-tool | 2 +- backend/dotnet-tools.json | 8 ++++++- docker-compose.yml | 4 ++-- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 97bf3ff2..5e0274f3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -105,9 +105,19 @@ WORKDIR /app COPY \ --chown=me:us \ ./dotnet-tools.json ./ +COPY \ + --chown=me:us \ + ./dotnet-certificate-tool ./dotnet-certificate-tool RUN \ + dotnet build \ + ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ + dotnet pack \ + ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ dotnet tool restore \ - --tool-manifest ./dotnet-tools.json + --tool-manifest ./dotnet-tools.json \ + --add-source ./dotnet-certificate-tool/src/nupkg && \ + rm --recursive --force \ + ./dotnet-certificate-tool RUN \ mkdir ./src && \ @@ -264,31 +274,12 @@ COPY \ --chown=me:us \ ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ ./ssl/ -# Once certificate tool supports .NET 7 simply use: -# ``` -# RUN \ -# dotnet tool run \ -# certificate-tool add \ -# --file ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ -# --store-name Root \ -# --password ${CERTIFICATE_AUTHORITY_PASSWORD} -# ``` -# Instead of: -COPY \ - --chown=me:us \ - ./dotnet-certificate-tool ./dotnet-certificate-tool RUN \ - dotnet publish \ - --configuration Release \ - --framework net7.0 \ - --output ./dotnet-certificate-tool/release \ - ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ - dotnet tool restore \ - --tool-manifest ./dotnet-tools.json \ - --add-source ./dotnet-certificate-tool/src/nupkg && \ - rm -rf ./dotnet-certificate-tool -# And remove the sub-module as was done before in -# https://github.com/building-envelope-data/metabase/commit/fbdd10f775e2289f9977b6c4a31ee6a5ff0b6ff9 + dotnet tool run \ + certificate-tool add \ + --file ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ + --store-name Root \ + --password ${CERTIFICATE_AUTHORITY_PASSWORD} # COPY \ # --chown=me:us \ @@ -302,7 +293,6 @@ RUN \ # https://code.visualstudio.com/docs/remote/containers-advanced#_avoiding-extension-reinstalls-on-container-rebuild RUN \ mkdir --parents \ - ./src/logs \ ./src/files && \ mkdir --parents \ ./src/bin \ diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index 934be67b..6133fc00 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit 934be67bb7fa10bcf4fb18c9494c6916417fadba +Subproject commit 6133fc001949ffa99e1db814659ab1c91040f0e9 diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 5b38f0fe..a666be84 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -79,6 +79,12 @@ "commands": [ "dotnet-graphql" ] - } + }, + "dotnet-certificate-tool": { + "version": "3.0.0", + "commands": [ + "certificate-tool" + ] + } } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index af138dc9..40f74add 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,7 @@ services: # https://www.postgresql.org/docs/current/runtime-config.html command: [ "-c", "max_connections=100" + # "-c", "log_statement=all" ] healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html @@ -77,7 +78,6 @@ services: retries: 3 volumes: - ./backend:/app - - backend_logs:/app/src/logs - files:/app/src/files - backend_vscode_server_extensions:/home/me/.vscode-server/extensions - backend_vscode_server_insiders_extensions:/home/me/.vscode-server-insiders/extensions @@ -86,6 +86,7 @@ services: # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. # - /app/node_modules + - /app/src/logs - /app/src/bin - /app/src/obj - /app/test/bin @@ -186,6 +187,5 @@ volumes: data: emails: files: - backend_logs: backend_vscode_server_extensions: backend_vscode_server_insiders_extensions: From 52f7b8f4773b19a1dbf8fd4bd030834ba2fa2147 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 21 Apr 2023 14:41:16 +0200 Subject: [PATCH 080/878] Explain what to do if metabase and database are both developed locally and they need to communicate over HTTPS --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bc73eb03..0e4ad035 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,13 @@ If you have a question for which you don't find the answer in this repository, p [GNU Make](https://www.gnu.org/software/make/). 1. List all GNU Make targets by running `make help`. 1. Generate and trust a self-signed certificate authority and SSL certificates - by running `make ssl`. + by running `make ssl`. If you are locally working the the metabase and the + database and if you need them to communicate over HTTPS, then instead of + running `make ssl`, make the `CERTIFICATE_AUTHORITY_*` variable values in + the `.env` file match the ones from the metabase, copy the certificate + authority files from the directories `./ssl`, `./backend/ssl`, and + `./frontend/ssl` of the metabase project into the respective directories in + the database project, and run the command `make generate-ssl-certificate`. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see From d31fbf4c71e39acfeb3c2226bf215c103ab21539 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 21 Apr 2023 15:33:44 +0200 Subject: [PATCH 081/878] Add GNU Make targets to print certificates --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 039550a8..cc5d65f8 100644 --- a/Makefile +++ b/Makefile @@ -434,3 +434,11 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig fetch-ssl-certificate : ## Fetch the SSL certificate of the server openssl s_client ${HOST}:${HTTPS_PORT} .PHONY : fetch-ssl-certificate + +ssl-certificate : ## Print the SSL certificate + openssl x509 -text -noout -in ./ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt +.PHONY : ssl-certificate + +certificate-authority : ## View the certificate authority + openssl x509 -text -noout -in ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt +.PHONY : certificate-authority From a36baec58ad994b64c53163061299c9eba28ef9f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 21 Apr 2023 16:01:01 +0200 Subject: [PATCH 082/878] Make redirect URIs unique --- backend/src/Configuration/AuthConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index cf159617..47a64325 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -164,8 +164,8 @@ X509Certificate2 signingCertificate // URI per provider, unless all the registered providers support returning a special "iss" // parameter containing their URL as part of authorization responses. For more information, // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - RedirectUri = new Uri("connect/callback/login/local", UriKind.Relative), - PostLogoutRedirectUri = new Uri("connect/callback/logout/local", UriKind.Relative) + RedirectUri = new Uri("connect/callback/login/metabase", UriKind.Relative), + PostLogoutRedirectUri = new Uri("connect/callback/logout/metabase", UriKind.Relative) } ); }); From f62d9f3bad36f4979713fac0d9c1e8e2e84b98da Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Apr 2023 11:24:55 +0200 Subject: [PATCH 083/878] Use the certificate authorities of the system in node.js using the option [--use-openssl-ca](https://nodejs.org/api/cli.html#--use-bundled-ca---use-openssl-ca) and thereby our self-signed certificate authority added in the Dockerfile via `update-ca-certificates` (alternatively we could have used [NODE_EXTRA_CA_CERTS](https://nodejs.org/api/cli.html#node_extra_ca_certsfile)) --- frontend/.env.development | 1 - frontend/.env.test | 8 +++----- frontend/package.json | 4 ++-- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/frontend/.env.development b/frontend/.env.development index 43d84b50..e69de29b 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1 +0,0 @@ -NODE_TLS_REJECT_UNAUTHORIZED=0 \ No newline at end of file diff --git a/frontend/.env.test b/frontend/.env.test index b2783c52..eb30bf27 100644 --- a/frontend/.env.test +++ b/frontend/.env.test @@ -1,8 +1,6 @@ -NODE_TLS_REJECT_UNAUTHORIZED=0 - -NEXT_PUBLIC_DATABASE_URL=http://localhost/ -NEXT_PUBLIC_METABASE_URL=http://localhost/ -NEXTAUTH_URL=http://localhost/ +NEXT_PUBLIC_DATABASE_URL=https://local.solarbuildingenvelopes.com:5051 +NEXT_PUBLIC_METABASE_URL=https://local.buildingenvelopedata.org:4041 +NEXTAUTH_URL=https://local.solarbuildingenvelopes.com:5051 AUTH_CLIENT_ID=testlab-solar-facades AUTH_CLIENT_SECRET=secret diff --git a/frontend/package.json b/frontend/package.json index 0576c476..4f515bad 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,9 +6,9 @@ "license": "MIT", "scripts": { "codegen": "graphql-let && graphql-codegen --config codegen.yml", - "dev": "yarn codegen && next -p 8080", + "dev": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' next -p 8080", "build": "yarn codegen && next build", - "test": "yarn codegen && jest", + "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", "start": "next start -p 8080" }, "dependencies": { From c2be970665a63c7e11e52b1ed9376fd1f4aba2c0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Apr 2023 12:28:30 +0200 Subject: [PATCH 084/878] Let logs be verbose in development --- backend/src/appsettings.development.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index a2c365de..6f8636cc 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -4,11 +4,7 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Debug", - "Override": { - "System": "Debug", - "Microsoft": "Debug" - } + "Default": "Verbose" } }, "Database": { From c00aa09f9f5107a373b0b8d8fcf19f00d893f33a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Apr 2023 13:08:28 +0200 Subject: [PATCH 085/878] Add unauthorized page (it is set as `AccessDeniedPath` of application cookies) --- frontend/pages/unauthorized.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 frontend/pages/unauthorized.tsx diff --git a/frontend/pages/unauthorized.tsx b/frontend/pages/unauthorized.tsx new file mode 100644 index 00000000..6dd3905d --- /dev/null +++ b/frontend/pages/unauthorized.tsx @@ -0,0 +1,16 @@ +import { Result } from "antd"; +import Layout from "../components/Layout"; + +function Page() { + return ( + + + + ); +} + +export default Page; From 25acd71459083b985cd30695b0fadd427de9980d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Apr 2023 14:24:23 +0200 Subject: [PATCH 086/878] Remove todo regarding disabling transport security requirement (we want it to be enabled) --- backend/src/Configuration/AuthConfiguration.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 47a64325..4c566338 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -131,12 +131,11 @@ X509Certificate2 signingCertificate .AddSigningCertificate(signingCertificate); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. - var aspNetCoreBuilder = - _.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough() - .EnablePostLogoutRedirectionEndpointPassthrough(); - // TODO aspNetCoreBuilder.DisableTransportSecurityRequirement(); + _.UseAspNetCore() + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough() + .EnablePostLogoutRedirectionEndpointPassthrough(); + // .DisableTransportSecurityRequirement(); // Register the System.Net.Http integration and use the identity of the current // assembly as a more specific user agent, which can be useful when dealing with From 756131269e207f3dddd1f24a3a20a1779f8fc4af Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Apr 2023 14:28:19 +0200 Subject: [PATCH 087/878] Link pull request #44 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10b9bbbe..089139b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) - Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) - Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) -- +- Login through the OpenId Connect endpoints of the metabase [#44](https://github.com/building-envelope-data/database/pull/44) - - - From fcee48a881f6054e88f6feaffb9b0849d2d225ac Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Apr 2023 17:12:40 +0200 Subject: [PATCH 088/878] Display database information stored in metabase under /database --- .../src/Configuration/GraphQlConfiguration.cs | 4 +- backend/src/GraphQl/Databases/Database.cs | 38 ++++ .../GraphQl/Databases/DatabaseMutations.cs | 49 +++++ .../src/GraphQl/Databases/DatabasePayload.cs | 53 ++++++ .../src/GraphQl/Databases/DatabaseQueries.cs | 91 ++++++++++ .../Databases/DatabaseVerificationState.cs | 8 + .../GraphQl/Databases/DatabasesConnection.cs | 19 ++ .../Databases/Queries/Databases.graphql | 16 ++ .../Databases/Queries/UpdateDatabase.graphql | 21 +++ .../src/GraphQl/Databases/QueryingMetabase.cs | 121 +++++++++++++ .../GraphQl/Databases/UpdateDatabaseError.cs | 17 ++ .../Databases/UpdateDatabaseErrorCode.cs | 9 + .../GraphQl/Databases/UpdateDatabaseInput.cs | 11 ++ .../Databases/UpdateDatabasePayload.cs | 20 ++ backend/src/Startup.cs | 1 + frontend/components/databases/Database.tsx | 83 +++++++++ .../components/databases/UpdateDatabase.tsx | 171 ++++++++++++++++++ frontend/pages/database.tsx | 12 ++ frontend/queries/databases.graphql | 47 +++++ frontend/type-defs.graphqls | 43 +++++ 20 files changed, 833 insertions(+), 1 deletion(-) create mode 100644 backend/src/GraphQl/Databases/Database.cs create mode 100644 backend/src/GraphQl/Databases/DatabaseMutations.cs create mode 100644 backend/src/GraphQl/Databases/DatabasePayload.cs create mode 100644 backend/src/GraphQl/Databases/DatabaseQueries.cs create mode 100644 backend/src/GraphQl/Databases/DatabaseVerificationState.cs create mode 100644 backend/src/GraphQl/Databases/DatabasesConnection.cs create mode 100644 backend/src/GraphQl/Databases/Queries/Databases.graphql create mode 100644 backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql create mode 100644 backend/src/GraphQl/Databases/QueryingMetabase.cs create mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseError.cs create mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs create mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseInput.cs create mode 100644 backend/src/GraphQl/Databases/UpdateDatabasePayload.cs create mode 100644 frontend/components/databases/Database.tsx create mode 100644 frontend/components/databases/UpdateDatabase.tsx create mode 100644 frontend/pages/database.tsx create mode 100644 frontend/queries/databases.graphql diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index bbf761fa..9d416371 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -114,16 +114,18 @@ AppSettings appSettings .AddType(new GraphQl.LocaleType()) // Query Types .AddQueryType(d => d.Name(nameof(GraphQl.Query))) - .AddType() .AddType() + .AddType() .AddType() .AddType() .AddType() .AddType() .AddType() + .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(GraphQl.Mutation))) .AddType() + .AddType() .AddType() .AddType() .AddType() diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs new file mode 100644 index 00000000..34c9e548 --- /dev/null +++ b/backend/src/GraphQl/Databases/Database.cs @@ -0,0 +1,38 @@ +using System; + +namespace Database.GraphQl.Databases +{ + public sealed class Database + { + public Guid Uuid { get; } + public string Name { get; } + public string Description { get; } + public Uri Locator { get; } + public DatabaseVerificationState VerificationState { get; } + public string VerificationCode { get; } + // public Institution? Operator { get; set; } + public bool CanCurrentUserUpdateNode { get; } + public bool CanCurrentUserVerifyNode { get; } + + public Database( + Guid uuid, + string name, + string description, + Uri locator, + DatabaseVerificationState verificationState, + string verificationCode, + bool canCurrentUserUpdateNode, + bool canCurrentUserVerifyNode + ) + { + Uuid = uuid; + Name = name; + Description = description; + Locator = locator; + VerificationState = verificationState; + VerificationCode = verificationCode; + CanCurrentUserUpdateNode = canCurrentUserUpdateNode; + CanCurrentUserVerifyNode = canCurrentUserVerifyNode; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs new file mode 100644 index 00000000..54b6d644 --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -0,0 +1,49 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; + +namespace Database.GraphQl.Databases +{ + [ExtendObjectType(nameof(Mutation))] + public sealed class DatabaseMutations + { + public async Task UpdateDatabaseAsync( + UpdateDatabaseInput input, + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await QueryingMetabase.QueryMetabase( + appSettings, + new GraphQL.GraphQLRequest( + query: await QueryingMetabase.ConstructQuery( + new[] { + "UpdateDatabase.graphql" + } + ).ConfigureAwait(false), + variables: new + { + input, + }, + operationName: "UpdateDatabase" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false))?.Data + ?? new UpdateDatabasePayload( + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + Array.Empty() + ) + ); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs new file mode 100644 index 00000000..dbf88711 --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.Databases +{ + public abstract class DatabasePayload + : Payload + where TDatabaseError : IUserError + { + public Database? Database { get; } + public IReadOnlyCollection? Errors { get; } + + protected DatabasePayload( + Database database + ) + { + Database = database; + } + + protected DatabasePayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected DatabasePayload( + TDatabaseError error + ) + : this(new[] { error }) + { + } + + protected DatabasePayload( + Database database, + IReadOnlyCollection errors + ) + { + Database = database; + Errors = errors; + } + + protected DatabasePayload( + Database database, + TDatabaseError error + ) + : this( + database, + new[] { error } + ) + { + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs new file mode 100644 index 00000000..1c6c74cc --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -0,0 +1,91 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Execution; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; + +namespace Database.GraphQl.Databases +{ + [ExtendObjectType(nameof(Query))] + public sealed class DatabaseQueries + { + private sealed class DatabasesData + { + public DatabasesConnection Databases { get; set; } = default!; + } + + public async Task GetDatabaseAsync( + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var response = + await QueryingMetabase.QueryMetabase( + appSettings, + new GraphQL.GraphQLRequest( + query: await QueryingMetabase.ConstructQuery( + new[] { + "Databases.graphql" + } + ).ConfigureAwait(false), + variables: new + { + where = new + { + locator = new + { + eq = new Uri(new Uri(appSettings.Host), "/graphql") + } + } + }, + operationName: "Databases" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + if (response is null) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NULL_RESPONSE") + .SetMessage("Response is null.") + .Build() + ); + } + if (response.Data.Databases.Nodes is null) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NULL_NODES") + .SetMessage("The supposed list of databases is null.") + .Build() + ); + } + if (response.Data.Databases.Nodes.Count == 0) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NO_DATABASE") + .SetMessage("The list of databases is empty.") + .Build() + ); + } + if (response.Data.Databases.Nodes.Count >= 2) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("AMBIGUOUS_DATABASE") + .SetMessage("The list of databases has more than one entry.") + .Build() + ); + } + return response.Data.Databases.Nodes[0]; + } + } +} diff --git a/backend/src/GraphQl/Databases/DatabaseVerificationState.cs b/backend/src/GraphQl/Databases/DatabaseVerificationState.cs new file mode 100644 index 00000000..e46eeb3b --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabaseVerificationState.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl.Databases +{ + public enum DatabaseVerificationState + { + PENDING, + VERIFIED, + } +} diff --git a/backend/src/GraphQl/Databases/DatabasesConnection.cs b/backend/src/GraphQl/Databases/DatabasesConnection.cs new file mode 100644 index 00000000..47770f03 --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabasesConnection.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.Databases +{ + public sealed class DatabasesConnection + { + // edges: [DatabasesEdge!] + public IReadOnlyList? Nodes { get; } + // pageInfo: PageInfo! + // totalCount: Int! + + public DatabasesConnection( + IReadOnlyList? nodes + ) + { + Nodes = nodes; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/Queries/Databases.graphql b/backend/src/GraphQl/Databases/Queries/Databases.graphql new file mode 100644 index 00000000..32a9c3c7 --- /dev/null +++ b/backend/src/GraphQl/Databases/Queries/Databases.graphql @@ -0,0 +1,16 @@ +query Databases( + $where: DatabaseFilterInput +) { + databases(where: $where) { + nodes { + uuid + name + description + locator + verificationState + verificationCode + canCurrentUserUpdateNode + canCurrentUserVerifyNode + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql b/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql new file mode 100644 index 00000000..a600a4f7 --- /dev/null +++ b/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql @@ -0,0 +1,21 @@ +query UpdateDatabase( + $input: UpdateDatabaseInput! +) { + updateDatabase(input: $input) { + database { + uuid + name + description + locator + verificationState + verificationCode + canCurrentUserUpdateNode + canCurrentUserVerifyNode + } + errors { + code + message + path + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/QueryingMetabase.cs b/backend/src/GraphQl/Databases/QueryingMetabase.cs new file mode 100644 index 00000000..cad0a425 --- /dev/null +++ b/backend/src/GraphQl/Databases/QueryingMetabase.cs @@ -0,0 +1,121 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using GraphQL.Client.Serializer.SystemTextJson; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using OpenIddict.Client.AspNetCore; + +namespace Database.GraphQl.Databases +{ + public sealed class QueryingMetabase + { + private static readonly JsonSerializerOptions SerializerOptions = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); + + public static async Task ConstructQuery( + string[] fileNames + ) + { + return string.Join( + Environment.NewLine, + await Task.WhenAll( + fileNames.Select(fileName => + File.ReadAllTextAsync($"GraphQl/Databases/Queries/{fileName}") + ) + ).ConfigureAwait(false) + ); + } + + public static async + Task> + QueryMetabase( + AppSettings appSettings, + GraphQL.GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class + { + using var httpClient = httpClientFactory.CreateClient(); + string? bearerToken = null; + if (httpContextAccessor.HttpContext is not null) + { + bearerToken = await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken + ).ConfigureAwait(false); + } + // For some reason `httpClient.PostAsJsonAsync` without `MakeJsonHttpContent` but with `SerializerOptions` results in `BadRequest` status code. It has to do with `JsonContent.Create` used within `PostAsJsonAsync` --- we also cannot use `JsonContent.Create` in `MakeJsonHttpContent`. What is happening here? + using var jsonHttpContent = MakeJsonHttpContent(request); + using var httpRequestMessage = new HttpRequestMessage( + HttpMethod.Post, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(new Uri(appSettings.MetabaseHost), "/graphql/") + ); + httpRequestMessage.Content = jsonHttpContent; + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { + throw new HttpRequestException($"The status code is not {HttpStatusCode.OK}.", null, httpResponseMessage.StatusCode); + } + // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ + using var graphQlResponseStream = + await httpResponseMessage.Content + .ReadAsStreamAsync(cancellationToken) + .ConfigureAwait(false); + // Console.WriteLine("aaaaaaaaaaaaaa"); + // Console.WriteLine(new StreamReader(graphQlResponseStream).ReadToEnd()); + // {"data":{"databases":{"nodes":[{"uuid":"70fe99ed-f212-4666-adde-6e0877f3e518","name":"A","description":"B","locator":"https://local.solarbuildingenvelopes.com:5051/graphql","verificationState":"VERIFIED","verificationCode":"","canCurrentUserUpdateNode":false,"canCurrentUserVerifyNode":false},{"uuid":"6313a485-3767-4352-b75c-2a8e431cc4a9","name":"X","description":"Y","locator":"https://local.solarbuildingenvelopes.com:5051/graphql","verificationState":"VERIFIED","verificationCode":"njckM853NWOJnH\u002BXyDXIQMwEdHteu9JIlQVgGp97ewZOTYzx3sVip3HcTRS9QfWdBUPnYAeKZg76BbTMEUwMZQ==","canCurrentUserUpdateNode":false,"canCurrentUserVerifyNode":false}]}}} + var deserializedGraphQlResponse = + await JsonSerializer.DeserializeAsync>( + graphQlResponseStream, + SerializerOptions, + cancellationToken + ).ConfigureAwait(false); + if (deserializedGraphQlResponse is null) + { + throw new JsonException("Failed to deserialize the GraphQL response."); + } + return deserializedGraphQlResponse; + } + + private static HttpContent MakeJsonHttpContent( + TContent content + ) + { + // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + SerializerOptions + ) + ); + result.Headers.ContentType = + new MediaTypeHeaderValue("application/json"); + return result; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs new file mode 100644 index 00000000..2d7b99d4 --- /dev/null +++ b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.Databases +{ + public sealed class UpdateDatabaseError + : GraphQl.UserErrorBase + { + public UpdateDatabaseError( + UpdateDatabaseErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs b/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs new file mode 100644 index 00000000..eeea44e5 --- /dev/null +++ b/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs @@ -0,0 +1,9 @@ +namespace Database.GraphQl.Databases +{ + public enum UpdateDatabaseErrorCode + { + UNKNOWN, + UNAUTHORIZED, + UNKNOWN_DATABASE + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs new file mode 100644 index 00000000..111fe98a --- /dev/null +++ b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs @@ -0,0 +1,11 @@ +using System; + +namespace Database.GraphQl.Databases +{ + public record UpdateDatabaseInput( + Guid DatabaseId, + string Name, + string Description, + Uri Locator + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs new file mode 100644 index 00000000..ca26bf2b --- /dev/null +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -0,0 +1,20 @@ +namespace Database.GraphQl.Databases +{ + public sealed class UpdateDatabasePayload + : DatabasePayload + { + public UpdateDatabasePayload( + Database database + ) + : base(database) + { + } + + public UpdateDatabasePayload( + UpdateDatabaseError error + ) + : base(error) + { + } + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8e6ecbd8..d9b3d365 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -48,6 +48,7 @@ public void ConfigureServices(IServiceCollection services) .AddDataProtection() .PersistKeysToDbContext(); services.AddHttpClient(); + services.AddHttpContextAccessor(); services .AddHealthChecks() .AddDbContextCheck(); diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx new file mode 100644 index 00000000..8bea66e0 --- /dev/null +++ b/frontend/components/databases/Database.tsx @@ -0,0 +1,83 @@ +import { useDatabaseQuery } from "../../queries/databases.graphql"; +import { Skeleton, Result, Descriptions, Typography, Tag } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { ReactNode, useEffect } from "react"; +import { messageApolloError } from "../../lib/apollo"; +import UpdateDatabase from "./UpdateDatabase"; +import { DatabaseVerificationState } from "../../__generated__/__types__"; + +export type DatabaseProps = {}; + +export default function Database({}: DatabaseProps) { + const { loading, error, data } = useDatabaseQuery(); + const database = data?.database; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!database) { + return ( + + ); + } + + return ( + <> + , + ] + : [] + )} + tags={[ + + {database.verificationState} + , + ]} + backIcon={false} + > + + {database.uuid} + + + {database.locator} + + + + + {database.canCurrentUserVerifyNode && + database.verificationState == DatabaseVerificationState.Pending && ( + + Have your database's GraphQL endpoint return the verification code " + {database.verificationCode}" (without the quotation marks), when + queried for the GraphQL query "verificationCode". Then, press the + "Verify" button above to make the metabase assert that the + verification codes match which proves that you control the GraphQL + endpoint {database.locator}. Verified databases are publicly listed + and included in data searches. + + )} + + ); +} diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx new file mode 100644 index 00000000..afed1944 --- /dev/null +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -0,0 +1,171 @@ +import * as React from "react"; +import { Alert, Form, Input, Button, Modal } from "antd"; +import { + useUpdateDatabaseMutation, + DatabasesDocument, + DatabaseDocument, +} from "../../queries/databases.graphql"; +import { Scalars } from "../../__generated__/__types__"; +import { useState } from "react"; +import { handleFormErrors } from "../../lib/form"; + +const layout = { + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, +}; +const tailLayout = { + wrapperCol: { offset: 8, span: 16 }, +}; + +type FormValues = { + newName: string; + newDescription: string; + newLocator: Scalars["Url"]; +}; + +export type UpdateDatabaseProps = { + databaseId: Scalars["Uuid"]; + name: string; + description: string; + locator: Scalars["Url"]; +}; + +export default function UpdateDatabase({ + databaseId, + name, + description, + locator, +}: UpdateDatabaseProps) { + const [open, setOpen] = useState(false); + const [updateDatabaseMutation] = useUpdateDatabaseMutation({ + // TODO Update the cache more efficiently as explained on https://www.apollographql.com/docs/react/caching/cache-interaction/ and https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates + // See https://www.apollographql.com/docs/react/data/mutations/#options + refetchQueries: [ + { + query: DatabaseDocument, + variables: { + uuid: databaseId, + }, + }, + { + query: DatabasesDocument, + }, + ], + }); + const [globalErrorMessages, setGlobalErrorMessages] = useState( + new Array() + ); + const [form] = Form.useForm(); + const [updating, setUpdating] = useState(false); + + const onFinish = ({ newName, newDescription, newLocator }: FormValues) => { + const update = async () => { + try { + setUpdating(true); + // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout + const { errors, data } = await updateDatabaseMutation({ + variables: { + databaseId: databaseId, + name: newName, + description: newDescription, + locator: newLocator, + }, + }); + handleFormErrors( + errors, + data?.updateDatabase?.errors?.map((x) => { + return { code: x.code, message: x.message, path: x.path }; + }), + setGlobalErrorMessages, + form + ); + if (!errors && !data?.updateDatabase?.errors) + data?.updateDatabase?.database; + { + setOpen(false); + } + } catch (error) { + // TODO Handle properly. + console.log("Failed:", error); + } finally { + setUpdating(false); + } + }; + update(); + }; + + const onFinishFailed = () => { + setGlobalErrorMessages(["Fix the errors below."]); + }; + + return ( + <> + + setOpen(false)} + footer={false} + > + {globalErrorMessages.length > 0 ? ( + + ) : ( + <> + )} +

+ + + + + + + + + + + + +
+ + + ); +} diff --git a/frontend/pages/database.tsx b/frontend/pages/database.tsx new file mode 100644 index 00000000..02433eec --- /dev/null +++ b/frontend/pages/database.tsx @@ -0,0 +1,12 @@ +import Database from "../components/databases/Database"; +import Layout from "../components/Layout"; + +function Page() { + return ( + + + + ); +} + +export default Page; diff --git a/frontend/queries/databases.graphql b/frontend/queries/databases.graphql new file mode 100644 index 00000000..11edeb30 --- /dev/null +++ b/frontend/queries/databases.graphql @@ -0,0 +1,47 @@ +fragment DatabasePartial on Database { + uuid + name + description + locator + # operator { + # node { + # uuid + # name + # } + # } + verificationState + verificationCode + canCurrentUserUpdateNode + canCurrentUserVerifyNode +} + +query Database { + database { + ...DatabasePartial + } +} + +mutation UpdateDatabase( + $databaseId: Uuid! + $name: String! + $description: String! + $locator: Url! +) { + updateDatabase( + input: { + databaseId: $databaseId + name: $name + description: $description + locator: $locator + } + ) { + database { + ...DatabasePartial + } + errors { + code + message + path + } + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index d6b30456..356b4883 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -171,6 +171,17 @@ type DataEdge { node: Data! } +type Database { + canCurrentUserUpdateNode: Boolean! + canCurrentUserVerifyNode: Boolean! + description: String! + locator: Url! + name: String! + uuid: Uuid! + verificationCode: String! + verificationState: DatabaseVerificationState! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -270,6 +281,7 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! } type NamedMethodArgument { @@ -388,6 +400,7 @@ type Query { allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData data(id: Uuid! locale: Locale timestamp: DateTime): Data + database: Database! getHttpsResource(id: Uuid!): GetHttpsResource getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput): GetHttpsResourceConnection hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData @@ -406,6 +419,18 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! + message: String! + path: [String!]! +} + +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] + query: Query! +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -911,6 +936,13 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType } +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + input UuidPropositionInput { equalTo: Uuid notEqualTo: Uuid @@ -962,11 +994,22 @@ enum DataKind { PHOTOVOLTAIC_DATA } +enum DatabaseVerificationState { + PENDING + VERIFIED +} + enum SortEnumType { ASC DESC } +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE +} + directive @authorize("Defines when when the authorize directive shall be applied.By default the authorize directives are applied during the validation phase." apply: ApplyPolicy! = VALIDATION "The name of the authorization policy that determines access to the annotated resource." policy: String "Roles that are allowed to access the annotated resource." roles: [String!]) repeatable on OBJECT | FIELD_DEFINITION "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." From 53cfe3c193a662b152df03213b92482b5855b6d3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 13:56:08 +0200 Subject: [PATCH 089/878] Do not validate anti-forgery token on log-out for now --- backend/src/Controllers/AuthenticationControllers.cs | 3 ++- frontend/components/NavBar.tsx | 12 +++++++++++- frontend/paths.ts | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index f0443158..047dd9b9 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -43,7 +43,8 @@ public ActionResult LogIn(string? returnUrl) ); } - [HttpPost("~/connect/logout"), ValidateAntiForgeryToken] + [HttpPost("~/connect/logout")] + // TODO Add `[ValidateAntiForgeryToken]`. For details see https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0#javascript-ajax-and-spas public async Task LogOut(string? returnUrl) { // Retrieve the identity stored in the local authentication cookie. If it's not available, diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 5d5a489c..7b6229dd 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import Link from "next/link"; import { useRouter } from "next/router"; -import { Menu } from "antd"; +import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; @@ -36,6 +36,16 @@ export default function NavBar({ items }: NavBarProps) { ))} }> + + Login + + +
+ +
+
{moderatorItems.map(({ path, label }) => ( {label} diff --git a/frontend/paths.ts b/frontend/paths.ts index d3fe1983..ff7dd56e 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -21,6 +21,8 @@ export default { }, createData: "/data/create", uploadFile: "/upload-file", + login: "/connect/login", + logout: "/connect/logout", metabase: { component(uuid: string) { return `${ From fb1ab67755d5d0b37428ec151bf3e4c5130e4c38 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 13:57:15 +0200 Subject: [PATCH 090/878] Mention that simply filtering by locators is error-prone (as for example a simple backslash at the end could make the result empty) --- backend/src/GraphQl/Databases/DatabaseQueries.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 1c6c74cc..c8b18bc0 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -39,6 +39,7 @@ await QueryingMetabase.QueryMetabase( { locator = new { + // TODO This is error-prone. eq = new Uri(new Uri(appSettings.Host), "/graphql") } } From 2df895627bec795dba8369002cb0e56578868e85 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 15:56:58 +0200 Subject: [PATCH 091/878] Manage users that authenticated through OpenId Connect in database (to be able to link them to other entities) --- .../src/Configuration/AuthConfiguration.cs | 8 +- .../src/Configuration/GraphQlConfiguration.cs | 2 + .../Controllers/AuthenticationControllers.cs | 51 +- backend/src/Data/ApplicationDbContext.cs | 16 +- backend/src/Data/User.cs | 29 + backend/src/GraphQl/Connection.cs | 1 - .../src/GraphQl/Users/UserByIdDataLoader.cs | 24 + backend/src/GraphQl/Users/UserQueries.cs | 33 + backend/src/GraphQl/Users/UserType.cs | 15 + ...20230427133946_CreateUserTable.Designer.cs | 1335 +++++++++++++++++ .../20230427133946_CreateUserTable.cs | 39 + .../ApplicationDbContextModelSnapshot.cs | 30 + frontend/components/NavBar.tsx | 37 +- frontend/queries/currentUser.graphql | 10 + frontend/type-defs.graphqls | 9 + 15 files changed, 1616 insertions(+), 23 deletions(-) create mode 100644 backend/src/Data/User.cs create mode 100644 backend/src/GraphQl/Users/UserByIdDataLoader.cs create mode 100644 backend/src/GraphQl/Users/UserQueries.cs create mode 100644 backend/src/GraphQl/Users/UserType.cs create mode 100644 backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs create mode 100644 backend/src/Migrations/20230427133946_CreateUserTable.cs create mode 100644 frontend/queries/currentUser.graphql diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 4c566338..6bf46f86 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -7,9 +7,9 @@ using Quartz; using Database.Data; using System.Security.Cryptography.X509Certificates; -using static OpenIddict.Abstractions.OpenIddictConstants; using System.Reflection; using System.IO; +using OpenIddict.Abstractions; namespace Database.Configuration { @@ -154,9 +154,11 @@ X509Certificate2 signingCertificate ClientId = "testlab-solar-facades", ClientSecret = appSettings.OpenIdConnectClientSecret, + // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims Scopes = { - Scopes.Email, - Scopes.Roles + OpenIddictConstants.Scopes.Email, + OpenIddictConstants.Scopes.Profile, + OpenIddictConstants.Scopes.Roles }, // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 9d416371..8739e7f1 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -121,6 +121,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(GraphQl.Mutation))) @@ -141,6 +142,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() // Data Loaders /* .AddDataLoader() */ // Paging diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index 047dd9b9..38c51471 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -2,10 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Security.Claims; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; @@ -16,10 +18,15 @@ namespace Database.Controllers public class AuthenticationController : Controller { private readonly string _issuer; + private readonly Data.ApplicationDbContext _context; - public AuthenticationController(AppSettings appSettings) + public AuthenticationController( + AppSettings appSettings, + Data.ApplicationDbContext context + ) { _issuer = appSettings.MetabaseHost; + _context = context; } [HttpGet("~/connect/login")] @@ -83,7 +90,7 @@ public async Task LogOut(string? returnUrl) // but for users who prefer using a different action per provider, // the following action can be split into separate actions. [HttpGet("~/connect/callback/login/{provider}"), HttpPost("~/connect/callback/login/{provider}"), IgnoreAntiforgeryToken] - public async Task LogInCallback() + public async Task LogInCallback(CancellationToken cancellationToken) { // Retrieve the authorization data validated by OpenIddict as part of the callback handling. var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); @@ -126,10 +133,14 @@ public async Task LogInCallback() result.Principal.Claims .Select(claim => claim switch { + // https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims // Map the standard "sub" and custom "id" claims to ClaimTypes.NameIdentifier, which is // the default claim type used by .NET and is required by the antiforgery components. { Type: Claims.Subject } => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer), + // Map the standard "email" claim to ClaimTypes.Email. + { Type: Claims.Email } + => new Claim(ClaimTypes.Email, claim.Value, claim.ValueType, claim.Issuer), // Map the standard "name" claim to ClaimTypes.Name. { Type: Claims.Name } => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer), @@ -138,7 +149,7 @@ public async Task LogInCallback() .Where(claim => claim switch { // Preserve the basic claims that are necessary for the application to work correctly. - { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true, + { Type: ClaimTypes.NameIdentifier or ClaimTypes.Email or ClaimTypes.Name } => true, // Don't preserve the other claims. _ => false })); @@ -171,6 +182,40 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or _ => false })); } + // Add the user to the database if he/she does not exist. + var subject = + identity.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value + ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.NameIdentifier}, which is the subject of the token, is missing for the identity with name {identity.Name}."); + var email = + identity.FindFirst(c => c.Type == ClaimTypes.Email)?.Value + ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.Email} is missing for the identity with subject {subject}."); + var name = + identity.FindFirst(c => c.Type == ClaimTypes.Name)?.Value + ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.Name} is missing for the identity with subject {subject}."); + var user = await + _context.Users.AsQueryable() + .SingleOrDefaultAsync( + u => u.Subject == subject, + cancellationToken + ); + if (user is null) + { + _context.Users.Add( + new Data.User( + subject: subject, + email: email, + name: name + ) + ); + } + else + { + user.Update( + email: email, + name: name + ); + } + await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); // Ask the cookie authentication handler to return a new cookie and redirect // the user agent to the return URL stored in the authentication properties. return SignIn( diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 36eae441..5c3ca2f5 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -67,11 +67,12 @@ ModelBuilder builder // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset public DbSet GetHttpsResources { get; private set; } = default!; + public DbSet DataProtectionKeys { get; private set; } = default!; public DbSet CalorimetricData { get; private set; } = default!; public DbSet HygrothermalData { get; private set; } = default!; public DbSet OpticalData { get; private set; } = default!; public DbSet PhotovoltaicData { get; private set; } = default!; - public DbSet DataProtectionKeys { get; private set; } = default!; + public DbSet Users { get; private set; } = default!; public ApplicationDbContext( DbContextOptions options @@ -96,7 +97,14 @@ protected override void OnModelCreating(ModelBuilder builder) ConfigureEntity( builder.Entity() ) - .OwnsOne(data => data.AppliedMethod, method => { method.OwnsMany(m => m.Arguments); method.OwnsMany(m => m.Sources); }) + .OwnsOne( + data => data.AppliedMethod, + method => + { + method.OwnsMany(m => m.Arguments); + method.OwnsMany(m => m.Sources); + } + ) .ToTable("calorimetric_data"); ConfigureEntity( builder.Entity() @@ -110,6 +118,10 @@ protected override void OnModelCreating(ModelBuilder builder) builder.Entity() ) .ToTable("photovoltaic_data"); + ConfigureEntity( + builder.Entity() + ) + .ToTable("user"); } } } \ No newline at end of file diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs new file mode 100644 index 00000000..3f022754 --- /dev/null +++ b/backend/src/Data/User.cs @@ -0,0 +1,29 @@ +namespace Database.Data +{ + public sealed class User : Entity + { + public string Subject { get; private set; } + public string Email { get; private set; } + public string Name { get; private set; } + + public User( + string subject, + string email, + string name + ) + { + Subject = subject; + Email = email; + Name = name; + } + + public void Update( + string email, + string name + ) + { + Email = email; + Name = name; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index 256f9551..54ade310 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using GreenDonut; -using HotChocolate; namespace Database.GraphQl { diff --git a/backend/src/GraphQl/Users/UserByIdDataLoader.cs b/backend/src/GraphQl/Users/UserByIdDataLoader.cs new file mode 100644 index 00000000..12b9ac9a --- /dev/null +++ b/backend/src/GraphQl/Users/UserByIdDataLoader.cs @@ -0,0 +1,24 @@ +using GreenDonut; +using Database.GraphQl.Entities; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.Users +{ + public sealed class UserByIdDataLoader + : EntityByIdDataLoader + { + public UserByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.Users + ) + { + } + } +} diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs new file mode 100644 index 00000000..7bca0b8f --- /dev/null +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using OpenIddict.Abstractions; + +namespace Database.GraphQl.Users +{ + [ExtendObjectType(nameof(Query))] + public sealed class UserQueries + { + public async Task GetCurrentUserAsync( + [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + Data.ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) + { + return null; + } + return await + context.Users.AsQueryable() + .SingleOrDefaultAsync( + u => u.Subject == claimsPrincipal.GetClaim(ClaimTypes.NameIdentifier), + cancellationToken + ).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserType.cs b/backend/src/GraphQl/Users/UserType.cs new file mode 100644 index 00000000..92d4d9c4 --- /dev/null +++ b/backend/src/GraphQl/Users/UserType.cs @@ -0,0 +1,15 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.Users +{ + public sealed class UserType + : EntityType + { + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } + } +} diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs b/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs new file mode 100644 index 00000000..cfd6fd51 --- /dev/null +++ b/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs @@ -0,0 +1,1335 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20230427133946_CreateUserTable")] + partial class CreateUserTable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.cs b/backend/src/Migrations/20230427133946_CreateUserTable.cs new file mode 100644 index 00000000..317b3e46 --- /dev/null +++ b/backend/src/Migrations/20230427133946_CreateUserTable.cs @@ -0,0 +1,39 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class CreateUserTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "user", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + Subject = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_user", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "user", + schema: "database"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 294d441a..e6725e63 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -277,6 +277,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("photovoltaic_data", "database"); }); + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => { b.Property("Id") diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 7b6229dd..1a60c642 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/router"; import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; +import { useCurrentUserQuery } from "../queries/currentUser.graphql"; type NavItemProps = { path: string; @@ -27,6 +28,7 @@ const moderatorItems = [ export default function NavBar({ items }: NavBarProps) { const router = useRouter(); + const currentUser = useCurrentUserQuery()?.data?.currentUser; return ( @@ -35,23 +37,30 @@ export default function NavBar({ items }: NavBarProps) { {label} ))} - }> + {currentUser ? ( + } + > + +
+ +
+
+ {moderatorItems.map(({ path, label }) => ( + + {label} + + ))} +
+ ) : ( Login - -
- -
-
- {moderatorItems.map(({ path, label }) => ( - - {label} - - ))} -
+ )}
); } diff --git a/frontend/queries/currentUser.graphql b/frontend/queries/currentUser.graphql new file mode 100644 index 00000000..7b06159f --- /dev/null +++ b/frontend/queries/currentUser.graphql @@ -0,0 +1,10 @@ +fragment CurrentUserPartial on User { + email + name +} + +query CurrentUser { + currentUser { + ...CurrentUserPartial + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 356b4883..6a723473 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -399,6 +399,7 @@ type Query { allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] timestamp: DateTime where: OpticalDataPropositionInput): OpticalDataConnection allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData + currentUser: User data(id: Uuid! locale: Locale timestamp: DateTime): Data database: Database! getHttpsResource(id: Uuid!): GetHttpsResource @@ -431,6 +432,14 @@ type UpdateDatabasePayload { query: Query! } +type User implements Node { + email: String! + id: ID! + name: String! + subject: String! + uuid: Uuid! +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! From a38e42dda9474563c7093171e7d0648e3200dca0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 17:02:43 +0200 Subject: [PATCH 092/878] Make update database payload deserializable by System.Json --- .../GraphQl/Databases/DatabaseMutations.cs | 14 +++++--- .../src/GraphQl/Databases/DatabasePayload.cs | 36 ++----------------- .../Databases/UpdateDatabasePayload.cs | 14 +++----- 3 files changed, 16 insertions(+), 48 deletions(-) diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 54b6d644..314a0181 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -38,11 +38,15 @@ CancellationToken cancellationToken cancellationToken ).ConfigureAwait(false))?.Data ?? new UpdateDatabasePayload( - new UpdateDatabaseError( - UpdateDatabaseErrorCode.UNKNOWN, - "Unknown error.", - Array.Empty() - ) + null, + new [] + { + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + Array.Empty() + ) + } ); } } diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index dbf88711..3771c530 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -10,44 +10,12 @@ public abstract class DatabasePayload public IReadOnlyCollection? Errors { get; } protected DatabasePayload( - Database database - ) - { - Database = database; - } - - protected DatabasePayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected DatabasePayload( - TDatabaseError error - ) - : this(new[] { error }) - { - } - - protected DatabasePayload( - Database database, - IReadOnlyCollection errors + Database? database, + IReadOnlyCollection? errors ) { Database = database; Errors = errors; } - - protected DatabasePayload( - Database database, - TDatabaseError error - ) - : this( - database, - new[] { error } - ) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index ca26bf2b..63b62dc7 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -1,19 +1,15 @@ +using System.Collections.Generic; + namespace Database.GraphQl.Databases { public sealed class UpdateDatabasePayload : DatabasePayload { public UpdateDatabasePayload( - Database database - ) - : base(database) - { - } - - public UpdateDatabasePayload( - UpdateDatabaseError error + Database? database, + IReadOnlyCollection? errors ) - : base(error) + : base(database, errors) { } } From 7946e52e1a069b9da561de89f262050975c170d5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 17:03:06 +0200 Subject: [PATCH 093/878] Use correct base type for the update database mutation --- backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql b/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql index a600a4f7..ba9eadda 100644 --- a/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql +++ b/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql @@ -1,4 +1,4 @@ -query UpdateDatabase( +mutation UpdateDatabase( $input: UpdateDatabaseInput! ) { updateDatabase(input: $input) { From c3da47edfc589fbc7778d2147e0c5319b322e713 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 17:03:30 +0200 Subject: [PATCH 094/878] Add the database page to the navigation bar --- frontend/components/Layout.tsx | 4 ++++ frontend/paths.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index d729a22a..cb8c2a98 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -31,6 +31,10 @@ const navItems = [ path: paths.photovoltaicData, label: "Photovoltaic Data", }, + { + path: paths.database, + label: "Database", + }, ]; export type LayoutProps = { diff --git a/frontend/paths.ts b/frontend/paths.ts index ff7dd56e..d3a93f8e 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -2,6 +2,7 @@ export default { home: "/", legalNotice: "/legal-notice", dataProtectionInformation: "/data-protection-information", + database: "/database", data: "/data", calorimetricData: "/data/calorimetric", calorimetricDatum(uuid: string) { From 49e9f5658a6ade0a6be2b1e24a7852b3b1924dad Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 17:04:22 +0200 Subject: [PATCH 095/878] Request the read and write API scopes (the write API scope is needed to be allowed to execute the update database mutation) --- backend/src/Configuration/AuthConfiguration.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 6bf46f86..9b0ece37 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -17,6 +17,9 @@ namespace Database.Configuration // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs public abstract class AuthConfiguration { + public static string ReadApiScope { get; } = "api:read"; + public static string WriteApiScope { get; } = "api:write"; + public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment, @@ -158,7 +161,9 @@ X509Certificate2 signingCertificate Scopes = { OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Profile, - OpenIddictConstants.Scopes.Roles + OpenIddictConstants.Scopes.Roles, + ReadApiScope, + WriteApiScope }, // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint From f254a0f6db3313b40a51b7c238254a2e0e759e78 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 27 Apr 2023 17:13:30 +0200 Subject: [PATCH 096/878] Note that the claims are fetched from the userinfo endpoint --- backend/src/Controllers/AuthenticationControllers.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index 38c51471..ca2045d9 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -129,6 +129,9 @@ public async Task LogInCallback(CancellationToken cancellationToke // // By default, all claims extracted during the authorization dance are available. The claims collection stored // in the cookie can be filtered out or mapped to different names depending on the claim name or its issuer. + // + // The claims are fetched from the userinfo endpoint of the + // authorization provider. var claims = new List( result.Principal.Claims .Select(claim => claim switch From edb27df1a6feb251dd85381d6e219caee6a11c9f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:05:37 +0200 Subject: [PATCH 097/878] Store only subject and name in database and use the /connect/userinfo endpoint of the metabase's OpenId Connect support for all other information (this way the personal info is always up-to-date, there are no concerns regarding encryption of the email address and other user related information in the database and data is not kept twice but only in the metabase) --- .../src/Configuration/AuthConfiguration.cs | 2 + .../Controllers/AuthenticationControllers.cs | 14 +- backend/src/Data/User.cs | 7 +- backend/src/Database.csproj | 1 + .../src/GraphQl/Databases/QueryingMetabase.cs | 4 +- backend/src/GraphQl/Users/UserInfo.cs | 20 +++ backend/src/GraphQl/Users/UserQueries.cs | 123 +++++++++++++++++- frontend/components/NavBar.tsx | 4 + frontend/pages/user-info.tsx | 64 +++++++++ frontend/paths.ts | 1 + frontend/queries/currentUser.graphql | 17 ++- frontend/type-defs.graphqls | 18 ++- 12 files changed, 250 insertions(+), 25 deletions(-) create mode 100644 backend/src/GraphQl/Users/UserInfo.cs create mode 100644 frontend/pages/user-info.tsx diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 9b0ece37..001db73b 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -159,7 +159,9 @@ X509Certificate2 signingCertificate // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims Scopes = { + OpenIddictConstants.Scopes.Address, OpenIddictConstants.Scopes.Email, + OpenIddictConstants.Scopes.Phone, OpenIddictConstants.Scopes.Profile, OpenIddictConstants.Scopes.Roles, ReadApiScope, diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index ca2045d9..355eb394 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -141,9 +141,6 @@ public async Task LogInCallback(CancellationToken cancellationToke // the default claim type used by .NET and is required by the antiforgery components. { Type: Claims.Subject } => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer), - // Map the standard "email" claim to ClaimTypes.Email. - { Type: Claims.Email } - => new Claim(ClaimTypes.Email, claim.Value, claim.ValueType, claim.Issuer), // Map the standard "name" claim to ClaimTypes.Name. { Type: Claims.Name } => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer), @@ -152,7 +149,7 @@ public async Task LogInCallback(CancellationToken cancellationToke .Where(claim => claim switch { // Preserve the basic claims that are necessary for the application to work correctly. - { Type: ClaimTypes.NameIdentifier or ClaimTypes.Email or ClaimTypes.Name } => true, + { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true, // Don't preserve the other claims. _ => false })); @@ -189,9 +186,6 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or var subject = identity.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.NameIdentifier}, which is the subject of the token, is missing for the identity with name {identity.Name}."); - var email = - identity.FindFirst(c => c.Type == ClaimTypes.Email)?.Value - ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.Email} is missing for the identity with subject {subject}."); var name = identity.FindFirst(c => c.Type == ClaimTypes.Name)?.Value ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.Name} is missing for the identity with subject {subject}."); @@ -206,17 +200,13 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or _context.Users.Add( new Data.User( subject: subject, - email: email, name: name ) ); } else { - user.Update( - email: email, - name: name - ); + user.Update(name: name); } await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); // Ask the cookie authentication handler to return a new cookie and redirect diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs index 3f022754..5d043007 100644 --- a/backend/src/Data/User.cs +++ b/backend/src/Data/User.cs @@ -3,27 +3,22 @@ namespace Database.Data public sealed class User : Entity { public string Subject { get; private set; } - public string Email { get; private set; } public string Name { get; private set; } public User( string subject, - string email, string name ) { Subject = subject; - Email = email; Name = name; } public void Update( - string email, string name ) { - Email = email; Name = name; - } + } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 2dd7453a..c7d6ad96 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -51,6 +51,7 @@ +
diff --git a/backend/src/GraphQl/Databases/QueryingMetabase.cs b/backend/src/GraphQl/Databases/QueryingMetabase.cs index cad0a425..9d0f8cd2 100644 --- a/backend/src/GraphQl/Databases/QueryingMetabase.cs +++ b/backend/src/GraphQl/Databases/QueryingMetabase.cs @@ -78,16 +78,14 @@ CancellationToken cancellationToken using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); if (httpResponseMessage.StatusCode != HttpStatusCode.OK) { - throw new HttpRequestException($"The status code is not {HttpStatusCode.OK}.", null, httpResponseMessage.StatusCode); + throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); } // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ using var graphQlResponseStream = await httpResponseMessage.Content .ReadAsStreamAsync(cancellationToken) .ConfigureAwait(false); - // Console.WriteLine("aaaaaaaaaaaaaa"); // Console.WriteLine(new StreamReader(graphQlResponseStream).ReadToEnd()); - // {"data":{"databases":{"nodes":[{"uuid":"70fe99ed-f212-4666-adde-6e0877f3e518","name":"A","description":"B","locator":"https://local.solarbuildingenvelopes.com:5051/graphql","verificationState":"VERIFIED","verificationCode":"","canCurrentUserUpdateNode":false,"canCurrentUserVerifyNode":false},{"uuid":"6313a485-3767-4352-b75c-2a8e431cc4a9","name":"X","description":"Y","locator":"https://local.solarbuildingenvelopes.com:5051/graphql","verificationState":"VERIFIED","verificationCode":"njckM853NWOJnH\u002BXyDXIQMwEdHteu9JIlQVgGp97ewZOTYzx3sVip3HcTRS9QfWdBUPnYAeKZg76BbTMEUwMZQ==","canCurrentUserUpdateNode":false,"canCurrentUserVerifyNode":false}]}}} var deserializedGraphQlResponse = await JsonSerializer.DeserializeAsync>( graphQlResponseStream, diff --git a/backend/src/GraphQl/Users/UserInfo.cs b/backend/src/GraphQl/Users/UserInfo.cs new file mode 100644 index 00000000..79e7a139 --- /dev/null +++ b/backend/src/GraphQl/Users/UserInfo.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.Users +{ + public sealed record Address( + string Formatted + ); + + public sealed record UserInfo( + Address? Address, + string Email, + bool EmailVerified, + string Name, + string? PhoneNumber, + bool PhoneNumberVerified, + IReadOnlyList? Roles, + string Sub, // Subject + string? Website + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 7bca0b8f..b496448e 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,11 +1,24 @@ +using System; using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using System.Security.Claims; +using System.Text.Json; +using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using GraphQL.Client.Serializer.SystemTextJson; using HotChocolate; +using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; +using OpenIddict.Client.AspNetCore; +using Yoh.Text.Json.NamingPolicies; namespace Database.GraphQl.Users { @@ -22,12 +35,118 @@ CancellationToken cancellationToken { return null; } - return await - context.Users.AsQueryable() + return + await context.Users.AsQueryable() .SingleOrDefaultAsync( u => u.Subject == claimsPrincipal.GetClaim(ClaimTypes.NameIdentifier), cancellationToken ).ConfigureAwait(false); } + + public async Task GetCurrentUserInfoAsync( + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); + try + { + return await Query( + uri, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + catch (HttpRequestException e) + { + resolverContext.ReportError( + ErrorBuilder.New() + .SetCode("METABASE_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code {e.StatusCode} to request {uri}.") + .SetException(e) + .Build() + ); + return null; + } + catch (JsonException e) + { + resolverContext.ReportError( + ErrorBuilder.New() + .SetCode("DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()).ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage($"Failed to deserialize GraphQL response of request to {uri}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetException(e) + .Build() + ); + return null; + } + } + + private static readonly JsonSerializerOptions SerializerOptions = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). + PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); + + private static async Task Query( + [Service] Uri uri, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { + using var httpClient = httpClientFactory.CreateClient(); + string? bearerToken = null; + if (httpContextAccessor.HttpContext is not null) + { + bearerToken = await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken + ).ConfigureAwait(false); + } + using var httpRequestMessage = new HttpRequestMessage( + HttpMethod.Get, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + uri + ); + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { + throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); + } + // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ + using var responseStream = + await httpResponseMessage.Content + .ReadAsStreamAsync(cancellationToken) + .ConfigureAwait(false); + // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); + var deserializedResponse = + await JsonSerializer.DeserializeAsync( + responseStream, + SerializerOptions, + cancellationToken + ).ConfigureAwait(false); + if (deserializedResponse is null) + { + throw new JsonException("Failed to deserialize the GraphQL response."); + } + return deserializedResponse; + } } } \ No newline at end of file diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 1a60c642..08add2c4 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -16,6 +16,10 @@ export type NavBarProps = { }; const moderatorItems = [ + { + path: paths.userInfo, + label: "User Info", + }, { path: paths.createData, label: "Create Data", diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx new file mode 100644 index 00000000..1bc7e377 --- /dev/null +++ b/frontend/pages/user-info.tsx @@ -0,0 +1,64 @@ +import Layout from "../components/Layout"; +import { useCurrentUserInfoQuery } from "../queries/currentUser.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import { messageApolloError } from "../lib/apollo"; + +function Page() { + const { loading, error, data } = useCurrentUserInfoQuery(); + const currentUserInfo = data?.currentUserInfo; + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!currentUserInfo) { + return ( + + ); + } + + return ( + + + + + {currentUserInfo.email} (Verified: {currentUserInfo.emailVerified}) + + + {currentUserInfo.phoneNumber} (Verified:{" "} + {currentUserInfo.phoneNumberVerified}) + + + {currentUserInfo.address?.formatted} + + + + {currentUserInfo.website} + + + + {currentUserInfo.roles} + + + + + ); +} + +export default Page; diff --git a/frontend/paths.ts b/frontend/paths.ts index d3a93f8e..67cfabc6 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -2,6 +2,7 @@ export default { home: "/", legalNotice: "/legal-notice", dataProtectionInformation: "/data-protection-information", + userInfo: "/user-info", database: "/database", data: "/data", calorimetricData: "/data/calorimetric", diff --git a/frontend/queries/currentUser.graphql b/frontend/queries/currentUser.graphql index 7b06159f..b08beab4 100644 --- a/frontend/queries/currentUser.graphql +++ b/frontend/queries/currentUser.graphql @@ -1,5 +1,4 @@ fragment CurrentUserPartial on User { - email name } @@ -8,3 +7,19 @@ query CurrentUser { ...CurrentUserPartial } } + +query CurrentUserInfo { + currentUserInfo { + address { + formatted + } + email + emailVerified + name + phoneNumber + phoneNumberVerified + roles + sub + website + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 6a723473..e937a4db 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -24,6 +24,10 @@ interface Node { id: ID! } +type Address { + formatted: String! +} + type AppliedMethod { arguments: [NamedMethodArgument!]! methodId: Uuid! @@ -400,6 +404,7 @@ type Query { allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData currentUser: User + currentUserInfo: UserInfo data(id: Uuid! locale: Locale timestamp: DateTime): Data database: Database! getHttpsResource(id: Uuid!): GetHttpsResource @@ -433,13 +438,24 @@ type UpdateDatabasePayload { } type User implements Node { - email: String! id: ID! name: String! subject: String! uuid: Uuid! } +type UserInfo { + address: Address + email: String! + emailVerified: Boolean! + name: String! + phoneNumber: String + phoneNumberVerified: Boolean! + roles: [String!] + sub: String! + website: String +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! From 75e69da70e79c678a0b7320ac2744426745dcb4d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:14:10 +0200 Subject: [PATCH 098/878] Display verification status properly --- frontend/pages/user-info.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 1bc7e377..0dff0dbc 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -38,11 +38,16 @@ function Page() { > - {currentUserInfo.email} (Verified: {currentUserInfo.emailVerified}) + {currentUserInfo.email} ( + {currentUserInfo.emailVerified === true ? "Verified" : "Unverified"} + ) - {currentUserInfo.phoneNumber} (Verified:{" "} - {currentUserInfo.phoneNumberVerified}) + {currentUserInfo.phoneNumber} ( + {currentUserInfo.phoneNumberVerified === true + ? "Verified" + : "Unverified"} + ) {currentUserInfo.address?.formatted} From 7efacacbe223bd24c705191909174c1c16a2bae2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:15:06 +0200 Subject: [PATCH 099/878] Do not use a default authentication scheme (be explicit instead to improve code understanding) --- backend/src/Configuration/AuthConfiguration.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 001db73b..0dc3d93b 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -58,12 +58,12 @@ private static void ConfigureAuthenticiationAndAuthorizationServices( IServiceCollection services ) { + // Dot not use the single authentication scheme as the default scheme + // https://learn.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-7.0#defaultscheme + AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", isEnabled: true); // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs services - .AddAuthentication(_ => - { - _.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; - }) + .AddAuthentication() .AddCookie(_ => { _.AccessDeniedPath = "/unauthorized"; From 7600b692339189372df2b134cfd1bdd7b7a6e054 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:17:15 +0200 Subject: [PATCH 100/878] Show only available user info --- frontend/pages/user-info.tsx | 58 ++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 0dff0dbc..50ba0030 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -37,29 +37,41 @@ function Page() { backIcon={false} > - - {currentUserInfo.email} ( - {currentUserInfo.emailVerified === true ? "Verified" : "Unverified"} - ) - - - {currentUserInfo.phoneNumber} ( - {currentUserInfo.phoneNumberVerified === true - ? "Verified" - : "Unverified"} - ) - - - {currentUserInfo.address?.formatted} - - - - {currentUserInfo.website} - - - - {currentUserInfo.roles} - + {currentUserInfo.email && ( + + {currentUserInfo.email} ( + {currentUserInfo.emailVerified === true + ? "Verified" + : "Unverified"} + ) + + )} + {currentUserInfo.phoneNumber && ( + + {currentUserInfo.phoneNumber} ( + {currentUserInfo.phoneNumberVerified === true + ? "Verified" + : "Unverified"} + ) + + )} + {currentUserInfo.address && ( + + {currentUserInfo.address.formatted} + + )} + {currentUserInfo.website && ( + + + {currentUserInfo.website} + + + )} + {currentUserInfo.roles && ( + + {currentUserInfo.roles} + + )} From 7113caddc5f09264ba94a4a1129c84c2731acc84 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:18:59 +0200 Subject: [PATCH 101/878] Remove next-auth (we use the OpenIddict client instead to authenticate through the Metabase) --- frontend/package.json | 1 - frontend/pages/api/auth/[...nextauth].ts | 344 ----------------------- frontend/yarn.lock | 76 +---- 3 files changed, 2 insertions(+), 419 deletions(-) delete mode 100644 frontend/pages/api/auth/[...nextauth].ts diff --git a/frontend/package.json b/frontend/package.json index 4f515bad..b7b7852a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,7 +19,6 @@ "dayjs": "^1.0", "graphql": "^16.0.1", "next": "^13.1.4", - "next-auth": "^4.18.8", "react": "^18.2.0", "react-cookie": "^4.0.3", "react-dom": "^18.2.0", diff --git a/frontend/pages/api/auth/[...nextauth].ts b/frontend/pages/api/auth/[...nextauth].ts deleted file mode 100644 index ceb3bb3c..00000000 --- a/frontend/pages/api/auth/[...nextauth].ts +++ /dev/null @@ -1,344 +0,0 @@ -import { NextApiHandler } from "next"; -import NextAuth from "next-auth"; - -// @ts-ignore: TODO Ask `@types/next-auth` to extend their types? For example, to allow the callback `session` getting a `token` instead of a `user` as second parameter. See https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/next-auth -const authHandler: NextApiHandler = (req, res) => NextAuth(req, res, options); -export default authHandler; - -interface ProviderJsonWebToken { - name: string; - email: string; - picture: number | null; - sub: string; -} - -interface StoredJsonWebToken { - refreshToken: string; - accessToken: string; - accessTokenExpires: number; - error?: string; - user: User; -} - -interface SessionJsonWebToken extends StoredJsonWebToken { - iat: number; - exp: number; -} - -// extends `SessionBase` from `@types/next-auth`, see https://github.com/DefinitelyTyped/DefinitelyTyped/blob/461c4b4dd7783684c16dbf1e9ef28b33a5455026/types/next-auth/_utils.d.ts#L26 -interface Session { - user?: User; - accessToken?: string; - expires?: number; - error?: string; -} - -interface ProviderAccount { - provider: string; - type: string; - id: string; - accessToken: string; - accessTokenExpires: number | null; - refreshToken: string; - idToken: string; - access_token: string; - token_type: "Bearer"; - expires_in: number; - scope: string; - id_token: string; - refresh_token: string; -} - -// For the list of registered claim names like `sub`, `exp`, `aud`, `iss`, and -// `iat`, see https://tools.ietf.org/html/rfc7519#section-4.1 -// For the list of public claim names like `name`, `azp`, and `at_hash` -// see https://www.iana.org/assignments/jwt/jwt.xhtml -interface ProviderProfile { - sub: string; - name: string; - email: string; - oi_au_id: string; - azp: string; - at_hash: string; - oi_tkn_id: string; - aud: string; - exp: number; - iss: string; - iat: number; -} - -interface User { - id: string; - name: string; - email: string; -} - -/** - * Takes a token, and returns a new token with updated - * `accessToken` and `accessTokenExpires`. If an error occurs, - * returns the old token and an error property - * - * See https://next-auth.js.org/tutorials/refresh-token-rotation - */ -async function refreshAccessToken( - token: StoredJsonWebToken -): Promise { - try { - const url = - `${process.env.NEXT_PUBLIC_METABASE_URL}/connect/token?` + - new URLSearchParams({ - client_id: process.env.AUTH_CLIENT_ID || "", // TODO Throw an error if environment variable is missing! - client_secret: process.env.AUTH_CLIENT_SECRET || "", // TODO Throw an error if environment variable is missing! - grant_type: "refresh_token", - refresh_token: token.refreshToken, - }); - const response = await fetch(url, { - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - method: "POST", - }); - const refreshedTokens = await response.json(); - if (!response.ok) { - throw refreshedTokens; - } - return { - ...token, - accessToken: refreshedTokens.access_token, - accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000, - refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token - }; - } catch (error) { - console.log(error); - return { - ...token, - error: "RefreshAccessTokenError", // TODO Use global constant instead of string! - }; - } -} - -// https://next-auth.js.org/configuration/options -const options = { - // database: { - // type: "sqlite", - // database: ":memory:", - // synchronize: true, - // }, - // database: { - // type: "postgres", - // host: "database", - // port: 5432, - // username: "postgres", - // password: "postgres", - // database: "xbase_development", - // entityPrefix: "nextauth_", - // synchronize: process.env.NODE_ENV !== "production", - // // uri: - // // process.env.NODE_ENV === "production" - // // ? "Host=database; Port=5432; Database=xbase; User Id=postgres; Passfile=/run/secrets/pgpass;" - // // : "Host=database; Port=5432; Database=xbase_development; User Id=postgres; Password=postgres;", - // }, - providers: [ - { - // https://next-auth.js.org/configuration/providers#using-a-custom-provider - // https://next-auth.js.org/configuration/providers#oauth-provider-options - // https://buildingenvelopedata.org/.well-known/openid-configuration - id: "metabase", - name: "Metabase", - type: "oauth", - version: "2.0", - scope: "openid email profile roles api:read api:write offline_access", - params: { grant_type: "authorization_code" }, - authorizationParams: {}, - - // Note that the host of `authorizationUrl`, `accessTokenUrl`, and - // `requestTokenUrl` must be the same (supposedly for security). And that - // `authorizationUrl` must be a valid URL outside of the Docker network - // (as it is requested within the web browser). That is why we cannot use - // http://backend:8080/connect/token as `accessTokenUrl` (although it is - // the correct URL within the Docker network and only used within that - // network from the `frontend` service). - accessTokenUrl: `${process.env.NEXT_PUBLIC_METABASE_URL}/connect/token`, - requestTokenUrl: `${process.env.NEXT_PUBLIC_METABASE_URL}/connect/authorize`, - authorizationUrl: `${process.env.NEXT_PUBLIC_METABASE_URL}/connect/authorize?response_type=code`, - - profileUrl: `${process.env.NEXT_PUBLIC_METABASE_URL}/connect/userinfo`, - protection: "pkce", // https://security.stackexchange.com/questions/214980/does-pkce-replace-state-in-the-authorization-code-oauth-flow/215027#215027 - idToken: true, - headers: {}, - async profile(profile: ProviderProfile): Promise { - // You can use the tokens, in case you want to fetch more profile information - // For example several OAuth provider does not return e-mail by default. - // Depending on your provider, will have tokens like `access_token`, `id_token` and or `refresh_token` - return { - id: profile.sub, - name: profile.name, - email: profile.email, - }; - }, - clientId: process.env.AUTH_CLIENT_ID, - clientSecret: process.env.AUTH_CLIENT_SECRET, - }, - ], - secret: process.env.AUTH_SECRET, - session: { - // Use JSON Web Tokens for session instead of database sessions. - // This option can be used with or without a database for users/accounts. - // Note: `jwt` is automatically set to `true` if no database is specified. - jwt: true, - - // Seconds - How long until an idle session expires and is no longer valid. - maxAge: 30 * 24 * 60 * 60, // 30 days - - // Seconds - Throttle how frequently to write to database to extend a session. - // Use it to limit write operations. Set to 0 to always update the database. - // Note: This option is ignored if using JSON Web Tokens - // updateAge: 24 * 60 * 60, // 24 hours - }, - jwt: { - // A secret to use for key generation - you should set this explicitly - // Defaults to NextAuth.js secret if not explicitly specified. - secret: process.env.AUTH_JWT_SECRET, - - // Set to true to use encryption. Defaults to false (signing only). - encryption: true, - - // You can define your own encode/decode functions for signing and encryption - // if you want to override the default behaviour. - // async encode({ secret, token, maxAge }) {}, - // async decode({ secret, token, maxAge }) {}, - }, - debug: process.env.NODE_ENV !== "production", - theme: "auto", - callbacks: { - // See https://next-auth.js.org/tutorials/refresh-token-rotation - /** - * This JSON Web Token callback is called whenever a JSON Web Token is - * created (i.e. at sign in) or updated (i.e whenever a session is accessed - * in the client). - * - * @param {object} token Decrypted JSON Web Token - * @param {object} user User object (only available on sign in) - * @param {object} account Provider account (only available on sign in) - * @param {object} profile Provider profile (only available on sign in) - * @param {boolean} isNewUser True if new user (only available on sign in) - * @return {object} JSON Web Token that will be saved - */ - async jwt( - token: ProviderJsonWebToken | StoredJsonWebToken, - user: User | undefined, - account: ProviderAccount | undefined, - _profile: ProviderProfile | undefined, - _isNewUser: boolean | undefined - ): Promise { - // Initial sign in - if (user && account) { - return { - accessToken: account.accessToken, - accessTokenExpires: Date.now() + account.expires_in * 1000, - refreshToken: "", // account.refreshToken, // TODO When `accessToken` and `refreshToken` are set, then signing in does not work, there just is no session after signing in. Why? I first thought that it is a problem with the cookie size but it's not because the problem persists when I switch to a database for session storage. - user, - }; - } - // Type Guard: https://basarat.gitbook.io/typescript/type-system/typeguard#in - if ("accessToken" in token) { - if (Date.now() < token.accessTokenExpires) { - // Return previous token if the access token has not expired yet - return token; - } - // Access token has expired, try to update it - return refreshAccessToken(token); - } - throw new Error("Impossible!"); - }, - - /** - * The session callback is called whenever a session is checked. By - * default, only a subset of the token is returned for increased security. - * If you want to make something available you added to the token through - * the jwt() callback, you have to explicitely forward it here to make it - * available to the client. - * - * @param {object} session Session object - * @param {object} token User object (if using database sessions) - * JSON Web Token (if not using database sessions) - * @return {object} Session that will be returned to the client - */ - async session( - _session: { - user: { name?: string; email?: string }; - accessToken?: string; - expires: number; - }, - token: SessionJsonWebToken - ): Promise { - if (token) { - return { - user: token.user, - accessToken: token.accessToken, - expires: token.accessTokenExpires, - error: token.error, - }; - } - return {}; - }, - - /** - * Use the signIn() callback to control if a user is allowed to sign in. - * - * @param {object} user User object - * @param {object} account Provider account - * @param {object} profile Provider profile - * @return {boolean|string} Return `true` to allow sign in - * Return `false` to deny access - * Return `string` to redirect to (eg.: "/unauthorized") - */ - // async signIn( - // user: User, - // account: ProviderAccount, - // profile: ProviderProfile - // ): Promise { - // const isAllowedToSignIn = true; - // if (isAllowedToSignIn) { - // return true; - // } else { - // // Return false to display a default error message - // return false; - // // Or you can return a URL to redirect to: - // // return '/unauthorized' - // } - // }, - - /** - * The redirect callback is called anytime the user is redirected to a callback URL (e.g. on signin or signout). - * - * By default only URLs on the same URL as the site are allowed, you can use the redirect callback to customise that behaviour. - * @param {string} url URL provided as callback URL by the client - * @param {string} baseUrl Default base URL of site (can be used as fallback) - * @return {string} URL the client will be redirect to - */ - // async redirect(url: string, baseUrl: string) { - // return url.startsWith(baseUrl) ? url : baseUrl; - // }, - }, - // pages: { - // signIn: '/auth/signin', - // signOut: '/auth/signout', - // error: '/auth/error', // Error code passed in query string as ?error= - // verifyRequest: '/auth/verify-request', // (used for check email message) - // newUser: null // If set, new users will be directed here on first sign in - // }, - // events: { - // async signIn(message) { /* on successful sign in */ }, - // async signOut(message) { /* on signout */ }, - // async createUser(message) { /* user created */ }, - // async linkAccount(message) { /* account linked to a user */ }, - // async session(message) { /* session is active */ }, - // async error(message) { /* error in authentication flow */ }, - // }, - // logger: { - // error(code, ...message) { log.error(code, message) }, - // warn(code, ...message) { log.warn(code, message) }, - // debug(code, ...message) { log.debug(code, message) }, - // }, -}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f348657e..6dda954c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -790,7 +790,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.7": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== @@ -1871,11 +1871,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@panva/hkdf@^1.0.2": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.4.tgz#4e02bb248402ff6c5c024e23a68438e2b0e69d67" - integrity sha512-003xWiCuvePbLaPHT+CRuaV4GlyCAVm6XYSbBZDHoWZGn1mNkVKFaDbGJjjxmEFvizUwlCoM6O18FCBMMky2zQ== - "@parcel/watcher@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" @@ -3109,11 +3104,6 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookie@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - copy-to-clipboard@^3.2.0: version "3.3.3" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" @@ -5102,7 +5092,7 @@ jiti@^1.17.1: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== -jose@^4.10.0, jose@^4.11.4: +jose@^4.11.4: version "4.13.1" resolved "https://registry.yarnpkg.com/jose/-/jose-4.13.1.tgz#449111bb5ab171db85c03f1bd2cb1647ca06db1c" integrity sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ== @@ -5484,21 +5474,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next-auth@^4.18.8: - version "4.22.0" - resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.22.0.tgz#325982b8efaaa55f505ee104a382c3118fec9d96" - integrity sha512-08+kjnDoE7aQ52O996x6cwA3ffc2CbHIkrCgLYhbE+aDIJBKI0oA9UbIEIe19/+ODYJgpAHHOtJx4izmsgaVag== - dependencies: - "@babel/runtime" "^7.20.13" - "@panva/hkdf" "^1.0.2" - cookie "^0.5.0" - jose "^4.11.4" - oauth "^0.9.15" - openid-client "^5.4.0" - preact "^10.6.3" - preact-render-to-string "^5.1.19" - uuid "^8.3.2" - next@^13.1.4: version "13.3.0" resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" @@ -5610,21 +5585,11 @@ nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== -oauth@^0.9.15: - version "0.9.15" - resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" - integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== - object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-hash@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" - integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== - object-inspect@^1.12.3, object-inspect@^1.9.0: version "1.12.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" @@ -5688,11 +5653,6 @@ object.values@^1.1.6: define-properties "^1.1.4" es-abstract "^1.20.4" -oidc-token-hash@^5.0.1: - version "5.0.2" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.2.tgz#f9ca7f7f1f92d721a2973e66b7430cb52a486648" - integrity sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ== - omit.js@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f" @@ -5721,16 +5681,6 @@ open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -openid-client@^5.4.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.0.tgz#77f1cda14e2911446f16ea3f455fc7c405103eac" - integrity sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ== - dependencies: - jose "^4.10.0" - lru-cache "^6.0.0" - object-hash "^2.0.1" - oidc-token-hash "^5.0.1" - optimism@^0.16.2: version "0.16.2" resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" @@ -5947,18 +5897,6 @@ postcss@8.4.14: picocolors "^1.0.0" source-map-js "^1.0.2" -preact-render-to-string@^5.1.19: - version "5.2.6" - resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz#0ff0c86cd118d30affb825193f18e92bd59d0604" - integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw== - dependencies: - pretty-format "^3.8.0" - -preact@^10.6.3: - version "10.13.2" - resolved "https://registry.yarnpkg.com/preact/-/preact-10.13.2.tgz#2c40c73d57248b57234c4ae6cd9ab9d8186ebc0a" - integrity sha512-q44QFLhOhty2Bd0Y46fnYW0gD/cbVM9dUVtNTDKPcdXSMA7jfY+Jpd6rk3GB0lcQss0z5s/6CmVP0Z/hV+g6pw== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5978,11 +5916,6 @@ pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" - integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== - promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -7409,11 +7342,6 @@ util-extend@^1.0.1: resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From e58a982bfc95c863cf0f993981a3f2c6dbb15594 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 16:43:02 +0200 Subject: [PATCH 102/878] Seal records --- backend/src/GraphQl/AppliedMethodInput.cs | 2 +- .../GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs | 2 +- backend/src/GraphQl/CielabColorInput.cs | 2 +- backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs | 2 +- backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs | 2 +- backend/src/GraphQl/DataApprovalInput.cs | 2 +- backend/src/GraphQl/Databases/UpdateDatabaseInput.cs | 2 +- backend/src/GraphQl/FileMetaInformationInput.cs | 2 +- .../GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs | 2 +- .../GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs | 2 +- backend/src/GraphQl/NamedMethodArgumentInput.cs | 2 +- backend/src/GraphQl/NamedMethodSourceInput.cs | 2 +- backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs | 2 +- .../GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs | 2 +- backend/src/GraphQl/RootGetHttpsResourceInput.cs | 2 +- backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/src/GraphQl/AppliedMethodInput.cs b/backend/src/GraphQl/AppliedMethodInput.cs index 2104e04a..34c58d4e 100644 --- a/backend/src/GraphQl/AppliedMethodInput.cs +++ b/backend/src/GraphQl/AppliedMethodInput.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl { - public record AppliedMethodInput( + public sealed record AppliedMethodInput( Guid MethodId, IReadOnlyList Arguments, IReadOnlyList Sources diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index ca76e285..5de111aa 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.CalorimetricDataX { - public record CreateCalorimetricDataInput( + public sealed record CreateCalorimetricDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, diff --git a/backend/src/GraphQl/CielabColorInput.cs b/backend/src/GraphQl/CielabColorInput.cs index b4e05998..93bafb4c 100644 --- a/backend/src/GraphQl/CielabColorInput.cs +++ b/backend/src/GraphQl/CielabColorInput.cs @@ -1,6 +1,6 @@ namespace Database.GraphQl { - public record CielabColorInput( + public sealed record CielabColorInput( double LStar, double AStar, double BStar diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs index c7b0e746..2f22341e 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.Common { - public record OpenEndedDateTimeRangeInput( + public sealed record OpenEndedDateTimeRangeInput( DateTime? From, DateTime? Until ); diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 22e47a27..4f986eb9 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl { - public record CrossDatabaseDataReferenceInput( + public sealed record CrossDatabaseDataReferenceInput( Guid DataId, DateTime DataTimestamp, DataKind DataKind, diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs index 4d6f8a92..f8110e45 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovalInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl { - public record DataApprovalInput( + public sealed record DataApprovalInput( DateTime Timestamp, string Signature, string KeyFingerprint, diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs index 111fe98a..246041a1 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.Databases { - public record UpdateDatabaseInput( + public sealed record UpdateDatabaseInput( Guid DatabaseId, string Name, string Description, diff --git a/backend/src/GraphQl/FileMetaInformationInput.cs b/backend/src/GraphQl/FileMetaInformationInput.cs index 4957734f..80633e80 100644 --- a/backend/src/GraphQl/FileMetaInformationInput.cs +++ b/backend/src/GraphQl/FileMetaInformationInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl { - public record FileMetaInformationInput( + public sealed record FileMetaInformationInput( string[] Path, Guid DataFormatId ); diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs index 654b64ce..96b2b166 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.GetHttpsResources { - public record CreateGetHttpsResourceInput( + public sealed record CreateGetHttpsResourceInput( string AccessToken, string Description, string HashValue, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index a50c4a2f..ef980885 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.HygrothermalDataX { - public record CreateHygrothermalDataInput( + public sealed record CreateHygrothermalDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, diff --git a/backend/src/GraphQl/NamedMethodArgumentInput.cs b/backend/src/GraphQl/NamedMethodArgumentInput.cs index a3ae1af7..bb97c46b 100644 --- a/backend/src/GraphQl/NamedMethodArgumentInput.cs +++ b/backend/src/GraphQl/NamedMethodArgumentInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl { - public record NamedMethodArgumentInput( + public sealed record NamedMethodArgumentInput( string Name, JsonElement Value ); diff --git a/backend/src/GraphQl/NamedMethodSourceInput.cs b/backend/src/GraphQl/NamedMethodSourceInput.cs index bc802f1d..30ba9faf 100644 --- a/backend/src/GraphQl/NamedMethodSourceInput.cs +++ b/backend/src/GraphQl/NamedMethodSourceInput.cs @@ -1,6 +1,6 @@ namespace Database.GraphQl { - public record NamedMethodSourceInput( + public sealed record NamedMethodSourceInput( string Name, CrossDatabaseDataReferenceInput Value ); diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index c0525343..41fb8586 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.OpticalDataX { - public record CreateOpticalDataInput( + public sealed record CreateOpticalDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index 8ae7499f..da1322c4 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.PhotovoltaicDataX { - public record CreatePhotovoltaicDataInput( + public sealed record CreatePhotovoltaicDataInput( string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 5075fc5c..808c3e3c 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl { - public record RootGetHttpsResourceInput( + public sealed record RootGetHttpsResourceInput( string Description, string HashValue, Guid DataFormatId, diff --git a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs index 891040ea..d6ed0886 100644 --- a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs +++ b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl { - public record ToTreeVertexAppliedConversionMethodInput( + public sealed record ToTreeVertexAppliedConversionMethodInput( Guid MethodId, IReadOnlyList Arguments, string SourceName From 002bb0a2e5ba08adb26337fddce9eaf673fa77d3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 28 Apr 2023 18:21:28 +0200 Subject: [PATCH 103/878] Extract metabase REST and GraphQl querying into common class and improve error reporting --- .../GraphQl/Databases/DatabaseMutations.cs | 4 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 127 ++++++++----- .../src/GraphQl/Databases/QueryingMetabase.cs | 119 ------------ backend/src/GraphQl/Users/UserQueries.cs | 66 +------ .../Queries/Databases.graphql | 0 .../Queries/UpdateDatabase.graphql | 0 backend/src/Metabase/QueryingMetabase.cs | 179 ++++++++++++++++++ 7 files changed, 263 insertions(+), 232 deletions(-) delete mode 100644 backend/src/GraphQl/Databases/QueryingMetabase.cs rename backend/src/{GraphQl/Databases => Metabase}/Queries/Databases.graphql (100%) rename backend/src/{GraphQl/Databases => Metabase}/Queries/UpdateDatabase.graphql (100%) create mode 100644 backend/src/Metabase/QueryingMetabase.cs diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 314a0181..0b35b505 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -19,10 +19,10 @@ public async Task UpdateDatabaseAsync( CancellationToken cancellationToken ) { - return (await QueryingMetabase.QueryMetabase( + return (await Metabase.QueryingMetabase.QueryGraphQl( appSettings, new GraphQL.GraphQLRequest( - query: await QueryingMetabase.ConstructQuery( + query: await Metabase.QueryingMetabase.ConstructGraphQlQuery( new[] { "UpdateDatabase.graphql" } diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index c8b18bc0..f9da5124 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,9 +1,12 @@ using System; +using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using HotChocolate; using HotChocolate.Execution; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -21,72 +24,102 @@ public async Task GetDatabaseAsync( [Service] AppSettings appSettings, [Service] IHttpClientFactory httpClientFactory, [Service] IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, CancellationToken cancellationToken ) { - var response = - await QueryingMetabase.QueryMetabase( - appSettings, - new GraphQL.GraphQLRequest( - query: await QueryingMetabase.ConstructQuery( - new[] { - "Databases.graphql" - } - ).ConfigureAwait(false), - variables: new - { - where = new + try + { + var response = + await Metabase.QueryingMetabase.QueryGraphQl( + appSettings, + new GraphQL.GraphQLRequest( + query: await Metabase.QueryingMetabase.ConstructGraphQlQuery( + new[] { + "Databases.graphql" + } + ).ConfigureAwait(false), + variables: new { - locator = new + where = new { - // TODO This is error-prone. - eq = new Uri(new Uri(appSettings.Host), "/graphql") + locator = new + { + // TODO This is error-prone. + eq = new Uri(new Uri(appSettings.Host), "/graphql") + } } - } - }, - operationName: "Databases" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - if (response is null) - { - throw new QueryException( - ErrorBuilder.New() - .SetCode("NULL_RESPONSE") - .SetMessage("Response is null.") - .Build() - ); - } - if (response.Data.Databases.Nodes is null) - { - throw new QueryException( - ErrorBuilder.New() - .SetCode("NULL_NODES") - .SetMessage("The supposed list of databases is null.") - .Build() - ); + }, + operationName: "Databases" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + if (response is null) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NULL_RESPONSE") + .SetPath(resolverContext.Path) + .SetMessage("Response is null.") + .Build() + ); + } + if (response.Data.Databases.Nodes is null) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NULL_NODES") + .SetPath(resolverContext.Path) + .SetMessage("The supposed list of databases is null.") + .Build() + ); + } + if (response.Data.Databases.Nodes.Count == 0) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("NO_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases is empty.") + .Build() + ); + } + if (response.Data.Databases.Nodes.Count >= 2) + { + throw new QueryException( + ErrorBuilder.New() + .SetCode("AMBIGUOUS_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases has more than one entry.") + .Build() + ); + } + return response.Data.Databases.Nodes[0]; } - if (response.Data.Databases.Nodes.Count == 0) + catch (HttpRequestException e) { throw new QueryException( ErrorBuilder.New() - .SetCode("NO_DATABASE") - .SetMessage("The list of databases is empty.") + .SetCode("METABASE_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") + .SetException(e) .Build() ); } - if (response.Data.Databases.Nodes.Count >= 2) + catch (JsonException e) { throw new QueryException( ErrorBuilder.New() - .SetCode("AMBIGUOUS_DATABASE") - .SetMessage("The list of databases has more than one entry.") + .SetCode("JSON_DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()).ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage($"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetException(e) .Build() ); } - return response.Data.Databases.Nodes[0]; } } } diff --git a/backend/src/GraphQl/Databases/QueryingMetabase.cs b/backend/src/GraphQl/Databases/QueryingMetabase.cs deleted file mode 100644 index 9d0f8cd2..00000000 --- a/backend/src/GraphQl/Databases/QueryingMetabase.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; -using GraphQL.Client.Serializer.SystemTextJson; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Http; -using OpenIddict.Client.AspNetCore; - -namespace Database.GraphQl.Databases -{ - public sealed class QueryingMetabase - { - private static readonly JsonSerializerOptions SerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); - - public static async Task ConstructQuery( - string[] fileNames - ) - { - return string.Join( - Environment.NewLine, - await Task.WhenAll( - fileNames.Select(fileName => - File.ReadAllTextAsync($"GraphQl/Databases/Queries/{fileName}") - ) - ).ConfigureAwait(false) - ); - } - - public static async - Task> - QueryMetabase( - AppSettings appSettings, - GraphQL.GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TGraphQlResponse : class - { - using var httpClient = httpClientFactory.CreateClient(); - string? bearerToken = null; - if (httpContextAccessor.HttpContext is not null) - { - bearerToken = await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken - ).ConfigureAwait(false); - } - // For some reason `httpClient.PostAsJsonAsync` without `MakeJsonHttpContent` but with `SerializerOptions` results in `BadRequest` status code. It has to do with `JsonContent.Create` used within `PostAsJsonAsync` --- we also cannot use `JsonContent.Create` in `MakeJsonHttpContent`. What is happening here? - using var jsonHttpContent = MakeJsonHttpContent(request); - using var httpRequestMessage = new HttpRequestMessage( - HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(appSettings.MetabaseHost), "/graphql/") - ); - httpRequestMessage.Content = jsonHttpContent; - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); - using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); - if (httpResponseMessage.StatusCode != HttpStatusCode.OK) - { - throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); - } - // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ - using var graphQlResponseStream = - await httpResponseMessage.Content - .ReadAsStreamAsync(cancellationToken) - .ConfigureAwait(false); - // Console.WriteLine(new StreamReader(graphQlResponseStream).ReadToEnd()); - var deserializedGraphQlResponse = - await JsonSerializer.DeserializeAsync>( - graphQlResponseStream, - SerializerOptions, - cancellationToken - ).ConfigureAwait(false); - if (deserializedGraphQlResponse is null) - { - throw new JsonException("Failed to deserialize the GraphQL response."); - } - return deserializedGraphQlResponse; - } - - private static HttpContent MakeJsonHttpContent( - TContent content - ) - { - // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. - var result = - new ByteArrayContent( - JsonSerializer.SerializeToUtf8Bytes( - content, - SerializerOptions - ) - ); - result.Headers.ContentType = - new MediaTypeHeaderValue("application/json"); - return result; - } - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index b496448e..214f90fb 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -54,7 +54,7 @@ CancellationToken cancellationToken var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); try { - return await Query( + return await Metabase.QueryingMetabase.QueryRest( uri, httpClientFactory, httpContextAccessor, @@ -77,7 +77,7 @@ CancellationToken cancellationToken { resolverContext.ReportError( ErrorBuilder.New() - .SetCode("DESERIALIZATION_FAILED") + .SetCode("JSON_DESERIALIZATION_FAILED") .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()).ToList()) // TODO Splitting the path at '.' is wrong in general. .SetMessage($"Failed to deserialize GraphQL response of request to {uri}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") .SetException(e) @@ -86,67 +86,5 @@ CancellationToken cancellationToken return null; } } - - private static readonly JsonSerializerOptions SerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). - PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); - - private static async Task Query( - [Service] Uri uri, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TResponse : class - { - using var httpClient = httpClientFactory.CreateClient(); - string? bearerToken = null; - if (httpContextAccessor.HttpContext is not null) - { - bearerToken = await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken - ).ConfigureAwait(false); - } - using var httpRequestMessage = new HttpRequestMessage( - HttpMethod.Get, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - uri - ); - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); - using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); - if (httpResponseMessage.StatusCode != HttpStatusCode.OK) - { - throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); - } - // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ - using var responseStream = - await httpResponseMessage.Content - .ReadAsStreamAsync(cancellationToken) - .ConfigureAwait(false); - // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); - var deserializedResponse = - await JsonSerializer.DeserializeAsync( - responseStream, - SerializerOptions, - cancellationToken - ).ConfigureAwait(false); - if (deserializedResponse is null) - { - throw new JsonException("Failed to deserialize the GraphQL response."); - } - return deserializedResponse; - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/Queries/Databases.graphql b/backend/src/Metabase/Queries/Databases.graphql similarity index 100% rename from backend/src/GraphQl/Databases/Queries/Databases.graphql rename to backend/src/Metabase/Queries/Databases.graphql diff --git a/backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql b/backend/src/Metabase/Queries/UpdateDatabase.graphql similarity index 100% rename from backend/src/GraphQl/Databases/Queries/UpdateDatabase.graphql rename to backend/src/Metabase/Queries/UpdateDatabase.graphql diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs new file mode 100644 index 00000000..5d5f2770 --- /dev/null +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -0,0 +1,179 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using GraphQL.Client.Serializer.SystemTextJson; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using OpenIddict.Client.AspNetCore; +using Yoh.Text.Json.NamingPolicies; + +namespace Database.Metabase +{ + public sealed class QueryingMetabase + { + private static async Task ExtractBearerToken( + IHttpContextAccessor httpContextAccessor + ) + { + if (httpContextAccessor.HttpContext is null) + { + return null; + } + return await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken + ).ConfigureAwait(false); + } + + private static readonly JsonSerializerOptions GraphQlSerializerOptions = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); + + public static async Task ConstructGraphQlQuery( + string[] fileNames + ) + { + return string.Join( + Environment.NewLine, + await Task.WhenAll( + fileNames.Select(fileName => + File.ReadAllTextAsync($"Metabase/Queries/{fileName}") + ) + ).ConfigureAwait(false) + ); + } + + public static Task> QueryGraphQl( + AppSettings appSettings, + GraphQL.GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class + { + return Query>( + HttpMethod.Post, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(new Uri(appSettings.MetabaseHost), "/graphql/"), + MakeJsonHttpContent(request), + GraphQlSerializerOptions, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + + private static HttpContent MakeJsonHttpContent( + TContent content + ) + { + // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + GraphQlSerializerOptions + ) + ); + result.Headers.ContentType = + new MediaTypeHeaderValue("application/json"); + return result; + } + + private static readonly JsonSerializerOptions RestSerializerOptions = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). + PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); + + public static Task QueryRest( + Uri uri, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { + return Query( + HttpMethod.Get, + uri, + null, + RestSerializerOptions, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + + private static async Task Query( + HttpMethod httpMethod, + Uri uri, + HttpContent? httpContent, + JsonSerializerOptions serializerOptions, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { + using var httpClient = httpClientFactory.CreateClient(); + var bearerToken = await ExtractBearerToken(httpContextAccessor).ConfigureAwait(false); + using var httpRequestMessage = new HttpRequestMessage( + httpMethod, + uri + ); + httpRequestMessage.Content = httpContent; + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { + throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); + } + // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ + using var responseStream = + await httpResponseMessage.Content + .ReadAsStreamAsync(cancellationToken) + .ConfigureAwait(false); + // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); + var deserializedResponse = + await JsonSerializer.DeserializeAsync( + responseStream, + serializerOptions, + cancellationToken + ).ConfigureAwait(false); + if (deserializedResponse is null) + { + throw new JsonException("Failed to deserialize the GraphQL response."); + } + return deserializedResponse; + } + } +} \ No newline at end of file From f0cd39fadfee406fe55c36ead3d8f5e0505152f4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 May 2023 14:34:18 +0200 Subject: [PATCH 104/878] Use `LocalRedirect` as an additional security measure to prevent open-redirect attacks (because `SanitizeReturnUrl` uses `IsLocalUrl` to sanitize the URL this is actually not necessary --- but better safe than sorry; for details see https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects?view=aspnetcore-5.0) --- backend/src/Controllers/AuthenticationControllers.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index 355eb394..59535af5 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -60,7 +60,8 @@ public async Task LogOut(string? returnUrl) if (result is not { Succeeded: true }) { // Only allow local return URLs to prevent open redirect attacks. - return Redirect(SanitizeReturnUrl(returnUrl)); + // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects + return LocalRedirect(SanitizeReturnUrl(returnUrl)); } // Remove the local authentication cookie before triggering a redirection to the remote server. await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); @@ -229,7 +230,10 @@ public async Task LogOutCallback() // In this sample, the local authentication cookie is always removed before the user agent is redirected // to the authorization server. Applications that prefer delaying the removal of the local cookie can // remove the corresponding code from the logout action and remove the authentication cookie in this action. - return Redirect( + // + // Only allow local return URLs to prevent open redirect attacks. + // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects + return LocalRedirect( SanitizeReturnUrl(result.Properties?.RedirectUri) ); } From fe029c5f344cc9d4632150d4efc919fe50331f5d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 May 2023 16:32:51 +0200 Subject: [PATCH 105/878] Allow CORS only for GraphQL endpoint --- backend/src/Startup.cs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index d9b3d365..8a0ecb1c 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -24,6 +24,8 @@ namespace Database { public sealed class Startup { + private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; + private readonly IWebHostEnvironment _environment; private readonly AppSettings _appSettings; @@ -44,6 +46,7 @@ public void ConfigureServices(IServiceCollection services) ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); ConfigureSessionServices(services); + services.AddAntiforgery(_ => _.HeaderName = "X-XSRF-TOKEN"); services .AddDataProtection() .PersistKeysToDbContext(); @@ -70,11 +73,13 @@ private static void ConfigureRequestResponseServices(IServiceCollection services } ); services.AddCors(_ => - _.AddDefaultPolicy(policy => - policy - .AllowAnyOrigin() - .AllowAnyHeader() - .AllowAnyMethod() + _.AddPolicy( + name: GraphQlCorsPolicy, + policy => + policy + .AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod() ) ); services.AddControllersWithViews(); @@ -170,7 +175,7 @@ public void Configure(IApplicationBuilder app) // app.UseHttpsRedirection(); // Done by NGINX app.UseSerilogRequestLogging(); app.UseStaticFiles(); - app.UseCookiePolicy(); + app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) app.UseRouting(); // TODO Do we really want this? See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-5.0 app.UseRequestLocalization(_ => @@ -189,7 +194,8 @@ public void Configure(IApplicationBuilder app) /* app.UseWebSockets(); */ app.UseEndpoints(_ => { - _.MapGraphQL().WithOptions( + _.MapGraphQL() + .WithOptions( // https://chillicream.com/docs/hotchocolate/server/middleware new GraphQLServerOptions { @@ -206,7 +212,8 @@ public void Configure(IApplicationBuilder app) Title = "GraphQL" } } - ); + ) + .RequireCors(GraphQlCorsPolicy); _.MapControllers(); _.MapHealthChecks("/health", new HealthCheckOptions From c04cf1af2b3ab55205e430753b92ff7e2de9466b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 May 2023 15:11:01 +0200 Subject: [PATCH 106/878] Fix spelling error --- backend/src/Configuration/AuthConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 0dc3d93b..fc1f8f7c 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -28,7 +28,7 @@ AppSettings appSettings { var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); - ConfigureAuthenticiationAndAuthorizationServices(services); + ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); ConfigureOpenIddictServices(services, appSettings, encryptionCertificate, signingCertificate); } @@ -54,7 +54,7 @@ string password ); } - private static void ConfigureAuthenticiationAndAuthorizationServices( + private static void ConfigureAuthenticationAndAuthorizationServices( IServiceCollection services ) { From 452d466ec70be7557bce87275e8ec89325a64a87 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 25 May 2023 11:50:54 +0200 Subject: [PATCH 107/878] Make auth schemes explicit by setting default schemes to null and support bearer token authentication --- .../src/Configuration/AuthConfiguration.cs | 36 ++++++++++++++++-- .../src/Configuration/GraphQlConfiguration.cs | 37 ++++++++++++++++--- 2 files changed, 64 insertions(+), 9 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index fc1f8f7c..4809e1b7 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Hosting; using OpenIddict.Client; using Microsoft.Extensions.Hosting; @@ -17,6 +16,7 @@ namespace Database.Configuration // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs public abstract class AuthConfiguration { + public const string ClientId = "testlab-solar-facades"; public static string ReadApiScope { get; } = "api:read"; public static string WriteApiScope { get; } = "api:write"; @@ -63,7 +63,18 @@ IServiceCollection services AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", isEnabled: true); // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs services - .AddAuthentication() + .AddAuthentication(_ => + { + // To make the various authentication control flows obvious, do + // not use default schemes for anything and always be explicit + // instead. + _.DefaultAuthenticateScheme = null; + _.DefaultChallengeScheme = null; + _.DefaultForbidScheme = null; + _.DefaultScheme = null; + _.DefaultSignInScheme = null; + _.DefaultSignOutScheme = null; + }) .AddCookie(_ => { _.AccessDeniedPath = "/unauthorized"; @@ -124,6 +135,25 @@ X509Certificate2 signingCertificate // Enable Quartz.NET integration. _.UseQuartz(); }) + .AddValidation(_ => + { + // The validation handler uses OpenID Connect discovery to + // retrieve the issuer signing keys used to validate tokens. + _.SetIssuer(new Uri(appSettings.MetabaseHost, UriKind.Absolute)); + // Configure the audience accepted by this resource server. + _.AddAudiences(ClientId); + // Configure the validation handler to use introspection and + // register the client credentials used when communicating with + // the remote introspection endpoint. + // https://www.oauth.com/oauth2-servers/token-introspection-endpoint/ + _.UseIntrospection() + .SetClientId(ClientId) + .SetClientSecret(appSettings.OpenIdConnectClientSecret); + // Register the ASP.NET Core host. + _.UseAspNetCore(); + // Register the System.Net.Http integration. + _.UseSystemNetHttp(); + }) .AddClient(_ => { _.AllowAuthorizationCodeFlow(); @@ -154,7 +184,7 @@ X509Certificate2 signingCertificate // Note: these settings must match the application details // inserted in the database at the server level. - ClientId = "testlab-solar-facades", + ClientId = ClientId, ClientSecret = appSettings.OpenIdConnectClientSecret, // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 8739e7f1..941512c5 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Database.GraphQl.Filters; using System.Net.Http; +using OpenIddict.Validation.AspNetCore; namespace Database.Configuration { @@ -93,13 +94,37 @@ AppSettings appSettings /* .UsePersistedQueryPipeline(); */ .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => { - // HotChocolate uses the default cookie authentication - // scheme `IdentityConstants.ApplicationScheme` by - // default. - var authenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); - if (authenticateResult.Succeeded && authenticateResult.Principal is not null) + try { + // HotChocolate uses the default authentication scheme, which we + // set to `null` in `AuthConfiguration` to force users to be + // explicit about what scheme to use when making it easier to + // grasp the various authentication flows. For the Web frontend, + // the database acts as OpenId Connect Client of the metabase + // and uses the cookie scheme for authentication. For + // third-party frontends, the database acts as resource server + // and uses authorization header bearer tokens for + // authentication, that is JavaScript Web Tokens (JWT), aka, + // Access Tokens, provided as `Authorization` HTTP header with + // the prefix `Bearer` as issued by OpenIddict. This Access + // Token includes Scopes and Claims. + var cookieAuthenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) + { + httpContext.User = cookieAuthenticateResult.Principal; + } + else + { + var jwtAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); + if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) + { + httpContext.User = jwtAuthenticateResult.Principal; + } + } + } + catch (Exception e) { - httpContext.User = authenticateResult.Principal; + // TODO Log to a `ILogger` instead. + Console.WriteLine(e); } }) .AddDiagnosticEventListener(_ => From 8983c4ebf23677609793a357cefeb4a38bd17de8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 25 May 2023 12:55:32 +0200 Subject: [PATCH 108/878] Add antiforgery token support --- .../src/Controllers/AntiforgeryController.cs | 35 +++++++++++++++++++ .../Controllers/AuthenticationControllers.cs | 2 +- backend/src/Startup.cs | 5 ++- frontend/components/Layout.tsx | 13 ++++++- frontend/components/NavBar.tsx | 20 ++++++++--- frontend/lib/apollo.ts | 35 +++++++++++++++---- frontend/paths.ts | 1 + .../development/default.conf.template | 5 +++ .../production/default.conf.template | 6 ++++ 9 files changed, 108 insertions(+), 14 deletions(-) create mode 100644 backend/src/Controllers/AntiforgeryController.cs diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs new file mode 100644 index 00000000..4e958140 --- /dev/null +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Database.Controllers +{ + public sealed class AntiforgeryController : Controller + { + private const string CookieKey = "XSRF-TOKEN"; + + private readonly IAntiforgery _antiforgeryService; + + public AntiforgeryController(IAntiforgery antiforgeryService) + { + _antiforgeryService = antiforgeryService; + } + + [HttpGet("~/antiforgery/token")] + // [Authorize] + public IActionResult Token() + { + var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); + HttpContext.Response.Cookies.Append( + CookieKey, + tokens.RequestToken ?? throw new InvalidOperationException("The request token supposed to be generated by the antiforgery service is null."), + new CookieOptions + { + HttpOnly = false + } + ); + return new StatusCodeResult(StatusCodes.Status200OK); + } + } +} \ No newline at end of file diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index 59535af5..e6c2227e 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -51,7 +51,7 @@ public ActionResult LogIn(string? returnUrl) } [HttpPost("~/connect/logout")] - // TODO Add `[ValidateAntiForgeryToken]`. For details see https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0#javascript-ajax-and-spas + [ValidateAntiForgeryToken] public async Task LogOut(string? returnUrl) { // Retrieve the identity stored in the local authentication cookie. If it's not available, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8a0ecb1c..5ec6b6a7 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -46,7 +46,10 @@ public void ConfigureServices(IServiceCollection services) ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); ConfigureSessionServices(services); - services.AddAntiforgery(_ => _.HeaderName = "X-XSRF-TOKEN"); + services.AddAntiforgery(_ => + { + _.HeaderName = "X-XSRF-TOKEN"; + }); services .AddDataProtection() .PersistKeysToDbContext(); diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index cb8c2a98..213be874 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -1,5 +1,5 @@ import Head from "next/head"; -import { ReactNode, useEffect } from "react"; +import { ReactNode, useEffect, useState } from "react"; import Footer from "./Footer"; import NavBar from "./NavBar"; import { Modal, Layout as AntLayout, Typography } from "antd"; @@ -50,6 +50,7 @@ export default function Layout({ children }: LayoutProps) { const [cookies, setCookie] = useCookies([cookieConsentName]); const shouldShowCookieConsent = cookies[cookieConsentName] != cookieConsentValue; + const [loadedAntiforgeryToken, setLoadedAntiforgeryToken] = useState(false); useEffect(() => { if (shouldShowCookieConsent) { @@ -70,6 +71,16 @@ export default function Layout({ children }: LayoutProps) { } }, [shouldShowCookieConsent, setCookie]); + useEffect(() => { + fetch(paths.antiforgeryToken).then((_) => { + setLoadedAntiforgeryToken(true); + }); + }, []); + + if (!loadedAntiforgeryToken) { + return null; + } + return ( diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 08add2c4..4afb82ff 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -5,6 +5,7 @@ import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; import { useCurrentUserQuery } from "../queries/currentUser.graphql"; +import { getXsrfToken } from "../lib/apollo"; type NavItemProps = { path: string; @@ -47,18 +48,27 @@ export default function NavBar({ items }: NavBarProps) { title={currentUser ? currentUser.name : null} icon={} > + {moderatorItems.map(({ path, label }) => ( + + {label} + + ))}
+
- {moderatorItems.map(({ path, label }) => ( - - {label} - - ))} ) : ( diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index b1da91ae..dddb6dcd 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -20,7 +20,35 @@ export type ResolverContext = { res?: ServerResponse; }; +function getCookie(name: string, cookies: string): string | null { + return ( + cookies + .split(";") + .map((cookie) => cookie.trim()) + .filter((cookie) => { + return cookie.substring(0, name.length + 1) === `${name}=`; + }) + .map((cookie) => { + return decodeURIComponent(cookie.substring(name.length + 1)); + })[0] || null + ); +} + +export function getXsrfToken(): string | null { + return getCookie("XSRF-TOKEN", document.cookie); +} + function createIsomorphLink(context: ResolverContext = {}) { + var headers: Record = {}; + headers["accept"] = "application/json"; + var cookie = context.req?.headers?.cookie; + if (cookie) { + headers["cookie"] = cookie; + } + if (typeof window !== "undefined") { + var antiforgeryToken = getXsrfToken(); + if (antiforgeryToken) headers["X-XSRF-TOKEN"] = antiforgeryToken; + } return createPersistedQueryLink({ sha256, useGETForHashedQueries: false, @@ -40,12 +68,7 @@ function createIsomorphLink(context: ResolverContext = {}) { : `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/`, useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", - headers: context.req?.headers?.cookie - ? { - accept: "application/json", - cookie: context.req.headers.cookie, - } - : { accept: "application/json" }, + headers: headers, }) ); } diff --git a/frontend/paths.ts b/frontend/paths.ts index 67cfabc6..50c4af48 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -2,6 +2,7 @@ export default { home: "/", legalNotice: "/legal-notice", dataProtectionInformation: "/data-protection-information", + antiforgeryToken: "/antiforgery/token", userInfo: "/user-info", database: "/database", data: "/data", diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index c3c0a0d4..fcff280e 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -101,6 +101,11 @@ server { limit_req zone=one burst=10 nodelay; } + location /antiforgery { + proxy_pass http://backend; + limit_req zone=one burst=10 nodelay; + } + location /api { proxy_pass http://backend; limit_req zone=one burst=10 nodelay; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index f0eb4186..ef13d2c0 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -84,6 +84,11 @@ server { limit_req zone=one burst=10 nodelay; } + location /antiforgery { + proxy_pass http://backend; + limit_req zone=one burst=10 nodelay; + } + location /api { proxy_pass http://backend; limit_req zone=one burst=10 nodelay; @@ -114,6 +119,7 @@ server { proxy_pass http://email; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; limit_req zone=one burst=20 nodelay; } From a0f72df6ad3f55452380e001f79e789127d68808 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 May 2023 16:18:30 +0200 Subject: [PATCH 109/878] Use the same logic for authenticating antiforgery token requests and GraphQL requests --- .../src/Configuration/GraphQlConfiguration.cs | 33 ++++--------------- .../src/Controllers/AntiforgeryController.cs | 20 ++++++----- 2 files changed, 19 insertions(+), 34 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 941512c5..3b7042ab 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -94,32 +94,13 @@ AppSettings appSettings /* .UsePersistedQueryPipeline(); */ .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => { - try { - // HotChocolate uses the default authentication scheme, which we - // set to `null` in `AuthConfiguration` to force users to be - // explicit about what scheme to use when making it easier to - // grasp the various authentication flows. For the Web frontend, - // the database acts as OpenId Connect Client of the metabase - // and uses the cookie scheme for authentication. For - // third-party frontends, the database acts as resource server - // and uses authorization header bearer tokens for - // authentication, that is JavaScript Web Tokens (JWT), aka, - // Access Tokens, provided as `Authorization` HTTP header with - // the prefix `Bearer` as issued by OpenIddict. This Access - // Token includes Scopes and Claims. - var cookieAuthenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); - if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) - { - httpContext.User = cookieAuthenticateResult.Principal; - } - else - { - var jwtAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); - if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) - { - httpContext.User = jwtAuthenticateResult.Principal; - } - } + // HotChocolate uses the default authentication scheme, which we + // set to `null` in `AuthConfiguration` to force users to be + // explicit about what scheme to use when making it easier to + // grasp the various authentication flows. + try + { + await HttpContextAuthentication.Authenticate(httpContext); } catch (Exception e) { diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 4e958140..93a810a0 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -1,4 +1,6 @@ using System; +using System.Threading.Tasks; +using Database.Configuration; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -7,7 +9,12 @@ namespace Database.Controllers { public sealed class AntiforgeryController : Controller { - private const string CookieKey = "XSRF-TOKEN"; + private const string XsrfCookieKey = "XSRF-TOKEN"; + private readonly CookieOptions XsrfCookieOptions = + new() + { + HttpOnly = false + }; private readonly IAntiforgery _antiforgeryService; @@ -17,17 +24,14 @@ public AntiforgeryController(IAntiforgery antiforgeryService) } [HttpGet("~/antiforgery/token")] - // [Authorize] - public IActionResult Token() + public async Task Token() { + await HttpContextAuthentication.Authenticate(HttpContext).ConfigureAwait(false); var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append( - CookieKey, + XsrfCookieKey, tokens.RequestToken ?? throw new InvalidOperationException("The request token supposed to be generated by the antiforgery service is null."), - new CookieOptions - { - HttpOnly = false - } + XsrfCookieOptions ); return new StatusCodeResult(StatusCodes.Status200OK); } From 9b755ef2ce2afa59b480b54e1cbe9f2f33383ff3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 May 2023 16:24:11 +0200 Subject: [PATCH 110/878] Authenticate user before validating antiforgery tokens (otherwise the token validation logic fails with the message that the token was issued for another user) --- .../HttpContextAuthentication.cs | 45 +++++++++++++++++++ .../Controllers/AuthenticationControllers.cs | 3 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 backend/src/Configuration/HttpContextAuthentication.cs diff --git a/backend/src/Configuration/HttpContextAuthentication.cs b/backend/src/Configuration/HttpContextAuthentication.cs new file mode 100644 index 00000000..4a58adbf --- /dev/null +++ b/backend/src/Configuration/HttpContextAuthentication.cs @@ -0,0 +1,45 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using OpenIddict.Validation.AspNetCore; + +namespace Database.Configuration +{ + public static class HttpContextAuthentication + { + public static async Task Authenticate( + HttpContext httpContext + ) + { + // For the Next.js Web frontend, the database acts as OpenId Connect + // Client and uses the cookie scheme for authentication. See + // `AuthConfiguration#ConfigureAuthenticationAndAuthorizationServices` + // for the configuration of the "cookie scheme" cookie. This cookie + // is set by methods in `AuthenticationController` and is related to + // `OpenIddictBuilder#AddClient` in + // `AuthConfiguration#ConfigureOpenIddictServices`. + var cookieAuthenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) + { + httpContext.User = cookieAuthenticateResult.Principal; + return cookieAuthenticateResult; + } + // For third-party frontends, the database acts as resource server + // and uses authorization-header bearer tokens for authentication, + // that is JavaScript Web Tokens (JWT), aka, Access Tokens, provided + // as `Authorization` HTTP header with the prefix `Bearer` as issued + // by OpenIddict. This Access Token includes Scopes and Claims. The + // scheme is configured in + // `AuthConfiguration#ConfigureOpenIddictServices` by + // `OpenIddictBuilder#AddValidation`. + var jwtAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); + if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) + { + httpContext.User = jwtAuthenticateResult.Principal; + return jwtAuthenticateResult; + } + return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); + } + } +} \ No newline at end of file diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index e6c2227e..341e3c25 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using OpenIddict.Client.AspNetCore; @@ -51,7 +52,7 @@ public ActionResult LogIn(string? returnUrl) } [HttpPost("~/connect/logout")] - [ValidateAntiForgeryToken] + [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme), ValidateAntiForgeryToken] public async Task LogOut(string? returnUrl) { // Retrieve the identity stored in the local authentication cookie. If it's not available, From 73522db004176fba147a363962a5293e3af6badc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 May 2023 16:42:32 +0200 Subject: [PATCH 111/878] Link to an issue with gotchas regarding antiforgery tokens in ASP.NET Core --- backend/src/Controllers/AntiforgeryController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 93a810a0..27df8253 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -7,6 +7,8 @@ namespace Database.Controllers { + // For gotchas regarding antiforgery tokens read + // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) public sealed class AntiforgeryController : Controller { private const string XsrfCookieKey = "XSRF-TOKEN"; From 05d5681574e1557494a60f4dafdd53d98388c6c9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 May 2023 17:38:56 +0200 Subject: [PATCH 112/878] Link pull request #46 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 089139b4..46d52d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) - Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) - Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) -- +- Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) - - - From 8f6f7c65c991e8d685c05b41e880f4564179a750 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 May 2023 17:43:18 +0200 Subject: [PATCH 113/878] Remove `GlobalState` attribute for `ClaimsPrincipal` parameters (it is not needed anymore according to https://chillicream.com/docs/hotchocolate/v13/security/authentication/#accessing-the-claimsprincipal) --- .../src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs | 2 +- .../src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs | 2 +- .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index dec3666e..fda8ab2b 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -17,7 +17,7 @@ public sealed class CalorimetricDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, Data.ApplicationDbContext context, [Service] AppSettings appSettings, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 72fffc71..d708c820 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -17,7 +17,7 @@ public sealed class GetHttpsResourceMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, Data.ApplicationDbContext context, [Service] AppSettings appSettings, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index df0f1e12..d1bb99c5 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -17,7 +17,7 @@ public sealed class HygrothermalDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, Data.ApplicationDbContext context, [Service] AppSettings appSettings, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 12413a6f..5fce9831 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -17,7 +17,7 @@ public sealed class OpticalDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, Data.ApplicationDbContext context, [Service] AppSettings appSettings, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 62ddd73c..5ba21504 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -17,7 +17,7 @@ public sealed class PhotovoltaicDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, - // [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, Data.ApplicationDbContext context, [Service] AppSettings appSettings, diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 214f90fb..dbfd7d1c 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -26,7 +26,7 @@ namespace Database.GraphQl.Users public sealed class UserQueries { public async Task GetCurrentUserAsync( - [GlobalState(nameof(ClaimsPrincipal))] ClaimsPrincipal claimsPrincipal, + ClaimsPrincipal claimsPrincipal, Data.ApplicationDbContext context, CancellationToken cancellationToken ) From 5184402b45a15523ddd63d1c1857986ee6b847a9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 31 May 2023 15:51:28 +0200 Subject: [PATCH 114/878] Migrate to the new minimal hosting model (see https://learn.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-7.0&tabs=visual-studio#use-startup-with-the-new-minimal-hosting-model and https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication?view=aspnetcore-7.0) --- backend/src/Program.cs | 163 ++++++++++++++--------------------------- backend/src/Startup.cs | 59 +++++++-------- 2 files changed, 81 insertions(+), 141 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 4e79c8ec..8a165c24 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -10,31 +10,41 @@ using Serilog; using Serilog.Events; using Serilog.Formatting.Compact; -using Microsoft.AspNetCore.Hosting.StaticWebAssets; +using Microsoft.AspNetCore.Builder; namespace Database { - public class Program + public sealed class Program { + public const string TestEnvironment = "test"; + public static async Task Main( string[] commandLineArguments ) { - var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? throw new Exception("Unknown enrivornment."); + var environment = + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") + ?? throw new Exception("Unknown enrivornment."); + // https://github.com/serilog/serilog-aspnetcore#two-stage-initialization ConfigureBootstrapLogging(environment); try { Log.Information("Starting web host"); - var host = CreateHostBuilder(commandLineArguments).Build(); - using var scope = host.Services.CreateScope(); - var services = scope.ServiceProvider; - var webHostEnvironment = services.GetRequiredService(); - if (webHostEnvironment.IsDevelopment()) + // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication + var builder = CreateWebApplicationBuilder(commandLineArguments); + var startup = new Startup(builder.Environment, builder.Configuration); + startup.ConfigureServices(builder.Services); + var application = builder.Build(); + startup.Configure(application); + using (var scope = application.Services.CreateScope()) { - // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDbIfNotExists(services).ConfigureAwait(false); + if (application.Environment.IsDevelopment()) + { + // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data + await CreateAndSeedDbIfNotExists(scope.ServiceProvider).ConfigureAwait(false); + } } - host.Run(); + application.Run(); return 0; } catch (Exception ex) @@ -72,17 +82,16 @@ string environment formatter: new CompactJsonFormatter(), path: "./logs/serilog.json", fileSizeLimitBytes: 1073741824, // 1 GB - retainedFileCountLimit: 7, rollingInterval: RollingInterval.Day, - rollOnFileSizeLimit: true - ); + rollOnFileSizeLimit: true, + retainedFileCountLimit: 7); if (environment != "production") { configuration.WriteTo.Debug(); } } - public static async Task CreateAndSeedDbIfNotExists( + private static async Task CreateAndSeedDbIfNotExists( IServiceProvider services ) { @@ -104,117 +113,51 @@ IServiceProvider services } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host - public static IHostBuilder CreateHostBuilder( + private static WebApplicationBuilder CreateWebApplicationBuilder( string[] commandLineArguments ) { - return new HostBuilder() - .UseContentRoot( - Directory.GetCurrentDirectory() - ) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host#host-configuration - .ConfigureHostConfiguration(configuration => + var builder = WebApplication.CreateBuilder( + new WebApplicationOptions { - configuration.AddEnvironmentVariables(prefix: "DOTNET_"); - configuration.AddCommandLine(commandLineArguments); + Args = commandLineArguments, + ContentRootPath = Directory.GetCurrentDirectory() } - ) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ - .ConfigureAppConfiguration((hostingContext, configuration) => - ConfigureAppConfiguration( - configuration, - hostingContext.HostingEnvironment, - commandLineArguments - ) - ) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/ - .ConfigureLogging((_, loggingBuilder) => - { - loggingBuilder.Configure(options => - { - options.ActivityTrackingOptions = ActivityTrackingOptions.SpanId - | ActivityTrackingOptions.TraceId - | ActivityTrackingOptions.ParentId; - }); - }) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection - .UseDefaultServiceProvider((context, options) => - { - var isDevelopment = context.HostingEnvironment.IsDevelopment(); - options.ValidateScopes = isDevelopment; - options.ValidateOnBuild = isDevelopment; - }) - .UseSerilog((webHostBuilderContext, loggerConfiguration) => - { - ConfigureLogging( - loggerConfiguration, - webHostBuilderContext.HostingEnvironment.EnvironmentName - ); - loggerConfiguration - .ReadFrom.Configuration(webHostBuilderContext.Configuration); - } - ) - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host - // https://github.com/dotnet/aspnetcore/blob/main/src/DefaultBuilder/src/WebHost.cs#L215 - .ConfigureWebHost(_ => - _ - .ConfigureAppConfiguration((context, _) => - { - if (context.HostingEnvironment.IsDevelopment()) - { - StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration); - } - }) - // Default web server https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel - .UseKestrel((context, options) => - options.Configure(context.Configuration.GetSection("Kestrel"), reloadOnChange: true) - ) - .ConfigureServices((context, services) => - { - // Fallback - // services.PostConfigure(options => - // { - // if (options.AllowedHosts == null || options.AllowedHosts.Count == 0) - // { - // // "AllowedHosts": "localhost;127.0.0.1;[::1]" - // var hosts = context.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - // // Fall back to "*" to disable. - // options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" }); - // } - // }); - // Change notification - // services.AddSingleton>( - // new ConfigurationChangeTokenSource(context.Configuration)); - // services.AddTransient(); - services.AddRouting(); - }) - // .UseIIS() - // .UseIISIntegration() - .UseStartup() ); - } - - public static void ConfigureAppConfiguration( - IConfigurationBuilder configuration, - IHostEnvironment environment, - string[] commandLineArguments - ) - { - configuration - .SetBasePath(environment.ContentRootPath) + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ + builder.Configuration + .SetBasePath(builder.Environment.ContentRootPath) .AddJsonFile( "appsettings.json", optional: false, - reloadOnChange: !environment.IsEnvironment("test") + reloadOnChange: !builder.Environment.IsEnvironment(TestEnvironment) ) .AddJsonFile( - $"appsettings.{environment.EnvironmentName}.json", + $"appsettings.{builder.Environment.EnvironmentName}.json", optional: false, - reloadOnChange: !environment.IsEnvironment("test") + reloadOnChange: !builder.Environment.IsEnvironment(TestEnvironment) ) .AddEnvironmentVariables() .AddEnvironmentVariables(prefix: "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables .AddCommandLine(commandLineArguments); + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection + // https://github.com/dotnet/aspnetcore/issues/38334#issuecomment-967709919 + builder.Host.UseDefaultServiceProvider(_ => + { + _.ValidateScopes = true; + _.ValidateOnBuild = true; + }); + // https://github.com/serilog/serilog-aspnetcore#instructions + builder.Host.UseSerilog((webHostBuilderContext, loggerConfiguration) => + { + ConfigureLogging( + loggerConfiguration, + webHostBuilderContext.HostingEnvironment.EnvironmentName + ); + loggerConfiguration + .ReadFrom.Configuration(webHostBuilderContext.Configuration); + }); + return builder; } } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 5ec6b6a7..68862cc2 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -152,7 +152,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) ); } - public void Configure(IApplicationBuilder app) + public void Configure(WebApplication app) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ if (_environment.IsDevelopment() || _environment.IsEnvironment("test")) @@ -195,36 +195,33 @@ public void Configure(IApplicationBuilder app) // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx /* app.UseWebSockets(); */ - app.UseEndpoints(_ => - { - _.MapGraphQL() - .WithOptions( - // https://chillicream.com/docs/hotchocolate/server/middleware - new GraphQLServerOptions - { - EnableSchemaRequests = true, - EnableGetRequests = false, - // AllowedGetOperations = AllowedGetOperations.Query - EnableMultipartRequests = false, - Tool = { - DisableTelemetry = true, - Enable = true, // _environment.IsDevelopment() - IncludeCookies = false, - GraphQLEndpoint = "/graphql", - HttpMethod = DefaultHttpMethod.Post, - Title = "GraphQL" - } - } - ) - .RequireCors(GraphQlCorsPolicy); - _.MapControllers(); - _.MapHealthChecks("/health", - new HealthCheckOptions - { - ResponseWriter = WriteJsonResponse - } - ); - }); + app.MapGraphQL() + .WithOptions( + // https://chillicream.com/docs/hotchocolate/server/middleware + new GraphQLServerOptions + { + EnableSchemaRequests = true, + EnableGetRequests = false, + // AllowedGetOperations = AllowedGetOperations.Query + EnableMultipartRequests = false, + Tool = { + DisableTelemetry = true, + Enable = true, // _environment.IsDevelopment() + IncludeCookies = false, + GraphQLEndpoint = "/graphql", + HttpMethod = DefaultHttpMethod.Post, + Title = "GraphQL" + } + } + ) + .RequireCors(GraphQlCorsPolicy); + app.MapControllers(); + app.MapHealthChecks("/health", + new HealthCheckOptions + { + ResponseWriter = WriteJsonResponse + } + ); } // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output From a9d80b79e28f54938d9d7b554787737582be4333 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 31 May 2023 16:08:37 +0200 Subject: [PATCH 115/878] Extract app configuration as it is needed for the testing setup --- backend/src/Program.cs | 44 ++++++++++++++++++++++++++++-------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 8a165c24..d1369f19 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -125,21 +125,11 @@ string[] commandLineArguments } ); // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ - builder.Configuration - .SetBasePath(builder.Environment.ContentRootPath) - .AddJsonFile( - "appsettings.json", - optional: false, - reloadOnChange: !builder.Environment.IsEnvironment(TestEnvironment) - ) - .AddJsonFile( - $"appsettings.{builder.Environment.EnvironmentName}.json", - optional: false, - reloadOnChange: !builder.Environment.IsEnvironment(TestEnvironment) - ) - .AddEnvironmentVariables() - .AddEnvironmentVariables(prefix: "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables - .AddCommandLine(commandLineArguments); + ConfigureAppConfiguration( + builder.Configuration, + builder.Environment, + commandLineArguments + ); // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection // https://github.com/dotnet/aspnetcore/issues/38334#issuecomment-967709919 builder.Host.UseDefaultServiceProvider(_ => @@ -159,5 +149,29 @@ string[] commandLineArguments }); return builder; } + + public static void ConfigureAppConfiguration( + IConfigurationBuilder configuration, + IHostEnvironment environment, + string[] commandLineArguments + ) + { + configuration.Sources.Clear(); + configuration + .SetBasePath(environment.ContentRootPath) + .AddJsonFile( + "appsettings.json", + optional: false, + reloadOnChange: !environment.IsEnvironment(TestEnvironment) + ) + .AddJsonFile( + $"appsettings.{environment.EnvironmentName}.json", + optional: false, + reloadOnChange: !environment.IsEnvironment(TestEnvironment) + ) + .AddEnvironmentVariables() + .AddEnvironmentVariables(prefix: "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables + .AddCommandLine(commandLineArguments); + } } } \ No newline at end of file From fdba93cd576962e02dd8b7b8ecdadb860c4e5040 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 31 May 2023 16:10:53 +0200 Subject: [PATCH 116/878] Add backslash at end of GraphQl endpoint --- backend/src/GraphQl/Databases/DatabaseQueries.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index f9da5124..4b25eb84 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -46,7 +46,7 @@ await Metabase.QueryingMetabase.QueryGraphQl( locator = new { // TODO This is error-prone. - eq = new Uri(new Uri(appSettings.Host), "/graphql") + eq = new Uri(new Uri(appSettings.Host), "/graphql/") } } }, From 51e770079bcf7180b7bc9b53cc02b35126116891 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 16 Jun 2023 15:28:36 +0200 Subject: [PATCH 117/878] Upgrade frontend and backend dependencies to the latest compatible versions --- backend/dotnet-tools.json | 22 +- backend/src/Database.csproj | 42 +- backend/test/Database.Tests.csproj | 12 +- frontend/components/databases/Database.tsx | 15 +- .../components/databases/UpdateDatabase.tsx | 4 - frontend/yarn.lock | 1971 +++++++++-------- 6 files changed, 1115 insertions(+), 951 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index a666be84..500a4b82 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,25 +3,25 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "7.0.6", + "version": "7.0.7", "commands": [ "dotnet-aspnet-codegenerator" ] }, "dotnet-counters": { - "version": "7.0.410101", + "version": "7.0.430602", "commands": [ "dotnet-counters" ] }, "dotnet-dump": { - "version": "7.0.410101", + "version": "7.0.430602", "commands": [ "dotnet-dump" ] }, "dotnet-ef": { - "version": "7.0.5", + "version": "7.0.7", "commands": [ "dotnet-ef" ] @@ -39,31 +39,31 @@ ] }, "dotnet-trace": { - "version": "7.0.410101", + "version": "7.0.430602", "commands": [ "dotnet-trace" ] }, "dotnet-gcdump": { - "version": "7.0.410101", + "version": "7.0.430602", "commands": [ "dotnet-gcdump" ] }, "dotnet-sos": { - "version": "7.0.410101", + "version": "7.0.430602", "commands": [ "dotnet-sos" ] }, "roslynator.dotnet.cli": { - "version": "0.5.0", + "version": "0.6.0", "commands": [ "roslynator" ] }, "jetbrains.resharper.globaltools": { - "version": "2023.1.0", + "version": "2023.1.2", "commands": [ "jb" ] @@ -75,7 +75,7 @@ ] }, "strawberryshake.tools": { - "version": "13.0.5", + "version": "13.2.1", "commands": [ "dotnet-graphql" ] @@ -85,6 +85,6 @@ "commands": [ "certificate-tool" ] - } + } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index c7d6ad96..57121a4a 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -17,36 +17,36 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - + + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 6f324dad..e3d5c30e 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -11,13 +11,13 @@ - - - - - + + + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index 8bea66e0..1d72ba15 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -69,13 +69,14 @@ export default function Database({}: DatabaseProps) { {database.canCurrentUserVerifyNode && database.verificationState == DatabaseVerificationState.Pending && ( - Have your database's GraphQL endpoint return the verification code " - {database.verificationCode}" (without the quotation marks), when - queried for the GraphQL query "verificationCode". Then, press the - "Verify" button above to make the metabase assert that the - verification codes match which proves that you control the GraphQL - endpoint {database.locator}. Verified databases are publicly listed - and included in data searches. + Have your database's GraphQL endpoint return the verification + code “ + {database.verificationCode}” (without the quotation marks), + when queried for the GraphQL query “verificationCode”. + Then, press the “Verify” button above to make the + metabase assert that the verification codes match which proves that + you control the GraphQL endpoint {database.locator}. Verified + databases are publicly listed and included in data searches. )} diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx index afed1944..045539b4 100644 --- a/frontend/components/databases/UpdateDatabase.tsx +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { Alert, Form, Input, Button, Modal } from "antd"; import { useUpdateDatabaseMutation, - DatabasesDocument, DatabaseDocument, } from "../../queries/databases.graphql"; import { Scalars } from "../../__generated__/__types__"; @@ -47,9 +46,6 @@ export default function UpdateDatabase({ uuid: databaseId, }, }, - { - query: DatabasesDocument, - }, ], }); const [globalErrorMessages, setGlobalErrorMessages] = useState( diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 6dda954c..71187cd1 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -123,14 +123,14 @@ throttle-debounce "^5.0.0" "@apollo/client@^3.7.0": - version "3.7.11" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.11.tgz#fbe9ac3cd508123aacae370dfb3d268fd74f3a44" - integrity sha512-uLg2KtxoAyj9ta7abLxXx8cGRM7HypCkXVmxtL7Ko//N5g37aoJ3ca7VYoFCMUFO1BXBulj+yKVl0U3+ILj5AQ== + version "3.7.15" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.15.tgz#59dbeb5d64479f8ce0af321d8c0bf6df1d873e2d" + integrity sha512-pLScjo4GAQRWKWyEg2J3FlQr9fbUAuADT0EI2+JlLf2BjaU9I7WUaZVD9w+0qNPE8BZqs53MRQq0OCm1QCW+eg== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/context" "^0.7.0" "@wry/equality" "^0.5.0" - "@wry/trie" "^0.3.0" + "@wry/trie" "^0.4.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" optimism "^0.16.2" @@ -178,7 +178,14 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/code-frame@^7.12.13": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== @@ -192,10 +199,10 @@ dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" - integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" + integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== "@babel/core@7.15.5": version "7.15.5" @@ -218,21 +225,21 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== +"@babel/core@^7.0", "@babel/core@^7.14.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" + integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -260,12 +267,33 @@ json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.20.7", "@babel/generator@^7.21.4", "@babel/generator@^7.7.2": +"@babel/core@^7.11.6", "@babel/core@^7.12.3": version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" - integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" + "@babel/helper-module-transforms" "^7.21.2" + "@babel/helpers" "^7.21.0" + "@babel/parser" "^7.21.4" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.4" "@babel/types" "^7.21.4" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.21.4", "@babel/generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" + integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== + dependencies: + "@babel/types" "^7.22.5" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -279,12 +307,22 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== +"@babel/generator@^7.20.7", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== + dependencies: + "@babel/types" "^7.22.5" "@babel/helper-compilation-targets@^7.15.4": version "7.20.7" @@ -297,35 +335,36 @@ lru-cache "^5.1.1" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" - integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" + integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== dependencies: - "@babel/compat-data" "^7.21.4" - "@babel/helper-validator-option" "^7.21.0" + "@babel/compat-data" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18" - integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" + integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + semver "^6.3.0" -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== "@babel/helper-function-name@^7.15.4": version "7.19.0" @@ -335,7 +374,7 @@ "@babel/template" "^7.18.10" "@babel/types" "^7.19.0" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": +"@babel/helper-function-name@^7.19.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== @@ -343,26 +382,41 @@ "@babel/template" "^7.20.7" "@babel/types" "^7.21.0" -"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.18.6": +"@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.15.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== dependencies: "@babel/types" "^7.18.6" -"@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" - integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q== +"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: - "@babel/types" "^7.21.0" + "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== + dependencies: + "@babel/types" "^7.22.5" "@babel/helper-module-transforms@^7.15.4": version "7.20.11" @@ -378,7 +432,7 @@ "@babel/traverse" "^7.20.10" "@babel/types" "^7.20.7" -"@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.2": +"@babel/helper-module-transforms@^7.20.11": version "7.21.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== @@ -392,71 +446,102 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== +"@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== dependencies: - "@babel/types" "^7.18.6" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== + dependencies: + "@babel/types" "^7.22.5" "@babel/helper-plugin-utils@7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.14.5": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz#243ecd2724d2071532b2c8ad2f0f9f083bcae331" - integrity sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A== +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" + integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== + +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-member-expression-to-functions" "^7.20.7" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.7" - "@babel/types" "^7.20.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== +"@babel/helper-simple-access@^7.20.2", "@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== dependencies: - "@babel/types" "^7.20.2" + "@babel/types" "^7.22.5" -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== +"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== dependencies: - "@babel/types" "^7.20.0" + "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.18.6": +"@babel/helper-split-export-declaration@^7.15.4": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: "@babel/types" "^7.18.6" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== +"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" + integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.21.0": +"@babel/helper-validator-option@^7.18.6": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" + integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== + "@babel/helpers@^7.15.4": version "7.20.13" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" @@ -466,7 +551,7 @@ "@babel/traverse" "^7.20.13" "@babel/types" "^7.20.7" -"@babel/helpers@^7.20.7", "@babel/helpers@^7.21.0": +"@babel/helpers@^7.20.7": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== @@ -475,12 +560,21 @@ "@babel/traverse" "^7.21.0" "@babel/types" "^7.21.0" -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/helpers@^7.21.0", "@babel/helpers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" + integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.18.6", "@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" chalk "^2.0.0" js-tokens "^4.0.0" @@ -489,11 +583,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.8", "@babel/parser@^7.20.13", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== +"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" + integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== + "@babel/parser@^7.15.4", "@babel/parser@^7.15.5": version "7.20.15" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" @@ -539,19 +638,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.18.6": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz#3e37fca4f06d93567c1cd9b75156422e90a67107" - integrity sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" + integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-import-assertions@7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -567,7 +666,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.7.2": +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-jsx@^7.7.2": version "7.21.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== @@ -631,169 +737,169 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz#bea332b0e8b2dab3dafe55a163d8227531ab0551" - integrity sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" + integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz#704cc2fd155d1c996551db8276d55b9d46e4d0aa" - integrity sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/template" "^7.20.7" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz#6aeca0adcb81dc627c8986e770bfaa4d9812aff5" - integrity sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz#0bb17110c7bf5b35a60754b2f00c58302381dee2" + integrity sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-flow" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.22.5" "@babel/plugin-transform-for-of@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.0.tgz#964108c9988de1a60b4be2354a7d7e245f36e86e" - integrity sha512-LlUYlydgDkKpIY7mcBWvyPPmMcOphEyYA27Ef4xpbh1IiDNLr0kZsos2nf92vz3IccvJI25QUwp86Eo5s6HmBQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-function-name@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.2.tgz#6ff5070e71e3192ef2b7e39820a06fb78e3058e7" - integrity sha512-Cln+Yy04Gxua7iPdj6nOV96smLGjpElir5YwzF0LBPKoPlLDNJePNlrGGaybAJkd0zKRnOVXOgizSqPYMNYkzA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== dependencies: - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-object-super@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" - integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz#656b42c2fdea0a6d8762075d58ef9d4e3c4ab8a2" - integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-jsx" "^7.18.6" - "@babel/types" "^7.21.0" + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.0.0": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== dependencies: - "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-plugin-utils" "^7.22.5" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.7": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.7": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" + integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== dependencies: regenerator-runtime "^0.13.11" @@ -804,7 +910,14 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3": +"@babel/runtime@^7.2.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.15.4", "@babel/template@^7.3.3": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== @@ -813,6 +926,15 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + "@babel/traverse@7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" @@ -828,19 +950,19 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" +"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" + integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" debug "^4.1.0" globals "^11.1.0" @@ -860,13 +982,29 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.7.2": version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" "@babel/types@^7.15.4": @@ -878,6 +1016,15 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" +"@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -913,18 +1060,18 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.5.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" - integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== -"@eslint/eslintrc@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" - integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== +"@eslint/eslintrc@^2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" + integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.1" + espree "^9.5.2" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -932,10 +1079,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.38.0": - version "8.38.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" - integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== +"@eslint/js@8.42.0": + version "8.42.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" + integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== "@graphql-codegen/add@^3.2.1": version "3.2.3" @@ -946,9 +1093,9 @@ tslib "~2.4.0" "@graphql-codegen/cli@^3.0.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.3.0.tgz#12755b6b20b5a24eca7514a6fa00507b61406bef" - integrity sha512-KRQoAG0stor/ztMsBxHqxnwQzcM1DEZigwzah1LFej8bh6TT1WpUt37zt6PH0EdlMpelmeN3keY4Pq6GMd4ADw== + version "3.3.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.3.1.tgz#103e7a2263126fdde168a1ce623fc2bdc05352f0" + integrity sha512-4Es8Y9zFeT0Zx2qRL7L3qXDbbqvXK6aID+8v8lP6gaYD+uWx3Jd4Hsq5vxwVBR+6flm0BW/C85Qm0cvmT7O6LA== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" @@ -1071,13 +1218,13 @@ tslib "~2.5.0" "@graphql-codegen/typescript-operations@^3.0.0": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.3.tgz#35b2dabd7d7d243ad7be7edccc605826aa305a9d" - integrity sha512-RtMRFpZ8nyXxwurdxklWVIHX9U6/fGWYWEpvarNIIci17BKajZPzdBPKP2GKhUEWzZVGNESqH3RX38jt0onLqQ== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.4.tgz#60163c07f0ef73655779ece450d02c1172c44027" + integrity sha512-6yE2OL2+WJ1vd5MwFEGXpaxsFGzjAGUytPVHDML3Bi3TwP1F3lnQlIko4untwvHW0JhZEGQ7Ck30H9HjcxpdKA== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-codegen/typescript" "^3.0.3" - "@graphql-codegen/visitor-plugin-common" "3.1.0" + "@graphql-codegen/typescript" "^3.0.4" + "@graphql-codegen/visitor-plugin-common" "3.1.1" auto-bind "~4.0.0" tslib "~2.5.0" @@ -1105,13 +1252,13 @@ tslib "~2.2.0" "@graphql-codegen/typescript-resolvers@^3.0.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.2.0.tgz#d4e7892208d9774059610753003c6a5d4c543b01" - integrity sha512-119zDHVb/DYuPW76VYRwYAVRFABHpMN0OZgXe/g2PIHlb/Vlfof4TLjrKkRM7iBFbZPWGL92jqiI8hWFHhpqdg== + version "3.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.2.1.tgz#d0e6aca10d2417b67267f53f3e0304c7be4cbd84" + integrity sha512-2ZIHk5J6HTuylse5ZIxw+aega54prHxvj7vM8hiKJ6vejZ94kvVPAq4aWmSFOkZ5lqU3YnM/ZyWfnhT5CUDj1g== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-codegen/typescript" "^3.0.3" - "@graphql-codegen/visitor-plugin-common" "3.1.0" + "@graphql-codegen/typescript" "^3.0.4" + "@graphql-codegen/visitor-plugin-common" "3.1.1" "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" tslib "~2.5.0" @@ -1126,14 +1273,14 @@ auto-bind "~4.0.0" tslib "~2.3.0" -"@graphql-codegen/typescript@^3.0.0", "@graphql-codegen/typescript@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.3.tgz#3732e2072ba27970006db14b22d412ac4c943628" - integrity sha512-oVV4/RPs3Lmr2XqywlNDmLhNBCIhefA8rYcuxoWcnd/WRc6GNRm1b2NOIzDhhF4xriVluMyk3QQVRepkdj9enw== +"@graphql-codegen/typescript@^3.0.0", "@graphql-codegen/typescript@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.4.tgz#e12dc106a2722ebc7d18556980ccf47fa9d0805f" + integrity sha512-x4O47447DZrWNtE/l5CU9QzzW4m1RbmCEdijlA3s2flG/y1Ckqdemob4CWfilSm5/tZ3w1junVDY616RDTSvZw== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-codegen/schema-ast" "^3.0.1" - "@graphql-codegen/visitor-plugin-common" "3.1.0" + "@graphql-codegen/visitor-plugin-common" "3.1.1" auto-bind "~4.0.0" tslib "~2.5.0" @@ -1169,10 +1316,10 @@ parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.0.tgz#93172d64440b103edfe8b63ea3ac8842a5277a54" - integrity sha512-S4BO/xP38eoEDULwbem5QeV7yWA8DpbcSGkvwrnMfVQW08nL6uHliCSV8hiuAi7Mhx7snVir4Iumk8uIDRVu6Q== +"@graphql-codegen/visitor-plugin-common@3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.1.tgz#50c2aa3c537a805ce68d2f115d0a9811b151428c" + integrity sha512-uAfp+zu/009R3HUAuTK2AamR1bxIltM6rrYYI6EXSmkM3rFtFsLTuJhjUDj98HcUCszJZrADppz8KKLGRUVlNg== dependencies: "@graphql-codegen/plugin-helpers" "^4.2.0" "@graphql-tools/optimize" "^1.3.0" @@ -1195,46 +1342,46 @@ "@whatwg-node/fetch" "^0.8.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@^8.5.18": - version "8.5.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.18.tgz#2f0e91cc12e8eed32f14bc814f27c6a498b75e17" - integrity sha512-mNv5bpZMLLwhkmPA6+RP81A6u3KF4CSKLf3VX9hbomOkQR4db8pNs8BOvpZU54wKsUzMzdlws/2g/Dabyb2Vsg== +"@graphql-tools/batch-execute@^8.5.22": + version "8.5.22" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.22.tgz#a742aa9d138fe794e786d8fb6429665dc7df5455" + integrity sha512-hcV1JaY6NJQFQEwCKrYhpfLK8frSXDbtNMoTur98u10Cmecy1zrqNKSqhEyGetpgHxaJRqszGzKeI3RuroDN6A== dependencies: - "@graphql-tools/utils" "9.2.1" - dataloader "2.2.2" + "@graphql-tools/utils" "^9.2.1" + dataloader "^2.2.2" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" "@graphql-tools/code-file-loader@^7.3.17": - version "7.3.21" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.21.tgz#3eed4ff4610cf0a6f4b1be17d0bce1eec9359479" - integrity sha512-dj+OLnz1b8SYkXcuiy0CUQ25DWnOEyandDlOcdBqU3WVwh5EEVbn0oXUYm90fDlq2/uut00OrtC5Wpyhi3tAvA== + version "7.3.23" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.23.tgz#33793f9a1f8e74981f8ae6ec4ab7061f9713db15" + integrity sha512-8Wt1rTtyTEs0p47uzsPJ1vAtfAx0jmxPifiNdmo9EOCuUPyQGEbMaik/YkqZ7QUFIEYEQu+Vgfo8tElwOPtx5Q== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.0" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/graphql-tag-pluck" "7.5.2" + "@graphql-tools/utils" "^9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@9.0.30", "@graphql-tools/delegate@^9.0.30": - version "9.0.30" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.30.tgz#d91b0b61b91491e6434b1738ed831e89a4e228be" - integrity sha512-Q4xmpmFcPPrcrR6hZRmoinq9vPR38w26YKu8otKC6TgGpxxk7IBNe6bnvB9h0l05bs8nhDcYZBiLerqtYUFy8Q== +"@graphql-tools/delegate@^9.0.31": + version "9.0.35" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.35.tgz#94683f4bcec63520b4a6c8b2abf2e2e9324ea4f1" + integrity sha512-jwPu8NJbzRRMqi4Vp/5QX1vIUeUPpWmlQpOkXQD2r1X45YsVceyUUBnktCrlJlDB4jPRVy7JQGwmYo3KFiOBMA== dependencies: - "@graphql-tools/batch-execute" "^8.5.18" - "@graphql-tools/executor" "^0.0.16" - "@graphql-tools/schema" "^9.0.16" + "@graphql-tools/batch-execute" "^8.5.22" + "@graphql-tools/executor" "^0.0.20" + "@graphql-tools/schema" "^9.0.19" "@graphql-tools/utils" "^9.2.1" dataloader "^2.2.2" tslib "^2.5.0" value-or-promise "^1.0.12" -"@graphql-tools/executor-graphql-ws@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.13.tgz#61deec5e87580147890b8d1a02817360e4466794" - integrity sha512-BCFTyXdNtX4BIMjVmctehTXJVdjknMx1d8uNuhENKc8x/hFWUBT2a2/vC/ewLzQ2aEdXMhru7ubYopp671/qUQ== +"@graphql-tools/executor-graphql-ws@^0.0.14": + version "0.0.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.14.tgz#e0f53fc4cfc8a06cc461b2bc1edb4bb9a8e837ed" + integrity sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" "@repeaterjs/repeater" "3.0.4" "@types/ws" "^8.0.0" graphql-ws "5.12.1" @@ -1243,9 +1390,9 @@ ws "8.13.0" "@graphql-tools/executor-http@^0.1.7", "@graphql-tools/executor-http@^0.1.9": - version "0.1.9" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.9.tgz#ddd74ef376b4a2ed59c622acbcca068890854a30" - integrity sha512-tNzMt5qc1ptlHKfpSv9wVBVKCZ7gks6Yb/JcYJluxZIT4qRV+TtOFjpptfBU63usgrGVOVcGjzWc/mt7KhmmpQ== + version "0.1.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.10.tgz#faf48e18e62a925796c9653c2f50cf2095bc8e6f" + integrity sha512-hnAfbKv0/lb9s31LhWzawQ5hghBfHS+gYWtqxME6Rl0Aufq9GltiiLBcl7OVVOnkLF0KhwgbYP1mB5VKmgTGpg== dependencies: "@graphql-tools/utils" "^9.2.1" "@repeaterjs/repeater" "^3.0.4" @@ -1256,35 +1403,35 @@ tslib "^2.4.0" value-or-promise "^1.0.12" -"@graphql-tools/executor-legacy-ws@^0.0.10": - version "0.0.10" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.10.tgz#9a75f46901db1e0406c110bac8340ba5d8d04189" - integrity sha512-DLd4t7LgPFkTIuVAgks6nj9kQCNmBIGg7enoAbkgcExW9xXm7dTB/QfHdu5/6EqFPbayt2G7NHWxY6IFTVkbkA== +"@graphql-tools/executor-legacy-ws@^0.0.11": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.11.tgz#a1e12be8279e92a363a23d4105461a34cd9e389e" + integrity sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" "@types/ws" "^8.0.0" isomorphic-ws "5.0.0" tslib "^2.4.0" ws "8.13.0" -"@graphql-tools/executor@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.16.tgz#6c7cfbc04c05149b8dd6630f9debeb0293e806d5" - integrity sha512-TGhvJPNaZbvhurLQtYBfedvNRuokvoPRC3vCjeHCe9AOUfb8Vn7Jvrpnys5R/wdCnXKsOpCm9QZt9OqNMfkByQ== +"@graphql-tools/executor@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.20.tgz#d51d159696e839522dd49d936636af251670e425" + integrity sha512-GdvNc4vszmfeGvUqlcaH1FjBoguvMYzxAfT6tDd4/LgwymepHhinqLNA5otqwVLW+JETcDaK7xGENzFomuE6TA== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" "@graphql-typed-document-node/core" "3.2.0" - "@repeaterjs/repeater" "3.0.4" + "@repeaterjs/repeater" "^3.0.4" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" "@graphql-tools/git-loader@^7.2.13": - version "7.2.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.2.20.tgz#b17917c89be961c272bfbf205dcf32287247494b" - integrity sha512-D/3uwTzlXxG50HI8BEixqirT4xiUp6AesTdfotRXAs2d4CT9wC6yuIWOHkSBqgI1cwKWZb6KXZr467YPS5ob1w== + version "7.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.3.0.tgz#ca10c17d4f58c4592432d9d2ac1c2b393aebad16" + integrity sha512-gcGAK+u16eHkwsMYqqghZbmDquh8QaO24Scsxq+cVR+vx1ekRlsEiXvu+yXVDbZdcJ6PBIbeLcQbEu+xhDLmvQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.0" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/graphql-tag-pluck" "7.5.2" + "@graphql-tools/utils" "^9.2.1" is-glob "4.0.3" micromatch "^4.0.4" tslib "^2.4.0" @@ -1304,26 +1451,26 @@ value-or-promise "^1.0.12" "@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": - version "7.5.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.16.tgz#d954b25ee14c6421ddcef43f4320a82e9800cb23" - integrity sha512-lK1N3Y2I634FS12nd4bu7oAJbai3bUc28yeX+boT+C83KTO4ujGHm+6hPC8X/FRGwhKOnZBxUM7I5nvb3HiUxw== + version "7.5.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz#7c281617ea3ab4db4d42a2bdb49850f2b937f0f9" + integrity sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw== dependencies: - "@graphql-tools/import" "6.7.17" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/import" "6.7.18" + "@graphql-tools/utils" "^9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@7.5.0", "@graphql-tools/graphql-tag-pluck@^7.4.6": - version "7.5.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.0.tgz#be99bc6b5e8331a2379ab4585d71b057eb981497" - integrity sha512-76SYzhSlH50ZWkhWH6OI94qrxa8Ww1ZeOU04MdtpSeQZVT2rjGWeTb3xM3kjTVWQJsr/YJBhDeNPGlwNUWfX4Q== +"@graphql-tools/graphql-tag-pluck@7.5.2", "@graphql-tools/graphql-tag-pluck@^7.4.6": + version "7.5.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.2.tgz#502f1e066e19d832ebdeba5f571d7636dc27572d" + integrity sha512-RW+H8FqOOLQw0BPXaahYepVSRjuOHw+7IL8Opaa5G5uYGOBxoXR7DceyQ7BcpMgktAOOmpDNQ2WtcboChOJSRA== dependencies: "@babel/parser" "^7.16.8" - "@babel/plugin-syntax-import-assertions" "7.20.0" + "@babel/plugin-syntax-import-assertions" "^7.20.0" "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" "@graphql-tools/import@6.4.0": @@ -1334,21 +1481,21 @@ resolve-from "5.0.0" tslib "~2.3.0" -"@graphql-tools/import@6.7.17": - version "6.7.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.17.tgz#ab51ed08bcbf757f952abf3f40793ce3db42d4a3" - integrity sha512-bn9SgrECXq3WIasgNP7ful/uON51wBajPXtxdY+z/ce7jLWaFE6lzwTDB/GAgiZ+jo7nb0ravlxteSAz2qZmuA== +"@graphql-tools/import@6.7.18": + version "6.7.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.18.tgz#ad092d8a4546bb6ffc3e871e499eec7ac368680b" + integrity sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" resolve-from "5.0.0" tslib "^2.4.0" "@graphql-tools/json-file-loader@^7.3.7", "@graphql-tools/json-file-loader@^7.4.1": - version "7.4.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.17.tgz#3f08e74ab1a3534c02dc97875acc7f15aa460011" - integrity sha512-KOSTP43nwjPfXgas90rLHAFgbcSep4nmiYyR9xRVz4ZAmw8VYHcKhOLTSGylCAzi7KUfyBXajoW+6Z7dQwdn3g== + version "7.4.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.18.tgz#d78ae40979bde51cfc59717757354afc9e35fba2" + integrity sha512-AJ1b6Y1wiVgkwsxT5dELXhIVUPs/u3VZ8/0/oOtpcoyO/vAeM5rOvvWegzicOOnQw8G45fgBRMkkRfeuwVt6+w== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" @@ -1363,46 +1510,53 @@ unixify "1.0.0" "@graphql-tools/load@^7.5.5", "@graphql-tools/load@^7.8.0": - version "7.8.13" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.13.tgz#a813bfc8195d27f465739c15fb1672a6def6e9ee" - integrity sha512-c97/GuUl81Wpa38cx3E6nMz8gUrvVcFokoPfDOaA5uTWSTXA1UxaF4KrvM9P5rNFaKVAtF9f6nMIusRE5B0mag== + version "7.8.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.14.tgz#f2356f9a5f658a42e33934ae036e4b2cadf2d1e9" + integrity sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg== dependencies: - "@graphql-tools/schema" "9.0.17" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/schema" "^9.0.18" + "@graphql-tools/utils" "^9.2.1" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@8.4.0", "@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.0.tgz#47fbe5c4b6764276dc35bd19c4e7d3c46d3dc0fc" - integrity sha512-3XYCWe0d3I4F1azNj1CdShlbHfTIfiDgj00R9uvFH8tHKh7i1IWN3F7QQYovcHKhayaR6zPok3YYMESYQcBoaA== +"@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.4.1": + version "8.4.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" + integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== dependencies: - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" -"@graphql-tools/optimize@^1.0.1", "@graphql-tools/optimize@^1.3.0": +"@graphql-tools/optimize@^1.0.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.3.1.tgz#29407991478dbbedc3e7deb8c44f46acb4e9278b" integrity sha512-5j5CZSRGWVobt4bgRRg7zhjPiSimk+/zIuColih8E8DxuFOaJ+t0qu7eZS5KXWBkjcd4BPNuhUPpNlEmHPqVRQ== dependencies: tslib "^2.4.0" +"@graphql-tools/optimize@^1.3.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.4.0.tgz#20d6a9efa185ef8fc4af4fd409963e0907c6e112" + integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== + dependencies: + tslib "^2.4.0" + "@graphql-tools/prisma-loader@^7.2.49": - version "7.2.69" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.69.tgz#cc823092650e4b2b790b106322c92d81bb2eb3e2" - integrity sha512-kSJ018huCzO0AMHAUK/OVSddGOWJxKtE6HXFyTPhJAnXdKEqOSuivaAXZt2msDVA8sL8x/qe0Fb5PTtP8Mhk1Q== + version "7.2.72" + resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.72.tgz#6304fc23600458396f3ede713d8e2371df7850e3" + integrity sha512-0a7uV7Fky6yDqd0tI9+XMuvgIo6GAqiVzzzFV4OSLry4AwiQlI3igYseBV7ZVOGhedOTqj/URxjpiv07hRcwag== dependencies: - "@graphql-tools/url-loader" "7.17.17" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/url-loader" "^7.17.18" + "@graphql-tools/utils" "^9.2.1" "@types/js-yaml" "^4.0.0" "@types/json-stable-stringify" "^1.0.32" "@whatwg-node/fetch" "^0.8.2" chalk "^4.1.0" debug "^4.3.1" dotenv "^16.0.0" - graphql-request "^5.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" + graphql-request "^6.0.0" + http-proxy-agent "^6.0.0" + https-proxy-agent "^6.0.0" jose "^4.11.4" js-yaml "^4.0.0" json-stable-stringify "^1.0.1" @@ -1411,7 +1565,7 @@ tslib "^2.4.0" yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.5.0": +"@graphql-tools/relay-operation-optimizer@^6.3.0": version "6.5.17" resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.17.tgz#4e4e2675d696a2a31f106b09ed436c43f7976f37" integrity sha512-hHPEX6ccRF3+9kfVz0A3In//Dej7QrHOLGZEokBmPDMDqn9CS7qUjpjyGzclbOX0tRBtLfuFUZ68ABSac3P1nA== @@ -1420,28 +1574,37 @@ "@graphql-tools/utils" "9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@9.0.17", "@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13", "@graphql-tools/schema@^9.0.16": - version "9.0.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.17.tgz#d731e9899465f88d5b9bf69e607ec465bb88b062" - integrity sha512-HVLq0ecbkuXhJlpZ50IHP5nlISqH2GbNgjBJhhRzHeXhfwlUOT4ISXGquWTmuq61K0xSaO0aCjMpxe4QYbKTng== +"@graphql-tools/relay-operation-optimizer@^6.5.0": + version "6.5.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz#a1b74a8e0a5d0c795b8a4d19629b654cf66aa5ab" + integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== dependencies: - "@graphql-tools/merge" "8.4.0" - "@graphql-tools/utils" "9.2.1" + "@ardatan/relay-compiler" "12.0.0" + "@graphql-tools/utils" "^9.2.1" + tslib "^2.4.0" + +"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13", "@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": + version "9.0.19" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" + integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== + dependencies: + "@graphql-tools/merge" "^8.4.1" + "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" -"@graphql-tools/url-loader@7.17.17", "@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.9.7": - version "7.17.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.17.tgz#a26238fa80a788fc2a981cefcec2fa6c14643208" - integrity sha512-zT0GskawhfVzu3LjP5iXi+chyrUYvnv6B8RbJbnkiFHdSLsgIkxEyKm4tQUMN1Rm1l3i5nmeMtSzpEI9jTYLGw== +"@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.17.18", "@graphql-tools/url-loader@^7.9.7": + version "7.17.18" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.18.tgz#3e253594d23483e4c0dd3a4c3dd2ad5cd0141192" + integrity sha512-ear0CiyTj04jCVAxi7TvgbnGDIN2HgqzXzwsfcqiVg9cvjT40NcMlZ2P1lZDgqMkZ9oyLTV8Bw6j+SyG6A+xPw== dependencies: "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/delegate" "^9.0.30" - "@graphql-tools/executor-graphql-ws" "^0.0.13" + "@graphql-tools/delegate" "^9.0.31" + "@graphql-tools/executor-graphql-ws" "^0.0.14" "@graphql-tools/executor-http" "^0.1.7" - "@graphql-tools/executor-legacy-ws" "^0.0.10" + "@graphql-tools/executor-legacy-ws" "^0.0.11" "@graphql-tools/utils" "^9.2.1" - "@graphql-tools/wrap" "^9.4.1" + "@graphql-tools/wrap" "^9.4.2" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.8.0" isomorphic-ws "^5.0.0" @@ -1473,26 +1636,26 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/wrap@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.1.tgz#c8297d96967d9f1c6176d3d71b56780ff9a1244c" - integrity sha512-uMT6A8CKQhtKyzSI7BfU5YSG3dMW+3TuMN9W9N3wG+8E2m6Jb1twp8bAb7Ho9cvT8/tiLhl/xNkdMt17p+JCTQ== +"@graphql-tools/wrap@^9.4.2": + version "9.4.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.2.tgz#30835587c4c73be1780908a7cb077d8013aa2703" + integrity sha512-DFcd9r51lmcEKn0JW43CWkkI2D6T9XI1juW/Yo86i04v43O9w2/k4/nx2XTJv4Yv+iXwUw7Ok81PGltwGJSDSA== dependencies: - "@graphql-tools/delegate" "9.0.30" - "@graphql-tools/schema" "9.0.17" - "@graphql-tools/utils" "9.2.1" + "@graphql-tools/delegate" "^9.0.31" + "@graphql-tools/schema" "^9.0.18" + "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" - value-or-promise "1.0.12" + value-or-promise "^1.0.12" -"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1": +"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -1793,62 +1956,62 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@next/env@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.3.0.tgz#cc2e49f03060a4684ce7ec7fd617a21bc5b9edba" - integrity sha512-AjppRV4uG3No7L1plinoTQETH+j2F10TEnrMfzbTUYwze5sBUPveeeBAPZPm8OkJZ1epq9OyYKhZrvbD6/9HCQ== +"@next/env@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.6.tgz#3f2041c7758660d7255707ae4cb9166519113dea" + integrity sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg== -"@next/eslint-plugin-next@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.3.0.tgz#3a4742b0817575cc0dd4d152cb10363584c215ac" - integrity sha512-wuGN5qSEjSgcq9fVkH0Y/qIPFjnZtW3ZPwfjJOn7l/rrf6y8J24h/lo61kwqunTyzZJm/ETGfGVU9PUs8cnzEA== +"@next/eslint-plugin-next@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.6.tgz#6a291305226b3f762fdbc01e123efadeb63e5aaf" + integrity sha512-bPigeu0RI7bgy1ucBA2Yqcfg539y0Lzo38P2hIkrRB1GNvFSbYg6RTu8n6tGqPVrH3TTlPTNKLXG01wc+5NuwQ== dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.3.0.tgz#38f18e0639cd4c7edc6a38d4b83fe00f38eea4f2" - integrity sha512-DmIQCNq6JtccLPPBzf0dgh2vzMWt5wjxbP71pCi5EWpWYE3MsP6FcRXi4MlAmFNDQOfcFXR2r7kBeG1LpZUh1w== - -"@next/swc-darwin-x64@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.3.0.tgz#b670ed1fd1d231aa21279173ec52e3ad56dc6aeb" - integrity sha512-oQoqFa88OGgwnYlnAGHVct618FRI/749se0N3S8t9Bzdv5CRbscnO0RcX901+YnNK4Q6yeiizfgO3b7kogtsZg== - -"@next/swc-linux-arm64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.3.0.tgz#b114935f6b4c94c123f6cac55a4823d483209ba5" - integrity sha512-Wzz2p/WqAJUqTVoLo6H18WMeAXo3i+9DkPDae4oQG8LMloJ3if4NEZTnOnTUlro6cq+S/W4pTGa97nWTrOjbGw== - -"@next/swc-linux-arm64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.3.0.tgz#67a57309f8761c7d00d629d6785d56ed0567a0d2" - integrity sha512-xPVrIQOQo9WXJYgmoTlMnAD/HlR/1e1ZIWGbwIzEirXBVBqMARUulBEIKdC19zuvoJ477qZJgBDCKtKEykCpyQ== - -"@next/swc-linux-x64-gnu@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.3.0.tgz#11bd2bea7c00b40be111c0dd16e71171f3792086" - integrity sha512-jOFlpGuPD7W2tuXVJP4wt9a3cpNxWAPcloq5EfMJRiXsBBOjLVFZA7boXYxEBzSVgUiVVr1V9T0HFM7pULJ1qA== - -"@next/swc-linux-x64-musl@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.3.0.tgz#d57e99f85890799b78719c3ea32a4624de8d701b" - integrity sha512-2OwKlzaBgmuet9XYHc3KwsEilzb04F540rlRXkAcjMHL7eCxB7uZIGtsVvKOnQLvC/elrUegwSw1+5f7WmfyOw== - -"@next/swc-win32-arm64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.3.0.tgz#0c209aa35d1c88b01e78259a89cd68f4139b5093" - integrity sha512-OeHiA6YEvndxT46g+rzFK/MQTfftKxJmzslERMu9LDdC6Kez0bdrgEYed5eXFK2Z1viKZJCGRlhd06rBusyztA== - -"@next/swc-win32-ia32-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.3.0.tgz#52ae74da1dd6d840c3743923367d27ed013803dd" - integrity sha512-4aB7K9mcVK1lYEzpOpqWrXHEZympU3oK65fnNcY1Qc4HLJFLJj8AViuqQd4jjjPNuV4sl8jAwTz3gN5VNGWB7w== - -"@next/swc-win32-x64-msvc@13.3.0": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.3.0.tgz#db7b55fee834dc8c2c484c696469e65bae2ee770" - integrity sha512-Reer6rkLLcoOvB0dd66+Y7WrWVFH7sEEkF/4bJCIfsSKnTStTYaHtwIJAwbqnt9I392Tqvku0KkoqZOryWV9LQ== +"@next/swc-darwin-arm64@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz#47485f3deaee6681b4a4036c74bb9c4b728d5ddd" + integrity sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg== + +"@next/swc-darwin-x64@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz#a6a5b232ec0f2079224fb8ed6bf11dc479af1acf" + integrity sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA== + +"@next/swc-linux-arm64-gnu@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz#2a67144e863d9c45fdbd13c7827370e7f2a28405" + integrity sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA== + +"@next/swc-linux-arm64-musl@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz#5a191ac3575a70598e9e9c6e7264fc0b8a90b2db" + integrity sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ== + +"@next/swc-linux-x64-gnu@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz#d38adf842a8b8f9de492454328fd32a2c53350f3" + integrity sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ== + +"@next/swc-linux-x64-musl@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz#74c745774358b78be7f958e7a8b7d93936cd6ebc" + integrity sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg== + +"@next/swc-win32-arm64-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz#1e1e02c175573e64808fc1a7e8650e3e217f1edc" + integrity sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw== + +"@next/swc-win32-ia32-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz#2b528ae3ec7f6e727f4f0d81a1015f63da55c7a6" + integrity sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ== + +"@next/swc-win32-x64-msvc@13.4.6": + version "13.4.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz#38620bd68267ff13e50ecd432f1822eac51382a8" + integrity sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1909,16 +2072,16 @@ webcrypto-core "^1.7.7" "@pkgr/utils@^2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" - integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + version "2.4.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.1.tgz#adf291d0357834c410ce80af16e711b56c7b1cd3" + integrity sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w== dependencies: cross-spawn "^7.0.3" + fast-glob "^3.2.12" is-glob "^4.0.3" - open "^8.4.0" + open "^9.1.0" picocolors "^1.0.0" - tiny-glob "^0.2.9" - tslib "^2.4.0" + tslib "^2.5.0" "@rc-component/context@^1.3.0": version "1.3.0" @@ -1970,9 +2133,9 @@ integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== "@rushstack/eslint-patch@^1.1.3": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728" - integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== + version "1.3.2" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz#31b9c510d8cada9683549e1dbb4284cca5001faf" + integrity sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw== "@sinclair/typebox@^0.25.16": version "0.25.24" @@ -1993,18 +2156,13 @@ dependencies: "@sinonjs/commons" "^2.0.0" -"@swc/helpers@0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" - integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== +"@swc/helpers@0.5.1": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" + integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== dependencies: tslib "^2.4.0" -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2117,10 +2275,15 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*", "@types/node@^18.11.18": - version "18.15.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== +"@types/node@*": + version "20.3.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" + integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + +"@types/node@^18.11.18": + version "18.16.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" + integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2145,9 +2308,9 @@ "@types/react" "*" "@types/react-dom@^18.0": - version "18.0.11" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.11.tgz#321351c1459bc9ca3d216aefc8a167beec334e33" - integrity sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw== + version "18.2.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.5.tgz#5c5f13548bda23cd98f50ca4a59107238bfe18f3" + integrity sha512-sRQsOS/sCLnpQhR4DSKGTtWFE3FZjpQa86KPVbhUqdYMRZ9FEFcfAytKhR/vUG2rH1oFbOOej6cuD7MFSobDRQ== dependencies: "@types/react" "*" @@ -2165,19 +2328,10 @@ dependencies: "@types/react" "*" -"@types/react@*": - version "18.0.28" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.28.tgz#accaeb8b86f4908057ad629a26635fe641480065" - integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^18.0": - version "18.0.35" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741" - integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag== +"@types/react@*", "@types/react@^18.0": + version "18.2.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.12.tgz#95d584338610b78bb9ba0415e3180fb03debdf97" + integrity sha512-ndmBMLCgn38v3SntMeoJaIrO6tGHYKMEBohCUmw8HoLLQdRMOIGXfeYaBTLe2lsFaSB3MOK1VXscYFnmLtTSmw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2194,9 +2348,9 @@ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== "@types/ws@^8.0.0": - version "8.5.4" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" - integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== dependencies: "@types/node" "*" @@ -2220,47 +2374,47 @@ "@types/yargs-parser" "*" "@typescript-eslint/parser@^5.42.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6" - integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ== + version "5.59.11" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.11.tgz#af7d4b7110e3068ce0b97550736de455e4250103" + integrity sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA== dependencies: - "@typescript-eslint/scope-manager" "5.58.0" - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/typescript-estree" "5.58.0" + "@typescript-eslint/scope-manager" "5.59.11" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/typescript-estree" "5.59.11" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8" - integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA== +"@typescript-eslint/scope-manager@5.59.11": + version "5.59.11" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz#5d131a67a19189c42598af9fb2ea1165252001ce" + integrity sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/visitor-keys" "5.59.11" -"@typescript-eslint/types@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8" - integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g== +"@typescript-eslint/types@5.59.11": + version "5.59.11" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.11.tgz#1a9018fe3c565ba6969561f2a49f330cf1fe8db1" + integrity sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA== -"@typescript-eslint/typescript-estree@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61" - integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q== +"@typescript-eslint/typescript-estree@5.59.11": + version "5.59.11" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz#b2caaa31725e17c33970c1197bcd54e3c5f42b9f" + integrity sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA== dependencies: - "@typescript-eslint/types" "5.58.0" - "@typescript-eslint/visitor-keys" "5.58.0" + "@typescript-eslint/types" "5.59.11" + "@typescript-eslint/visitor-keys" "5.59.11" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@5.58.0": - version "5.58.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4" - integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA== +"@typescript-eslint/visitor-keys@5.59.11": + version "5.59.11" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz#dca561ddad169dc27d62396d64f45b2d2c3ecc56" + integrity sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA== dependencies: - "@typescript-eslint/types" "5.58.0" + "@typescript-eslint/types" "5.59.11" eslint-visitor-keys "^3.3.0" "@umijs/route-utils@^4.0.0": @@ -2273,44 +2427,44 @@ resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== -"@whatwg-node/events@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.2.tgz#7b7107268d2982fc7b7aff5ee6803c64018f84dd" - integrity sha512-WKj/lI4QjnLuPrim0cfO7i+HsDSXHxNv1y0CrJhdntuO3hxWZmnXCwNDnwOvry11OjRin6cgWNF+j/9Pn8TN4w== +"@whatwg-node/events@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" + integrity sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA== "@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1", "@whatwg-node/fetch@^0.8.2": - version "0.8.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.4.tgz#ae1c306d1e4f5ba5cf9badf070de259f04c2cda8" - integrity sha512-xK0NGWt49P+JmsdfN+8zmHzZoscENrV0KL1SyyncvWkc6vbFmSqGSpvItEBuhj1PAfTGFEUpyiRMCsut2hLy/Q== + version "0.8.8" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.8.tgz#48c6ad0c6b7951a73e812f09dd22d75e9fa18cae" + integrity sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg== dependencies: "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.3" + "@whatwg-node/node-fetch" "^0.3.6" busboy "^1.6.0" - urlpattern-polyfill "^6.0.2" + urlpattern-polyfill "^8.0.0" web-streams-polyfill "^3.2.1" -"@whatwg-node/node-fetch@^0.3.3": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.4.tgz#4beb88579c53ebd870e28d0f2f0376191b9fd6c3" - integrity sha512-gP1MN6DiHVbhkLWH1eCELhE2ZtLRxb+HRKu4eYze1Tijxz0uT1T2kk3lseZp94txzxCfbxGFU0jsWkxNdH3EXA== +"@whatwg-node/node-fetch@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz#e28816955f359916e2d830b68a64493124faa6d0" + integrity sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA== dependencies: - "@whatwg-node/events" "^0.0.2" + "@whatwg-node/events" "^0.0.3" busboy "^1.6.0" fast-querystring "^1.1.1" fast-url-parser "^1.1.3" tslib "^2.3.1" "@wry/context@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.0.tgz#be88e22c0ddf62aeb0ae9f95c3d90932c619a5c8" - integrity sha512-LcDAiYWRtwAoSOArfk7cuYvFXytxfVrdX7yxoUmK7pPITLk5jYh2F8knCwS7LjgYL8u1eidPlKKV6Ikqq0ODqQ== + version "0.7.3" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.3.tgz#240f6dfd4db5ef54f81f6597f6714e58d4f476a1" + integrity sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA== dependencies: tslib "^2.3.0" "@wry/equality@^0.5.0": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.3.tgz#fafebc69561aa2d40340da89fa7dc4b1f6fb7831" - integrity sha512-avR+UXdSrsF2v8vIqIgmeTY0UR91UT+IyablCyKe/uk22uOJ8fusKZnH9JH9e1/EtLeNJBtagNmL3eJdnOV53g== + version "0.5.6" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e" + integrity sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA== dependencies: tslib "^2.3.0" @@ -2321,6 +2475,13 @@ dependencies: tslib "^2.3.0" +"@wry/trie@^0.4.0": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" + integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== + dependencies: + tslib "^2.3.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -2336,17 +2497,22 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^8.4.1, acorn@^8.8.0: +acorn@^8.4.1: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== +acorn@^8.8.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" + integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + +agent-base@^7.0.2, agent-base@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" + integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== dependencies: - debug "4" + debug "^4.3.4" aggregate-error@^3.0.0: version "3.1.0" @@ -2481,11 +2647,11 @@ argparse@^2.0.1: integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== + version "5.2.1" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.2.1.tgz#bc285d9d654d1df121bcd0c134880d415ca67c15" + integrity sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g== dependencies: - deep-equal "^2.0.5" + dequal "^2.0.3" array-buffer-byte-length@^1.0.0: version "1.0.0" @@ -2581,11 +2747,6 @@ async-validator@^4.1.0: resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -2597,16 +2758,16 @@ available-typed-arrays@^1.0.5: integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== axe-core@^4.6.2: - version "4.6.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece" - integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg== + version "4.7.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" + integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== axobject-query@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" - integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== + version "3.2.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" + integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== dependencies: - deep-equal "^2.0.5" + dequal "^2.0.3" babel-jest@27.1.0: version "27.1.0" @@ -2748,6 +2909,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2762,6 +2928,13 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2778,14 +2951,14 @@ braces@^3.0.2: fill-range "^7.0.1" browserslist@^4.21.3: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" bser@2.1.1: version "2.1.1" @@ -2807,6 +2980,13 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + busboy@1.6.0, busboy@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -2850,10 +3030,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001449: - version "1.0.30001478" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" - integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001503: + version "1.0.30001503" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz#88b6ff1b2cf735f1f3361dc1a15b59f0561aa398" + integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== capital-case@^1.0.4: version "1.0.4" @@ -2969,9 +3149,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.5.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.8.0.tgz#e97a3e2bd00e6d85aa0c13d7f9e3ce236f7787fc" - integrity sha512-/eG5sJcvEIwxcdYM86k5tPwn0MUzkX5YY3eImTGpJOZgVe4SdTMY14vQpcxgBzJ0wXwAYrS8E+c3uHeK4JNyzQ== + version "2.9.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" + integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== cli-truncate@^2.1.0: version "2.1.0" @@ -3049,16 +3229,9 @@ color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.16: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== common-tags@1.8.0: version "1.8.0" @@ -3138,11 +3311,11 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + version "3.1.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c" + integrity sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g== dependencies: - node-fetch "2.6.7" + node-fetch "^2.6.11" cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" @@ -3173,12 +3346,17 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -dataloader@2.2.2, dataloader@^2.2.2: +dataloader@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== -dayjs@^1.0, dayjs@^1.11.1, dayjs@^1.11.4: +dayjs@^1.0: + version "1.11.8" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea" + integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ== + +dayjs@^1.11.1, dayjs@^1.11.4: version "1.11.7" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== @@ -3217,29 +3395,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-equal@^2.0.5: - version "2.2.0" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6" - integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw== - dependencies: - call-bind "^1.0.2" - es-get-iterator "^1.1.2" - get-intrinsic "^1.1.3" - is-arguments "^1.1.1" - is-array-buffer "^3.0.1" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" - deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3250,6 +3405,24 @@ deepmerge@^4.0, deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -3257,12 +3430,12 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== -define-properties@^1.1.3, define-properties@^1.1.4: +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== @@ -3270,16 +3443,16 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - dependency-graph@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -3343,19 +3516,19 @@ dot-case@^3.0.4: tslib "^2.0.3" dotenv@^16.0.0: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== + version "16.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.2.0.tgz#d0c7110551d39c407f407d1afdce49c501c8e320" + integrity sha512-jcq2vR1DY1+QA+vH58RIrWLDZOifTGmyQJWzP9arDUbgZcySdzuBb1WvhWZzZtiXgfm+GW2pjBqStqlfpzq7wQ== dset@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== -electron-to-chromium@^1.4.284: - version "1.4.359" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz#5c4d13cb08032469fcd6bd36457915caa211356b" - integrity sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw== +electron-to-chromium@^1.4.431: + version "1.4.432" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.432.tgz#154a69d5ead974347f534aea4d28b03c7149fd7b" + integrity sha512-yz3U/khQgAFT2HURJA3/F4fKIyO2r5eK09BQzBZFd6BvBSSaRuzKc2ZNBHtJcO75/EKiRYbVYJZ2RB0P4BuD2g== emittery@^0.13.1: version "0.13.1" @@ -3378,9 +3551,9 @@ emojis-list@^3.0.0: integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== enhanced-resolve@^5.12.0: - version "5.12.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" - integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ== + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3432,21 +3605,6 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-get-iterator@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -3493,11 +3651,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.1.4: - version "13.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.3.0.tgz#c302fbecfe2b976ea306f7622af637ef9d9e3802" - integrity sha512-6YEwmFBX0VjBd3ODGW9df0Is0FLaRFdMN8eAahQG9CN6LjQ28J8AFr19ngxqMSg7Qv6Uca/3VeeBosJh1bzu0w== + version "13.4.6" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.4.6.tgz#ccccf5dad45b23a56418d66caad5339116297308" + integrity sha512-nlv4FYish1RYYHILbQwM5/rD37cOvEqtMfDjtQCYbXdE2O3MggqHu2q6IDeLE2Z6u8ZJyNPgWOA6OimWcxj3qw== dependencies: - "@next/eslint-plugin-next" "13.3.0" + "@next/eslint-plugin-next" "13.4.6" "@rushstack/eslint-patch" "^1.1.3" "@typescript-eslint/parser" "^5.42.0" eslint-import-resolver-node "^0.3.6" @@ -3531,9 +3689,9 @@ eslint-import-resolver-typescript@^3.5.2: synckit "^0.8.5" eslint-module-utils@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" @@ -3606,29 +3764,29 @@ eslint-plugin-react@^7.31.7: semver "^6.3.0" string.prototype.matchall "^4.0.8" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" - integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== eslint@^8.0.0: - version "8.38.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" - integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== + version "8.42.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.42.0.tgz#7bebdc3a55f9ed7167251fe7259f75219cade291" + integrity sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.2" - "@eslint/js" "8.38.0" - "@humanwhocodes/config-array" "^0.11.8" + "@eslint/eslintrc" "^2.0.3" + "@eslint/js" "8.42.0" + "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" @@ -3637,9 +3795,9 @@ eslint@^8.0.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.4.0" - espree "^9.5.1" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.5.2" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3647,13 +3805,12 @@ eslint@^8.0.0: find-up "^5.0.0" glob-parent "^6.0.2" globals "^13.19.0" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" @@ -3665,14 +3822,14 @@ eslint@^8.0.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" - integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== +espree@^9.5.2: + version "9.5.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" + integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.0" + eslint-visitor-keys "^3.4.1" esprima@^4.0.0: version "4.0.1" @@ -3718,6 +3875,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" +execa@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" + integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== + 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" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -3748,11 +3920,6 @@ extract-files@^11.0.0: resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== -extract-files@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" - integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== - fast-decode-uri-component@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" @@ -3763,7 +3930,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.9: +fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -3785,9 +3952,9 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fast-querystring@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.1.tgz#f4c56ef56b1a954880cfd8c01b83f9e1a3d3fda2" - integrity sha512-qR2r+e3HvhEFmpdHMv//U8FnFlnYjaC6QKDuaXALDkw2kvHO8WDjxH+f/rHGR4Me4pnk8p9JAkRNTjYHAKRn2Q== + version "1.1.2" + resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" + integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== dependencies: fast-decode-uri-component "^1.0.1" @@ -3818,9 +3985,9 @@ fbjs-css-vars@^1.0.0: integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6" - integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ== + version "3.0.5" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== dependencies: cross-fetch "^3.1.5" fbjs-css-vars "^1.0.0" @@ -3828,7 +3995,7 @@ fbjs@^3.0.0: object-assign "^4.1.0" promise "^7.1.1" setimmediate "^1.0.5" - ua-parser-js "^0.7.30" + ua-parser-js "^1.0.35" figures@^3.0.0: version "3.2.0" @@ -3887,15 +4054,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3921,7 +4079,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functions-have-names@^1.2.2: +functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -3937,12 +4095,13 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== dependencies: function-bind "^1.1.1" has "^1.0.3" + has-proto "^1.0.1" has-symbols "^1.0.3" get-package-type@^0.1.0: @@ -3950,7 +4109,7 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -3964,9 +4123,11 @@ get-symbol-description@^1.0.0: get-intrinsic "^1.1.1" get-tsconfig@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f" - integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== + version "4.6.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.6.0.tgz#e977690993a42f3e320e932427502a40f7af6d05" + integrity sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg== + dependencies: + resolve-pkg-maps "^1.0.0" glob-parent@^5.1.2: version "5.1.2" @@ -3982,6 +4143,11 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" @@ -4025,11 +4191,6 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" -globalyzer@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" - integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== - globby@11.0.4: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" @@ -4065,11 +4226,6 @@ globby@^13.1.3: merge2 "^1.4.1" slash "^4.0.0" -globrex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" - integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -4087,10 +4243,10 @@ graceful-fs@^4.2.4, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== graphql-config@^4.5.0: version "4.5.0" @@ -4134,15 +4290,13 @@ graphql-let@^0.18: "@graphql-codegen/typescript-resolvers" "1.19.1" babel-jest "27.1.0" -graphql-request@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-5.2.0.tgz#a05fb54a517d91bb2d7aefa17ade4523dc5ebdca" - integrity sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ== +graphql-request@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" + integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== dependencies: - "@graphql-typed-document-node/core" "^3.1.1" + "@graphql-typed-document-node/core" "^3.2.0" cross-fetch "^3.1.5" - extract-files "^9.0.0" - form-data "^3.0.0" graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" @@ -4237,21 +4391,20 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== +http-proxy-agent@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-6.1.1.tgz#dc04f1a84e09511740cfbd984a56f86cc42e4277" + integrity sha512-JRCz+4Whs6yrrIoIlrH+ZTmhrRwtMnmOHsHn8GFEn9O2sVfSE+DAZ3oyyGIKF8tjJEeSJmP89j7aTjVsSqsU0g== dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" + agent-base "^7.1.0" + debug "^4.3.4" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== +https-proxy-agent@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz#0965ab47371b3e531cf6794d1eb148710a992ba7" + integrity sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA== dependencies: - agent-base "6" + agent-base "^7.0.2" debug "4" human-signals@^2.1.0: @@ -4259,6 +4412,11 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4346,7 +4504,7 @@ inquirer@^8.0.0: through "^2.3.6" wrap-ansi "^7.0.0" -internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: +internal-slot@^1.0.3, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== @@ -4370,14 +4528,6 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -4413,24 +4563,29 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" - integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== dependencies: has "^1.0.3" -is-date-object@^1.0.1, is-date-object@^1.0.5: +is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" -is-docker@^2.0.0, is-docker@^2.1.1: +is-docker@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -4453,6 +4608,13 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -4465,11 +4627,6 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" -is-map@^2.0.1, is-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -4507,11 +4664,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-set@^2.0.1, is-set@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -4524,6 +4676,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -4573,11 +4730,6 @@ is-upper-case@^2.0.2: dependencies: tslib "^2.0.3" -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -4585,14 +4737,6 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -4605,11 +4749,6 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -5093,14 +5232,9 @@ jiti@^1.17.1: integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== jose@^4.11.4: - version "4.13.1" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.13.1.tgz#449111bb5ab171db85c03f1bd2cb1647ca06db1c" - integrity sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ== - -js-sdsl@^4.1.4: - version "4.4.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" - integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== + version "4.14.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" + integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5389,9 +5523,9 @@ merge2@^1.3.0, merge2@^1.4.1: integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.1.tgz#056f7a76e8571d0aaf3c7afcbe7eb6407ff7329e" - integrity sha512-R2f/jxYqCAGI19KhAvaxSOxALBMkaXWH2a7rOyqQw+ZmizX5bKkEYWLzdhC+U82ZVVPVp6MCXe3EkVligh+12g== + version "1.3.0" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" + integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== micromatch@^4.0.4, micromatch@^4.0.5: version "4.0.5" @@ -5401,23 +5535,16 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + minimatch@4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" @@ -5475,26 +5602,28 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^13.1.4: - version "13.3.0" - resolved "https://registry.yarnpkg.com/next/-/next-13.3.0.tgz#40632d303d74fc8521faa0a5bf4a033a392749b1" - integrity sha512-OVTw8MpIPa12+DCUkPqRGPS3thlJPcwae2ZL4xti3iBff27goH024xy4q2lhlsdoYiKOi8Kz6uJoLW/GXwgfOA== + version "13.4.6" + resolved "https://registry.yarnpkg.com/next/-/next-13.4.6.tgz#ebe52f5c74d60176d45b45e73f25a51103713ea4" + integrity sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw== dependencies: - "@next/env" "13.3.0" - "@swc/helpers" "0.4.14" + "@next/env" "13.4.6" + "@swc/helpers" "0.5.1" busboy "1.6.0" caniuse-lite "^1.0.30001406" postcss "8.4.14" styled-jsx "5.1.1" + watchpack "2.4.0" + zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.3.0" - "@next/swc-darwin-x64" "13.3.0" - "@next/swc-linux-arm64-gnu" "13.3.0" - "@next/swc-linux-arm64-musl" "13.3.0" - "@next/swc-linux-x64-gnu" "13.3.0" - "@next/swc-linux-x64-musl" "13.3.0" - "@next/swc-win32-arm64-msvc" "13.3.0" - "@next/swc-win32-ia32-msvc" "13.3.0" - "@next/swc-win32-x64-msvc" "13.3.0" + "@next/swc-darwin-arm64" "13.4.6" + "@next/swc-darwin-x64" "13.4.6" + "@next/swc-linux-arm64-gnu" "13.4.6" + "@next/swc-linux-arm64-musl" "13.4.6" + "@next/swc-linux-x64-gnu" "13.4.6" + "@next/swc-linux-x64-musl" "13.4.6" + "@next/swc-win32-arm64-msvc" "13.4.6" + "@next/swc-win32-ia32-msvc" "13.4.6" + "@next/swc-win32-x64-msvc" "13.4.6" no-case@^3.0.4: version "3.0.4" @@ -5509,17 +5638,10 @@ node-addon-api@^3.2.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.1: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== +node-fetch@^2.6.1, node-fetch@^2.6.11: + version "2.6.11" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" + integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== dependencies: whatwg-url "^5.0.0" @@ -5533,10 +5655,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +node-releases@^2.0.12: + version "2.0.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" + integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== nopt@^4.0.1: version "4.0.3" @@ -5580,6 +5702,13 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -5595,14 +5724,6 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5672,13 +5793,21 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^8.4.0: - version "8.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" is-wsl "^2.2.0" optimism@^0.16.2: @@ -5839,6 +5968,11 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -5903,9 +6037,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^2.8.4: - version "2.8.7" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" - integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== pretty-format@^29.5.0: version "29.5.0" @@ -6462,13 +6596,13 @@ regenerator-runtime@^0.13.11: integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" relay-runtime@12.0.0: version "12.0.0" @@ -6526,6 +6660,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -6588,6 +6727,13 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -6601,9 +6747,9 @@ run-parallel@^1.1.9: queue-microtask "^1.2.2" rxjs@^7.5.5: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== dependencies: tslib "^2.1.0" @@ -6664,13 +6810,20 @@ semver@^6.0.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.5, semver@^7.3.7: +semver@^7.3.5: version "7.4.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== dependencies: lru-cache "^6.0.0" +semver@^7.3.7: + version "7.5.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" + integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== + dependencies: + lru-cache "^6.0.0" + sentence-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" @@ -6863,13 +7016,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -6972,6 +7118,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -7071,14 +7222,6 @@ through@^2.3.6, through@^2.3.8: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -tiny-glob@^0.2.9: - version "0.2.9" - resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" - integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== - dependencies: - globalyzer "0.1.0" - globrex "^0.1.2" - title-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" @@ -7086,6 +7229,11 @@ title-case@^3.0.3: dependencies: tslib "^2.0.3" +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -7172,9 +7320,9 @@ tslib@^1.8.1: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== tslib@~2.0.1: version "2.0.3" @@ -7242,14 +7390,14 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" - integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + version "5.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" + integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== -ua-parser-js@^0.7.30: - version "0.7.35" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.35.tgz#8bda4827be4f0b1dda91699a29499575a1f1d307" - integrity sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g== +ua-parser-js@^1.0.35: + version "1.0.35" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011" + integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA== unbox-primitive@^1.0.2: version "1.0.2" @@ -7281,10 +7429,15 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -7310,12 +7463,10 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urlpattern-polyfill@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-6.0.2.tgz#a193fe773459865a2a5c93b246bb794b13d07256" - integrity sha512-5vZjFlH9ofROmuWmXM9yj2wljYKgWstGwe8YTyiqM7hVum/g9LyCizPZtb3UqsuppVwety9QJmfc42VggLpTgg== - dependencies: - braces "^3.0.2" +urlpattern-polyfill@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" + integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== use-json-comparison@^1.0.3: version "1.0.6" @@ -7364,7 +7515,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -value-or-promise@1.0.12, value-or-promise@^1.0.11, value-or-promise@^1.0.12: +value-or-promise@^1.0.11, value-or-promise@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== @@ -7383,6 +7534,14 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" +watchpack@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -7430,20 +7589,10 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.9: version "1.1.9" @@ -7589,7 +7738,20 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.0.0, yargs@^17.3.1: +yargs@^17.0.0: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yargs@^17.3.1: version "17.7.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== @@ -7623,3 +7785,8 @@ zen-observable@0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== + +zod@3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== From a7cc652e80af9a130b24c7f5ae277a6ffbc3cfa6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jul 2023 15:56:23 +0200 Subject: [PATCH 118/878] Improve description of how to draft a new release --- README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0e4ad035..713b92af 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,17 @@ and the pages following it. ### Creating a release -1. [Draft a new release](https://github.com/building-envelope-data/database/actions/workflows/draft-new-release.yml) - with a new version according to [Semantic Versioning](https://semver.org) by - running the GitHub action which, in particular, creates a new branch named - `release/v*.*.*`, where `*.*.*` is the version, and a corresponding pull - request. +1. Draft a new release with a new version according to + [Semantic Versioning](https://semver.org) by running the GitHub action + [Draft a new release](https://github.com/building-envelope-data/database/actions/workflows/draft-new-release.yml) + which, creates a new branch named `release/v*.*.*`, + creates a corresponding pull request, updates the + [Changelog](https://github.com/building-envelope-data/database/blob/develop/CHANGELOG.md), + and bumps the version in + [`package.json`](https://github.com/building-envelope-data/database/blob/develop/frontend/package.json), + where `*.*.*` is the version. Note that this is **not** the same as "Draft + a new release" on + [Releases](https://github.com/building-envelope-data/database/releases). 1. Fetch the release branch by running `git fetch` and check it out by running `git checkout release/v*.*.*`, where `*.*.*` is the version. 1. Prepare the release by running `make prepare-release` in your shell, review, @@ -133,6 +139,4 @@ and the pages following it. ## Useful Resources -- [Fullstack Authentication Example with Next.js and NextAuth.js](https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-nextjs-api-routes-auth) -- [NextAuth.js Example App](https://github.com/nextauthjs/next-auth-example) - [Set up a GraphQL client with Apollo](https://hasura.io/learn/graphql/typescript-react-apollo/apollo-client/) From 26adf0dc33067f58adc9afb3d4c02461b58167b5 Mon Sep 17 00:00:00 2001 From: Christoph Maurer <33456252+christoph-maurer@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:42:22 +0200 Subject: [PATCH 119/878] Refer to API specification and TestLabDB in README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 713b92af..f1f44c38 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # Database -This repository presents an example of a database which can be part of the network of [buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before deploying this repository, [machine](https://github.com/building-envelope-data/machine) can be used to set up the machine. The database uses [this API specification](https://github.com/building-envelope-data/api). +This repository presents the reference implementation of a product data server as part of the product data network [buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before deploying this repository, [machine](https://github.com/building-envelope-data/machine) can be used to set up the machine. + +The [API specificatiom of the product data servers](https://github.com/building-envelope-data/api/apis/database.graphql) is available in the repository [api](https://github.com/building-envelope-data/api). There is also a [visualization of the API of a product data server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). + +This repository is deployed as the [product data server of TestLab Solar Facades of Fraunhofer ISE](https://www.solarbuildingenvelopes.com). If you have a question for which you don't find the answer in this repository, please raise a [new issue](https://github.com/building-envelope-data/database/issues/new) and add the tag `question`! All ways to contribute are presented by [CONTRIBUTING.md](https://github.com/building-envelope-data/database/blob/develop/CONTRIBUTING.md). The basis for our collaboration is decribed by our [Code of Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). From 7e82b9f8cebb9b2504d77038ef9dc66f1d47f5da Mon Sep 17 00:00:00 2001 From: Christoph Maurer <33456252+christoph-maurer@users.noreply.github.com> Date: Thu, 19 Oct 2023 22:47:17 +0200 Subject: [PATCH 120/878] Update link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f1f44c38..74d2dd74 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository presents the reference implementation of a product data server as part of the product data network [buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before deploying this repository, [machine](https://github.com/building-envelope-data/machine) can be used to set up the machine. -The [API specificatiom of the product data servers](https://github.com/building-envelope-data/api/apis/database.graphql) is available in the repository [api](https://github.com/building-envelope-data/api). There is also a [visualization of the API of a product data server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). +The [API specification of the product data servers](https://github.com/building-envelope-data/api/blob/develop/apis/database.graphql) is available in the repository [api](https://github.com/building-envelope-data/api). There is also a [visualization of the API of a product data server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). This repository is deployed as the [product data server of TestLab Solar Facades of Fraunhofer ISE](https://www.solarbuildingenvelopes.com). From 43397f7260d2f6422e22f7a45e02c620ce804c39 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 Jan 2024 16:40:09 +0100 Subject: [PATCH 121/878] Upgrade Docker images to latest compatible versions --- Dockerfile-show-build-context | 2 +- Makefile | 4 ++-- backend/Dockerfile | 10 ++++++---- backend/Dockerfile-bootstrap | 4 ++-- backend/Dockerfile-production | 6 +++--- backend/dotnet-certificate-tool | 2 +- backend/dotnet-tools.json | 4 ++-- backend/src/Database.csproj | 4 ++-- backend/test/Database.Tests.csproj | 4 ++-- docker-compose.production.yml | 4 ++-- docker-compose.yml | 4 ++-- frontend/Dockerfile | 4 ++-- frontend/Dockerfile-production | 8 ++++---- 13 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Dockerfile-show-build-context b/Dockerfile-show-build-context index 66f7087a..234a952b 100644 --- a/Dockerfile-show-build-context +++ b/Dockerfile-show-build-context @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/Makefile b/Makefile index cc5d65f8..2de7ef61 100644 --- a/Makefile +++ b/Makefile @@ -240,7 +240,7 @@ generate-certificate-authority : ## Generate certificate authority ECDSA private --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.23 \ + nginx:1.25-bookworm \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ @@ -348,7 +348,7 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.23 \ + nginx:1.25-bookworm \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ diff --git a/backend/Dockerfile b/backend/Dockerfile index 5e0274f3..95cb4add 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile @@ -6,7 +6,7 @@ # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 ARG GROUP_ID ARG USER_ID @@ -81,6 +81,9 @@ RUN \ ENV HOME=/home/me RUN \ mkdir --parents ${HOME}/app && \ + chown \ + me:us \ + ${HOME} && \ chown \ me:us \ ${HOME}/app && \ @@ -109,10 +112,9 @@ COPY \ --chown=me:us \ ./dotnet-certificate-tool ./dotnet-certificate-tool RUN \ + # `dotnet pack` is run automatically by `dotnet build` because `GeneratePackageOnBuild` is set to `True`. dotnet build \ ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ - dotnet pack \ - ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ dotnet tool restore \ --tool-manifest ./dotnet-tools.json \ --add-source ./dotnet-certificate-tool/src/nupkg && \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 1293f129..ce379c56 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,10 +1,10 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 ARG GROUP_ID ARG USER_ID diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index cb0da244..7e59b9b7 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile @@ -13,7 +13,7 @@ ######### # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:7.0-bullseye-slim-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 AS build RUN \ apt-get update && \ @@ -41,7 +41,7 @@ RUN \ ########### # https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/aspnet:7.0-bullseye-slim-amd64 +FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim-amd64 RUN \ apt-get update && \ diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index 6133fc00..db992a18 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit 6133fc001949ffa99e1db814659ab1c91040f0e9 +Subproject commit db992a18fbe01b97315316af669c61cbf1eb63f1 diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 500a4b82..44e7b55c 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -81,10 +81,10 @@ ] }, "dotnet-certificate-tool": { - "version": "3.0.0", + "version": "3.1.0", "commands": [ "certificate-tool" ] } } -} \ No newline at end of file +} diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 57121a4a..b7fe56de 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,8 +1,8 @@ - net7.0 - 11.0 + net8.0 + 12.0 31666837686487542159 enable diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index e3d5c30e..095ca487 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,8 +1,8 @@ - net7.0 - 11.0 + net8.0 + 12.0 false enable true diff --git a/docker-compose.production.yml b/docker-compose.production.yml index bc19acc3..c5d43bce 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -3,7 +3,7 @@ version: '3.3' services: database: # https://hub.docker.com/_/postgres restart: always - image: postgres:13.9-bullseye + image: postgres:13.13-bookworm # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -102,7 +102,7 @@ services: nginx: # https://hub.docker.com/_/nginx restart: always - image: nginx:1.23 + image: nginx:1.25-bookworm command: [ "nginx", "-g", "daemon off;" diff --git a/docker-compose.yml b/docker-compose.yml index 40f74add..cfc4ca44 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: '3.3' services: database: # https://hub.docker.com/_/postgres - image: postgres:13.9-bullseye + image: postgres:13.13-bookworm # ports: # - 5432:5432 # For possible command-line arguments see @@ -153,7 +153,7 @@ services: - backend nginx: # https://hub.docker.com/_/nginx - image: nginx:1.23 + image: nginx:1.25-bookworm command: [ "nginx-debug", "-g", "daemon off;" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 27659414..81e1c4b4 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile @@ -6,7 +6,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:18.13-bullseye-slim +FROM node:20.10-bookworm-slim ARG GROUP_ID ARG USER_ID diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 20167c7d..294e1e5f 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,11 +1,11 @@ -# syntax=docker/dockerfile:1.4 +# syntax=docker/dockerfile:1.6 # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:18.13-bullseye-slim AS dependencies +FROM node:20.10-bookworm-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -17,7 +17,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:18.13-bullseye-slim AS build +FROM node:20.10-bookworm-slim AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -30,7 +30,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:18.13-bullseye-slim +FROM node:20.10-bookworm-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ From 212a540b3aac3897fe0ee3e21a7b9a46aa78f09b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 14:44:05 +0100 Subject: [PATCH 122/878] Add missing home and .dotnet directories to bootstrap Dockerfile --- backend/Dockerfile-bootstrap | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index ce379c56..45efe9ef 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -2,8 +2,8 @@ # The above line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile -# https://hub.docker.com/_/microsoft-dotnet-sdk +# https://hub.docker.com/_/microsoft-dotnet-sdk FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 ARG GROUP_ID @@ -14,9 +14,19 @@ RUN \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me +ENV HOME=/home/me +RUN \ + mkdir --parents ${HOME} && \ + chown \ + me:us \ + ${HOME} + USER me WORKDIR /app +RUN \ + mkdir --parents ${HOME}/.dotnet + ENV PATH=$PATH:/home/me/.dotnet/tools RUN \ dotnet tool install --global \ From 175bb31ccc53f22867c7cf2e7f0ab09e089a7b3f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 15:01:09 +0100 Subject: [PATCH 123/878] Exclude `dotnet-certificate-tool` from tool updating (because we do not use the version published to NuGet but a custom one that we build in `backend/Dockerfile`) --- backend/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/Makefile b/backend/Makefile index 3c3c4f3c..a5e892d1 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -130,6 +130,7 @@ update-tools : ## Update tools dotnet tool list | \ tail -n +3 | \ awk '{print $$1}' | \ + awk '!/dotnet-certificate-tool/' | \ xargs -n 1 dotnet tool update dotnet tool run dotnet-sos install .PHONY : update-tools From 56834d715c3473913f500aee51258d75093fde7d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 15:38:58 +0100 Subject: [PATCH 124/878] Add the missing step to generate JSON Web Token (JWT) encryption and signing certificates --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 74d2dd74..e3d5cd0a 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,8 @@ If you have a question for which you don't find the answer in this repository, p authority files from the directories `./ssl`, `./backend/ssl`, and `./frontend/ssl` of the metabase project into the respective directories in the database project, and run the command `make generate-ssl-certificate`. +1. Generate JSON Web Token (JWT) encryption and signing certificates by running + `make jwt-certificates`. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see From 638faf57250ba1cee03f4bf6113d3df94106e5d7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 16:11:01 +0100 Subject: [PATCH 125/878] Upgrade backend dependencies and tooling --- backend/dotnet-tools.json | 24 ++++++------- backend/src/Data/ApplicationDbContext.cs | 22 ++---------- backend/src/Database.csproj | 46 ++++++++++++------------ backend/src/Startup.cs | 6 ++-- backend/test/Database.Tests.csproj | 18 +++++----- 5 files changed, 48 insertions(+), 68 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 44e7b55c..bbed6b37 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,25 +3,25 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "7.0.7", + "version": "8.0.0", "commands": [ "dotnet-aspnet-codegenerator" ] }, "dotnet-counters": { - "version": "7.0.430602", + "version": "8.0.505301", "commands": [ "dotnet-counters" ] }, "dotnet-dump": { - "version": "7.0.430602", + "version": "8.0.505301", "commands": [ "dotnet-dump" ] }, "dotnet-ef": { - "version": "7.0.7", + "version": "8.0.1", "commands": [ "dotnet-ef" ] @@ -33,37 +33,37 @@ ] }, "dotnet-script": { - "version": "1.4.0", + "version": "1.5.0", "commands": [ "dotnet-script" ] }, "dotnet-trace": { - "version": "7.0.430602", + "version": "8.0.505301", "commands": [ "dotnet-trace" ] }, "dotnet-gcdump": { - "version": "7.0.430602", + "version": "8.0.505301", "commands": [ "dotnet-gcdump" ] }, "dotnet-sos": { - "version": "7.0.430602", + "version": "8.0.505301", "commands": [ "dotnet-sos" ] }, "roslynator.dotnet.cli": { - "version": "0.6.0", + "version": "0.8.2", "commands": [ "roslynator" ] }, "jetbrains.resharper.globaltools": { - "version": "2023.1.2", + "version": "2023.3.2", "commands": [ "jb" ] @@ -75,7 +75,7 @@ ] }, "strawberryshake.tools": { - "version": "13.2.1", + "version": "13.8.1", "commands": [ "dotnet-graphql" ] @@ -87,4 +87,4 @@ ] } } -} +} \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 5c3ca2f5..6bf9dd20 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -12,29 +12,11 @@ namespace Database.Data public sealed class ApplicationDbContext : DbContext, IDataProtectionKeyContext { - [System.Obsolete] - static ApplicationDbContext() - { - RegisterEnumerations(); - } - - [System.Obsolete] - private static void RegisterEnumerations() - { - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - // Mapping enums like this is marked as obsolete. The preferred way - // is described in - // https://www.npgsql.org/doc/release-notes/7.0.html#managing-type-mappings-at-the-connection-level-is-no-longer-supported - // I tried to go the new way in `Startup#ConfigureDatabaseServices`. - // The problem was though that the tool `dotnet ef` did not pick up - // the registered enumerations. - NpgsqlConnection.GlobalTypeMapper.MapEnum(); - } - private static void CreateEnumerations(ModelBuilder builder) { // https://www.npgsql.org/efcore/mapping/enum.html#creating-your-database-enum - // Create enumerations in public schema because that is where `NpgsqlConnection.GlobalTypeMapper.MapEnum` expects them to be by default. + // Create enumerations in public schema because that is where + // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. builder.HasPostgresEnum("public"); } diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index b7fe56de..4d61b17a 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -17,37 +17,37 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - - - - + + + + + + + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 68862cc2..7d32372e 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -118,10 +118,8 @@ private void ConfigureDatabaseServices(IServiceCollection services) services.AddPooledDbContextFactory(options => { var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); - // Enumerations registered as below are not picked up by - // the tool `dotnet ef` when creating migrations. We thus - // register enumerations in `ApplicationDbContext`. - // dataSourceBuilder.MapEnum(); + // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum + dataSourceBuilder.MapEnum(); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 095ca487..9b97ad72 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -11,19 +11,19 @@ - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all From 8d6ba96ea57cef9f33c56c8b0e2bf50f023a831a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 16:11:33 +0100 Subject: [PATCH 126/878] Upgrade frontned dependencies to latest patch versions --- frontend/yarn.lock | 4551 +++++++++++++++++++++----------------------- 1 file changed, 2193 insertions(+), 2358 deletions(-) diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 71187cd1..f598d38c 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,7 +2,12 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.1.0", "@ampproject/remapping@^2.2.0": +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== @@ -10,111 +15,92 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@ant-design/colors@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298" - integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ== - dependencies: - "@ctrl/tinycolor" "^3.4.0" - -"@ant-design/colors@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.0.tgz#eb7eecead124c3533aea05d61254f0a17f2b61b3" - integrity sha512-iVm/9PfGCbC0dSMBrz7oiEXZaaGH7ceU40OJEfKmyuzR9R5CRimJYPlRiFtMQGQcbNMea/ePcoIebi4ASGYXtg== +"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.2.tgz#c5c753a467ce8d86ba7ca4736d2c01f599bb5492" + integrity sha512-7KJkhTiPiLHSu+LmMJnehfJ6242OCxSlR3xHVBecYxnMW8MS/878NXct1GqYARyL59fyeFdKRxXTfvR9SnDgJg== dependencies: - "@ctrl/tinycolor" "^3.4.0" + "@ctrl/tinycolor" "^3.6.1" -"@ant-design/cssinjs@^1.5.6": - version "1.5.6" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.5.6.tgz#9eb60822a307339d729624533205209c99429062" - integrity sha512-1S7LUPC9BMyQ/CUYgzfePJJwEfsbVHJe3Tpd9zhujTxRM/6LYpN9N4FTaPHVqpnPazm0S2vG0WBkh2T5Erwuug== +"@ant-design/cssinjs@^1.11.1", "@ant-design/cssinjs@^1.18.2": + version "1.18.2" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.18.2.tgz#d993a64c1d0bf51f4a9d662ddc8ed8426977b5c3" + integrity sha512-514V9rjLaFYb3v4s55/8bg2E6fb81b99s3crDZf4nSwtiDLLXs8axnIph+q2TVkY2hbJPZOn/cVsVcnLkzFy7w== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" "@emotion/unitless" "^0.7.5" classnames "^2.3.1" - csstype "^3.0.10" - rc-util "^5.27.0" + csstype "^3.1.3" + rc-util "^5.35.0" stylis "^4.0.13" -"@ant-design/icons-svg@^4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a" - integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw== - -"@ant-design/icons@^4.3.0": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.8.0.tgz#3084e2bb494cac3dad6c0392f77c1efc90ee1fa4" - integrity sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg== - dependencies: - "@ant-design/colors" "^6.0.0" - "@ant-design/icons-svg" "^4.2.1" - "@babel/runtime" "^7.11.2" - classnames "^2.2.6" - rc-util "^5.9.4" +"@ant-design/icons-svg@^4.3.0": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.3.1.tgz#4b2f65a17d4d32b526baa6414aca2117382bf8da" + integrity sha512-4QBZg8ccyC6LPIRii7A0bZUk3+lEDCLnhB+FVsflGdcWPPmV+j3fire4AwwoqHV/BibgvBmR9ZIo4s867smv+g== -"@ant-design/icons@^5.0.0": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.0.1.tgz#febb1fdc5776f58187b2c953ac9a4496069d045b" - integrity sha512-ZyF4ksXCcdtwA/1PLlnFLcF/q8/MhwxXhKHh4oCHDA4Ip+ZzAHoICtyp4wZWfiCVDP0yuz3HsjyvuldHFb3wjA== +"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.2.6": + version "5.2.6" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.2.6.tgz#2d4a9a37f531eb2a20cebec01d6fb69cf593900d" + integrity sha512-4wn0WShF43TrggskBJPRqCD0fcHbzTYjnaoskdiJrVHg86yxoZ8ZUqsXvyn4WUqehRiFKnaclOhqk9w4Ui2KVw== dependencies: "@ant-design/colors" "^7.0.0" - "@ant-design/icons-svg" "^4.2.1" + "@ant-design/icons-svg" "^4.3.0" "@babel/runtime" "^7.11.2" classnames "^2.2.6" - rc-util "^5.9.4" + rc-util "^5.31.1" "@ant-design/pro-layout@^7.10.3": - version "7.10.3" - resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.10.3.tgz#742a29d41e29dd42f7247d6188424be5dcde53f9" - integrity sha512-r8S+KqtyKFmt9eG5/G7KS2O8oIpmxZA3/WIYGMbSOyD4YkwmK1MF9KCjUErdHxQxIS9IL8Fc6V23/8KVAZmZyQ== + version "7.17.19" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.17.19.tgz#f61097f5e16e859e29d2baf03e17702bf92f303d" + integrity sha512-X3L+/0Vro9AyN51oGBMz+bfjHjod43wyMJ+7gePiK8ECqYTMZLWGXhrEZnPMY/GCdk0OeGzWD5N9DFuUtwcSLQ== dependencies: "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "^2.7.0" - "@ant-design/pro-utils" "^2.7.2" + "@ant-design/pro-provider" "2.13.5" + "@ant-design/pro-utils" "2.15.4" "@babel/runtime" "^7.18.0" "@umijs/route-utils" "^4.0.0" "@umijs/use-params" "^1.0.9" - classnames "^2.2.6" + classnames "^2.3.2" lodash.merge "^4.6.2" omit.js "^2.0.2" path-to-regexp "2.4.0" rc-resize-observer "^1.1.0" rc-util "^5.0.6" swr "^2.0.0" - use-json-comparison "^1.0.3" - use-media-antd-query "^1.1.0" warning "^4.0.3" -"@ant-design/pro-provider@^2.7.0": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.7.0.tgz#3d9b708c1d6753c53c96c348ca3e68963eb629ee" - integrity sha512-5iKr0rIGekmOH7t5C6k0AsNp8CROroTzewjnaYLF3gt+kdz4+q8IQ5rmQG78e0npcJp6yQLJvIxevuJl5kFNfw== +"@ant-design/pro-provider@2.13.5": + version "2.13.5" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.13.5.tgz#4f2a14ec21a014fc334e8a56741433ff06b02be6" + integrity sha512-ZVmzY2cq4nUvgmAlfgyCAaSZYV2l3n/upIQPXPj8sYcT+N/Pt1CeSVkkgW6By3EqokF6apWdIFU7hZMK2rNhrg== dependencies: - "@ant-design/cssinjs" "^1.5.6" + "@ant-design/cssinjs" "^1.11.1" "@babel/runtime" "^7.18.0" "@ctrl/tinycolor" "^3.4.0" rc-util "^5.0.1" swr "^2.0.0" -"@ant-design/pro-utils@^2.7.2": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.7.2.tgz#42435147b037e5c2a13f404fd02bd9e10f9a974e" - integrity sha512-1nzw9x4Hy/LXPUGWvCaFQd1g5SkX86yDp9KFZ3m4gvVREo4/7QTp0iyZasEWweaND3JNLcaXXU8sLfMiMgHIiw== +"@ant-design/pro-utils@2.15.4": + version "2.15.4" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.15.4.tgz#0e9cea50f8baad4a707683fd020033b3065196b6" + integrity sha512-nbacIMl5lbMlNHlaPl2tt/ezvHhnBtGL2KLVaqijLou5zAuZprkHAJnckXoqm9T6X9R2rE4jH96WZHLpJ27nFw== dependencies: - "@ant-design/icons" "^4.3.0" - "@ant-design/pro-provider" "^2.7.0" + "@ant-design/icons" "^5.0.0" + "@ant-design/pro-provider" "2.13.5" "@babel/runtime" "^7.18.0" - classnames "^2.2.6" - dayjs "^1.11.4" + classnames "^2.3.2" + dayjs "^1.11.10" + lodash.merge "^4.6.2" rc-util "^5.0.6" - react-sortable-hoc "^2.0.0" + safe-stable-stringify "^2.4.3" swr "^2.0.0" -"@ant-design/react-slick@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.0.tgz#4696eecaa2dea0429e47ae24c267015cfd6df35c" - integrity sha512-OKxZsn8TAf8fYxP79rDXgLs9zvKMTslK6dJ4iLhDXOujUqC5zJPBRszyrcEHXcMPOm1Sgk40JgyF3yiL/Swd7w== +"@ant-design/react-slick@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.2.tgz#241bb412aeacf7ff5d50c61fa5db66773fde6b56" + integrity sha512-Wj8onxL/T8KQLFFiCA4t8eIRGpRR+UPgOdac2sYzonv+i0n3kXHmvHLLiOYL655DQx2Umii9Y9nNgL7ssu5haQ== dependencies: "@babel/runtime" "^7.10.4" classnames "^2.2.5" @@ -123,17 +109,16 @@ throttle-debounce "^5.0.0" "@apollo/client@^3.7.0": - version "3.7.15" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.15.tgz#59dbeb5d64479f8ce0af321d8c0bf6df1d873e2d" - integrity sha512-pLScjo4GAQRWKWyEg2J3FlQr9fbUAuADT0EI2+JlLf2BjaU9I7WUaZVD9w+0qNPE8BZqs53MRQq0OCm1QCW+eg== + version "3.8.9" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.8.9.tgz#0e4ac133eb04c63e618138c1ebf273d9f110a4d0" + integrity sha512-IcQDFEEPc9+PEQsxhxQvsoQ04BRarOzi/Ila5PcniRSDeKJWgY22dnp6+V1i1fWXRDVd1ybdvze4sFESDVQUCQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" - "@wry/context" "^0.7.0" - "@wry/equality" "^0.5.0" - "@wry/trie" "^0.4.0" + "@wry/equality" "^0.5.6" + "@wry/trie" "^0.5.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" - optimism "^0.16.2" + optimism "^0.18.0" prop-types "^15.7.2" response-iterator "^0.2.6" symbol-observable "^4.0.0" @@ -178,31 +163,18 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.22.5" + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" -"@babel/code-frame@^7.12.13": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/code-frame@^7.14.5": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.5.tgz#b1f6c86a02d85d2dd3368a2b67c09add8cd0c255" - integrity sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@7.15.5": version "7.15.5" @@ -225,94 +197,33 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.14.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.5.tgz#d67d9747ecf26ee7ecd3ebae1ee22225fe902a89" - integrity sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg== +"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helpers" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/core@^7.1.0": - version "7.20.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d" - integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helpers" "^7.20.7" - "@babel/parser" "^7.20.7" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.12" - "@babel/types" "^7.20.7" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" - integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-compilation-targets" "^7.21.4" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.4" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.4" - "@babel/types" "^7.21.4" - convert-source-map "^1.7.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.7" + "@babel/parser" "^7.23.6" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.21.4", "@babel/generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== +"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.22.5" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.15.4": - version "7.20.14" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.14.tgz#9fa772c9f86a46c6ac9b321039400712b96f64ce" - integrity sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg== - dependencies: - "@babel/types" "^7.20.7" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.20.7", "@babel/generator@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" - integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== - dependencies: - "@babel/types" "^7.21.4" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -324,141 +235,76 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.15.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.4", "@babel/helper-compilation-targets@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz#fc7319fc54c5e2fa14b2909cf3c5fd3046813e02" - integrity sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw== - dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" + semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c" - integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" + integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" + "@babel/helper-member-expression-to-functions" "^7.23.0" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== + "@babel/helper-split-export-declaration" "^7.22.6" + semver "^6.3.1" -"@babel/helper-function-name@^7.15.4": - version "7.19.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" - integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== - dependencies: - "@babel/template" "^7.18.10" - "@babel/types" "^7.19.0" +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.19.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== +"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" -"@babel/helper-function-name@^7.21.0", "@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== - dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/helper-hoist-variables@^7.15.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-hoist-variables@^7.18.6", "@babel/helper-hoist-variables@^7.22.5": +"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-member-expression-to-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" - integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== +"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" + integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.23.0" -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-transforms@^7.15.4": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0" - integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.10" - "@babel/types" "^7.20.7" + "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.20.11": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - -"@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" - integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -472,29 +318,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.14.5": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-replace-supers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" - integrity sha512-aLdNM5I3kdI/V9xGNyKSF3X/gTyMUBohTZ+/3QdQKAA9vxIiy12E+8E2HoOP1/DjeqU+g6as35QHJNMDDYpuCg== +"@babel/helper-replace-supers@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" + integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" -"@babel/helper-simple-access@^7.20.2", "@babel/helper-simple-access@^7.22.5": +"@babel/helper-simple-access@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== @@ -508,74 +346,44 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.15.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.18.6", "@babel/helper-split-export-declaration@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz#88cf11050edb95ed08d596f7a044462189127a08" - integrity sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ== +"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4", "@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-validator-identifier@^7.19.1", "@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== - -"@babel/helper-validator-option@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== - -"@babel/helpers@^7.15.4": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.13.tgz#e3cb731fb70dc5337134cadc24cbbad31cc87ad2" - integrity sha512-nzJ0DWCL3gB5RCXbUO3KIMMsBY2Eqbx8mBpKGE/02PgyRQFcPQLbkQ1vyy596mZLaP+dAfD+R4ckASzNVmW3jg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.20.13" - "@babel/types" "^7.20.7" - -"@babel/helpers@^7.20.7": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" - -"@babel/helpers@^7.21.0", "@babel/helpers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.5.tgz#74bb4373eb390d1ceed74a15ef97767e63120820" - integrity sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q== - dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/highlight@^7.18.6", "@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== + +"@babel/helpers@^7.15.4", "@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== + dependencies: + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" "@babel/parser@7.15.5": @@ -583,20 +391,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.13": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" - integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== - -"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.5.tgz#721fd042f3ce1896238cf1b341c77eb7dee7dbea" - integrity sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q== - -"@babel/parser@^7.15.4", "@babel/parser@^7.15.5": - version "7.20.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.15.tgz#eec9f36d8eaf0948bb88c87a46784b5ee9fd0c89" - integrity sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -638,17 +436,17 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz#163b820b9e7696ce134df3ee716d9c0c98035859" - integrity sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a" + integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" - integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" + integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -666,20 +464,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" - integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz#f264ed7bf40ffc9ec239edabc17a50c4f5b6fea2" - integrity sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -730,210 +521,187 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" - integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" + integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== dependencies: - "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" - integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" + integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" - integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" + integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" - integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" + integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-classes@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.5.tgz#635d4e98da741fad814984639f4c0149eb0135e1" - integrity sha512-2edQhLfibpWpsVBx2n/GKOz6JdGQvLruZQfGr9l1qes2KQaWswjBzhQF7UDUZMNaMMQeYnQzxwOMPsbYF7wqPQ== + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" + integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-split-export-declaration" "^7.22.6" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" - integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" + integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.5" + "@babel/template" "^7.22.15" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" - integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" + integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz#0bb17110c7bf5b35a60754b2f00c58302381dee2" - integrity sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz#cfa7ca159cc3306fab526fc67091556b51af26ff" + integrity sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-flow" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.23.3" "@babel/plugin-transform-for-of@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" - integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" + integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-function-name@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" - integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" + integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== dependencies: - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.15" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-literals@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" - integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" + integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" - integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" + integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" + integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.3" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" "@babel/plugin-transform-object-super@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" - integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" + integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.20" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" - integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" + integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" - integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" + integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" - integrity sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" + integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" - integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" - integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" + integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-spread@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" - integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" + integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" - integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" + integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.20.7": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec" - integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b" - integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" + integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== dependencies: - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" -"@babel/runtime@^7.2.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" - integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== +"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.3.3": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - regenerator-runtime "^0.13.11" - -"@babel/template@^7.15.4", "@babel/template@^7.3.3": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" "@babel/traverse@7.15.4": version "7.15.4" @@ -950,79 +718,29 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.5.tgz#44bd276690db6f4940fdb84e1cb4abd2f729ccd1" - integrity sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ== +"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.15.4": - version "7.20.13" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.13.tgz#817c1ba13d11accca89478bd5481b2d168d07473" - integrity sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.19.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.20.13" - "@babel/types" "^7.20.7" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.13", "@babel/traverse@^7.7.2": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" - integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.4" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.4" - "@babel/types" "^7.21.4" - debug "^4.1.0" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.3.3": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - to-fast-properties "^2.0.0" - -"@babel/types@^7.15.4": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f" - integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.19.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" - integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1037,10 +755,10 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@ctrl/tinycolor@^3.4.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz#53fa5fe9c34faee89469e48f91d51a3766108bc8" - integrity sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ== +"@ctrl/tinycolor@^3.4.0", "@ctrl/tinycolor@^3.6.1": + version "3.6.1" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== "@emotion/hash@^0.8.0": version "0.8.0" @@ -1059,19 +777,19 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": - version "4.5.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" - integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== +"@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint/eslintrc@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331" - integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.2" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1079,10 +797,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.42.0": - version "8.42.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6" - integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw== +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== "@graphql-codegen/add@^3.2.1": version "3.2.3" @@ -1527,14 +1245,7 @@ "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" -"@graphql-tools/optimize@^1.0.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.3.1.tgz#29407991478dbbedc3e7deb8c44f46acb4e9278b" - integrity sha512-5j5CZSRGWVobt4bgRRg7zhjPiSimk+/zIuColih8E8DxuFOaJ+t0qu7eZS5KXWBkjcd4BPNuhUPpNlEmHPqVRQ== - dependencies: - tslib "^2.4.0" - -"@graphql-tools/optimize@^1.3.0": +"@graphql-tools/optimize@^1.0.1", "@graphql-tools/optimize@^1.3.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.4.0.tgz#20d6a9efa185ef8fc4af4fd409963e0907c6e112" integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== @@ -1565,16 +1276,7 @@ tslib "^2.4.0" yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@^6.3.0": - version "6.5.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.17.tgz#4e4e2675d696a2a31f106b09ed436c43f7976f37" - integrity sha512-hHPEX6ccRF3+9kfVz0A3In//Dej7QrHOLGZEokBmPDMDqn9CS7qUjpjyGzclbOX0tRBtLfuFUZ68ABSac3P1nA== - dependencies: - "@ardatan/relay-compiler" "12.0.0" - "@graphql-tools/utils" "9.2.1" - tslib "^2.4.0" - -"@graphql-tools/relay-operation-optimizer@^6.5.0": +"@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.5.0": version "6.5.18" resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz#a1b74a8e0a5d0c795b8a4d19629b654cf66aa5ab" integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== @@ -1612,14 +1314,6 @@ value-or-promise "^1.0.11" ws "^8.12.0" -"@graphql-tools/utils@9.2.1", "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" - integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - tslib "^2.4.0" - "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.9.1": version "7.10.0" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.10.0.tgz#07a4cb5d1bec1ff1dc1d47a935919ee6abd38699" @@ -1636,6 +1330,14 @@ dependencies: tslib "^2.4.0" +"@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" + integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + tslib "^2.4.0" + "@graphql-tools/wrap@^9.4.2": version "9.4.2" resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.2.tgz#30835587c4c73be1780908a7cb077d8013aa2703" @@ -1652,13 +1354,13 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -1666,10 +1368,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1687,110 +1389,110 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57" - integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" -"@jest/core@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03" - integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/console" "^29.5.0" - "@jest/reporters" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" ci-info "^3.2.0" exit "^0.1.2" graceful-fs "^4.2.9" - jest-changed-files "^29.5.0" - jest-config "^29.5.0" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-resolve-dependencies "^29.5.0" - jest-runner "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - jest-watcher "^29.5.0" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65" - integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ== +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== dependencies: - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.5.0" + jest-mock "^29.7.0" -"@jest/expect-utils@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036" - integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg== +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== dependencies: - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" -"@jest/expect@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba" - integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - expect "^29.5.0" - jest-snapshot "^29.5.0" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c" - integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@sinonjs/fake-timers" "^10.0.2" "@types/node" "*" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -"@jest/globals@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298" - integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/types" "^29.5.0" - jest-mock "^29.5.0" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b" - integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" @@ -1798,52 +1500,52 @@ glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.5.0" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" - integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - "@sinclair/typebox" "^0.25.16" + "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^29.4.3": - version "29.4.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" - integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: - "@jridgewell/trace-mapping" "^0.3.15" + "@jridgewell/trace-mapping" "^0.3.18" callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408" - integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/console" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4" - integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@jest/test-result" "^29.5.0" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.7.0" slash "^3.0.0" "@jest/transform@^27.1.0": @@ -1867,22 +1569,22 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/transform@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9" - integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.5.0" - "@jridgewell/trace-mapping" "^0.3.15" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-regex-util "^29.4.3" - jest-util "^29.5.0" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -1899,12 +1601,12 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@jest/types@^29.5.0": - version "29.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593" - integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -1920,22 +1622,17 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -1948,70 +1645,70 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.21" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz#5dc1df7b3dc4a6209e503a924e1ca56097a2bb15" + integrity sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" -"@next/env@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.6.tgz#3f2041c7758660d7255707ae4cb9166519113dea" - integrity sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg== +"@next/env@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.6.tgz#c1148e2e1aa166614f05161ee8f77ded467062bc" + integrity sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw== -"@next/eslint-plugin-next@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.6.tgz#6a291305226b3f762fdbc01e123efadeb63e5aaf" - integrity sha512-bPigeu0RI7bgy1ucBA2Yqcfg539y0Lzo38P2hIkrRB1GNvFSbYg6RTu8n6tGqPVrH3TTlPTNKLXG01wc+5NuwQ== +"@next/eslint-plugin-next@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.6.tgz#cf279b94ddc7de49af8e8957f0c3b7349bc489bf" + integrity sha512-ng7pU/DDsxPgT6ZPvuprxrkeew3XaRf4LAT4FabaEO/hAbvVx4P7wqnqdbTdDn1kgTvsI4tpIgT4Awn/m0bGbg== dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz#47485f3deaee6681b4a4036c74bb9c4b728d5ddd" - integrity sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg== - -"@next/swc-darwin-x64@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz#a6a5b232ec0f2079224fb8ed6bf11dc479af1acf" - integrity sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA== - -"@next/swc-linux-arm64-gnu@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz#2a67144e863d9c45fdbd13c7827370e7f2a28405" - integrity sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA== - -"@next/swc-linux-arm64-musl@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz#5a191ac3575a70598e9e9c6e7264fc0b8a90b2db" - integrity sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ== - -"@next/swc-linux-x64-gnu@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz#d38adf842a8b8f9de492454328fd32a2c53350f3" - integrity sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ== - -"@next/swc-linux-x64-musl@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz#74c745774358b78be7f958e7a8b7d93936cd6ebc" - integrity sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg== - -"@next/swc-win32-arm64-msvc@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz#1e1e02c175573e64808fc1a7e8650e3e217f1edc" - integrity sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw== - -"@next/swc-win32-ia32-msvc@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz#2b528ae3ec7f6e727f4f0d81a1015f63da55c7a6" - integrity sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ== - -"@next/swc-win32-x64-msvc@13.4.6": - version "13.4.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz#38620bd68267ff13e50ecd432f1822eac51382a8" - integrity sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw== +"@next/swc-darwin-arm64@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.6.tgz#b15d139d8971360fca29be3bdd703c108c9a45fb" + integrity sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA== + +"@next/swc-darwin-x64@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz#9c72ee31cc356cb65ce6860b658d807ff39f1578" + integrity sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA== + +"@next/swc-linux-arm64-gnu@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz#59f5f66155e85380ffa26ee3d95b687a770cfeab" + integrity sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg== + +"@next/swc-linux-arm64-musl@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz#f012518228017052736a87d69bae73e587c76ce2" + integrity sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q== + +"@next/swc-linux-x64-gnu@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz#339b867a7e9e7ee727a700b496b269033d820df4" + integrity sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw== + +"@next/swc-linux-x64-musl@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz#ae0ae84d058df758675830bcf70ca1846f1028f2" + integrity sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ== + +"@next/swc-win32-arm64-msvc@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz#a5cc0c16920485a929a17495064671374fdbc661" + integrity sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg== + +"@next/swc-win32-ia32-msvc@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz#6a2409b84a2cbf34bf92fe714896455efb4191e4" + integrity sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg== + +"@next/swc-win32-x64-msvc@13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz#4a3e2a206251abc729339ba85f60bc0433c2865d" + integrity sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -2034,24 +1731,97 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@parcel/watcher-android-arm64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.0.tgz#9c93763794153e4f76920994a423b6ea3257059d" + integrity sha512-+fPtO/GsbYX1LJnCYCaDVT3EOBjvSFdQN9Mrzh9zWAOOfvidPWyScTrHIZHHfJBvlHzNA0Gy0U3NXFA/M7PHUA== + +"@parcel/watcher-darwin-arm64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.0.tgz#2c79c2abde16aa24cac67e555b60802fd13fe210" + integrity sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA== + +"@parcel/watcher-darwin-x64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.0.tgz#23d82f198c5d033f047467c68d7c335f3df49b46" + integrity sha512-vZMv9jl+szz5YLsSqEGCMSllBl1gU1snfbRL5ysJU03MEa6gkVy9OMcvXV1j4g0++jHEcvzhs3Z3LpeEbVmY6Q== + +"@parcel/watcher-freebsd-x64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.0.tgz#7310cc86abc27dacd57624bcdba1f0ba092e76df" + integrity sha512-dHTRMIplPDT1M0+BkXjtMN+qLtqq24sLDUhmU+UxxLP2TEY2k8GIoqIJiVrGWGomdWsy5IO27aDV1vWyQ6gfHA== + +"@parcel/watcher-linux-arm-glibc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.0.tgz#c31b76e695027eeb1078d3d6f1d641d0b900c335" + integrity sha512-9NQXD+qk46RwATNC3/UB7HWurscY18CnAPMTFcI9Y8CTbtm63/eex1SNt+BHFinEQuLBjaZwR2Lp+n7pmEJPpQ== + +"@parcel/watcher-linux-arm64-glibc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.0.tgz#56e09b86e9d8a4096f606be118b588da6e965080" + integrity sha512-QuJTAQdsd7PFW9jNGaV9Pw+ZMWV9wKThEzzlY3Lhnnwy7iW23qtQFPql8iEaSFMCVI5StNNmONUopk+MFKpiKg== + +"@parcel/watcher-linux-arm64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.0.tgz#27ffd5ca5f510ecd638f9ad22e2e813049db54e7" + integrity sha512-oyN+uA9xcTDo/45bwsd6TFHa7Lc7hKujyMlvwrCLvSckvWogndCEoVYFNfZ6JJ2KNL/6fFiGPcbjp8jJmEh5Ng== + +"@parcel/watcher-linux-x64-glibc@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.0.tgz#44cbbb1e5884a1ca900655f47a0775218318f934" + integrity sha512-KphV8awJmxU3q52JQvJot0QMu07CIyEjV+2Tb2ZtbucEgqyRcxOBDMsqp1JNq5nuDXtcCC0uHQICeiEz38dPBQ== + +"@parcel/watcher-linux-x64-musl@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.0.tgz#4c33993618c8d5113722852806239cb80360494b" + integrity sha512-7jzcOonpXNWcSijPpKD5IbC6xC7yTibjJw9jviVzZostYLGxbz8LDJLUnLzLzhASPlPGgpeKLtFUMjAAzM+gSA== + +"@parcel/watcher-win32-arm64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.0.tgz#2a172fd2fda95fe5389298ca3e70b5a96316162a" + integrity sha512-NOej2lqlq8bQNYhUMnOD0nwvNql8ToQF+1Zhi9ULZoG+XTtJ9hNnCFfyICxoZLXor4bBPTOnzs/aVVoefYnjIg== + +"@parcel/watcher-win32-ia32@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.0.tgz#279225b2ebe1fadd3c5137c9b2365ad422656904" + integrity sha512-IO/nM+K2YD/iwjWAfHFMBPz4Zqn6qBDqZxY4j2n9s+4+OuTSRM/y/irksnuqcspom5DjkSeF9d0YbO+qpys+JA== + +"@parcel/watcher-win32-x64@2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.0.tgz#93e0bd0ad1bda2c9a688764b9b30b71dc5b72a71" + integrity sha512-pAUyUVjfFjWaf/pShmJpJmNxZhbMvJASUpdes9jL6bTEJ+gDxPRSpXTIemNyNsb9AtbiGXs9XduP1reThmd+dA== + "@parcel/watcher@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.1.0.tgz#5f32969362db4893922c526a842d8af7a8538545" - integrity sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.0.tgz#2d3c4ef8832a5cdfdbb76b914f022489933e664f" + integrity sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg== dependencies: + detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" - node-addon-api "^3.2.1" - node-gyp-build "^4.3.0" - -"@peculiar/asn1-schema@^2.3.6": - version "2.3.6" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" - integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.4.0" + "@parcel/watcher-darwin-arm64" "2.4.0" + "@parcel/watcher-darwin-x64" "2.4.0" + "@parcel/watcher-freebsd-x64" "2.4.0" + "@parcel/watcher-linux-arm-glibc" "2.4.0" + "@parcel/watcher-linux-arm64-glibc" "2.4.0" + "@parcel/watcher-linux-arm64-musl" "2.4.0" + "@parcel/watcher-linux-x64-glibc" "2.4.0" + "@parcel/watcher-linux-x64-musl" "2.4.0" + "@parcel/watcher-win32-arm64" "2.4.0" + "@parcel/watcher-win32-ia32" "2.4.0" + "@parcel/watcher-win32-x64" "2.4.0" + +"@peculiar/asn1-schema@^2.3.6", "@peculiar/asn1-schema@^2.3.8": + version "2.3.8" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" + integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== dependencies: asn1js "^3.0.5" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" "@peculiar/json-schema@^1.1.12": version "1.1.12" @@ -2061,105 +1831,120 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.4.0": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" - integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== + version "1.4.4" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.4.tgz#3f529abca96c8d78cecfd39ddec2148c5df3f8ac" + integrity sha512-VETlZgJqOP3OLRC7AcEYfb6/u05evqsW0Vgs9DyCBATp3FSx7D30Z8ALuDJHsmXCsMblfyTwvB9PLR6IfdlRhg== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.5.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" webcrypto-core "^1.7.7" -"@pkgr/utils@^2.3.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.1.tgz#adf291d0357834c410ce80af16e711b56c7b1cd3" - integrity sha512-JOqwkgFEyi+OROIyq7l4Jy28h/WwhDnG/cPkXG2Z1iFbubB6jsHW1NDvmyOzTBxHr3yg68YGirmh1JUgMqa+9w== +"@rc-component/color-picker@~1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.5.1.tgz#5d41a10f96aab8eb020999bd397fb4419431814c" + integrity sha512-onyAFhWKXuG4P162xE+7IgaJkPkwM94XlOYnQuu69XdXWMfxpeFi6tpJBsieIMV7EnyLV5J3lDzdLiFeK0iEBA== dependencies: - cross-spawn "^7.0.3" - fast-glob "^3.2.12" - is-glob "^4.0.3" - open "^9.1.0" - picocolors "^1.0.0" - tslib "^2.5.0" + "@babel/runtime" "^7.23.6" + "@ctrl/tinycolor" "^3.6.1" + classnames "^2.2.6" + rc-util "^5.38.1" -"@rc-component/context@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.3.0.tgz#608ccf0abcbec9406751b17a4b35db08e481c110" - integrity sha512-6QdaCJ7Wn5UZLJs15IEfqy4Ru3OaL5ctqpQYWd5rlfV9wwzrzdt6+kgAQZV/qdB0MUPN4nhyBfRembQCIvBf+w== +"@rc-component/context@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" + integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== dependencies: "@babel/runtime" "^7.10.1" rc-util "^5.27.0" "@rc-component/mini-decimal@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.0.1.tgz#e5dbc20a6a5b0e234d279bc71ce730ab865d3910" - integrity sha512-9N8nRk0oKj1qJzANKl+n9eNSMUGsZtjwNuDCiZ/KA+dt1fE3zq5x2XxclRcAbOIXnZcJ53ozP2Pa60gyELXagA== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" + integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== dependencies: "@babel/runtime" "^7.18.0" -"@rc-component/mutate-observer@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.0.0.tgz#ce99af3239ed9c74ee3e7302f1c67098de920b46" - integrity sha512-okqRJSfNisXdI6CUeOLZC5ukBW/8kir2Ii4PJiKpUt+3+uS7dxwJUMxsUZquxA1rQuL8YcEmKVp/TCnR+yUdZA== +"@rc-component/mutate-observer@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" + integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== dependencies: "@babel/runtime" "^7.18.0" classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/portal@^1.0.0-6", "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.0.tgz#6b94450d2c2b00d50b141bd7a0be23bd96503dbe" - integrity sha512-tbXM9SB1r5FOuZjRCljERFByFiEUcMmCWMXLog/NmgCzlAzreXyf23Vei3ZpSMxSMavzPnhCovfZjZdmxS3d1w== +"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== dependencies: "@babel/runtime" "^7.18.0" classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/tour@~1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.6.0.tgz#ef3888c8ccc5d1a2e465d7d2615abd1d2dd51e27" - integrity sha512-b/s7LCb7bW4wxpWfZyNpl7khHUzSyObSlsLaIScRGd+W/v1wFVk8F7gRytl/z8ik9ZSXbLWx9EvexIuHoO/RcQ== +"@rc-component/tour@~1.12.1": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.12.2.tgz#2d006d71dd5568526a49bc7868aa228f481596fc" + integrity sha512-2he76Iwf0cVchI70dHCowR5DCWpPRY9+foNoO1h+TD2cZbsGSoEk+m3jEaFPh4ChXYhdzsxp+0siz8/br91JhA== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/portal" "^1.0.0-9" + "@rc-component/trigger" "^1.3.6" classnames "^2.3.2" - rc-trigger "^5.3.4" rc-util "^5.24.4" -"@repeaterjs/repeater@3.0.4", "@repeaterjs/repeater@^3.0.4": +"@rc-component/trigger@^1.17.0", "@rc-component/trigger@^1.18.0", "@rc-component/trigger@^1.18.2", "@rc-component/trigger@^1.3.6", "@rc-component/trigger@^1.5.0", "@rc-component/trigger@^1.7.0": + version "1.18.2" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-1.18.2.tgz#dc52c4c66fa8aaccaf0710498f2429fc05454e3b" + integrity sha512-jRLYgFgjLEPq3MvS87fIhcfuywFSRDaDrYw1FLku7Cm4esszvzTbA0JBsyacAyLrK9rF3TiHFcvoEDMzoD3CTA== + dependencies: + "@babel/runtime" "^7.23.2" + "@rc-component/portal" "^1.1.0" + classnames "^2.3.2" + rc-motion "^2.0.0" + rc-resize-observer "^1.3.1" + rc-util "^5.38.0" + +"@repeaterjs/repeater@3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== -"@rushstack/eslint-patch@^1.1.3": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz#31b9c510d8cada9683549e1dbb4284cca5001faf" - integrity sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw== +"@repeaterjs/repeater@^3.0.4": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.5.tgz#b77571685410217a548a9c753aa3cdfc215bfc78" + integrity sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA== -"@sinclair/typebox@^0.25.16": - version "0.25.24" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" - integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@rushstack/eslint-patch@^1.3.3": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.7.0.tgz#b5bc1e081428794f6a4d239707b359404be35ce2" + integrity sha512-Jh4t/593gxs0lJZ/z3NnasKlplXT2f+4y/LZYuaKZW5KAaiVFL/fThhs+17EbUd53jUVJ0QudYCBGbN/psvaqg== -"@sinonjs/commons@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" - integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@sinonjs/commons@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" + integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^10.0.2": - version "10.0.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c" - integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw== + version "10.3.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" + integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== dependencies: - "@sinonjs/commons" "^2.0.0" + "@sinonjs/commons" "^3.0.0" -"@swc/helpers@0.5.1": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.1.tgz#e9031491aa3f26bfcc974a67f48bd456c8a5357a" - integrity sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg== +"@swc/helpers@0.5.2": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d" + integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw== dependencies: tslib "^2.4.0" @@ -2179,14 +1964,14 @@ integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891" - integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -2195,26 +1980,26 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.4" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + version "7.6.8" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" + integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + version "7.4.4" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.18.3" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.3.tgz#dfc508a85781e5698d5b33443416b6268c4b3e8d" - integrity sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w== + version "7.20.5" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" + integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== dependencies: - "@babel/types" "^7.3.0" + "@babel/types" "^7.20.7" "@types/cookie@^0.3.3": version "0.3.3" @@ -2222,53 +2007,53 @@ integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== "@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" - integrity sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" + integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== dependencies: "@types/node" "*" "@types/hoist-non-react-statics@^3.0.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" - integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + version "3.3.5" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" + integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== dependencies: "@types/react" "*" hoist-non-react-statics "^3.3.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/js-yaml@^4.0.0": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" - integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== "@types/json-schema@^7.0.6": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json-stable-stringify@^1.0.32": - version "1.0.34" - resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75" - integrity sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw== + version "1.0.36" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz#fe6c6001a69ff8160a772da08779448a333c7ddd" + integrity sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw== "@types/json5@^0.0.29": version "0.0.29" @@ -2276,146 +2061,147 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "20.3.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe" - integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg== + version "20.11.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e" + integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== + dependencies: + undici-types "~5.26.4" "@types/node@^18.11.18": - version "18.16.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.18.tgz#85da09bafb66d4bc14f7c899185336d0c1736390" - integrity sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw== + version "18.19.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.8.tgz#c1e42b165e5a526caf1f010747e0522cb2c9c36a" + integrity sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg== + dependencies: + undici-types "~5.26.4" "@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - -"@types/prettier@^2.1.5": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" - integrity sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg== + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" + integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/prop-types@*": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + version "15.7.11" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" + integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== "@types/qrcode.react@^1.0": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/qrcode.react/-/qrcode.react-1.0.2.tgz#f892432cc41b5dac52e3ca8873b717c8bfea6002" - integrity sha512-I9Oq5Cjlkgy3Tw7krCnCXLw2/zMhizkTere49OOcta23tkvH0xBTP0yInimTh0gstLRtb8Ki9NZVujE5UI6ffQ== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/qrcode.react/-/qrcode.react-1.0.5.tgz#d4ddcacee8f34d22a663029a230c5f0ab908cfb7" + integrity sha512-BghPtnlwvrvq8QkGa1H25YnN+5OIgCKFuQruncGWLGJYOzeSKiix/4+B9BtfKF2wf5ja8yfyWYA3OXju995G8w== dependencies: "@types/react" "*" "@types/react-dom@^18.0": - version "18.2.5" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.5.tgz#5c5f13548bda23cd98f50ca4a59107238bfe18f3" - integrity sha512-sRQsOS/sCLnpQhR4DSKGTtWFE3FZjpQa86KPVbhUqdYMRZ9FEFcfAytKhR/vUG2rH1oFbOOej6cuD7MFSobDRQ== + version "18.2.18" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" + integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== dependencies: "@types/react" "*" "@types/react-highlight-words@^0.16.3": - version "0.16.4" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.4.tgz#441b6f05c27ce6ab76833803a84bc385f0a1e8f2" - integrity sha512-KITBX3xzheQLu2s3bUgLmRE7ekmhc52zRjRTwkKayQARh30L4fjEGzGm7ULK9TuX2LgxWWavZqyQGDGjAHbL3w== + version "0.16.7" + resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.7.tgz#82fdd2d2abe4c4a5c0da44be749660f6ec42f0c2" + integrity sha512-+upXTIaRd3rGvh1aDQSs9z5X+sV3UM6Jrmjk03GN2GXl4v/+iOJKQj2LZHo6Vp2IoTvMdtxgME26feqo12xXLg== dependencies: "@types/react" "*" "@types/react-test-renderer@^18.0": - version "18.0.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243" - integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ== + version "18.0.7" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz#2cfe657adb3688cdf543995eceb2e062b5a68728" + integrity sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0": - version "18.2.12" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.12.tgz#95d584338610b78bb9ba0415e3180fb03debdf97" - integrity sha512-ndmBMLCgn38v3SntMeoJaIrO6tGHYKMEBohCUmw8HoLLQdRMOIGXfeYaBTLe2lsFaSB3MOK1VXscYFnmLtTSmw== + version "18.2.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1" + integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/scheduler@*": - version "0.16.3" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" - integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== "@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/ws@^8.0.0": - version "8.5.5" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" - integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== dependencies: "@types/node" "*" "@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + version "21.0.3" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^16.0.0": - version "16.0.5" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.5.tgz#12cc86393985735a283e387936398c2f9e5f88e3" - integrity sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ== + version "16.0.9" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" + integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== dependencies: "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + version "17.0.32" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" + integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/parser@^5.42.0": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.59.11.tgz#af7d4b7110e3068ce0b97550736de455e4250103" - integrity sha512-s9ZF3M+Nym6CAZEkJJeO2TFHHDsKAM3ecNkLuH4i4s8/RCPnF5JRip2GyviYkeEAcwGMJxkqG9h2dAsnA1nZpA== +"@typescript-eslint/parser@^5.4.2 || ^6.0.0": + version "6.19.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.19.0.tgz#80344086f362181890ade7e94fc35fe0480bfdf5" + integrity sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow== dependencies: - "@typescript-eslint/scope-manager" "5.59.11" - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/typescript-estree" "5.59.11" + "@typescript-eslint/scope-manager" "6.19.0" + "@typescript-eslint/types" "6.19.0" + "@typescript-eslint/typescript-estree" "6.19.0" + "@typescript-eslint/visitor-keys" "6.19.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.11.tgz#5d131a67a19189c42598af9fb2ea1165252001ce" - integrity sha512-dHFOsxoLFtrIcSj5h0QoBT/89hxQONwmn3FOQ0GOQcLOOXm+MIrS8zEAhs4tWl5MraxCY3ZJpaXQQdFMc2Tu+Q== +"@typescript-eslint/scope-manager@6.19.0": + version "6.19.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz#b6d2abb825b29ab70cb542d220e40c61c1678116" + integrity sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "6.19.0" + "@typescript-eslint/visitor-keys" "6.19.0" -"@typescript-eslint/types@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.11.tgz#1a9018fe3c565ba6969561f2a49f330cf1fe8db1" - integrity sha512-epoN6R6tkvBYSc+cllrz+c2sOFWkbisJZWkOE+y3xHtvYaOE6Wk6B8e114McRJwFRjGvYdJwLXQH5c9osME/AA== +"@typescript-eslint/types@6.19.0": + version "6.19.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.19.0.tgz#689b0498c436272a6a2059b09f44bcbd90de294a" + integrity sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A== -"@typescript-eslint/typescript-estree@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.11.tgz#b2caaa31725e17c33970c1197bcd54e3c5f42b9f" - integrity sha512-YupOpot5hJO0maupJXixi6l5ETdrITxeo5eBOeuV7RSKgYdU3G5cxO49/9WRnJq9EMrB7AuTSLH/bqOsXi7wPA== +"@typescript-eslint/typescript-estree@6.19.0": + version "6.19.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz#0813ba364a409afb4d62348aec0202600cb468fa" + integrity sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ== dependencies: - "@typescript-eslint/types" "5.59.11" - "@typescript-eslint/visitor-keys" "5.59.11" + "@typescript-eslint/types" "6.19.0" + "@typescript-eslint/visitor-keys" "6.19.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" -"@typescript-eslint/visitor-keys@5.59.11": - version "5.59.11" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.11.tgz#dca561ddad169dc27d62396d64f45b2d2c3ecc56" - integrity sha512-KGYniTGG3AMTuKF9QBD7EIrvufkB6O6uX3knP73xbKLMpH+QRPcgnCxjWXSHjMRuOxFLovljqQgQpR0c7GvjoA== +"@typescript-eslint/visitor-keys@6.19.0": + version "6.19.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz#4565e0ecd63ca1f81b96f1dd76e49f746c6b2b49" + integrity sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ== dependencies: - "@typescript-eslint/types" "5.59.11" - eslint-visitor-keys "^3.3.0" + "@typescript-eslint/types" "6.19.0" + eslint-visitor-keys "^3.4.1" "@umijs/route-utils@^4.0.0": version "4.0.1" @@ -2427,6 +2213,11 @@ resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -2454,34 +2245,41 @@ fast-url-parser "^1.1.3" tslib "^2.3.1" -"@wry/context@^0.7.0": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.3.tgz#240f6dfd4db5ef54f81f6597f6714e58d4f476a1" - integrity sha512-Nl8WTesHp89RF803Se9X3IiHjdmLBrIvPMaJkl+rKVJAYyPsz1TEUbu89943HpvujtSJgDUx9W4vZw3K1Mr3sA== +"@wry/caches@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" + integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== dependencies: tslib "^2.3.0" -"@wry/equality@^0.5.0": - version "0.5.6" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e" - integrity sha512-D46sfMTngaYlrH+OspKf8mIJETntFnf6Hsjb0V41jAXJ7Bx2kB8Rv8RCUujuVWYttFtHkUNp7g+FwxNQAr6mXA== +"@wry/context@^0.7.0": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" + integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== dependencies: tslib "^2.3.0" -"@wry/trie@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.2.tgz#a06f235dc184bd26396ba456711f69f8c35097e6" - integrity sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ== +"@wry/equality@^0.5.6": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" + integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== dependencies: tslib "^2.3.0" -"@wry/trie@^0.4.0": +"@wry/trie@^0.4.3": version "0.4.3" resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== dependencies: tslib "^2.3.0" +"@wry/trie@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" + integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== + dependencies: + tslib "^2.3.0" + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -2493,19 +2291,14 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -acorn@^8.4.1: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -acorn@^8.8.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" - integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== +acorn@^8.4.1, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" @@ -2527,7 +2320,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2569,56 +2362,56 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== antd@^5.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.2.1.tgz#96ee27776bf920fc89a508d3db4da6554b396973" - integrity sha512-yuTeYEhfvsEjHdPwhb47GXBk9EHZ/pHDaUgJMXHhOUbaWPWnTliHtQwgHYMJT9rEZobi3O0YRP2wnYWVtbDGsw== - dependencies: - "@ant-design/colors" "^7.0.0" - "@ant-design/cssinjs" "^1.5.6" - "@ant-design/icons" "^5.0.0" - "@ant-design/react-slick" "~1.0.0" - "@babel/runtime" "^7.18.3" - "@ctrl/tinycolor" "^3.4.0" - "@rc-component/mutate-observer" "^1.0.0" - "@rc-component/tour" "~1.6.0" - classnames "^2.2.6" - copy-to-clipboard "^3.2.0" - dayjs "^1.11.1" + version "5.13.1" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.13.1.tgz#098fd8495a89bbd1f6cba493c56e468fd9741360" + integrity sha512-/qAPsr6UyJPSFZQD9G7kW98GelH2Bajli+1q7CRW4IinYQ0R0UVJckFX11emByhiU4Jd4WNH/hOO+fZtp0eVDA== + dependencies: + "@ant-design/colors" "^7.0.2" + "@ant-design/cssinjs" "^1.18.2" + "@ant-design/icons" "^5.2.6" + "@ant-design/react-slick" "~1.0.2" + "@ctrl/tinycolor" "^3.6.1" + "@rc-component/color-picker" "~1.5.1" + "@rc-component/mutate-observer" "^1.1.0" + "@rc-component/tour" "~1.12.1" + "@rc-component/trigger" "^1.18.2" + classnames "^2.5.1" + copy-to-clipboard "^3.3.3" + dayjs "^1.11.10" qrcode.react "^3.1.0" - rc-cascader "~3.8.0" - rc-checkbox "~2.3.0" - rc-collapse "~3.5.2" - rc-dialog "~9.0.2" - rc-drawer "~6.1.1" - rc-dropdown "~4.0.0" - rc-field-form "~1.27.0" - rc-image "~5.13.0" - rc-input "~0.2.1" - rc-input-number "~7.4.0" - rc-mentions "~2.0.0" - rc-menu "~9.8.2" - rc-motion "^2.6.1" - rc-notification "~5.0.0" - rc-pagination "~3.2.0" - rc-picker "~3.1.1" - rc-progress "~3.4.1" - rc-rate "~2.9.0" - rc-resize-observer "^1.2.0" - rc-segmented "~2.1.2" - rc-select "~14.2.0" - rc-slider "~10.1.0" - rc-steps "~6.0.0" - rc-switch "~4.0.0" - rc-table "~7.30.2" - rc-tabs "~12.5.6" - rc-textarea "~1.0.0" - rc-tooltip "~5.3.1" - rc-tree "~5.7.0" - rc-tree-select "~5.6.0" - rc-trigger "^5.3.4" - rc-upload "~4.3.0" - rc-util "^5.27.0" - scroll-into-view-if-needed "^3.0.3" + rc-cascader "~3.21.0" + rc-checkbox "~3.1.0" + rc-collapse "~3.7.2" + rc-dialog "~9.3.4" + rc-drawer "~7.0.0" + rc-dropdown "~4.1.0" + rc-field-form "~1.41.0" + rc-image "~7.5.1" + rc-input "~1.4.3" + rc-input-number "~8.6.1" + rc-mentions "~2.10.1" + rc-menu "~9.12.4" + rc-motion "^2.9.0" + rc-notification "~5.3.0" + rc-pagination "~4.0.4" + rc-picker "~3.14.6" + rc-progress "~3.5.1" + rc-rate "~2.12.0" + rc-resize-observer "^1.4.0" + rc-segmented "~2.2.2" + rc-select "~14.11.0" + rc-slider "~10.5.0" + rc-steps "~6.0.1" + rc-switch "~4.1.0" + rc-table "~7.37.0" + rc-tabs "~14.0.0" + rc-textarea "~1.6.3" + rc-tooltip "~6.1.3" + rc-tree "~5.8.2" + rc-tree-select "~5.17.0" + rc-upload "~4.5.2" + rc-util "^5.38.1" + scroll-into-view-if-needed "^3.1.0" throttle-debounce "^5.0.0" anymatch@^3.0.3: @@ -2646,10 +2439,10 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.1.3: - version "5.2.1" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.2.1.tgz#bc285d9d654d1df121bcd0c134880d415ca67c15" - integrity sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g== +aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== dependencies: dequal "^2.0.3" @@ -2666,15 +2459,15 @@ array-find-index@^1.0.2: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== -array-includes@^3.1.5, array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-tree-filter@^2.1.0: @@ -2687,36 +2480,60 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" array.prototype.tosorted@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" - integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" + integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.1" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" asap@^2.0.0, asap@~2.0.3: version "2.0.6" @@ -2732,10 +2549,10 @@ asn1js@^3.0.1, asn1js@^3.0.5: pvutils "^1.1.3" tslib "^2.4.0" -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== +ast-types-flow@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" + integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== astral-regex@^2.0.0: version "2.0.0" @@ -2747,6 +2564,13 @@ async-validator@^4.1.0: resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" + auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -2757,12 +2581,12 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axe-core@^4.6.2: - version "4.7.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" - integrity sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g== +axe-core@=4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" + integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== -axobject-query@^3.1.1: +axobject-query@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== @@ -2783,15 +2607,15 @@ babel-jest@27.1.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-jest@^29.0, babel-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" - integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q== +babel-jest@^29.0, babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@jest/transform" "^29.5.0" + "@jest/transform" "^29.7.0" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.5.0" + babel-preset-jest "^29.6.3" chalk "^4.0.0" graceful-fs "^4.2.9" slash "^3.0.0" @@ -2817,10 +2641,10 @@ babel-plugin-jest-hoist@^27.5.1: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" - integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -2891,12 +2715,12 @@ babel-preset-jest@^27.0.6: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-preset-jest@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" - integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg== +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== dependencies: - babel-plugin-jest-hoist "^29.5.0" + babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" balanced-match@^1.0.0: @@ -2909,11 +2733,6 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -big-integer@^1.6.44: - version "1.6.51" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" - integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2928,13 +2747,6 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bplist-parser@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e" - integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== - dependencies: - big-integer "^1.6.44" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2943,6 +2755,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -2950,15 +2769,15 @@ braces@^3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.21.3: - version "4.21.9" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" - integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== +browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== dependencies: - caniuse-lite "^1.0.30001503" - electron-to-chromium "^1.4.431" - node-releases "^2.0.12" - update-browserslist-db "^1.0.11" + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" bser@2.1.1: version "2.1.1" @@ -2980,13 +2799,6 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -bundle-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bundle-name/-/bundle-name-3.0.0.tgz#ba59bcc9ac785fb67ccdbf104a2bf60c099f0e1a" - integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== - dependencies: - run-applescript "^5.0.0" - busboy@1.6.0, busboy@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -2994,13 +2806,14 @@ busboy@1.6.0, busboy@^1.6.0: dependencies: streamsearch "^1.1.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" caller@1.0.1: version "1.0.1" @@ -3030,10 +2843,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001503: - version "1.0.30001503" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001503.tgz#88b6ff1b2cf735f1f3361dc1a15b59f0561aa398" - integrity sha512-Sf9NiF+wZxPfzv8Z3iS0rXM1Do+iOy2Lxvib38glFX+08TCYYYGR5fRJXk4d77C4AYwhUjgYgMsMudbh2TqCKw== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001565: + version "1.0.30001579" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz#45c065216110f46d6274311a4b3fcf6278e0852a" + integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== capital-case@^1.0.4: version "1.0.4" @@ -3044,7 +2857,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3122,19 +2935,19 @@ chardet@^0.7.0: integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== +classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== clean-stack@^2.0.0: version "2.2.0" @@ -3149,9 +2962,9 @@ cli-cursor@^3.1.0: restore-cursor "^3.1.0" cli-spinners@^2.5.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" - integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-truncate@^2.1.0: version "2.1.0" @@ -3166,7 +2979,7 @@ cli-width@^3.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== -client-only@0.0.1: +client-only@0.0.1, client-only@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== @@ -3200,9 +3013,9 @@ co@^4.6.0: integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + version "1.0.2" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" + integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== color-convert@^1.9.0: version "1.9.3" @@ -3243,10 +3056,10 @@ common-tags@1.8.2: resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== -compute-scroll-into-view@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.0.0.tgz#95d2f2f4653e7edda74dd1e38edaaa897918e0f0" - integrity sha512-Yk1An4qzo5++Cu6peT9PsmRKIU8tALpmdoE09n//AfGQFcPfx21/tMGMsmKYmLJWaBJrGOJ5Jz5hoU+7cZZUWQ== +compute-scroll-into-view@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" + integrity sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg== concat-map@0.0.1: version "0.0.1" @@ -3262,7 +3075,7 @@ constant-case@^3.0.4: tslib "^2.0.3" upper-case "^2.0.2" -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.4.0, convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -3277,7 +3090,7 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -copy-to-clipboard@^3.2.0: +copy-to-clipboard@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== @@ -3305,17 +3118,30 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.5: - version "3.1.6" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.6.tgz#bae05aa31a4da760969756318feeee6e70f15d6c" - integrity sha512-riRvo06crlE8HiqOwIpQhxwdOk4fOeR7FVM/wXoxchFEqMNUjvbs3bfo4OTgMEMHzppd4DxFBDbyySj8Cv781g== + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== dependencies: - node-fetch "^2.6.11" + node-fetch "^2.6.12" cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" @@ -3331,15 +3157,10 @@ crypto-hash@^2.0.1: resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-2.0.1.tgz#46c3732e65a078ea06b8b4ae686db41216f81213" integrity sha512-t4mkp7Vh6MuCZRBf0XLzBOfhkH3nW6YEAotMDSjshVQ1GffCMGdPLSr7pKH0rdXY02jTjAZ7QW2apD0buaZXcQ== -csstype@^3.0.10: - version "3.1.1" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" - integrity sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw== - -csstype@^3.0.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" - integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +csstype@^3.0.2, csstype@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== damerau-levenshtein@^1.0.8: version "1.0.8" @@ -3351,15 +3172,10 @@ dataloader@^2.2.2: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== -dayjs@^1.0: - version "1.11.8" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea" - integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ== - -dayjs@^1.11.1, dayjs@^1.11.4: - version "1.11.7" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" - integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== +dayjs@^1.0, dayjs@^1.11.10: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== debounce@^1.2.0: version "1.2.1" @@ -3390,10 +3206,10 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +dedent@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== deep-is@^0.1.3: version "0.1.4" @@ -3405,24 +3221,6 @@ deepmerge@^4.0, deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -default-browser-id@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-3.0.0.tgz#bee7bbbef1f4e75d31f98f4d3f1556a14cea790c" - integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== - dependencies: - bplist-parser "^0.2.0" - untildify "^4.0.0" - -default-browser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-4.0.0.tgz#53c9894f8810bf86696de117a6ce9085a3cbc7da" - integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== - dependencies: - bundle-name "^3.0.0" - default-browser-id "^3.0.0" - execa "^7.1.1" - titleize "^3.0.0" - defaults@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" @@ -3430,16 +3228,21 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-lazy-prop@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz#dbb19adfb746d7fc6d734a06b72f4a00d021255f" - integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" @@ -3458,6 +3261,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -3471,10 +3279,10 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" - integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== diff@^4.0.1: version "4.0.2" @@ -3502,11 +3310,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dom-align@^1.7.0: - version "1.12.4" - resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.4.tgz#3503992eb2a7cfcb2ed3b2a6d21e0b9c00d54511" - integrity sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw== - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -3516,19 +3319,19 @@ dot-case@^3.0.4: tslib "^2.0.3" dotenv@^16.0.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.2.0.tgz#d0c7110551d39c407f407d1afdce49c501c8e320" - integrity sha512-jcq2vR1DY1+QA+vH58RIrWLDZOifTGmyQJWzP9arDUbgZcySdzuBb1WvhWZzZtiXgfm+GW2pjBqStqlfpzq7wQ== + version "16.3.1" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" + integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== dset@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" - integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== + version "3.1.3" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" + integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== -electron-to-chromium@^1.4.431: - version "1.4.432" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.432.tgz#154a69d5ead974347f534aea4d28b03c7149fd7b" - integrity sha512-yz3U/khQgAFT2HURJA3/F4fKIyO2r5eK09BQzBZFd6BvBSSaRuzKc2ZNBHtJcO75/EKiRYbVYJZ2RB0P4BuD2g== +electron-to-chromium@^1.4.601: + version "1.4.637" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.637.tgz#ed8775cf5e0c380c3e8452e9818a0e4b7a671ac4" + integrity sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w== emittery@^0.13.1: version "0.13.1" @@ -3565,25 +3368,26 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" internal-slot "^1.0.5" is-array-buffer "^3.0.2" is-callable "^1.2.7" @@ -3591,35 +3395,59 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.3" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.13" + +es-iterator-helpers@^1.0.12, es-iterator-helpers@^1.0.15: + version "1.0.15" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" + integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.1" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.2" + safe-array-concat "^1.0.1" es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - has "^1.0.3" + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -3651,107 +3479,109 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.1.4: - version "13.4.6" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.4.6.tgz#ccccf5dad45b23a56418d66caad5339116297308" - integrity sha512-nlv4FYish1RYYHILbQwM5/rD37cOvEqtMfDjtQCYbXdE2O3MggqHu2q6IDeLE2Z6u8ZJyNPgWOA6OimWcxj3qw== + version "13.5.6" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.5.6.tgz#3a5a6222d5cb32256760ad68ab8e976e866a08c8" + integrity sha512-o8pQsUHTo9aHqJ2YiZDym5gQAMRf7O2HndHo/JZeY7TDD+W4hk6Ma8Vw54RHiBeb7OWWO5dPirQB+Is/aVQ7Kg== dependencies: - "@next/eslint-plugin-next" "13.4.6" - "@rushstack/eslint-patch" "^1.1.3" - "@typescript-eslint/parser" "^5.42.0" + "@next/eslint-plugin-next" "13.5.6" + "@rushstack/eslint-patch" "^1.3.3" + "@typescript-eslint/parser" "^5.4.2 || ^6.0.0" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" - eslint-plugin-import "^2.26.0" - eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.31.7" - eslint-plugin-react-hooks "^4.5.0" + eslint-plugin-import "^2.28.1" + eslint-plugin-jsx-a11y "^6.7.1" + eslint-plugin-react "^7.33.2" + eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" -eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" eslint-import-resolver-typescript@^3.5.2: - version "3.5.5" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz#0a9034ae7ed94b254a360fbea89187b60ea7456d" - integrity sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw== + version "3.6.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" + integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== dependencies: debug "^4.3.4" enhanced-resolve "^5.12.0" eslint-module-utils "^2.7.4" + fast-glob "^3.3.1" get-tsconfig "^4.5.0" - globby "^13.1.3" is-core-module "^2.11.0" is-glob "^4.0.3" - synckit "^0.8.5" -eslint-module-utils@^2.7.4: +eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== dependencies: debug "^3.2.7" -eslint-plugin-import@^2.26.0: - version "2.27.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" - integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== +eslint-plugin-import@^2.28.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.7.4" - has "^1.0.3" - is-core-module "^2.11.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.6" - resolve "^1.22.1" - semver "^6.3.0" - tsconfig-paths "^3.14.1" - -eslint-plugin-jsx-a11y@^6.5.1: - version "6.7.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" - integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA== - dependencies: - "@babel/runtime" "^7.20.7" - aria-query "^5.1.3" - array-includes "^3.1.6" - array.prototype.flatmap "^1.3.1" - ast-types-flow "^0.0.7" - axe-core "^4.6.2" - axobject-query "^3.1.1" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" + +eslint-plugin-jsx-a11y@^6.7.1: + version "6.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz#2fa9c701d44fcd722b7c771ec322432857fcbad2" + integrity sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA== + dependencies: + "@babel/runtime" "^7.23.2" + aria-query "^5.3.0" + array-includes "^3.1.7" + array.prototype.flatmap "^1.3.2" + ast-types-flow "^0.0.8" + axe-core "=4.7.0" + axobject-query "^3.2.1" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" - has "^1.0.3" - jsx-ast-utils "^3.3.3" - language-tags "=1.0.5" + es-iterator-helpers "^1.0.15" + hasown "^2.0.0" + jsx-ast-utils "^3.3.5" + language-tags "^1.0.9" minimatch "^3.1.2" - object.entries "^1.1.6" - object.fromentries "^2.0.6" - semver "^6.3.0" + object.entries "^1.1.7" + object.fromentries "^2.0.7" -eslint-plugin-react-hooks@^4.5.0: +"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== -eslint-plugin-react@^7.31.7: - version "7.32.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" - integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== +eslint-plugin-react@^7.33.2: + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== dependencies: array-includes "^3.1.6" array.prototype.flatmap "^1.3.1" array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" @@ -3761,43 +3591,44 @@ eslint-plugin-react@^7.31.7: object.values "^1.1.6" prop-types "^15.8.1" resolve "^2.0.0-next.4" - semver "^6.3.0" + semver "^6.3.1" string.prototype.matchall "^4.0.8" -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.0.0: - version "8.42.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.42.0.tgz#7bebdc3a55f9ed7167251fe7259f75219cade291" - integrity sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A== + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.3" - "@eslint/js" "8.42.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.5.2" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3807,7 +3638,6 @@ eslint@^8.0.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -3817,17 +3647,16 @@ eslint@^8.0.0: lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b" - integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" @@ -3875,36 +3704,21 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43" - integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q== - 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" - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== -expect@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" - integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg== +expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== dependencies: - "@jest/expect-utils" "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" external-editor@^3.0.3: version "3.1.0" @@ -3930,10 +3744,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +fast-glob@^3.1.1, fast-glob@^3.2.9, fast-glob@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3966,9 +3780,9 @@ fast-url-parser@^1.1.3: punycode "^1.3.2" fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== dependencies: reusify "^1.0.4" @@ -4035,17 +3849,18 @@ find-up@^5.0.0: path-exists "^4.0.0" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== for-each@^0.3.3: version "0.3.3" @@ -4060,26 +3875,26 @@ fs.realpath@^1.0.0: integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -4094,22 +3909,22 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== -get-stream@^6.0.0, get-stream@^6.0.1: +get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -4123,9 +3938,9 @@ get-symbol-description@^1.0.0: get-intrinsic "^1.1.1" get-tsconfig@^4.5.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.6.0.tgz#e977690993a42f3e320e932427502a40f7af6d05" - integrity sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg== + version "4.7.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" + integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== dependencies: resolve-pkg-maps "^1.0.0" @@ -4178,9 +3993,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" @@ -4215,17 +4030,6 @@ globby@11.1.0, globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.3: - version "13.1.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.4.tgz#2f91c116066bcec152465ba36e5caa4a13c01317" - integrity sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g== - dependencies: - dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^4.0.0" - gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -4233,12 +4037,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -graceful-fs@^4.1.2: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -4311,9 +4110,9 @@ graphql-ws@5.12.1: integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== graphql@^16.0.1: - version "16.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" - integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== + version "16.8.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" + integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" @@ -4330,12 +4129,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" @@ -4354,12 +4153,12 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" header-case@^2.0.4: version "2.0.4" @@ -4412,11 +4211,6 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -human-signals@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2" - integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== - iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4430,16 +4224,16 @@ ieee754@^1.1.13: integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.4, ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -4484,9 +4278,9 @@ inherits@2, inherits@^2.0.3, inherits@^2.0.4: integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== inquirer@^8.0.0: - version "8.2.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" - integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -4502,15 +4296,15 @@ inquirer@^8.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" + wrap-ansi "^6.0.1" -internal-slot@^1.0.3, internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== +internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" + get-intrinsic "^1.2.2" + hasown "^2.0.0" side-channel "^1.0.4" invariant@^2.2.4: @@ -4542,6 +4336,13 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -4562,35 +4363,32 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== dependencies: has-tostringtag "^1.0.0" -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-docker@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" - integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -4601,6 +4399,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4608,13 +4413,6 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" -is-inside-container@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" - integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== - dependencies: - is-docker "^3.0.0" - is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -4627,6 +4425,11 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" +is-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -4664,6 +4467,11 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" +is-set@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -4676,11 +4484,6 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -4695,16 +4498,12 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-typedarray@^1.0.0: version "1.0.0" @@ -4730,6 +4529,11 @@ is-upper-case@^2.0.2: dependencies: tslib "^2.0.3" +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -4737,17 +4541,23 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + is-windows@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isexe@^2.0.0: version "2.0.0" @@ -4760,11 +4570,11 @@ isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: +istanbul-lib-instrument@^5.0.4: version "5.2.1" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== @@ -4775,13 +4585,24 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" + make-dir "^4.0.0" supports-color "^7.1.0" istanbul-lib-source-maps@^4.0.0: @@ -4794,142 +4615,153 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + version "3.1.6" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" + integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + javascript-stringify@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" - integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: execa "^5.0.0" + jest-util "^29.7.0" p-limit "^3.1.0" -jest-circus@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317" - integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/environment" "^29.5.0" - "@jest/expect" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" - jest-each "^29.5.0" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-runtime "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" p-limit "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" pure-rand "^6.0.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67" - integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@jest/core" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" + create-jest "^29.7.0" exit "^0.1.2" - graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" - prompts "^2.0.1" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" yargs "^17.3.1" -jest-config@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da" - integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA== +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.5.0" - "@jest/types" "^29.5.0" - babel-jest "^29.5.0" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.5.0" - jest-environment-node "^29.5.0" - jest-get-type "^29.4.3" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-runner "^29.5.0" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" - integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: chalk "^4.0.0" - diff-sequences "^29.4.3" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-docblock@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8" - integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: detect-newline "^3.0.0" -jest-each@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06" - integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" - jest-get-type "^29.4.3" - jest-util "^29.5.0" - pretty-format "^29.5.0" - -jest-environment-node@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967" - integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/types" "^29.5.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" + +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^29.5.0" - jest-util "^29.5.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" -jest-get-type@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" - integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== jest-haste-map@^27.5.1: version "27.5.1" @@ -4951,66 +4783,66 @@ jest-haste-map@^27.5.1: optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de" - integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.4.3" - jest-util "^29.5.0" - jest-worker "^29.5.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c" - integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow== +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== dependencies: - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-matcher-utils@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5" - integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: chalk "^4.0.0" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - pretty-format "^29.5.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e" - integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.5.0" + pretty-format "^29.7.0" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed" - integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw== +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.7.0" jest-pnp-resolver@^1.2.2: version "1.2.3" @@ -5022,86 +4854,86 @@ jest-regex-util@^27.5.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== -jest-regex-util@^29.4.3: - version "29.4.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" - integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== -jest-resolve-dependencies@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4" - integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg== +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== dependencies: - jest-regex-util "^29.4.3" - jest-snapshot "^29.5.0" + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" -jest-resolve@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc" - integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" + jest-haste-map "^29.7.0" jest-pnp-resolver "^1.2.2" - jest-util "^29.5.0" - jest-validate "^29.5.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" resolve "^1.20.0" resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8" - integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ== +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== dependencies: - "@jest/console" "^29.5.0" - "@jest/environment" "^29.5.0" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" - jest-docblock "^29.4.3" - jest-environment-node "^29.5.0" - jest-haste-map "^29.5.0" - jest-leak-detector "^29.5.0" - jest-message-util "^29.5.0" - jest-resolve "^29.5.0" - jest-runtime "^29.5.0" - jest-util "^29.5.0" - jest-watcher "^29.5.0" - jest-worker "^29.5.0" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420" - integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw== - dependencies: - "@jest/environment" "^29.5.0" - "@jest/fake-timers" "^29.5.0" - "@jest/globals" "^29.5.0" - "@jest/source-map" "^29.4.3" - "@jest/test-result" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.5.0" - jest-message-util "^29.5.0" - jest-mock "^29.5.0" - jest-regex-util "^29.4.3" - jest-resolve "^29.5.0" - jest-snapshot "^29.5.0" - jest-util "^29.5.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" strip-bom "^4.0.0" @@ -5113,34 +4945,31 @@ jest-serializer@^27.5.1: "@types/node" "*" graceful-fs "^4.2.9" -jest-snapshot@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce" - integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" "@babel/plugin-syntax-jsx" "^7.7.2" "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.5.0" - "@jest/transform" "^29.5.0" - "@jest/types" "^29.5.0" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.5.0" + expect "^29.7.0" graceful-fs "^4.2.9" - jest-diff "^29.5.0" - jest-get-type "^29.4.3" - jest-matcher-utils "^29.5.0" - jest-message-util "^29.5.0" - jest-util "^29.5.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" natural-compare "^1.4.0" - pretty-format "^29.5.0" - semver "^7.3.5" + pretty-format "^29.7.0" + semver "^7.5.3" jest-util@^27.5.1: version "27.5.1" @@ -5154,42 +4983,42 @@ jest-util@^27.5.1: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f" - integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc" - integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/types" "^29.5.0" + "@jest/types" "^29.6.3" camelcase "^6.2.0" chalk "^4.0.0" - jest-get-type "^29.4.3" + jest-get-type "^29.6.3" leven "^3.1.0" - pretty-format "^29.5.0" + pretty-format "^29.7.0" -jest-watcher@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363" - integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA== +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== dependencies: - "@jest/test-result" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.5.0" + jest-util "^29.7.0" string-length "^4.0.1" jest-worker@^27.5.1: @@ -5201,25 +5030,25 @@ jest-worker@^27.5.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d" - integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: "@types/node" "*" - jest-util "^29.5.0" + jest-util "^29.7.0" merge-stream "^2.0.0" supports-color "^8.0.0" jest@^29.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" - integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: - "@jest/core" "^29.5.0" - "@jest/types" "^29.5.0" + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" import-local "^3.0.2" - jest-cli "^29.5.0" + jest-cli "^29.7.0" jiti@1.17.1: version "1.17.1" @@ -5227,14 +5056,14 @@ jiti@1.17.1: integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== jiti@^1.17.1: - version "1.18.2" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" - integrity sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg== + version "1.21.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" + integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== jose@^4.11.4: - version "4.14.4" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" - integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== + version "4.15.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" + integrity sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5261,6 +5090,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -5277,11 +5111,14 @@ json-stable-stringify-without-jsonify@^1.0.1: integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-stable-stringify@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0" - integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" + integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== dependencies: + call-bind "^1.0.5" + isarray "^2.0.5" jsonify "^0.0.1" + object-keys "^1.1.1" json-to-pretty-yaml@^1.2.2: version "1.2.2" @@ -5305,7 +5142,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.2: +json5@^2.1.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -5315,30 +5152,39 @@ jsonify@^0.0.1: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" - integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: - array-includes "^3.1.5" - object.assign "^4.1.3" + json-buffer "3.0.1" kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -language-subtag-registry@~0.3.2: +language-subtag-registry@^0.3.20: version "0.3.22" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== -language-tags@=1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== +language-tags@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== dependencies: - language-subtag-registry "~0.3.2" + language-subtag-registry "^0.3.20" leven@^3.1.0: version "3.1.0" @@ -5483,13 +5329,20 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@3.1.0, make-dir@^3.0.0: +make-dir@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== dependencies: semver "^6.0.0" +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -5540,11 +5393,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - minimatch@4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" @@ -5552,6 +5400,13 @@ minimatch@4.2.3: dependencies: brace-expansion "^1.1.7" +minimatch@9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -5591,10 +5446,10 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nanoid@^3.3.4: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^3.3.6: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== natural-compare@^1.4.0: version "1.4.0" @@ -5602,28 +5457,27 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^13.1.4: - version "13.4.6" - resolved "https://registry.yarnpkg.com/next/-/next-13.4.6.tgz#ebe52f5c74d60176d45b45e73f25a51103713ea4" - integrity sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw== + version "13.5.6" + resolved "https://registry.yarnpkg.com/next/-/next-13.5.6.tgz#e964b5853272236c37ce0dd2c68302973cf010b1" + integrity sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw== dependencies: - "@next/env" "13.4.6" - "@swc/helpers" "0.5.1" + "@next/env" "13.5.6" + "@swc/helpers" "0.5.2" busboy "1.6.0" caniuse-lite "^1.0.30001406" - postcss "8.4.14" + postcss "8.4.31" styled-jsx "5.1.1" watchpack "2.4.0" - zod "3.21.4" optionalDependencies: - "@next/swc-darwin-arm64" "13.4.6" - "@next/swc-darwin-x64" "13.4.6" - "@next/swc-linux-arm64-gnu" "13.4.6" - "@next/swc-linux-arm64-musl" "13.4.6" - "@next/swc-linux-x64-gnu" "13.4.6" - "@next/swc-linux-x64-musl" "13.4.6" - "@next/swc-win32-arm64-msvc" "13.4.6" - "@next/swc-win32-ia32-msvc" "13.4.6" - "@next/swc-win32-x64-msvc" "13.4.6" + "@next/swc-darwin-arm64" "13.5.6" + "@next/swc-darwin-x64" "13.5.6" + "@next/swc-linux-arm64-gnu" "13.5.6" + "@next/swc-linux-arm64-musl" "13.5.6" + "@next/swc-linux-x64-gnu" "13.5.6" + "@next/swc-linux-x64-musl" "13.5.6" + "@next/swc-win32-arm64-msvc" "13.5.6" + "@next/swc-win32-ia32-msvc" "13.5.6" + "@next/swc-win32-x64-msvc" "13.5.6" no-case@^3.0.4: version "3.0.4" @@ -5633,32 +5487,27 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -node-addon-api@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" - integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== +node-addon-api@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e" + integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA== -node-fetch@^2.6.1, node-fetch@^2.6.11: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== +node-fetch@^2.6.1, node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-gyp-build@^4.3.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" - integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== nopt@^4.0.1: version "4.0.3" @@ -5702,13 +5551,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npm-run-path@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" - integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== - dependencies: - path-key "^4.0.0" - nullthrows@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" @@ -5719,60 +5561,70 @@ object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.3, object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" - integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== +object.entries@^1.1.6, object.entries@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -object.fromentries@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== +object.fromentries@^2.0.6, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" object.hasown@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" - integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" + integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== dependencies: - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.values@^1.1.6, object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" omit.js@^2.0.2: version "2.0.2" @@ -5793,42 +5645,27 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - -open@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/open/-/open-9.1.0.tgz#684934359c90ad25742f5a26151970ff8c6c80b6" - integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== - dependencies: - default-browser "^4.0.0" - define-lazy-prop "^3.0.0" - is-inside-container "^1.0.0" - is-wsl "^2.2.0" - -optimism@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" - integrity sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ== +optimism@^0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" + integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ== dependencies: + "@wry/caches" "^1.0.0" "@wry/context" "^0.7.0" - "@wry/trie" "^0.3.0" + "@wry/trie" "^0.4.3" + tslib "^2.3.0" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" ora@^5.4.1: version "5.4.1" @@ -5968,11 +5805,6 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" @@ -6011,9 +5843,9 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== pkg-dir@^4.2.0: version "4.2.0" @@ -6022,12 +5854,12 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -postcss@8.4.14: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== +postcss@8.4.31: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: - nanoid "^3.3.4" + nanoid "^3.3.6" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -6041,12 +5873,12 @@ prettier@^2.8.4: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -pretty-format@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a" - integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw== +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== dependencies: - "@jest/schemas" "^29.4.3" + "@jest/schemas" "^29.6.3" ansi-styles "^5.0.0" react-is "^18.0.0" @@ -6065,7 +5897,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6080,21 +5912,21 @@ punycode@^1.3.2: integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.1.tgz#31207dddd15d43f299fdcdb2f572df65030c19af" - integrity sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg== + version "6.0.4" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" + integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== -pvtsutils@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" - integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== +pvtsutils@^1.3.2, pvtsutils@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== dependencies: - tslib "^2.4.0" + tslib "^2.6.1" pvutils@^1.1.3: version "1.1.3" @@ -6111,51 +5943,41 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -rc-align@^4.0.0: - version "4.0.15" - resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.15.tgz#2bbd665cf85dfd0b0244c5a752b07565e9098577" - integrity sha512-wqJtVH60pka/nOX7/IspElA8gjPNQKIx/ZqJ6heATCkXpe1Zg4cPVrMD2vC96wjsFFL8WsmhPbx9tdMo1qqlIA== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - dom-align "^1.7.0" - rc-util "^5.26.0" - resize-observer-polyfill "^1.5.1" - -rc-cascader@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.8.0.tgz#5eaca8998b2e3f5692d13f16bfe2346eccc87c6a" - integrity sha512-zCz/NzsNRQ1TIfiR3rQNxjeRvgRHEkNdo0FjHQZ6Ay6n4tdCmMrM7+81ThNaf21JLQ1gS2AUG2t5uikGV78obA== +rc-cascader@~3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.21.0.tgz#285332cfe97e75a2d6829887bbbcd6cfa23f2b1d" + integrity sha512-7aADjbfqiR4HrTHG9S019p2jeKM/AxISPA5+sBJR7Mlhm/i+lR7VjBju3KQulJNJLKNEnQYg4TFhcPf2SLua9g== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.2.0" - rc-tree "~5.7.0" - rc-util "^5.6.1" + rc-select "~14.11.0-0" + rc-tree "~5.8.1" + rc-util "^5.37.0" -rc-checkbox@~2.3.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-2.3.2.tgz#f91b3678c7edb2baa8121c9483c664fa6f0aefc1" - integrity sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg== +rc-checkbox@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.1.0.tgz#6be0d9d8de2cc96fb5e37f9036a1c3e360d0a42d" + integrity sha512-PAwpJFnBa3Ei+5pyqMMXdcKYKNBMS+TvSDiLdDnARnMJHC8ESxwPfm4Ao1gJiKtWLdmGfigascnCpwrHFgoOBQ== dependencies: "@babel/runtime" "^7.10.1" - classnames "^2.2.1" + classnames "^2.3.2" + rc-util "^5.25.2" -rc-collapse@~3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.5.2.tgz#abb7d144ad55bd9cbd201fa95bc5b271da2aa7c3" - integrity sha512-/TNiT3DW1t3sUCiVD/DPUYooJZ3BLA93/2rZsB3eM2bGJCCla2X9D2E4tgm7LGMQGy5Atb2lMUn2FQuvQNvavQ== +rc-collapse@~3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.2.tgz#d11538ff9c705a5c988d9a4dfcc051a919692fe3" + integrity sha512-ZRw6ipDyOnfLFySxAiCMdbHtb5ePAsB9mT17PA6y1mRD/W6KHRaZeb5qK/X9xDV1CqgyxMpzw0VdS74PCcUk4A== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.3.4" rc-util "^5.27.0" -rc-dialog@~9.0.0, rc-dialog@~9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.0.2.tgz#aadfebdeba145f256c1fac9b9f509f893cdbb5b8" - integrity sha512-s3U+24xWUuB6Bn2Lk/Qt6rufy+uT+QvWkiFhNBcO9APLxcFFczWamaq7x9h8SCuhfc1nHcW4y8NbMsnAjNnWyg== +rc-dialog@~9.3.4: + version "9.3.4" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.3.4.tgz#e0decb3d4a0dbe36524a67ed2f8fe2daa4b7b73c" + integrity sha512-975X3018GhR+EjZFbxA2Z57SX5rnu0G0/OxFgMMvZK4/hQWEm3MHaNvP4wXpxYDoJsp+xUvVW+GB9CMMCm81jA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/portal" "^1.0.0-8" @@ -6163,320 +5985,312 @@ rc-dialog@~9.0.0, rc-dialog@~9.0.2: rc-motion "^2.3.0" rc-util "^5.21.0" -rc-drawer@~6.1.1: - version "6.1.3" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-6.1.3.tgz#4b2277db09f059be7144dc82d5afede9c2ab2191" - integrity sha512-AvHisO90A+xMLMKBw2zs89HxjWxusM2BUABlgK60RhweIHF8W/wk0hSOrxBlUXoA9r1F+10na3g6GZ97y1qDZA== +rc-drawer@~7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.0.0.tgz#5632ad00284431665d4ff622b274d234fc9eefb2" + integrity sha512-ePcS4KtQnn57bCbVXazHN2iC8nTPCXlWEIA/Pft87Pd9U7ZeDkdRzG47jWG2/TAFXFlFltRAMcslqmUM8NPCGA== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/portal" "^1.0.0-6" + "@rc-component/portal" "^1.1.1" classnames "^2.2.6" rc-motion "^2.6.1" - rc-util "^5.21.2" + rc-util "^5.36.0" -rc-dropdown@~4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68" - integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g== +rc-dropdown@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.1.0.tgz#418a68939631520de80d0865d02b440eeeb4168e" + integrity sha512-VZjMunpBdlVzYpEdJSaV7WM7O0jf8uyDjirxXLZRNZ+tAC+NzD3PXPEtliFwGzVwBBdCmGuSqiS9DWcOLxQ9tw== dependencies: "@babel/runtime" "^7.18.3" + "@rc-component/trigger" "^1.7.0" classnames "^2.2.6" - rc-trigger "^5.3.1" rc-util "^5.17.0" -rc-field-form@~1.27.0: - version "1.27.4" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.4.tgz#53600714af5b28c226c70d34867a8c52ccd64d44" - integrity sha512-PQColQnZimGKArnOh8V2907+VzDCXcqtFvHgevDLtqWc/P7YASb/FqntSmdS8q3VND5SHX3Y1vgMIzY22/f/0Q== +rc-field-form@~1.41.0: + version "1.41.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.41.0.tgz#660ed8691fdabbc1e5b1ee6b5e0e4f534b295cf0" + integrity sha512-k9AS0wmxfJfusWDP/YXWTpteDNaQ4isJx9UKxx4/e8Dub4spFeZ54/EuN2sYrMRID/+hUznPgVZeg+Gf7XSYCw== dependencies: "@babel/runtime" "^7.18.0" async-validator "^4.1.0" - rc-util "^5.8.0" + rc-util "^5.32.2" -rc-image@~5.13.0: - version "5.13.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.13.0.tgz#1ed9b852a40b5eff34786ba7d2f0e9d26eeab874" - integrity sha512-iZTOmw5eWo2+gcrJMMcnd7SsxVHl3w5xlyCgsULUdJhJbnuI8i/AL0tVOsE7aLn9VfOh1qgDT3mC2G75/c7mqg== +rc-image@~7.5.1: + version "7.5.1" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.5.1.tgz#39a93354e14fe3e5eaafd9c9464e8fe3c6c171a0" + integrity sha512-Z9loECh92SQp0nSipc0MBuf5+yVC05H/pzC+Nf8xw1BKDFUJzUeehYBjaWlxly8VGBZJcTHYri61Fz9ng1G3Ag== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" classnames "^2.2.6" - rc-dialog "~9.0.0" + rc-dialog "~9.3.4" rc-motion "^2.6.2" - rc-util "^5.0.6" + rc-util "^5.34.1" -rc-input-number@~7.4.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.4.0.tgz#b8b4ffa8bbc04198e79ce8b9611756d046d128ec" - integrity sha512-r/Oub/sPYbzqLNUOHnnc9sbCu78a81KX+RCbRwmpvB4W6nptUySbdWS5KHV4Hak5CAE1LAd+wWm5JjvZizG1FA== +rc-input-number@~8.6.1: + version "8.6.1" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-8.6.1.tgz#f0ebc66c34c8775a3245953521abcbd240d7fe03" + integrity sha512-gaAMUKtUKLktJ3Yx93tjgYY1M0HunnoqzPEqkb9//Ydup4DcG0TFL9yHBA3pgVdNIt5f0UWyHCgFBj//JxeD6A== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" - rc-util "^5.23.0" + rc-input "~1.4.0" + rc-util "^5.28.0" -rc-input@^0.2.1, rc-input@^0.2.2, rc-input@~0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.2.2.tgz#de1c3c0e090d15777a6b1a3fd4a9566e25b3fbee" - integrity sha512-xgkVcFgtRO0Hl9hmvslZhObNyxbSpTmy3nR1Tk4XrjjZ9lFJ7GcJBy6ss30Pdb0oX36cHzLN8I7VCjBGeRNB9A== +rc-input@~1.4.0, rc-input@~1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.4.3.tgz#03f720f88cb56924cd1d5f78f859f41f2d62d1e0" + integrity sha512-aHyQUAIRmTlOnvk5EcNqEpJ+XMtfMpYRAJayIlJfsvvH9cAKUWboh4egm23vgMA7E+c/qm4BZcnrDcA960GC1w== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.0.0.tgz#688846d698943340334657d96c1448d8b57e5666" - integrity sha512-58NSeM6R5MrgYAhR2TH27JgAN7ivp3iBTmty3q6gvrrGHelPMdGxpJ5aH7AIlodCrPWLAm1lT4XoiuI4s9snXA== +rc-mentions@~2.10.1: + version "2.10.1" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.10.1.tgz#842a227cb1f9f798cbf0c906a519f7de3ab8a9b9" + integrity sha512-72qsEcr/7su+a07ndJ1j8rI9n0Ka/ngWOLYnWMMv0p2mi/5zPwPrEDTt6Uqpe8FWjWhueDJx/vzunL6IdKDYMg== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.22.5" + "@rc-component/trigger" "^1.5.0" classnames "^2.2.6" - rc-input "^0.2.2" - rc-menu "~9.8.0" - rc-textarea "^1.0.0" - rc-trigger "^5.0.4" - rc-util "^5.22.5" + rc-input "~1.4.0" + rc-menu "~9.12.0" + rc-textarea "~1.6.1" + rc-util "^5.34.1" -rc-menu@~9.8.0, rc-menu@~9.8.2: - version "9.8.2" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.8.2.tgz#179df4ea0fc46fffb7a21e18d3cddb901ce35986" - integrity sha512-EahOJVjLuEnJsThoPN+mGnVm431RzVzDLZWHRS/YnXTQULa7OsgdJa/Y7qXxc3Z5sz8mgT6xYtgpmBXLxrZFaQ== +rc-menu@~9.12.0, rc-menu@~9.12.4: + version "9.12.4" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.12.4.tgz#4959b5eeb780be7ff52aac31952b35efca46b9a3" + integrity sha512-t2NcvPLV1mFJzw4F21ojOoRVofK2rWhpKPx69q2raUsiHPDP6DDevsBILEYdsIegqBeSXoWs2bf6CueBKg3BFg== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.17.0" classnames "2.x" rc-motion "^2.4.3" - rc-overflow "^1.2.8" - rc-trigger "^5.1.2" + rc-overflow "^1.3.1" rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.0, rc-motion@^2.6.1, rc-motion@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.3.tgz#e6d8ca06591c2c1bcd3391a8e7a822ebc4d94e9c" - integrity sha512-xFLkes3/7VL/J+ah9jJruEW/Akbx5F6jVa2wG5o/ApGKQKSOd5FR3rseHLL9+xtJg4PmCwo6/1tqhDO/T+jFHA== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" + integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.21.0" -rc-notification@~5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.0.2.tgz#31811ac90e9516d88990fd2a93dbe61cc1712eec" - integrity sha512-74wUFiLlyr6lRGEY1m1BaTiDp+0lIT4FRAblMnh9FApyK2JGdsSLbrQ/1rgM7d2N/IX5UIr8kLLW3TdXxFt/jQ== +rc-notification@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.3.0.tgz#e31c86fe2350598ade8cff383babd1befa7a94fe" + integrity sha512-WCf0uCOkZ3HGfF0p1H4Sgt7aWfipxORWTPp7o6prA3vxwtWhtug3GfpYls1pnBp4WA+j8vGIi5c2/hQRpGzPcQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-motion "^2.6.0" + rc-motion "^2.9.0" rc-util "^5.20.1" -rc-overflow@^1.0.0, rc-overflow@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.8.tgz#40f140fabc244118543e627cdd1ef750d9481a88" - integrity sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ== +rc-overflow@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" + integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" - rc-util "^5.19.2" + rc-util "^5.37.0" -rc-pagination@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.2.0.tgz#4f2fdba9fdac0f48e5c9fb1141973818138af7e1" - integrity sha512-5tIXjB670WwwcAJzAqp2J+cOBS9W3cH/WU1EiYwXljuZ4vtZXKlY2Idq8FZrnYBz8KhN3vwPo9CoV/SJS6SL1w== +rc-pagination@~4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.0.4.tgz#ea401388ae86eac17ed5b41212d487f12b65b773" + integrity sha512-GGrLT4NgG6wgJpT/hHIpL9nELv27A1XbSZzECIuQBQTVSf4xGKxWr6I/jhpRPauYEWEbWVw22ObG6tJQqwJqWQ== dependencies: "@babel/runtime" "^7.10.1" - classnames "^2.2.1" + classnames "^2.3.2" + rc-util "^5.38.0" -rc-picker@~3.1.1: - version "3.1.5" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.1.5.tgz#49953655a92cda3dee59678f4cbcdee61f5b0f0c" - integrity sha512-Hh3ml+u+5mxLfl4ahVWlRGiX5+0EJrALR6tSW9yP0eea+6j+YjvjfetbvuVidViMDMweZa38dr8HTfAFLG6GFw== +rc-picker@~3.14.6: + version "3.14.6" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.14.6.tgz#60fc34f9883272e10f6c593fa6d82e7e7a70781b" + integrity sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.5.0" classnames "^2.2.1" - rc-trigger "^5.0.4" - rc-util "^5.27.0" + rc-util "^5.30.0" -rc-progress@~3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.4.1.tgz#a9ffe099e88a4fc03afb09d8603162bf0760d743" - integrity sha512-eAFDHXlk8aWpoXl0llrenPMt9qKHQXphxcVsnKs0FHC6eCSk1ebJtyaVjJUzKe0233ogiLDeEFK1Uihz3s67hw== +rc-progress@~3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.5.1.tgz#a3cdfd2fe04eb5c3d43fa1c69e7dd70c73b102ae" + integrity sha512-V6Amx6SbLRwPin/oD+k1vbPrO8+9Qf8zW1T8A7o83HdNafEVvAxPV5YsgtKFP+Ud5HghLj33zKOcEHrcrUGkfw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-util "^5.16.1" -rc-rate@~2.9.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.9.2.tgz#4a58965d1ecf91896ebae01d458b59056df0b4ea" - integrity sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g== +rc-rate@~2.12.0: + version "2.12.0" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.12.0.tgz#0182deffed3b009cdcc61660da8746c39ed91ed5" + integrity sha512-g092v5iZCdVzbjdn28FzvWebK2IutoVoiTeqoLTj9WM7SjA/gOJIw5/JFZMRyJYYVe1jLAU2UhAfstIpCNRozg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-util "^5.0.1" -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.3.1.tgz#b61b9f27048001243617b81f95e53d7d7d7a6a3d" - integrity sha512-iFUdt3NNhflbY3mwySv5CA1TC06zdJ+pfo0oc27xpf4PIOvfZwZGtD9Kz41wGYqC4SLio93RVAirSSpYlV/uYg== +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" + integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== dependencies: "@babel/runtime" "^7.20.7" classnames "^2.2.1" - rc-util "^5.27.0" + rc-util "^5.38.0" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.2.tgz#14c9077a1dae9c2ccb2ef5fbc5662c1c48c7ce8e" - integrity sha512-qGo1bCr83ESXpXVOCXjFe1QJlCAQXyi9KCiy8eX3rIMYlTeJr/ftySIaTnYsitL18SvWf5ZEHsfqIWoX0EMfFQ== +rc-segmented@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.2.2.tgz#a34f12ce6c0975fc3042ae7656bcd18e1744798e" + integrity sha512-Mq52M96QdHMsNdE/042ibT5vkcGcD5jxKp7HgPC2SRofpia99P5fkfHy1pEaajLMF/kj0+2Lkq1UZRvqzo9mSA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.2.0: - version "14.2.0" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.2.0.tgz#60e83a7d5a5dae7fd9e3918d9b209074ea2c92d4" - integrity sha512-tvxHmbAA0EIhBkB7dyaRhcBUIWHocQbUFY/fBlezj2jg5p65a5VQ/UhBg2I9TA1wjpsr5CCx0ruZPkYcUMjDoQ== +rc-select@~14.11.0, rc-select@~14.11.0-0: + version "14.11.0" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.11.0.tgz#37c63acea92ac1dcd0e1b7ebd9c0614b53dd8346" + integrity sha512-8J8G/7duaGjFiTXCBLWfh5P+KDWyA3KTlZDfV3xj/asMPqB2cmxfM+lH50wRiPIRsCQ6EbkCFBccPuaje3DHIg== dependencies: "@babel/runtime" "^7.10.1" + "@rc-component/trigger" "^1.5.0" classnames "2.x" rc-motion "^2.0.1" - rc-overflow "^1.0.0" - rc-trigger "^5.0.4" + rc-overflow "^1.3.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.13" + rc-virtual-list "^3.5.2" -rc-slider@~10.1.0: - version "10.1.1" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.1.1.tgz#5e82036e60b61021aba3ea0e353744dd7c74e104" - integrity sha512-gn8oXazZISEhnmRinI89Z/JD/joAaM35jp+gDtIVSTD/JJMCCBqThqLk1SVJmvtfeiEF/kKaFY0+qt4SDHFUDw== +rc-slider@~10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.5.0.tgz#1bd4853d114cb3403b67c485125887adb6a2a117" + integrity sha512-xiYght50cvoODZYI43v3Ylsqiw14+D7ELsgzR40boDZaya1HFa1Etnv9MDkQE8X/UrXAffwv2AcNAhslgYuDTw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-util "^5.27.0" -rc-steps@~6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.0.tgz#f7148f8097d5d135f19b96c1b4f4b50ad6093753" - integrity sha512-+KfMZIty40mYCQSDvYbZ1jwnuObLauTiIskT1hL4FFOBHP6ZOr8LK0m143yD3kEN5XKHSEX1DIwCj3AYZpoeNQ== +rc-steps@~6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" + integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== dependencies: "@babel/runtime" "^7.16.7" classnames "^2.2.3" rc-util "^5.16.1" -rc-switch@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.0.0.tgz#55fbf99fc2d680791175037d379e170ba51fbe78" - integrity sha512-IfrYC99vN0gKaTyjQdqYuADU0eH00SAFHg3jOp8HrmUpJruhV1SohJzrCbPqPraZeX/6X/QKkdLfkdnUub05WA== +rc-switch@~4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" + integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.21.0" classnames "^2.2.1" - rc-util "^5.0.1" + rc-util "^5.30.0" -rc-table@~7.30.2: - version "7.30.4" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.30.4.tgz#08da9ed849fa700a15a87d097c3a65f5622ca875" - integrity sha512-NXfitP7CEXAnnuxdEYZNwEMyWmWOSsv9WgiP2yI10pyy0BwomFn4aa8nX/9x832edLwuIlrYuL6ojfOK60An2w== +rc-table@~7.37.0: + version "7.37.0" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.37.0.tgz#e7cbf5c577bef27322fcd75fbdda99d1d6e70627" + integrity sha512-hEB17ktLRVfVmdo+U8MjGr+PuIgdQ8Cxj/N5lwMvP/Az7TOrQxwTMLVEDoj207tyPYLTWifHIF9EJREWwyk67g== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/context" "^1.3.0" + "@rc-component/context" "^1.4.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.27.1" + rc-util "^5.37.0" + rc-virtual-list "^3.11.1" -rc-tabs@~12.5.6: - version "12.5.6" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-12.5.6.tgz#611728575d5f3ef010a8e5e75d2c1b67bb71e86a" - integrity sha512-aArXHzxK7YICxe+622CZ8FlO5coMi8P7E6tXpseCPKm1gdTjUt0LrQK1/AxcrRXZXG3K4QqhlKmET0+cX5DQaQ== +rc-tabs@~14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-14.0.0.tgz#ac1798959e1835c4d5216c9dae80943b7e5505f6" + integrity sha512-lp1YWkaPnjlyhOZCPrAWxK6/P6nMGX/BAZcAC3nuVwKz0Byfp+vNnQKK8BRCP2g/fzu+SeB5dm9aUigRu3tRkQ== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "~4.0.0" - rc-menu "~9.8.0" + rc-dropdown "~4.1.0" + rc-menu "~9.12.0" rc-motion "^2.6.2" rc-resize-observer "^1.0.0" - rc-util "^5.16.0" + rc-util "^5.34.1" -rc-textarea@^1.0.0, rc-textarea@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.0.1.tgz#44b1e09eda37a7af19cf884251e530cc7b03050b" - integrity sha512-dtIm96apjJpCUcCeTtbnLGJaVlqbOqVgN0P9z+bqMSi7rcV5QVeUtBnG+jQTGk/uD183Z7jbhc8Dx7G3luDCwg== +rc-textarea@~1.6.1, rc-textarea@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.6.3.tgz#3f294fdf5dfadbe9d0e7b695cea4da557728a9be" + integrity sha512-8k7+8Y2GJ/cQLiClFMg8kUXOOdvcFQrnGeSchOvI2ZMIVvX5a3zQpLxoODL0HTrvU63fPkRmMuqaEcOF9dQemA== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" - rc-input "^0.2.1" + rc-input "~1.4.0" rc-resize-observer "^1.0.0" rc-util "^5.27.0" -rc-tooltip@~5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.3.1.tgz#3dde4e1865f79cd23f202bba4e585c2a1173024b" - integrity sha512-e6H0dMD38EPaSPD2XC8dRfct27VvT2TkPdoBSuNl3RRZ5tspiY/c5xYEmGC0IrABvMBgque4Mr2SMZuliCvoiQ== +rc-tooltip@~6.1.3: + version "6.1.3" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.1.3.tgz#83b97004a1ab918ed4936bbf089bc754254efd1b" + integrity sha512-HMSbSs5oieZ7XddtINUddBLSVgsnlaSb3bZrzzGWjXa7/B7nNedmsuz72s7EWFEro9mNa7RyF3gOXKYqvJiTcQ== dependencies: "@babel/runtime" "^7.11.2" + "@rc-component/trigger" "^1.18.0" classnames "^2.3.1" - rc-trigger "^5.3.1" -rc-tree-select@~5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.6.0.tgz#f34147f4c14341430bcece481804496d0abd3371" - integrity sha512-XG6pu0a9l6+mzhQqUYfR2VIONbe/3LjVc3wKt28k6uBMZsI1j+SSxRyt/7jWRq8Kok8jHJBQASlDg6ehr9Sp0w== +rc-tree-select@~5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.17.0.tgz#be5bcad09b5874bfcff14be758e76dd992e42359" + integrity sha512-7sRGafswBhf7n6IuHyCEFCildwQIgyKiV8zfYyUoWfZEFdhuk7lCH+DN0aHt+oJrdiY9+6Io/LDXloGe01O8XQ== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.2.0" - rc-tree "~5.7.0" + rc-select "~14.11.0-0" + rc-tree "~5.8.1" rc-util "^5.16.1" -rc-tree@~5.7.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.7.2.tgz#270ea7d9e1b2e5e81cd3659eba2fbd022a4831f6" - integrity sha512-nmnL6qLnfwVckO5zoqKL2I9UhwDqzyCtjITQCkwhimyz1zfuFkG5ZPIXpzD/Guzso94qQA/QrMsvzic5W6QDjg== +rc-tree@~5.8.1, rc-tree@~5.8.2: + version "5.8.2" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.2.tgz#ed3a3f7c56597bbeab3303407a9e1739bbf15621" + integrity sha512-xH/fcgLHWTLmrSuNphU8XAqV7CdaOQgm4KywlLGNoTMhDAcNR3GVNP6cZzb0GrKmIZ9yae+QLot/cAgUdPRMzg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.0.1" rc-util "^5.16.1" - rc-virtual-list "^3.4.8" - -rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.3.1, rc-trigger@^5.3.4: - version "5.3.4" - resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.4.tgz#6b4b26e32825677c837d1eb4d7085035eecf9a61" - integrity sha512-mQv+vas0TwKcjAO2izNPkqR4j86OemLRmvL2nOzdP9OWNWA1ivoTt5hzFqYNW9zACwmTezRiN8bttrC7cZzYSw== - dependencies: - "@babel/runtime" "^7.18.3" - classnames "^2.2.6" - rc-align "^4.0.0" - rc-motion "^2.0.0" - rc-util "^5.19.2" + rc-virtual-list "^3.5.1" -rc-upload@~4.3.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.4.tgz#83ff7d3867631c37adbfd72ea3d1fd7e97ca84af" - integrity sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ== +rc-upload@~4.5.2: + version "4.5.2" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.5.2.tgz#ea493fbaaf57d9369ee954b20e1d8bc35c818a1a" + integrity sha512-QO3ne77DwnAPKFn0bA5qJM81QBjQi0e0NHdkvpFyY73Bea2NfITiotqJqVjHgeYPOJu5lLVR32TNGP084aSoXA== dependencies: "@babel/runtime" "^7.18.3" classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.24.4, rc-util@^5.26.0, rc-util@^5.27.0, rc-util@^5.27.1, rc-util@^5.6.1, rc-util@^5.8.0, rc-util@^5.9.4: - version "5.27.2" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.27.2.tgz#472a7bab26a62856c2c016d18dc6356e46d01012" - integrity sha512-8XHRbeJOWlTR2Hk1K2xLaPOf7lZu+3taskAGuqOPccA676vB3ygrz3ZgdrA3wml40CzR9RlIEHDWwI7FZT3wBQ== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.28.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1: + version "5.38.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.38.1.tgz#4915503b89855f5c5cd9afd4c72a7a17568777bb" + integrity sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng== dependencies: "@babel/runtime" "^7.18.3" - react-is "^16.12.0" + react-is "^18.2.0" -rc-virtual-list@^3.4.13, rc-virtual-list@^3.4.8: - version "3.4.13" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.13.tgz#20acc934b263abcf7b7c161f50ef82281b2f7e8d" - integrity sha512-cPOVDmcNM7rH6ANotanMDilW/55XnFPw0Jh/GQYtrzZSy3AmWvCnqVNyNC/pgg3lfVmX2994dlzAhuUrd4jG7w== +rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: + version "3.11.3" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.3.tgz#77d4e12e20c1ba314b43c0e37e118296674c5401" + integrity sha512-tu5UtrMk/AXonHwHxUogdXAWynaXsrx1i6dsgg+lOo/KJSF8oBAcprh1z5J3xgnPJD5hXxTL58F8s8onokdt0Q== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" rc-resize-observer "^1.0.0" - rc-util "^5.15.0" + rc-util "^5.36.0" react-cookie@^4.0.3: version "4.1.1" @@ -6504,16 +6318,16 @@ react-highlight-words@^0.20.0: memoize-one "^4.0.0" prop-types "^15.5.8" -react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-shallow-renderer@^16.15.0: version "16.15.0" resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" @@ -6522,15 +6336,6 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -react-sortable-hoc@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" - integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg== - dependencies: - "@babel/runtime" "^7.2.0" - invariant "^2.2.4" - prop-types "^15.5.7" - react-test-renderer@^18.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" @@ -6590,19 +6395,31 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +reflect.getprototypeof@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" + integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" -regexp.prototype.flags@^1.4.3: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" - functions-have-names "^1.2.3" + set-function-name "^2.0.0" relay-runtime@12.0.0: version "12.0.0" @@ -6670,30 +6487,21 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.10.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.20.0, resolve@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== +resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.11.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6727,13 +6535,6 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -run-applescript@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-5.0.0.tgz#e11e1c932e055d5c6b40d98374e0268d9b11899c" - integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== - dependencies: - execa "^5.0.0" - run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -6753,20 +6554,35 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" +safe-array-concat@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== + dependencies: + call-bind "^1.0.5" + get-intrinsic "^1.2.2" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" + integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" is-regex "^1.1.4" +safe-stable-stringify@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -6788,12 +6604,12 @@ schema-utils@3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -scroll-into-view-if-needed@^3.0.3: - version "3.0.6" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.6.tgz#2c803a509c1036bc4a9c009fecc5c145f87e47cf" - integrity sha512-x+CW0kOzlFNOnseF0DBr0AJ5m+TgGmSOdEZwyiZW0gV87XBvxQKw5A8DvFFgabznA68XqLgVX+PwPX8OzsFvRA== +scroll-into-view-if-needed@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" + integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== dependencies: - compute-scroll-into-view "^3.0.0" + compute-scroll-into-view "^3.0.2" scuid@^1.1.0: version "1.1.0" @@ -6801,26 +6617,19 @@ scuid@^1.1.0: integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== "semver@2 || 3 || 4 || 5", semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.3.5: - version "7.4.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318" - integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw== - dependencies: - lru-cache "^6.0.0" +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.7: - version "7.5.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" - integrity sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ== +semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -6838,6 +6647,26 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +set-function-name@^2.0.0, set-function-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" @@ -6889,11 +6718,6 @@ slash@3.0.0, slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== - slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -6958,9 +6782,9 @@ spdx-compare@^1.0.0: spdx-ranges "^2.0.0" spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" @@ -6979,9 +6803,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.12" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz#69077835abe2710b65f03969898b6637b505a779" - integrity sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA== + version "3.0.16" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" + integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== spdx-ranges@^2.0.0: version "2.1.1" @@ -7049,45 +6873,46 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: strip-ansi "^6.0.1" string.prototype.matchall@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" - integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" side-channel "^1.0.4" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" string_decoder@^1.1.1: version "1.3.0" @@ -7118,12 +6943,7 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -7136,9 +6956,9 @@ styled-jsx@5.1.1: client-only "0.0.1" stylis@^4.0.13: - version "4.1.3" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.3.tgz#fd2fbe79f5fed17c55269e16ed8da14c84d069f7" - integrity sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA== + version "4.3.1" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.1.tgz#ed8a9ebf9f76fe1e12d462f5cc3c4c980b23a7eb" + integrity sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ== supports-color@^5.3.0: version "5.5.0" @@ -7174,10 +6994,11 @@ swap-case@^2.0.2: tslib "^2.0.3" swr@^2.0.0: - version "2.1.3" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.3.tgz#ae3608d4dc19f75121e0b51d1982735fbf02f52e" - integrity sha512-g3ApxIM4Fjbd6vvEAlW60hJlKcYxHb+wtehogTygrh6Jsw7wNagv9m4Oj5Gq6zvvZw0tcyhVGL9L0oISvl3sUw== + version "2.2.4" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.4.tgz#03ec4c56019902fbdc904d78544bd7a9a6fa3f07" + integrity sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ== dependencies: + client-only "^0.0.1" use-sync-external-store "^1.2.0" symbol-observable@^4.0.0: @@ -7185,14 +7006,6 @@ symbol-observable@^4.0.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== -synckit@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.5.tgz#b7f4358f9bb559437f9f167eb6bc46b3c9818fa3" - integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== - dependencies: - "@pkgr/utils" "^2.3.1" - tslib "^2.5.0" - tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -7229,11 +7042,6 @@ title-case@^3.0.3: dependencies: tslib "^2.0.3" -titleize@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" - integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== - tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -7273,6 +7081,11 @@ treeify@^1.1.0: resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + ts-invariant@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" @@ -7286,9 +7099,9 @@ ts-log@^2.2.3: integrity sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA== ts-node@^10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" "@tsconfig/node10" "^1.0.7" @@ -7304,25 +7117,20 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -tsconfig-paths@^3.14.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@~2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== tslib@~2.0.1: version "2.0.3" @@ -7344,12 +7152,10 @@ tslib@~2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" +tslib@~2.5.0: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -7373,6 +7179,36 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -7390,14 +7226,14 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^5.0.4: - version "5.1.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" - integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== ua-parser-js@^1.0.35: - version "1.0.35" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.35.tgz#c4ef44343bc3db0a3cbefdf21822f1b1fc1ab011" - integrity sha512-fKnGuqmTBnIE+/KXSzCn4db8RTigUzw1AN0DmdU6hJovUTbYJKyqj+8Mt1c4VfRDnOVJnENmfYkIPZ946UrSAA== + version "1.0.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" + integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== unbox-primitive@^1.0.2: version "1.0.2" @@ -7414,6 +7250,11 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + universal-cookie@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" @@ -7429,15 +7270,10 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -7468,16 +7304,6 @@ urlpattern-polyfill@^8.0.0: resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== -use-json-comparison@^1.0.3: - version "1.0.6" - resolved "https://registry.yarnpkg.com/use-json-comparison/-/use-json-comparison-1.0.6.tgz#a012bbc258ce745db1f56745dc653f575226cb21" - integrity sha512-xPadt5yMRbEmVfOSGFSMqjjICrq7nLbfSH3rYIXsrtcuFX7PmbYDN/ku8ObBn3v8o/yZelO1OxUS5+5TI3+fUw== - -use-media-antd-query@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/use-media-antd-query/-/use-media-antd-query-1.1.0.tgz#f083ad7e292c1c0261b6bbfaac0edc3e0920d85d" - integrity sha512-B6kKZwNV4R+l4Rl11sWO7HqOay9alzs1Vp1b4YJqjz33YxbltBCZtt/yxXxkXN9rc1S7OeEL/GbwC30Wmqhw6Q== - use-sync-external-store@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" @@ -7499,13 +7325,13 @@ v8-compile-cache-lib@^3.0.1: integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" - integrity sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA== + version "9.2.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" + integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" + convert-source-map "^2.0.0" validate-npm-package-license@^3.0.1: version "3.0.4" @@ -7550,9 +7376,9 @@ wcwidth@^1.0.1: defaults "^1.0.3" web-streams-polyfill@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== + version "3.3.2" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" + integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== webcrypto-core@^1.7.7: version "1.7.7" @@ -7589,22 +7415,49 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -7613,12 +7466,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -word-wrap@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrap-ansi@^6.2.0: +wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== @@ -7659,11 +7507,16 @@ write-file-atomic@^4.0.2: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@8.13.0, ws@^8.12.0: +ws@8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@^8.12.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -7704,9 +7557,9 @@ yaml@1.10.2, yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4" - integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw== + version "2.3.4" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" + integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== yargs-parser@^18.1.2: version "18.1.3" @@ -7738,7 +7591,7 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.0.0: +yargs@^17.0.0, yargs@^17.3.1: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -7751,19 +7604,6 @@ yargs@^17.0.0: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^17.3.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -7785,8 +7625,3 @@ zen-observable@0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== - -zod@3.21.4: - version "3.21.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" - integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== From f1eb79a03476491cebc6cf950ae656f57236ad16 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jan 2024 16:32:04 +0100 Subject: [PATCH 127/878] Upgrade GraphQL schema (it changed because the HotChocolate version changed) --- frontend/type-defs.graphqls | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index e937a4db..36c0e750 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -1035,11 +1035,6 @@ enum UpdateDatabaseErrorCode { UNKNOWN_DATABASE } -directive @authorize("Defines when when the authorize directive shall be applied.By default the authorize directives are applied during the validation phase." apply: ApplyPolicy! = VALIDATION "The name of the authorization policy that determines access to the annotated resource." policy: String "Roles that are allowed to access the annotated resource." roles: [String!]) repeatable on OBJECT | FIELD_DEFINITION - -"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR - scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." From 6545717498a3a3b011f8b42560be18c761049c09 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 6 Feb 2024 16:33:16 +0100 Subject: [PATCH 128/878] Analyse code quality and style on build --- backend/src/Database.csproj | 4 ++++ backend/test/Database.Tests.csproj | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 4d61b17a..9b8d4fa4 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -6,8 +6,12 @@ 31666837686487542159 enable + + true true true + latest-Recommended + true diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 9b97ad72..a8a619e5 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -2,11 +2,16 @@ net8.0 + false 12.0 false enable + + true true true + latest-Recommended + true false From 006e6a265902f903f7110cd6ede037ca9d6c1577 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 6 Feb 2024 16:42:23 +0100 Subject: [PATCH 129/878] Add GNU Make target `inspect` to inspect code with JetBrains --- backend/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/Makefile b/backend/Makefile index a5e892d1..c4730008 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -84,6 +84,11 @@ analyze : ## Analyze with Roslynator dotnet tool run roslynator analyze .PHONY : analyze +# https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html#install-and-use-resharper-command-line-tools-as-net-core-tools +inspect : ## Inspect with ReSharper + dotnet tool run jb inspectcode --build ./Database.sln +.PHONY : inspect + play : ## Run facts in `Test.Play` (semi-alternative to the buggy `dotnet script` REPL) dotnet test --filter "FullyQualifiedName~Test.Play" .PHONY : play From 477b56609b2f466535a56cc0b93d2c2e0f6b9c90 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Apr 2024 15:16:14 +0200 Subject: [PATCH 130/878] Remove version from docker-compose file as it is obsolete as of Docker v2.25.0 (and never served a real purpose anyway according to https://github.com/compose-spec/compose-spec/blob/master/spec.md#version-and-name-top-level-elements) --- docker-compose.production.yml | 2 -- docker-compose.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c5d43bce..4e7fa0c5 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,5 +1,3 @@ -version: '3.3' - services: database: # https://hub.docker.com/_/postgres restart: always diff --git a/docker-compose.yml b/docker-compose.yml index cfc4ca44..2b331aa7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.3' - services: database: # https://hub.docker.com/_/postgres image: postgres:13.13-bookworm From b44111725a87f271fda40aa4f84510463dcbf077 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Apr 2024 17:25:45 +0200 Subject: [PATCH 131/878] Add vscode workspace settings --- .gitignore | 1 - backend/.vscode/settings.json | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 backend/.vscode/settings.json diff --git a/.gitignore b/.gitignore index 72f15aa8..6b7a9f17 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ /tags* .env -.vscode # Compiled output dist diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json new file mode 100644 index 00000000..b3a1df7c --- /dev/null +++ b/backend/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "ms-dotnettools.csdevkit" +} \ No newline at end of file From 9652b8ce50cf4b568c9777472ac779d565e5fa7a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Apr 2024 17:29:28 +0200 Subject: [PATCH 132/878] Ignore the .mono directory --- backend/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/.gitignore b/backend/.gitignore index 29292c49..7893655f 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -4,6 +4,7 @@ /src/files/ /trace.* /.secrets +/.mono /secrets /ssl From d20ca05fbfda04eb42e54a6c957cf2c3e32928b7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Apr 2024 17:32:34 +0200 Subject: [PATCH 133/878] Use JetBrain's cleanupcode to format code, apply syntax rules, and clean-up code --- backend/Makefile | 31 ++++++++++++++++--------------- backend/dotnet-tools.json | 6 ------ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index c4730008..8faed0ee 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -85,12 +85,12 @@ analyze : ## Analyze with Roslynator .PHONY : analyze # https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html#install-and-use-resharper-command-line-tools-as-net-core-tools -inspect : ## Inspect with ReSharper - dotnet tool run jb inspectcode --build ./Database.sln +inspect : ## Inspect with ReSharper, for example, `make OUTPUT=./inspection.xml inspect` + dotnet tool run jb inspectcode --build ./Database.sln -o ${OUTPUT} .PHONY : inspect -play : ## Run facts in `Test.Play` (semi-alternative to the buggy `dotnet script` REPL) - dotnet test --filter "FullyQualifiedName~Test.Play" +play : ## Run tests in `Database.Tests.Play` (semi-alternative to the buggy `dotnet script` REPL) + dotnet test --filter "FullyQualifiedName~Database.Tests.Play" .PHONY : play # TODO Apply not only to `src` folder but ignore files and directories within .gitignore. @@ -109,19 +109,20 @@ dos2unix : ## Strip the byte-order mark, also known as, BOM, and remove carriage # find . -type f -exec dos2unix {} \; .PHONY : dos2unix -# TODO We run `dotnet-format` three times because subsequent runs sometimes -# still make changes. It would be best if we'd check the output to figure out -# whether changes have been made or not and run it again and again until no -# more changes are made. -dosformat : ## Format code and add byte-order marks - dotnet tool run dotnet-format && \ - dotnet tool run dotnet-format && \ - dotnet tool run dotnet-format -.PHONY : dosformat - -format : dosformat dos2unix ## Format code +format : ## Format code + dotnet tool run jb cleanupcode --profile="Built-in: Reformat Code" ./Database.sln .PHONY : format +syntax : ## Apply syntax style + dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./Database.sln +.PHONY : syntax + +# The profile `Built-in: Full Cleanup` does not update file headers. +# See https://www.jetbrains.com/help/resharper/Code_Cleanup__Index.html#profiles +cleanup : ## Clean-up code + dotnet tool run jb cleanupcode ./Database.sln +.PHONY : cleanup + update-packages : ## Update packages for subproject in src test ; do ( \ cd ./$${subproject} && \ diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index bbed6b37..eea68ae2 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -26,12 +26,6 @@ "dotnet-ef" ] }, - "dotnet-format": { - "version": "5.1.250801", - "commands": [ - "dotnet-format" - ] - }, "dotnet-script": { "version": "1.5.0", "commands": [ From dcb3f188d41be0d82748eeca922f48014a3346de Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Apr 2024 17:38:54 +0200 Subject: [PATCH 134/878] Use `NUnit` instead of `xUnit` --- backend/test/Database.Tests.csproj | 18 +++++------------- backend/test/Play.cs | 15 +++++++-------- backend/test/Program.cs | 19 +++++++++---------- backend/test/xunit.runner.json | 6 ------ 4 files changed, 21 insertions(+), 37 deletions(-) delete mode 100644 backend/test/xunit.runner.json diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index a8a619e5..0922038a 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -21,23 +21,14 @@ - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - PreserveNewest - @@ -49,4 +40,5 @@ + diff --git a/backend/test/Play.cs b/backend/test/Play.cs index ab7914b0..be721d28 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -1,15 +1,14 @@ using System; using System.Threading.Tasks; -using Xunit; +using NUnit.Framework; -namespace Test +namespace Database.Tests; + +public sealed class Play { - public sealed class Play + [Test] + public async Task Do() { - [Fact] - public async Task Do() - { - Console.WriteLine("Do play!"); - } + Console.WriteLine("Do play!"); } } \ No newline at end of file diff --git a/backend/test/Program.cs b/backend/test/Program.cs index 3fc4eadf..6284d39e 100644 --- a/backend/test/Program.cs +++ b/backend/test/Program.cs @@ -1,14 +1,13 @@ -namespace Database.Tests +using NUnitLite; + +namespace Database.Tests; + +public static class Program { - public class Program + public static int Main( + string[] commandLineArguments + ) { - public static int Main() - { - return Xunit.ConsoleClient.Program.Main( - new[] { - typeof(Program).Assembly.Location - } - ); - } + return new AutoRun().Execute(commandLineArguments); } } diff --git a/backend/test/xunit.runner.json b/backend/test/xunit.runner.json deleted file mode 100644 index c3155894..00000000 --- a/backend/test/xunit.runner.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", - "parallelizeAssembly": false, - "parallelizeTestCollections": false, - "maxParallelThreads": 1 -} From d3d3f0c11d798e11e82578ba3933b6ecf2968931 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Apr 2024 18:49:14 +0200 Subject: [PATCH 135/878] Appease the roslynator analyzer and clean-up code with JetBrain's cleanupcode --- backend/src/AppSettings.cs | 73 ++- .../src/Configuration/AuthConfiguration.cs | 204 ++++---- .../src/Configuration/GraphQlConfiguration.cs | 484 ++++++++++-------- .../HttpContextAuthentication.cs | 69 +-- .../src/Controllers/AntiforgeryController.cs | 56 +- .../Controllers/AuthenticationControllers.cs | 391 +++++++------- .../src/Controllers/FileUploadController.cs | 367 ++++++------- backend/src/Data/ApplicationDbContext.cs | 190 +++---- backend/src/Data/AppliedMethod.cs | 47 +- backend/src/Data/CalorimetricData.cs | 123 +++-- backend/src/Data/CielabColor.cs | 33 +- .../src/Data/CrossDatabaseDataReference.cs | 37 +- backend/src/Data/DataApproval.cs | 55 +- backend/src/Data/DataX.cs | 122 ++--- backend/src/Data/DbSeeder.cs | 33 +- backend/src/Data/Entity.cs | 23 +- ...SchemaDbContextOptionsBuilderExtensions.cs | 39 +- .../Extensions/SchemaNameOptionsExtension.cs | 83 ++- backend/src/Data/FileMetaInformation.cs | 29 +- backend/src/Data/GetHttpsResource.cs | 240 ++++----- backend/src/Data/HygrothermalData.cs | 101 ++-- backend/src/Data/IApproval.cs | 21 +- backend/src/Data/IData.cs | 33 +- backend/src/Data/IEntity.cs | 11 +- backend/src/Data/NamedMethodArgument.cs | 32 +- backend/src/Data/NamedMethodSource.cs | 43 +- backend/src/Data/OpticalData.cs | 169 +++--- backend/src/Data/PhotovoltaicData.cs | 101 ++-- backend/src/Data/ResponseApproval.cs | 1 + .../ToTreeVertexAppliedConversionMethod.cs | 55 +- backend/src/Data/User.cs | 35 +- backend/src/Enumerations/DataKind.cs | 21 +- backend/src/Extensions/StringExtensions.cs | 46 +- .../DisableFormValueModelBindingAttribute.cs | 25 +- ...GenerateAntiforgeryTokenCookieAttribute.cs | 35 +- backend/src/GraphQl/AppliedMethodInput.cs | 15 +- .../CalorimetricDataByIdDataLoader.cs | 36 +- .../CalorimetricDataFilterType.cs | 26 +- .../CalorimetricDataMutations.cs | 221 ++++---- .../CalorimetricDataPayload.cs | 88 ++-- .../CalorimetricDataQueries.cs | 62 +-- .../CalorimetricDataResolvers.cs | 41 +- .../CalorimetricDataX/CalorimetricDataType.cs | 46 +- .../CreateCalorimetricDataError.cs | 23 +- .../CreateCalorimetricDataErrorCode.cs | 13 +- .../CreateCalorimetricDataInput.cs | 39 +- .../CreateCalorimetricDataPayload.cs | 31 +- backend/src/GraphQl/CielabColorInput.cs | 15 +- .../Common/OpenEndedDateTimeRangeInput.cs | 13 +- .../Common/OpenEndedDateTimeRangeType.cs | 65 ++- backend/src/GraphQl/Connection.cs | 49 +- .../CrossDatabaseDataReferenceInput.cs | 17 +- backend/src/GraphQl/DataApprovalInput.cs | 21 +- backend/src/GraphQl/DataX/DataFilterType.cs | 20 +- .../src/GraphQl/DataX/DataFilterTypeBase.cs | 99 ++-- backend/src/GraphQl/DataX/DataQueries.cs | 112 ++-- backend/src/GraphQl/DataX/DataType.cs | 46 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 63 ++- .../GetHttpsResourceTreeNonRootVertex.cs | 48 +- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 45 +- .../GraphQl/DataX/GetHttpsResourceTreeRoot.cs | 28 +- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 45 +- .../GetHttpsResourcesByDataIdDataLoader.cs | 44 +- .../DataX/IGetHttpsResourceTreeVertex.cs | 17 +- backend/src/GraphQl/Databases/Database.cs | 65 +-- .../GraphQl/Databases/DatabaseMutations.cs | 86 ++-- .../src/GraphQl/Databases/DatabasePayload.cs | 29 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 158 +++--- .../Databases/DatabaseVerificationState.cs | 13 +- .../GraphQl/Databases/DatabasesConnection.cs | 26 +- .../GraphQl/Databases/UpdateDatabaseError.cs | 21 +- .../Databases/UpdateDatabaseErrorCode.cs | 16 +- .../GraphQl/Databases/UpdateDatabaseInput.cs | 17 +- .../Databases/UpdateDatabasePayload.cs | 19 +- backend/src/GraphQl/Edge.cs | 36 +- .../AssociationsByAssociateIdDataLoader.cs | 66 +-- .../GraphQl/Entities/EntityByIdDataLoader.cs | 66 +-- backend/src/GraphQl/Entities/EntityType.cs | 60 +-- .../src/GraphQl/FileMetaInformationInput.cs | 13 +- .../Filters/AdditionalFilterOperations.cs | 10 +- backend/src/GraphQl/Filters/INotField.cs | 13 +- backend/src/GraphQl/Filters/NotField.cs | 55 +- backend/src/GraphQl/ForkingConnection.cs | 81 +-- .../CreateGetHttpsResourceError.cs | 23 +- .../CreateGetHttpsResourceErrorCode.cs | 13 +- .../CreateGetHttpsResourceInput.cs | 28 +- .../CreateGetHttpsResourcePayload.cs | 31 +- .../GetHttpsResourceByIdDataLoader.cs | 36 +- ...eChildrenByGetHttpsResourceIdDataLoader.cs | 43 +- .../GetHttpsResourceFilterType.cs | 38 +- .../GetHttpsResourceMutations.cs | 146 +++--- .../GetHttpsResourcePayload.cs | 88 ++-- .../GetHttpsResourceQueries.cs | 51 +- .../GetHttpsResourceResolvers.cs | 129 +++-- .../GetHttpsResources/GetHttpsResourceType.cs | 105 ++-- .../CreateHygrothermalDataError.cs | 23 +- .../CreateHygrothermalDataErrorCode.cs | 13 +- .../CreateHygrothermalDataInput.cs | 35 +- .../CreateHygrothermalDataPayload.cs | 31 +- .../HygrothermalDataByIdDataLoader.cs | 36 +- .../HygrothermalDataFilterType.cs | 22 +- .../HygrothermalDataMutations.cs | 217 ++++---- .../HygrothermalDataPayload.cs | 88 ++-- .../HygrothermalDataQueries.cs | 62 +-- .../HygrothermalDataResolvers.cs | 41 +- .../HygrothermalDataX/HygrothermalDataType.cs | 46 +- backend/src/GraphQl/IUserError.cs | 15 +- backend/src/GraphQl/LocaleType.cs | 93 ++-- .../GraphQl/LoggingDiagnosticEventListener.cs | 318 +++++++----- backend/src/GraphQl/Mutation.cs | 7 +- .../GraphQl/NamedMethodArgumentFilterType.cs | 24 +- .../src/GraphQl/NamedMethodArgumentInput.cs | 13 +- .../src/GraphQl/NamedMethodArgumentType.cs | 34 +- backend/src/GraphQl/NamedMethodSourceInput.cs | 13 +- .../OpticalDataX/CreateOpticalDataError.cs | 23 +- .../CreateOpticalDataErrorCode.cs | 13 +- .../OpticalDataX/CreateOpticalDataInput.cs | 49 +- .../OpticalDataX/CreateOpticalDataPayload.cs | 31 +- .../OpticalDataX/OpticalDataByIdDataLoader.cs | 36 +- .../OpticalDataX/OpticalDataFilterType.cs | 36 +- .../OpticalDataX/OpticalDataMutations.cs | 243 +++++---- .../OpticalDataX/OpticalDataPayload.cs | 88 ++-- .../OpticalDataX/OpticalDataQueries.cs | 62 +-- .../OpticalDataX/OpticalDataResolvers.cs | 41 +- .../GraphQl/OpticalDataX/OpticalDataType.cs | 46 +- backend/src/GraphQl/Payload.cs | 7 +- .../CreatePhotovoltaicDataError.cs | 23 +- .../CreatePhotovoltaicDataErrorCode.cs | 13 +- .../CreatePhotovoltaicDataInput.cs | 35 +- .../CreatePhotovoltaicDataPayload.cs | 31 +- .../PhotovoltaicDataByIdDataLoader.cs | 36 +- .../PhotovoltaicDataFilterType.cs | 22 +- .../PhotovoltaicDataMutations.cs | 217 ++++---- .../PhotovoltaicDataPayload.cs | 88 ++-- .../PhotovoltaicDataQueries.cs | 62 +-- .../PhotovoltaicDataResolvers.cs | 41 +- .../PhotovoltaicDataX/PhotovoltaicDataType.cs | 46 +- backend/src/GraphQl/Query.cs | 7 +- .../src/GraphQl/RootGetHttpsResourceInput.cs | 19 +- backend/src/GraphQl/Subscription.cs | 7 +- ...oTreeVertexAppliedConversionMethodInput.cs | 15 +- backend/src/GraphQl/UserErrorBase.cs | 37 +- .../src/GraphQl/Users/UserByIdDataLoader.cs | 36 +- backend/src/GraphQl/Users/UserInfo.cs | 33 +- backend/src/GraphQl/Users/UserQueries.cs | 106 ++-- backend/src/GraphQl/Users/UserType.cs | 20 +- .../src/GraphQl/VerificationCodeQueries.cs | 17 +- backend/src/Metabase/QueryingMetabase.cs | 291 ++++++----- .../Migrations/20230420143838_OpenIddict.cs | 6 +- backend/src/Program.cs | 302 +++++------ backend/src/Services/EmailSender.cs | 116 +++-- backend/src/Services/IEmailSender.cs | 17 +- backend/src/Startup.cs | 442 ++++++++-------- backend/src/Utilities/FileHelpers.cs | 425 ++++++++------- .../src/Utilities/MultipartRequestHelper.cs | 84 ++- backend/test/Program.cs | 2 +- 156 files changed, 5408 insertions(+), 5390 deletions(-) diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 0fed9093..0def98d9 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,67 +1,60 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited -namespace Database +namespace Database; + +public sealed class AppSettings { - public sealed class AppSettings - { - public string Host { get; set; } + public string Host { get; set; } = ""; - public string MetabaseHost { get; set; } + public string MetabaseHost { get; set; } = ""; - public string AccessToken { get; set; } + public string AccessToken { get; set; } = ""; - public string VerificationCode { get; set; } + public string VerificationCode { get; set; } = ""; - public LoggingSettings Logging { get; set; } - = new LoggingSettings(); + public LoggingSettings Logging { get; set; } = new(); - public sealed class LoggingSettings - { - public bool EnableSensitiveDataLogging { get; set; } - = false; - } + public JsonWebTokenSettings JsonWebToken { get; set; } = new(); - public JsonWebTokenSettings JsonWebToken { get; set; } - = new JsonWebTokenSettings(); + public EmailSettings Email { get; set; } = new(); - public sealed class JsonWebTokenSettings - { - public string EncryptionCertificatePassword { get; set; } - = ""; + public string OpenIdConnectClientSecret { get; set; } + = ""; - public string SigningCertificatePassword { get; set; } - = ""; - } + public DatabaseSettings Database { get; set; } = new(); - public EmailSettings Email { get; set; } - = new EmailSettings(); + public sealed class LoggingSettings + { + public bool EnableSensitiveDataLogging { get; set; } + } - public sealed class EmailSettings - { - public string SmtpHost { get; set; } + public sealed class JsonWebTokenSettings + { + public string EncryptionCertificatePassword { get; set; } = ""; - public int SmtpPort { get; set; } - = 0; - } + public string SigningCertificatePassword { get; set; } + = ""; + } - public string OpenIdConnectClientSecret { get; set; } - = ""; + public sealed class EmailSettings + { + public string SmtpHost { get; set; } + = ""; - public DatabaseSettings Database { get; set; } - = new DatabaseSettings(); + public int SmtpPort { get; set; } + } - public sealed class DatabaseSettings - { - public string ConnectionString { get; set; } + public sealed class DatabaseSettings + { + public string ConnectionString { get; set; } = ""; - public string SchemaName { get; set; } + public string SchemaName { get; set; } = ""; - } } } \ No newline at end of file diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 4809e1b7..ca242308 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,68 +1,67 @@ using System; -using Microsoft.Extensions.DependencyInjection; +using System.IO; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using Database.Data; using Microsoft.AspNetCore.Hosting; -using OpenIddict.Client; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Quartz; -using Database.Data; -using System.Security.Cryptography.X509Certificates; -using System.Reflection; -using System.IO; using OpenIddict.Abstractions; +using OpenIddict.Client; +using Quartz; -namespace Database.Configuration +namespace Database.Configuration; + +// Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Startup.cs +// https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs +public abstract class AuthConfiguration { - // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Startup.cs - // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs - public abstract class AuthConfiguration - { - public const string ClientId = "testlab-solar-facades"; - public static string ReadApiScope { get; } = "api:read"; - public static string WriteApiScope { get; } = "api:write"; + public const string ClientId = "testlab-solar-facades"; + public static string ReadApiScope { get; } = "api:read"; + public static string WriteApiScope { get; } = "api:write"; - public static void ConfigureServices( - IServiceCollection services, - IWebHostEnvironment environment, - AppSettings appSettings - ) - { - var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); - var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); - ConfigureAuthenticationAndAuthorizationServices(services); - ConfigureTaskScheduling(services, environment); - ConfigureOpenIddictServices(services, appSettings, encryptionCertificate, signingCertificate); - } + public static void ConfigureServices( + IServiceCollection services, + IWebHostEnvironment environment, + AppSettings appSettings + ) + { + var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", + appSettings.JsonWebToken.EncryptionCertificatePassword); + var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", + appSettings.JsonWebToken.SigningCertificatePassword); + ConfigureAuthenticationAndAuthorizationServices(services); + ConfigureTaskScheduling(services, environment); + ConfigureOpenIddictServices(services, appSettings, encryptionCertificate, signingCertificate); + } - private static X509Certificate2 LoadCertificate( - string fileName, - string password - ) - { - if (string.IsNullOrEmpty(password)) - { - throw new Exception($"Empty password for certificate {fileName}."); - } - var stream = - Assembly.GetExecutingAssembly().GetManifestResourceStream($"Database.{fileName}") - ?? throw new Exception($"Missing certificate {fileName}."); - using var buffer = new MemoryStream(); - stream.CopyTo(buffer); - return new X509Certificate2( - buffer.ToArray(), - password, - X509KeyStorageFlags.EphemeralKeySet - ); - } + private static X509Certificate2 LoadCertificate( + string fileName, + string password + ) + { + if (string.IsNullOrEmpty(password)) throw new ArgumentException($"Empty password for certificate {fileName}."); + var stream = + Assembly.GetExecutingAssembly().GetManifestResourceStream($"Database.{fileName}") + ?? throw new ArgumentException($"Missing certificate {fileName}."); + using var buffer = new MemoryStream(); + stream.CopyTo(buffer); + return new X509Certificate2( + buffer.ToArray(), + password, + X509KeyStorageFlags.EphemeralKeySet + ); + } - private static void ConfigureAuthenticationAndAuthorizationServices( - IServiceCollection services - ) - { - // Dot not use the single authentication scheme as the default scheme - // https://learn.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-7.0#defaultscheme - AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", isEnabled: true); - // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs - services + private static void ConfigureAuthenticationAndAuthorizationServices( + IServiceCollection services + ) + { + // Dot not use the single authentication scheme as the default scheme + // https://learn.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-7.0#defaultscheme + AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", true); + // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs + services .AddAuthentication(_ => { // To make the various authentication control flows obvious, do @@ -84,54 +83,51 @@ IServiceCollection services _.ExpireTimeSpan = TimeSpan.FromMinutes(50); _.SlidingExpiration = false; }); - } + } - private static void ConfigureTaskScheduling( - IServiceCollection services, - IWebHostEnvironment environment - ) + private static void ConfigureTaskScheduling( + IServiceCollection services, + IWebHostEnvironment environment + ) + { + // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks + // (like pruning orphaned authorizations/tokens from the database) at regular intervals. + // For configuring Quartz see + // https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/hosted-services-integration.html + services.AddQuartz(_ => { - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - // For configuring Quartz see - // https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/hosted-services-integration.html - services.AddQuartz(_ => - { - _.SchedulerId = "database"; - _.SchedulerName = "Database"; - _.UseMicrosoftDependencyInjectionJobFactory(); - _.UseSimpleTypeLoader(); - _.UseInMemoryStore(); - _.UseDefaultThreadPool(_ => - _.MaxConcurrency = 10 - ); - if (environment.IsEnvironment("test")) - { - // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 - _.SchedulerName = Guid.NewGuid().ToString(); - } - }); - // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(_ => - _.WaitForJobsToComplete = true - ); - } + _.SchedulerId = "database"; + _.SchedulerName = "Database"; + _.UseSimpleTypeLoader(); + _.UseInMemoryStore(); + _.UseDefaultThreadPool(_ => + _.MaxConcurrency = 10 + ); + if (environment.IsEnvironment("test")) + // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 + _.SchedulerName = Guid.NewGuid().ToString(); + }); + // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. + services.AddQuartzHostedService(_ => + _.WaitForJobsToComplete = true + ); + } - private static void ConfigureOpenIddictServices( - IServiceCollection services, - AppSettings appSettings, - X509Certificate2 encryptionCertificate, - X509Certificate2 signingCertificate - ) - { - services.AddOpenIddict() + private static void ConfigureOpenIddictServices( + IServiceCollection services, + AppSettings appSettings, + X509Certificate2 encryptionCertificate, + X509Certificate2 signingCertificate + ) + { + services.AddOpenIddict() // Register the OpenIddict core components. .AddCore(_ => { // Configure OpenIddict to use the Entity Framework Core stores and models. // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities. _.UseEntityFrameworkCore() - .UseDbContext(); + .UseDbContext(); // Enable Quartz.NET integration. _.UseQuartz(); }) @@ -147,8 +143,8 @@ X509Certificate2 signingCertificate // the remote introspection endpoint. // https://www.oauth.com/oauth2-servers/token-introspection-endpoint/ _.UseIntrospection() - .SetClientId(ClientId) - .SetClientSecret(appSettings.OpenIdConnectClientSecret); + .SetClientId(ClientId) + .SetClientSecret(appSettings.OpenIdConnectClientSecret); // Register the ASP.NET Core host. _.UseAspNetCore(); // Register the System.Net.Http integration. @@ -161,20 +157,20 @@ X509Certificate2 signingCertificate // Register the signing and encryption credentials. // See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 _.AddEncryptionCertificate(encryptionCertificate) - .AddSigningCertificate(signingCertificate); + .AddSigningCertificate(signingCertificate); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. _.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough() - .EnablePostLogoutRedirectionEndpointPassthrough(); + .EnableStatusCodePagesIntegration() + .EnableRedirectionEndpointPassthrough() + .EnablePostLogoutRedirectionEndpointPassthrough(); // .DisableTransportSecurityRequirement(); // Register the System.Net.Http integration and use the identity of the current // assembly as a more specific user agent, which can be useful when dealing with // providers that use the user agent as a way to throttle requests (e.g Reddit). _.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); + .SetProductInformation(typeof(Startup).Assembly); // Add a client registration matching the client application definition in the server project. _.AddRegistration( @@ -188,7 +184,8 @@ X509Certificate2 signingCertificate ClientSecret = appSettings.OpenIdConnectClientSecret, // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims - Scopes = { + Scopes = + { OpenIddictConstants.Scopes.Address, OpenIddictConstants.Scopes.Email, OpenIddictConstants.Scopes.Phone, @@ -207,6 +204,5 @@ X509Certificate2 signingCertificate } ); }); - } } } \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 3b7042ab..a23f17a7 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,82 +1,82 @@ using System; -using HotChocolate; -using HotChocolate.Types; +using System.Net.Http; +using System.Text.RegularExpressions; +using Database.Data; +using Database.GraphQl; +using Database.GraphQl.CalorimetricDataX; +using Database.GraphQl.Common; +using Database.GraphQl.Databases; +using Database.GraphQl.DataX; +using Database.GraphQl.Filters; +using Database.GraphQl.GetHttpsResources; +using Database.GraphQl.HygrothermalDataX; +using Database.GraphQl.OpticalDataX; +using Database.GraphQl.PhotovoltaicDataX; +using Database.GraphQl.Users; +using HotChocolate.Data; using HotChocolate.Data.Filters; +using HotChocolate.Language; +using HotChocolate.Types; using HotChocolate.Types.Pagination; -using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; -using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using HotChocolate.Data; -using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Authentication.Cookies; -using Database.GraphQl.Filters; -using System.Net.Http; -using OpenIddict.Validation.AspNetCore; +using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; + +namespace Database.Configuration; -namespace Database.Configuration +public static class GraphQlConfiguration { - public static class GraphQlConfiguration + public static void ConfigureServices( + IServiceCollection services, + IWebHostEnvironment environment, + AppSettings appSettings + ) { - public static class WellKnownSchemaNames - { - public const string Metabase = "metabase"; - } - - public static void ConfigureServices( - IServiceCollection services, - IWebHostEnvironment environment, - AppSettings appSettings - ) - { - // Stitching Services - // https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching - var httpClientBuilder = services.AddHttpClient( - WellKnownSchemaNames.Metabase, - _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") + // Stitching Services + // https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching + var httpClientBuilder = services.AddHttpClient( + WellKnownSchemaNames.Metabase, + _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") + ); + if (!environment.IsProduction()) + httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler + { + // ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } ); - if (!environment.IsProduction()) - { - httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => - new HttpClientHandler - { - // ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator - } - ); - } - // Automatic-Persisted-Queries Services - services + // Automatic-Persisted-Queries Services + services .AddMemoryCache() - .AddSha256DocumentHashProvider(HotChocolate.Language.HashFormat.Hex); - // GraphQL Server - services + .AddSha256DocumentHashProvider(HashFormat.Hex); + // GraphQL Server + services .AddGraphQLServer() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext - .RegisterDbContext(DbContextKind.Pooled) + .RegisterDbContext(DbContextKind.Pooled) // Extensions .AddProjections() .AddFiltering() .AddSorting() .AddAuthorization() - .AddApolloTracing( - HotChocolate.Execution.Options.TracingPreference.OnDemand - ) // TODO Do we want or need this? + .AddApolloTracing() // TODO Do we want or need this? .AddGlobalObjectIdentification() .AddQueryFieldToMutationPayloads() .ModifyOptions(options => - { - // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Types/Configuration/Contracts/ISchemaOptions.cs - options.StrictValidation = true; - options.UseXmlDocumentation = false; - options.SortFieldsByName = true; - options.RemoveUnreachableTypes = false; - options.DefaultBindingBehavior = HotChocolate.Types.BindingBehavior.Implicit; - /* options.FieldMiddleware = ... */ - } - ) + { + // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Types/Configuration/Contracts/ISchemaOptions.cs + options.StrictValidation = true; + options.UseXmlDocumentation = false; + options.SortFieldsByName = true; + options.RemoveUnreachableTypes = false; + options.DefaultBindingBehavior = BindingBehavior.Implicit; + /* options.FieldMiddleware = ... */ + } + ) .ModifyRequestOptions(options => { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs @@ -109,46 +109,49 @@ AppSettings appSettings } }) .AddDiagnosticEventListener(_ => - new GraphQl.LoggingDiagnosticEventListener( - _.GetApplicationService>() + new LoggingDiagnosticEventListener( + _.GetApplicationService>() ) ) // Scalar Types - .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type + .AddType(new UuidType("Uuid", + defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type .AddType(new UrlType("Url")) - .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar - .AddType(new GraphQl.LocaleType()) + .AddType(new JsonType("Any", + BindingBehavior + .Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar + .AddType(new LocaleType()) // Query Types - .AddQueryType(d => d.Name(nameof(GraphQl.Query))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() + .AddQueryType(d => d.Name(nameof(Query))) + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() // Mutation Types - .AddMutationType(d => d.Name(nameof(GraphQl.Mutation))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() + .AddMutationType(d => d.Name(nameof(Mutation))) + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() + .AddType() // Data Loaders /* .AddDataLoader() */ // Paging @@ -172,159 +175,192 @@ AppSettings appSettings // Automatic Peristed Queries .UseAutomaticPersistedQueryPipeline() .AddInMemoryQueryStorage(); - } } - // See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions - public class CustomFilterConvention : FilterConvention + public static class WellKnownSchemaNames { - protected override void Configure(IFilterConventionDescriptor descriptor) - { - descriptor.AddDefaults(); - // Use argument name `where` - descriptor.ArgumentName("where"); - // Allow conjunction and disjunction - descriptor.AllowAnd(); - descriptor.AllowOr(); - // Bind custom types - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - } + public const string Metabase = "metabase"; + } +} + +// See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions +public partial class CustomFilterConvention : FilterConvention +{ + protected override void Configure(IFilterConventionDescriptor descriptor) + { + descriptor.AddDefaults(); + // Use argument name `where` + descriptor.ArgumentName("where"); + // Allow conjunction and disjunction + descriptor.AllowAnd(); + descriptor.AllowOr(); + // Bind custom types + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + } - // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? - // For the base implementation see https://github.com/ChilliCream/hotchocolate/blob/f0dff93a14cb7ddecc7b3a0530a687a5bc4bad71/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 - public override string GetTypeName(Type runtimeType) - { - var nameString = base.GetTypeName(runtimeType); - return - new Regex(@"IData").Replace( - new Regex(@"Double").Replace( - new Regex(@"Float").Replace( - new Regex(@"ListString").Replace( - new Regex(@"ListFloat").Replace( - new Regex(@"Guid").Replace( - new Regex(@"^Comparable").Replace( - new Regex(@"FilterInput$").Replace( - new Regex(@"OperationFilterInput").Replace( - nameString, - "PropositionInput" - ), + // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? + // For the base implementation see https://github.com/ChilliCream/hotchocolate/blob/f0dff93a14cb7ddecc7b3a0530a687a5bc4bad71/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 + public override string GetTypeName(Type runtimeType) + { + var nameString = base.GetTypeName(runtimeType); + return + IDataRegex().Replace( + DoubleRegex().Replace( + FloatRegex().Replace( + ListStringRegex().Replace( + ListFloatRegex().Replace( + GuidRegex().Replace( + ComparableRegex().Replace( + FilterInputRegex().Replace( + OperationFilterInputRegex().Replace( + nameString, "PropositionInput" ), - "" + "PropositionInput" ), - "Uuid" + "" ), - "Floats" + "Uuid" ), - "Strings" + "Floats" ), - "Float" + "Strings" ), "Float" ), - "Data" - ); - } + "Float" + ), + "Data" + ); } - public static class FilterConventionDescriptorExtensions + [GeneratedRegex(@"IData")] + private static partial Regex IDataRegex(); + + [GeneratedRegex(@"Double")] + private static partial Regex DoubleRegex(); + + [GeneratedRegex(@"Float")] + private static partial Regex FloatRegex(); + + [GeneratedRegex(@"ListString")] + private static partial Regex ListStringRegex(); + + [GeneratedRegex(@"ListFloat")] + private static partial Regex ListFloatRegex(); + + [GeneratedRegex(@"Guid")] + private static partial Regex GuidRegex(); + + [GeneratedRegex(@"^Comparable")] + private static partial Regex ComparableRegex(); + + [GeneratedRegex(@"FilterInput$")] + private static partial Regex FilterInputRegex(); + + [GeneratedRegex(@"OperationFilterInput")] + private static partial Regex OperationFilterInputRegex(); +} + +public static class FilterConventionDescriptorExtensions +{ + // Inspired by FilterConventionDescriptorExtensions#AddDefaults + // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L16 + public static IFilterConventionDescriptor AddDefaults( + this IFilterConventionDescriptor descriptor) { - // Inspired by FilterConventionDescriptorExtensions#AddDefaults - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L16 - public static IFilterConventionDescriptor AddDefaults( - this IFilterConventionDescriptor descriptor) => - descriptor.AddDefaultOperations().BindDefaultTypes().UseQueryableProvider(); + return descriptor.AddDefaultOperations().BindDefaultTypes().UseQueryableProvider(); + } - // Inspired by FilterConventionDescriptorExtensions#AddDefaultOperations - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L28 - public static IFilterConventionDescriptor AddDefaultOperations( - this IFilterConventionDescriptor descriptor) - { - descriptor.Operation(DefaultFilterOperations.Equals).Name("equalTo"); - descriptor.Operation(DefaultFilterOperations.NotEquals).Name("notEqualTo"); - descriptor.Operation(DefaultFilterOperations.GreaterThan).Name("greaterThan"); - descriptor.Operation(DefaultFilterOperations.NotGreaterThan).Name("notGreaterThan"); - descriptor.Operation(DefaultFilterOperations.GreaterThanOrEquals).Name("greaterThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.NotGreaterThanOrEquals).Name("notGreaterThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.LowerThan).Name("lessThan"); - descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("lessThan"); - descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("notLessThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.NotLowerThanOrEquals).Name("notLessThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.Contains).Name("contains"); - descriptor.Operation(DefaultFilterOperations.NotContains).Name("doesNotContain"); - descriptor.Operation(DefaultFilterOperations.In).Name("in"); - descriptor.Operation(DefaultFilterOperations.NotIn).Name("notIn"); - descriptor.Operation(DefaultFilterOperations.StartsWith).Name("startsWith"); - descriptor.Operation(DefaultFilterOperations.NotStartsWith).Name("doesNotStartWith"); - descriptor.Operation(DefaultFilterOperations.EndsWith).Name("endsWith"); - descriptor.Operation(DefaultFilterOperations.NotEndsWith).Name("doesNotEndWith"); - descriptor.Operation(DefaultFilterOperations.All).Name("all"); - descriptor.Operation(DefaultFilterOperations.None).Name("none"); - descriptor.Operation(DefaultFilterOperations.Some).Name("some"); - descriptor.Operation(DefaultFilterOperations.Any).Name("any"); - descriptor.Operation(DefaultFilterOperations.And).Name("and"); - descriptor.Operation(DefaultFilterOperations.Or).Name("or"); - descriptor.Operation(DefaultFilterOperations.Data).Name("data"); - descriptor.Operation(AdditionalFilterOperations.Not).Name("not"); - // TODO `inClosedInterval` - return descriptor; - } + // Inspired by FilterConventionDescriptorExtensions#AddDefaultOperations + // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L28 + public static IFilterConventionDescriptor AddDefaultOperations( + this IFilterConventionDescriptor descriptor) + { + descriptor.Operation(DefaultFilterOperations.Equals).Name("equalTo"); + descriptor.Operation(DefaultFilterOperations.NotEquals).Name("notEqualTo"); + descriptor.Operation(DefaultFilterOperations.GreaterThan).Name("greaterThan"); + descriptor.Operation(DefaultFilterOperations.NotGreaterThan).Name("notGreaterThan"); + descriptor.Operation(DefaultFilterOperations.GreaterThanOrEquals).Name("greaterThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.NotGreaterThanOrEquals).Name("notGreaterThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.LowerThan).Name("lessThan"); + descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("lessThan"); + descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("notLessThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.NotLowerThanOrEquals).Name("notLessThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.Contains).Name("contains"); + descriptor.Operation(DefaultFilterOperations.NotContains).Name("doesNotContain"); + descriptor.Operation(DefaultFilterOperations.In).Name("in"); + descriptor.Operation(DefaultFilterOperations.NotIn).Name("notIn"); + descriptor.Operation(DefaultFilterOperations.StartsWith).Name("startsWith"); + descriptor.Operation(DefaultFilterOperations.NotStartsWith).Name("doesNotStartWith"); + descriptor.Operation(DefaultFilterOperations.EndsWith).Name("endsWith"); + descriptor.Operation(DefaultFilterOperations.NotEndsWith).Name("doesNotEndWith"); + descriptor.Operation(DefaultFilterOperations.All).Name("all"); + descriptor.Operation(DefaultFilterOperations.None).Name("none"); + descriptor.Operation(DefaultFilterOperations.Some).Name("some"); + descriptor.Operation(DefaultFilterOperations.Any).Name("any"); + descriptor.Operation(DefaultFilterOperations.And).Name("and"); + descriptor.Operation(DefaultFilterOperations.Or).Name("or"); + descriptor.Operation(DefaultFilterOperations.Data).Name("data"); + descriptor.Operation(AdditionalFilterOperations.Not).Name("not"); + // TODO `inClosedInterval` + return descriptor; + } - // Inspired by FilterConventionDescriptorExtensions#BindDefaultTypes - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L73 - public static IFilterConventionDescriptor BindDefaultTypes( - this IFilterConventionDescriptor descriptor) - { - descriptor - .BindRuntimeType() - .BindRuntimeType() - .BindRuntimeType() - .BindComparableType("BytePropositionInput") - .BindComparableType("ShortPropositionInput") - .BindComparableType("IntPropositionInput") - .BindComparableType("LongPropositionInput") - .BindComparableType("FloatXPropositionInput") - .BindComparableType("FloatPropositionInput") - .BindComparableType("DecimalPropositionInput") - .BindComparableType("SignedBytePropositionInput") - .BindComparableType("UnsignedShortPropositionInput") - .BindComparableType("UnsignedIntPropositionInput") - .BindComparableType("UnsigendLongPropositionInput") - .BindComparableType("UuidPropositionInput") - .BindComparableType("DateTimePropositionInput") - .BindComparableType("DateTimeOffsetPropositionInput") - .BindComparableType("TimeSpanPropositionInput"); - // TODO Why does this not work? - // descriptor - // .Configure(x => x.Name("StringPropositionInput")) - // .Configure(x => x.Name("BooleanPropositionInput")); - return descriptor; - } + // Inspired by FilterConventionDescriptorExtensions#BindDefaultTypes + // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L73 + public static IFilterConventionDescriptor BindDefaultTypes( + this IFilterConventionDescriptor descriptor) + { + descriptor + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindComparableType("BytePropositionInput") + .BindComparableType("ShortPropositionInput") + .BindComparableType("IntPropositionInput") + .BindComparableType("LongPropositionInput") + .BindComparableType("FloatXPropositionInput") + .BindComparableType("FloatPropositionInput") + .BindComparableType("DecimalPropositionInput") + .BindComparableType("SignedBytePropositionInput") + .BindComparableType("UnsignedShortPropositionInput") + .BindComparableType("UnsignedIntPropositionInput") + .BindComparableType("UnsigendLongPropositionInput") + .BindComparableType("UuidPropositionInput") + .BindComparableType("DateTimePropositionInput") + .BindComparableType("DateTimeOffsetPropositionInput") + .BindComparableType("TimeSpanPropositionInput"); + // TODO Why does this not work? + // descriptor + // .Configure(x => x.Name("StringPropositionInput")) + // .Configure(x => x.Name("BooleanPropositionInput")); + return descriptor; + } - // Inspired by FilterConventionDescriptorExtensions#FilterConventionDescriptorExtensions - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L102 - private static IFilterConventionDescriptor BindComparableType( - this IFilterConventionDescriptor descriptor, - string? name = null) - where T : struct - { - descriptor - .BindRuntimeType>() - .BindRuntimeType>(); - // TODO Why does this not work? - // if (name is not null) - // { - // descriptor - // .Configure>(x => x.Name(name)) - // .Configure>(x => x.Name($"Maybe{name}")); - // } - return descriptor; - } + // Inspired by FilterConventionDescriptorExtensions#FilterConventionDescriptorExtensions + // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L102 + private static IFilterConventionDescriptor BindComparableType( + this IFilterConventionDescriptor descriptor, + string? name = null) + where T : struct + { + descriptor + .BindRuntimeType>() + .BindRuntimeType>(); + // TODO Why does this not work? + // if (name is not null) + // { + // descriptor + // .Configure>(x => x.Name(name)) + // .Configure>(x => x.Name($"Maybe{name}")); + // } + return descriptor; } } \ No newline at end of file diff --git a/backend/src/Configuration/HttpContextAuthentication.cs b/backend/src/Configuration/HttpContextAuthentication.cs index 4a58adbf..b1999886 100644 --- a/backend/src/Configuration/HttpContextAuthentication.cs +++ b/backend/src/Configuration/HttpContextAuthentication.cs @@ -4,42 +4,45 @@ using Microsoft.AspNetCore.Http; using OpenIddict.Validation.AspNetCore; -namespace Database.Configuration +namespace Database.Configuration; + +public static class HttpContextAuthentication { - public static class HttpContextAuthentication + public static async Task Authenticate( + HttpContext httpContext + ) { - public static async Task Authenticate( - HttpContext httpContext - ) + // For the Next.js Web frontend, the database acts as OpenId Connect + // Client and uses the cookie scheme for authentication. See + // `AuthConfiguration#ConfigureAuthenticationAndAuthorizationServices` + // for the configuration of the "cookie scheme" cookie. This cookie + // is set by methods in `AuthenticationController` and is related to + // `OpenIddictBuilder#AddClient` in + // `AuthConfiguration#ConfigureOpenIddictServices`. + var cookieAuthenticateResult = await httpContext + .AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) { - // For the Next.js Web frontend, the database acts as OpenId Connect - // Client and uses the cookie scheme for authentication. See - // `AuthConfiguration#ConfigureAuthenticationAndAuthorizationServices` - // for the configuration of the "cookie scheme" cookie. This cookie - // is set by methods in `AuthenticationController` and is related to - // `OpenIddictBuilder#AddClient` in - // `AuthConfiguration#ConfigureOpenIddictServices`. - var cookieAuthenticateResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); - if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) - { - httpContext.User = cookieAuthenticateResult.Principal; - return cookieAuthenticateResult; - } - // For third-party frontends, the database acts as resource server - // and uses authorization-header bearer tokens for authentication, - // that is JavaScript Web Tokens (JWT), aka, Access Tokens, provided - // as `Authorization` HTTP header with the prefix `Bearer` as issued - // by OpenIddict. This Access Token includes Scopes and Claims. The - // scheme is configured in - // `AuthConfiguration#ConfigureOpenIddictServices` by - // `OpenIddictBuilder#AddValidation`. - var jwtAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); - if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) - { - httpContext.User = jwtAuthenticateResult.Principal; - return jwtAuthenticateResult; - } - return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); + httpContext.User = cookieAuthenticateResult.Principal; + return cookieAuthenticateResult; } + + // For third-party frontends, the database acts as resource server + // and uses authorization-header bearer tokens for authentication, + // that is JavaScript Web Tokens (JWT), aka, Access Tokens, provided + // as `Authorization` HTTP header with the prefix `Bearer` as issued + // by OpenIddict. This Access Token includes Scopes and Claims. The + // scheme is configured in + // `AuthConfiguration#ConfigureOpenIddictServices` by + // `OpenIddictBuilder#AddValidation`. + var jwtAuthenticateResult = await httpContext + .AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); + if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) + { + httpContext.User = jwtAuthenticateResult.Principal; + return jwtAuthenticateResult; + } + + return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); } } \ No newline at end of file diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 27df8253..67fc3788 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -5,37 +5,39 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace Database.Controllers +namespace Database.Controllers; + +// For gotchas regarding antiforgery tokens read +// [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) +public sealed class AntiforgeryController : Controller { - // For gotchas regarding antiforgery tokens read - // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) - public sealed class AntiforgeryController : Controller - { - private const string XsrfCookieKey = "XSRF-TOKEN"; - private readonly CookieOptions XsrfCookieOptions = - new() - { - HttpOnly = false - }; + private const string XsrfCookieKey = "XSRF-TOKEN"; - private readonly IAntiforgery _antiforgeryService; + private readonly IAntiforgery _antiforgeryService; - public AntiforgeryController(IAntiforgery antiforgeryService) + private readonly CookieOptions XsrfCookieOptions = + new() { - _antiforgeryService = antiforgeryService; - } + HttpOnly = false + }; - [HttpGet("~/antiforgery/token")] - public async Task Token() - { - await HttpContextAuthentication.Authenticate(HttpContext).ConfigureAwait(false); - var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); - HttpContext.Response.Cookies.Append( - XsrfCookieKey, - tokens.RequestToken ?? throw new InvalidOperationException("The request token supposed to be generated by the antiforgery service is null."), - XsrfCookieOptions - ); - return new StatusCodeResult(StatusCodes.Status200OK); - } + public AntiforgeryController(IAntiforgery antiforgeryService) + { + _antiforgeryService = antiforgeryService; + } + + [HttpGet("~/antiforgery/token")] + public async Task Token() + { + await HttpContextAuthentication.Authenticate(HttpContext).ConfigureAwait(false); + var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); + HttpContext.Response.Cookies.Append( + XsrfCookieKey, + tokens.RequestToken ?? + throw new InvalidOperationException( + "The request token supposed to be generated by the antiforgery service is null."), + XsrfCookieOptions + ); + return new StatusCodeResult(StatusCodes.Status200OK); } } \ No newline at end of file diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationControllers.cs index 341e3c25..b291b136 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationControllers.cs @@ -4,6 +4,7 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; @@ -12,130 +13,130 @@ using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; -namespace Database.Controllers +namespace Database.Controllers; + +// Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs +// https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs +public class AuthenticationController : Controller { - // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs - // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs - public class AuthenticationController : Controller + private readonly ApplicationDbContext _context; + private readonly string _issuer; + + public AuthenticationController( + AppSettings appSettings, + ApplicationDbContext context + ) { - private readonly string _issuer; - private readonly Data.ApplicationDbContext _context; + _issuer = appSettings.MetabaseHost; + _context = context; + } - public AuthenticationController( - AppSettings appSettings, - Data.ApplicationDbContext context + [HttpGet("~/connect/login")] + public ActionResult LogIn(string? returnUrl) + { + // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. + return Challenge( + new AuthenticationProperties( + new Dictionary + { + // Note: when only one client is registered in the client options, + // setting the issuer property is not required and can be omitted. + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer + } ) - { - _issuer = appSettings.MetabaseHost; - _context = context; - } + { + // Only allow local return URLs to prevent open redirect attacks. + RedirectUri = SanitizeReturnUrl(returnUrl) + }, + OpenIddictClientAspNetCoreDefaults.AuthenticationScheme + ); + } - [HttpGet("~/connect/login")] - public ActionResult LogIn(string? returnUrl) - { - // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. - return Challenge( - new AuthenticationProperties( - new Dictionary - { - // Note: when only one client is registered in the client options, - // setting the issuer property is not required and can be omitted. - [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer - } - ) + [HttpPost("~/connect/logout")] + [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] + [ValidateAntiForgeryToken] + public async Task LogOut(string? returnUrl) + { + // Retrieve the identity stored in the local authentication cookie. If it's not available, + // this indicate that the user is already logged out locally (or has not logged in yet). + var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + if (result is not { Succeeded: true }) + // Only allow local return URLs to prevent open redirect attacks. + // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects + return LocalRedirect(SanitizeReturnUrl(returnUrl)); + // Remove the local authentication cookie before triggering a redirection to the remote server. + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. + return SignOut( + new AuthenticationProperties( + new Dictionary { - // Only allow local return URLs to prevent open redirect attacks. - RedirectUri = SanitizeReturnUrl(returnUrl) - }, - OpenIddictClientAspNetCoreDefaults.AuthenticationScheme - ); - } - - [HttpPost("~/connect/logout")] - [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme), ValidateAntiForgeryToken] - public async Task LogOut(string? returnUrl) - { - // Retrieve the identity stored in the local authentication cookie. If it's not available, - // this indicate that the user is already logged out locally (or has not logged in yet). - var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (result is not { Succeeded: true }) + // Note: when only one client is registered in the client options, + // setting the issuer property is not required and can be omitted. + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer, + // While not required, the specification encourages sending an id_token_hint + // parameter containing an identity token returned by the server for this user. + [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = + result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens + .BackchannelIdentityToken) + } + ) { // Only allow local return URLs to prevent open redirect attacks. - // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects - return LocalRedirect(SanitizeReturnUrl(returnUrl)); - } - // Remove the local authentication cookie before triggering a redirection to the remote server. - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); - // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. - return SignOut( - new AuthenticationProperties( - new Dictionary - { - // Note: when only one client is registered in the client options, - // setting the issuer property is not required and can be omitted. - [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer, - // While not required, the specification encourages sending an id_token_hint - // parameter containing an identity token returned by the server for this user. - [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = - result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken) - } - ) - { - // Only allow local return URLs to prevent open redirect attacks. - RedirectUri = SanitizeReturnUrl(returnUrl) - }, - OpenIddictClientAspNetCoreDefaults.AuthenticationScheme - ); - } + RedirectUri = SanitizeReturnUrl(returnUrl) + }, + OpenIddictClientAspNetCoreDefaults.AuthenticationScheme + ); + } - // Note: this controller uses the same callback action for all providers - // but for users who prefer using a different action per provider, - // the following action can be split into separate actions. - [HttpGet("~/connect/callback/login/{provider}"), HttpPost("~/connect/callback/login/{provider}"), IgnoreAntiforgeryToken] - public async Task LogInCallback(CancellationToken cancellationToken) - { - // Retrieve the authorization data validated by OpenIddict as part of the callback handling. - var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); - // Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons: - // - // * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable - // for applications that don't need a long-term access to the user's resources or don't want to store - // access/refresh tokens in a database or in an authentication cookie (which has security implications). - // It is also suitable for applications that don't need to authenticate users but only need to perform - // action(s) on their behalf by making API calls using the access token returned by the remote server. - // - // * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an - // authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET - // Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens. - // - // Note: in this case, it's recommended to use column encryption to protect the tokens in the database. - // - // * Storing the external claims/tokens in an authentication cookie, which doesn't require having - // a user database but may be affected by the cookie size limits enforced by most browser vendors - // (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies). - // - // Note: this is the approach used here, but the external claims are first filtered to only persist - // a few claims like the user identifier. The same approach is used to store the access/refresh tokens. - // - // Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint, - // result.Principal.Identity will represent an unauthenticated identity and won't contain any claim. - // - // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the - // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but - // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls. - if (result.Principal?.Identity is not ClaimsIdentity { IsAuthenticated: true }) - { - throw new InvalidOperationException("The external authorization data cannot be used for authentication."); - } - // Build an identity based on the external claims and that will be used to create the authentication cookie. - // - // By default, all claims extracted during the authorization dance are available. The claims collection stored - // in the cookie can be filtered out or mapped to different names depending on the claim name or its issuer. - // - // The claims are fetched from the userinfo endpoint of the - // authorization provider. - var claims = new List( - result.Principal.Claims + // Note: this controller uses the same callback action for all providers + // but for users who prefer using a different action per provider, + // the following action can be split into separate actions. + [HttpGet("~/connect/callback/login/{provider}")] + [HttpPost("~/connect/callback/login/{provider}")] + [IgnoreAntiforgeryToken] + public async Task LogInCallback(string provider, CancellationToken cancellationToken) + { + // Retrieve the authorization data validated by OpenIddict as part of the callback handling. + var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); + // Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons: + // + // * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable + // for applications that don't need a long-term access to the user's resources or don't want to store + // access/refresh tokens in a database or in an authentication cookie (which has security implications). + // It is also suitable for applications that don't need to authenticate users but only need to perform + // action(s) on their behalf by making API calls using the access token returned by the remote server. + // + // * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an + // authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET + // Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens. + // + // Note: in this case, it's recommended to use column encryption to protect the tokens in the database. + // + // * Storing the external claims/tokens in an authentication cookie, which doesn't require having + // a user database but may be affected by the cookie size limits enforced by most browser vendors + // (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies). + // + // Note: this is the approach used here, but the external claims are first filtered to only persist + // a few claims like the user identifier. The same approach is used to store the access/refresh tokens. + // + // Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint, + // result.Principal.Identity will represent an unauthenticated identity and won't contain any claim. + // + // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the + // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but + // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls. + if (result.Principal?.Identity is not ClaimsIdentity { IsAuthenticated: true }) + throw new InvalidOperationException("The external authorization data cannot be used for authentication."); + // Build an identity based on the external claims and that will be used to create the authentication cookie. + // + // By default, all claims extracted during the authorization dance are available. The claims collection stored + // in the cookie can be filtered out or mapped to different names depending on the claim name or its issuer. + // + // The claims are fetched from the userinfo endpoint of the + // authorization provider. + var claims = new List( + result.Principal.Claims .Select(claim => claim switch { // https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims @@ -155,96 +156,96 @@ public async Task LogInCallback(CancellationToken cancellationToke // Don't preserve the other claims. _ => false })); - var identity = new ClaimsIdentity( - claims, - authenticationType: CookieAuthenticationDefaults.AuthenticationScheme, - nameType: ClaimTypes.Name, - roleType: ClaimTypes.Role - ); - AuthenticationProperties properties; - if (result.Properties is null) - { - properties = new AuthenticationProperties(); - } - else + var identity = new ClaimsIdentity( + claims, + CookieAuthenticationDefaults.AuthenticationScheme, + ClaimTypes.Name, + ClaimTypes.Role + ); + AuthenticationProperties properties; + if (result.Properties is null) + { + properties = new AuthenticationProperties(); + } + else + { + // Build the authentication properties based on the properties that were added when the challenge was triggered. + properties = new AuthenticationProperties(result.Properties.Items); + // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. + // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. + properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch { - // Build the authentication properties based on the properties that were added when the challenge was triggered. - properties = new AuthenticationProperties(result.Properties.Items); - // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. - // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. - properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch + // Preserve the access, identity and refresh tokens returned in the token response, if available. { - // Preserve the access, identity and refresh tokens returned in the token response, if available. - { - Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or - OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken - } => true, - // Ignore the other tokens. - _ => false - })); - } - // Add the user to the database if he/she does not exist. - var subject = - identity.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value - ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.NameIdentifier}, which is the subject of the token, is missing for the identity with name {identity.Name}."); - var name = - identity.FindFirst(c => c.Type == ClaimTypes.Name)?.Value - ?? throw new InvalidOperationException($"Impossible! The claim {ClaimTypes.Name} is missing for the identity with subject {subject}."); - var user = await - _context.Users.AsQueryable() + Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or + OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken + } => true, + // Ignore the other tokens. + _ => false + })); + } + + // Add the user to the database if he/she does not exist. + var subject = + identity.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value + ?? throw new InvalidOperationException( + $"Impossible! The claim {ClaimTypes.NameIdentifier}, which is the subject of the token, is missing for the identity with name {identity.Name}."); + var name = + identity.FindFirst(c => c.Type == ClaimTypes.Name)?.Value + ?? throw new InvalidOperationException( + $"Impossible! The claim {ClaimTypes.Name} is missing for the identity with subject {subject}."); + var user = await + _context.Users.AsQueryable() .SingleOrDefaultAsync( u => u.Subject == subject, cancellationToken ); - if (user is null) - { - _context.Users.Add( - new Data.User( - subject: subject, - name: name - ) - ); - } - else - { - user.Update(name: name); - } - await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - // Ask the cookie authentication handler to return a new cookie and redirect - // the user agent to the return URL stored in the authentication properties. - return SignIn( - new ClaimsPrincipal(identity), - properties, - CookieAuthenticationDefaults.AuthenticationScheme + if (user is null) + _context.Users.Add( + new User( + subject, + name + ) ); - } + else + user.Update(name); + await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + // Ask the cookie authentication handler to return a new cookie and redirect + // the user agent to the return URL stored in the authentication properties. + return SignIn( + new ClaimsPrincipal(identity), + properties, + CookieAuthenticationDefaults.AuthenticationScheme + ); + } - // Note: this controller uses the same callback action for all providers - // but for users who prefer using a different action per provider, - // the following action can be split into separate actions. - [HttpGet("~/connect/callback/logout/{provider}"), HttpPost("~/connect/callback/logout/{provider}"), IgnoreAntiforgeryToken] - public async Task LogOutCallback() - { - // Retrieve the data stored by OpenIddict in the state token created when the logout was triggered. - var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); - // In this sample, the local authentication cookie is always removed before the user agent is redirected - // to the authorization server. Applications that prefer delaying the removal of the local cookie can - // remove the corresponding code from the logout action and remove the authentication cookie in this action. - // - // Only allow local return URLs to prevent open redirect attacks. - // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects - return LocalRedirect( - SanitizeReturnUrl(result.Properties?.RedirectUri) - ); - } + // Note: this controller uses the same callback action for all providers + // but for users who prefer using a different action per provider, + // the following action can be split into separate actions. + [HttpGet("~/connect/callback/logout/{provider}")] + [HttpPost("~/connect/callback/logout/{provider}")] + [IgnoreAntiforgeryToken] + public async Task LogOutCallback(string provider) + { + // Retrieve the data stored by OpenIddict in the state token created when the logout was triggered. + var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); + // In this sample, the local authentication cookie is always removed before the user agent is redirected + // to the authorization server. Applications that prefer delaying the removal of the local cookie can + // remove the corresponding code from the logout action and remove the authentication cookie in this action. + // + // Only allow local return URLs to prevent open redirect attacks. + // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects + return LocalRedirect( + SanitizeReturnUrl(result.Properties?.RedirectUri) + ); + } - private string SanitizeReturnUrl(string? returnUrl) - { - return - returnUrl is not null && Url.IsLocalUrl(returnUrl) + private string SanitizeReturnUrl(string? returnUrl) + { + return + returnUrl is not null && Url.IsLocalUrl(returnUrl) ? returnUrl : "/"; - } } } \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 0debd29d..3cf32483 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -1,209 +1,226 @@ using System; using System.IO; +using System.Linq; using System.Net; using System.Text; +using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; +using Database.Data; +using Database.Filters; +using Database.Utilities; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -using Database.Data; -using Database.Filters; -using Database.Utilities; -using System.Linq; -using System.Threading; -using Microsoft.EntityFrameworkCore; -namespace Database.Controllers +namespace Database.Controllers; + +public static partial class Log +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Information, + Message = + "Uploaded file '{TrustedFileNameForDisplay}' saved to '{TargetDirectoryPath}' as '{TrustedFileNameForFileStorage}'")] + public static partial void SavedUploadedFile( + this ILogger logger, + string? trustedFileNameForDisplay, string targetDirectoryPath, + string trustedFileNameForFileStorage + ); +} + +// Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming +// and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ +public class FileUploadController : Controller { - // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming - // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ - public class FileUploadController : Controller + private const long + _fileSizeLimit = + 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte + + private const string _targetDirectoryPath = "./files/"; + + // Get the default form options so that we can use them to set the default + // limits for request body data. + private static readonly FormOptions _defaultFormOptions = new(); + private readonly string _accessToken; + private readonly ApplicationDbContext _context; + private readonly ILogger _logger; + + private readonly string[] _permittedExtensions = + { ".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png" }; + + public FileUploadController( + ILogger logger, + ApplicationDbContext context, + AppSettings appSettings + ) { - private readonly ILogger _logger; - private readonly ApplicationDbContext _context; - private readonly string _accessToken; - private const long _fileSizeLimit = 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte - private readonly string[] _permittedExtensions = { ".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png" }; - private const string _targetDirectoryPath = "./files/"; - - // Get the default form options so that we can use them to set the default - // limits for request body data. - private static readonly FormOptions _defaultFormOptions = new(); - - public FileUploadController( - ILogger logger, - ApplicationDbContext context, - AppSettings appSettings - ) + _logger = logger; + _context = context; + _accessToken = appSettings.AccessToken; + // To save physical files to the temporary files folder, use: + //_targetDirectoryPath = Path.GetTempPath(); + } + + // The following upload methods: + // + // 1. Disable the form value model binding to take control of handling + // potentially large files. + // + // 2. Typically, antiforgery tokens are sent in request body. Since we + // don't want to read the request body early, the tokens are sent via + // headers. The antiforgery token filter first looks for tokens in + // the request header and then falls back to reading the body. + + [HttpPost("~/api/upload-file")] + [DisableFormValueModelBinding] + // TODO Add this `[ValidateAntiForgeryToken]` once we know where to set the generation token cookie! + // TODO Where to put: [GenerateAntiforgeryTokenCookie] ? + // TODO Are both RequestFormLimits and RequestSizeLimit needed? + // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#multipart-body-length-limit + [RequestFormLimits(MultipartBodyLengthLimit = 10737418240)] // 10 GiB + // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#kestrel-maximum-request-body-size + [RequestSizeLimit(10737418240)] // 10 GiB + public async Task UploadFile( + [FromQuery] Guid getHttpsResourceUuid, + CancellationToken cancellationToken + ) + { + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { - _logger = logger; - _context = context; - _accessToken = appSettings.AccessToken; - // To save physical files to the temporary files folder, use: - //_targetDirectoryPath = Path.GetTempPath(); + ModelState.AddModelError("File", + "The request content is not multipart."); + return BadRequest(ModelState); } - // The following upload methods: - // - // 1. Disable the form value model binding to take control of handling - // potentially large files. - // - // 2. Typically, antiforgery tokens are sent in request body. Since we - // don't want to read the request body early, the tokens are sent via - // headers. The antiforgery token filter first looks for tokens in - // the request header and then falls back to reading the body. - - [HttpPost("~/api/upload-file")] - [DisableFormValueModelBinding] - // TODO Add this `[ValidateAntiForgeryToken]` once we know where to set the generation token cookie! - // TODO Where to put: [GenerateAntiforgeryTokenCookie] ? - // TODO Are both RequestFormLimits and RequestSizeLimit needed? - // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#multipart-body-length-limit - [RequestFormLimits(MultipartBodyLengthLimit = 10737418240)] // 10 GiB - // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#kestrel-maximum-request-body-size - [RequestSizeLimit(10737418240)] // 10 GiB - public async Task UploadFile( - [FromQuery] Guid getHttpsResourceUuid, - CancellationToken cancellationToken - ) - { - if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) - { - ModelState.AddModelError("File", - $"The request content is not multipart."); - return BadRequest(ModelState); - } - if (!await _context.GetHttpsResources.AsQueryable() + if (!await _context.GetHttpsResources.AsQueryable() .Where(u => u.Id == getHttpsResourceUuid) .AnyAsync(cancellationToken) .ConfigureAwait(false) - ) - { - ModelState.AddModelError("GetHttpsResourceUuid", $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); - return BadRequest(ModelState); - } + ) + { + ModelState.AddModelError("GetHttpsResourceUuid", + $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); + return BadRequest(ModelState); + } - var boundary = MultipartRequestHelper.GetBoundary( - MediaTypeHeaderValue.Parse(Request.ContentType), - _defaultFormOptions.MultipartBoundaryLengthLimit); - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + var boundary = MultipartRequestHelper.GetBoundary( + MediaTypeHeaderValue.Parse(Request.ContentType), + _defaultFormOptions.MultipartBoundaryLengthLimit); + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); - if (section is null) - { - ModelState.AddModelError("AccessToken", "Cannot read access token."); - return BadRequest(ModelState); - } - var hasFirstContentDispositionHeader = - ContentDispositionHeaderValue.TryParse( - section.ContentDisposition, out var firstContentDisposition); - if (!hasFirstContentDispositionHeader) - { - ModelState.AddModelError("AccessToken", "Cannot parse content disposition header value."); - return BadRequest(ModelState); - } - if (!firstContentDisposition?.Name.Equals("accessToken") ?? throw new Exception("Impossible (because `hasFirstContentDispositionHeader` is `true`)")) - { - ModelState.AddModelError("AccessToken", $"Access token must come first, got {firstContentDisposition?.Name} instead."); - return BadRequest(ModelState); - } - var accessToken = await new StreamReader(section.Body).ReadToEndAsync(cancellationToken).ConfigureAwait(false); - if (accessToken != _accessToken) - { - ModelState.AddModelError("AccessToken", $"The access token {accessToken} is invalid."); - return BadRequest(ModelState); - } + if (section is null) + { + ModelState.AddModelError("AccessToken", "Cannot read access token."); + return BadRequest(ModelState); + } - Directory.CreateDirectory(_targetDirectoryPath); + var hasFirstContentDispositionHeader = + ContentDispositionHeaderValue.TryParse( + section.ContentDisposition, out var firstContentDisposition); + if (!hasFirstContentDispositionHeader) + { + ModelState.AddModelError("AccessToken", "Cannot parse content disposition header value."); + return BadRequest(ModelState); + } - section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); - while (section != null) - { - var hasContentDispositionHeader = - ContentDispositionHeaderValue.TryParse( - section.ContentDisposition, out var contentDisposition); + if (!firstContentDisposition?.Name.Equals("accessToken") ?? + throw new ArgumentException("Impossible (because `hasFirstContentDispositionHeader` is `true`)")) + { + ModelState.AddModelError("AccessToken", + $"Access token must come first, got {firstContentDisposition?.Name} instead."); + return BadRequest(ModelState); + } + + var accessToken = await new StreamReader(section.Body).ReadToEndAsync(cancellationToken).ConfigureAwait(false); + if (accessToken != _accessToken) + { + ModelState.AddModelError("AccessToken", $"The access token {accessToken} is invalid."); + return BadRequest(ModelState); + } - if (hasContentDispositionHeader) + Directory.CreateDirectory(_targetDirectoryPath); + + section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + while (section != null) + { + var hasContentDispositionHeader = + ContentDispositionHeaderValue.TryParse( + section.ContentDisposition, out var contentDisposition); + + if (hasContentDispositionHeader) + { + // This check assumes that there's a file + // present without form data. If form data + // is present, this method immediately fails + // and returns the model error. + if (!MultipartRequestHelper + .HasFileContentDisposition(contentDisposition ?? + throw new ArgumentException( + "Impossible (because `hasContentDispositionHeader` is `true`)"))) { - // This check assumes that there's a file - // present without form data. If form data - // is present, this method immediately fails - // and returns the model error. - if (!MultipartRequestHelper - .HasFileContentDisposition(contentDisposition ?? throw new Exception("Impossible (because `hasContentDispositionHeader` is `true`)"))) - { - ModelState.AddModelError("File", - $"There is no content-disposition header."); - return BadRequest(ModelState); - } - else - { - // Don't trust the file name sent by the client. To display - // the file name, HTML-encode the value. - var trustedFileNameForDisplay = WebUtility.HtmlEncode( - contentDisposition.FileName.Value); - var trustedFileNameForFileStorage = getHttpsResourceUuid.ToString("D"); - - // **WARNING!** - // In the following example, the file is saved without - // scanning the file's contents. In most production - // scenarios, an anti-virus/anti-malware scanner API - // is used on the file before making the file available - // for download or for use by other systems. - // For more information, see the topic that accompanies - // this sample. - - var streamedFileContent = await FileHelpers.ProcessStreamedFile( - section, - contentDisposition, - ModelState, - _permittedExtensions, - _fileSizeLimit - ).ConfigureAwait(false); - - if (!ModelState.IsValid) - { - return BadRequest(ModelState); - } - - using var targetStream = System.IO.File.Create( - Path.Combine(_targetDirectoryPath, trustedFileNameForFileStorage)); - await targetStream.WriteAsync(streamedFileContent, cancellationToken).ConfigureAwait(false); - - _logger.LogInformation( - "Uploaded file '{TrustedFileNameForDisplay}' saved to " + - "'{TargetFilePath}' as {TrustedFileNameForFileStorage}", - trustedFileNameForDisplay, _targetDirectoryPath, - trustedFileNameForFileStorage); - } + ModelState.AddModelError("File", + "There is no content-disposition header."); + return BadRequest(ModelState); } - // Drain any remaining section body that hasn't been consumed and - // read the headers for the next section. - section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + // Don't trust the file name sent by the client. To display + // the file name, HTML-encode the value. + var trustedFileNameForDisplay = WebUtility.HtmlEncode( + contentDisposition.FileName.Value); + var trustedFileNameForFileStorage = getHttpsResourceUuid.ToString("D"); + + // **WARNING!** + // In the following example, the file is saved without + // scanning the file's contents. In most production + // scenarios, an anti-virus/anti-malware scanner API + // is used on the file before making the file available + // for download or for use by other systems. + // For more information, see the topic that accompanies + // this sample. + + var streamedFileContent = await FileHelpers.ProcessStreamedFile( + section, + contentDisposition, + ModelState, + _permittedExtensions, + _fileSizeLimit + ).ConfigureAwait(false); + + if (!ModelState.IsValid) return BadRequest(ModelState); + + using var targetStream = System.IO.File.Create( + Path.Combine(_targetDirectoryPath, trustedFileNameForFileStorage)); + await targetStream.WriteAsync(streamedFileContent, cancellationToken).ConfigureAwait(false); + + _logger.SavedUploadedFile(trustedFileNameForDisplay, _targetDirectoryPath, + trustedFileNameForFileStorage); } - return Created(nameof(FileUploadController), null); + // Drain any remaining section body that hasn't been consumed and + // read the headers for the next section. + section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); } - private static Encoding GetEncoding(MultipartSection section) - { - var hasMediaTypeHeader = - MediaTypeHeaderValue.TryParse(section.ContentType, out var mediaType); - - // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in - // most cases. - if (!hasMediaTypeHeader || Encoding.UTF7.Equals( - mediaType?.Encoding ?? throw new Exception("Impossible (because `hasMediaTypeHeader` is `true`)!")) - ) - { - return Encoding.UTF8; - } + return Created(nameof(FileUploadController), null); + } - return mediaType.Encoding; - } + private static Encoding GetEncoding(MultipartSection section) + { + var hasMediaTypeHeader = + MediaTypeHeaderValue.TryParse(section.ContentType, out var mediaType); + + // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in + // most cases. + if (!hasMediaTypeHeader || Encoding.UTF7.Equals( + mediaType?.Encoding ?? + throw new ArgumentException("Impossible (because `hasMediaTypeHeader` is `true`)!")) + ) + return Encoding.UTF8; + + return mediaType.Encoding; } } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 6bf9dd20..e8260d49 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,109 +1,109 @@ +using Database.Enumerations; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; -using Npgsql; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; -namespace Database.Data +namespace Database.Data; + +// Inspired by +// [Authentication and authorization for SPAs](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0) +// [Customize Identity Model](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.0) +public sealed class ApplicationDbContext + : DbContext, IDataProtectionKeyContext { - // Inspired by - // [Authentication and authorization for SPAs](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-api-authorization?view=aspnetcore-3.0) - // [Customize Identity Model](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-3.0) - public sealed class ApplicationDbContext - : DbContext, IDataProtectionKeyContext + private readonly string _schemaName; + + public ApplicationDbContext( + DbContextOptions options + ) + : base(options) { - private static void CreateEnumerations(ModelBuilder builder) - { - // https://www.npgsql.org/efcore/mapping/enum.html#creating-your-database-enum - // Create enumerations in public schema because that is where - // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. - builder.HasPostgresEnum("public"); - } + var schemaNameOptions = options.FindExtension(); + _schemaName = schemaNameOptions is null ? "database" : schemaNameOptions.SchemaName; + } - private static - EntityTypeBuilder - ConfigureEntity( - EntityTypeBuilder builder - ) - where TEntity : Data.Entity - { - // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - builder - .Property(e => e.Id) - .HasDefaultValueSql("gen_random_uuid()"); - // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column - builder - .Property(e => e.Version) - .IsRowVersion(); - return builder; - } + // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset + public DbSet GetHttpsResources { get; private set; } = default!; + public DbSet CalorimetricData { get; private set; } = default!; + public DbSet HygrothermalData { get; private set; } = default!; + public DbSet OpticalData { get; private set; } = default!; + public DbSet PhotovoltaicData { get; private set; } = default!; + public DbSet Users { get; private set; } = default!; + public DbSet DataProtectionKeys { get; } = default!; - private static void ConfigureIdentityEntities( - ModelBuilder builder - ) - { - // https://stackoverflow.com/questions/19902756/asp-net-identity-dbcontext-confusion/35722688#35722688 - } + private static void CreateEnumerations(ModelBuilder builder) + { + // https://www.npgsql.org/efcore/mapping/enum.html#creating-your-database-enum + // Create enumerations in public schema because that is where + // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. + builder.HasPostgresEnum("public"); + } - private readonly string _schemaName; + private static + EntityTypeBuilder + ConfigureEntity( + EntityTypeBuilder builder + ) + where TEntity : Entity + { + // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + builder + .Property(e => e.Id) + .HasDefaultValueSql("gen_random_uuid()"); + // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column + builder + .Property(e => e.Version) + .IsRowVersion(); + return builder; + } - // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset - public DbSet GetHttpsResources { get; private set; } = default!; - public DbSet DataProtectionKeys { get; private set; } = default!; - public DbSet CalorimetricData { get; private set; } = default!; - public DbSet HygrothermalData { get; private set; } = default!; - public DbSet OpticalData { get; private set; } = default!; - public DbSet PhotovoltaicData { get; private set; } = default!; - public DbSet Users { get; private set; } = default!; + private static void ConfigureIdentityEntities( + ModelBuilder builder + ) + { + // https://stackoverflow.com/questions/19902756/asp-net-identity-dbcontext-confusion/35722688#35722688 + } - public ApplicationDbContext( - DbContextOptions options + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + modelBuilder.HasDefaultSchema(_schemaName); + modelBuilder.HasPostgresExtension( + "pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + CreateEnumerations(modelBuilder); + ConfigureIdentityEntities(modelBuilder); + ConfigureEntity( + modelBuilder.Entity() ) - : base(options) - { - var schemaNameOptions = options.FindExtension(); - _schemaName = schemaNameOptions is null ? "database" : schemaNameOptions.SchemaName; - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - builder.HasDefaultSchema(_schemaName); - builder.HasPostgresExtension("pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - CreateEnumerations(builder); - ConfigureIdentityEntities(builder); - ConfigureEntity( - builder.Entity() - ) - .ToTable("get_https_resource"); - ConfigureEntity( - builder.Entity() - ) - .OwnsOne( - data => data.AppliedMethod, - method => - { - method.OwnsMany(m => m.Arguments); - method.OwnsMany(m => m.Sources); - } - ) - .ToTable("calorimetric_data"); - ConfigureEntity( - builder.Entity() - ) - .ToTable("hygrothermal_data"); - ConfigureEntity( - builder.Entity() - ) - .ToTable("optical_data"); - ConfigureEntity( - builder.Entity() - ) - .ToTable("photovoltaic_data"); - ConfigureEntity( - builder.Entity() - ) - .ToTable("user"); - } + .ToTable("get_https_resource"); + ConfigureEntity( + modelBuilder.Entity() + ) + .OwnsOne( + data => data.AppliedMethod, + method => + { + method.OwnsMany(m => m.Arguments); + method.OwnsMany(m => m.Sources); + } + ) + .ToTable("calorimetric_data"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("hygrothermal_data"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("optical_data"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("photovoltaic_data"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("user"); } } \ No newline at end of file diff --git a/backend/src/Data/AppliedMethod.cs b/backend/src/Data/AppliedMethod.cs index 1311db11..ebb2d35c 100644 --- a/backend/src/Data/AppliedMethod.cs +++ b/backend/src/Data/AppliedMethod.cs @@ -2,32 +2,31 @@ using System.Collections.Generic; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class AppliedMethod { - [Owned] - public sealed class AppliedMethod + public AppliedMethod( + Guid methodId, + ICollection arguments, + ICollection sources + ) + : this(methodId) { - public Guid MethodId { get; private set; } - public ICollection Arguments { get; private set; } = new List(); - public ICollection Sources { get; private set; } = new List(); - - public AppliedMethod( - Guid methodId, - ICollection arguments, - ICollection sources - ) - : this(methodId: methodId) - { - Arguments = arguments; - Sources = sources; - } + Arguments = arguments; + Sources = sources; + } - // `DbContext` needs this constructor without owned entities. - public AppliedMethod( - Guid methodId - ) - { - MethodId = methodId; - } + // `DbContext` needs this constructor without owned entities. + public AppliedMethod( + Guid methodId + ) + { + MethodId = methodId; } + + public Guid MethodId { get; private set; } + public ICollection Arguments { get; private set; } = new List(); + public ICollection Sources { get; private set; } = new List(); } \ No newline at end of file diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index affdf121..ed3f8260 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -2,70 +2,69 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -namespace Database.Data -{ - public sealed class CalorimetricData +namespace Database.Data; + +public sealed class CalorimetricData : DataX +{ + public CalorimetricData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + ICollection approvals, + // ResponseApproval approval + double[] gValues, + double[] uValues + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + approvals + ) { - [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] - public override ICollection Resources { get; } = new List(); + GValues = gValues; + UValues = uValues; + } - public double[] GValues { get; private set; } - public double[] UValues { get; private set; } + // `DbContext` needs this constructor without owned entities. + public CalorimetricData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + // ResponseApproval approval + double[] gValues, + double[] uValues + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + GValues = gValues; + UValues = uValues; + } - public CalorimetricData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals, - // ResponseApproval approval - double[] gValues, - double[] uValues - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt, - appliedMethod: appliedMethod, - approvals: approvals - ) - { - GValues = gValues; - UValues = uValues; - } + [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] + public override ICollection Resources { get; } = new List(); - // `DbContext` needs this constructor without owned entities. - public CalorimetricData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - // ResponseApproval approval - double[] gValues, - double[] uValues - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt - ) - { - GValues = gValues; - UValues = uValues; - } - } -} + public double[] GValues { get; private set; } + public double[] UValues { get; private set; } +} \ No newline at end of file diff --git a/backend/src/Data/CielabColor.cs b/backend/src/Data/CielabColor.cs index 425056d7..a8ad1ac7 100644 --- a/backend/src/Data/CielabColor.cs +++ b/backend/src/Data/CielabColor.cs @@ -1,23 +1,22 @@ using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class CielabColor { - [Owned] - public sealed class CielabColor + public CielabColor( + double lStar, + double aStar, + double bStar + ) { - public double LStar { get; private set; } - public double AStar { get; private set; } - public double BStar { get; private set; } - - public CielabColor( - double lStar, - double aStar, - double bStar - ) - { - LStar = lStar; - AStar = aStar; - BStar = bStar; - } + LStar = lStar; + AStar = aStar; + BStar = bStar; } + + public double LStar { get; private set; } + public double AStar { get; private set; } + public double BStar { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index f026836a..5d3d1554 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -1,28 +1,27 @@ using System; -using Microsoft.EntityFrameworkCore; using Database.Enumerations; +using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class CrossDatabaseDataReference - { - public Guid DataId { get; private set; } - public DateTime DataTimestamp { get; private set; } - public DataKind DataKind { get; private set; } - public Guid DatabaseId { get; private set; } +namespace Database.Data; - public CrossDatabaseDataReference( +[Owned] +public sealed class CrossDatabaseDataReference +{ + public CrossDatabaseDataReference( Guid dataId, DateTime dataTimestamp, DataKind dataKind, Guid databaseId - ) - { - DataId = dataId; - DataTimestamp = dataTimestamp; - DataKind = dataKind; - DatabaseId = databaseId; - } + ) + { + DataId = dataId; + DataTimestamp = dataTimestamp; + DataKind = dataKind; + DatabaseId = databaseId; } -} + + public Guid DataId { get; private set; } + public DateTime DataTimestamp { get; private set; } + public DataKind DataKind { get; private set; } + public Guid DatabaseId { get; private set; } +} \ No newline at end of file diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 83d59b7d..18724a1d 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,34 +1,33 @@ using System; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class DataApproval +namespace Database.Data; + +[Owned] +public sealed class DataApproval : IApproval +{ + public DataApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + string response, + Guid approverId + ) { - public DateTime Timestamp { get; private set; } - public string Signature { get; private set; } - public string KeyFingerprint { get; private set; } - public string Query { get; private set; } - public string Response { get; private set; } - public Guid ApproverId { get; private set; } - - public DataApproval( - DateTime timestamp, - string signature, - string keyFingerprint, - string query, - string response, - Guid approverId - ) - { - Timestamp = timestamp; - Signature = signature; - KeyFingerprint = keyFingerprint; - Query = query; - Response = response; - ApproverId = approverId; - } + Timestamp = timestamp; + Signature = signature; + KeyFingerprint = keyFingerprint; + Query = query; + Response = response; + ApproverId = approverId; } -} + + public Guid ApproverId { get; private set; } + public DateTime Timestamp { get; } + public string Signature { get; } + public string KeyFingerprint { get; } + public string Query { get; } + public string Response { get; } +} \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 68ed4fbf..54ca8373 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,70 +1,70 @@ using System; using System.Collections.Generic; -namespace Database.Data -{ - public abstract class DataX - : Data.Entity, IData - { - public string Locale { get; private set; } - public Guid ComponentId { get; private set; } - public string? Name { get; private set; } - public string? Description { get; private set; } - public string[] Warnings { get; private set; } - public Guid CreatorId { get; private set; } - public DateTime CreatedAt { get; private set; } - public AppliedMethod AppliedMethod { get; private set; } = default!; - public ICollection Approvals { get; private set; } = new List(); - // public ResponseApproval Approval { get; private set; } - - // TODO Exactly one resource must not have a parent and each other resource must have one from this list and the graph must be connected. In other words, the resources must form a tree. - public virtual ICollection Resources { get; } = new List(); +namespace Database.Data; - protected DataX( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals +public abstract class DataX + : Entity, IData +{ + protected DataX( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + ICollection approvals // ResponseApproval approval - ) + ) : this( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt ) - { - AppliedMethod = appliedMethod; - Approvals = approvals; - // Approval = approval; - } + { + AppliedMethod = appliedMethod; + Approvals = approvals; + // Approval = approval; + } - // `DbContext` needs this constructor without owned entities. - protected DataX( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt - ) - { - Locale = locale; - ComponentId = componentId; - Name = name; - Description = description; - Warnings = warnings; - CreatorId = creatorId; - CreatedAt = createdAt; - } + // `DbContext` needs this constructor without owned entities. + protected DataX( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt + ) + { + Locale = locale; + ComponentId = componentId; + Name = name; + Description = description; + Warnings = warnings; + CreatorId = creatorId; + CreatedAt = createdAt; } -} + + public string Locale { get; } + public Guid ComponentId { get; } + public string? Name { get; } + public string? Description { get; } + public string[] Warnings { get; } + public Guid CreatorId { get; } + public DateTime CreatedAt { get; } + public AppliedMethod AppliedMethod { get; } = default!; + + public ICollection Approvals { get; } = new List(); + // public ResponseApproval Approval { get; private set; } + + // TODO Exactly one resource must not have a parent and each other resource must have one from this list and the graph must be connected. In other words, the resources must form a tree. + public virtual ICollection Resources { get; } = new List(); +} \ No newline at end of file diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index 60871ed1..115489b6 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -1,21 +1,30 @@ using System; using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -namespace Database.Data +namespace Database.Data; + +public static partial class Log +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Debug, + Message = "Seeding the database")] + public static partial void SeedingDatabase( + this ILogger logger + ); +} + +public sealed class DbSeeder { - public sealed class DbSeeder + public static async Task DoAsync( + IServiceProvider services + ) { - public static async Task DoAsync( - IServiceProvider services - ) - { - var logger = services.GetRequiredService>(); - logger.LogDebug("Seeding the database"); - // var environment = services.GetRequiredService(); - // var appSettings = services.GetRequiredService(); - } + var logger = services.GetRequiredService>(); + logger.SeedingDatabase(); + // var environment = services.GetRequiredService(); + // var appSettings = services.GetRequiredService(); } } \ No newline at end of file diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index f94e988c..536310b7 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -1,21 +1,16 @@ using Guid = System.Guid; + // using System.ComponentModel.DataAnnotations.Schema; -namespace Database.Data -{ - public abstract class Entity - : IEntity - { - public Guid Id { get; private set; } +namespace Database.Data; - // [NotMapped] - // public Guid Uuid { get => Id; } +public abstract class Entity + : IEntity +{ + public Guid Id { get; } - public uint Version { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html + // [NotMapped] + // public Guid Uuid { get => Id; } - protected Entity( - ) - { - } - } + public uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html } \ No newline at end of file diff --git a/backend/src/Data/Extensions/SchemaDbContextOptionsBuilderExtensions.cs b/backend/src/Data/Extensions/SchemaDbContextOptionsBuilderExtensions.cs index 63aab5a6..18327354 100644 --- a/backend/src/Data/Extensions/SchemaDbContextOptionsBuilderExtensions.cs +++ b/backend/src/Data/Extensions/SchemaDbContextOptionsBuilderExtensions.cs @@ -2,27 +2,28 @@ using Microsoft.EntityFrameworkCore.Infrastructure; // Inspired by https://github.com/npgsql/efcore.pg/blob/main/src/EFCore.PG/Extensions/NpgsqlDbContextOptionsBuilderExtensions.cs -namespace Database.Data.Extensions +namespace Database.Data.Extensions; + +public static class SchemaNameDbContextOptionsBuilderExtensions { - public static class SchemaNameDbContextOptionsBuilderExtensions + public static DbContextOptionsBuilder UseSchemaName( + this DbContextOptionsBuilder optionsBuilder, + string schemaName + ) { - public static DbContextOptionsBuilder UseSchemaName( - this DbContextOptionsBuilder optionsBuilder, - string schemaName - ) - { - ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension( - new SchemaNameOptionsExtension(schemaName) - ); - return optionsBuilder; - } + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension( + new SchemaNameOptionsExtension(schemaName) + ); + return optionsBuilder; + } - public static DbContextOptionsBuilder UseSchemaName( - this DbContextOptionsBuilder optionsBuilder, - string connectionString - ) - where TContext : DbContext - => (DbContextOptionsBuilder)UseSchemaName( - (DbContextOptionsBuilder)optionsBuilder, connectionString); + public static DbContextOptionsBuilder UseSchemaName( + this DbContextOptionsBuilder optionsBuilder, + string connectionString + ) + where TContext : DbContext + { + return (DbContextOptionsBuilder)UseSchemaName( + (DbContextOptionsBuilder)optionsBuilder, connectionString); } } \ No newline at end of file diff --git a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs index 80c090e4..008d241a 100644 --- a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs +++ b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs @@ -3,62 +3,61 @@ using Microsoft.Extensions.DependencyInjection; // Inspired by https://github.com/npgsql/efcore.pg/blob/main/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs -namespace Database.Data.Extensions +namespace Database.Data.Extensions; + +public sealed class SchemaNameOptionsExtension + : IDbContextOptionsExtension { - public sealed class SchemaNameOptionsExtension - : IDbContextOptionsExtension + public SchemaNameOptionsExtension(string schemaName) { - public DbContextOptionsExtensionInfo Info - => new SchemaNameExtensionInfo(this); + SchemaName = schemaName; + } - public string SchemaName { get; } + public string SchemaName { get; } - public SchemaNameOptionsExtension(string schemaName) - { - SchemaName = schemaName; - } + public DbContextOptionsExtensionInfo Info + => new SchemaNameExtensionInfo(this); - public void ApplyServices(IServiceCollection services) - { - } + public void ApplyServices(IServiceCollection services) + { + } - public void Validate(IDbContextOptions options) - { - } + public void Validate(IDbContextOptions options) + { + } - public sealed class SchemaNameExtensionInfo - : DbContextOptionsExtensionInfo + public sealed class SchemaNameExtensionInfo + : DbContextOptionsExtensionInfo + { + public SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) + : base(extension) { - public override bool IsDatabaseProvider - => false; + } - public override string LogFragment - => $"{nameof(Extension.SchemaName)}={Extension.SchemaName}"; + public override bool IsDatabaseProvider + => false; - public new SchemaNameOptionsExtension Extension => (SchemaNameOptionsExtension)base.Extension; + public override string LogFragment + => $"{nameof(Extension.SchemaName)}={Extension.SchemaName}"; - public SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) - : base(extension) - { - } + public new SchemaNameOptionsExtension Extension => (SchemaNameOptionsExtension)base.Extension; - public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) - { - return true; - } + public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other) + { + return true; + } - public override int GetServiceProviderHashCode() - { - return 0; - } + public override int GetServiceProviderHashCode() + { + return 0; + } - public override void PopulateDebugInfo( - IDictionary debugInfo - ) - { - debugInfo[$"Database.Data.Extensions:${nameof(SchemaNameOptionsExtension.SchemaName)}"] - = Extension.SchemaName; - } + public override void PopulateDebugInfo( + IDictionary debugInfo + ) + { + debugInfo[$"Database.Data.Extensions:${nameof(SchemaName)}"] + = Extension.SchemaName; } } } \ No newline at end of file diff --git a/backend/src/Data/FileMetaInformation.cs b/backend/src/Data/FileMetaInformation.cs index ea337915..586bfdd6 100644 --- a/backend/src/Data/FileMetaInformation.cs +++ b/backend/src/Data/FileMetaInformation.cs @@ -1,21 +1,20 @@ using System; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class FileMetaInformation { - [Owned] - public sealed class FileMetaInformation + public FileMetaInformation( + string[] path, + Guid dataFormatId + ) { - public string[] Path { get; private set; } - public Guid DataFormatId { get; private set; } - - public FileMetaInformation( - string[] path, - Guid dataFormatId - ) - { - Path = path; - DataFormatId = dataFormatId; - } + Path = path; + DataFormatId = dataFormatId; } -} + + public string[] Path { get; private set; } + public Guid DataFormatId { get; private set; } +} \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 8fc9bf07..703b8451 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -3,139 +3,141 @@ using System.ComponentModel.DataAnnotations.Schema; using Database.Extensions; -namespace Database.Data +namespace Database.Data; + +public sealed class GetHttpsResource + : Entity { - public sealed class GetHttpsResource - : Data.Entity + public GetHttpsResource( + string description, + string hashValue, + Guid dataFormatId, + Guid? calorimetricDataId, + Guid? hygrothermalDataId, + Guid? opticalDataId, + Guid? photovoltaicDataId, + Guid? parentId, + ICollection archivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethod? appliedConversionMethod + ) + : this( + description, + hashValue, + dataFormatId, + parentId, + archivedFilesMetaInformation, + appliedConversionMethod + ) { - public static string ConstructVertexId(Guid id) - { - return id.ToString("D").Base64Encode(); - } + CalorimetricDataId = calorimetricDataId; + HygrothermalDataId = hygrothermalDataId; + OpticalDataId = opticalDataId; + PhotovoltaicDataId = photovoltaicDataId; + } - public string Description { get; private set; } - public string HashValue { get; private set; } - public Guid DataFormatId { get; private set; } - public ICollection ArchivedFilesMetaInformation { get; private set; } = new List(); + public GetHttpsResource( + string description, + string hashValue, + Guid dataFormatId, + Guid? parentId, + ICollection archivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethod? appliedConversionMethod + ) + : this( + description, + hashValue, + dataFormatId, + parentId + ) + { + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + AppliedConversionMethod = appliedConversionMethod; + } - // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! - [NotMapped] - public Guid DataId { get => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; } - [NotMapped] - public IData? Data { get => CalorimetricData ?? HygrothermalData ?? OpticalData ?? PhotovoltaicData as IData; } + // `DbContext` needs this constructor without owned entities. + public GetHttpsResource( + string description, + string hashValue, + Guid dataFormatId, + Guid? calorimetricDataId, + Guid? hygrothermalDataId, + Guid? opticalDataId, + Guid? photovoltaicDataId, + Guid? parentId + ) + : this( + description, + hashValue, + dataFormatId, + parentId + ) + { + CalorimetricDataId = calorimetricDataId; + HygrothermalDataId = hygrothermalDataId; + OpticalDataId = opticalDataId; + PhotovoltaicDataId = photovoltaicDataId; + } - public Guid? CalorimetricDataId { get; private set; } - [InverseProperty(nameof(Database.Data.CalorimetricData.Resources))] - public CalorimetricData? CalorimetricData { get; set; } + public GetHttpsResource( + string description, + string hashValue, + Guid dataFormatId, + Guid? parentId + ) + { + Description = description; + HashValue = hashValue; + DataFormatId = dataFormatId; + ParentId = parentId; + } - public Guid? HygrothermalDataId { get; private set; } + public string Description { get; private set; } + public string HashValue { get; private set; } + public Guid DataFormatId { get; private set; } - [InverseProperty(nameof(Database.Data.HygrothermalData.Resources))] - public HygrothermalData? HygrothermalData { get; set; } + public ICollection ArchivedFilesMetaInformation { get; private set; } = + new List(); - public Guid? OpticalDataId { get; private set; } + // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! + [NotMapped] + public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; - [InverseProperty(nameof(Database.Data.OpticalData.Resources))] - public OpticalData? OpticalData { get; set; } + [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? PhotovoltaicData as IData; - public Guid? PhotovoltaicDataId { get; private set; } + public Guid? CalorimetricDataId { get; } - [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] - public PhotovoltaicData? PhotovoltaicData { get; set; } + [InverseProperty(nameof(Database.Data.CalorimetricData.Resources))] + public CalorimetricData? CalorimetricData { get; set; } + public Guid? HygrothermalDataId { get; } - public Guid? ParentId { get; private set; } - // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. - public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } + [InverseProperty(nameof(Database.Data.HygrothermalData.Resources))] + public HygrothermalData? HygrothermalData { get; set; } - // TODO The parent's `Data` must be the same as this resource's `Data`. - [InverseProperty(nameof(Children))] - public GetHttpsResource? Parent { get; set; } + public Guid? OpticalDataId { get; } - [InverseProperty(nameof(Parent))] - public ICollection Children { get; } = new List(); + [InverseProperty(nameof(Database.Data.OpticalData.Resources))] + public OpticalData? OpticalData { get; set; } - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? calorimetricDataId, - Guid? hygrothermalDataId, - Guid? opticalDataId, - Guid? photovoltaicDataId, - Guid? parentId, - ICollection archivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethod? appliedConversionMethod - ) - : this( - description: description, - hashValue: hashValue, - dataFormatId: dataFormatId, - parentId: parentId, - archivedFilesMetaInformation: archivedFilesMetaInformation, - appliedConversionMethod: appliedConversionMethod - ) - { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; - } - - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? parentId, - ICollection archivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethod? appliedConversionMethod - ) - : this( - description: description, - hashValue: hashValue, - dataFormatId: dataFormatId, - parentId: parentId - ) - { - ArchivedFilesMetaInformation = archivedFilesMetaInformation; - AppliedConversionMethod = appliedConversionMethod; - } - - // `DbContext` needs this constructor without owned entities. - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? calorimetricDataId, - Guid? hygrothermalDataId, - Guid? opticalDataId, - Guid? photovoltaicDataId, - Guid? parentId - ) - : this( - description: description, - hashValue: hashValue, - dataFormatId: dataFormatId, - parentId: parentId - ) - { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; - } - - public GetHttpsResource( - string description, - string hashValue, - Guid dataFormatId, - Guid? parentId - ) - { - Description = description; - HashValue = hashValue; - DataFormatId = dataFormatId; - ParentId = parentId; - } + public Guid? PhotovoltaicDataId { get; } + + [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] + public PhotovoltaicData? PhotovoltaicData { get; set; } + + + public Guid? ParentId { get; private set; } + + // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. + public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } + + // TODO The parent's `Data` must be the same as this resource's `Data`. + [InverseProperty(nameof(Children))] public GetHttpsResource? Parent { get; set; } + + [InverseProperty(nameof(Parent))] + public ICollection Children { get; } = new List(); + + public static string ConstructVertexId(Guid id) + { + return id.ToString("D").Base64Encode(); } -} +} \ No newline at end of file diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 97501231..7fe32480 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -2,59 +2,58 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -namespace Database.Data -{ - public sealed class HygrothermalData - : DataX - { - [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] - public override ICollection Resources { get; } = new List(); +namespace Database.Data; - public HygrothermalData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals +public sealed class HygrothermalData + : DataX +{ + public HygrothermalData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + ICollection approvals // ResponseApproval approval - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt, - appliedMethod: appliedMethod, - approvals: approvals - ) - { - } + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + approvals + ) + { + } - // `DbContext` needs this constructor without owned entities. - public HygrothermalData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt + // `DbContext` needs this constructor without owned entities. + public HygrothermalData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt // ResponseApproval approval - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt - ) - { - } + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { } -} + + [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] + public override ICollection Resources { get; } = new List(); +} \ No newline at end of file diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 5d623eb5..98911da3 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,15 +1,14 @@ using System; using HotChocolate.Types; -namespace Database.Data +namespace Database.Data; + +[InterfaceType("Approval")] +public interface IApproval { - [InterfaceType("Approval")] - public interface IApproval - { - DateTime Timestamp { get; } - string Signature { get; } - string KeyFingerprint { get; } - string Query { get; } - string Response { get; } - } -} + DateTime Timestamp { get; } + string Signature { get; } + string KeyFingerprint { get; } + string Query { get; } + string Response { get; } +} \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 93c6a08b..4758df04 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,20 +1,21 @@ using System; using System.Collections.Generic; -namespace Database.Data +namespace Database.Data; + +public interface IData { - public interface IData - { - Guid ComponentId { get; } - string? Name { get; } - string? Description { get; } - string[] Warnings { get; } - Guid CreatorId { get; } - DateTime CreatedAt { get; } - AppliedMethod AppliedMethod { get; } - ICollection Approvals { get; } - ICollection Resources { get; } - // ResponseApproval Approval { get; } - string Locale { get; } - } -} + Guid ComponentId { get; } + string? Name { get; } + string? Description { get; } + string[] Warnings { get; } + Guid CreatorId { get; } + DateTime CreatedAt { get; } + AppliedMethod AppliedMethod { get; } + ICollection Approvals { get; } + + ICollection Resources { get; } + + // ResponseApproval Approval { get; } + string Locale { get; } +} \ No newline at end of file diff --git a/backend/src/Data/IEntity.cs b/backend/src/Data/IEntity.cs index a63b8b19..3e065f5a 100644 --- a/backend/src/Data/IEntity.cs +++ b/backend/src/Data/IEntity.cs @@ -1,11 +1,10 @@ using Guid = System.Guid; -namespace Database.Data +namespace Database.Data; + +public interface IEntity { - public interface IEntity - { - Guid Id { get; } + Guid Id { get; } - uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html - } + uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html } \ No newline at end of file diff --git a/backend/src/Data/NamedMethodArgument.cs b/backend/src/Data/NamedMethodArgument.cs index 13bc8f83..03462578 100644 --- a/backend/src/Data/NamedMethodArgument.cs +++ b/backend/src/Data/NamedMethodArgument.cs @@ -2,24 +2,26 @@ using System.Text.Json; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class NamedMethodArgument + : IDisposable { - [Owned] - public sealed class NamedMethodArgument - : IDisposable + public NamedMethodArgument( + string name, + JsonDocument value + ) { - public string Name { get; private set; } - public JsonDocument Value { get; private set; } + Name = name; + Value = value; + } - public NamedMethodArgument( - string name, - JsonDocument value - ) - { - Name = name; - Value = value; - } + public string Name { get; private set; } + public JsonDocument Value { get; } - public void Dispose() => Value.Dispose(); + public void Dispose() + { + Value.Dispose(); } } \ No newline at end of file diff --git a/backend/src/Data/NamedMethodSource.cs b/backend/src/Data/NamedMethodSource.cs index 1245a4ec..1a6820af 100644 --- a/backend/src/Data/NamedMethodSource.cs +++ b/backend/src/Data/NamedMethodSource.cs @@ -1,29 +1,28 @@ using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class NamedMethodSource { - [Owned] - public sealed class NamedMethodSource + public NamedMethodSource( + string name, + CrossDatabaseDataReference value + ) + : this(name) { - public string Name { get; private set; } - public CrossDatabaseDataReference Value { get; private set; } = default!; - - public NamedMethodSource( - string name, - CrossDatabaseDataReference value - ) - : this(name: name) - { - Name = name; - Value = value; - } + Name = name; + Value = value; + } - // `DbContext` needs this constructor without owned entities. - public NamedMethodSource( - string name - ) - { - Name = name; - } + // `DbContext` needs this constructor without owned entities. + public NamedMethodSource( + string name + ) + { + Name = name; } + + public string Name { get; private set; } + public CrossDatabaseDataReference Value { get; private set; } = default!; } \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 477c7d95..e54ecb36 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -2,93 +2,92 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -namespace Database.Data -{ - public sealed class OpticalData +namespace Database.Data; + +public sealed class OpticalData : DataX +{ + public OpticalData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + ICollection approvals, + // ResponseApproval approval + double[] nearnormalHemisphericalVisibleTransmittances, + double[] nearnormalHemisphericalVisibleReflectances, + double[] nearnormalHemisphericalSolarTransmittances, + double[] nearnormalHemisphericalSolarReflectances, + double[] infraredEmittances, + double[] colorRenderingIndices, + ICollection cielabColors + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + approvals + ) { - [InverseProperty(nameof(GetHttpsResource.OpticalData))] - public override ICollection Resources { get; } = new List(); + NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; + NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; + NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; + NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; + InfraredEmittances = infraredEmittances; + ColorRenderingIndices = colorRenderingIndices; + CielabColors = cielabColors; + } - public double[] NearnormalHemisphericalVisibleTransmittances { get; private set; } - public double[] NearnormalHemisphericalVisibleReflectances { get; private set; } - public double[] NearnormalHemisphericalSolarTransmittances { get; private set; } - public double[] NearnormalHemisphericalSolarReflectances { get; private set; } - public double[] InfraredEmittances { get; private set; } - public double[] ColorRenderingIndices { get; private set; } - public ICollection CielabColors { get; private set; } = new List(); + // `DbContext` needs this constructor without owned entities. + public OpticalData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + // ResponseApproval approval + double[] nearnormalHemisphericalVisibleTransmittances, + double[] nearnormalHemisphericalVisibleReflectances, + double[] nearnormalHemisphericalSolarTransmittances, + double[] nearnormalHemisphericalSolarReflectances, + double[] infraredEmittances, + double[] colorRenderingIndices + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; + NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; + NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; + NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; + InfraredEmittances = infraredEmittances; + ColorRenderingIndices = colorRenderingIndices; + } - public OpticalData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals, - // ResponseApproval approval - double[] nearnormalHemisphericalVisibleTransmittances, - double[] nearnormalHemisphericalVisibleReflectances, - double[] nearnormalHemisphericalSolarTransmittances, - double[] nearnormalHemisphericalSolarReflectances, - double[] infraredEmittances, - double[] colorRenderingIndices, - ICollection cielabColors - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt, - appliedMethod: appliedMethod, - approvals: approvals - ) - { - NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; - NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; - NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; - NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; - InfraredEmittances = infraredEmittances; - ColorRenderingIndices = colorRenderingIndices; - CielabColors = cielabColors; - } + [InverseProperty(nameof(GetHttpsResource.OpticalData))] + public override ICollection Resources { get; } = new List(); - // `DbContext` needs this constructor without owned entities. - public OpticalData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - // ResponseApproval approval - double[] nearnormalHemisphericalVisibleTransmittances, - double[] nearnormalHemisphericalVisibleReflectances, - double[] nearnormalHemisphericalSolarTransmittances, - double[] nearnormalHemisphericalSolarReflectances, - double[] infraredEmittances, - double[] colorRenderingIndices - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt - ) - { - NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; - NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; - NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; - NearnormalHemisphericalSolarReflectances = nearnormalHemisphericalSolarReflectances; - InfraredEmittances = infraredEmittances; - ColorRenderingIndices = colorRenderingIndices; - } - } -} + public double[] NearnormalHemisphericalVisibleTransmittances { get; private set; } + public double[] NearnormalHemisphericalVisibleReflectances { get; private set; } + public double[] NearnormalHemisphericalSolarTransmittances { get; private set; } + public double[] NearnormalHemisphericalSolarReflectances { get; private set; } + public double[] InfraredEmittances { get; private set; } + public double[] ColorRenderingIndices { get; private set; } + public ICollection CielabColors { get; private set; } = new List(); +} \ No newline at end of file diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 06d37513..f0e5de9d 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -2,59 +2,58 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -namespace Database.Data -{ - public sealed class PhotovoltaicData - : DataX - { - [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] - public override ICollection Resources { get; } = new List(); +namespace Database.Data; - public PhotovoltaicData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals +public sealed class PhotovoltaicData + : DataX +{ + public PhotovoltaicData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + ICollection approvals // ResponseApproval approval - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt, - appliedMethod: appliedMethod, - approvals: approvals - ) - { - } + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + approvals + ) + { + } - // `DbContext` needs this constructor without owned entities. - public PhotovoltaicData( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt + // `DbContext` needs this constructor without owned entities. + public PhotovoltaicData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt // ResponseApproval approval - ) : base( - locale: locale, - componentId: componentId, - name: name, - description: description, - warnings: warnings, - creatorId: creatorId, - createdAt: createdAt - ) - { - } + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { } -} + + [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] + public override ICollection Resources { get; } = new List(); +} \ No newline at end of file diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index d2e888f5..0c7c4a56 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -28,3 +28,4 @@ // public string Response { get; private set; } // } // } + diff --git a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs index 6d9369e4..c05a8183 100644 --- a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs +++ b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs @@ -2,36 +2,35 @@ using System.Collections.Generic; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class ToTreeVertexAppliedConversionMethod - { - public Guid MethodId { get; private set; } - public ICollection Arguments { get; private set; } = new List(); - public string SourceName { get; private set; } +namespace Database.Data; - public ToTreeVertexAppliedConversionMethod( - Guid methodId, - ICollection arguments, - string sourceName - ) +[Owned] +public sealed class ToTreeVertexAppliedConversionMethod +{ + public ToTreeVertexAppliedConversionMethod( + Guid methodId, + ICollection arguments, + string sourceName + ) : this( - methodId: methodId, - sourceName: sourceName + methodId, + sourceName ) - { - Arguments = arguments; - } + { + Arguments = arguments; + } - // `DbContext` needs this constructor without owned entities. - public ToTreeVertexAppliedConversionMethod( - Guid methodId, - string sourceName - ) - { - MethodId = methodId; - SourceName = sourceName; - } + // `DbContext` needs this constructor without owned entities. + public ToTreeVertexAppliedConversionMethod( + Guid methodId, + string sourceName + ) + { + MethodId = methodId; + SourceName = sourceName; } -} + + public Guid MethodId { get; private set; } + public ICollection Arguments { get; private set; } = new List(); + public string SourceName { get; private set; } +} \ No newline at end of file diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs index 5d043007..d1767fca 100644 --- a/backend/src/Data/User.cs +++ b/backend/src/Data/User.cs @@ -1,24 +1,23 @@ -namespace Database.Data +namespace Database.Data; + +public sealed class User : Entity { - public sealed class User : Entity + public User( + string subject, + string name + ) { - public string Subject { get; private set; } - public string Name { get; private set; } + Subject = subject; + Name = name; + } - public User( - string subject, - string name - ) - { - Subject = subject; - Name = name; - } + public string Subject { get; private set; } + public string Name { get; private set; } - public void Update( - string name - ) - { - Name = name; - } + public void Update( + string name + ) + { + Name = name; } } \ No newline at end of file diff --git a/backend/src/Enumerations/DataKind.cs b/backend/src/Enumerations/DataKind.cs index 27a343e6..f64d6884 100644 --- a/backend/src/Enumerations/DataKind.cs +++ b/backend/src/Enumerations/DataKind.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; -namespace Database.Enumerations +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum DataKind { - public enum DataKind - { - CALORIMETRIC_DATA, - HYGROTHERMAL_DATA, - OPTICAL_DATA, - PHOTOVOLTAIC_DATA - } -} + CALORIMETRIC_DATA, + HYGROTHERMAL_DATA, + OPTICAL_DATA, + PHOTOVOLTAIC_DATA +} \ No newline at end of file diff --git a/backend/src/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 7acfb70c..00642a3f 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -1,28 +1,30 @@ -namespace Database.Extensions +using System; +using System.Text; + +namespace Database.Extensions; + +public static class StringExtensions { - public static class StringExtensions + public static string FirstCharToLower(this string str) { - public static string FirstCharToLower(this string str) - { - return string.IsNullOrEmpty(str) - || !char.IsLetter(str, 0) - || char.IsLower(str, 0) - ? str - : char.ToLowerInvariant(str[0]) + str[1..]; - } + return string.IsNullOrEmpty(str) + || !char.IsLetter(str, 0) + || char.IsLower(str, 0) + ? str + : char.ToLowerInvariant(str[0]) + str[1..]; + } - public static string Base64Encode(this string plainText) - { - return System.Convert.ToBase64String( - System.Text.Encoding.UTF8.GetBytes(plainText) - ); - } + public static string Base64Encode(this string plainText) + { + return Convert.ToBase64String( + Encoding.UTF8.GetBytes(plainText) + ); + } - public static string Base64Decode(this string base64EncodedData) - { - return System.Text.Encoding.UTF8.GetString( - System.Convert.FromBase64String(base64EncodedData) - ); - } + public static string Base64Decode(this string base64EncodedData) + { + return Encoding.UTF8.GetString( + Convert.FromBase64String(base64EncodedData) + ); } } \ No newline at end of file diff --git a/backend/src/Filters/DisableFormValueModelBindingAttribute.cs b/backend/src/Filters/DisableFormValueModelBindingAttribute.cs index b38ecae8..841254b3 100644 --- a/backend/src/Filters/DisableFormValueModelBindingAttribute.cs +++ b/backend/src/Filters/DisableFormValueModelBindingAttribute.cs @@ -2,21 +2,20 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.ModelBinding; -namespace Database.Filters +namespace Database.Filters; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter + public void OnResourceExecuting(ResourceExecutingContext context) { - public void OnResourceExecuting(ResourceExecutingContext context) - { - var factories = context.ValueProviderFactories; - factories.RemoveType(); - factories.RemoveType(); - factories.RemoveType(); - } + var factories = context.ValueProviderFactories; + factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + } - public void OnResourceExecuted(ResourceExecutedContext context) - { - } + public void OnResourceExecuted(ResourceExecutedContext context) + { } } \ No newline at end of file diff --git a/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs b/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs index c9d70b6c..e81bd485 100644 --- a/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs +++ b/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs @@ -4,26 +4,25 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; -namespace Database.Filters +namespace Database.Filters; + +public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute { - public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute - { - public const string RequestVerificationTokenName = "RequestVerificationToken"; + public const string RequestVerificationTokenName = "RequestVerificationToken"; - public override void OnResultExecuting(ResultExecutingContext context) - { - var antiforgery = context.HttpContext.RequestServices.GetRequiredService(); - // Send the request token as a JavaScript-readable cookie - var tokens = antiforgery.GetAndStoreTokens(context.HttpContext); - context.HttpContext.Response.Cookies.Append( - RequestVerificationTokenName, - tokens.RequestToken ?? throw new Exception("There is no request token."), - new CookieOptions() { HttpOnly = false } - ); - } + public override void OnResultExecuting(ResultExecutingContext context) + { + var antiforgery = context.HttpContext.RequestServices.GetRequiredService(); + // Send the request token as a JavaScript-readable cookie + var tokens = antiforgery.GetAndStoreTokens(context.HttpContext); + context.HttpContext.Response.Cookies.Append( + RequestVerificationTokenName, + tokens.RequestToken ?? throw new ArgumentException("There is no request token."), + new CookieOptions { HttpOnly = false } + ); + } - public override void OnResultExecuted(ResultExecutedContext context) - { - } + public override void OnResultExecuted(ResultExecutedContext context) + { } } \ No newline at end of file diff --git a/backend/src/GraphQl/AppliedMethodInput.cs b/backend/src/GraphQl/AppliedMethodInput.cs index 34c58d4e..26a6d294 100644 --- a/backend/src/GraphQl/AppliedMethodInput.cs +++ b/backend/src/GraphQl/AppliedMethodInput.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -namespace Database.GraphQl -{ - public sealed record AppliedMethodInput( - Guid MethodId, - IReadOnlyList Arguments, - IReadOnlyList Sources - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record AppliedMethodInput( + Guid MethodId, + IReadOnlyList Arguments, + IReadOnlyList Sources +); \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs index 4d93156a..927ba17d 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataByIdDataLoader + : EntityByIdDataLoader { - public sealed class CalorimetricDataByIdDataLoader - : EntityByIdDataLoader + public CalorimetricDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.CalorimetricData + ) { - public CalorimetricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.CalorimetricData - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index f6b5f098..620ad408 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -1,19 +1,19 @@ +using Database.Data; using Database.GraphQl.DataX; using HotChocolate.Data.Filters; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataFilterType + : DataFilterTypeBase { - public sealed class CalorimetricDataFilterType - : DataFilterTypeBase + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Field(x => x.Id); - descriptor.Field(x => x.GValues); - descriptor.Field(x => x.UValues); - } + base.Configure(descriptor); + descriptor.Field(x => x.Id); + descriptor.Field(x => x.GValues); + descriptor.Field(x => x.UValues); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index fda8ab2b..a6a7358e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -2,59 +2,111 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.Extensions; using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; using HotChocolate.Types; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CalorimetricDataMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class CalorimetricDataMutations + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreateCalorimetricDataAsync( + CreateCalorimetricDataInput input, + // ClaimsPrincipal claimsPrincipal, + // [ScopedService] UserManager userManager, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreateCalorimetricDataAsync( - CreateCalorimetricDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - Data.ApplicationDbContext context, - [Service] AppSettings appSettings, - CancellationToken cancellationToken - ) - { - // if (!await CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) - { - return new CreateCalorimetricDataPayload( - new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); - } - var calorimetricData = new Data.CalorimetricData( - locale: input.Locale, - componentId: input.ComponentId, - name: input.Name, - description: input.Description, - warnings: input.Warnings, - creatorId: input.CreatorId, - createdAt: input.CreatedAt, - appliedMethod: new Data.AppliedMethod( - methodId: input.AppliedMethod.MethodId, - arguments: input.AppliedMethod.Arguments - .Select(a => new Data.NamedMethodArgument( - name: a.Name, + // if (!await CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( + // claimsPrincipal, + // input.CreatorId, + // userManager, + // context, + // cancellationToken + // ).ConfigureAwait(false) + // ) + if (input.AccessToken != appSettings.AccessToken) + return new CreateCalorimetricDataPayload( + new CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) + ); + var calorimetricData = new CalorimetricData( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + new AppliedMethod( + input.AppliedMethod.MethodId, + input.AppliedMethod.Arguments + .Select(a => new NamedMethodArgument( + a.Name, + // TODO Turn `a.Value` into `JsonDocument`. It comes + // as nested `IReadOnlyDictionary/-List` as said on + // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type + // Take inspiration from + // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ + // and + // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs + // This is also needed in `GetHttpsResourceMutations`. + JsonDocument.Parse(@"""TODO""") + )) + .ToList(), + input.AppliedMethod.Sources + .Select(s => new NamedMethodSource( + s.Name, + new CrossDatabaseDataReference( + s.Value.DataId, + s.Value.DataTimestamp, + s.Value.DataKind, + s.Value.DatabaseId + ) + )) + .ToList() + ), + input.Approvals.Select(a => + new DataApproval( + a.Timestamp, + a.Signature, + a.KeyFingerprint, + a.Query, + a.Response, + a.ApproverId + ) + ).ToList(), + // approval: input.Approval + input.GValues, + input.UValues + ); + var resource = new GetHttpsResource( + input.RootResource.Description, + input.RootResource.HashValue, + input.RootResource.DataFormatId, + null, + input.RootResource.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.RootResource.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.RootResource.AppliedConversionMethod.MethodId, + input.RootResource.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, // TODO Turn `a.Value` into `JsonDocument`. It comes // as nested `IReadOnlyDictionary/-List` as said on // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type @@ -63,72 +115,15 @@ CancellationToken cancellationToken // and // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - )) - .ToList(), - sources: input.AppliedMethod.Sources - .Select(s => new Data.NamedMethodSource( - name: s.Name, - value: new Data.CrossDatabaseDataReference( - dataId: s.Value.DataId, - dataTimestamp: s.Value.DataTimestamp, - dataKind: s.Value.DataKind, - databaseId: s.Value.DatabaseId - ) - )) - .ToList() - ), - approvals: input.Approvals.Select(a => - new Data.DataApproval( - timestamp: a.Timestamp, - signature: a.Signature, - keyFingerprint: a.KeyFingerprint, - query: a.Query, - response: a.Response, - approverId: a.ApproverId - ) - ).ToList(), - // approval: input.Approval - gValues: input.GValues, - uValues: input.UValues - ); - var resource = new Data.GetHttpsResource( - description: input.RootResource.Description, - hashValue: input.RootResource.HashValue, - dataFormatId: input.RootResource.DataFormatId, - parentId: null, - archivedFilesMetaInformation: input.RootResource.ArchivedFilesMetaInformation.Select(i => - new Data.FileMetaInformation( - path: i.Path, - dataFormatId: i.DataFormatId + JsonDocument.Parse(@"""TODO""") ) ).ToList(), - appliedConversionMethod: - input.RootResource.AppliedConversionMethod is null - ? null - : new Data.ToTreeVertexAppliedConversionMethod( - methodId: input.RootResource.AppliedConversionMethod.MethodId, - arguments: input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new Data.NamedMethodArgument( - name: a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - ) - ).ToList(), - sourceName: input.RootResource.AppliedConversionMethod.SourceName - ) - ); - calorimetricData.Resources.Add(resource); - context.CalorimetricData.Add(calorimetricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreateCalorimetricDataPayload(calorimetricData); - } + input.RootResource.AppliedConversionMethod.SourceName + ) + ); + calorimetricData.Resources.Add(resource); + context.CalorimetricData.Add(calorimetricData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateCalorimetricDataPayload(calorimetricData); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs index 45ce9d33..578963fa 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs @@ -1,53 +1,53 @@ using System.Collections.Generic; +using Database.Data; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public abstract class CalorimetricDataPayload + : Payload + where TCalorimetricDataError : IUserError { - public abstract class CalorimetricDataPayload - : Payload - where TCalorimetricDataError : IUserError + protected CalorimetricDataPayload( + CalorimetricData calorimetricData + ) { - public Data.CalorimetricData? CalorimetricData { get; } - public IReadOnlyCollection? Errors { get; } - - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData - ) - { - CalorimetricData = calorimetricData; - } + CalorimetricData = calorimetricData; + } - protected CalorimetricDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } + protected CalorimetricDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } - protected CalorimetricDataPayload( - TCalorimetricDataError error - ) - : this(new[] { error }) - { - } + protected CalorimetricDataPayload( + TCalorimetricDataError error + ) + : this(new[] { error }) + { + } - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData, - IReadOnlyCollection errors - ) - { - CalorimetricData = calorimetricData; - Errors = errors; - } + protected CalorimetricDataPayload( + CalorimetricData calorimetricData, + IReadOnlyCollection errors + ) + { + CalorimetricData = calorimetricData; + Errors = errors; + } - protected CalorimetricDataPayload( - Data.CalorimetricData calorimetricData, - TCalorimetricDataError error - ) - : this( - calorimetricData, - new[] { error } - ) - { - } + protected CalorimetricDataPayload( + CalorimetricData calorimetricData, + TCalorimetricDataError error + ) + : this( + calorimetricData, + new[] { error } + ) + { } -} + + public CalorimetricData? CalorimetricData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index bff93e9b..e0a5680e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -2,43 +2,43 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class CalorimetricDataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class CalorimetricDataQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public IQueryable GetAllCalorimetricData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] - public IQueryable GetAllCalorimetricData( - DateTime? timestamp, - [GraphQLType] string? locale, - Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.CalorimetricData; - } + // TODO Use `timestamp` and `locale`. + return context.CalorimetricData; + } - public Task GetCalorimetricDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType] string? locale, - CalorimetricDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + public Task GetCalorimetricDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + CalorimetricDataByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + // TODO Use `timestamp` and `locale`. + return byId.LoadAsync( + id, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs index bc3fd6b8..7d575ba7 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs @@ -1,33 +1,32 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.DataX; using HotChocolate; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataResolvers { - public sealed class CalorimetricDataResolvers + public Task GetGetHttpsResources( + [Parent] CalorimetricData calorimetricData, + GetHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) { - public Task GetGetHttpsResources( - [Parent] Data.CalorimetricData calorimetricData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(calorimetricData.Id, cancellationToken); - } + return byId.LoadAsync(calorimetricData.Id, cancellationToken); + } - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.CalorimetricData calorimetricData - ) - { - return new GetHttpsResourceTree(calorimetricData); - } + public GetHttpsResourceTree GetGetHttpsResourceTree( + [Parent] CalorimetricData calorimetricData + ) + { + return new GetHttpsResourceTree(calorimetricData); + } - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } + public DateTime GetTimestamp() + { + return DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs index a8181c73..24824537 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs @@ -1,28 +1,28 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataType + : EntityType { - public sealed class CalorimetricDataType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); - } + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field("resourceTree") + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field("timestamp") + .Type>() + .ResolveWith(t => t.GetTimestamp()); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs index ffef708a..0c2ef28a 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CreateCalorimetricDataError + : UserErrorBase { - public sealed class CreateCalorimetricDataError - : GraphQl.UserErrorBase + public CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs index 489d1bdb..e457400e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.CalorimetricDataX +namespace Database.GraphQl.CalorimetricDataX; + +public enum CreateCalorimetricDataErrorCode { - public enum CreateCalorimetricDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 5de111aa..7d09961c 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -3,23 +3,22 @@ using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.CalorimetricDataX -{ - public sealed record CreateCalorimetricDataInput( - string AccessToken, - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource, - double[] GValues, - double[] UValues - ); -} +namespace Database.GraphQl.CalorimetricDataX; + +public sealed record CreateCalorimetricDataInput( + string AccessToken, + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + IReadOnlyList Approvals, + // ResponseApproval Approval + RootGetHttpsResourceInput RootResource, + double[] GValues, + double[] UValues +); \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs index eaf6ae61..e8d0bd23 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs @@ -1,20 +1,21 @@ -namespace Database.GraphQl.CalorimetricDataX +using Database.Data; + +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CreateCalorimetricDataPayload + : CalorimetricDataPayload { - public sealed class CreateCalorimetricDataPayload - : CalorimetricDataPayload + public CreateCalorimetricDataPayload( + CalorimetricData calorimetricData + ) + : base(calorimetricData) { - public CreateCalorimetricDataPayload( - Data.CalorimetricData calorimetricData - ) - : base(calorimetricData) - { - } + } - public CreateCalorimetricDataPayload( - CreateCalorimetricDataError error - ) + public CreateCalorimetricDataPayload( + CreateCalorimetricDataError error + ) : base(error) - { - } + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CielabColorInput.cs b/backend/src/GraphQl/CielabColorInput.cs index 93bafb4c..3bca12da 100644 --- a/backend/src/GraphQl/CielabColorInput.cs +++ b/backend/src/GraphQl/CielabColorInput.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl -{ - public sealed record CielabColorInput( - double LStar, - double AStar, - double BStar - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record CielabColorInput( + double LStar, + double AStar, + double BStar +); \ No newline at end of file diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs index 2f22341e..f465106b 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs @@ -1,9 +1,8 @@ using DateTime = System.DateTime; -namespace Database.GraphQl.Common -{ - public sealed record OpenEndedDateTimeRangeInput( - DateTime? From, - DateTime? Until - ); -} \ No newline at end of file +namespace Database.GraphQl.Common; + +public sealed record OpenEndedDateTimeRangeInput( + DateTime? From, + DateTime? Until +); \ No newline at end of file diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs index 5a3835b0..9c904140 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -2,43 +2,42 @@ using NpgsqlTypes; using DateTime = System.DateTime; -namespace Database.GraphQl.Common +namespace Database.GraphQl.Common; + +public sealed class OpenEndedDateTimeRangeType + : ObjectType> { - public sealed class OpenEndedDateTimeRangeType - : ObjectType> + protected override void Configure( + IObjectTypeDescriptor> descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor> descriptor - ) - { - descriptor.BindFieldsExplicitly(); + descriptor.BindFieldsExplicitly(); - var suffixedName = nameof(OpenEndedDateTimeRangeType); - descriptor.Name(suffixedName.Remove(suffixedName.Length - "Type".Length)); + var suffixedName = nameof(OpenEndedDateTimeRangeType); + descriptor.Name(suffixedName.Remove(suffixedName.Length - "Type".Length)); - descriptor - .Field("from") - .Type() - .Resolve(context => - { - var range = context.Parent>(); - return range.LowerBoundInfinite - ? null - : range.LowerBound; - } - ); + descriptor + .Field("from") + .Type() + .Resolve(context => + { + var range = context.Parent>(); + return range.LowerBoundInfinite + ? null + : range.LowerBound; + } + ); - descriptor - .Field("until") - .Type() - .Resolve(context => - { - var range = context.Parent>(); - return range.UpperBoundInfinite - ? null - : range.UpperBound; - } - ); - } + descriptor + .Field("until") + .Type() + .Resolve(context => + { + var range = context.Parent>(); + return range.UpperBoundInfinite + ? null + : range.UpperBound; + } + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index 54ade310..b1a31832 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -3,36 +3,37 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class Connection + where TSubject : IEntity + where TAssociationsByAssociateIdDataLoader : IDataLoader { - public abstract class Connection - where TSubject : Data.IEntity - where TAssociationsByAssociateIdDataLoader : IDataLoader + private readonly Func _createEdge; + + protected Connection( + TSubject subject, + Func createEdge + ) { - protected TSubject Subject { get; } - private readonly Func _createEdge; + Subject = subject; + _createEdge = createEdge; + } - protected Connection( - TSubject subject, - Func createEdge - ) - { - Subject = subject; - _createEdge = createEdge; - } + protected TSubject Subject { get; } - public async Task> GetEdgesAsync( - TAssociationsByAssociateIdDataLoader dataLoader, - CancellationToken cancellationToken - ) - { - return ( + public async Task> GetEdgesAsync( + TAssociationsByAssociateIdDataLoader dataLoader, + CancellationToken cancellationToken + ) + { + return ( await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) - ) - .Select(_createEdge); - } + .ConfigureAwait(false) + ) + .Select(_createEdge); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 4f986eb9..1fb8451b 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -1,12 +1,11 @@ using System; using Database.Enumerations; -namespace Database.GraphQl -{ - public sealed record CrossDatabaseDataReferenceInput( - Guid DataId, - DateTime DataTimestamp, - DataKind DataKind, - Guid DatabaseId - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record CrossDatabaseDataReferenceInput( + Guid DataId, + DateTime DataTimestamp, + DataKind DataKind, + Guid DatabaseId +); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs index f8110e45..5381ec69 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovalInput.cs @@ -1,13 +1,12 @@ using System; -namespace Database.GraphQl -{ - public sealed record DataApprovalInput( - DateTime Timestamp, - string Signature, - string KeyFingerprint, - string Query, - string Response, - Guid ApproverId - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record DataApprovalInput( + DateTime Timestamp, + string Signature, + string KeyFingerprint, + string Query, + string Response, + Guid ApproverId +); \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index c722140e..d1ffeb54 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -1,15 +1,15 @@ +using Database.Data; using HotChocolate.Data.Filters; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public class DataFilterType + : DataFilterTypeBase { - public class DataFilterType - : DataFilterTypeBase + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - } + base.Configure(descriptor); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index ceb50e8b..42cf2805 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -1,58 +1,51 @@ -using System; -using System.Linq; -using Database.GraphQl.Filters; -using HotChocolate.Configuration; +using Database.Data; using HotChocolate.Data.Filters; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors.Definitions; -using static HotChocolate.Internal.FieldInitHelper; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public class DataFilterTypeBase + : FilterInputType + where TData : IData { - public class DataFilterTypeBase - : FilterInputType - where TData : Data.IData + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); - } - - // Inspired by https://github.com/ChilliCream/hotchocolate/blob/1319a4e3841beaec3bfbb282a909d2d1441f9c52/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs#L74 - // protected override FieldCollection OnCompleteFields( - // ITypeCompletionContext context, - // InputObjectTypeDefinition definition) - // { - // var fieldCollection = base.OnCompleteFields(context, definition); - // var index = 0; - // var fields = new InputField[fieldCollection.Count + 1]; - // if (definition is FilterInputTypeDefinition { UseAnd: true, UseOr: true } def) - // { - // fields[index] = new NotField(context.DescriptorContext, index, def.Scope); - // index++; - // } - // foreach (var field in fieldCollection) - // { - // fields[index] = field; - // index++; - // } - // if (fields.Length > index) - // { - // Array.Resize(ref fields, index); - // } - // return CompleteFields(context, this, fields); - // } + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } -} + + // Inspired by https://github.com/ChilliCream/hotchocolate/blob/1319a4e3841beaec3bfbb282a909d2d1441f9c52/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs#L74 + // protected override FieldCollection OnCompleteFields( + // ITypeCompletionContext context, + // InputObjectTypeDefinition definition) + // { + // var fieldCollection = base.OnCompleteFields(context, definition); + // var index = 0; + // var fields = new InputField[fieldCollection.Count + 1]; + // if (definition is FilterInputTypeDefinition { UseAnd: true, UseOr: true } def) + // { + // fields[index] = new NotField(context.DescriptorContext, index, def.Scope); + // index++; + // } + // foreach (var field in fieldCollection) + // { + // fields[index] = field; + // index++; + // } + // if (fields.Length > index) + // { + // Array.Resize(ref fields, index); + // } + // return CompleteFields(context, this, fields); + // } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index f40ecf57..c8780edf 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; @@ -12,63 +13,62 @@ using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +[ExtendObjectType(nameof(Query))] +public sealed class DataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class DataQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] // TODO Filtering does not work with unions. + [UseSorting] + public IEnumerable GetAllData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] // TODO Filtering does not work with unions. - [UseSorting] - public IEnumerable GetAllData( - DateTime? timestamp, - [GraphQLType] string? locale, - Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - // return context.CalorimetricData.AsQueryable(); - // The union below does sadly not work because the different kinds of data have different include operations. - // return context.CalorimetricData.AsQueryable() - // .Union(context.HygrothermalData.AsQueryable()) - // .Union(context.OpticalData.AsQueryable()) - // .Union(context.PhotovoltaicData.AsQueryable()); - return context.CalorimetricData.AsQueryable().AsEnumerable() - .Concat(context.HygrothermalData.AsQueryable().AsEnumerable()) - .Concat(context.OpticalData.AsQueryable().AsEnumerable()) - .Concat(context.PhotovoltaicData.AsQueryable().AsEnumerable()); - } + // TODO Use `timestamp` and `locale`. + // return context.CalorimetricData.AsQueryable(); + // The union below does sadly not work because the different kinds of data have different include operations. + // return context.CalorimetricData.AsQueryable() + // .Union(context.HygrothermalData.AsQueryable()) + // .Union(context.OpticalData.AsQueryable()) + // .Union(context.PhotovoltaicData.AsQueryable()); + return context.CalorimetricData.AsQueryable().AsEnumerable() + .Concat(context.HygrothermalData.AsQueryable().AsEnumerable()) + .Concat(context.OpticalData.AsQueryable().AsEnumerable()) + .Concat(context.PhotovoltaicData.AsQueryable().AsEnumerable()); + } - public async Task GetDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType] string? locale, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return - await calorimetricDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await hygrothermalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await opticalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - (await photovoltaicDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) as Data.IData); - } + public async Task GetDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + CalorimetricDataByIdDataLoader calorimetricDataById, + HygrothermalDataByIdDataLoader hygrothermalDataById, + OpticalDataByIdDataLoader opticalDataById, + PhotovoltaicDataByIdDataLoader photovoltaicDataById, + CancellationToken cancellationToken + ) + { + // TODO Use `timestamp` and `locale`. + return + await calorimetricDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await hygrothermalDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await opticalDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await photovoltaicDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) as IData; } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 718232c3..2d4d7a2a 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,28 +1,28 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class DataType + : InterfaceType { - public sealed class DataType - : InterfaceType + protected override void Configure( + IInterfaceTypeDescriptor descriptor + ) { - protected override void Configure( - IInterfaceTypeDescriptor descriptor - ) - { - // `1..` is a range as introduced in https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges - descriptor.Name(nameof(Data.IData)[1..]); - descriptor - .Field("uuid") - .Type>(); - descriptor - .Field("timestamp") - .Type>(); - descriptor - .Field("resourceTree") - .Type>>(); - descriptor - .Field(x => x.Locale) - .Type>(); - } + // `1..` is a range as introduced in https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges + descriptor.Name(nameof(IData)[1..]); + descriptor + .Field("uuid") + .Type>(); + descriptor + .Field("timestamp") + .Type>(); + descriptor + .Field("resourceTree") + .Type>>(); + descriptor + .Field(x => x.Locale) + .Type>(); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 034b6f60..05f207cf 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -3,40 +3,39 @@ using System.Threading; using System.Threading.Tasks; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTree { - public sealed class GetHttpsResourceTree - { - private readonly Data.DataX _data; + private readonly Data.DataX _data; - public GetHttpsResourceTree( - Data.DataX data - ) - { - _data = data; - } + public GetHttpsResourceTree( + Data.DataX data + ) + { + _data = data; + } - public async Task GetRoot( - GetHttpsResourceTreeRootByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return new GetHttpsResourceTreeRoot( - (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)).Single() - ); - } + public async Task GetRoot( + GetHttpsResourceTreeRootByDataIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return new GetHttpsResourceTreeRoot( + (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)).Single() + ); + } - public async Task> GetNonRootVertices( - GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)) - .Select(v => - new GetHttpsResourceTreeNonRootVertex(v) - ) - .ToList() - .AsReadOnly(); - } + public async Task> GetNonRootVertices( + GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)) + .Select(v => + new GetHttpsResourceTreeNonRootVertex(v) + ) + .ToList() + .AsReadOnly(); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs index a337b9a4..6615de6f 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -1,37 +1,33 @@ using System; +using Database.Data; using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.DataX -{ - public sealed class GetHttpsResourceTreeNonRootVertex +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeNonRootVertex : IGetHttpsResourceTreeVertex +{ + public GetHttpsResourceTreeNonRootVertex( + GetHttpsResource value + ) { - [GraphQLType>] - public string VertexId - { - get => Data.GetHttpsResource.ConstructVertexId(Value.Id); - } + Value = value; + AppliedConversionMethod = value.AppliedConversionMethod ?? + throw new InvalidOperationException( + "Each non-root vertex has an applied conversion method."); + } - public Data.GetHttpsResource Value { get; } + [GraphQLType>] + public string ParentId => + GetHttpsResource.ConstructVertexId( + Value.ParentId + ?? throw new InvalidOperationException("Impossible! Each non-root vertex has a parent.") + ); - [GraphQLType>] - public string ParentId - { - get => Data.GetHttpsResource.ConstructVertexId( - Value.ParentId - ?? throw new InvalidOperationException("Impossible! Each non-root vertex has a parent.") - ); - } + public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } - public Data.ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } + [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); - public GetHttpsResourceTreeNonRootVertex( - Data.GetHttpsResource value - ) - { - Value = value; - AppliedConversionMethod = value.AppliedConversionMethod ?? throw new InvalidOperationException("Each non-root vertex has an applied conversion method."); - } - } + public GetHttpsResource Value { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 228d3044..2e51f695 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -1,29 +1,32 @@ using System; using System.Linq; +using Database.Data; +using Database.GraphQl.Entities; using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader + : AssociationsByAssociateIdDataLoader { - public sealed class GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader - : Entities.AssociationsByAssociateIdDataLoader + public GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => + dbContext.GetHttpsResources.AsQueryable().Where(x => + x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? + x.HygrothermalDataId ?? x.OpticalDataId ?? + x.PhotovoltaicDataId ?? Guid.Empty) + ), + x => x.DataId + ) { - public GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => - x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? Guid.Empty) - ), - x => x.DataId - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs index 81abe03c..76ba74da 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs @@ -1,23 +1,19 @@ +using Database.Data; using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeRoot : IGetHttpsResourceTreeVertex { - public sealed class GetHttpsResourceTreeRoot : IGetHttpsResourceTreeVertex + public GetHttpsResourceTreeRoot( + GetHttpsResource value + ) { - [GraphQLType>] - public string VertexId - { - get => Data.GetHttpsResource.ConstructVertexId(Value.Id); - } + Value = value; + } - public Data.GetHttpsResource Value { get; } + [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); - public GetHttpsResourceTreeRoot( - Data.GetHttpsResource value - ) - { - Value = value; - } - } -} + public GetHttpsResource Value { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 4d68de7b..7d7e22da 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -1,29 +1,32 @@ using System; using System.Linq; +using Database.Data; +using Database.GraphQl.Entities; using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeRootByDataIdDataLoader + : AssociationsByAssociateIdDataLoader { - public sealed class GetHttpsResourceTreeRootByDataIdDataLoader - : Entities.AssociationsByAssociateIdDataLoader + public GetHttpsResourceTreeRootByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => + dbContext.GetHttpsResources.AsQueryable().Where(x => + x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? + x.HygrothermalDataId ?? x.OpticalDataId ?? + x.PhotovoltaicDataId ?? Guid.Empty) + ), + x => x.DataId + ) { - public GetHttpsResourceTreeRootByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => - x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? Guid.Empty) - ), - x => x.DataId - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index 3c58ff11..d7c9dbc4 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -1,29 +1,31 @@ using System; using System.Linq; +using Database.Data; +using Database.GraphQl.Entities; using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourcesByDataIdDataLoader + : AssociationsByAssociateIdDataLoader { - public sealed class GetHttpsResourcesByDataIdDataLoader - : Entities.AssociationsByAssociateIdDataLoader + public GetHttpsResourcesByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => + dbContext.GetHttpsResources.AsQueryable().Where(x => + ids.Contains(x.CalorimetricDataId ?? + x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? Guid.Empty) + ), + x => x.DataId + ) { - public GetHttpsResourcesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => - ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? Guid.Empty) - ), - x => x.DataId - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs index 21985d52..e1f177c6 100644 --- a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs +++ b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs @@ -1,14 +1,13 @@ +using Database.Data; using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.DataX +namespace Database.GraphQl.DataX; + +[InterfaceType("GetHttpsResourceTreeVertex")] +public interface IGetHttpsResourceTreeVertex { - [InterfaceType("GetHttpsResourceTreeVertex")] - public interface IGetHttpsResourceTreeVertex - { - [GraphQLType] - string VertexId { get; } + [GraphQLType] string VertexId { get; } - Data.GetHttpsResource Value { get; } - } -} + GetHttpsResource Value { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs index 34c9e548..71459584 100644 --- a/backend/src/GraphQl/Databases/Database.cs +++ b/backend/src/GraphQl/Databases/Database.cs @@ -1,38 +1,39 @@ using System; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public sealed class Database { - public sealed class Database + public Database( + Guid uuid, + string name, + string description, + Uri locator, + DatabaseVerificationState verificationState, + string verificationCode, + bool canCurrentUserUpdateNode, + bool canCurrentUserVerifyNode + ) { - public Guid Uuid { get; } - public string Name { get; } - public string Description { get; } - public Uri Locator { get; } - public DatabaseVerificationState VerificationState { get; } - public string VerificationCode { get; } - // public Institution? Operator { get; set; } - public bool CanCurrentUserUpdateNode { get; } - public bool CanCurrentUserVerifyNode { get; } - - public Database( - Guid uuid, - string name, - string description, - Uri locator, - DatabaseVerificationState verificationState, - string verificationCode, - bool canCurrentUserUpdateNode, - bool canCurrentUserVerifyNode - ) - { - Uuid = uuid; - Name = name; - Description = description; - Locator = locator; - VerificationState = verificationState; - VerificationCode = verificationCode; - CanCurrentUserUpdateNode = canCurrentUserUpdateNode; - CanCurrentUserVerifyNode = canCurrentUserVerifyNode; - } + Uuid = uuid; + Name = name; + Description = description; + Locator = locator; + VerificationState = verificationState; + VerificationCode = verificationCode; + CanCurrentUserUpdateNode = canCurrentUserUpdateNode; + CanCurrentUserVerifyNode = canCurrentUserVerifyNode; } + + public Guid Uuid { get; } + public string Name { get; } + public string Description { get; } + public Uri Locator { get; } + public DatabaseVerificationState VerificationState { get; } + + public string VerificationCode { get; } + + // public Institution? Operator { get; set; } + public bool CanCurrentUserUpdateNode { get; } + public bool CanCurrentUserVerifyNode { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 0b35b505..bb20bfb7 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -2,52 +2,56 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.Metabase; +using GraphQL; using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DatabaseMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class DatabaseMutations + private static readonly string[] _updateDatabaseFileNames = + { + "UpdateDatabase.graphql" + }; + + public async Task UpdateDatabaseAsync( + UpdateDatabaseInput input, + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) { - public async Task UpdateDatabaseAsync( - UpdateDatabaseInput input, - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - return (await Metabase.QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQL.GraphQLRequest( - query: await Metabase.QueryingMetabase.ConstructGraphQlQuery( - new[] { - "UpdateDatabase.graphql" - } - ).ConfigureAwait(false), - variables: new - { - input, - }, - operationName: "UpdateDatabase" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false))?.Data - ?? new UpdateDatabasePayload( - null, - new [] - { - new UpdateDatabaseError( - UpdateDatabaseErrorCode.UNKNOWN, - "Unknown error.", - Array.Empty() - ) - } - ); - } + return (await QueryingMetabase.QueryGraphQl( + appSettings, + new GraphQLRequest( + await QueryingMetabase.ConstructGraphQlQuery( + _updateDatabaseFileNames + ).ConfigureAwait(false), + new + { + input + }, + "UpdateDatabase" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false))?.Data + ?? new UpdateDatabasePayload( + null, + new[] + { + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + Array.Empty() + ) + } + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index 3771c530..14f27274 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -1,21 +1,20 @@ using System.Collections.Generic; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public abstract class DatabasePayload + : Payload + where TDatabaseError : IUserError { - public abstract class DatabasePayload - : Payload - where TDatabaseError : IUserError + protected DatabasePayload( + Database? database, + IReadOnlyCollection? errors + ) { - public Database? Database { get; } - public IReadOnlyCollection? Errors { get; } - - protected DatabasePayload( - Database? database, - IReadOnlyCollection? errors - ) - { - Database = database; - Errors = errors; - } + Database = database; + Errors = errors; } + + public Database? Database { get; } + public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 4b25eb84..22b28279 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -4,122 +4,120 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Metabase; +using GraphQL; using HotChocolate; using HotChocolate.Execution; using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +[ExtendObjectType(nameof(Query))] +public sealed class DatabaseQueries { - [ExtendObjectType(nameof(Query))] - public sealed class DatabaseQueries + private static readonly string[] _databaseFileNames = { - private sealed class DatabasesData - { - public DatabasesConnection Databases { get; set; } = default!; - } + "Databases.graphql" + }; - public async Task GetDatabaseAsync( - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, - IResolverContext resolverContext, - CancellationToken cancellationToken - ) + public async Task GetDatabaseAsync( + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + try { - try - { - var response = - await Metabase.QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQL.GraphQLRequest( - query: await Metabase.QueryingMetabase.ConstructGraphQlQuery( - new[] { - "Databases.graphql" - } - ).ConfigureAwait(false), - variables: new + var response = + await QueryingMetabase.QueryGraphQl( + appSettings, + new GraphQLRequest( + await QueryingMetabase.ConstructGraphQlQuery( + _databaseFileNames + ).ConfigureAwait(false), + new + { + where = new { - where = new + locator = new { - locator = new - { - // TODO This is error-prone. - eq = new Uri(new Uri(appSettings.Host), "/graphql/") - } + // TODO This is error-prone. + eq = new Uri(new Uri(appSettings.Host), "/graphql/") } - }, - operationName: "Databases" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - if (response is null) - { - throw new QueryException( - ErrorBuilder.New() + } + }, + "Databases" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + if (response is null) + throw new QueryException( + ErrorBuilder.New() .SetCode("NULL_RESPONSE") .SetPath(resolverContext.Path) .SetMessage("Response is null.") .Build() - ); - } - if (response.Data.Databases.Nodes is null) - { - throw new QueryException( - ErrorBuilder.New() + ); + if (response.Data.Databases.Nodes is null) + throw new QueryException( + ErrorBuilder.New() .SetCode("NULL_NODES") .SetPath(resolverContext.Path) .SetMessage("The supposed list of databases is null.") .Build() - ); - } - if (response.Data.Databases.Nodes.Count == 0) - { - throw new QueryException( - ErrorBuilder.New() + ); + if (response.Data.Databases.Nodes.Count == 0) + throw new QueryException( + ErrorBuilder.New() .SetCode("NO_DATABASE") .SetPath(resolverContext.Path) .SetMessage("The list of databases is empty.") .Build() - ); - } - if (response.Data.Databases.Nodes.Count >= 2) - { - throw new QueryException( - ErrorBuilder.New() + ); + if (response.Data.Databases.Nodes.Count >= 2) + throw new QueryException( + ErrorBuilder.New() .SetCode("AMBIGUOUS_DATABASE") .SetPath(resolverContext.Path) .SetMessage("The list of databases has more than one entry.") .Build() - ); - } - return response.Data.Databases.Nodes[0]; - } - catch (HttpRequestException e) - { - throw new QueryException( - ErrorBuilder.New() + ); + return response.Data.Databases.Nodes[0]; + } + catch (HttpRequestException e) + { + throw new QueryException( + ErrorBuilder.New() .SetCode("METABASE_REQUEST_FAILED") .SetPath(resolverContext.Path) .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") .SetException(e) .Build() - ); - } - catch (JsonException e) - { - throw new QueryException( - ErrorBuilder.New() + ); + } + catch (JsonException e) + { + throw new QueryException( + ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()).ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage($"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") .SetException(e) .Build() - ); - } + ); } } -} + + private sealed class DatabasesData + { + public DatabasesConnection Databases { get; } = default!; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseVerificationState.cs b/backend/src/GraphQl/Databases/DatabaseVerificationState.cs index e46eeb3b..7e621902 100644 --- a/backend/src/GraphQl/Databases/DatabaseVerificationState.cs +++ b/backend/src/GraphQl/Databases/DatabaseVerificationState.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public enum DatabaseVerificationState { - public enum DatabaseVerificationState - { - PENDING, - VERIFIED, - } -} + PENDING, + VERIFIED +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasesConnection.cs b/backend/src/GraphQl/Databases/DatabasesConnection.cs index 47770f03..3e487b91 100644 --- a/backend/src/GraphQl/Databases/DatabasesConnection.cs +++ b/backend/src/GraphQl/Databases/DatabasesConnection.cs @@ -1,19 +1,19 @@ using System.Collections.Generic; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public sealed class DatabasesConnection { - public sealed class DatabasesConnection - { - // edges: [DatabasesEdge!] - public IReadOnlyList? Nodes { get; } - // pageInfo: PageInfo! - // totalCount: Int! + // pageInfo: PageInfo! + // totalCount: Int! - public DatabasesConnection( - IReadOnlyList? nodes - ) - { - Nodes = nodes; - } + public DatabasesConnection( + IReadOnlyList? nodes + ) + { + Nodes = nodes; } + + // edges: [DatabasesEdge!] + public IReadOnlyList? Nodes { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs index 2d7b99d4..431fec23 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public sealed class UpdateDatabaseError + : UserErrorBase { - public sealed class UpdateDatabaseError - : GraphQl.UserErrorBase + public UpdateDatabaseError( + UpdateDatabaseErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public UpdateDatabaseError( - UpdateDatabaseErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs b/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs index eeea44e5..84756185 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs @@ -1,9 +1,11 @@ -namespace Database.GraphQl.Databases +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.Databases; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateDatabaseErrorCode { - public enum UpdateDatabaseErrorCode - { - UNKNOWN, - UNAUTHORIZED, - UNKNOWN_DATABASE - } + UNKNOWN, + UNAUTHORIZED, + UNKNOWN_DATABASE } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs index 246041a1..00310ce7 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs @@ -1,11 +1,10 @@ using System; -namespace Database.GraphQl.Databases -{ - public sealed record UpdateDatabaseInput( - Guid DatabaseId, - string Name, - string Description, - Uri Locator - ); -} \ No newline at end of file +namespace Database.GraphQl.Databases; + +public sealed record UpdateDatabaseInput( + Guid DatabaseId, + string Name, + string Description, + Uri Locator +); \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index 63b62dc7..4463e551 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -1,16 +1,15 @@ using System.Collections.Generic; -namespace Database.GraphQl.Databases +namespace Database.GraphQl.Databases; + +public sealed class UpdateDatabasePayload + : DatabasePayload { - public sealed class UpdateDatabasePayload - : DatabasePayload + public UpdateDatabasePayload( + Database? database, + IReadOnlyCollection? errors + ) + : base(database, errors) { - public UpdateDatabasePayload( - Database? database, - IReadOnlyCollection? errors - ) - : base(database, errors) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Edge.cs b/backend/src/GraphQl/Edge.cs index a7622ba5..33acf61b 100644 --- a/backend/src/GraphQl/Edge.cs +++ b/backend/src/GraphQl/Edge.cs @@ -2,28 +2,26 @@ using System.Threading; using System.Threading.Tasks; using GreenDonut; -using HotChocolate; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class Edge + where TNodeByIdDataLoader : IDataLoader { - public abstract class Edge - where TNodeByIdDataLoader : IDataLoader - { - private readonly Guid _nodeId; + private readonly Guid _nodeId; - protected Edge( - Guid nodeId - ) - { - _nodeId = nodeId; - } + protected Edge( + Guid nodeId + ) + { + _nodeId = nodeId; + } - public async Task GetNodeAsync( - TNodeByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return (await byId.LoadAsync(_nodeId, cancellationToken).ConfigureAwait(false))!; - } + public async Task GetNodeAsync( + TNodeByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return (await byId.LoadAsync(_nodeId, cancellationToken).ConfigureAwait(false))!; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs index 11e3a79c..74938d74 100644 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs @@ -3,46 +3,46 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; using Microsoft.EntityFrameworkCore; using Guid = System.Guid; -namespace Database.GraphQl.Entities +namespace Database.GraphQl.Entities; + +public abstract class AssociationsByAssociateIdDataLoader + : GroupedDataLoader { - public abstract class AssociationsByAssociateIdDataLoader - : GroupedDataLoader - { - private readonly IDbContextFactory _dbContextFactory; - private readonly Func, IQueryable> _getAssociations; - private readonly Func _getAssociateId; + private readonly IDbContextFactory _dbContextFactory; + private readonly Func _getAssociateId; + private readonly Func, IQueryable> _getAssociations; - protected AssociationsByAssociateIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func, IQueryable> getAssociations, - Func getAssociateId - ) - : base(batchScheduler, options) - { - _dbContextFactory = dbContextFactory; - _getAssociations = getAssociations; - _getAssociateId = getAssociateId; - } + protected AssociationsByAssociateIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory, + Func, IQueryable> getAssociations, + Func getAssociateId + ) + : base(batchScheduler, options) + { + _dbContextFactory = dbContextFactory; + _getAssociations = getAssociations; + _getAssociateId = getAssociateId; + } - protected override async Task> LoadGroupedBatchAsync( - IReadOnlyList ids, - CancellationToken cancellationToken + protected override async Task> LoadGroupedBatchAsync( + IReadOnlyList keys, + CancellationToken cancellationToken + ) + { + await using var dbContext = + _dbContextFactory.CreateDbContext(); + return ( + await _getAssociations(dbContext, keys) + .ToListAsync(cancellationToken) + .ConfigureAwait(false) ) - { - await using var dbContext = - _dbContextFactory.CreateDbContext(); - return ( - await _getAssociations(dbContext, ids) - .ToListAsync(cancellationToken) - .ConfigureAwait(false) - ) - .ToLookup(_getAssociateId); - } + .ToLookup(_getAssociateId); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs index 7a1cbdfb..b9726e4f 100644 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs @@ -3,46 +3,46 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; using Microsoft.EntityFrameworkCore; using Guid = System.Guid; -namespace Database.GraphQl.Entities +namespace Database.GraphQl.Entities; + +public abstract class EntityByIdDataLoader + : BatchDataLoader + where TEntity : class, IEntity { - public abstract class EntityByIdDataLoader - : BatchDataLoader - where TEntity : class, Data.IEntity - { - private readonly IDbContextFactory _dbContextFactory; - private readonly Func> _getQueryable; + private readonly IDbContextFactory _dbContextFactory; + private readonly Func> _getQueryable; - protected EntityByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func> getQueryable - ) - : base(batchScheduler, options) - { - _dbContextFactory = dbContextFactory; - _getQueryable = getQueryable; - } + protected EntityByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory, + Func> getQueryable + ) + : base(batchScheduler, options) + { + _dbContextFactory = dbContextFactory; + _getQueryable = getQueryable; + } - protected override async Task> LoadBatchAsync( - IReadOnlyList ids, - CancellationToken cancellationToken + protected override async Task> LoadBatchAsync( + IReadOnlyList keys, + CancellationToken cancellationToken + ) + { + await using var dbContext = + _dbContextFactory.CreateDbContext(); + return await _getQueryable(dbContext).AsQueryable() + .Where(entity => keys.Contains(entity.Id)) + .ToDictionaryAsync( + entity => entity.Id, + entity => (TEntity?)entity, + cancellationToken ) - { - await using var dbContext = - _dbContextFactory.CreateDbContext(); - return await _getQueryable(dbContext).AsQueryable() - .Where(entity => ids.Contains(entity.Id)) - .ToDictionaryAsync( - entity => entity.Id, - entity => (TEntity?)entity, - cancellationToken - ) - .ConfigureAwait(false); - } + .ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index f41ceed7..13a5eb89 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -1,39 +1,39 @@ using System; +using Database.Data; using GreenDonut; -using HotChocolate.Resolvers; using HotChocolate.Types; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class EntityType + : ObjectType + where TEntity : IEntity + where TEntityByIdDataLoader : IDataLoader { - public abstract class EntityType - : ObjectType - where TEntity : Data.IEntity - where TEntityByIdDataLoader : IDataLoader + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - descriptor - .ImplementsNode() - .IdField(t => t.Id) - .ResolveNode((context, id) => + descriptor + .ImplementsNode() + .IdField(t => t.Id) + .ResolveNode((context, id) => context - .DataLoader() - .LoadAsync(id, context.RequestAborted)! // Notice the null-forgiving operator `!`. It's bad that we need to use it here. - ); - descriptor - .Field("uuid") - .Type>() - .Resolve(context => - context.Parent().Id - ); - // TODO Do we want to expose this, require it as input, and use it to discover concurrent writes? - descriptor - .Field(t => t.Version) - .Type>() - .Name("version") - .Ignore(); - } + .DataLoader() + .LoadAsync(id, context.RequestAborted) + ! // Notice the null-forgiving operator `!`. It's bad that we need to use it here. + ); + descriptor + .Field("uuid") + .Type>() + .Resolve(context => + context.Parent().Id + ); + // TODO Do we want to expose this, require it as input, and use it to discover concurrent writes? + descriptor + .Field(t => t.Version) + .Type>() + .Name("version") + .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/FileMetaInformationInput.cs b/backend/src/GraphQl/FileMetaInformationInput.cs index 80633e80..0479cf76 100644 --- a/backend/src/GraphQl/FileMetaInformationInput.cs +++ b/backend/src/GraphQl/FileMetaInformationInput.cs @@ -1,9 +1,8 @@ using System; -namespace Database.GraphQl -{ - public sealed record FileMetaInformationInput( - string[] Path, - Guid DataFormatId - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record FileMetaInformationInput( + string[] Path, + Guid DataFormatId +); \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs b/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs index 9217176f..c93ef708 100644 --- a/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs +++ b/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs @@ -1,8 +1,8 @@ // Inspired by `HotChocolate.Data.Filters.DefaultFilterOperations`. -namespace Database.GraphQl.Filters + +namespace Database.GraphQl.Filters; + +public static class AdditionalFilterOperations { - public static class AdditionalFilterOperations - { - public const int Not = 100; - } + public const int Not = 100; } \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/INotField.cs b/backend/src/GraphQl/Filters/INotField.cs index c0e48a4b..3e2b98ac 100644 --- a/backend/src/GraphQl/Filters/INotField.cs +++ b/backend/src/GraphQl/Filters/INotField.cs @@ -1,12 +1,11 @@ using HotChocolate.Data.Filters; using HotChocolate.Types; -namespace Database.GraphQl.Filters -{ - public interface INotField - : IInputField +namespace Database.GraphQl.Filters; + +public interface INotField + : IInputField , IHasRuntimeType - { - new IFilterInputType DeclaringType { get; } - } +{ + new IFilterInputType DeclaringType { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/NotField.cs b/backend/src/GraphQl/Filters/NotField.cs index 1a8437cc..928fed99 100644 --- a/backend/src/GraphQl/Filters/NotField.cs +++ b/backend/src/GraphQl/Filters/NotField.cs @@ -4,39 +4,40 @@ using HotChocolate.Types.Descriptors; using HotChocolate.Types.Descriptors.Definitions; -namespace Database.GraphQl.Filters -{ - public sealed class NotField - : InputField +namespace Database.GraphQl.Filters; + +public sealed class NotField + : InputField , INotField +{ + internal NotField(IDescriptorContext context, int index, string? scope) + : base(CreateDefinition(context, scope), index) { - internal NotField(IDescriptorContext context, int index, string? scope) - : base(CreateDefinition(context, scope), index) - { - } + } - public new FilterInputType DeclaringType => (FilterInputType)base.DeclaringType; + public new FilterInputType DeclaringType => (FilterInputType)base.DeclaringType; - IFilterInputType INotField.DeclaringType => DeclaringType; + IFilterInputType INotField.DeclaringType => DeclaringType; - protected override void OnCompleteField( - ITypeCompletionContext context, - ITypeSystemMember declaringMember, - InputFieldDefinition definition) - { - definition.Type = TypeReference.Parse( - $"[{context.Type.Name}!]", - TypeContext.Input, - context.Type.Scope); + protected override void OnCompleteField( + ITypeCompletionContext context, + ITypeSystemMember declaringMember, + InputFieldDefinition definition) + { + definition.Type = TypeReference.Parse( + $"[{context.Type.Name}!]", + TypeContext.Input, + context.Type.Scope); - base.OnCompleteField(context, declaringMember, definition); - } + base.OnCompleteField(context, declaringMember, definition); + } - private static InputFieldDefinition CreateDefinition( - IDescriptorContext context, - string? scope) => - FilterOperationFieldDescriptor - .New(context, AdditionalFilterOperations.Not, scope) - .CreateDefinition(); + private static FilterOperationFieldDefinition CreateDefinition( + IDescriptorContext context, + string? scope) + { + return FilterOperationFieldDescriptor + .New(context, AdditionalFilterOperations.Not, scope) + .CreateDefinition(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index c787d2fc..1fd9d149 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -3,53 +3,54 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; -using HotChocolate; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class ForkingConnection + where TSubject : IEntity + where TSomeAssociationsByAssociateIdDataLoader : IDataLoader + where TOtherAssociationsByAssociateIdDataLoader : IDataLoader { - public abstract class ForkingConnection - where TSubject : Data.IEntity - where TSomeAssociationsByAssociateIdDataLoader : IDataLoader - where TOtherAssociationsByAssociateIdDataLoader : IDataLoader + private readonly Func _createEdge; + private readonly bool _useFirstDataLoader; + + protected ForkingConnection( + TSubject subject, + bool useFirstDataLoader, + Func createEdge + ) { - protected TSubject Subject { get; } - private readonly bool _useFirstDataLoader; - private readonly Func _createEdge; + Subject = subject; + _useFirstDataLoader = useFirstDataLoader; + _createEdge = createEdge; + } - protected ForkingConnection( - TSubject subject, - bool useFirstDataLoader, - Func createEdge - ) - { - Subject = subject; - _useFirstDataLoader = useFirstDataLoader; - _createEdge = createEdge; - } + protected TSubject Subject { get; } - public Task> GetEdgesAsync( - TSomeAssociationsByAssociateIdDataLoader someDataLoader, - TOtherAssociationsByAssociateIdDataLoader otherDataLoader, - CancellationToken cancellationToken - ) - { - return _useFirstDataLoader - ? GetEdgesAsync(someDataLoader, cancellationToken) - : GetEdgesAsync(otherDataLoader, cancellationToken); - } + public Task> GetEdgesAsync( + TSomeAssociationsByAssociateIdDataLoader someDataLoader, + TOtherAssociationsByAssociateIdDataLoader otherDataLoader, + CancellationToken cancellationToken + ) + { + return _useFirstDataLoader + ? GetEdgesAsync(someDataLoader, cancellationToken) + : GetEdgesAsync(otherDataLoader, cancellationToken); + } - private async Task> GetEdgesAsync( - TDataLoader dataLoader, - CancellationToken cancellationToken - ) - where TDataLoader : IDataLoader - { - return ( + private async Task> GetEdgesAsync( + TDataLoader dataLoader, + CancellationToken cancellationToken + ) + where TDataLoader : IDataLoader + { + return ( await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) - ) - .Select(_createEdge); - } + .ConfigureAwait(false) + ) + .Select(_createEdge); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs index 47ff5f6b..5252c6a7 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class CreateGetHttpsResourceError + : UserErrorBase { - public sealed class CreateGetHttpsResourceError - : GraphQl.UserErrorBase + public CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs index 266a92e6..85d591c0 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public enum CreateGetHttpsResourceErrorCode { - public enum CreateGetHttpsResourceErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs index 96b2b166..fb3d62bf 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; +using Database.Enumerations; -namespace Database.GraphQl.GetHttpsResources -{ - public sealed record CreateGetHttpsResourceInput( - string AccessToken, - string Description, - string HashValue, - Guid DataFormatId, - Guid DataId, - Enumerations.DataKind DataKind, - Guid? ParentId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod - ); -} +namespace Database.GraphQl.GetHttpsResources; + +public sealed record CreateGetHttpsResourceInput( + string AccessToken, + string Description, + string HashValue, + Guid DataFormatId, + Guid DataId, + DataKind DataKind, + Guid? ParentId, + IReadOnlyList ArchivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod +); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs index 503c5d2d..6eaca41a 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs @@ -1,20 +1,21 @@ -namespace Database.GraphQl.GetHttpsResources +using Database.Data; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class CreateGetHttpsResourcePayload + : GetHttpsResourcePayload { - public sealed class CreateGetHttpsResourcePayload - : GetHttpsResourcePayload + public CreateGetHttpsResourcePayload( + GetHttpsResource getHttpsResource + ) + : base(getHttpsResource) { - public CreateGetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource - ) - : base(getHttpsResource) - { - } + } - public CreateGetHttpsResourcePayload( - CreateGetHttpsResourceError error - ) + public CreateGetHttpsResourcePayload( + CreateGetHttpsResourceError error + ) : base(error) - { - } + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs index 44e93181..80f97933 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceByIdDataLoader + : EntityByIdDataLoader { - public sealed class GetHttpsResourceByIdDataLoader - : EntityByIdDataLoader + public GetHttpsResourceByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.GetHttpsResources + ) { - public GetHttpsResourceByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GetHttpsResources - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs index f3373046..7d324df0 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs @@ -1,29 +1,30 @@ using System; using System.Linq; +using Database.Data; +using Database.GraphQl.Entities; using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader + : AssociationsByAssociateIdDataLoader { - public sealed class GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader - : Entities.AssociationsByAssociateIdDataLoader + public GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => + dbContext.GetHttpsResources.AsQueryable().Where(x => + ids.Contains(x.ParentId ?? Guid.Empty) + ), + x => x.Id + ) { - public GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => - ids.Contains(x.ParentId ?? Guid.Empty) - ), - x => x.Id - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 229bd3e2..fa884dff 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,24 +1,24 @@ +using Database.Data; using HotChocolate.Data.Filters; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceFilterType + : FilterInputType { - public sealed class GetHttpsResourceFilterType - : FilterInputType + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Id); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.HashValue); - descriptor.Field(x => x.DataFormatId); - descriptor.Field(x => x.AppliedConversionMethod); - descriptor.Field(x => x.ArchivedFilesMetaInformation); - descriptor.Field(x => x.DataId); - descriptor.Field(x => x.ParentId); - descriptor.Field(x => x.Parent); - } + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Id); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.HashValue); + descriptor.Field(x => x.DataFormatId); + descriptor.Field(x => x.AppliedConversionMethod); + descriptor.Field(x => x.ArchivedFilesMetaInformation); + descriptor.Field(x => x.DataId); + descriptor.Field(x => x.ParentId); + descriptor.Field(x => x.Parent); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index d708c820..c3bc7039 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -2,86 +2,82 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Data; +using Database.Enumerations; using Database.Extensions; using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; using HotChocolate.Types; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +[ExtendObjectType(nameof(Mutation))] +public sealed class GetHttpsResourceMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class GetHttpsResourceMutations + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreateGetHttpsResourceAsync( + CreateGetHttpsResourceInput input, + // ClaimsPrincipal claimsPrincipal, + // [ScopedService] UserManager userManager, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreateGetHttpsResourceAsync( - CreateGetHttpsResourceInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - Data.ApplicationDbContext context, - [Service] AppSettings appSettings, - CancellationToken cancellationToken - ) - { - // if (!await GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) - { - return new CreateGetHttpsResourcePayload( - new CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); - } - var getHttpsResource = new Data.GetHttpsResource( - description: input.Description, - hashValue: input.HashValue, - dataFormatId: input.DataFormatId, - calorimetricDataId: input.DataKind == Enumerations.DataKind.CALORIMETRIC_DATA ? input.DataId : null, - hygrothermalDataId: input.DataKind == Enumerations.DataKind.HYGROTHERMAL_DATA ? input.DataId : null, - opticalDataId: input.DataKind == Enumerations.DataKind.OPTICAL_DATA ? input.DataId : null, - photovoltaicDataId: input.DataKind == Enumerations.DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, - parentId: input.ParentId, - archivedFilesMetaInformation: input.ArchivedFilesMetaInformation.Select(i => - new Data.FileMetaInformation( - path: i.Path, - dataFormatId: i.DataFormatId - ) - ).ToList(), - appliedConversionMethod: - input.AppliedConversionMethod is null - ? null - : new Data.ToTreeVertexAppliedConversionMethod( - methodId: input.AppliedConversionMethod.MethodId, - arguments: input.AppliedConversionMethod.Arguments.Select(a => - new Data.NamedMethodArgument( - name: a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `OpticalDataMutations`. - value: JsonDocument.Parse(@"""TODO""") - ) - ).ToList(), - sourceName: input.AppliedConversionMethod.SourceName - ) + // if (!await GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( + // claimsPrincipal, + // input.CreatorId, + // userManager, + // context, + // cancellationToken + // ).ConfigureAwait(false) + // ) + if (input.AccessToken != appSettings.AccessToken) + return new CreateGetHttpsResourcePayload( + new CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) ); - context.GetHttpsResources.Add(getHttpsResource); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreateGetHttpsResourcePayload(getHttpsResource); - } + var getHttpsResource = new GetHttpsResource( + input.Description, + input.HashValue, + input.DataFormatId, + input.DataKind == DataKind.CALORIMETRIC_DATA ? input.DataId : null, + input.DataKind == DataKind.HYGROTHERMAL_DATA ? input.DataId : null, + input.DataKind == DataKind.OPTICAL_DATA ? input.DataId : null, + input.DataKind == DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, + input.ParentId, + input.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.AppliedConversionMethod.MethodId, + input.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, + // TODO Turn `a.Value` into `JsonDocument`. It comes + // as nested `IReadOnlyDictionary/-List` as said on + // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type + // Take inspiration from + // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ + // and + // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs + // This is also needed in `OpticalDataMutations`. + JsonDocument.Parse(@"""TODO""") + ) + ).ToList(), + input.AppliedConversionMethod.SourceName + ) + ); + context.GetHttpsResources.Add(getHttpsResource); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateGetHttpsResourcePayload(getHttpsResource); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs index 9b78c03c..97fecfb4 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs @@ -1,53 +1,53 @@ using System.Collections.Generic; +using Database.Data; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public abstract class GetHttpsResourcePayload + : Payload + where TGetHttpsResourceError : IUserError { - public abstract class GetHttpsResourcePayload - : Payload - where TGetHttpsResourceError : IUserError + protected GetHttpsResourcePayload( + GetHttpsResource getHttpsResource + ) { - public Data.GetHttpsResource? GetHttpsResource { get; } - public IReadOnlyCollection? Errors { get; } - - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource - ) - { - GetHttpsResource = getHttpsResource; - } + GetHttpsResource = getHttpsResource; + } - protected GetHttpsResourcePayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } + protected GetHttpsResourcePayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } - protected GetHttpsResourcePayload( - TGetHttpsResourceError error - ) - : this(new[] { error }) - { - } + protected GetHttpsResourcePayload( + TGetHttpsResourceError error + ) + : this(new[] { error }) + { + } - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource, - IReadOnlyCollection errors - ) - { - GetHttpsResource = getHttpsResource; - Errors = errors; - } + protected GetHttpsResourcePayload( + GetHttpsResource getHttpsResource, + IReadOnlyCollection errors + ) + { + GetHttpsResource = getHttpsResource; + Errors = errors; + } - protected GetHttpsResourcePayload( - Data.GetHttpsResource getHttpsResource, - TGetHttpsResourceError error - ) - : this( - getHttpsResource, - new[] { error } - ) - { - } + protected GetHttpsResourcePayload( + GetHttpsResource getHttpsResource, + TGetHttpsResourceError error + ) + : this( + getHttpsResource, + new[] { error } + ) + { } -} + + public GetHttpsResource? GetHttpsResource { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index aadcb61b..629824d9 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -1,37 +1,36 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate; +using Database.Data; using HotChocolate.Data; using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +[ExtendObjectType(nameof(Query))] +public sealed class GetHttpsResourceQueries { - [ExtendObjectType(nameof(Query))] - public sealed class GetHttpsResourceQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public IQueryable GetGetHttpsResources( + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] - public IQueryable GetGetHttpsResources( - Data.ApplicationDbContext context - ) - { - return context.GetHttpsResources; - } + return context.GetHttpsResources; + } - public Task GetGetHttpsResourceAsync( - Guid id, - GetHttpsResourceByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return byId.LoadAsync( - id, - cancellationToken - ); - } + public Task GetGetHttpsResourceAsync( + Guid id, + GetHttpsResourceByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync( + id, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 5c8ae47d..31bdf065 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,86 +1,75 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; using HotChocolate; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceResolvers { - public sealed class GetHttpsResourceResolvers + public async Task GetData( + [Parent] GetHttpsResource getHttpsResource, + CalorimetricDataByIdDataLoader calorimetricDataById, + HygrothermalDataByIdDataLoader hygrothermalDataById, + OpticalDataByIdDataLoader opticalDataById, + PhotovoltaicDataByIdDataLoader photovoltaicDataById, + CancellationToken cancellationToken + ) { - public async Task GetData( - [Parent] Data.GetHttpsResource getHttpsResource, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - CancellationToken cancellationToken - - ) - { - if (getHttpsResource.CalorimetricDataId is not null) - { - return await calorimetricDataById.LoadAsync( - getHttpsResource.CalorimetricDataId ?? throw new Exception("Impossible!"), - cancellationToken - ).ConfigureAwait(false); - } - if (getHttpsResource.HygrothermalDataId is not null) - { - return await hygrothermalDataById.LoadAsync( - getHttpsResource.HygrothermalDataId ?? throw new Exception("Impossible!"), - cancellationToken - ).ConfigureAwait(false); - } - if (getHttpsResource.OpticalDataId is not null) - { - return await opticalDataById.LoadAsync( - getHttpsResource.OpticalDataId ?? throw new Exception("Impossible!"), - cancellationToken - ).ConfigureAwait(false); - } - if (getHttpsResource.PhotovoltaicDataId is not null) - { - return await photovoltaicDataById.LoadAsync( - getHttpsResource.PhotovoltaicDataId ?? throw new Exception("Impossible!"), - cancellationToken - ).ConfigureAwait(false); - } - return null; - } - - public Uri GetLocator( - [Parent] Data.GetHttpsResource getHttpsResource, - [Service] AppSettings appSettings - ) - { - return new Uri($"{appSettings.Host}/api/resources/{getHttpsResource.Id}"); - } - - public async Task GetParent( - [Parent] Data.GetHttpsResource getHttpsResource, - GetHttpsResourceByIdDataLoader byId, - CancellationToken cancellationToken + if (getHttpsResource.CalorimetricDataId is not null) + return await calorimetricDataById.LoadAsync( + getHttpsResource.CalorimetricDataId ?? throw new ArgumentException("Impossible!"), + cancellationToken + ).ConfigureAwait(false); + if (getHttpsResource.HygrothermalDataId is not null) + return await hygrothermalDataById.LoadAsync( + getHttpsResource.HygrothermalDataId ?? throw new ArgumentException("Impossible!"), + cancellationToken + ).ConfigureAwait(false); + if (getHttpsResource.OpticalDataId is not null) + return await opticalDataById.LoadAsync( + getHttpsResource.OpticalDataId ?? throw new ArgumentException("Impossible!"), + cancellationToken + ).ConfigureAwait(false); + if (getHttpsResource.PhotovoltaicDataId is not null) + return await photovoltaicDataById.LoadAsync( + getHttpsResource.PhotovoltaicDataId ?? throw new ArgumentException("Impossible!"), + cancellationToken + ).ConfigureAwait(false); + return null; + } - ) - { - // TODO Why is `?? Guid.Empty` below necessary although `getHttpsResource.ParentId` is not null? - return getHttpsResource.ParentId is null - ? null - : await byId.LoadAsync(getHttpsResource.ParentId ?? Guid.Empty, cancellationToken)!; - } + public Uri GetLocator( + [Parent] GetHttpsResource getHttpsResource, + [Service] AppSettings appSettings + ) + { + return new Uri($"{appSettings.Host}/api/resources/{getHttpsResource.Id}"); + } - public Task GetChildren( - [Parent] Data.GetHttpsResource getHttpsResource, - GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, - CancellationToken cancellationToken + public async Task GetParent( + [Parent] GetHttpsResource getHttpsResource, + GetHttpsResourceByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + // TODO Why is `?? Guid.Empty` below necessary although `getHttpsResource.ParentId` is not null? + return getHttpsResource.ParentId is null + ? null + : await byId.LoadAsync(getHttpsResource.ParentId ?? Guid.Empty, cancellationToken)!; + } - ) - { - return byId.LoadAsync(getHttpsResource.Id, cancellationToken)!; - } + public Task GetChildren( + [Parent] GetHttpsResource getHttpsResource, + GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(getHttpsResource.Id, cancellationToken)!; } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 3f64c1c1..c18a3658 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,57 +1,58 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.GetHttpsResources +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceType + : EntityType { - public sealed class GetHttpsResourceType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor - .Field("locator") - .ResolveWith(t => t.GetLocator(default!, default!)); - descriptor - .Field(x => x.ParentId) - .Ignore(); - descriptor - .Field(x => x.Parent) - .ResolveWith(t => t.GetParent(default!, default!, default!)); - descriptor - .Field(x => x.Children) - .ResolveWith(t => t.GetChildren(default!, default!, default!)); - descriptor - .Field(x => x.DataId) - .Ignore(); - descriptor - .Field(x => x.CalorimetricDataId) - .Ignore(); - descriptor - .Field(x => x.CalorimetricData) - .Ignore(); - descriptor - .Field(x => x.HygrothermalDataId) - .Ignore(); - descriptor - .Field(x => x.HygrothermalData) - .Ignore(); - descriptor - .Field(x => x.OpticalDataId) - .Ignore(); - descriptor - .Field(x => x.OpticalData) - .Ignore(); - descriptor - .Field(x => x.PhotovoltaicDataId) - .Ignore(); - descriptor - .Field(x => x.PhotovoltaicData) - .Ignore(); - descriptor - .Field(x => x.Data) - .ResolveWith(t => t.GetData(default!, default!, default!, default!, default!, default!)); - } + base.Configure(descriptor); + descriptor + .Field("locator") + .ResolveWith(t => t.GetLocator(default!, default!)); + descriptor + .Field(x => x.ParentId) + .Ignore(); + descriptor + .Field(x => x.Parent) + .ResolveWith(t => t.GetParent(default!, default!, default!)); + descriptor + .Field(x => x.Children) + .ResolveWith(t => t.GetChildren(default!, default!, default!)); + descriptor + .Field(x => x.DataId) + .Ignore(); + descriptor + .Field(x => x.CalorimetricDataId) + .Ignore(); + descriptor + .Field(x => x.CalorimetricData) + .Ignore(); + descriptor + .Field(x => x.HygrothermalDataId) + .Ignore(); + descriptor + .Field(x => x.HygrothermalData) + .Ignore(); + descriptor + .Field(x => x.OpticalDataId) + .Ignore(); + descriptor + .Field(x => x.OpticalData) + .Ignore(); + descriptor + .Field(x => x.PhotovoltaicDataId) + .Ignore(); + descriptor + .Field(x => x.PhotovoltaicData) + .Ignore(); + descriptor + .Field(x => x.Data) + .ResolveWith(t => + t.GetData(default!, default!, default!, default!, default!, default!)); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs index fd0c6528..625ffdf2 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class CreateHygrothermalDataError + : UserErrorBase { - public sealed class CreateHygrothermalDataError - : GraphQl.UserErrorBase + public CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs index 42815def..afbbdb2d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public enum CreateHygrothermalDataErrorCode { - public enum CreateHygrothermalDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index ef980885..61b0d09e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -3,21 +3,20 @@ using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.HygrothermalDataX -{ - public sealed record CreateHygrothermalDataInput( - string AccessToken, - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource - ); -} +namespace Database.GraphQl.HygrothermalDataX; + +public sealed record CreateHygrothermalDataInput( + string AccessToken, + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + IReadOnlyList Approvals, + // ResponseApproval Approval + RootGetHttpsResourceInput RootResource +); \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs index 30995aa9..a08758c4 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs @@ -1,20 +1,21 @@ -namespace Database.GraphQl.HygrothermalDataX +using Database.Data; + +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class CreateHygrothermalDataPayload + : HygrothermalDataPayload { - public sealed class CreateHygrothermalDataPayload - : HygrothermalDataPayload + public CreateHygrothermalDataPayload( + HygrothermalData hygrothermalData + ) + : base(hygrothermalData) { - public CreateHygrothermalDataPayload( - Data.HygrothermalData hygrothermalData - ) - : base(hygrothermalData) - { - } + } - public CreateHygrothermalDataPayload( - CreateHygrothermalDataError error - ) + public CreateHygrothermalDataPayload( + CreateHygrothermalDataError error + ) : base(error) - { - } + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs index b94edfe3..252cf1a9 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataByIdDataLoader + : EntityByIdDataLoader { - public sealed class HygrothermalDataByIdDataLoader - : EntityByIdDataLoader + public HygrothermalDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.HygrothermalData + ) { - public HygrothermalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.HygrothermalData - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index 853cf18b..d05181f0 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -1,17 +1,17 @@ +using Database.Data; using Database.GraphQl.DataX; using HotChocolate.Data.Filters; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataFilterType + : DataFilterTypeBase { - public sealed class HygrothermalDataFilterType - : DataFilterTypeBase + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Field(x => x.Id); - } + base.Configure(descriptor); + descriptor.Field(x => x.Id); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index d1bb99c5..1c140eac 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -2,59 +2,109 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.Extensions; using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; using HotChocolate.Types; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class HygrothermalDataMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class HygrothermalDataMutations + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreateHygrothermalDataAsync( + CreateHygrothermalDataInput input, + // ClaimsPrincipal claimsPrincipal, + // [ScopedService] UserManager userManager, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreateHygrothermalDataAsync( - CreateHygrothermalDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - Data.ApplicationDbContext context, - [Service] AppSettings appSettings, - CancellationToken cancellationToken - ) - { - // if (!await HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) - { - return new CreateHygrothermalDataPayload( - new CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); - } - var hygrothermalData = new Data.HygrothermalData( - locale: input.Locale, - componentId: input.ComponentId, - name: input.Name, - description: input.Description, - warnings: input.Warnings, - creatorId: input.CreatorId, - createdAt: input.CreatedAt, - appliedMethod: new Data.AppliedMethod( - methodId: input.AppliedMethod.MethodId, - arguments: input.AppliedMethod.Arguments - .Select(a => new Data.NamedMethodArgument( - name: a.Name, + // if (!await HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( + // claimsPrincipal, + // input.CreatorId, + // userManager, + // context, + // cancellationToken + // ).ConfigureAwait(false) + // ) + if (input.AccessToken != appSettings.AccessToken) + return new CreateHygrothermalDataPayload( + new CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) + ); + var hygrothermalData = new HygrothermalData( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + new AppliedMethod( + input.AppliedMethod.MethodId, + input.AppliedMethod.Arguments + .Select(a => new NamedMethodArgument( + a.Name, + // TODO Turn `a.Value` into `JsonDocument`. It comes + // as nested `IReadOnlyDictionary/-List` as said on + // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type + // Take inspiration from + // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ + // and + // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs + // This is also needed in `GetHttpsResourceMutations`. + JsonDocument.Parse(@"""TODO""") + )) + .ToList(), + input.AppliedMethod.Sources + .Select(s => new NamedMethodSource( + s.Name, + new CrossDatabaseDataReference( + s.Value.DataId, + s.Value.DataTimestamp, + s.Value.DataKind, + s.Value.DatabaseId + ) + )) + .ToList() + ), + input.Approvals.Select(a => + new DataApproval( + a.Timestamp, + a.Signature, + a.KeyFingerprint, + a.Query, + a.Response, + a.ApproverId + ) + ).ToList() + // approval: input.Approval + ); + var resource = new GetHttpsResource( + input.RootResource.Description, + input.RootResource.HashValue, + input.RootResource.DataFormatId, + null, + input.RootResource.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.RootResource.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.RootResource.AppliedConversionMethod.MethodId, + input.RootResource.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, // TODO Turn `a.Value` into `JsonDocument`. It comes // as nested `IReadOnlyDictionary/-List` as said on // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type @@ -63,70 +113,15 @@ CancellationToken cancellationToken // and // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - )) - .ToList(), - sources: input.AppliedMethod.Sources - .Select(s => new Data.NamedMethodSource( - name: s.Name, - value: new Data.CrossDatabaseDataReference( - dataId: s.Value.DataId, - dataTimestamp: s.Value.DataTimestamp, - dataKind: s.Value.DataKind, - databaseId: s.Value.DatabaseId - ) - )) - .ToList() - ), - approvals: input.Approvals.Select(a => - new Data.DataApproval( - timestamp: a.Timestamp, - signature: a.Signature, - keyFingerprint: a.KeyFingerprint, - query: a.Query, - response: a.Response, - approverId: a.ApproverId - ) - ).ToList() - // approval: input.Approval - ); - var resource = new Data.GetHttpsResource( - description: input.RootResource.Description, - hashValue: input.RootResource.HashValue, - dataFormatId: input.RootResource.DataFormatId, - parentId: null, - archivedFilesMetaInformation: input.RootResource.ArchivedFilesMetaInformation.Select(i => - new Data.FileMetaInformation( - path: i.Path, - dataFormatId: i.DataFormatId + JsonDocument.Parse(@"""TODO""") ) ).ToList(), - appliedConversionMethod: - input.RootResource.AppliedConversionMethod is null - ? null - : new Data.ToTreeVertexAppliedConversionMethod( - methodId: input.RootResource.AppliedConversionMethod.MethodId, - arguments: input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new Data.NamedMethodArgument( - name: a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - ) - ).ToList(), - sourceName: input.RootResource.AppliedConversionMethod.SourceName - ) - ); - hygrothermalData.Resources.Add(resource); - context.HygrothermalData.Add(hygrothermalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreateHygrothermalDataPayload(hygrothermalData); - } + input.RootResource.AppliedConversionMethod.SourceName + ) + ); + hygrothermalData.Resources.Add(resource); + context.HygrothermalData.Add(hygrothermalData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateHygrothermalDataPayload(hygrothermalData); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs index 57820f4d..bac1715c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs @@ -1,53 +1,53 @@ using System.Collections.Generic; +using Database.Data; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public abstract class HygrothermalDataPayload + : Payload + where THygrothermalDataError : IUserError { - public abstract class HygrothermalDataPayload - : Payload - where THygrothermalDataError : IUserError + protected HygrothermalDataPayload( + HygrothermalData hygrothermalData + ) { - public Data.HygrothermalData? HygrothermalData { get; } - public IReadOnlyCollection? Errors { get; } - - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData - ) - { - HygrothermalData = hygrothermalData; - } + HygrothermalData = hygrothermalData; + } - protected HygrothermalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } + protected HygrothermalDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } - protected HygrothermalDataPayload( - THygrothermalDataError error - ) - : this(new[] { error }) - { - } + protected HygrothermalDataPayload( + THygrothermalDataError error + ) + : this(new[] { error }) + { + } - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData, - IReadOnlyCollection errors - ) - { - HygrothermalData = hygrothermalData; - Errors = errors; - } + protected HygrothermalDataPayload( + HygrothermalData hygrothermalData, + IReadOnlyCollection errors + ) + { + HygrothermalData = hygrothermalData; + Errors = errors; + } - protected HygrothermalDataPayload( - Data.HygrothermalData hygrothermalData, - THygrothermalDataError error - ) - : this( - hygrothermalData, - new[] { error } - ) - { - } + protected HygrothermalDataPayload( + HygrothermalData hygrothermalData, + THygrothermalDataError error + ) + : this( + hygrothermalData, + new[] { error } + ) + { } -} + + public HygrothermalData? HygrothermalData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 51897701..5e066406 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -2,43 +2,43 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class HygrothermalDataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class HygrothermalDataQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public IQueryable GetAllHygrothermalData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] - public IQueryable GetAllHygrothermalData( - DateTime? timestamp, - [GraphQLType] string? locale, - Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.HygrothermalData; - } + // TODO Use `timestamp` and `locale`. + return context.HygrothermalData; + } - public Task GetHygrothermalDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType] string? locale, - HygrothermalDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + public Task GetHygrothermalDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + HygrothermalDataByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + // TODO Use `timestamp` and `locale`. + return byId.LoadAsync( + id, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs index ff18570e..2145366f 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs @@ -1,33 +1,32 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.DataX; using HotChocolate; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataResolvers { - public sealed class HygrothermalDataResolvers + public Task GetGetHttpsResources( + [Parent] HygrothermalData hygrothermalData, + GetHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) { - public Task GetGetHttpsResources( - [Parent] Data.HygrothermalData hygrothermalData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(hygrothermalData.Id, cancellationToken); - } + return byId.LoadAsync(hygrothermalData.Id, cancellationToken); + } - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.HygrothermalData hygrothermalData - ) - { - return new GetHttpsResourceTree(hygrothermalData); - } + public GetHttpsResourceTree GetGetHttpsResourceTree( + [Parent] HygrothermalData hygrothermalData + ) + { + return new GetHttpsResourceTree(hygrothermalData); + } - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } + public DateTime GetTimestamp() + { + return DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs index f972aa40..0fb0a10c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs @@ -1,28 +1,28 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.HygrothermalDataX +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataType + : EntityType { - public sealed class HygrothermalDataType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); - } + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field("resourceTree") + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field("timestamp") + .Type>() + .ResolveWith(t => t.GetTimestamp()); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/IUserError.cs b/backend/src/GraphQl/IUserError.cs index fcc6ffe4..c2e00b4c 100644 --- a/backend/src/GraphQl/IUserError.cs +++ b/backend/src/GraphQl/IUserError.cs @@ -2,12 +2,11 @@ using HotChocolate.Types; // See section `Stage 6a: ...` in https://productionreadygraphql.com/2020-08-01-guide-to-graphql-errors -namespace Database.GraphQl +namespace Database.GraphQl; + +[InterfaceType("UserError")] +public interface IUserError { - [InterfaceType("UserError")] - public interface IUserError - { - public string Message { get; } - public IReadOnlyList Path { get; } - } -} + public string Message { get; } + public IReadOnlyList Path { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs index b73d4b1f..7191b940 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/LocaleType.cs @@ -1,58 +1,55 @@ using System.Text.RegularExpressions; -using HotChocolate; using HotChocolate.Types; // TODO Maybe use an enumeration as runtime type instead of string (and fallback to english when the given one does not exist).namespace Database.GraphQl -namespace Database.GraphQl +namespace Database.GraphQl; + +/// +/// [BCP 47](https://tools.ietf.org/html/bcp47) +/// compliant +/// [Language Tag](https://tools.ietf.org/html/bcp47#section-2) +/// string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language +/// part is essentially an +/// [ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), +/// the script an +/// [ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), +/// and the region an +/// [ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). +/// Note that the +/// [Internet Assigned Numbers Authority (IANA)](https://www.iana.org) +/// maintains the +/// [Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). +/// +public class LocaleType + : RegexType { + private const string _validationPattern = + "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; + /// - /// [BCP 47](https://tools.ietf.org/html/bcp47) - /// compliant - /// [Language Tag](https://tools.ietf.org/html/bcp47#section-2) - /// string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language - /// part is essentially an - /// [ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), - /// the script an - /// [ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), - /// and the region an - /// [ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). - /// - /// Note that the - /// [Internet Assigned Numbers Authority (IANA)](https://www.iana.org) - /// maintains the - /// [Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). + /// Initializes a new instance of the class. /// - public class LocaleType - : RegexType + public LocaleType() + : this( + nameof(LocaleType).Remove(nameof(LocaleType).Length - "Type".Length), + "BCP 47 compliant Language Tag string" + ) { - private const string _validationPattern = - "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; - - /// - /// Initializes a new instance of the class. - /// - public LocaleType() - : this( - nameof(LocaleType).Remove(nameof(LocaleType).Length - "Type".Length), - "BCP 47 compliant Language Tag string" - ) - { - } + } - /// - /// Initializes a new instance of the class. - /// - public LocaleType( - string name, - string? description = null, - BindingBehavior bind = BindingBehavior.Explicit) - : base( - name, - _validationPattern, - description, - RegexOptions.Compiled | RegexOptions.IgnoreCase, - bind) - { - } + /// + /// Initializes a new instance of the class. + /// + public LocaleType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit) + : base( + name, + _validationPattern, + description, + RegexOptions.Compiled | RegexOptions.IgnoreCase, + bind) + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index cbb8622f..f5e87bb3 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Text; using HotChocolate; @@ -10,155 +11,232 @@ using HotChocolate.Resolvers; using Microsoft.Extensions.Logging; -namespace Database.GraphQl +namespace Database.GraphQl; + +public static partial class Log { - // Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate - // and https://chillicream.com/blog/2021/01/10/hot-chocolate-logging - public sealed class LoggingDiagnosticEventListener + [LoggerMessage( + EventId = 0, + Level = LogLevel.Error, + Message = "Failed executing the query {Query}.")] + public static partial void FailedQueryExecution( + this ILogger logger, + Exception exception, + IQuery? query + ); + + [LoggerMessage( + EventId = 1, + Level = LogLevel.Error, + Message = "Failed executing the operation {Operation} with the error {Error}.")] + public static partial void FailedOperationExecution( + this ILogger logger, + Exception? exception, + IOperation operation, + string error + ); + + [LoggerMessage( + EventId = 2, + Level = LogLevel.Error, + Message = "Failed handling the subscription event of the operation {Operation}.")] + public static partial void FailedSubscriptionEvent( + this ILogger logger, + Exception exception, + IOperation operation + ); + + [LoggerMessage( + EventId = 3, + Level = LogLevel.Error, + Message = "Failed transporting the subscription of the operation {Operation}.")] + public static partial void FailedSubscriptionTransport( + this ILogger logger, + Exception exception, + IOperation operation + ); + + [LoggerMessage( + EventId = 4, + Level = LogLevel.Error, + Message = "The query {Query} has the syntax error {Error}.")] + public static partial void FailedSyntax( + this ILogger logger, + Exception? exception, + IQuery? query, + string error + ); + + [LoggerMessage( + EventId = 5, + Level = LogLevel.Error, + Message = "Failed processing the task {Task} with the error {Error}.")] + public static partial void FailedTask( + this ILogger logger, + Exception? exception, + IExecutionTask task, + string error + ); + + [LoggerMessage( + EventId = 6, + Level = LogLevel.Error, + Message = "Failed validating the query {Query} with the error {Error}.")] + public static partial void FailedValidation( + this ILogger logger, + Exception? exception, + IQuery? query, + string error + ); +} + +// Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate +// and https://chillicream.com/blog/2021/01/10/hot-chocolate-logging +public sealed class LoggingDiagnosticEventListener : ExecutionDiagnosticEventListener +{ + private static Stopwatch s_queryTimer = null!; + private readonly ILogger _logger; + + public LoggingDiagnosticEventListener( + ILogger logger + ) { - private static Stopwatch s_queryTimer = null!; - private readonly ILogger _logger; + _logger = logger; + } - public LoggingDiagnosticEventListener( - ILogger logger - ) - { - _logger = logger; - } + // this diagnostic event is raised when a request is executed ... + public override IDisposable ExecuteRequest(IRequestContext context) + { + // ... we will return an activity scope that is used to signal when the request is finished. + return new RequestScope(_logger, context); + } - // this diagnostic event is raised when a request is executed ... - public override IDisposable ExecuteRequest(IRequestContext context) - { - // ... we will return an activity scope that is used to signal when the request is finished. - return new RequestScope(_logger, context); - } + public override void RequestError( + IRequestContext context, + Exception exception + ) + { + _logger.FailedQueryExecution(exception, context.Request.Query); + } - public override void RequestError( - IRequestContext context, - Exception exception - ) - { - _logger.LogError(exception, $"During execution of the query {context.Request.Query} the exception {exception} occurred."); - } + public override void ResolverError( + IMiddlewareContext context, + IError error + ) + { + _logger.FailedOperationExecution(error.Exception, context.Operation, ConvertErrorToString(error)); + } - public override void ResolverError( - IMiddlewareContext context, - IError error - ) - { - _logger.LogError(error.Exception, $"During execution of the operation {context.Operation} the error {ConvertErrorToString(error)} occurred."); - } + public override void SubscriptionEventError(SubscriptionEventContext context, Exception exception) + { + _logger.FailedSubscriptionEvent(exception, context.Subscription.Operation); + } - public override void SubscriptionEventError(SubscriptionEventContext context, Exception exception) - { - _logger.LogError(exception, $"During execution of the subscription operation {context.Subscription.Operation} the exception {exception} occurred."); - } + public override void SubscriptionTransportError(ISubscription subscription, Exception exception) + { + _logger.FailedSubscriptionTransport(exception, subscription.Operation); + } - public override void SubscriptionTransportError(ISubscription subscription, Exception exception) - { - _logger.LogError(exception, $"During execution of the subscription operation {subscription.Operation} the exception {exception} occurred."); - } + public override void SyntaxError( + IRequestContext context, + IError error + ) + { + _logger.FailedSyntax(error.Exception, context.Request.Query, ConvertErrorToString(error)); + } - public override void SyntaxError( - IRequestContext context, - IError error - ) - { - _logger.LogError(error.Exception, $"During execution of the query {context.Request.Query} the error {ConvertErrorToString(error)} occurred."); - } + public override void TaskError( + IExecutionTask task, + IError error + ) + { + _logger.FailedTask(error.Exception, task, ConvertErrorToString(error)); + } - public override void TaskError( - IExecutionTask task, - IError error - ) - { - _logger.LogError(error.Exception, $"During execution of the task {task} the error {ConvertErrorToString(error)} occurred."); - } + public override void ValidationErrors( + IRequestContext context, + IReadOnlyList errors + ) + { + foreach (var error in errors) + _logger.FailedValidation(error.Exception, context.Request.Query, ConvertErrorToString(error)); + } - public override void ValidationErrors( - IRequestContext context, - IReadOnlyList errors - ) - { - foreach (var error in errors) - { - _logger.LogError(error.Exception, $"During execution of the query {context.Request.Query} the error {ConvertErrorToString(error)} occurred."); - } - } + private static string ConvertErrorToString( + IError error + ) + { + return $"{error}(Code {error.Code}, Message {error.Message}, Path {error.Path?.Print()})"; + } + + private sealed class RequestScope : IDisposable + { + private readonly IRequestContext _context; + private readonly ILogger _logger; - private static string ConvertErrorToString( - IError error - ) + public RequestScope(ILogger logger, IRequestContext context) { - return $"{error}(Code {error.Code}, Message {error.Message}, Path {error.Path?.Print()})"; + _logger = logger; + _context = context; + s_queryTimer = new Stopwatch(); + s_queryTimer.Start(); } - private class RequestScope : IDisposable + public void Dispose() { - private readonly IRequestContext _context; - private readonly ILogger _logger; - - public RequestScope(ILogger logger, IRequestContext context) + // when the request is finished it will dispose the activity scope and + // this is when we print the parsed query. + if (_context.Document is not null) { - _logger = logger; - _context = context; - s_queryTimer = new Stopwatch(); - s_queryTimer.Start(); - } + // we just need to do a ToString on the Document which represents the parsed + // GraphQL request document. + StringBuilder stringBuilder = new(_context.Document.ToString(true)); + stringBuilder.AppendLine(); - public void Dispose() - { - // when the request is finished it will dispose the activity scope and - // this is when we print the parsed query. - if (_context.Document is not null) + if (_context.Variables != null) { - // we just need to do a ToString on the Document which represents the parsed - // GraphQL request document. - StringBuilder stringBuilder = new(_context.Document.ToString(true)); - stringBuilder.AppendLine(); - - if (_context.Variables != null) + var variablesConcrete = _context.Variables!.ToList(); + if (variablesConcrete.Count > 0) { - var variablesConcrete = _context.Variables!.ToList(); - if (variablesConcrete.Count > 0) + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, + $"Variables {Environment.NewLine}"); + try { - stringBuilder.AppendFormat($"Variables {Environment.NewLine}"); - try + foreach (var variableValue in _context.Variables!) { - foreach (var variableValue in _context.Variables!) + static string PadRightHelper(string existingString, int lengthToPadTo) { - static string PadRightHelper(string existingString, int lengthToPadTo) - { - if (string.IsNullOrEmpty(existingString)) - { - return "".PadRight(lengthToPadTo); - } - - if (existingString.Length > lengthToPadTo) - { - return existingString[..lengthToPadTo]; - } - - return existingString + " ".PadRight(lengthToPadTo - existingString.Length); - } - stringBuilder.AppendFormat( - $" {PadRightHelper(variableValue.Name, 20)} : {PadRightHelper(variableValue.Value.ToString(), 20)}: {variableValue.Type}"); - stringBuilder.AppendFormat($"{Environment.NewLine}"); + if (string.IsNullOrEmpty(existingString)) return "".PadRight(lengthToPadTo); + + if (existingString.Length > lengthToPadTo) return existingString[..lengthToPadTo]; + + return existingString + " ".PadRight(lengthToPadTo - existingString.Length); } - } - catch - { - // all input type records will land here. - stringBuilder.Append(" Formatting Variables Error. Continuing..."); - stringBuilder.AppendFormat($"{Environment.NewLine}"); + + stringBuilder.AppendFormat( + CultureInfo.InvariantCulture, + $" {PadRightHelper(variableValue.Name, 20)} : {PadRightHelper(variableValue.Value.ToString(), 20)}: {variableValue.Type}"); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); } } + catch + { + // all input type records will land here. + stringBuilder.Append(" Formatting Variables Error. Continuing..."); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } } - s_queryTimer.Stop(); - stringBuilder.AppendFormat($"Ellapsed time for query is {s_queryTimer.Elapsed.TotalMilliseconds:0.#} milliseconds."); - _logger.LogInformation(stringBuilder.ToString()); } + + s_queryTimer.Stop(); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, + $"Ellapsed time for query is {s_queryTimer.Elapsed.TotalMilliseconds:0.#} milliseconds."); +#pragma warning disable CA1848 // Use the LoggerMessage delegates +#pragma warning disable CA2254 // Template should be a static expression + _logger.LogInformation(stringBuilder.ToString()); +#pragma warning restore CA2254 // Template should be a static expression +#pragma warning restore CA1848 // Use the LoggerMessage delegates } } } diff --git a/backend/src/GraphQl/Mutation.cs b/backend/src/GraphQl/Mutation.cs index b3869ae8..d4f389da 100644 --- a/backend/src/GraphQl/Mutation.cs +++ b/backend/src/GraphQl/Mutation.cs @@ -1,6 +1,5 @@ -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed class Mutation { - public sealed class Mutation - { - } } \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs index a785ee6e..bb56cb58 100644 --- a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs @@ -1,17 +1,17 @@ +using Database.Data; using HotChocolate.Data.Filters; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed class NamedMethodArgumentFilterType + : FilterInputType { - public sealed class NamedMethodArgumentFilterType - : FilterInputType + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Name); - // TODO descriptor.Field(x => x.Value); - } + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Name); + // TODO descriptor.Field(x => x.Value); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentInput.cs b/backend/src/GraphQl/NamedMethodArgumentInput.cs index bb97c46b..08a3030d 100644 --- a/backend/src/GraphQl/NamedMethodArgumentInput.cs +++ b/backend/src/GraphQl/NamedMethodArgumentInput.cs @@ -1,9 +1,8 @@ using System.Text.Json; -namespace Database.GraphQl -{ - public sealed record NamedMethodArgumentInput( - string Name, - JsonElement Value - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record NamedMethodArgumentInput( + string Name, + JsonElement Value +); \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentType.cs b/backend/src/GraphQl/NamedMethodArgumentType.cs index 01097866..bd72b35b 100644 --- a/backend/src/GraphQl/NamedMethodArgumentType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentType.cs @@ -1,24 +1,24 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed class NamedMethodArgumentType + : ObjectType { - public sealed class NamedMethodArgumentType - : ObjectType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - // Note that in the GraphQL configuration we map the scalar "Any" - // to `JsonType`. - descriptor - .Field(x => x.Value) - .Type>() - .Resolve(context => - context.Parent() + // Note that in the GraphQL configuration we map the scalar "Any" + // to `JsonType`. + descriptor + .Field(x => x.Value) + .Type>() + .Resolve(context => + context.Parent() .Value .RootElement - ); - } + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodSourceInput.cs b/backend/src/GraphQl/NamedMethodSourceInput.cs index 30ba9faf..5016a2e2 100644 --- a/backend/src/GraphQl/NamedMethodSourceInput.cs +++ b/backend/src/GraphQl/NamedMethodSourceInput.cs @@ -1,7 +1,6 @@ -namespace Database.GraphQl -{ - public sealed record NamedMethodSourceInput( - string Name, - CrossDatabaseDataReferenceInput Value - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record NamedMethodSourceInput( + string Name, + CrossDatabaseDataReferenceInput Value +); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs index 62aadbc0..cd4b0ec9 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class CreateOpticalDataError + : UserErrorBase { - public sealed class CreateOpticalDataError - : GraphQl.UserErrorBase + public CreateOpticalDataError( + CreateOpticalDataErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public CreateOpticalDataError( - CreateOpticalDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs index 8567ad14..6dd5b4ed 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public enum CreateOpticalDataErrorCode { - public enum CreateOpticalDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index 41fb8586..96a021ed 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -3,28 +3,27 @@ using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.OpticalDataX -{ - public sealed record CreateOpticalDataInput( - string AccessToken, - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource, - double[] NearnormalHemisphericalVisibleTransmittances, - double[] NearnormalHemisphericalVisibleReflectances, - double[] NearnormalHemisphericalSolarTransmittances, - double[] NearnormalHemisphericalSolarReflectances, - double[] InfraredEmittances, - double[] ColorRenderingIndices, - IReadOnlyList CielabColors - ); -} +namespace Database.GraphQl.OpticalDataX; + +public sealed record CreateOpticalDataInput( + string AccessToken, + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + IReadOnlyList Approvals, + // ResponseApproval Approval + RootGetHttpsResourceInput RootResource, + double[] NearnormalHemisphericalVisibleTransmittances, + double[] NearnormalHemisphericalVisibleReflectances, + double[] NearnormalHemisphericalSolarTransmittances, + double[] NearnormalHemisphericalSolarReflectances, + double[] InfraredEmittances, + double[] ColorRenderingIndices, + IReadOnlyList CielabColors +); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs index 55f54d15..a354c283 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs @@ -1,20 +1,21 @@ -namespace Database.GraphQl.OpticalDataX +using Database.Data; + +namespace Database.GraphQl.OpticalDataX; + +public sealed class CreateOpticalDataPayload + : OpticalDataPayload { - public sealed class CreateOpticalDataPayload - : OpticalDataPayload + public CreateOpticalDataPayload( + OpticalData opticalData + ) + : base(opticalData) { - public CreateOpticalDataPayload( - Data.OpticalData opticalData - ) - : base(opticalData) - { - } + } - public CreateOpticalDataPayload( - CreateOpticalDataError error - ) + public CreateOpticalDataPayload( + CreateOpticalDataError error + ) : base(error) - { - } + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs index 5f805eda..9e9f1f00 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataByIdDataLoader + : EntityByIdDataLoader { - public sealed class OpticalDataByIdDataLoader - : EntityByIdDataLoader + public OpticalDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.OpticalData + ) { - public OpticalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.OpticalData - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 95278a05..8d728532 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -1,24 +1,24 @@ +using Database.Data; using Database.GraphQl.DataX; using HotChocolate.Data.Filters; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataFilterType + : DataFilterTypeBase { - public sealed class OpticalDataFilterType - : DataFilterTypeBase + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Field(x => x.Id); - descriptor.Field(x => x.NearnormalHemisphericalSolarReflectances); - descriptor.Field(x => x.NearnormalHemisphericalSolarTransmittances); - descriptor.Field(x => x.NearnormalHemisphericalVisibleReflectances); - descriptor.Field(x => x.NearnormalHemisphericalVisibleTransmittances); - descriptor.Field(x => x.InfraredEmittances); - descriptor.Field(x => x.ColorRenderingIndices); - descriptor.Field(x => x.CielabColors); - } + base.Configure(descriptor); + descriptor.Field(x => x.Id); + descriptor.Field(x => x.NearnormalHemisphericalSolarReflectances); + descriptor.Field(x => x.NearnormalHemisphericalSolarTransmittances); + descriptor.Field(x => x.NearnormalHemisphericalVisibleReflectances); + descriptor.Field(x => x.NearnormalHemisphericalVisibleTransmittances); + descriptor.Field(x => x.InfraredEmittances); + descriptor.Field(x => x.ColorRenderingIndices); + descriptor.Field(x => x.CielabColors); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 5fce9831..69311fc7 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -2,59 +2,122 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.Extensions; using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; using HotChocolate.Types; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class OpticalDataMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class OpticalDataMutations + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreateOpticalDataAsync( + CreateOpticalDataInput input, + // ClaimsPrincipal claimsPrincipal, + // [ScopedService] UserManager userManager, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreateOpticalDataAsync( - CreateOpticalDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - Data.ApplicationDbContext context, - [Service] AppSettings appSettings, - CancellationToken cancellationToken - ) - { - // if (!await OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) - { - return new CreateOpticalDataPayload( - new CreateOpticalDataError( - CreateOpticalDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); - } - var opticalData = new Data.OpticalData( - locale: input.Locale, - componentId: input.ComponentId, - name: input.Name, - description: input.Description, - warnings: input.Warnings, - creatorId: input.CreatorId, - createdAt: input.CreatedAt, - appliedMethod: new Data.AppliedMethod( - methodId: input.AppliedMethod.MethodId, - arguments: input.AppliedMethod.Arguments - .Select(a => new Data.NamedMethodArgument( - name: a.Name, + // if (!await OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( + // claimsPrincipal, + // input.CreatorId, + // userManager, + // context, + // cancellationToken + // ).ConfigureAwait(false) + // ) + if (input.AccessToken != appSettings.AccessToken) + return new CreateOpticalDataPayload( + new CreateOpticalDataError( + CreateOpticalDataErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) + ); + var opticalData = new OpticalData( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + new AppliedMethod( + input.AppliedMethod.MethodId, + input.AppliedMethod.Arguments + .Select(a => new NamedMethodArgument( + a.Name, + // TODO Turn `a.Value` into `JsonDocument`. It comes + // as nested `IReadOnlyDictionary/-List` as said on + // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type + // Take inspiration from + // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ + // and + // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs + // This is also needed in `GetHttpsResourceMutations`. + JsonDocument.Parse(@"""TODO""") + )) + .ToList(), + input.AppliedMethod.Sources + .Select(s => new NamedMethodSource( + s.Name, + new CrossDatabaseDataReference( + s.Value.DataId, + s.Value.DataTimestamp, + s.Value.DataKind, + s.Value.DatabaseId + ) + )) + .ToList() + ), + input.Approvals.Select(a => + new DataApproval( + a.Timestamp, + a.Signature, + a.KeyFingerprint, + a.Query, + a.Response, + a.ApproverId + ) + ).ToList(), + // approval: input.Approval + input.NearnormalHemisphericalVisibleTransmittances, + input.NearnormalHemisphericalVisibleReflectances, + input.NearnormalHemisphericalSolarTransmittances, + input.NearnormalHemisphericalSolarReflectances, + input.InfraredEmittances, + input.ColorRenderingIndices, + input.CielabColors.Select(c => + new CielabColor( + c.LStar, + c.AStar, + c.BStar + ) + ).ToList() + ); + var resource = new GetHttpsResource( + input.RootResource.Description, + input.RootResource.HashValue, + input.RootResource.DataFormatId, + null, + input.RootResource.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.RootResource.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.RootResource.AppliedConversionMethod.MethodId, + input.RootResource.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, // TODO Turn `a.Value` into `JsonDocument`. It comes // as nested `IReadOnlyDictionary/-List` as said on // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type @@ -63,83 +126,15 @@ CancellationToken cancellationToken // and // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - )) - .ToList(), - sources: input.AppliedMethod.Sources - .Select(s => new Data.NamedMethodSource( - name: s.Name, - value: new Data.CrossDatabaseDataReference( - dataId: s.Value.DataId, - dataTimestamp: s.Value.DataTimestamp, - dataKind: s.Value.DataKind, - databaseId: s.Value.DatabaseId - ) - )) - .ToList() - ), - approvals: input.Approvals.Select(a => - new Data.DataApproval( - timestamp: a.Timestamp, - signature: a.Signature, - keyFingerprint: a.KeyFingerprint, - query: a.Query, - response: a.Response, - approverId: a.ApproverId - ) - ).ToList(), - // approval: input.Approval - nearnormalHemisphericalVisibleTransmittances: input.NearnormalHemisphericalVisibleTransmittances, - nearnormalHemisphericalVisibleReflectances: input.NearnormalHemisphericalVisibleReflectances, - nearnormalHemisphericalSolarTransmittances: input.NearnormalHemisphericalSolarTransmittances, - nearnormalHemisphericalSolarReflectances: input.NearnormalHemisphericalSolarReflectances, - infraredEmittances: input.InfraredEmittances, - colorRenderingIndices: input.ColorRenderingIndices, - cielabColors: input.CielabColors.Select(c => - new Data.CielabColor( - lStar: c.LStar, - aStar: c.AStar, - bStar: c.BStar - ) - ).ToList() - ); - var resource = new Data.GetHttpsResource( - description: input.RootResource.Description, - hashValue: input.RootResource.HashValue, - dataFormatId: input.RootResource.DataFormatId, - parentId: null, - archivedFilesMetaInformation: input.RootResource.ArchivedFilesMetaInformation.Select(i => - new Data.FileMetaInformation( - path: i.Path, - dataFormatId: i.DataFormatId + JsonDocument.Parse(@"""TODO""") ) ).ToList(), - appliedConversionMethod: - input.RootResource.AppliedConversionMethod is null - ? null - : new Data.ToTreeVertexAppliedConversionMethod( - methodId: input.RootResource.AppliedConversionMethod.MethodId, - arguments: input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new Data.NamedMethodArgument( - name: a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - ) - ).ToList(), - sourceName: input.RootResource.AppliedConversionMethod.SourceName - ) - ); - opticalData.Resources.Add(resource); - context.OpticalData.Add(opticalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreateOpticalDataPayload(opticalData); - } + input.RootResource.AppliedConversionMethod.SourceName + ) + ); + opticalData.Resources.Add(resource); + context.OpticalData.Add(opticalData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateOpticalDataPayload(opticalData); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs index 90617183..a0eba70e 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs @@ -1,53 +1,53 @@ using System.Collections.Generic; +using Database.Data; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public abstract class OpticalDataPayload + : Payload + where TOpticalDataError : IUserError { - public abstract class OpticalDataPayload - : Payload - where TOpticalDataError : IUserError + protected OpticalDataPayload( + OpticalData opticalData + ) { - public Data.OpticalData? OpticalData { get; } - public IReadOnlyCollection? Errors { get; } - - protected OpticalDataPayload( - Data.OpticalData opticalData - ) - { - OpticalData = opticalData; - } + OpticalData = opticalData; + } - protected OpticalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } + protected OpticalDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } - protected OpticalDataPayload( - TOpticalDataError error - ) - : this(new[] { error }) - { - } + protected OpticalDataPayload( + TOpticalDataError error + ) + : this(new[] { error }) + { + } - protected OpticalDataPayload( - Data.OpticalData opticalData, - IReadOnlyCollection errors - ) - { - OpticalData = opticalData; - Errors = errors; - } + protected OpticalDataPayload( + OpticalData opticalData, + IReadOnlyCollection errors + ) + { + OpticalData = opticalData; + Errors = errors; + } - protected OpticalDataPayload( - Data.OpticalData opticalData, - TOpticalDataError error - ) - : this( - opticalData, - new[] { error } - ) - { - } + protected OpticalDataPayload( + OpticalData opticalData, + TOpticalDataError error + ) + : this( + opticalData, + new[] { error } + ) + { } -} + + public OpticalData? OpticalData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 8850d795..278829db 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -2,43 +2,43 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class OpticalDataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class OpticalDataQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public IQueryable GetAllOpticalData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] - public IQueryable GetAllOpticalData( - DateTime? timestamp, - [GraphQLType] string? locale, - Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.OpticalData; - } + // TODO Use `timestamp` and `locale`. + return context.OpticalData; + } - public Task GetOpticalDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType] string? locale, - OpticalDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + public Task GetOpticalDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + OpticalDataByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + // TODO Use `timestamp` and `locale`. + return byId.LoadAsync( + id, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs index b286dfa8..35374bb4 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs @@ -1,33 +1,32 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.DataX; using HotChocolate; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataResolvers { - public sealed class OpticalDataResolvers + public Task GetGetHttpsResources( + [Parent] OpticalData opticalData, + GetHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) { - public Task GetGetHttpsResources( - [Parent] Data.OpticalData opticalData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(opticalData.Id, cancellationToken); - } + return byId.LoadAsync(opticalData.Id, cancellationToken); + } - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.OpticalData opticalData - ) - { - return new GetHttpsResourceTree(opticalData); - } + public GetHttpsResourceTree GetGetHttpsResourceTree( + [Parent] OpticalData opticalData + ) + { + return new GetHttpsResourceTree(opticalData); + } - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } + public DateTime GetTimestamp() + { + return DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs index da010b74..3fff01b5 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs @@ -1,28 +1,28 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.OpticalDataX +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataType + : EntityType { - public sealed class OpticalDataType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); - } + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field("resourceTree") + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field("timestamp") + .Type>() + .ResolveWith(t => t.GetTimestamp()); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Payload.cs b/backend/src/GraphQl/Payload.cs index 1220153d..56fe2b41 100644 --- a/backend/src/GraphQl/Payload.cs +++ b/backend/src/GraphQl/Payload.cs @@ -1,6 +1,5 @@ -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class Payload { - public abstract class Payload - { - } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs index f41f478d..80c99ee3 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class CreatePhotovoltaicDataError + : UserErrorBase { - public sealed class CreatePhotovoltaicDataError - : GraphQl.UserErrorBase + public CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) { - public CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs index 7dfe4393..9a24fac3 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public enum CreatePhotovoltaicDataErrorCode { - public enum CreatePhotovoltaicDataErrorCode - { - UNKNOWN, - UNAUTHORIZED - } -} + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index da1322c4..e73d0ffc 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -3,21 +3,20 @@ using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl.PhotovoltaicDataX -{ - public sealed record CreatePhotovoltaicDataInput( - string AccessToken, - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval - RootGetHttpsResourceInput RootResource - ); -} +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed record CreatePhotovoltaicDataInput( + string AccessToken, + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + IReadOnlyList Approvals, + // ResponseApproval Approval + RootGetHttpsResourceInput RootResource +); \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs index d5d0cad8..94dc0e3d 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs @@ -1,20 +1,21 @@ -namespace Database.GraphQl.PhotovoltaicDataX +using Database.Data; + +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class CreatePhotovoltaicDataPayload + : PhotovoltaicDataPayload { - public sealed class CreatePhotovoltaicDataPayload - : PhotovoltaicDataPayload + public CreatePhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData + ) + : base(photovoltaicData) { - public CreatePhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData - ) - : base(photovoltaicData) - { - } + } - public CreatePhotovoltaicDataPayload( - CreatePhotovoltaicDataError error - ) + public CreatePhotovoltaicDataPayload( + CreatePhotovoltaicDataError error + ) : base(error) - { - } + { } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs index 542566fa..3a682bc5 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataByIdDataLoader + : EntityByIdDataLoader { - public sealed class PhotovoltaicDataByIdDataLoader - : EntityByIdDataLoader + public PhotovoltaicDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.PhotovoltaicData + ) { - public PhotovoltaicDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.PhotovoltaicData - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index ff32fa41..373756a6 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -1,17 +1,17 @@ +using Database.Data; using Database.GraphQl.DataX; using HotChocolate.Data.Filters; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataFilterType + : DataFilterTypeBase { - public sealed class PhotovoltaicDataFilterType - : DataFilterTypeBase + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) { - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Field(x => x.Id); - } + base.Configure(descriptor); + descriptor.Field(x => x.Id); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 5ba21504..70d73693 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -2,59 +2,109 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.Extensions; using HotChocolate; -using HotChocolate.AspNetCore.Authorization; -using HotChocolate.Data; using HotChocolate.Types; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class PhotovoltaicDataMutations { - [ExtendObjectType(nameof(Mutation))] - public sealed class PhotovoltaicDataMutations + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreatePhotovoltaicDataAsync( + CreatePhotovoltaicDataInput input, + // ClaimsPrincipal claimsPrincipal, + // [ScopedService] UserManager userManager, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreatePhotovoltaicDataAsync( - CreatePhotovoltaicDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, - Data.ApplicationDbContext context, - [Service] AppSettings appSettings, - CancellationToken cancellationToken - ) - { - // if (!await PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) - { - return new CreatePhotovoltaicDataPayload( - new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); - } - var photovoltaicData = new Data.PhotovoltaicData( - locale: input.Locale, - componentId: input.ComponentId, - name: input.Name, - description: input.Description, - warnings: input.Warnings, - creatorId: input.CreatorId, - createdAt: input.CreatedAt, - appliedMethod: new Data.AppliedMethod( - methodId: input.AppliedMethod.MethodId, - arguments: input.AppliedMethod.Arguments - .Select(a => new Data.NamedMethodArgument( - name: a.Name, + // if (!await PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( + // claimsPrincipal, + // input.CreatorId, + // userManager, + // context, + // cancellationToken + // ).ConfigureAwait(false) + // ) + if (input.AccessToken != appSettings.AccessToken) + return new CreatePhotovoltaicDataPayload( + new CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) + ); + var photovoltaicData = new PhotovoltaicData( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + new AppliedMethod( + input.AppliedMethod.MethodId, + input.AppliedMethod.Arguments + .Select(a => new NamedMethodArgument( + a.Name, + // TODO Turn `a.Value` into `JsonDocument`. It comes + // as nested `IReadOnlyDictionary/-List` as said on + // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type + // Take inspiration from + // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ + // and + // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs + // This is also needed in `GetHttpsResourceMutations`. + JsonDocument.Parse(@"""TODO""") + )) + .ToList(), + input.AppliedMethod.Sources + .Select(s => new NamedMethodSource( + s.Name, + new CrossDatabaseDataReference( + s.Value.DataId, + s.Value.DataTimestamp, + s.Value.DataKind, + s.Value.DatabaseId + ) + )) + .ToList() + ), + input.Approvals.Select(a => + new DataApproval( + a.Timestamp, + a.Signature, + a.KeyFingerprint, + a.Query, + a.Response, + a.ApproverId + ) + ).ToList() + // approval: input.Approval + ); + var resource = new GetHttpsResource( + input.RootResource.Description, + input.RootResource.HashValue, + input.RootResource.DataFormatId, + null, + input.RootResource.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.RootResource.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.RootResource.AppliedConversionMethod.MethodId, + input.RootResource.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, // TODO Turn `a.Value` into `JsonDocument`. It comes // as nested `IReadOnlyDictionary/-List` as said on // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type @@ -63,70 +113,15 @@ CancellationToken cancellationToken // and // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - )) - .ToList(), - sources: input.AppliedMethod.Sources - .Select(s => new Data.NamedMethodSource( - name: s.Name, - value: new Data.CrossDatabaseDataReference( - dataId: s.Value.DataId, - dataTimestamp: s.Value.DataTimestamp, - dataKind: s.Value.DataKind, - databaseId: s.Value.DatabaseId - ) - )) - .ToList() - ), - approvals: input.Approvals.Select(a => - new Data.DataApproval( - timestamp: a.Timestamp, - signature: a.Signature, - keyFingerprint: a.KeyFingerprint, - query: a.Query, - response: a.Response, - approverId: a.ApproverId - ) - ).ToList() - // approval: input.Approval - ); - var resource = new Data.GetHttpsResource( - description: input.RootResource.Description, - hashValue: input.RootResource.HashValue, - dataFormatId: input.RootResource.DataFormatId, - parentId: null, - archivedFilesMetaInformation: input.RootResource.ArchivedFilesMetaInformation.Select(i => - new Data.FileMetaInformation( - path: i.Path, - dataFormatId: i.DataFormatId + JsonDocument.Parse(@"""TODO""") ) ).ToList(), - appliedConversionMethod: - input.RootResource.AppliedConversionMethod is null - ? null - : new Data.ToTreeVertexAppliedConversionMethod( - methodId: input.RootResource.AppliedConversionMethod.MethodId, - arguments: input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new Data.NamedMethodArgument( - name: a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - value: JsonDocument.Parse(@"""TODO""") - ) - ).ToList(), - sourceName: input.RootResource.AppliedConversionMethod.SourceName - ) - ); - photovoltaicData.Resources.Add(resource); - context.PhotovoltaicData.Add(photovoltaicData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new CreatePhotovoltaicDataPayload(photovoltaicData); - } + input.RootResource.AppliedConversionMethod.SourceName + ) + ); + photovoltaicData.Resources.Add(resource); + context.PhotovoltaicData.Add(photovoltaicData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreatePhotovoltaicDataPayload(photovoltaicData); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs index aca2b591..f73670b7 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs @@ -1,53 +1,53 @@ using System.Collections.Generic; +using Database.Data; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public abstract class PhotovoltaicDataPayload + : Payload + where TPhotovoltaicDataError : IUserError { - public abstract class PhotovoltaicDataPayload - : Payload - where TPhotovoltaicDataError : IUserError + protected PhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData + ) { - public Data.PhotovoltaicData? PhotovoltaicData { get; } - public IReadOnlyCollection? Errors { get; } - - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData - ) - { - PhotovoltaicData = photovoltaicData; - } + PhotovoltaicData = photovoltaicData; + } - protected PhotovoltaicDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } + protected PhotovoltaicDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } - protected PhotovoltaicDataPayload( - TPhotovoltaicDataError error - ) - : this(new[] { error }) - { - } + protected PhotovoltaicDataPayload( + TPhotovoltaicDataError error + ) + : this(new[] { error }) + { + } - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData, - IReadOnlyCollection errors - ) - { - PhotovoltaicData = photovoltaicData; - Errors = errors; - } + protected PhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData, + IReadOnlyCollection errors + ) + { + PhotovoltaicData = photovoltaicData; + Errors = errors; + } - protected PhotovoltaicDataPayload( - Data.PhotovoltaicData photovoltaicData, - TPhotovoltaicDataError error - ) - : this( - photovoltaicData, - new[] { error } - ) - { - } + protected PhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData, + TPhotovoltaicDataError error + ) + : this( + photovoltaicData, + new[] { error } + ) + { } -} + + public PhotovoltaicData? PhotovoltaicData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 0a248d50..f26f383b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -2,43 +2,43 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; using Guid = System.Guid; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class PhotovoltaicDataQueries { - [ExtendObjectType(nameof(Query))] - public sealed class PhotovoltaicDataQueries + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public IQueryable GetAllPhotovoltaicData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) { - [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] - public IQueryable GetAllPhotovoltaicData( - DateTime? timestamp, - [GraphQLType] string? locale, - Data.ApplicationDbContext context - ) - { - // TODO Use `timestamp` and `locale`. - return context.PhotovoltaicData; - } + // TODO Use `timestamp` and `locale`. + return context.PhotovoltaicData; + } - public Task GetPhotovoltaicDataAsync( - Guid id, - DateTime? timestamp, - [GraphQLType] string? locale, - PhotovoltaicDataByIdDataLoader byId, - CancellationToken cancellationToken - ) - { - // TODO Use `timestamp` and `locale`. - return byId.LoadAsync( - id, - cancellationToken - ); - } + public Task GetPhotovoltaicDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + PhotovoltaicDataByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + // TODO Use `timestamp` and `locale`. + return byId.LoadAsync( + id, + cancellationToken + ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs index fc0f3779..4a0f3f2f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs @@ -1,33 +1,32 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Database.GraphQl.DataX; using HotChocolate; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataResolvers { - public sealed class PhotovoltaicDataResolvers + public Task GetGetHttpsResources( + [Parent] PhotovoltaicData photovoltaicData, + GetHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) { - public Task GetGetHttpsResources( - [Parent] Data.PhotovoltaicData photovoltaicData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - - ) - { - return byId.LoadAsync(photovoltaicData.Id, cancellationToken); - } + return byId.LoadAsync(photovoltaicData.Id, cancellationToken); + } - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] Data.PhotovoltaicData photovoltaicData - ) - { - return new GetHttpsResourceTree(photovoltaicData); - } + public GetHttpsResourceTree GetGetHttpsResourceTree( + [Parent] PhotovoltaicData photovoltaicData + ) + { + return new GetHttpsResourceTree(photovoltaicData); + } - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } + public DateTime GetTimestamp() + { + return DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs index ed84ea3b..f29a8177 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs @@ -1,28 +1,28 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.PhotovoltaicDataX +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataType + : EntityType { - public sealed class PhotovoltaicDataType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); - } + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field("resourceTree") + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field("timestamp") + .Type>() + .ResolveWith(t => t.GetTimestamp()); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Query.cs b/backend/src/GraphQl/Query.cs index 6707b9d8..b11fe36f 100644 --- a/backend/src/GraphQl/Query.cs +++ b/backend/src/GraphQl/Query.cs @@ -1,6 +1,5 @@ -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed class Query { - public sealed class Query - { - } } \ No newline at end of file diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 808c3e3c..6bd69b0b 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -namespace Database.GraphQl -{ - public sealed record RootGetHttpsResourceInput( - string Description, - string HashValue, - Guid DataFormatId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod - ); -} +namespace Database.GraphQl; + +public sealed record RootGetHttpsResourceInput( + string Description, + string HashValue, + Guid DataFormatId, + IReadOnlyList ArchivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod +); \ No newline at end of file diff --git a/backend/src/GraphQl/Subscription.cs b/backend/src/GraphQl/Subscription.cs index 3e046525..c5b0ac1c 100644 --- a/backend/src/GraphQl/Subscription.cs +++ b/backend/src/GraphQl/Subscription.cs @@ -1,6 +1,5 @@ -namespace Database.GraphQl +namespace Database.GraphQl; + +public sealed class Subscription { - public sealed class Subscription - { - } } \ No newline at end of file diff --git a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs index d6ed0886..1ee7d1a3 100644 --- a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs +++ b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; -namespace Database.GraphQl -{ - public sealed record ToTreeVertexAppliedConversionMethodInput( - Guid MethodId, - IReadOnlyList Arguments, - string SourceName - ); -} \ No newline at end of file +namespace Database.GraphQl; + +public sealed record ToTreeVertexAppliedConversionMethodInput( + Guid MethodId, + IReadOnlyList Arguments, + string SourceName +); \ No newline at end of file diff --git a/backend/src/GraphQl/UserErrorBase.cs b/backend/src/GraphQl/UserErrorBase.cs index 596bd63c..8751c523 100644 --- a/backend/src/GraphQl/UserErrorBase.cs +++ b/backend/src/GraphQl/UserErrorBase.cs @@ -1,25 +1,24 @@ using System.Collections.Generic; using Enum = System.Enum; -namespace Database.GraphQl +namespace Database.GraphQl; + +public abstract class UserErrorBase + : IUserError + where TUserErrorCode : struct, Enum { - public abstract class UserErrorBase - : IUserError - where TUserErrorCode : struct, Enum + protected UserErrorBase( + TUserErrorCode code, + string message, + IReadOnlyList path + ) { - public TUserErrorCode Code { get; } - public string Message { get; } - public IReadOnlyList Path { get; } - - protected UserErrorBase( - TUserErrorCode code, - string message, - IReadOnlyList path - ) - { - Code = code; - Message = message; - Path = path; - } + Code = code; + Message = message; + Path = path; } -} + + public TUserErrorCode Code { get; } + public string Message { get; } + public IReadOnlyList Path { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserByIdDataLoader.cs b/backend/src/GraphQl/Users/UserByIdDataLoader.cs index 12b9ac9a..bb64e7a5 100644 --- a/backend/src/GraphQl/Users/UserByIdDataLoader.cs +++ b/backend/src/GraphQl/Users/UserByIdDataLoader.cs @@ -1,24 +1,24 @@ -using GreenDonut; +using Database.Data; using Database.GraphQl.Entities; +using GreenDonut; using Microsoft.EntityFrameworkCore; -namespace Database.GraphQl.Users +namespace Database.GraphQl.Users; + +public sealed class UserByIdDataLoader + : EntityByIdDataLoader { - public sealed class UserByIdDataLoader - : EntityByIdDataLoader + public UserByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.Users + ) { - public UserByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.Users - ) - { - } } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserInfo.cs b/backend/src/GraphQl/Users/UserInfo.cs index 79e7a139..8c04ab68 100644 --- a/backend/src/GraphQl/Users/UserInfo.cs +++ b/backend/src/GraphQl/Users/UserInfo.cs @@ -1,20 +1,19 @@ using System.Collections.Generic; -namespace Database.GraphQl.Users -{ - public sealed record Address( - string Formatted - ); +namespace Database.GraphQl.Users; - public sealed record UserInfo( - Address? Address, - string Email, - bool EmailVerified, - string Name, - string? PhoneNumber, - bool PhoneNumberVerified, - IReadOnlyList? Roles, - string Sub, // Subject - string? Website - ); -} \ No newline at end of file +public sealed record Address( + string Formatted +); + +public sealed record UserInfo( + Address? Address, + string Email, + bool EmailVerified, + string Name, + string? PhoneNumber, + bool PhoneNumberVerified, + IReadOnlyList? Roles, + string Sub, // Subject + string? Website +); \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index dbfd7d1c..a2bb48e0 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,90 +1,82 @@ using System; using System.Linq; -using System.Net; using System.Net.Http; -using System.Net.Http.Headers; using System.Security.Claims; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using GraphQL.Client.Serializer.SystemTextJson; +using Database.Data; +using Database.Metabase; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; -using OpenIddict.Client.AspNetCore; -using Yoh.Text.Json.NamingPolicies; -namespace Database.GraphQl.Users +namespace Database.GraphQl.Users; + +[ExtendObjectType(nameof(Query))] +public sealed class UserQueries { - [ExtendObjectType(nameof(Query))] - public sealed class UserQueries + public async Task GetCurrentUserAsync( + ClaimsPrincipal claimsPrincipal, + ApplicationDbContext context, + CancellationToken cancellationToken + ) { - public async Task GetCurrentUserAsync( - ClaimsPrincipal claimsPrincipal, - Data.ApplicationDbContext context, - CancellationToken cancellationToken - ) - { - if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) - { - return null; - } - return - await context.Users.AsQueryable() + if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) return null; + return + await context.Users.AsQueryable() .SingleOrDefaultAsync( u => u.Subject == claimsPrincipal.GetClaim(ClaimTypes.NameIdentifier), cancellationToken ).ConfigureAwait(false); - } + } - public async Task GetCurrentUserInfoAsync( - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, - IResolverContext resolverContext, - CancellationToken cancellationToken - ) + public async Task GetCurrentUserInfoAsync( + [Service] AppSettings appSettings, + [Service] IHttpClientFactory httpClientFactory, + [Service] IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); + try { - var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); - try - { - return await Metabase.QueryingMetabase.QueryRest( - uri, - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - } - catch (HttpRequestException e) - { - resolverContext.ReportError( - ErrorBuilder.New() + return await QueryingMetabase.QueryRest( + uri, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + catch (HttpRequestException e) + { + resolverContext.ReportError( + ErrorBuilder.New() .SetCode("METABASE_REQUEST_FAILED") .SetPath(resolverContext.Path) .SetMessage($"Failed with status code {e.StatusCode} to request {uri}.") .SetException(e) .Build() - ); - return null; - } - catch (JsonException e) - { - resolverContext.ReportError( - ErrorBuilder.New() + ); + return null; + } + catch (JsonException e) + { + resolverContext.ReportError( + ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()).ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage($"Failed to deserialize GraphQL response of request to {uri}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to {uri}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") .SetException(e) .Build() - ); - return null; - } + ); + return null; } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserType.cs b/backend/src/GraphQl/Users/UserType.cs index 92d4d9c4..08a24a23 100644 --- a/backend/src/GraphQl/Users/UserType.cs +++ b/backend/src/GraphQl/Users/UserType.cs @@ -1,15 +1,15 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.Users +namespace Database.GraphQl.Users; + +public sealed class UserType + : EntityType { - public sealed class UserType - : EntityType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - } + base.Configure(descriptor); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/VerificationCodeQueries.cs b/backend/src/GraphQl/VerificationCodeQueries.cs index 258479ae..44f5f9aa 100644 --- a/backend/src/GraphQl/VerificationCodeQueries.cs +++ b/backend/src/GraphQl/VerificationCodeQueries.cs @@ -1,16 +1,15 @@ using HotChocolate; using HotChocolate.Types; -namespace Database.GraphQl +namespace Database.GraphQl; + +[ExtendObjectType(nameof(Query))] +public sealed class VerificationCodeQueries { - [ExtendObjectType(nameof(Query))] - public sealed class VerificationCodeQueries + public string GetVerificationCode( + [Service] AppSettings appSettings + ) { - public string GetVerificationCode( - [Service] AppSettings appSettings - ) - { - return appSettings.VerificationCode; - } + return appSettings.VerificationCode; } } \ No newline at end of file diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs index 5d5f2770..34f5e9ed 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -8,6 +8,7 @@ using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using GraphQL; using GraphQL.Client.Serializer.SystemTextJson; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; @@ -15,165 +16,159 @@ using OpenIddict.Client.AspNetCore; using Yoh.Text.Json.NamingPolicies; -namespace Database.Metabase +namespace Database.Metabase; + +public sealed class QueryingMetabase { - public sealed class QueryingMetabase - { - private static async Task ExtractBearerToken( - IHttpContextAccessor httpContextAccessor - ) + private static readonly JsonSerializerOptions GraphQlSerializerOptions = + new() { - if (httpContextAccessor.HttpContext is null) - { - return null; - } - return await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken - ).ConfigureAwait(false); - } - - private static readonly JsonSerializerOptions GraphQlSerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); - public static async Task ConstructGraphQlQuery( - string[] fileNames - ) + private static readonly JsonSerializerOptions RestSerializerOptions = + new() { - return string.Join( - Environment.NewLine, - await Task.WhenAll( - fileNames.Select(fileName => - File.ReadAllTextAsync($"Metabase/Queries/{fileName}") - ) - ).ConfigureAwait(false) - ); - } + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). + PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); - public static Task> QueryGraphQl( - AppSettings appSettings, - GraphQL.GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TGraphQlResponse : class - { - return Query>( - HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(appSettings.MetabaseHost), "/graphql/"), - MakeJsonHttpContent(request), - GraphQlSerializerOptions, - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - } + private static async Task ExtractBearerToken( + IHttpContextAccessor httpContextAccessor + ) + { + if (httpContextAccessor.HttpContext is null) return null; + return await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken + ).ConfigureAwait(false); + } - private static HttpContent MakeJsonHttpContent( - TContent content - ) - { - // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. - var result = - new ByteArrayContent( - JsonSerializer.SerializeToUtf8Bytes( - content, - GraphQlSerializerOptions - ) - ); - result.Headers.ContentType = - new MediaTypeHeaderValue("application/json"); - return result; - } + public static async Task ConstructGraphQlQuery( + string[] fileNames + ) + { + return string.Join( + Environment.NewLine, + await Task.WhenAll( + fileNames.Select(fileName => + File.ReadAllTextAsync($"Metabase/Queries/{fileName}") + ) + ).ConfigureAwait(false) + ); + } - private static readonly JsonSerializerOptions RestSerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false), }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). - PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + public static Task> QueryGraphQl( + AppSettings appSettings, + GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class + { + return Query>( + HttpMethod.Post, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(new Uri(appSettings.MetabaseHost), "/graphql/"), + MakeJsonHttpContent(request), + GraphQlSerializerOptions, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } - public static Task QueryRest( - Uri uri, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TResponse : class - { - return Query( - HttpMethod.Get, - uri, - null, - RestSerializerOptions, - httpClientFactory, - httpContextAccessor, - cancellationToken + private static ByteArrayContent MakeJsonHttpContent( + TContent content + ) + { + // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + GraphQlSerializerOptions + ) ); - } + result.Headers.ContentType = + new MediaTypeHeaderValue("application/json"); + return result; + } - private static async Task Query( - HttpMethod httpMethod, - Uri uri, - HttpContent? httpContent, - JsonSerializerOptions serializerOptions, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TResponse : class - { - using var httpClient = httpClientFactory.CreateClient(); - var bearerToken = await ExtractBearerToken(httpContextAccessor).ConfigureAwait(false); - using var httpRequestMessage = new HttpRequestMessage( - httpMethod, - uri - ); - httpRequestMessage.Content = httpContent; - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); - using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); - if (httpResponseMessage.StatusCode != HttpStatusCode.OK) - { - throw new HttpRequestException($"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); - } - // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ - using var responseStream = - await httpResponseMessage.Content + public static Task QueryRest( + Uri uri, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { + return Query( + HttpMethod.Get, + uri, + null, + RestSerializerOptions, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + + private static async Task Query( + HttpMethod httpMethod, + Uri uri, + HttpContent? httpContent, + JsonSerializerOptions serializerOptions, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { + using var httpClient = httpClientFactory.CreateClient(); + var bearerToken = await ExtractBearerToken(httpContextAccessor).ConfigureAwait(false); + using var httpRequestMessage = new HttpRequestMessage( + httpMethod, + uri + ); + httpRequestMessage.Content = httpContent; + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + using var httpResponseMessage = + await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + throw new HttpRequestException( + $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, + httpResponseMessage.StatusCode); + // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ + using var responseStream = + await httpResponseMessage.Content .ReadAsStreamAsync(cancellationToken) .ConfigureAwait(false); - // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); - var deserializedResponse = - await JsonSerializer.DeserializeAsync( - responseStream, - serializerOptions, - cancellationToken - ).ConfigureAwait(false); - if (deserializedResponse is null) - { - throw new JsonException("Failed to deserialize the GraphQL response."); - } - return deserializedResponse; - } + // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); + var deserializedResponse = + await JsonSerializer.DeserializeAsync( + responseStream, + serializerOptions, + cancellationToken + ).ConfigureAwait(false); + if (deserializedResponse is null) throw new JsonException("Failed to deserialize the GraphQL response."); + return deserializedResponse; } } \ No newline at end of file diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs index caa7451b..ce093f4c 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -8,6 +8,8 @@ namespace Database.Migrations /// public partial class OpenIddict : Migration { + private static readonly string[] _applicationIdStatusSubjectAndTypeIndexColumns = new[] { "ApplicationId", "Status", "Subject", "Type" }; + /// protected override void Up(MigrationBuilder migrationBuilder) { @@ -128,7 +130,7 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", schema: "database", table: "OpenIddictAuthorizations", - columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + columns: _applicationIdStatusSubjectAndTypeIndexColumns); migrationBuilder.CreateIndex( name: "IX_OpenIddictScopes_Name", @@ -141,7 +143,7 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", schema: "database", table: "OpenIddictTokens", - columns: new[] { "ApplicationId", "Status", "Subject", "Type" }); + columns: _applicationIdStatusSubjectAndTypeIndexColumns); migrationBuilder.CreateIndex( name: "IX_OpenIddictTokens_AuthorizationId", diff --git a/backend/src/Program.cs b/backend/src/Program.cs index d1369f19..21ec9267 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -1,177 +1,187 @@ using System; +using System.Globalization; +using System.IO; using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; +using Database.Data; +using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using System.IO; using Serilog; using Serilog.Events; using Serilog.Formatting.Compact; -using Microsoft.AspNetCore.Builder; +using ILogger = Microsoft.Extensions.Logging.ILogger; +using Log = Serilog.Log; -namespace Database +namespace Database; + +public static partial class LoggerExtensions { - public sealed class Program - { - public const string TestEnvironment = "test"; + [LoggerMessage( + EventId = 0, + Level = LogLevel.Error, + Message = "An error occurred creating and seeding the database.")] + public static partial void FailedToCreateAndSeedDatabase( + this ILogger logger, + // The first exception is implicitly taken care of as detailed in + // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy + Exception exception + ); +} - public static async Task Main( - string[] commandLineArguments - ) +public sealed class Program +{ + public const string TestEnvironment = "test"; + + public static async Task Main( + string[] commandLineArguments + ) + { + var environment = + Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") + ?? throw new ArgumentException("Unknown enrivornment."); + // https://github.com/serilog/serilog-aspnetcore#two-stage-initialization + ConfigureBootstrapLogging(environment); + try { - var environment = - Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") - ?? throw new Exception("Unknown enrivornment."); - // https://github.com/serilog/serilog-aspnetcore#two-stage-initialization - ConfigureBootstrapLogging(environment); - try + Log.Information("Starting web host"); + // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication + var builder = CreateWebApplicationBuilder(commandLineArguments); + var startup = new Startup(builder.Environment, builder.Configuration); + startup.ConfigureServices(builder.Services); + var application = builder.Build(); + startup.Configure(application); + using (var scope = application.Services.CreateScope()) { - Log.Information("Starting web host"); - // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication - var builder = CreateWebApplicationBuilder(commandLineArguments); - var startup = new Startup(builder.Environment, builder.Configuration); - startup.ConfigureServices(builder.Services); - var application = builder.Build(); - startup.Configure(application); - using (var scope = application.Services.CreateScope()) - { - if (application.Environment.IsDevelopment()) - { - // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDbIfNotExists(scope.ServiceProvider).ConfigureAwait(false); - } - } - application.Run(); - return 0; + if (application.Environment.IsDevelopment()) + // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data + await CreateAndSeedDbIfNotExists(scope.ServiceProvider).ConfigureAwait(false); } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly"); - return 1; - } - finally - { - Log.CloseAndFlush(); - } - } - private static void ConfigureBootstrapLogging( - string environment - ) + application.Run(); + return 0; + } + catch (Exception ex) { - var configuration = new LoggerConfiguration() - .MinimumLevel.Override("Microsoft", LogEventLevel.Information); - ConfigureLogging(configuration, environment); - Log.Logger = configuration.CreateBootstrapLogger(); + Log.Fatal(ex, "Host terminated unexpectedly"); + return 1; } - - private static void ConfigureLogging( - LoggerConfiguration configuration, - string environment - ) + finally { - configuration - .Enrich.FromLogContext() - .Enrich.WithMachineName() - .Enrich.WithProperty("Environment", environment) - .WriteTo.Console() - .WriteTo.File( - formatter: new CompactJsonFormatter(), - path: "./logs/serilog.json", - fileSizeLimitBytes: 1073741824, // 1 GB - rollingInterval: RollingInterval.Day, - rollOnFileSizeLimit: true, - retainedFileCountLimit: 7); - if (environment != "production") - { - configuration.WriteTo.Debug(); - } + Log.CloseAndFlush(); } + } - private static async Task CreateAndSeedDbIfNotExists( - IServiceProvider services - ) + private static void ConfigureBootstrapLogging( + string environment + ) + { + var configuration = new LoggerConfiguration() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information); + ConfigureLogging(configuration, environment); + Log.Logger = configuration.CreateBootstrapLogger(); + } + + private static void ConfigureLogging( + LoggerConfiguration configuration, + string environment + ) + { + configuration + .Enrich.FromLogContext() + .Enrich.WithMachineName() + .Enrich.WithProperty("Environment", environment) + .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) + .WriteTo.File( + new CompactJsonFormatter(), + "./logs/serilog.json", + fileSizeLimitBytes: 1073741824, // 1 GB + rollingInterval: RollingInterval.Day, + rollOnFileSizeLimit: true, + retainedFileCountLimit: 7); + if (environment != "production") configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); + } + + private static async Task CreateAndSeedDbIfNotExists( + IServiceProvider services + ) + { + try { - try - { - using var dbContext = - services.GetRequiredService>() - .CreateDbContext(); - if (dbContext.Database.EnsureCreated()) - { - await Data.DbSeeder.DoAsync(services).ConfigureAwait(false); - } - } - catch (Exception exception) - { - var logger = services.GetRequiredService>(); - logger.LogError(exception, "An error occurred creating and seeding the database."); - } + using var dbContext = + services.GetRequiredService>() + .CreateDbContext(); + if (dbContext.Database.EnsureCreated()) await DbSeeder.DoAsync(services).ConfigureAwait(false); + } + catch (Exception exception) + { + var logger = services.GetRequiredService>(); + logger.FailedToCreateAndSeedDatabase(exception); } + } - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host - private static WebApplicationBuilder CreateWebApplicationBuilder( - string[] commandLineArguments - ) + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host + private static WebApplicationBuilder CreateWebApplicationBuilder( + string[] commandLineArguments + ) + { + var builder = WebApplication.CreateBuilder( + new WebApplicationOptions + { + Args = commandLineArguments, + ContentRootPath = Directory.GetCurrentDirectory() + } + ); + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ + ConfigureAppConfiguration( + builder.Configuration, + builder.Environment, + commandLineArguments + ); + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection + // https://github.com/dotnet/aspnetcore/issues/38334#issuecomment-967709919 + builder.Host.UseDefaultServiceProvider(_ => { - var builder = WebApplication.CreateBuilder( - new WebApplicationOptions - { - Args = commandLineArguments, - ContentRootPath = Directory.GetCurrentDirectory() - } - ); - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ - ConfigureAppConfiguration( - builder.Configuration, - builder.Environment, - commandLineArguments + _.ValidateScopes = true; + _.ValidateOnBuild = true; + }); + // https://github.com/serilog/serilog-aspnetcore#instructions + builder.Host.UseSerilog((webHostBuilderContext, loggerConfiguration) => + { + ConfigureLogging( + loggerConfiguration, + webHostBuilderContext.HostingEnvironment.EnvironmentName ); - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection - // https://github.com/dotnet/aspnetcore/issues/38334#issuecomment-967709919 - builder.Host.UseDefaultServiceProvider(_ => - { - _.ValidateScopes = true; - _.ValidateOnBuild = true; - }); - // https://github.com/serilog/serilog-aspnetcore#instructions - builder.Host.UseSerilog((webHostBuilderContext, loggerConfiguration) => - { - ConfigureLogging( - loggerConfiguration, - webHostBuilderContext.HostingEnvironment.EnvironmentName - ); - loggerConfiguration - .ReadFrom.Configuration(webHostBuilderContext.Configuration); - }); - return builder; - } + loggerConfiguration + .ReadFrom.Configuration(webHostBuilderContext.Configuration); + }); + return builder; + } - public static void ConfigureAppConfiguration( - IConfigurationBuilder configuration, - IHostEnvironment environment, - string[] commandLineArguments + public static void ConfigureAppConfiguration( + IConfigurationBuilder configuration, + IHostEnvironment environment, + string[] commandLineArguments + ) + { + configuration.Sources.Clear(); + configuration + .SetBasePath(environment.ContentRootPath) + .AddJsonFile( + "appsettings.json", + false, + !environment.IsEnvironment(TestEnvironment) ) - { - configuration.Sources.Clear(); - configuration - .SetBasePath(environment.ContentRootPath) - .AddJsonFile( - "appsettings.json", - optional: false, - reloadOnChange: !environment.IsEnvironment(TestEnvironment) - ) - .AddJsonFile( - $"appsettings.{environment.EnvironmentName}.json", - optional: false, - reloadOnChange: !environment.IsEnvironment(TestEnvironment) - ) - .AddEnvironmentVariables() - .AddEnvironmentVariables(prefix: "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables - .AddCommandLine(commandLineArguments); - } + .AddJsonFile( + $"appsettings.{environment.EnvironmentName}.json", + false, + !environment.IsEnvironment(TestEnvironment) + ) + .AddEnvironmentVariables() + .AddEnvironmentVariables( + "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables + .AddCommandLine(commandLineArguments); } } \ No newline at end of file diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index f1bfef48..6717d9ed 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -4,63 +4,77 @@ using Microsoft.Extensions.Logging; using MimeKit; -namespace Database.Services +namespace Database.Services; + +public static partial class Log +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Debug, + Message = "About to send email to `{Recipient}` with subject `{Subject}` and body `{Body}`")] + public static partial void AboutToSendEmail( + this ILogger logger, + (string name, string address) Recipient, + string Subject, + string Body + ); +} + +public sealed class EmailSender + : IEmailSender { - public sealed class EmailSender - : IEmailSender + private readonly ILogger _logger; + private readonly string _smtpHost; + private readonly int _smtpPort; + + public EmailSender( + string smtpHost, + int smtpPort, + ILogger logger + ) { - private readonly string _smtpHost; - private readonly int _smtpPort; - private readonly ILogger _logger; + _smtpHost = smtpHost; + _smtpPort = smtpPort; + _logger = logger; + } - public EmailSender( - string smtpHost, - int smtpPort, - ILogger logger - ) + public Task SendAsync( + (string name, string address) recipient, + string subject, + string body + ) + { + _logger.AboutToSendEmail(recipient, subject, body); + var message = new MimeMessage(); + message.From.Add( + new MailboxAddress( + "Database", + "database@solarbuildingenvelopes.com" + ) + ); + message.To.Add( + new MailboxAddress( + recipient.name, + recipient.address + ) + ); + message.Subject = subject; + message.Body = new TextPart("plain") { - _smtpHost = smtpHost; - _smtpPort = smtpPort; - _logger = logger; - } - - public Task SendAsync( - (string name, string address) to, - string subject, - string body - ) + Text = body + }; + using (var client = new SmtpClient()) { - _logger.LogDebug("About to send email to `{To}` with subject `{Subject}` and body `{Body}`", to, subject, body); - var message = new MimeMessage(); - message.From.Add( - new MailboxAddress( - "Database", - "database@solarbuildingenvelopes.com" - ) + client.Connect( + _smtpHost, + _smtpPort, + SecureSocketOptions.StartTlsWhenAvailable ); - message.To.Add( - new MailboxAddress( - to.name, - to.address - ) - ); - message.Subject = subject; - message.Body = new TextPart("plain") - { - Text = body - }; - using (var client = new SmtpClient()) - { - client.Connect( - _smtpHost, - _smtpPort, - SecureSocketOptions.StartTlsWhenAvailable - ); - // client.Authenticate("joey", "password"); - client.Send(message); - client.Disconnect(quit: true); - } - return Task.FromResult(0); + // client.Authenticate("joey", "password"); + client.Send(message); + client.Disconnect(true); } + + return Task.FromResult(0); } } \ No newline at end of file diff --git a/backend/src/Services/IEmailSender.cs b/backend/src/Services/IEmailSender.cs index 10845501..1c7c7130 100644 --- a/backend/src/Services/IEmailSender.cs +++ b/backend/src/Services/IEmailSender.cs @@ -1,13 +1,12 @@ using System.Threading.Tasks; -namespace Database.Services +namespace Database.Services; + +public interface IEmailSender { - public interface IEmailSender - { - public Task SendAsync( - (string name, string address) to, - string subject, - string body - ); - } + public Task SendAsync( + (string name, string address) recipient, + string subject, + string body + ); } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7d32372e..5e54a44e 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -4,7 +4,10 @@ using System.Text.Json; using System.Threading.Tasks; using Database.Configuration; +using Database.Data; using Database.Data.Extensions; +using Database.Enumerations; +using Database.Services; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; @@ -17,56 +20,55 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Npgsql; using Serilog; -namespace Database +namespace Database; + +public sealed class Startup { - public sealed class Startup - { - private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; + private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; + private readonly AppSettings _appSettings; - private readonly IWebHostEnvironment _environment; - private readonly AppSettings _appSettings; + private readonly IWebHostEnvironment _environment; - public Startup( - IWebHostEnvironment environment, - IConfiguration configuration - ) - { - _environment = environment; - _appSettings = configuration.Get() ?? throw new Exception("Failed to get application settings from configuration."); - } + public Startup( + IWebHostEnvironment environment, + IConfiguration configuration + ) + { + _environment = environment; + _appSettings = configuration.Get() ?? + throw new ArgumentException("Failed to get application settings from configuration."); + } - public void ConfigureServices(IServiceCollection services) - { - AuthConfiguration.ConfigureServices(services, _environment, _appSettings); - GraphQlConfiguration.ConfigureServices(services, _environment, _appSettings); - ConfigureDatabaseServices(services); - ConfigureMessageSenderServices(services); - ConfigureRequestResponseServices(services); - ConfigureSessionServices(services); - services.AddAntiforgery(_ => - { - _.HeaderName = "X-XSRF-TOKEN"; - }); - services - .AddDataProtection() - .PersistKeysToDbContext(); - services.AddHttpClient(); - services.AddHttpContextAccessor(); - services - .AddHealthChecks() - .AddDbContextCheck(); - services.AddSingleton(_appSettings); - services.AddSingleton(_environment); - // services.AddDatabaseDeveloperPageExceptionFilter(); - } + public void ConfigureServices(IServiceCollection services) + { + AuthConfiguration.ConfigureServices(services, _environment, _appSettings); + GraphQlConfiguration.ConfigureServices(services, _environment, _appSettings); + ConfigureDatabaseServices(services); + ConfigureMessageSenderServices(services); + ConfigureRequestResponseServices(services); + ConfigureSessionServices(services); + services.AddAntiforgery(_ => { _.HeaderName = "X-XSRF-TOKEN"; }); + services + .AddDataProtection() + .PersistKeysToDbContext(); + services.AddHttpClient(); + services.AddHttpContextAccessor(); + services + .AddHealthChecks() + .AddDbContextCheck(); + services.AddSingleton(_appSettings); + services.AddSingleton(_environment); + // services.AddDatabaseDeveloperPageExceptionFilter(); + } - private static void ConfigureRequestResponseServices(IServiceCollection services) - { - // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forwarded-headers-middleware-order - services.Configure(_ => + private static void ConfigureRequestResponseServices(IServiceCollection services) + { + // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forwarded-headers-middleware-order + services.Configure(_ => { // TODO _.AllowedHosts = ... _.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; @@ -74,219 +76,207 @@ private static void ConfigureRequestResponseServices(IServiceCollection services _.KnownNetworks.Clear(); _.KnownProxies.Clear(); } - ); - services.AddCors(_ => - _.AddPolicy( - name: GraphQlCorsPolicy, - policy => - policy + ); + services.AddCors(_ => + _.AddPolicy( + GraphQlCorsPolicy, + policy => + policy .AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod() - ) - ); - services.AddControllersWithViews(); - } + ) + ); + services.AddControllersWithViews(); + } - private void ConfigureMessageSenderServices(IServiceCollection services) - { - services.AddTransient(serviceProvider => - new Services.EmailSender( - _appSettings.Email.SmtpHost, - _appSettings.Email.SmtpPort, - serviceProvider.GetRequiredService>() - ) - ); - } + private void ConfigureMessageSenderServices(IServiceCollection services) + { + services.AddTransient(serviceProvider => + new EmailSender( + _appSettings.Email.SmtpHost, + _appSettings.Email.SmtpPort, + serviceProvider.GetRequiredService>() + ) + ); + } - private static void ConfigureSessionServices(IServiceCollection services) + private static void ConfigureSessionServices(IServiceCollection services) + { + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state + services.AddDistributedMemoryCache(); + services.AddSession(options => { - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state - services.AddDistributedMemoryCache(); - services.AddSession(options => - { - // Set a short timeout for easy testing. - options.IdleTimeout = TimeSpan.FromSeconds(10); - options.Cookie.HttpOnly = true; - // Make the session cookie essential - options.Cookie.IsEssential = true; - }); - } + // Set a short timeout for easy testing. + options.IdleTimeout = TimeSpan.FromSeconds(10); + options.Cookie.HttpOnly = true; + // Make the session cookie essential + options.Cookie.IsEssential = true; + }); + } - private void ConfigureDatabaseServices(IServiceCollection services) - { - services.AddPooledDbContextFactory(options => - { - var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - dataSourceBuilder.MapEnum(); - options + private void ConfigureDatabaseServices(IServiceCollection services) + { + services.AddPooledDbContextFactory(options => + { + var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); + // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum + dataSourceBuilder.MapEnum(); + options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) .UseOpenIddict(); - if (!_environment.IsProduction()) - { - options + if (!_environment.IsProduction()) + options .EnableSensitiveDataLogging() .EnableDetailedErrors(); - } - } - ); - // Database context as services are used by `Identity`. - services.AddDbContext( - (serviceProvider, options) => - { - if (!_environment.IsProduction()) - { - options + } + ); + // Database context as services are used by `Identity`. + services.AddDbContext( + (serviceProvider, options) => + { + if (!_environment.IsProduction()) + options .EnableSensitiveDataLogging() .EnableDetailedErrors(); - } - serviceProvider - .GetRequiredService>() + serviceProvider + .GetRequiredService>() .CreateDbContext(); - }, - ServiceLifetime.Transient - ); + }, + ServiceLifetime.Transient + ); + } + + public void Configure(WebApplication app) + { + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ + if (_environment.IsDevelopment() || _environment.IsEnvironment("test")) + { + app.UseDeveloperExceptionPage(); + // app.UseMigrationsEndPoint(); + // Forwarded Headers Middleware must run before other middleware except diagnostics and error handling middleware. In particular before HSTS middleware. + // See https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#other-proxy-server-and-load-balancer-scenarios + app.UseForwardedHeaders(); + } + else + { + app.UseExceptionHandler("/Error"); + // Forwarded Headers Middleware must run before other middleware except diagnostics and error handling middleware. In particular before HSTS middleware. + // See https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#other-proxy-server-and-load-balancer-scenarios + app.UseForwardedHeaders(); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + // ASP.NET advices to not use HSTS for APIs, see the warning on + // https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl + // app.UseHsts(); // Done by NGINX, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ } - public void Configure(WebApplication app) + // app.UseStatusCodePages(); + // app.UseHttpsRedirection(); // Done by NGINX + app.UseSerilogRequestLogging(); + app.UseStaticFiles(); + app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) + app.UseRouting(); + // TODO Do we really want this? See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-5.0 + app.UseRequestLocalization(_ => { - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ - if (_environment.IsDevelopment() || _environment.IsEnvironment("test")) - { - app.UseDeveloperExceptionPage(); - // app.UseMigrationsEndPoint(); - // Forwarded Headers Middleware must run before other middleware except diagnostics and error handling middleware. In particular before HSTS middleware. - // See https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#other-proxy-server-and-load-balancer-scenarios - app.UseForwardedHeaders(); - } - else - { - app.UseExceptionHandler("/Error"); - // Forwarded Headers Middleware must run before other middleware except diagnostics and error handling middleware. In particular before HSTS middleware. - // See https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#other-proxy-server-and-load-balancer-scenarios - app.UseForwardedHeaders(); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - // ASP.NET advices to not use HSTS for APIs, see the warning on - // https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl - // app.UseHsts(); // Done by NGINX, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ - } - // app.UseStatusCodePages(); - // app.UseHttpsRedirection(); // Done by NGINX - app.UseSerilogRequestLogging(); - app.UseStaticFiles(); - app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) - app.UseRouting(); - // TODO Do we really want this? See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-5.0 - app.UseRequestLocalization(_ => - { - _.AddSupportedCultures("en-US", "de-DE"); - _.AddSupportedUICultures("en-US", "de-DE"); - _.SetDefaultCulture("en-US"); - }); - app.UseCors(); - // app.UseCertificateForwarding(); // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0#other-web-proxies - app.UseAuthentication(); - app.UseAuthorization(); - app.UseSession(); - // app.UseResponseCompression(); // Done by Nginx - // app.UseResponseCaching(); // Done by Nginx - /* app.UseWebSockets(); */ - app.MapGraphQL() - .WithOptions( - // https://chillicream.com/docs/hotchocolate/server/middleware - new GraphQLServerOptions - { - EnableSchemaRequests = true, - EnableGetRequests = false, - // AllowedGetOperations = AllowedGetOperations.Query - EnableMultipartRequests = false, - Tool = { - DisableTelemetry = true, - Enable = true, // _environment.IsDevelopment() - IncludeCookies = false, - GraphQLEndpoint = "/graphql", - HttpMethod = DefaultHttpMethod.Post, - Title = "GraphQL" - } - } - ) - .RequireCors(GraphQlCorsPolicy); - app.MapControllers(); - app.MapHealthChecks("/health", - new HealthCheckOptions + _.AddSupportedCultures("en-US", "de-DE"); + _.AddSupportedUICultures("en-US", "de-DE"); + _.SetDefaultCulture("en-US"); + }); + app.UseCors(); + // app.UseCertificateForwarding(); // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0#other-web-proxies + app.UseAuthentication(); + app.UseAuthorization(); + app.UseSession(); + // app.UseResponseCompression(); // Done by Nginx + // app.UseResponseCaching(); // Done by Nginx + /* app.UseWebSockets(); */ + app.MapGraphQL() + .WithOptions( + // https://chillicream.com/docs/hotchocolate/server/middleware + new GraphQLServerOptions { - ResponseWriter = WriteJsonResponse + EnableSchemaRequests = true, + EnableGetRequests = false, + // AllowedGetOperations = AllowedGetOperations.Query + EnableMultipartRequests = false, + Tool = + { + DisableTelemetry = true, + Enable = true, // _environment.IsDevelopment() + IncludeCookies = false, + GraphQLEndpoint = "/graphql", + HttpMethod = DefaultHttpMethod.Post, + Title = "GraphQL" + } } - ); - } + ) + .RequireCors(GraphQlCorsPolicy); + app.MapControllers(); + app.MapHealthChecks("/health", + new HealthCheckOptions + { + ResponseWriter = WriteJsonResponse + } + ); + } - // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output - private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) + // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output + private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) + { + context.Response.ContentType = "application/json; charset=utf-8"; + var options = new JsonWriterOptions { Indented = true }; + using var memoryStream = new MemoryStream(); + using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) { - context.Response.ContentType = "application/json; charset=utf-8"; - var options = new JsonWriterOptions { Indented = true }; - using var memoryStream = new MemoryStream(); - using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) + jsonWriter.WriteStartObject(); + jsonWriter.WriteString("status", healthReport.Status.ToString()); + jsonWriter.WriteString("duration", healthReport.TotalDuration.ToString()); + jsonWriter.WriteStartObject("results"); + foreach (var healthReportEntry in healthReport.Entries) { - jsonWriter.WriteStartObject(); - jsonWriter.WriteString("status", healthReport.Status.ToString()); - jsonWriter.WriteString("duration", healthReport.TotalDuration.ToString()); - jsonWriter.WriteStartObject("results"); - foreach (var healthReportEntry in healthReport.Entries) + jsonWriter.WriteStartObject(healthReportEntry.Key); + jsonWriter.WriteString("status", + healthReportEntry.Value.Status.ToString()); + jsonWriter.WriteString("description", + healthReportEntry.Value.Description); + jsonWriter.WriteString("duration", + healthReportEntry.Value.Duration.ToString()); + jsonWriter.WriteStartArray("tags"); + foreach (var tag in healthReportEntry.Value.Tags) jsonWriter.WriteStringValue(tag); + jsonWriter.WriteEndArray(); + var exception = healthReportEntry.Value.Exception; + if (exception is not null) { - jsonWriter.WriteStartObject(healthReportEntry.Key); - jsonWriter.WriteString("status", - healthReportEntry.Value.Status.ToString()); - jsonWriter.WriteString("description", - healthReportEntry.Value.Description); - jsonWriter.WriteString("duration", - healthReportEntry.Value.Duration.ToString()); - jsonWriter.WriteStartArray("tags"); - foreach (var tag in healthReportEntry.Value.Tags) - { - jsonWriter.WriteStringValue(tag); - } - jsonWriter.WriteEndArray(); - var exception = healthReportEntry.Value.Exception; - if (exception is not null) - { - jsonWriter.WriteStartObject("exception"); - jsonWriter.WriteString("message", exception.Message); - if (exception.StackTrace is not null) - { - jsonWriter.WriteString("stackTrace", exception.StackTrace); - } - if (exception.InnerException is not null) - { - jsonWriter.WriteString("innerException", exception.InnerException.ToString()); - } - if (exception.Source is not null) - { - jsonWriter.WriteString("source", exception.Source); - } - if (exception.TargetSite is not null) - { - jsonWriter.WriteString("targetSite", exception.TargetSite.ToString()); - } - jsonWriter.WriteEndObject(); - } - jsonWriter.WriteStartObject("data"); - foreach (var item in healthReportEntry.Value.Data) - { - jsonWriter.WritePropertyName(item.Key); - JsonSerializer.Serialize(jsonWriter, item.Value, - item.Value?.GetType() ?? typeof(object)); - } - jsonWriter.WriteEndObject(); + jsonWriter.WriteStartObject("exception"); + jsonWriter.WriteString("message", exception.Message); + if (exception.StackTrace is not null) jsonWriter.WriteString("stackTrace", exception.StackTrace); + if (exception.InnerException is not null) + jsonWriter.WriteString("innerException", exception.InnerException.ToString()); + if (exception.Source is not null) jsonWriter.WriteString("source", exception.Source); + if (exception.TargetSite is not null) + jsonWriter.WriteString("targetSite", exception.TargetSite.ToString()); jsonWriter.WriteEndObject(); } + + jsonWriter.WriteStartObject("data"); + foreach (var item in healthReportEntry.Value.Data) + { + jsonWriter.WritePropertyName(item.Key); + JsonSerializer.Serialize(jsonWriter, item.Value, + item.Value?.GetType() ?? typeof(object)); + } + jsonWriter.WriteEndObject(); jsonWriter.WriteEndObject(); } - return context.Response.WriteAsync( - Encoding.UTF8.GetString(memoryStream.ToArray())); + + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndObject(); } + + return context.Response.WriteAsync( + Encoding.UTF8.GetString(memoryStream.ToArray())); } } \ No newline at end of file diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 1a93ac73..6fb8eadd 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -11,259 +11,238 @@ using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; -namespace Database.Utilities +namespace Database.Utilities; + +public static class FileHelpers { - public static class FileHelpers + // If you require a check on specific characters in the IsValidFileExtensionAndSignature + // method, supply the characters in the _allowedChars field. + // private static readonly byte[] _allowedChars = Array.Empty(); + // For more file signatures, see the File Signatures Database (https://www.filesignatures.net/) + // and the official specifications for the file types you wish to add. + private static readonly Dictionary> _fileSignature = new() { - // If you require a check on specific characters in the IsValidFileExtensionAndSignature - // method, supply the characters in the _allowedChars field. - // private static readonly byte[] _allowedChars = Array.Empty(); - // For more file signatures, see the File Signatures Database (https://www.filesignatures.net/) - // and the official specifications for the file types you wish to add. - private static readonly Dictionary> _fileSignature = new() + { ".gif", new List { "GIF8"u8.ToArray() } }, + { ".png", new List { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } }, { - { ".gif", new List { "GIF8"u8.ToArray() } }, - { ".png", new List { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } }, + ".jpeg", + new List { - ".jpeg", - new List - { - new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }, - } - }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 } + } + }, + { + ".jpg", + new List { - ".jpg", - new List - { - new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 }, - } - }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, + new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 } + } + }, + { + ".zip", + new List { - ".zip", - new List - { - new byte[] { 0x50, 0x4B, 0x03, 0x04 }, - "PKLITE"u8.ToArray(), - "PKSpX"u8.ToArray(), - new byte[] { 0x50, 0x4B, 0x05, 0x06 }, - new byte[] { 0x50, 0x4B, 0x07, 0x08 }, - "WinZip"u8.ToArray(), - } - }, - }; + new byte[] { 0x50, 0x4B, 0x03, 0x04 }, + "PKLITE"u8.ToArray(), + "PKSpX"u8.ToArray(), + new byte[] { 0x50, 0x4B, 0x05, 0x06 }, + new byte[] { 0x50, 0x4B, 0x07, 0x08 }, + "WinZip"u8.ToArray() + } + } + }; + + // **WARNING!** + // In the following file processing methods, the file's content isn't scanned. + // In most production scenarios, an anti-virus/anti-malware scanner API is + // used on the file before making the file available to users or other + // systems. For more information, see the topic that accompanies this sample + // app. + + public static async Task ProcessFormFile(IFormFile formFile, + ModelStateDictionary modelState, string[] permittedExtensions, + long sizeLimit) + { + var fieldDisplayName = string.Empty; + + // Use reflection to obtain the display name for the model + // property associated with this IFormFile. If a display + // name isn't found, error messages simply won't show + // a display name. + var property = + typeof(T).GetProperty( + formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); + + if (property != null) + if (property.GetCustomAttribute(typeof(DisplayAttribute)) is + DisplayAttribute displayAttribute) + fieldDisplayName = $"{displayAttribute.Name} "; + + // Don't trust the file name sent by the client. To display + // the file name, HTML-encode the value. + var trustedFileNameForDisplay = WebUtility.HtmlEncode( + formFile.FileName); + + // Check the file length. This check doesn't catch files that only have + // a BOM as their content. + if (formFile.Length == 0) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - // **WARNING!** - // In the following file processing methods, the file's content isn't scanned. - // In most production scenarios, an anti-virus/anti-malware scanner API is - // used on the file before making the file available to users or other - // systems. For more information, see the topic that accompanies this sample - // app. + return Array.Empty(); + } - public static async Task ProcessFormFile(IFormFile formFile, - ModelStateDictionary modelState, string[] permittedExtensions, - long sizeLimit) + if (formFile.Length > sizeLimit) { - var fieldDisplayName = string.Empty; - - // Use reflection to obtain the display name for the model - // property associated with this IFormFile. If a display - // name isn't found, error messages simply won't show - // a display name. - var property = - typeof(T).GetProperty( - formFile.Name[(formFile.Name.IndexOf(".", - StringComparison.Ordinal) + 1)..]); + var megabyteSizeLimit = sizeLimit / 1048576; // 1 MiB = 1024^2 Byte = 1048576 Byte + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " + + $"{megabyteSizeLimit:N1} MB."); - if (property != null) - { - if (property.GetCustomAttribute(typeof(DisplayAttribute)) is - DisplayAttribute displayAttribute) - { - fieldDisplayName = $"{displayAttribute.Name} "; - } - } + return Array.Empty(); + } - // Don't trust the file name sent by the client. To display - // the file name, HTML-encode the value. - var trustedFileNameForDisplay = WebUtility.HtmlEncode( - formFile.FileName); + try + { + using var memoryStream = new MemoryStream(); + await formFile.CopyToAsync(memoryStream); - // Check the file length. This check doesn't catch files that only have - // a BOM as their content. - if (formFile.Length == 0) - { + // Check the content length in case the file's only + // content was a BOM and the content is actually + // empty after removing the BOM. + if (memoryStream.Length == 0) modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - return Array.Empty(); - } - - if (formFile.Length > sizeLimit) - { - var megabyteSizeLimit = sizeLimit / 1048576; // 1 MiB = 1024^2 Byte = 1048576 Byte - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " + - $"{megabyteSizeLimit:N1} MB."); - - return Array.Empty(); - } - - try - { - using var memoryStream = new MemoryStream(); - await formFile.CopyToAsync(memoryStream); - - // Check the content length in case the file's only - // content was a BOM and the content is actually - // empty after removing the BOM. - if (memoryStream.Length == 0) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - } - - if (!IsValidFileExtensionAndSignature( + if (!IsValidFileExtensionAndSignature( formFile.FileName, memoryStream, permittedExtensions)) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) file " + - "type isn't permitted or the file's signature " + - "doesn't match the file's extension."); - } - else - { - return memoryStream.ToArray(); - } - } - catch (Exception ex) - { modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " + - $"Please contact the Help Desk for support. Error: {ex.HResult}"); - // Log the exception - } - - return Array.Empty(); + $"{fieldDisplayName}({trustedFileNameForDisplay}) file " + + "type isn't permitted or the file's signature " + + "doesn't match the file's extension."); + else + return memoryStream.ToArray(); + } + catch (Exception ex) + { + modelState.AddModelError(formFile.Name, + $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " + + $"Please contact the Help Desk for support. Error: {ex.HResult}"); + // Log the exception } - public static async Task ProcessStreamedFile( - MultipartSection section, ContentDispositionHeaderValue contentDisposition, - ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) + return Array.Empty(); + } + + public static async Task ProcessStreamedFile( + MultipartSection section, ContentDispositionHeaderValue contentDisposition, + ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) + { + try { - try - { - using var memoryStream = new MemoryStream(); - await section.Body.CopyToAsync(memoryStream); + using var memoryStream = new MemoryStream(); + await section.Body.CopyToAsync(memoryStream); - // Check if the file is empty or exceeds the size limit. - if (memoryStream.Length == 0) - { - modelState.AddModelError("File", "The file is empty."); - } - else if (memoryStream.Length > sizeLimit) - { - var megabyteSizeLimit = sizeLimit / 1048576; - modelState.AddModelError("File", - $"The file exceeds {megabyteSizeLimit:N1} MB."); - } - else if (!IsValidFileExtensionAndSignature( - contentDisposition.FileName.Value, memoryStream, - permittedExtensions)) - { - modelState.AddModelError("File", - "The file type isn't permitted or the file's " + - "signature doesn't match the file's extension."); - } - else - { - return memoryStream.ToArray(); - } - } - catch (Exception ex) + // Check if the file is empty or exceeds the size limit. + if (memoryStream.Length == 0) { - modelState.AddModelError("File", - "The upload failed. Please contact the Help Desk " + - $" for support. Error: {ex.HResult}"); - // Log the exception + modelState.AddModelError("File", "The file is empty."); } - - return Array.Empty(); - } - - private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) - { - if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) + else if (memoryStream.Length > sizeLimit) { - return false; + var megabyteSizeLimit = sizeLimit / 1048576; + modelState.AddModelError("File", + $"The file exceeds {megabyteSizeLimit:N1} MB."); } - - var ext = Path.GetExtension(fileName).ToLowerInvariant(); - - if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) + else if (!IsValidFileExtensionAndSignature( + contentDisposition.FileName.Value, memoryStream, + permittedExtensions)) { - return false; + modelState.AddModelError("File", + "The file type isn't permitted or the file's " + + "signature doesn't match the file's extension."); } - - data.Position = 0; - - using var reader = new BinaryReader(data); - // TODO Uncomment the following but allow UTF-X, at least for `.txt` files - // if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn")) - // { - // if (_allowedChars.Length == 0) - // { - // // Limits characters to ASCII encoding. - // for (var i = 0; i < data.Length; i++) - // { - // if (reader.ReadByte() > sbyte.MaxValue) - // { - // return false; - // } - // } - // } - // else - // { - // // Limits characters to ASCII encoding and - // // values of the _allowedChars array. - // for (var i = 0; i < data.Length; i++) - // { - // var b = reader.ReadByte(); - // if (b > sbyte.MaxValue || - // !_allowedChars.Contains(b)) - // { - // return false; - // } - // } - // } - - // return true; - // } - - // Uncomment the following code block if you must permit - // files whose signature isn't provided in the _fileSignature - // dictionary. We recommend that you add file signatures - // for files (when possible) for all file types you intend - // to allow on the system and perform the file signature - // check. - if (!_fileSignature.ContainsKey(ext)) + else { - return true; + return memoryStream.ToArray(); } + } + catch (Exception ex) + { + modelState.AddModelError("File", + "The upload failed. Please contact the Help Desk " + + $" for support. Error: {ex.HResult}"); + // Log the exception + } - // File signature check - // -------------------- - // With the file signatures provided in the _fileSignature - // dictionary, the following code tests the input content's - // file signature. - var signatures = _fileSignature[ext]; - var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); + return Array.Empty(); + } - return signatures.Any(signature => - headerBytes.Take(signature.Length).SequenceEqual(signature)); - } + private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) + { + if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) return false; + + var ext = Path.GetExtension(fileName).ToLowerInvariant(); + + if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) return false; + + data.Position = 0; + + using var reader = new BinaryReader(data); + // TODO Uncomment the following but allow UTF-X, at least for `.txt` files + // if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn")) + // { + // if (_allowedChars.Length == 0) + // { + // // Limits characters to ASCII encoding. + // for (var i = 0; i < data.Length; i++) + // { + // if (reader.ReadByte() > sbyte.MaxValue) + // { + // return false; + // } + // } + // } + // else + // { + // // Limits characters to ASCII encoding and + // // values of the _allowedChars array. + // for (var i = 0; i < data.Length; i++) + // { + // var b = reader.ReadByte(); + // if (b > sbyte.MaxValue || + // !_allowedChars.Contains(b)) + // { + // return false; + // } + // } + // } + + // return true; + // } + + // Uncomment the following code block if you must permit + // files whose signature isn't provided in the _fileSignature + // dictionary. We recommend that you add file signatures + // for files (when possible) for all file types you intend + // to allow on the system and perform the file signature + // check. + if (!_fileSignature.TryGetValue(ext, out var value)) return true; + + // File signature check + // -------------------- + // With the file signatures provided in the _fileSignature + // dictionary, the following code tests the input content's + // file signature. + var signatures = value; + var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); + + return signatures.Any(signature => + headerBytes.Take(signature.Length).SequenceEqual(signature)); } } \ No newline at end of file diff --git a/backend/src/Utilities/MultipartRequestHelper.cs b/backend/src/Utilities/MultipartRequestHelper.cs index 90070750..d50eaace 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -2,52 +2,46 @@ using System.IO; using Microsoft.Net.Http.Headers; -namespace Database.Utilities +namespace Database.Utilities; + +public static class MultipartRequestHelper { - public static class MultipartRequestHelper + // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq" + // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit. + public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit) + { + var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value; + + if (string.IsNullOrWhiteSpace(boundary)) throw new InvalidDataException("Missing content-type boundary."); + + if (boundary.Length > lengthLimit) + throw new InvalidDataException( + $"Multipart boundary length limit {lengthLimit} exceeded."); + + return boundary; + } + + public static bool IsMultipartContentType(string? contentType) + { + return !string.IsNullOrEmpty(contentType) + && contentType.Contains("multipart/", StringComparison.OrdinalIgnoreCase); + } + + public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) + { + // Content-Disposition: form-data; name="key"; + return contentDisposition != null + && contentDisposition.DispositionType.Equals("form-data") + && string.IsNullOrEmpty(contentDisposition.FileName.Value) + && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value); + } + + public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition) { - // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq" - // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit. - public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit) - { - var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value; - - if (string.IsNullOrWhiteSpace(boundary)) - { - throw new InvalidDataException("Missing content-type boundary."); - } - - if (boundary.Length > lengthLimit) - { - throw new InvalidDataException( - $"Multipart boundary length limit {lengthLimit} exceeded."); - } - - return boundary; - } - - public static bool IsMultipartContentType(string? contentType) - { - return !string.IsNullOrEmpty(contentType) - && contentType.Contains("multipart/", StringComparison.OrdinalIgnoreCase); - } - - public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) - { - // Content-Disposition: form-data; name="key"; - return contentDisposition != null - && contentDisposition.DispositionType.Equals("form-data") - && string.IsNullOrEmpty(contentDisposition.FileName.Value) - && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value); - } - - public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition) - { - // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg" - return contentDisposition != null - && contentDisposition.DispositionType.Equals("form-data") - && (!string.IsNullOrEmpty(contentDisposition.FileName.Value) - || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value)); - } + // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg" + return contentDisposition != null + && contentDisposition.DispositionType.Equals("form-data") + && (!string.IsNullOrEmpty(contentDisposition.FileName.Value) + || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value)); } } \ No newline at end of file diff --git a/backend/test/Program.cs b/backend/test/Program.cs index 6284d39e..cc52b131 100644 --- a/backend/test/Program.cs +++ b/backend/test/Program.cs @@ -10,4 +10,4 @@ string[] commandLineArguments { return new AutoRun().Execute(commandLineArguments); } -} +} \ No newline at end of file From 771df59fd1ef3919d784f4eb1c1adcf6f25c1058 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:14:07 +0200 Subject: [PATCH 136/878] Fix `Host terminated unexpectedly` being printed to the console when running `dotnet ef` tools --- backend/src/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 21ec9267..522cb49a 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -63,7 +63,7 @@ string[] commandLineArguments application.Run(); return 0; } - catch (Exception ex) + catch (Exception ex) when (ex is not HostAbortedException && ex.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { Log.Fatal(ex, "Host terminated unexpectedly"); return 1; From e77d0ff48066cd4cb48028a6cde3729d953f1c7c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:15:00 +0200 Subject: [PATCH 137/878] Fix rollback of migration `InitialCreate` --- backend/src/Migrations/20211019125407_InitialCreate.cs | 4 ++++ .../rollback_from_0_to_20211019125407_InitialCreate.sql | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/backend/src/Migrations/20211019125407_InitialCreate.cs b/backend/src/Migrations/20211019125407_InitialCreate.cs index 658137b0..3da7159e 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.cs @@ -653,6 +653,10 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "photovoltaic_data", schema: "database"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); } } } diff --git a/backend/src/Migrations/rollback_from_0_to_20211019125407_InitialCreate.sql b/backend/src/Migrations/rollback_from_0_to_20211019125407_InitialCreate.sql index 1b3cb09b..ca6e00c9 100644 --- a/backend/src/Migrations/rollback_from_0_to_20211019125407_InitialCreate.sql +++ b/backend/src/Migrations/rollback_from_0_to_20211019125407_InitialCreate.sql @@ -40,6 +40,10 @@ DROP TABLE database.optical_data; DROP TABLE database.photovoltaic_data; +DROP EXTENSION IF EXISTS pgcrypto; + +DROP TYPE public.data_kind; + DELETE FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20211019125407_InitialCreate'; From 1eb2355f017423f6707e7e239f881bb2e2d4110a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:18:01 +0200 Subject: [PATCH 138/878] Add GNU Make target to drop the database --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2de7ef61..d0c04a71 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,7 @@ list : ## List all containers with health status --all .PHONY : list -createdb : ## Create databases +createdb : ## Create database ${docker_compose} exec \ database \ bash -c " \ @@ -178,6 +178,14 @@ createdb : ## Create databases " .PHONY : createdb +dropdb : ## Drop database + ${docker_compose} exec \ + database \ + bash -c " \ + dropdb --username postgres xbase_development ; \ + " +.PHONY : dropdb + begin-maintenance : ## Begin maintenance cp \ ./nginx/html/maintenance.off.html \ From 1f834a11a0ff3e561ccf2e9c4ed28172a8ab479b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:19:46 +0200 Subject: [PATCH 139/878] Add missing schema to enum mapping --- backend/src/Migrations/20211019125407_InitialCreate.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs b/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs index 66b2cbc9..323c4cf8 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.Designer.cs @@ -20,7 +20,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasPostgresEnum("data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }) + .HasPostgresEnum("public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }) .HasPostgresExtension("pgcrypto") .HasAnnotation("Relational:MaxIdentifierLength", 63) .HasAnnotation("ProductVersion", "5.0.10") From d20face1fe8cf1cac622205dad89c6d173a46e13 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:21:31 +0200 Subject: [PATCH 140/878] Add missing setter to map properties to PostgreSQL table columns --- backend/src/Data/ApplicationDbContext.cs | 2 +- backend/src/Data/DataApproval.cs | 10 +++++----- backend/src/Data/DataX.cs | 16 ++++++++-------- backend/src/Data/Entity.cs | 4 ++-- backend/src/Data/GetHttpsResource.cs | 8 ++++---- backend/src/Data/NamedMethodArgument.cs | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index e8260d49..a8a324be 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -30,7 +30,7 @@ DbContextOptions options public DbSet OpticalData { get; private set; } = default!; public DbSet PhotovoltaicData { get; private set; } = default!; public DbSet Users { get; private set; } = default!; - public DbSet DataProtectionKeys { get; } = default!; + public DbSet DataProtectionKeys { get; private set; } = default!; private static void CreateEnumerations(ModelBuilder builder) { diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 18724a1d..98e5d891 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -25,9 +25,9 @@ Guid approverId } public Guid ApproverId { get; private set; } - public DateTime Timestamp { get; } - public string Signature { get; } - public string KeyFingerprint { get; } - public string Query { get; } - public string Response { get; } + public DateTime Timestamp { get; private set; } + public string Signature { get; private set; } + public string KeyFingerprint { get; private set; } + public string Query { get; private set; } + public string Response { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 54ca8373..b7fd6c1e 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -53,14 +53,14 @@ DateTime createdAt CreatedAt = createdAt; } - public string Locale { get; } - public Guid ComponentId { get; } - public string? Name { get; } - public string? Description { get; } - public string[] Warnings { get; } - public Guid CreatorId { get; } - public DateTime CreatedAt { get; } - public AppliedMethod AppliedMethod { get; } = default!; + public string Locale { get; private set; } + public Guid ComponentId { get; private set; } + public string? Name { get; private set; } + public string? Description { get; private set; } + public string[] Warnings { get; private set; } + public Guid CreatorId { get; private set; } + public DateTime CreatedAt { get; private set; } + public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); // public ResponseApproval Approval { get; private set; } diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index 536310b7..87016107 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -7,10 +7,10 @@ namespace Database.Data; public abstract class Entity : IEntity { - public Guid Id { get; } + public Guid Id { get; private set; } // [NotMapped] // public Guid Uuid { get => Id; } - public uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html + public uint Version { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 703b8451..0d1d934a 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -104,22 +104,22 @@ public GetHttpsResource( [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? PhotovoltaicData as IData; - public Guid? CalorimetricDataId { get; } + public Guid? CalorimetricDataId { get; private set; } [InverseProperty(nameof(Database.Data.CalorimetricData.Resources))] public CalorimetricData? CalorimetricData { get; set; } - public Guid? HygrothermalDataId { get; } + public Guid? HygrothermalDataId { get; private set; } [InverseProperty(nameof(Database.Data.HygrothermalData.Resources))] public HygrothermalData? HygrothermalData { get; set; } - public Guid? OpticalDataId { get; } + public Guid? OpticalDataId { get; private set; } [InverseProperty(nameof(Database.Data.OpticalData.Resources))] public OpticalData? OpticalData { get; set; } - public Guid? PhotovoltaicDataId { get; } + public Guid? PhotovoltaicDataId { get; private set; } [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] public PhotovoltaicData? PhotovoltaicData { get; set; } diff --git a/backend/src/Data/NamedMethodArgument.cs b/backend/src/Data/NamedMethodArgument.cs index 03462578..87f53cf5 100644 --- a/backend/src/Data/NamedMethodArgument.cs +++ b/backend/src/Data/NamedMethodArgument.cs @@ -18,7 +18,7 @@ JsonDocument value } public string Name { get; private set; } - public JsonDocument Value { get; } + public JsonDocument Value { get; private set; } public void Dispose() { From 10c2783ce0df08ba1fb8b6e0d8761d1f1ba289da Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 13:21:52 +0200 Subject: [PATCH 141/878] Upgrade dependencies --- backend/dotnet-tools.json | 59 +++++++++++-------- backend/src/Database.csproj | 46 +++++++-------- .../GraphQl/LoggingDiagnosticEventListener.cs | 7 ++- backend/test/Database.Tests.csproj | 14 ++--- 4 files changed, 70 insertions(+), 56 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index eea68ae2..74e1b9e1 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,82 +3,95 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "8.0.0", + "version": "8.0.2", "commands": [ "dotnet-aspnet-codegenerator" - ] + ], + "rollForward": false }, "dotnet-counters": { - "version": "8.0.505301", + "version": "8.0.510501", "commands": [ "dotnet-counters" - ] + ], + "rollForward": false }, "dotnet-dump": { - "version": "8.0.505301", + "version": "8.0.510501", "commands": [ "dotnet-dump" - ] + ], + "rollForward": false }, "dotnet-ef": { - "version": "8.0.1", + "version": "8.0.5", "commands": [ "dotnet-ef" - ] + ], + "rollForward": false }, "dotnet-script": { "version": "1.5.0", "commands": [ "dotnet-script" - ] + ], + "rollForward": false }, "dotnet-trace": { - "version": "8.0.505301", + "version": "8.0.510501", "commands": [ "dotnet-trace" - ] + ], + "rollForward": false }, "dotnet-gcdump": { - "version": "8.0.505301", + "version": "8.0.510501", "commands": [ "dotnet-gcdump" - ] + ], + "rollForward": false }, "dotnet-sos": { - "version": "8.0.505301", + "version": "8.0.510501", "commands": [ "dotnet-sos" - ] + ], + "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.8.2", + "version": "0.8.8", "commands": [ "roslynator" - ] + ], + "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2023.3.2", + "version": "2024.1.2", "commands": [ "jb" - ] + ], + "rollForward": false }, "dotnet-project-licenses": { "version": "2.7.1", "commands": [ "dotnet-project-licenses" - ] + ], + "rollForward": false }, "strawberryshake.tools": { - "version": "13.8.1", + "version": "13.9.4", "commands": [ "dotnet-graphql" - ] + ], + "rollForward": false }, "dotnet-certificate-tool": { "version": "3.1.0", "commands": [ "certificate-tool" - ] + ], + "rollForward": false } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 9b8d4fa4..437e5e53 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -21,41 +21,41 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - - - + + + + + + - + diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index f5e87bb3..f464a602 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -70,11 +70,12 @@ string error [LoggerMessage( EventId = 5, Level = LogLevel.Error, - Message = "Failed processing the task {Task} with the error {Error}.")] + Message = "Failed processing the task of kind {Kind} with status {Status} with the error {Error}.")] public static partial void FailedTask( this ILogger logger, Exception? exception, - IExecutionTask task, + ExecutionTaskKind kind, + ExecutionTaskStatus status, string error ); @@ -151,7 +152,7 @@ public override void TaskError( IError error ) { - _logger.FailedTask(error.Exception, task, ConvertErrorToString(error)); + _logger.FailedTask(error.Exception, task.Kind, task.Status, ConvertErrorToString(error)); } public override void ValidationErrors( diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 0922038a..12edca7d 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -17,15 +17,15 @@ - - - - - + + + + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 25639cf99eadeb0259fdbdad2658e7d13cc4a2d5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 May 2024 14:03:13 +0200 Subject: [PATCH 142/878] Upgrade OpenIddict PostgreSQL tables --- ...240522120225_UpgradeOpenIddict.Designer.cs | 1341 +++++++++++++++++ .../20240522120225_UpgradeOpenIddict.cs | 80 + .../ApplicationDbContextModelSnapshot.cs | 22 +- 3 files changed, 1435 insertions(+), 8 deletions(-) create mode 100644 backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs create mode 100644 backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs new file mode 100644 index 00000000..1c405b3a --- /dev/null +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs @@ -0,0 +1,1341 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240522120225_UpgradeOpenIddict")] + partial class UpgradeOpenIddict + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs new file mode 100644 index 00000000..0022bebf --- /dev/null +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs @@ -0,0 +1,80 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class UpgradeOpenIddict : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Email", + schema: "database", + table: "user"); + + migrationBuilder.RenameColumn( + name: "Type", + schema: "database", + table: "OpenIddictApplications", + newName: "ClientType"); + + migrationBuilder.AddColumn( + name: "ApplicationType", + schema: "database", + table: "OpenIddictApplications", + type: "character varying(50)", + maxLength: 50, + nullable: true); + + migrationBuilder.AddColumn( + name: "JsonWebKeySet", + schema: "database", + table: "OpenIddictApplications", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Settings", + schema: "database", + table: "OpenIddictApplications", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApplicationType", + schema: "database", + table: "OpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "JsonWebKeySet", + schema: "database", + table: "OpenIddictApplications"); + + migrationBuilder.DropColumn( + name: "Settings", + schema: "database", + table: "OpenIddictApplications"); + + migrationBuilder.RenameColumn( + name: "ClientType", + schema: "database", + table: "OpenIddictApplications", + newName: "Type"); + + migrationBuilder.AddColumn( + name: "Email", + schema: "database", + table: "user", + type: "text", + nullable: false, + defaultValue: ""); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index e6725e63..e15db1ce 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -20,7 +20,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "7.0.5") + .HasAnnotation("ProductVersion", "8.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); @@ -284,10 +284,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - b.Property("Name") .IsRequired() .HasColumnType("text"); @@ -332,6 +328,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("text"); + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + b.Property("ClientId") .HasMaxLength(100) .HasColumnType("character varying(100)"); @@ -339,6 +339,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ClientSecret") .HasColumnType("text"); + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + b.Property("ConcurrencyToken") .IsConcurrencyToken() .HasMaxLength(50) @@ -354,6 +358,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("DisplayNames") .HasColumnType("text"); + b.Property("JsonWebKeySet") + .HasColumnType("text"); + b.Property("Permissions") .HasColumnType("text"); @@ -369,9 +376,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Requirements") .HasColumnType("text"); - b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); + b.Property("Settings") + .HasColumnType("text"); b.HasKey("Id"); From 42083db0f052783aaddbbb3a8d8c1fd76fef6475 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 25 Jun 2024 12:10:29 +0200 Subject: [PATCH 143/878] Upgrade dependencies --- backend/dotnet-tools.json | 8 +++---- backend/src/Database.csproj | 38 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 6 ++--- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 74e1b9e1..118653a9 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "8.0.5", + "version": "8.0.6", "commands": [ "dotnet-ef" ], @@ -59,14 +59,14 @@ "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.8.8", + "version": "0.8.9", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.1.2", + "version": "2024.1.4", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "13.9.4", + "version": "13.9.6", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 437e5e53..2fd20fd9 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -23,37 +23,37 @@ - - - - - - - - + + + + + + + + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - + + + + - - - + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 12edca7d..d038d28e 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -18,9 +18,9 @@ - - - + + + From 0a6c1328dd86c59bf0144c2e4a16d05fb2cafb8a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 09:00:39 +0200 Subject: [PATCH 144/878] Remove as of .NET 7.0.200 SDK unsupported use of `--output` --- see https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid --- backend/Dockerfile-production | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 7e59b9b7..81a860ce 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -29,10 +29,15 @@ WORKDIR /app RUN \ dotnet restore +# `dotnet publish` writes its output to +# [project_file_folder]/bin/[configuration]/[framework]/publish/ +# as told in the description of the option `--output` on +# https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#options +# We cannot use this option though to specify a custom folder as explained on +# https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid RUN \ dotnet publish \ --configuration release \ - --output out \ --self-contained false \ --no-restore @@ -59,7 +64,7 @@ WORKDIR /app COPY \ --from=build \ - /app/out \ + /app/bin/Release/net8.0/publish \ ./ RUN \ From a9eb8ffb4059948898a92961fce7401498474270 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 09:13:17 +0200 Subject: [PATCH 145/878] Correct publishing destination --- backend/Dockerfile-production | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 81a860ce..69a90010 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -64,7 +64,7 @@ WORKDIR /app COPY \ --from=build \ - /app/bin/Release/net8.0/publish \ + /app/src/bin/Release/net8.0/publish \ ./ RUN \ From 8f6c596a61397503ce5e4bf0a26b3299abec08fd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 11:48:56 +0200 Subject: [PATCH 146/878] Correct type of `onFilter` --- frontend/lib/freeTextFilter.tsx | 4 ++-- frontend/lib/table.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/lib/freeTextFilter.tsx b/frontend/lib/freeTextFilter.tsx index 7d0bfcc4..061df3dc 100644 --- a/frontend/lib/freeTextFilter.tsx +++ b/frontend/lib/freeTextFilter.tsx @@ -23,7 +23,7 @@ export function setMapValue( export function doesFieldIncludeFilterValue( field: string, - value: string | number | boolean + value: React.Key | boolean ) { return field.toLowerCase().includes(value.toString().toLowerCase()); } @@ -94,7 +94,7 @@ export function getFreeTextFilterProps( filterIcon: (filtered: boolean) => ( ), - onFilter: (value: string | number | boolean, record: RecordType) => { + onFilter: (value: React.Key | boolean, record: RecordType) => { const field = getField(record); return field ? doesFieldIncludeFilterValue(field, value) : false; }, diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index e64889f6..18947e6a 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -313,7 +313,7 @@ export function getFilterableEnumListColumnProps< text: key, value: value, })), - onFilter: (value: string | number | boolean, record: RecordType) => { + onFilter: (value: React.Key | boolean, record: RecordType) => { const values = getValues(record); if ( typeof value === "string" && From 100e5779812037301b81ac1317a43455f0862722 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 12:01:39 +0200 Subject: [PATCH 147/878] Add GNU Make targets for `yarn run ...` commands defined in ./package.json --- frontend/Makefile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontend/Makefile b/frontend/Makefile index 1aa5c50d..985fe8d9 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -13,6 +13,18 @@ help : ## Print this help # Tasks to run, for example, in a Docker container # # ------------------------------------------------ # +build : ## Build the project + yarn run build +.PHONY : build + +test : ## Run tests + yarn run test +.PHONY : test + +graphql : ## Generate TypeScript code from the GraphQL queries ./queries/*.graphql + yarn run codegen +.PHONY : graphql + install : ## Install packages (without touching the lockfile) yarn install --pure-lockfile .PHONY : install From 2fcf71c05bb90a33f2810eeac9c4afc4fbb3d4a7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 13:00:15 +0200 Subject: [PATCH 148/878] Set NODE_ENV to production when building next.js to not get the error https://nextjs.org/docs/messages/no-document-import-in-page --- see https ://github.com/vercel/next.js/issues/52158#issuecomment-1901092680 --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index b7b7852a..e7d8db9d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,7 @@ "scripts": { "codegen": "graphql-let && graphql-codegen --config codegen.yml", "dev": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' next -p 8080", - "build": "yarn codegen && next build", + "build": "yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", "start": "next start -p 8080" }, From 34d79dba92573a5de6488915b012d5107cd0a7fb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 26 Jun 2024 13:02:42 +0200 Subject: [PATCH 149/878] Upgrade to latest frontend packages (except for graphql-codegen and next) --- frontend/components/NavBar.tsx | 4 +- .../data/calorimetric/CalorimetricData.tsx | 4 +- .../data/hygrothermal/HygrothermalData.tsx | 4 +- .../components/data/optical/OpticalData.tsx | 2 +- .../data/photovoltaic/PhotovoltaicData.tsx | 4 +- frontend/lib/table.tsx | 2 +- frontend/package.json | 34 +- frontend/pages/index.tsx | 6 +- frontend/yarn.lock | 3038 +++++++++-------- 9 files changed, 1622 insertions(+), 1476 deletions(-) diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 4afb82ff..f00930a8 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -39,7 +39,7 @@ export default function NavBar({ items }: NavBarProps) { {items.map(({ path, label }) => ( - {label} + {label} ))} {currentUser ? ( @@ -50,7 +50,7 @@ export default function NavBar({ items }: NavBarProps) { > {moderatorItems.map(({ path, label }) => ( - {label} + {label} ))} diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 048b57df..183956cb 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -61,7 +61,9 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat - + {calorimetricData.creatorId} diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index 6342c8e5..315c2d34 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -61,7 +61,9 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat - + {hygrothermalData.creatorId} diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 7cb5feee..1feaf647 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -61,7 +61,7 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { - + {opticalData.creatorId} diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 48bdb0da..f5a36c33 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -61,7 +61,9 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat - + {photovoltaicData.creatorId} diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index 18947e6a..1ca1de9f 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -146,7 +146,7 @@ export function getInternallyLinkedFilterableStringColumnProps( (record, _highlightedValue, value) => value ? ( // TODO Why does this not work with `_highlightedValue`? An error is raised saying "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?": https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-function-component or https://reactjs.org/docs/forwarding-refs.html or https://deepscan.io/docs/rules/react-func-component-invalid-ref-prop or https://www.carlrippon.com/react-forwardref-typescript/ - {value} + {value} ) : ( <> ) diff --git a/frontend/package.json b/frontend/package.json index e7d8db9d..97684382 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,46 +13,46 @@ }, "dependencies": { "@ant-design/pro-layout": "^7.10.3", - "@apollo/client": "^3.7.0", + "@apollo/client": "^3.7.8", "antd": "^5.0", - "crypto-hash": "^2.0.1", + "crypto-hash": "^3.0.0", "dayjs": "^1.0", "graphql": "^16.0.1", - "next": "^13.1.4", - "react": "^18.2.0", - "react-cookie": "^4.0.3", - "react-dom": "^18.2.0", + "next": "^13.0.0", + "react": "^18.3.1", + "react-cookie": "^7.1.4", + "react-dom": "^18.3.1", "react-highlight-words": "^0.20.0" }, "devDependencies": { "@babel/core": "^7.0", "@graphql-codegen/cli": "^3.0.0", "@graphql-codegen/fragment-matcher": "^4.0.0", - "@graphql-codegen/import-types-preset": "^2.0.0", + "@graphql-codegen/import-types-preset": "^2.2.6", "@graphql-codegen/plugin-helpers": "^4.0.0", "@graphql-codegen/typescript": "^3.0.0", "@graphql-codegen/typescript-operations": "^3.0.0", - "@graphql-codegen/typescript-react-apollo": "^3.0.0", + "@graphql-codegen/typescript-react-apollo": "^3.3.7", "@graphql-codegen/typescript-resolvers": "^3.0.0", - "@graphql-tools/load-files": "^6.0", - "@graphql-tools/merge": "^8.0.1", - "@graphql-tools/schema": "^9.0.13", - "@types/node": "^18.11.18", + "@graphql-tools/load-files": "^6.6.1", + "@graphql-tools/merge": "^8.3.18", + "@graphql-tools/schema": "^9.0.16", + "@types/node": "^20.14.9", "@types/qrcode.react": "^1.0", "@types/react": "^18.0", "@types/react-dom": "^18.0", - "@types/react-highlight-words": "^0.16.3", + "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^18.0", "babel-jest": "^29.0", "deepmerge": "^4.0", - "eslint": "^8.0.0", - "eslint-config-next": "^13.1.4", + "eslint": "^9.5.0", + "eslint-config-next": "^13.0.0", "graphql-let": "^0.18", "graphql-tag": "^2.0", "jest": "^29.0", "license-checker": "^25.0.1", - "prettier": "^2.8.4", - "react-test-renderer": "^18.0", + "prettier": "^3.3.2", + "react-test-renderer": "^18.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4", "yaml-loader": "^0.8" diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 8d88a145..f0f6c5f9 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -149,8 +149,10 @@ function Page() { Schematic depiction of how users like architects, planners, or engineers can use the metabase to find products and data in and across databases. + style={{ + maxWidth: "100%", + height: "auto", + }} /> ); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index f598d38c..06f76da6 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,18 +2,13 @@ # yarn lockfile v1 -"@aashutoshrathi/word-wrap@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" - integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== - "@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" "@ant-design/colors@^7.0.0", "@ant-design/colors@^7.0.2": version "7.0.2" @@ -22,10 +17,10 @@ dependencies: "@ctrl/tinycolor" "^3.6.1" -"@ant-design/cssinjs@^1.11.1", "@ant-design/cssinjs@^1.18.2": - version "1.18.2" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.18.2.tgz#d993a64c1d0bf51f4a9d662ddc8ed8426977b5c3" - integrity sha512-514V9rjLaFYb3v4s55/8bg2E6fb81b99s3crDZf4nSwtiDLLXs8axnIph+q2TVkY2hbJPZOn/cVsVcnLkzFy7w== +"@ant-design/cssinjs@^1.11.1", "@ant-design/cssinjs@^1.21.0": + version "1.21.0" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.0.tgz#de7289bfd71c7a494a28b96569ad88f999619105" + integrity sha512-gIilraPl+9EoKdYxnupxjHB/Q6IHNRjEXszKbDxZdsgv4sAZ9pjkCq8yanDWNvyfjp4leir2OVAJm0vxwKK8YA== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" @@ -35,30 +30,30 @@ rc-util "^5.35.0" stylis "^4.0.13" -"@ant-design/icons-svg@^4.3.0": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.3.1.tgz#4b2f65a17d4d32b526baa6414aca2117382bf8da" - integrity sha512-4QBZg8ccyC6LPIRii7A0bZUk3+lEDCLnhB+FVsflGdcWPPmV+j3fire4AwwoqHV/BibgvBmR9ZIo4s867smv+g== +"@ant-design/icons-svg@^4.4.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" + integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== -"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.2.6": - version "5.2.6" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.2.6.tgz#2d4a9a37f531eb2a20cebec01d6fb69cf593900d" - integrity sha512-4wn0WShF43TrggskBJPRqCD0fcHbzTYjnaoskdiJrVHg86yxoZ8ZUqsXvyn4WUqehRiFKnaclOhqk9w4Ui2KVw== +"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.3.7": + version "5.3.7" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.3.7.tgz#d9f3654bf7934ee5faba43f91b5a187f5309ec68" + integrity sha512-bCPXTAg66f5bdccM4TT21SQBDO1Ek2gho9h3nO9DAKXJP4sq+5VBjrQMSxMVXSB3HyEz+cUbHQ5+6ogxCOpaew== dependencies: "@ant-design/colors" "^7.0.0" - "@ant-design/icons-svg" "^4.3.0" + "@ant-design/icons-svg" "^4.4.0" "@babel/runtime" "^7.11.2" classnames "^2.2.6" rc-util "^5.31.1" "@ant-design/pro-layout@^7.10.3": - version "7.17.19" - resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.17.19.tgz#f61097f5e16e859e29d2baf03e17702bf92f303d" - integrity sha512-X3L+/0Vro9AyN51oGBMz+bfjHjod43wyMJ+7gePiK8ECqYTMZLWGXhrEZnPMY/GCdk0OeGzWD5N9DFuUtwcSLQ== + version "7.19.8" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.19.8.tgz#5d070ae2d74a232a657dca6fb207d097b9e8a621" + integrity sha512-f/vC83jmCjEDP7rrLBxRBqCK8298keSRrXqpd2RqigYlJpGqeKVGcevmk5ngBjDxz+UqjoNfOokG7cU3t3eIvQ== dependencies: "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.13.5" - "@ant-design/pro-utils" "2.15.4" + "@ant-design/pro-provider" "2.14.7" + "@ant-design/pro-utils" "2.15.14" "@babel/runtime" "^7.18.0" "@umijs/route-utils" "^4.0.0" "@umijs/use-params" "^1.0.9" @@ -71,10 +66,10 @@ swr "^2.0.0" warning "^4.0.3" -"@ant-design/pro-provider@2.13.5": - version "2.13.5" - resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.13.5.tgz#4f2a14ec21a014fc334e8a56741433ff06b02be6" - integrity sha512-ZVmzY2cq4nUvgmAlfgyCAaSZYV2l3n/upIQPXPj8sYcT+N/Pt1CeSVkkgW6By3EqokF6apWdIFU7hZMK2rNhrg== +"@ant-design/pro-provider@2.14.7": + version "2.14.7" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.14.7.tgz#911dac05cd9c501ddb0800fd0141c29102a9c100" + integrity sha512-yEAOph/fTzIhOWMtYAbcsjffyYq8tL6IgMQqRlYZmIisVMRDZZ32F+Ab18a4S18foZiDChEr0e6NZuuY7du5rA== dependencies: "@ant-design/cssinjs" "^1.11.1" "@babel/runtime" "^7.18.0" @@ -82,13 +77,13 @@ rc-util "^5.0.1" swr "^2.0.0" -"@ant-design/pro-utils@2.15.4": - version "2.15.4" - resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.15.4.tgz#0e9cea50f8baad4a707683fd020033b3065196b6" - integrity sha512-nbacIMl5lbMlNHlaPl2tt/ezvHhnBtGL2KLVaqijLou5zAuZprkHAJnckXoqm9T6X9R2rE4jH96WZHLpJ27nFw== +"@ant-design/pro-utils@2.15.14": + version "2.15.14" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.15.14.tgz#852c3727aa0c0058924fd539cc375022901c1cea" + integrity sha512-h/GdYDN19dTnyWvbfoEF5DyeTwkB+iRUXlhQ1O1NMkD2eOUpKTjVKF8J+Kekb7Bp7Fkf1dBVktVrcY8sioTkrw== dependencies: "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.13.5" + "@ant-design/pro-provider" "2.14.7" "@babel/runtime" "^7.18.0" classnames "^2.3.2" dayjs "^1.11.10" @@ -97,10 +92,10 @@ safe-stable-stringify "^2.4.3" swr "^2.0.0" -"@ant-design/react-slick@~1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.0.2.tgz#241bb412aeacf7ff5d50c61fa5db66773fde6b56" - integrity sha512-Wj8onxL/T8KQLFFiCA4t8eIRGpRR+UPgOdac2sYzonv+i0n3kXHmvHLLiOYL655DQx2Umii9Y9nNgL7ssu5haQ== +"@ant-design/react-slick@~1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" + integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== dependencies: "@babel/runtime" "^7.10.4" classnames "^2.2.5" @@ -108,18 +103,20 @@ resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" -"@apollo/client@^3.7.0": - version "3.8.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.8.9.tgz#0e4ac133eb04c63e618138c1ebf273d9f110a4d0" - integrity sha512-IcQDFEEPc9+PEQsxhxQvsoQ04BRarOzi/Ila5PcniRSDeKJWgY22dnp6+V1i1fWXRDVd1ybdvze4sFESDVQUCQ== +"@apollo/client@^3.7.8": + version "3.10.6" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.10.6.tgz#cf0bf034504c7b3d9984c985172d8af87f0ef639" + integrity sha512-3lLFGJtzC1/mEnK11BRf+Bf8536kBQUSB1G9yMtcRsxmY+tCKdTPzsP3fMUKy10BPIE0sDUY1pux3iMPIn2vow== dependencies: "@graphql-typed-document-node/core" "^3.1.1" + "@wry/caches" "^1.0.0" "@wry/equality" "^0.5.6" "@wry/trie" "^0.5.0" graphql-tag "^2.12.6" hoist-non-react-statics "^3.3.2" optimism "^0.18.0" prop-types "^15.7.2" + rehackt "^0.1.0" response-iterator "^0.2.6" symbol-observable "^4.0.0" ts-invariant "^0.10.3" @@ -163,18 +160,18 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" - integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.7.tgz#d23bbea508c3883ba8251fb4164982c36ea577ed" + integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== "@babel/core@7.15.5": version "7.15.5" @@ -197,204 +194,210 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" - integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== +"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.23.9": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.7.tgz#b676450141e0b52a3d43bc91da86aa608f950ac4" + integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.7" - "@babel/parser" "^7.23.6" - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helpers" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/template" "^7.24.7" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== +"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.24.7", "@babel/generator@^7.7.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" + integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" + "@babel/types" "^7.24.7" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== +"@babel/helper-annotate-as-pure@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" + integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.24.7" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== +"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz#4eb6c4a80d6ffeac25ab8cd9a21b5dfa48d503a9" + integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" + "@babel/compat-data" "^7.24.7" + "@babel/helper-validator-option" "^7.24.7" browserslist "^4.22.2" lru-cache "^5.1.1" semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz#b2e6826e0e20d337143655198b79d58fdc9bd43d" - integrity sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-member-expression-to-functions" "^7.23.0" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz#2eaed36b3a1c11c53bdf80d53838b293c52f5b3b" + integrity sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.7" + "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" semver "^6.3.1" -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== +"@babel/helper-environment-visitor@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9" + integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== + dependencies: + "@babel/types" "^7.24.7" -"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== +"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" + integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== +"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" + integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.24.7" -"@babel/helper-member-expression-to-functions@^7.22.15", "@babel/helper-member-expression-to-functions@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz#9263e88cc5e41d39ec18c9a3e0eced59a3e7d366" - integrity sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA== +"@babel/helper-member-expression-to-functions@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz#67613d068615a70e4ed5101099affc7a41c5225f" + integrity sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w== dependencies: - "@babel/types" "^7.23.0" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-module-imports@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== dependencies: - "@babel/types" "^7.22.15" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz#31b6c9a2930679498db65b685b1698bfd6c7daf8" + integrity sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== +"@babel/helper-optimise-call-expression@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" + integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.24.7" "@babel/helper-plugin-utils@7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.8.0": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0" + integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== -"@babel/helper-replace-supers@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz#e37d367123ca98fe455a9887734ed2e16eb7a793" - integrity sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw== +"@babel/helper-replace-supers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz#f933b7eed81a1c0265740edc91491ce51250f765" + integrity sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg== dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-member-expression-to-functions" "^7.22.15" - "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.24.7" + "@babel/helper-optimise-call-expression" "^7.24.7" -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== dependencies: - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== +"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" + integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== dependencies: - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== +"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" + integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.24.7" -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== +"@babel/helper-string-parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2" + integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== +"@babel/helper-validator-option@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz#24c3bb77c7a425d1742eec8fb433b5a1b38e62f6" + integrity sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== -"@babel/helpers@^7.15.4", "@babel/helpers@^7.23.7": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" - integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== +"@babel/helpers@^7.15.4", "@babel/helpers@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.7.tgz#aa2ccda29f62185acb5d42fb4a3a1b1082107416" + integrity sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg== dependencies: - "@babel/template" "^7.22.15" - "@babel/traverse" "^7.23.7" - "@babel/types" "^7.23.6" + "@babel/template" "^7.24.7" + "@babel/types" "^7.24.7" -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== dependencies: - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-validator-identifier" "^7.24.7" chalk "^2.4.2" js-tokens "^4.0.0" + picocolors "^1.0.0" "@babel/parser@7.15.5": version "7.15.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" - integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" + integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -436,19 +439,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.23.3": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz#084564e0f3cc21ea6c70c44cff984a1c0509729a" - integrity sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz#d1759e84dd4b437cf9fae69b4c06c41d7625bfb7" + integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz#9c05a7f592982aff1a2768260ad84bcd3f0c77fc" - integrity sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz#2a0b406b5871a20a841240586b1300ce2088a778" + integrity sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" @@ -464,12 +467,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.23.3", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" - integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.24.7", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" + integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" @@ -521,187 +524,187 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f" - integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" + integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz#94c6dcfd731af90f27a79509f9ab7fb2120fc38b" - integrity sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz#4f6886c11e423bd69f3ce51dbf42424a5f275514" + integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz#fe1177d715fb569663095e04f3598525d98e8c77" - integrity sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz#a4251d98ea0c0f399dafe1a35801eaba455bbf1f" + integrity sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz#b2d38589531c6c80fbe25e6b58e763622d2d3cf5" - integrity sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz#42063e4deb850c7bd7c55e626bf4e7ab48e6ce02" + integrity sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-classes@^7.0.0": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz#d08ae096c240347badd68cdf1b6d1624a6435d92" - integrity sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" - "@babel/helper-split-export-declaration" "^7.22.6" + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz#4ae6ef43a12492134138c1e45913f7c46c41b4bf" + integrity sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz#652e69561fcc9d2b50ba4f7ac7f60dcf65e86474" - integrity sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz#4cab3214e80bc71fae3853238d13d097b004c707" + integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.15" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/template" "^7.24.7" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz#8c9ee68228b12ae3dff986e56ed1ba4f3c446311" - integrity sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz#a097f25292defb6e6cc16d6333a4cfc1e3c72d9e" + integrity sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz#cfa7ca159cc3306fab526fc67091556b51af26ff" - integrity sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz#ae454e62219288fbb734541ab00389bfb13c063e" + integrity sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-flow" "^7.23.3" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-flow" "^7.24.7" "@babel/plugin-transform-for-of@^7.0.0": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz#81c37e24171b37b370ba6aaffa7ac86bcb46f94e" - integrity sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz#f25b33f72df1d8be76399e1b8f3f9d366eb5bc70" + integrity sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-transform-function-name@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz#8f424fcd862bf84cb9a1a6b42bc2f47ed630f8dc" - integrity sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz#6d8601fbffe665c894440ab4470bc721dd9131d6" + integrity sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== dependencies: - "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-compilation-targets" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz#8214665f00506ead73de157eba233e7381f3beb4" - integrity sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz#36b505c1e655151a9d7607799a9988fc5467d06c" + integrity sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz#e37b3f0502289f477ac0e776b05a833d853cabcc" - integrity sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz#3b4454fb0e302e18ba4945ba3246acb1248315df" + integrity sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz#661ae831b9577e52be57dd8356b734f9700b53b4" - integrity sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz#9fd5f7fdadee9085886b183f1ad13d1ab260f4ab" + integrity sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== dependencies: - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-module-transforms" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" "@babel/plugin-transform-object-super@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz#81fdb636dcb306dd2e4e8fd80db5b2362ed2ebcd" - integrity sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz#66eeaff7830bba945dd8989b632a40c04ed625be" + integrity sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.20" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-replace-supers" "^7.24.7" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af" - integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz#5881f0ae21018400e320fc7eb817e529d1254b68" + integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz#54518f14ac4755d22b92162e4a852d308a560875" - integrity sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz#f0d2ed8380dfbed949c42d4d790266525d63bbdc" + integrity sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200" - integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz#9caff79836803bc666bcfe210aeb6626230c293b" + integrity sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.23.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" - integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz#17cd06b75a9f0e2bd076503400e7c4b99beedac4" + integrity sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/types" "^7.23.4" + "@babel/helper-annotate-as-pure" "^7.24.7" + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/plugin-syntax-jsx" "^7.24.7" + "@babel/types" "^7.24.7" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz#97d82a39b0e0c24f8a981568a8ed851745f59210" - integrity sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz#85448c6b996e122fa9e289746140aaa99da64e73" + integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" "@babel/plugin-transform-spread@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz#41d17aacb12bde55168403c6f2d6bdca563d362c" - integrity sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz#e8a38c0fde7882e0fb8f160378f74bd885cc7bb3" + integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.23.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz#5f0f028eb14e50b5d0f76be57f90045757539d07" - integrity sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg== + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz#a05debb4a9072ae8f985bcf77f3f215434c8f8c8" + integrity sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== dependencies: - "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-plugin-utils" "^7.24.7" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6": - version "7.23.8" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" - integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" + integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.22.15", "@babel/template@^7.3.3": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== +"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.24.7", "@babel/template@^7.3.3": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" + integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" "@babel/traverse@7.15.4": version "7.15.4" @@ -718,29 +721,29 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.23.7": - version "7.23.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" - integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.6" - "@babel/types" "^7.23.6" +"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5" + integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.24.7" + "@babel/helper-environment-visitor" "^7.24.7" + "@babel/helper-function-name" "^7.24.7" + "@babel/helper-hoist-variables" "^7.24.7" + "@babel/helper-split-export-declaration" "^7.24.7" + "@babel/parser" "^7.24.7" + "@babel/types" "^7.24.7" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.3.3": - version "7.23.6" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" - integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.3.3": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" + integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" + "@babel/helper-string-parser" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -778,29 +781,43 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + version "4.10.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" + integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== +"@eslint/config-array@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.16.0.tgz#bb3364fc39ee84ec3a62abdc4b8d988d99dfd706" + integrity sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg== + dependencies: + "@eslint/object-schema" "^2.1.4" + debug "^4.3.1" + minimatch "^3.0.5" + +"@eslint/eslintrc@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" + integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" + espree "^10.0.1" + globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.56.0": - version "8.56.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" - integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== +"@eslint/js@9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.5.0.tgz#0e9c24a670b8a5c86bff97b40be13d8d8f238045" + integrity sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w== + +"@eslint/object-schema@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" + integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== "@graphql-codegen/add@^3.2.1": version "3.2.3" @@ -869,7 +886,7 @@ "@graphql-codegen/plugin-helpers" "^4.1.0" tslib "~2.5.0" -"@graphql-codegen/import-types-preset@^2.0.0": +"@graphql-codegen/import-types-preset@^2.2.6": version "2.2.6" resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.2.6.tgz#aab292d93f79bffc09bc12a51843c3272b526475" integrity sha512-Lo2ITOln3UVdyyEPiijj8bVhVg0Ghp/JzHXA2LXxrJVCRbXizQhVC2vjiaWTjMskPt9Zub0yIoce4+RrbsXKcg== @@ -946,7 +963,7 @@ auto-bind "~4.0.0" tslib "~2.5.0" -"@graphql-codegen/typescript-react-apollo@^3.0.0": +"@graphql-codegen/typescript-react-apollo@^3.3.7": version "3.3.7" resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.3.7.tgz#e856caa22c5f7bc9a546c44f54e5f3bd5801ab67" integrity sha512-9DUiGE8rcwwEkf/S1kpBT/Py/UUs9Qak14bOnTT1JHWs1MWhiDA7vml+A8opU7YFI1EVbSSaE5jjRv11WHoikQ== @@ -1218,7 +1235,7 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/load-files@^6.0": +"@graphql-tools/load-files@^6.6.1": version "6.6.1" resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.6.1.tgz#91ce18d910baf8678459486d8cccd474767bec0a" integrity sha512-nd4GOjdD68bdJkHfRepILb0gGwF63mJI7uD4oJuuf2Kzeq8LorKa6WfyxUhdMuLmZhnx10zdAlWPfwv1NOAL4Q== @@ -1237,7 +1254,7 @@ p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@^8.0.1", "@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.4.1": +"@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.3.18", "@graphql-tools/merge@^8.4.1": version "8.4.2" resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== @@ -1285,7 +1302,7 @@ "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.13", "@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": +"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.16", "@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": version "9.0.19" resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== @@ -1354,24 +1371,15 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@humanwhocodes/config-array@^0.11.13": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== - dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" - "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" - integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== +"@humanwhocodes/retry@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" + integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1384,7 +1392,7 @@ js-yaml "^3.13.1" resolve-from "^5.0.0" -"@istanbuljs/schema@^0.1.2": +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== @@ -1613,24 +1621,24 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== dependencies: - "@jridgewell/set-array" "^1.0.1" + "@jridgewell/set-array" "^1.2.1" "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" + "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" @@ -1645,10 +1653,10 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.21" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz#5dc1df7b3dc4a6209e503a924e1ca56097a2bb15" - integrity sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" @@ -1731,90 +1739,90 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@parcel/watcher-android-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.0.tgz#9c93763794153e4f76920994a423b6ea3257059d" - integrity sha512-+fPtO/GsbYX1LJnCYCaDVT3EOBjvSFdQN9Mrzh9zWAOOfvidPWyScTrHIZHHfJBvlHzNA0Gy0U3NXFA/M7PHUA== +"@parcel/watcher-android-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" + integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== -"@parcel/watcher-darwin-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.0.tgz#2c79c2abde16aa24cac67e555b60802fd13fe210" - integrity sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA== +"@parcel/watcher-darwin-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" + integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== -"@parcel/watcher-darwin-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.0.tgz#23d82f198c5d033f047467c68d7c335f3df49b46" - integrity sha512-vZMv9jl+szz5YLsSqEGCMSllBl1gU1snfbRL5ysJU03MEa6gkVy9OMcvXV1j4g0++jHEcvzhs3Z3LpeEbVmY6Q== +"@parcel/watcher-darwin-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" + integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== -"@parcel/watcher-freebsd-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.0.tgz#7310cc86abc27dacd57624bcdba1f0ba092e76df" - integrity sha512-dHTRMIplPDT1M0+BkXjtMN+qLtqq24sLDUhmU+UxxLP2TEY2k8GIoqIJiVrGWGomdWsy5IO27aDV1vWyQ6gfHA== +"@parcel/watcher-freebsd-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" + integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== -"@parcel/watcher-linux-arm-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.0.tgz#c31b76e695027eeb1078d3d6f1d641d0b900c335" - integrity sha512-9NQXD+qk46RwATNC3/UB7HWurscY18CnAPMTFcI9Y8CTbtm63/eex1SNt+BHFinEQuLBjaZwR2Lp+n7pmEJPpQ== +"@parcel/watcher-linux-arm-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" + integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== -"@parcel/watcher-linux-arm64-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.0.tgz#56e09b86e9d8a4096f606be118b588da6e965080" - integrity sha512-QuJTAQdsd7PFW9jNGaV9Pw+ZMWV9wKThEzzlY3Lhnnwy7iW23qtQFPql8iEaSFMCVI5StNNmONUopk+MFKpiKg== +"@parcel/watcher-linux-arm64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" + integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== -"@parcel/watcher-linux-arm64-musl@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.0.tgz#27ffd5ca5f510ecd638f9ad22e2e813049db54e7" - integrity sha512-oyN+uA9xcTDo/45bwsd6TFHa7Lc7hKujyMlvwrCLvSckvWogndCEoVYFNfZ6JJ2KNL/6fFiGPcbjp8jJmEh5Ng== +"@parcel/watcher-linux-arm64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" + integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== -"@parcel/watcher-linux-x64-glibc@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.0.tgz#44cbbb1e5884a1ca900655f47a0775218318f934" - integrity sha512-KphV8awJmxU3q52JQvJot0QMu07CIyEjV+2Tb2ZtbucEgqyRcxOBDMsqp1JNq5nuDXtcCC0uHQICeiEz38dPBQ== +"@parcel/watcher-linux-x64-glibc@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" + integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== -"@parcel/watcher-linux-x64-musl@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.0.tgz#4c33993618c8d5113722852806239cb80360494b" - integrity sha512-7jzcOonpXNWcSijPpKD5IbC6xC7yTibjJw9jviVzZostYLGxbz8LDJLUnLzLzhASPlPGgpeKLtFUMjAAzM+gSA== +"@parcel/watcher-linux-x64-musl@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" + integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== -"@parcel/watcher-win32-arm64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.0.tgz#2a172fd2fda95fe5389298ca3e70b5a96316162a" - integrity sha512-NOej2lqlq8bQNYhUMnOD0nwvNql8ToQF+1Zhi9ULZoG+XTtJ9hNnCFfyICxoZLXor4bBPTOnzs/aVVoefYnjIg== +"@parcel/watcher-win32-arm64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" + integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== -"@parcel/watcher-win32-ia32@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.0.tgz#279225b2ebe1fadd3c5137c9b2365ad422656904" - integrity sha512-IO/nM+K2YD/iwjWAfHFMBPz4Zqn6qBDqZxY4j2n9s+4+OuTSRM/y/irksnuqcspom5DjkSeF9d0YbO+qpys+JA== +"@parcel/watcher-win32-ia32@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" + integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== -"@parcel/watcher-win32-x64@2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.0.tgz#93e0bd0ad1bda2c9a688764b9b30b71dc5b72a71" - integrity sha512-pAUyUVjfFjWaf/pShmJpJmNxZhbMvJASUpdes9jL6bTEJ+gDxPRSpXTIemNyNsb9AtbiGXs9XduP1reThmd+dA== +"@parcel/watcher-win32-x64@2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" + integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== "@parcel/watcher@^2.1.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.0.tgz#2d3c4ef8832a5cdfdbb76b914f022489933e664f" - integrity sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg== + version "2.4.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" + integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.4.0" - "@parcel/watcher-darwin-arm64" "2.4.0" - "@parcel/watcher-darwin-x64" "2.4.0" - "@parcel/watcher-freebsd-x64" "2.4.0" - "@parcel/watcher-linux-arm-glibc" "2.4.0" - "@parcel/watcher-linux-arm64-glibc" "2.4.0" - "@parcel/watcher-linux-arm64-musl" "2.4.0" - "@parcel/watcher-linux-x64-glibc" "2.4.0" - "@parcel/watcher-linux-x64-musl" "2.4.0" - "@parcel/watcher-win32-arm64" "2.4.0" - "@parcel/watcher-win32-ia32" "2.4.0" - "@parcel/watcher-win32-x64" "2.4.0" - -"@peculiar/asn1-schema@^2.3.6", "@peculiar/asn1-schema@^2.3.8": + "@parcel/watcher-android-arm64" "2.4.1" + "@parcel/watcher-darwin-arm64" "2.4.1" + "@parcel/watcher-darwin-x64" "2.4.1" + "@parcel/watcher-freebsd-x64" "2.4.1" + "@parcel/watcher-linux-arm-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-glibc" "2.4.1" + "@parcel/watcher-linux-arm64-musl" "2.4.1" + "@parcel/watcher-linux-x64-glibc" "2.4.1" + "@parcel/watcher-linux-x64-musl" "2.4.1" + "@parcel/watcher-win32-arm64" "2.4.1" + "@parcel/watcher-win32-ia32" "2.4.1" + "@parcel/watcher-win32-x64" "2.4.1" + +"@peculiar/asn1-schema@^2.3.8": version "2.3.8" resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== @@ -1831,20 +1839,27 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.4.0": - version "1.4.4" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.4.tgz#3f529abca96c8d78cecfd39ddec2148c5df3f8ac" - integrity sha512-VETlZgJqOP3OLRC7AcEYfb6/u05evqsW0Vgs9DyCBATp3FSx7D30Z8ALuDJHsmXCsMblfyTwvB9PLR6IfdlRhg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz#9e57174c02c1291051c553600347e12b81469e10" + integrity sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== dependencies: "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" pvtsutils "^1.3.5" tslib "^2.6.2" - webcrypto-core "^1.7.7" + webcrypto-core "^1.8.0" -"@rc-component/color-picker@~1.5.1": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.5.1.tgz#5d41a10f96aab8eb020999bd397fb4419431814c" - integrity sha512-onyAFhWKXuG4P162xE+7IgaJkPkwM94XlOYnQuu69XdXWMfxpeFi6tpJBsieIMV7EnyLV5J3lDzdLiFeK0iEBA== +"@rc-component/async-validator@^5.0.3": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" + integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== + dependencies: + "@babel/runtime" "^7.24.4" + +"@rc-component/color-picker@~1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.5.3.tgz#f3b0e14bb67ec5ee77d1fd5d261f63dd4fd00449" + integrity sha512-+tGGH3nLmYXTalVe0L8hSZNs73VTP5ueSHwUlDC77KKRaN7G4DS4wcpG5DTDzdcV/Yas+rzA6UGgIyzd8fS4cw== dependencies: "@babel/runtime" "^7.23.6" "@ctrl/tinycolor" "^3.6.1" @@ -1884,21 +1899,21 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/tour@~1.12.1": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.12.2.tgz#2d006d71dd5568526a49bc7868aa228f481596fc" - integrity sha512-2he76Iwf0cVchI70dHCowR5DCWpPRY9+foNoO1h+TD2cZbsGSoEk+m3jEaFPh4ChXYhdzsxp+0siz8/br91JhA== +"@rc-component/tour@~1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.0.tgz#37a66ae5af8eefaf0ab0e22ddd8e6fecdbdc14a7" + integrity sha512-h6hyILDwL+In9GAgRobwRWihLqqsD7Uft3fZGrJ7L4EiyCoxbnNYwzPXDfz7vNDhWeVyvAWQJj9fJCzpI4+b4g== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/portal" "^1.0.0-9" - "@rc-component/trigger" "^1.3.6" + "@rc-component/trigger" "^2.0.0" classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/trigger@^1.17.0", "@rc-component/trigger@^1.18.0", "@rc-component/trigger@^1.18.2", "@rc-component/trigger@^1.3.6", "@rc-component/trigger@^1.5.0", "@rc-component/trigger@^1.7.0": - version "1.18.2" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-1.18.2.tgz#dc52c4c66fa8aaccaf0710498f2429fc05454e3b" - integrity sha512-jRLYgFgjLEPq3MvS87fIhcfuywFSRDaDrYw1FLku7Cm4esszvzTbA0JBsyacAyLrK9rF3TiHFcvoEDMzoD3CTA== +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.0.tgz#503a48b0895a2cfddee0a5b7b11492c3df2a493d" + integrity sha512-QarBCji02YE9aRFhZgRZmOpXBj0IZutRippsVBv85sxvG4FGk/vRxwAlkn3MS9zK5mwbETd86mAVg2tKqTkdJA== dependencies: "@babel/runtime" "^7.23.2" "@rc-component/portal" "^1.1.0" @@ -1913,14 +1928,14 @@ integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== "@repeaterjs/repeater@^3.0.4": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.5.tgz#b77571685410217a548a9c753aa3cdfc215bfc78" - integrity sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA== + version "3.0.6" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" + integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== "@rushstack/eslint-patch@^1.3.3": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.7.0.tgz#b5bc1e081428794f6a4d239707b359404be35ce2" - integrity sha512-Jh4t/593gxs0lJZ/z3NnasKlplXT2f+4y/LZYuaKZW5KAaiVFL/fThhs+17EbUd53jUVJ0QudYCBGbN/psvaqg== + version "1.10.3" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" + integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -1928,9 +1943,9 @@ integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== "@sinonjs/commons@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" - integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" @@ -1949,9 +1964,9 @@ tslib "^2.4.0" "@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== "@tsconfig/node12@^1.0.7": version "1.0.11" @@ -1995,16 +2010,16 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" - integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== + version "7.20.6" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" + integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== dependencies: "@babel/types" "^7.20.7" -"@types/cookie@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803" - integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow== +"@types/cookie@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" + integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== "@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": version "4.1.9" @@ -2013,7 +2028,7 @@ dependencies: "@types/node" "*" -"@types/hoist-non-react-statics@^3.0.1": +"@types/hoist-non-react-statics@^3.3.5": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== @@ -2060,17 +2075,10 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*": - version "20.11.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e" - integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== - dependencies: - undici-types "~5.26.4" - -"@types/node@^18.11.18": - version "18.19.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.8.tgz#c1e42b165e5a526caf1f010747e0522cb2c9c36a" - integrity sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg== +"@types/node@*", "@types/node@^20.14.9": + version "20.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" + integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== dependencies: undici-types "~5.26.4" @@ -2080,9 +2088,9 @@ integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/prop-types@*": - version "15.7.11" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.11.tgz#2596fb352ee96a1379c657734d4b913a613ad563" - integrity sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng== + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== "@types/qrcode.react@^1.0": version "1.0.5" @@ -2092,40 +2100,34 @@ "@types/react" "*" "@types/react-dom@^18.0": - version "18.2.18" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.18.tgz#16946e6cd43971256d874bc3d0a72074bb8571dd" - integrity sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw== + version "18.3.0" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" + integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== dependencies: "@types/react" "*" -"@types/react-highlight-words@^0.16.3": - version "0.16.7" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.16.7.tgz#82fdd2d2abe4c4a5c0da44be749660f6ec42f0c2" - integrity sha512-+upXTIaRd3rGvh1aDQSs9z5X+sV3UM6Jrmjk03GN2GXl4v/+iOJKQj2LZHo6Vp2IoTvMdtxgME26feqo12xXLg== +"@types/react-highlight-words@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c7e3e8302acb5082980e611ce2d93c6e6d3b4022" + integrity sha512-Qm512TiOakvtNzHJ2+TNVHnLn5cJ2wLQV0+LrhuispVth6dRf5b8ydjq3Kc0thpZ7bz4s6RnG6meboAXHWRK+Q== dependencies: "@types/react" "*" "@types/react-test-renderer@^18.0": - version "18.0.7" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz#2cfe657adb3688cdf543995eceb2e062b5a68728" - integrity sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg== + version "18.3.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0" + integrity sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw== dependencies: "@types/react" "*" "@types/react@*", "@types/react@^18.0": - version "18.2.48" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1" - integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== + version "18.3.3" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f" + integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" csstype "^3.0.2" -"@types/scheduler@*": - version "0.16.8" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" - integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== - "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" @@ -2158,36 +2160,36 @@ "@types/yargs-parser" "*" "@typescript-eslint/parser@^5.4.2 || ^6.0.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.19.0.tgz#80344086f362181890ade7e94fc35fe0480bfdf5" - integrity sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow== - dependencies: - "@typescript-eslint/scope-manager" "6.19.0" - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/typescript-estree" "6.19.0" - "@typescript-eslint/visitor-keys" "6.19.0" + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" + integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== + dependencies: + "@typescript-eslint/scope-manager" "6.21.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/typescript-estree" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz#b6d2abb825b29ab70cb542d220e40c61c1678116" - integrity sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ== +"@typescript-eslint/scope-manager@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" + integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== dependencies: - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/visitor-keys" "6.19.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" -"@typescript-eslint/types@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.19.0.tgz#689b0498c436272a6a2059b09f44bcbd90de294a" - integrity sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A== +"@typescript-eslint/types@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" + integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== -"@typescript-eslint/typescript-estree@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz#0813ba364a409afb4d62348aec0202600cb468fa" - integrity sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ== +"@typescript-eslint/typescript-estree@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" + integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== dependencies: - "@typescript-eslint/types" "6.19.0" - "@typescript-eslint/visitor-keys" "6.19.0" + "@typescript-eslint/types" "6.21.0" + "@typescript-eslint/visitor-keys" "6.21.0" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -2195,12 +2197,12 @@ semver "^7.5.4" ts-api-utils "^1.0.1" -"@typescript-eslint/visitor-keys@6.19.0": - version "6.19.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz#4565e0ecd63ca1f81b96f1dd76e49f746c6b2b49" - integrity sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ== +"@typescript-eslint/visitor-keys@6.21.0": + version "6.21.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" + integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== dependencies: - "@typescript-eslint/types" "6.19.0" + "@typescript-eslint/types" "6.21.0" eslint-visitor-keys "^3.4.1" "@umijs/route-utils@^4.0.0": @@ -2213,11 +2215,6 @@ resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== - "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -2291,19 +2288,21 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.3.2" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" - integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== + version "8.3.3" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" -acorn@^8.4.1, acorn@^8.9.0: - version "8.11.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +acorn@^8.11.0, acorn@^8.12.0, acorn@^8.4.1: + version "8.12.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" + integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" - integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== dependencies: debug "^4.3.4" @@ -2362,55 +2361,56 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== antd@^5.0: - version "5.13.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.13.1.tgz#098fd8495a89bbd1f6cba493c56e468fd9741360" - integrity sha512-/qAPsr6UyJPSFZQD9G7kW98GelH2Bajli+1q7CRW4IinYQ0R0UVJckFX11emByhiU4Jd4WNH/hOO+fZtp0eVDA== + version "5.18.3" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.18.3.tgz#71455216af07da202bddfa8cd16976e29dcf575c" + integrity sha512-Dm3P8HBxoo/DiR/QZLj5Mk+rQZsSXxCCArSZACHGiklkkjW6klzlebAElOUr9NyDeFX7UnQ6LVk7vznXlnjTqQ== dependencies: "@ant-design/colors" "^7.0.2" - "@ant-design/cssinjs" "^1.18.2" - "@ant-design/icons" "^5.2.6" - "@ant-design/react-slick" "~1.0.2" + "@ant-design/cssinjs" "^1.21.0" + "@ant-design/icons" "^5.3.7" + "@ant-design/react-slick" "~1.1.2" + "@babel/runtime" "^7.24.7" "@ctrl/tinycolor" "^3.6.1" - "@rc-component/color-picker" "~1.5.1" + "@rc-component/color-picker" "~1.5.3" "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/tour" "~1.12.1" - "@rc-component/trigger" "^1.18.2" + "@rc-component/tour" "~1.15.0" + "@rc-component/trigger" "^2.2.0" classnames "^2.5.1" copy-to-clipboard "^3.3.3" - dayjs "^1.11.10" + dayjs "^1.11.11" qrcode.react "^3.1.0" - rc-cascader "~3.21.0" - rc-checkbox "~3.1.0" - rc-collapse "~3.7.2" - rc-dialog "~9.3.4" - rc-drawer "~7.0.0" - rc-dropdown "~4.1.0" - rc-field-form "~1.41.0" - rc-image "~7.5.1" - rc-input "~1.4.3" - rc-input-number "~8.6.1" - rc-mentions "~2.10.1" - rc-menu "~9.12.4" - rc-motion "^2.9.0" - rc-notification "~5.3.0" + rc-cascader "~3.26.0" + rc-checkbox "~3.3.0" + rc-collapse "~3.7.3" + rc-dialog "~9.5.2" + rc-drawer "~7.2.0" + rc-dropdown "~4.2.0" + rc-field-form "~2.2.1" + rc-image "~7.9.0" + rc-input "~1.5.1" + rc-input-number "~9.1.0" + rc-mentions "~2.14.0" + rc-menu "~9.14.0" + rc-motion "^2.9.2" + rc-notification "~5.6.0" rc-pagination "~4.0.4" - rc-picker "~3.14.6" - rc-progress "~3.5.1" - rc-rate "~2.12.0" + rc-picker "~4.5.0" + rc-progress "~4.0.0" + rc-rate "~2.13.0" rc-resize-observer "^1.4.0" - rc-segmented "~2.2.2" - rc-select "~14.11.0" - rc-slider "~10.5.0" + rc-segmented "~2.3.0" + rc-select "~14.14.0" + rc-slider "~10.6.2" rc-steps "~6.0.1" rc-switch "~4.1.0" - rc-table "~7.37.0" - rc-tabs "~14.0.0" - rc-textarea "~1.6.3" - rc-tooltip "~6.1.3" - rc-tree "~5.8.2" - rc-tree-select "~5.17.0" + rc-table "~7.45.7" + rc-tabs "~15.1.1" + rc-textarea "~1.7.0" + rc-tooltip "~6.2.0" + rc-tree "~5.8.8" + rc-tree-select "~5.21.0" rc-upload "~4.5.2" - rc-util "^5.38.1" + rc-util "^5.43.0" scroll-into-view-if-needed "^3.1.0" throttle-debounce "^5.0.0" @@ -2439,35 +2439,36 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== +aria-query@~5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== dependencies: - dequal "^2.0.3" + deep-equal "^2.0.5" -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== +array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" + call-bind "^1.0.5" + is-array-buffer "^3.0.4" array-find-index@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== -array-includes@^3.1.6, array-includes@^3.1.7: - version "3.1.7" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" - integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== +array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" + integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" is-string "^1.0.7" array-tree-filter@^2.1.0: @@ -2480,16 +2481,29 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +array.prototype.findlast@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" + array.prototype.findlastindex@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" - integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" + integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-shim-unscopables "^1.0.2" array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" @@ -2501,7 +2515,7 @@ array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: +array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== @@ -2511,28 +2525,39 @@ array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.1, array.prototype. es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.tosorted@^1.1.1: +array.prototype.toreversed@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" - integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== + resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" + integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" - get-intrinsic "^1.2.1" -arraybuffer.prototype.slice@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" - integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== +array.prototype.tosorted@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-shim-unscopables "^1.0.2" + +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" asap@^2.0.0, asap@~2.0.3: @@ -2559,39 +2584,29 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-validator@^4.1.0: - version "4.2.5" - resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339" - integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg== - -asynciterator.prototype@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" - integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== - dependencies: - has-symbols "^1.0.3" - auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" -axe-core@=4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf" - integrity sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ== +axe-core@^4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" + integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== -axobject-query@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" - integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== +axobject-query@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" + integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== dependencies: - dequal "^2.0.3" + deep-equal "^2.0.5" babel-jest@27.1.0: version "27.1.0" @@ -2762,22 +2777,22 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" browserslist@^4.22.2: - version "4.22.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" - integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + version "4.23.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" + integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== dependencies: - caniuse-lite "^1.0.30001565" - electron-to-chromium "^1.4.601" + caniuse-lite "^1.0.30001629" + electron-to-chromium "^1.4.796" node-releases "^2.0.14" - update-browserslist-db "^1.0.13" + update-browserslist-db "^1.0.16" bser@2.1.1: version "2.1.1" @@ -2806,14 +2821,16 @@ busboy@1.6.0, busboy@^1.6.0: dependencies: streamsearch "^1.1.0" -call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== +call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" function-bind "^1.1.2" - get-intrinsic "^1.2.1" - set-function-length "^1.1.1" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" caller@1.0.1: version "1.0.1" @@ -2843,10 +2860,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001565: - version "1.0.30001579" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz#45c065216110f46d6274311a4b3fcf6278e0852a" - integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001629: + version "1.0.30001637" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001637.tgz#d9fab531493d9ef46a8ff305e9812190ac463f21" + integrity sha512-1x0qRI1mD1o9e+7mBI7XtzFAP4XszbHaVWsMiGbSPLYekKTJF7K+FNk6AsXH4sUpc+qrsI3pVgf1Jdl/uGkuSQ== capital-case@^1.0.4: version "1.0.4" @@ -2940,9 +2957,9 @@ ci-info@^3.2.0: integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" - integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + version "1.3.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: version "2.5.1" @@ -3085,10 +3102,10 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" - integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +cookie@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" + integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== copy-to-clipboard@^3.3.3: version "3.3.3" @@ -3152,10 +3169,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-hash@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-2.0.1.tgz#46c3732e65a078ea06b8b4ae686db41216f81213" - integrity sha512-t4mkp7Vh6MuCZRBf0XLzBOfhkH3nW6YEAotMDSjshVQ1GffCMGdPLSr7pKH0rdXY02jTjAZ7QW2apD0buaZXcQ== +crypto-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-3.0.0.tgz#1f217b7134c5e05adc013d1edce67c64fa459f7f" + integrity sha512-5l5xGtzuvGTU28GXxGV1JYVFou68buZWpkV1Fx5hIDRPnfbQ8KzabTlNIuDIeSCYGVPFehupzDqlnbXm2IXmdQ== csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" @@ -3167,15 +3184,42 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + dataloader@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== -dayjs@^1.0, dayjs@^1.11.10: - version "1.11.10" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" - integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== +dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: + version "1.11.11" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" + integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== debounce@^1.2.0: version "1.2.1" @@ -3183,9 +3227,9 @@ debounce@^1.2.0: integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" @@ -3207,9 +3251,33 @@ decamelize@^1.2.0: integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== dedent@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" - integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== + +deep-equal@^2.0.5: + version "2.2.3" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" + integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.5" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.2" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.13" deep-is@^0.1.3: version "0.1.4" @@ -3228,14 +3296,14 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-data-property@^1.0.1, define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: - get-intrinsic "^1.2.1" + es-define-property "^1.0.0" + es-errors "^1.3.0" gopd "^1.0.1" - has-property-descriptors "^1.0.0" define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" @@ -3251,11 +3319,6 @@ dependency-graph@^0.11.0: resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== -dequal@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -3303,13 +3366,6 @@ doctrine@^2.1.0: dependencies: esutils "^2.0.2" -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -3319,19 +3375,19 @@ dot-case@^3.0.4: tslib "^2.0.3" dotenv@^16.0.0: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + version "16.4.5" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" + integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== dset@^3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== -electron-to-chromium@^1.4.601: - version "1.4.637" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.637.tgz#ed8775cf5e0c380c3e8452e9818a0e4b7a671ac4" - integrity sha512-G7j3UCOukFtxVO1vWrPQUoDk3kL70mtvjc/DC/k2o7lE0wAdq+Vwp1ipagOow+BH0uVztFysLWbkM/RTIrbK3w== +electron-to-chromium@^1.4.796: + version "1.4.812" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz#21b78709c5a13af5d5c688d135a22dcea7617acf" + integrity sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg== emittery@^0.13.1: version "0.13.1" @@ -3354,9 +3410,9 @@ emojis-list@^3.0.0: integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== enhanced-resolve@^5.12.0: - version "5.15.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + version "5.17.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" + integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3368,81 +3424,122 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1: - version "1.22.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" - integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.2" - available-typed-arrays "^1.0.5" - call-bind "^1.0.5" - es-set-tostringtag "^2.0.1" +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" es-to-primitive "^1.2.1" function.prototype.name "^1.1.6" - get-intrinsic "^1.2.2" - get-symbol-description "^1.0.0" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" globalthis "^1.0.3" gopd "^1.0.1" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" has-symbols "^1.0.3" - hasown "^2.0.0" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" is-callable "^1.2.7" - is-negative-zero "^2.0.2" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" + is-shared-array-buffer "^1.0.3" is-string "^1.0.7" - is-typed-array "^1.1.12" + is-typed-array "^1.1.13" is-weakref "^1.0.2" object-inspect "^1.13.1" object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - safe-array-concat "^1.0.1" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.8" - string.prototype.trimend "^1.0.7" - string.prototype.trimstart "^1.0.7" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" unbox-primitive "^1.0.2" - which-typed-array "^1.1.13" + which-typed-array "^1.1.15" -es-iterator-helpers@^1.0.12, es-iterator-helpers@^1.0.15: - version "1.0.15" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" - integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== dependencies: - asynciterator.prototype "^1.0.0" call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-iterator-helpers@^1.0.19: + version "1.0.19" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" + integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== + dependencies: + call-bind "^1.0.7" define-properties "^1.2.1" - es-abstract "^1.22.1" - es-set-tostringtag "^2.0.1" - function-bind "^1.1.1" - get-intrinsic "^1.2.1" + es-abstract "^1.23.3" + es-errors "^1.3.0" + es-set-tostringtag "^2.0.3" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" globalthis "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" has-symbols "^1.0.3" - internal-slot "^1.0.5" + internal-slot "^1.0.7" iterator.prototype "^1.1.2" - safe-array-concat "^1.0.1" + safe-array-concat "^1.1.2" -es-set-tostringtag@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" - integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== dependencies: - get-intrinsic "^1.2.2" - has-tostringtag "^1.0.0" - hasown "^2.0.0" + es-errors "^1.3.0" + +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" -es-shim-unscopables@^1.0.0: +es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== @@ -3458,10 +3555,10 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escalade@^3.1.1, escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== escape-string-regexp@^1.0.5: version "1.0.5" @@ -3478,7 +3575,7 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@^13.1.4: +eslint-config-next@^13.0.0: version "13.5.6" resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.5.6.tgz#3a5a6222d5cb32256760ad68ab8e976e866a08c8" integrity sha512-o8pQsUHTo9aHqJ2YiZDym5gQAMRf7O2HndHo/JZeY7TDD+W4hk6Ma8Vw54RHiBeb7OWWO5dPirQB+Is/aVQ7Kg== @@ -3516,9 +3613,9 @@ eslint-import-resolver-typescript@^3.5.2: is-glob "^4.0.3" eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" - integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + version "2.8.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" + integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== dependencies: debug "^3.2.7" @@ -3546,102 +3643,105 @@ eslint-plugin-import@^2.28.1: tsconfig-paths "^3.15.0" eslint-plugin-jsx-a11y@^6.7.1: - version "6.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz#2fa9c701d44fcd722b7c771ec322432857fcbad2" - integrity sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA== + version "6.9.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz#67ab8ff460d4d3d6a0b4a570e9c1670a0a8245c8" + integrity sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g== dependencies: - "@babel/runtime" "^7.23.2" - aria-query "^5.3.0" - array-includes "^3.1.7" + aria-query "~5.1.3" + array-includes "^3.1.8" array.prototype.flatmap "^1.3.2" ast-types-flow "^0.0.8" - axe-core "=4.7.0" - axobject-query "^3.2.1" + axe-core "^4.9.1" + axobject-query "~3.1.1" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" - es-iterator-helpers "^1.0.15" - hasown "^2.0.0" + es-iterator-helpers "^1.0.19" + hasown "^2.0.2" jsx-ast-utils "^3.3.5" language-tags "^1.0.9" minimatch "^3.1.2" - object.entries "^1.1.7" - object.fromentries "^2.0.7" + object.fromentries "^2.0.8" + safe-regex-test "^1.0.3" + string.prototype.includes "^2.0.0" "eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": - version "4.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" - integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + version "4.6.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" + integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== eslint-plugin-react@^7.33.2: - version "7.33.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" - integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== + version "7.34.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz#9965f27bd1250a787b5d4cfcc765e5a5d58dcb7b" + integrity sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA== dependencies: - array-includes "^3.1.6" - array.prototype.flatmap "^1.3.1" - array.prototype.tosorted "^1.1.1" + array-includes "^3.1.8" + array.prototype.findlast "^1.2.5" + array.prototype.flatmap "^1.3.2" + array.prototype.toreversed "^1.1.2" + array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.12" + es-iterator-helpers "^1.0.19" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.6" - object.fromentries "^2.0.6" - object.hasown "^1.1.2" - object.values "^1.1.6" + object.entries "^1.1.8" + object.fromentries "^2.0.8" + object.hasown "^1.1.4" + object.values "^1.2.0" prop-types "^15.8.1" - resolve "^2.0.0-next.4" + resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.8" + string.prototype.matchall "^4.0.11" -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== +eslint-scope@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc" + integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.0.0: - version "8.56.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" - integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== +eslint-visitor-keys@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" + integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== + +eslint@^9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.5.0.tgz#11856034b94a9e1a02cfcc7e96a9f0956963cd2f" + integrity sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.56.0" - "@humanwhocodes/config-array" "^0.11.13" + "@eslint/config-array" "^0.16.0" + "@eslint/eslintrc" "^3.1.0" + "@eslint/js" "9.5.0" "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.3.0" "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" - doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" + eslint-scope "^8.0.1" + eslint-visitor-keys "^4.0.0" + espree "^10.0.1" + esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" + file-entry-cache "^8.0.0" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" @@ -3651,21 +3751,21 @@ eslint@^8.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== +espree@^10.0.1: + version "10.1.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" + integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== dependencies: - acorn "^8.9.0" + acorn "^8.12.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" + eslint-visitor-keys "^4.0.0" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.4.2: +esquery@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -3780,9 +3880,9 @@ fast-url-parser@^1.1.3: punycode "^1.3.2" fastq@^1.6.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" - integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== dependencies: reusify "^1.0.4" @@ -3818,17 +3918,17 @@ figures@^3.0.0: dependencies: escape-string-regexp "^1.0.5" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: - flat-cache "^3.0.4" + flat-cache "^4.0.0" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -3848,19 +3948,18 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^3.0.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" - integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" - keyv "^4.5.3" - rimraf "^3.0.2" + keyv "^4.5.4" flatted@^3.2.9: - version "3.2.9" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" - integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== for-each@^0.3.3: version "0.3.3" @@ -3879,7 +3978,7 @@ fsevents@^2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1, function-bind@^1.1.2: +function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== @@ -3909,11 +4008,12 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== dependencies: + es-errors "^1.3.0" function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" @@ -3929,18 +4029,19 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" get-tsconfig@^4.5.0: - version "4.7.2" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" - integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== dependencies: resolve-pkg-maps "^1.0.0" @@ -3992,19 +4093,18 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: - version "13.24.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" - integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== - dependencies: - type-fest "^0.20.2" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: - define-properties "^1.1.3" + define-properties "^1.2.1" + gopd "^1.0.1" globby@11.0.4: version "11.0.4" @@ -4042,11 +4142,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - graphql-config@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.5.0.tgz#257c2338950b8dce295a27f75c5f6c39f8f777b2" @@ -4110,9 +4205,9 @@ graphql-ws@5.12.1: integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== graphql@^16.0.1: - version "16.8.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" - integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== + version "16.9.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" + integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" @@ -4129,34 +4224,34 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" - integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: - get-intrinsic "^1.2.2" + es-define-property "^1.0.0" -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== +has-proto@^1.0.1, has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: - has-symbols "^1.0.2" + has-symbols "^1.0.3" -hasown@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" - integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" @@ -4173,7 +4268,7 @@ highlight-words-core@^1.2.0: resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa" integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -4224,9 +4319,9 @@ ieee754@^1.1.13: integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.4, ignore@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" - integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== immutable@~3.7.6: version "3.7.6" @@ -4298,12 +4393,12 @@ inquirer@^8.0.0: through "^2.3.6" wrap-ansi "^6.0.1" -internal-slot@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" - integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== +internal-slot@^1.0.4, internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== dependencies: - get-intrinsic "^1.2.2" + es-errors "^1.3.0" hasown "^2.0.0" side-channel "^1.0.4" @@ -4322,14 +4417,21 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== dependencies: call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" is-arrayish@^0.2.1: version "0.2.1" @@ -4364,11 +4466,18 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + version "2.14.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" + integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== dependencies: - hasown "^2.0.0" + hasown "^2.0.2" + +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" @@ -4425,15 +4534,15 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" -is-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== +is-map@^2.0.2, is-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-object@^1.0.4: version "1.0.7" @@ -4467,17 +4576,17 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-set@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== +is-set@^2.0.2, is-set@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== +is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" is-stream@^2.0.0: version "2.0.1" @@ -4498,12 +4607,12 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== dependencies: - which-typed-array "^1.1.11" + which-typed-array "^1.1.14" is-typedarray@^1.0.0: version "1.0.0" @@ -4529,10 +4638,10 @@ is-upper-case@^2.0.2: dependencies: tslib "^2.0.3" -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== +is-weakmap@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== is-weakref@^1.0.2: version "1.0.2" @@ -4541,13 +4650,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== +is-weakset@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" + integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" + call-bind "^1.0.7" + get-intrinsic "^1.2.4" is-windows@^1.0.1: version "1.0.2" @@ -4586,13 +4695,13 @@ istanbul-lib-instrument@^5.0.4: semver "^6.3.0" istanbul-lib-instrument@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" - integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + version "6.0.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" + integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" istanbul-lib-coverage "^3.2.0" semver "^7.5.4" @@ -4615,9 +4724,9 @@ istanbul-lib-source-maps@^4.0.0: source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.6" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a" - integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg== + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -5056,14 +5165,14 @@ jiti@1.17.1: integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== jiti@^1.17.1: - version "1.21.0" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" - integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== + version "1.21.6" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" + integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== jose@^4.11.4: - version "4.15.4" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" - integrity sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ== + version "4.15.7" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.7.tgz#96ad68d786632bd03c9068aa281810dbbe1b60d8" + integrity sha512-L7ioP+JAuZe8v+T5+zVI9Tx8LtU8BL7NxkyDFVMv+Qr3JW0jSoYDedLtodaXwfqMpeCyx4WXFNyu9tJt4WvC1A== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5162,7 +5271,7 @@ jsonify@^0.0.1: object.assign "^4.1.4" object.values "^1.1.6" -keyv@^4.5.3: +keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -5175,9 +5284,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== language-subtag-registry@^0.3.20: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== + version "0.3.23" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== language-tags@^1.0.9: version "1.0.9" @@ -5322,13 +5431,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - make-dir@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -5381,11 +5483,11 @@ meros@^1.2.1: integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + version "4.0.7" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" + integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== dependencies: - braces "^3.0.2" + braces "^3.0.3" picomatch "^2.3.1" mimic-fn@^2.1.0: @@ -5456,7 +5558,7 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^13.1.4: +next@^13.0.0: version "13.5.6" resolved "https://registry.yarnpkg.com/next/-/next-13.5.6.tgz#e964b5853272236c37ce0dd2c68302973cf010b1" integrity sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw== @@ -5488,9 +5590,9 @@ no-case@^3.0.4: tslib "^2.0.3" node-addon-api@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.0.0.tgz#8136add2f510997b3b94814f4af1cce0b0e3962e" - integrity sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA== + version "7.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" + integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== node-fetch@^2.6.1, node-fetch@^2.6.12: version "2.7.0" @@ -5561,17 +5663,25 @@ object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1, object-inspect@^1.9.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.4: +object.assign@^4.1.4, object.assign@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -5581,50 +5691,51 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" -object.entries@^1.1.6, object.entries@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" - integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== +object.entries@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" + integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" -object.fromentries@^2.0.6, object.fromentries@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" - integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== +object.fromentries@^2.0.7, object.fromentries@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" object.groupby@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" - integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" -object.hasown@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" - integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== +object.hasown@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" + integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== dependencies: - define-properties "^1.2.0" - es-abstract "^1.22.1" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-object-atoms "^1.0.0" -object.values@^1.1.6, object.values@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" - integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== +object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" + integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" omit.js@^2.0.2: version "2.0.2" @@ -5656,16 +5767,16 @@ optimism@^0.18.0: tslib "^2.3.0" optionator@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" - integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: - "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" + word-wrap "^1.2.5" ora@^5.4.1: version "5.4.1" @@ -5832,10 +5943,10 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -5854,6 +5965,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + postcss@8.4.31: version "8.4.31" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" @@ -5868,10 +5984,10 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.8.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" + integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== pretty-format@^29.7.0: version "29.7.0" @@ -5917,9 +6033,9 @@ punycode@^2.1.0: integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^6.0.0: - version "6.0.4" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7" - integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA== + version "6.1.0" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" + integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== pvtsutils@^1.3.2, pvtsutils@^1.3.5: version "1.3.5" @@ -5943,41 +6059,41 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -rc-cascader@~3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.21.0.tgz#285332cfe97e75a2d6829887bbbcd6cfa23f2b1d" - integrity sha512-7aADjbfqiR4HrTHG9S019p2jeKM/AxISPA5+sBJR7Mlhm/i+lR7VjBju3KQulJNJLKNEnQYg4TFhcPf2SLua9g== +rc-cascader@~3.26.0: + version "3.26.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.26.0.tgz#1bcc9c29451047dc99e28fdd125c94828d7ddf76" + integrity sha512-L1dml383TPSJD1I11YwxuVbmqaJY64psZqFp1ETlgl3LEOwDu76Cyl11fw5dmjJhMlUWwM5dECQfqJgfebhUjg== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.11.0-0" + rc-select "~14.14.0" rc-tree "~5.8.1" rc-util "^5.37.0" -rc-checkbox@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.1.0.tgz#6be0d9d8de2cc96fb5e37f9036a1c3e360d0a42d" - integrity sha512-PAwpJFnBa3Ei+5pyqMMXdcKYKNBMS+TvSDiLdDnARnMJHC8ESxwPfm4Ao1gJiKtWLdmGfigascnCpwrHFgoOBQ== +rc-checkbox@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.3.0.tgz#0ffcb65ab78c7d2fcd1a0d6554af36786516bd02" + integrity sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.3.2" rc-util "^5.25.2" -rc-collapse@~3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.2.tgz#d11538ff9c705a5c988d9a4dfcc051a919692fe3" - integrity sha512-ZRw6ipDyOnfLFySxAiCMdbHtb5ePAsB9mT17PA6y1mRD/W6KHRaZeb5qK/X9xDV1CqgyxMpzw0VdS74PCcUk4A== +rc-collapse@~3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.3.tgz#68161683d8fd1004bef4eb281fc106f3c8dc16eb" + integrity sha512-60FJcdTRn0X5sELF18TANwtVi7FtModq649H11mYF1jh83DniMoM4MqY627sEKRCTm4+WXfGDcB7hY5oW6xhyw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.3.4" rc-util "^5.27.0" -rc-dialog@~9.3.4: - version "9.3.4" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.3.4.tgz#e0decb3d4a0dbe36524a67ed2f8fe2daa4b7b73c" - integrity sha512-975X3018GhR+EjZFbxA2Z57SX5rnu0G0/OxFgMMvZK4/hQWEm3MHaNvP4wXpxYDoJsp+xUvVW+GB9CMMCm81jA== +rc-dialog@~9.5.2: + version "9.5.2" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.5.2.tgz#4cf7cca23aedb6fd3d9344ea8ffd14daa94ee3a0" + integrity sha512-qVUjc8JukG+j/pNaHVSRa2GO2/KbV2thm7yO4hepQ902eGdYK913sGkwg/fh9yhKYV1ql3BKIN2xnud3rEXAPw== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/portal" "^1.0.0-8" @@ -5985,113 +6101,113 @@ rc-dialog@~9.3.4: rc-motion "^2.3.0" rc-util "^5.21.0" -rc-drawer@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.0.0.tgz#5632ad00284431665d4ff622b274d234fc9eefb2" - integrity sha512-ePcS4KtQnn57bCbVXazHN2iC8nTPCXlWEIA/Pft87Pd9U7ZeDkdRzG47jWG2/TAFXFlFltRAMcslqmUM8NPCGA== +rc-drawer@~7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.2.0.tgz#8d7de2f1fd52f3ac5a25f54afbb8ac14c62e5663" + integrity sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.23.9" "@rc-component/portal" "^1.1.1" classnames "^2.2.6" rc-motion "^2.6.1" - rc-util "^5.36.0" + rc-util "^5.38.1" -rc-dropdown@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.1.0.tgz#418a68939631520de80d0865d02b440eeeb4168e" - integrity sha512-VZjMunpBdlVzYpEdJSaV7WM7O0jf8uyDjirxXLZRNZ+tAC+NzD3PXPEtliFwGzVwBBdCmGuSqiS9DWcOLxQ9tw== +rc-dropdown@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.0.tgz#c6052fcfe9c701487b141e411cdc277dc7c6f061" + integrity sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng== dependencies: "@babel/runtime" "^7.18.3" - "@rc-component/trigger" "^1.7.0" + "@rc-component/trigger" "^2.0.0" classnames "^2.2.6" rc-util "^5.17.0" -rc-field-form@~1.41.0: - version "1.41.0" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.41.0.tgz#660ed8691fdabbc1e5b1ee6b5e0e4f534b295cf0" - integrity sha512-k9AS0wmxfJfusWDP/YXWTpteDNaQ4isJx9UKxx4/e8Dub4spFeZ54/EuN2sYrMRID/+hUznPgVZeg+Gf7XSYCw== +rc-field-form@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.2.1.tgz#0a8c76a0103535c229311dac7f91d32ea13cd3bc" + integrity sha512-uoNqDoR7A4tn4QTSqoWPAzrR7ZwOK5I+vuZ/qdcHtbKx+ZjEsTg7QXm2wk/jalDiSksAQmATxL0T5LJkRREdIA== dependencies: "@babel/runtime" "^7.18.0" - async-validator "^4.1.0" + "@rc-component/async-validator" "^5.0.3" rc-util "^5.32.2" -rc-image@~7.5.1: - version "7.5.1" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.5.1.tgz#39a93354e14fe3e5eaafd9c9464e8fe3c6c171a0" - integrity sha512-Z9loECh92SQp0nSipc0MBuf5+yVC05H/pzC+Nf8xw1BKDFUJzUeehYBjaWlxly8VGBZJcTHYri61Fz9ng1G3Ag== +rc-image@~7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.9.0.tgz#2d700a5cf891bb3d0d800b7c38348927ebb9f49b" + integrity sha512-l4zqO5E0quuLMCtdKfBgj4Suv8tIS011F5k1zBBlK25iMjjiNHxA0VeTzGFtUZERSA45gvpXDg8/P6qNLjR25g== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" classnames "^2.2.6" - rc-dialog "~9.3.4" + rc-dialog "~9.5.2" rc-motion "^2.6.2" rc-util "^5.34.1" -rc-input-number@~8.6.1: - version "8.6.1" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-8.6.1.tgz#f0ebc66c34c8775a3245953521abcbd240d7fe03" - integrity sha512-gaAMUKtUKLktJ3Yx93tjgYY1M0HunnoqzPEqkb9//Ydup4DcG0TFL9yHBA3pgVdNIt5f0UWyHCgFBj//JxeD6A== +rc-input-number@~9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.1.0.tgz#fd577db284b65548c156500322a2feaa04321565" + integrity sha512-NqJ6i25Xn/AgYfVxynlevIhX3FuKlMwIFpucGG1h98SlK32wQwDK0zhN9VY32McOmuaqzftduNYWWooWz8pXQA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" - rc-input "~1.4.0" - rc-util "^5.28.0" + rc-input "~1.5.0" + rc-util "^5.40.1" -rc-input@~1.4.0, rc-input@~1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.4.3.tgz#03f720f88cb56924cd1d5f78f859f41f2d62d1e0" - integrity sha512-aHyQUAIRmTlOnvk5EcNqEpJ+XMtfMpYRAJayIlJfsvvH9cAKUWboh4egm23vgMA7E+c/qm4BZcnrDcA960GC1w== +rc-input@~1.5.0, rc-input@~1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.5.1.tgz#36d37eb045f1fa17de7da1a3fab94edfa331ab92" + integrity sha512-+nOzQJDeIfIpNP/SgY45LXSKbuMlp4Yap2y8c+ZpU7XbLmNzUd6+d5/S75sA/52jsVE6S/AkhkkDEAOjIu7i6g== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~2.10.1: - version "2.10.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.10.1.tgz#842a227cb1f9f798cbf0c906a519f7de3ab8a9b9" - integrity sha512-72qsEcr/7su+a07ndJ1j8rI9n0Ka/ngWOLYnWMMv0p2mi/5zPwPrEDTt6Uqpe8FWjWhueDJx/vzunL6IdKDYMg== +rc-mentions@~2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.14.0.tgz#7cad133de2e95b7478c0ebb5d2a4315dbe9bda29" + integrity sha512-qKR59FMuF8PK4ZqsbWX3UuA5P1M/snzyqV6Yt3y1DCFbCEdqUGIBgQp6vEfLCO6Z0RoRFlzXtCeSlBTcDDpg1A== dependencies: "@babel/runtime" "^7.22.5" - "@rc-component/trigger" "^1.5.0" + "@rc-component/trigger" "^2.0.0" classnames "^2.2.6" - rc-input "~1.4.0" - rc-menu "~9.12.0" - rc-textarea "~1.6.1" + rc-input "~1.5.0" + rc-menu "~9.14.0" + rc-textarea "~1.7.0" rc-util "^5.34.1" -rc-menu@~9.12.0, rc-menu@~9.12.4: - version "9.12.4" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.12.4.tgz#4959b5eeb780be7ff52aac31952b35efca46b9a3" - integrity sha512-t2NcvPLV1mFJzw4F21ojOoRVofK2rWhpKPx69q2raUsiHPDP6DDevsBILEYdsIegqBeSXoWs2bf6CueBKg3BFg== +rc-menu@~9.14.0: + version "9.14.1" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.14.1.tgz#5c2aea72bdce421e9d50bf721ad8b76c154ae66f" + integrity sha512-5wlRb3M8S4yGlWhSoEYJ7ZVRElyScdcpUHxgiLxkeig1tEdyKrnED3B2fhpN0Rrpdp9jyhnmZR/Lwq2fH5VvDQ== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^1.17.0" + "@rc-component/trigger" "^2.0.0" classnames "2.x" rc-motion "^2.4.3" rc-overflow "^1.3.1" rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.0.tgz#9e18a1b8d61e528a97369cf9a7601e9b29205710" - integrity sha512-XIU2+xLkdIr1/h6ohPZXyPBMvOmuyFZQ/T0xnawz+Rh+gh4FINcnZmMT5UTIj6hgI0VLDjTaPeRd+smJeSPqiQ== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.2.tgz#f7c6d480250df8a512d0cfdce07ff3da906958cf" + integrity sha512-fUAhHKLDdkAXIDLH0GYwof3raS58dtNUmzLF2MeiR8o6n4thNpSDQhOqQzWE4WfFZDCi9VEN8n7tiB7czREcyw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.21.0" + rc-util "^5.43.0" -rc-notification@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.3.0.tgz#e31c86fe2350598ade8cff383babd1befa7a94fe" - integrity sha512-WCf0uCOkZ3HGfF0p1H4Sgt7aWfipxORWTPp7o6prA3vxwtWhtug3GfpYls1pnBp4WA+j8vGIi5c2/hQRpGzPcQ== +rc-notification@~5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.0.tgz#1639aa30686d79ee4bb8ace05a698a5a104aaa74" + integrity sha512-TGQW5T7waOxLwgJG7fXcw8l7AQiFOjaZ7ISF5PrU526nunHRNcTMuzKihQHaF4E/h/KfOCDk3Mv8eqzbu2e28w== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.9.0" rc-util "^5.20.1" -rc-overflow@^1.3.1: +rc-overflow@^1.3.1, rc-overflow@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== @@ -6110,29 +6226,31 @@ rc-pagination@~4.0.4: classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~3.14.6: - version "3.14.6" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-3.14.6.tgz#60fc34f9883272e10f6c593fa6d82e7e7a70781b" - integrity sha512-AdKKW0AqMwZsKvIpwUWDUnpuGKZVrbxVTZTNjcO+pViGkjC1EBcjMgxVe8tomOEaIHJL5Gd13vS8Rr3zzxWmag== +rc-picker@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.5.0.tgz#ae7a028ed6184e0ef40a2c8aaeb9d5dbef89d4b8" + integrity sha512-suqz9bzuhBQlf7u+bZd1bJLPzhXpk12w6AjQ9BTPTiFwexVZgUKViG1KNLyfFvW6tCUZZK0HmCCX7JAyM+JnCg== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^1.5.0" + "@rc-component/trigger" "^2.0.0" classnames "^2.2.1" - rc-util "^5.30.0" + rc-overflow "^1.3.2" + rc-resize-observer "^1.4.0" + rc-util "^5.38.1" -rc-progress@~3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.5.1.tgz#a3cdfd2fe04eb5c3d43fa1c69e7dd70c73b102ae" - integrity sha512-V6Amx6SbLRwPin/oD+k1vbPrO8+9Qf8zW1T8A7o83HdNafEVvAxPV5YsgtKFP+Ud5HghLj33zKOcEHrcrUGkfw== +rc-progress@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" + integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.6" rc-util "^5.16.1" -rc-rate@~2.12.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.12.0.tgz#0182deffed3b009cdcc61660da8746c39ed91ed5" - integrity sha512-g092v5iZCdVzbjdn28FzvWebK2IutoVoiTeqoLTj9WM7SjA/gOJIw5/JFZMRyJYYVe1jLAU2UhAfstIpCNRozg== +rc-rate@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.0.tgz#642f591ccf55c3a5d84d8d212caf1f7951d203a8" + integrity sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" @@ -6148,37 +6266,37 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-util "^5.38.0" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.2.2.tgz#a34f12ce6c0975fc3042ae7656bcd18e1744798e" - integrity sha512-Mq52M96QdHMsNdE/042ibT5vkcGcD5jxKp7HgPC2SRofpia99P5fkfHy1pEaajLMF/kj0+2Lkq1UZRvqzo9mSA== +rc-segmented@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.3.0.tgz#b3fe080fb434a266c02e30bb62a47d2c6e094341" + integrity sha512-I3FtM5Smua/ESXutFfb8gJ8ZPcvFR+qUgeeGFQHBOvRiRKyAk4aBE5nfqrxXx+h8/vn60DQjOt6i4RNtrbOobg== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.11.0, rc-select@~14.11.0-0: - version "14.11.0" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.11.0.tgz#37c63acea92ac1dcd0e1b7ebd9c0614b53dd8346" - integrity sha512-8J8G/7duaGjFiTXCBLWfh5P+KDWyA3KTlZDfV3xj/asMPqB2cmxfM+lH50wRiPIRsCQ6EbkCFBccPuaje3DHIg== +rc-select@~14.14.0: + version "14.14.0" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.14.0.tgz#110771fad72496843b245236bcb0007553b9fe0d" + integrity sha512-Uo2wulrjoPPRLCPd7zlK4ZFVJxlTN//yp1xWP/U+TUOQCyXrT+Duvq/Si5OzVcmQyWAUSbsplc2OwNNhvbOeKQ== dependencies: "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^1.5.0" + "@rc-component/trigger" "^2.1.1" classnames "2.x" rc-motion "^2.0.1" rc-overflow "^1.3.1" rc-util "^5.16.1" rc-virtual-list "^3.5.2" -rc-slider@~10.5.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.5.0.tgz#1bd4853d114cb3403b67c485125887adb6a2a117" - integrity sha512-xiYght50cvoODZYI43v3Ylsqiw14+D7ELsgzR40boDZaya1HFa1Etnv9MDkQE8X/UrXAffwv2AcNAhslgYuDTw== +rc-slider@~10.6.2: + version "10.6.2" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.6.2.tgz#8bd3b63b24f2f3682ea1bf86d021073189cf33eb" + integrity sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" - rc-util "^5.27.0" + rc-util "^5.36.0" rc-steps@~6.0.1: version "6.0.1" @@ -6198,66 +6316,66 @@ rc-switch@~4.1.0: classnames "^2.2.1" rc-util "^5.30.0" -rc-table@~7.37.0: - version "7.37.0" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.37.0.tgz#e7cbf5c577bef27322fcd75fbdda99d1d6e70627" - integrity sha512-hEB17ktLRVfVmdo+U8MjGr+PuIgdQ8Cxj/N5lwMvP/Az7TOrQxwTMLVEDoj207tyPYLTWifHIF9EJREWwyk67g== +rc-table@~7.45.7: + version "7.45.7" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.45.7.tgz#f7c509e05c677a30ad5b212750122da6f5318004" + integrity sha512-wi9LetBL1t1csxyGkMB2p3mCiMt+NDexMlPbXHvQFmBBAsMxrgNSAPwUci2zDLUq9m8QdWc1Nh8suvrpy9mXrg== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/context" "^1.4.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" rc-util "^5.37.0" - rc-virtual-list "^3.11.1" + rc-virtual-list "^3.14.2" -rc-tabs@~14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-14.0.0.tgz#ac1798959e1835c4d5216c9dae80943b7e5505f6" - integrity sha512-lp1YWkaPnjlyhOZCPrAWxK6/P6nMGX/BAZcAC3nuVwKz0Byfp+vNnQKK8BRCP2g/fzu+SeB5dm9aUigRu3tRkQ== +rc-tabs@~15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.1.1.tgz#99f4c7647e01d3e22216d94222d717e928ed98d0" + integrity sha512-Tc7bJvpEdkWIVCUL7yQrMNBJY3j44NcyWS48jF/UKMXuUlzaXK+Z/pEL5LjGcTadtPvVmNqA40yv7hmr+tCOAw== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" - rc-dropdown "~4.1.0" - rc-menu "~9.12.0" + rc-dropdown "~4.2.0" + rc-menu "~9.14.0" rc-motion "^2.6.2" rc-resize-observer "^1.0.0" rc-util "^5.34.1" -rc-textarea@~1.6.1, rc-textarea@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.6.3.tgz#3f294fdf5dfadbe9d0e7b695cea4da557728a9be" - integrity sha512-8k7+8Y2GJ/cQLiClFMg8kUXOOdvcFQrnGeSchOvI2ZMIVvX5a3zQpLxoODL0HTrvU63fPkRmMuqaEcOF9dQemA== +rc-textarea@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.7.0.tgz#115c421359dddee58c601008ec2209b41cb8f8df" + integrity sha512-UxizYJkWkmxP3zofXgc487QiGyDmhhheDLLjIWbFtDmiru1ls30KpO8odDaPyqNUIy9ugj5djxTEuezIn6t3Jg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" - rc-input "~1.4.0" + rc-input "~1.5.0" rc-resize-observer "^1.0.0" rc-util "^5.27.0" -rc-tooltip@~6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.1.3.tgz#83b97004a1ab918ed4936bbf089bc754254efd1b" - integrity sha512-HMSbSs5oieZ7XddtINUddBLSVgsnlaSb3bZrzzGWjXa7/B7nNedmsuz72s7EWFEro9mNa7RyF3gOXKYqvJiTcQ== +rc-tooltip@~6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.0.tgz#4dd7575674137a5b14f118a5c16435d3f5e4a9c9" + integrity sha512-iS/3iOAvtDh9GIx1ulY7EFUXUtktFccNLsARo3NPgLf0QW9oT0w3dA9cYWlhqAKmD+uriEwdWz1kH0Qs4zk2Aw== dependencies: "@babel/runtime" "^7.11.2" - "@rc-component/trigger" "^1.18.0" + "@rc-component/trigger" "^2.0.0" classnames "^2.3.1" -rc-tree-select@~5.17.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.17.0.tgz#be5bcad09b5874bfcff14be758e76dd992e42359" - integrity sha512-7sRGafswBhf7n6IuHyCEFCildwQIgyKiV8zfYyUoWfZEFdhuk7lCH+DN0aHt+oJrdiY9+6Io/LDXloGe01O8XQ== +rc-tree-select@~5.21.0: + version "5.21.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.21.0.tgz#4ff94d5c28cba9810c4970d1f12ab7248c3b6e55" + integrity sha512-w+9qEu6zh0G3wt9N/hzWNSnqYH1i9mH1Nqxo0caxLRRFXF5yZWYmpCDoDTMdQM1Y4z3Q5yj08qyrPH/d4AtumA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.11.0-0" + rc-select "~14.14.0" rc-tree "~5.8.1" rc-util "^5.16.1" -rc-tree@~5.8.1, rc-tree@~5.8.2: - version "5.8.2" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.2.tgz#ed3a3f7c56597bbeab3303407a9e1739bbf15621" - integrity sha512-xH/fcgLHWTLmrSuNphU8XAqV7CdaOQgm4KywlLGNoTMhDAcNR3GVNP6cZzb0GrKmIZ9yae+QLot/cAgUdPRMzg== +rc-tree@~5.8.1, rc-tree@~5.8.8: + version "5.8.8" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.8.tgz#650a13ec825a5a4feec6bbaf6a380465986ee0db" + integrity sha512-S+mCMWo91m5AJqjz3PdzKilGgbFm7fFJRFiTDOcoRbD7UfMOPnerXwMworiga0O2XIo383UoWuEfeHs1WOltag== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6274,40 +6392,40 @@ rc-upload@~4.5.2: classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.28.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1: - version "5.38.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.38.1.tgz#4915503b89855f5c5cd9afd4c72a7a17568777bb" - integrity sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0: + version "5.43.0" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" + integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== dependencies: "@babel/runtime" "^7.18.3" react-is "^18.2.0" -rc-virtual-list@^3.11.1, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.11.3" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.11.3.tgz#77d4e12e20c1ba314b43c0e37e118296674c5401" - integrity sha512-tu5UtrMk/AXonHwHxUogdXAWynaXsrx1i6dsgg+lOo/KJSF8oBAcprh1z5J3xgnPJD5hXxTL58F8s8onokdt0Q== +rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: + version "3.14.5" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.14.5.tgz#593cd13fe05eabf4ad098329704a30c77701869e" + integrity sha512-ZMOnkCLv2wUN8Jz7yI4XiSLa9THlYvf00LuMhb1JlsQCewuU7ydPuHw1rGVPhe9VZYl/5UqODtNd7QKJ2DMGfg== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" rc-resize-observer "^1.0.0" rc-util "^5.36.0" -react-cookie@^4.0.3: - version "4.1.1" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-4.1.1.tgz#832e134ad720e0de3e03deaceaab179c4061a19d" - integrity sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A== +react-cookie@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.1.4.tgz#1e35c9f11394b44fbbc30c5eba1287d89258e993" + integrity sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw== dependencies: - "@types/hoist-non-react-statics" "^3.0.1" - hoist-non-react-statics "^3.0.0" - universal-cookie "^4.0.0" + "@types/hoist-non-react-statics" "^3.3.5" + hoist-non-react-statics "^3.3.2" + universal-cookie "^7.0.0" -react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== +react-dom@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== dependencies: loose-envify "^1.1.0" - scheduler "^0.23.0" + scheduler "^0.23.2" react-highlight-words@^0.20.0: version "0.20.0" @@ -6318,10 +6436,10 @@ react-highlight-words@^0.20.0: memoize-one "^4.0.0" prop-types "^15.5.8" -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0, react-is@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" @@ -6336,19 +6454,19 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" -react-test-renderer@^18.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" - integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA== +react-test-renderer@^18.2.0: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4" + integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA== dependencies: - react-is "^18.2.0" + react-is "^18.3.1" react-shallow-renderer "^16.15.0" - scheduler "^0.23.0" + scheduler "^0.23.2" -react@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== +react@^18.3.1: + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" @@ -6396,14 +6514,15 @@ readdir-scoped-modules@^1.0.0: once "^1.3.0" reflect.getprototypeof@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" - integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== + version "1.0.6" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" + integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.1" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" globalthis "^1.0.3" which-builtin-type "^1.1.3" @@ -6412,14 +6531,20 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" - integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== +regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - set-function-name "^2.0.0" + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + +rehackt@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" + integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== relay-runtime@12.0.0: version "12.0.0" @@ -6496,7 +6621,7 @@ resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.4: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.4: +resolve@^2.0.0-next.5: version "2.0.0-next.5" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== @@ -6524,16 +6649,9 @@ reusify@^1.0.4: integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfdc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" - integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== run-async@^2.4.0: version "2.4.1" @@ -6554,13 +6672,13 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -safe-array-concat@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" - integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" + call-bind "^1.0.7" + get-intrinsic "^1.2.4" has-symbols "^1.0.3" isarray "^2.0.5" @@ -6569,13 +6687,13 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex-test@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" - integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== dependencies: - call-bind "^1.0.5" - get-intrinsic "^1.2.2" + call-bind "^1.0.6" + es-errors "^1.3.0" is-regex "^1.1.4" safe-stable-stringify@^2.4.3: @@ -6588,10 +6706,10 @@ safe-stable-stringify@^2.4.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== dependencies: loose-envify "^1.1.0" @@ -6627,11 +6745,9 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== sentence-case@^3.0.4: version "3.0.4" @@ -6647,25 +6763,27 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" - integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: - define-data-property "^1.1.1" + define-data-property "^1.1.4" + es-errors "^1.3.0" function-bind "^1.1.2" - get-intrinsic "^1.2.2" + get-intrinsic "^1.2.4" gopd "^1.0.1" - has-property-descriptors "^1.0.1" + has-property-descriptors "^1.0.2" -set-function-name@^2.0.0, set-function-name@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" - integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== +set-function-name@^2.0.1, set-function-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: - define-data-property "^1.0.1" + define-data-property "^1.1.4" + es-errors "^1.3.0" functions-have-names "^1.2.3" - has-property-descriptors "^1.0.0" + has-property-descriptors "^1.0.2" setimmediate@^1.0.5: version "1.0.5" @@ -6689,14 +6807,15 @@ shell-quote@^1.7.3: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel@^1.0.4, side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" @@ -6750,9 +6869,9 @@ snake-case@^3.0.4: tslib "^2.0.3" source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== source-map-support@0.5.13: version "0.5.13" @@ -6790,9 +6909,9 @@ spdx-correct@^3.0.0: spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" @@ -6803,9 +6922,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.16" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz#a14f64e0954f6e25cc6587bd4f392522db0d998f" - integrity sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw== + version "3.0.18" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" + integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== spdx-ranges@^2.0.0: version "2.1.1" @@ -6840,6 +6959,13 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== + dependencies: + internal-slot "^1.0.4" + streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -6872,47 +6998,59 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.8: - version "4.0.10" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" - integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== +string.prototype.includes@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz#8986d57aee66d5460c144620a6d873778ad7289f" + integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - get-intrinsic "^1.2.1" + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.matchall@^4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" + integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.4" + gopd "^1.0.1" has-symbols "^1.0.3" - internal-slot "^1.0.5" - regexp.prototype.flags "^1.5.0" - set-function-name "^2.0.0" - side-channel "^1.0.4" + internal-slot "^1.0.7" + regexp.prototype.flags "^1.5.2" + set-function-name "^2.0.2" + side-channel "^1.0.6" -string.prototype.trim@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" - integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" -string.prototype.trimend@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" - integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" - integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" string_decoder@^1.1.1: version "1.3.0" @@ -6956,9 +7094,9 @@ styled-jsx@5.1.1: client-only "0.0.1" stylis@^4.0.13: - version "4.3.1" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.1.tgz#ed8a9ebf9f76fe1e12d462f5cc3c4c980b23a7eb" - integrity sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ== + version "4.3.2" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" + integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== supports-color@^5.3.0: version "5.5.0" @@ -6994,9 +7132,9 @@ swap-case@^2.0.2: tslib "^2.0.3" swr@^2.0.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.4.tgz#03ec4c56019902fbdc904d78544bd7a9a6fa3f07" - integrity sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ== + version "2.2.5" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b" + integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg== dependencies: client-only "^0.0.1" use-sync-external-store "^1.2.0" @@ -7026,9 +7164,9 @@ text-table@^0.2.0: integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== throttle-debounce@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.0.tgz#a17a4039e82a2ed38a5e7268e4132d6960d41933" - integrity sha512-2iQTSgkkc1Zyk0MeVrt/3BvuOXYPl/R8Z0U2xxo9rjwNciaHDG3R+Lm6dh4EeUci49DanvBnuqI6jshoQQRGEg== + version "5.0.2" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" + integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== through@^2.3.6, through@^2.3.8: version "2.3.8" @@ -7082,9 +7220,9 @@ treeify@^1.1.0: integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-api-utils@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" - integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== ts-invariant@^0.10.3: version "0.10.3" @@ -7128,9 +7266,9 @@ tsconfig-paths@^3.15.0: strip-bom "^3.0.0" tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1, tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== tslib@~2.0.1: version "2.0.3" @@ -7169,54 +7307,54 @@ type-detect@4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" -typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" -typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== dependencies: - call-bind "^1.0.2" + call-bind "^1.0.7" for-each "^0.3.3" - is-typed-array "^1.1.9" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -7226,14 +7364,14 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^5.0.4: - version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" - integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + version "5.5.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" + integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== ua-parser-js@^1.0.35: - version "1.0.37" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" - integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== + version "1.0.38" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.38.tgz#66bb0c4c0e322fe48edfe6d446df6042e62f25e2" + integrity sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ== unbox-primitive@^1.0.2: version "1.0.2" @@ -7255,13 +7393,13 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -universal-cookie@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d" - integrity sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw== +universal-cookie@^7.0.0: + version "7.1.4" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.1.4.tgz#d11bb95e405639c0ff0b467a64a5ccc5ce97dfc6" + integrity sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g== dependencies: - "@types/cookie" "^0.3.3" - cookie "^0.4.0" + "@types/cookie" "^0.6.0" + cookie "^0.6.0" unixify@1.0.0, unixify@^1.0.0: version "1.0.0" @@ -7270,13 +7408,13 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== +update-browserslist-db@^1.0.16: + version "1.0.16" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" + integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" + escalade "^3.1.2" + picocolors "^1.0.1" upper-case-first@^2.0.2: version "2.0.2" @@ -7305,9 +7443,9 @@ urlpattern-polyfill@^8.0.0: integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== use-sync-external-store@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== util-deprecate@^1.0.1: version "1.0.2" @@ -7325,9 +7463,9 @@ v8-compile-cache-lib@^3.0.1: integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: - version "9.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" - integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== + version "9.3.0" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" "@types/istanbul-lib-coverage" "^2.0.1" @@ -7376,20 +7514,20 @@ wcwidth@^1.0.1: defaults "^1.0.3" web-streams-polyfill@^3.2.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz#32e26522e05128203a7de59519be3c648004343b" - integrity sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== -webcrypto-core@^1.7.7: - version "1.7.7" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" - integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== +webcrypto-core@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.0.tgz#aaea17f3dd9c77c304e3c494eb27ca07cc72ca37" + integrity sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" webidl-conversions@^3.0.0: version "3.0.1" @@ -7434,30 +7572,30 @@ which-builtin-type@^1.1.3: which-typed-array "^1.1.9" which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" + is-map "^2.0.3" + is-set "^2.0.3" + is-weakmap "^2.0.2" + is-weakset "^2.0.3" which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: - version "1.1.13" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" - integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== +which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" for-each "^0.3.3" gopd "^1.0.1" - has-tostringtag "^1.0.0" + has-tostringtag "^1.0.2" which@^2.0.1: version "2.0.2" @@ -7466,6 +7604,11 @@ which@^2.0.1: dependencies: isexe "^2.0.0" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -7513,9 +7656,9 @@ ws@8.13.0: integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== ws@^8.12.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== y18n@^4.0.0: version "4.0.3" @@ -7532,20 +7675,15 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - yaml-ast-parser@^0.0.43: version "0.0.43" resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== yaml-loader@^0.8: - version "0.8.0" - resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.0.tgz#c839325e3fdee082b3768b2a21fe34fde5d96f61" - integrity sha512-LjeKnTzVBKWiQBeE2L9ssl6WprqaUIxCSNs5tle8PaDydgu3wVFXTbMfsvF2MSErpy9TDVa092n4q6adYwJaWg== + version "0.8.1" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.1.tgz#034f901147073cfc307cdcce8bd44c1547e60ba1" + integrity sha512-BCEndnUoi3BaZmePkwGGe93txRxLgMhBa/gE725v1/GHnura8QvNs7c4+4C1yyhhKoj3Dg63M7IqhA++15j6ww== dependencies: javascript-stringify "^2.0.1" loader-utils "^2.0.0" @@ -7557,9 +7695,9 @@ yaml@1.10.2, yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2" - integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA== + version "2.4.5" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" + integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== yargs-parser@^18.1.2: version "18.1.3" From 8b05912cb47ff891bd68c9f6a44a62ec49f2a3d0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 15:07:11 +0200 Subject: [PATCH 150/878] Fix names of `(not) less than` filter operations --- backend/src/Configuration/GraphQlConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index a23f17a7..61957d96 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -290,8 +290,8 @@ public static IFilterConventionDescriptor AddDefaultOperations( descriptor.Operation(DefaultFilterOperations.GreaterThanOrEquals).Name("greaterThanOrEqualTo"); descriptor.Operation(DefaultFilterOperations.NotGreaterThanOrEquals).Name("notGreaterThanOrEqualTo"); descriptor.Operation(DefaultFilterOperations.LowerThan).Name("lessThan"); - descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("lessThan"); - descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("notLessThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("notLessThan"); + descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("lessThanOrEqualTo"); descriptor.Operation(DefaultFilterOperations.NotLowerThanOrEquals).Name("notLessThanOrEqualTo"); descriptor.Operation(DefaultFilterOperations.Contains).Name("contains"); descriptor.Operation(DefaultFilterOperations.NotContains).Name("doesNotContain"); From b3e2295cc6e3fe013c1780a838c9c6c4202e2c1e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 15:10:35 +0200 Subject: [PATCH 151/878] Update GraphQL schema dump --- frontend/type-defs.graphqls | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 36c0e750..84a62036 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -1,4 +1,4 @@ -schema { +schema { query: Query mutation: Mutation } @@ -623,12 +623,12 @@ input DataApprovalInput { input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] or: [DataApprovalPropositionInput!] + approverId: UuidPropositionInput timestamp: DateTimePropositionInput signature: StringPropositionInput keyFingerprint: StringPropositionInput query: StringPropositionInput response: StringPropositionInput - approverId: UuidPropositionInput } input DataKindPropositionInput { @@ -663,6 +663,8 @@ input DateTimePropositionInput { greaterThanOrEqualTo: DateTime notGreaterThanOrEqualTo: DateTime lessThan: DateTime + notLessThan: DateTime + lessThanOrEqualTo: DateTime notLessThanOrEqualTo: DateTime } @@ -688,6 +690,8 @@ input FloatPropositionInput { greaterThanOrEqualTo: Float notGreaterThanOrEqualTo: Float lessThan: Float + notLessThan: Float + lessThanOrEqualTo: Float notLessThanOrEqualTo: Float } @@ -846,6 +850,8 @@ input NullableOfUuidPropositionInput { greaterThanOrEqualTo: Uuid notGreaterThanOrEqualTo: Uuid lessThan: Uuid + notLessThan: Uuid + lessThanOrEqualTo: Uuid notLessThanOrEqualTo: Uuid } @@ -978,6 +984,8 @@ input UuidPropositionInput { greaterThanOrEqualTo: Uuid notGreaterThanOrEqualTo: Uuid lessThan: Uuid + notLessThan: Uuid + lessThanOrEqualTo: Uuid notLessThanOrEqualTo: Uuid } From 4004662a565f80797a270c7d5d8eada8869f2871 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 15:30:35 +0200 Subject: [PATCH 152/878] Write logs from serilog to host machine instead of anonymous volume --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2b331aa7..f8f599da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -84,7 +84,7 @@ services: # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. # - /app/node_modules - - /app/src/logs + # - /app/src/logs - /app/src/bin - /app/src/obj - /app/test/bin From 60017ff081e2467b679fec21e4c91d48426821b9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 15:31:50 +0200 Subject: [PATCH 153/878] Run dotnet watch non-interactively and without hot reloading --- docker-compose.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f8f599da..1b4e3a8c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,13 +61,14 @@ services: # user: 1000:1000 command: [ "dotnet", "watch", - "--project", "./src/Database.csproj", + "--non-interactive", + "--no-hot-reload", # as of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803 + "--project", "/app/src/Database.csproj", "--", "run", - "--no-launch-profile" + "--no-launch-profile", + "--project", "/app/src/Database.csproj" ] - # "--no-restore", - # ["dotnet", "watch", "run", "--no-restore"] healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] From 54ba8b8fe1cf031e46d939a817a4d2f9e7501ca3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 15:46:01 +0200 Subject: [PATCH 154/878] Fix the error `Multiple claims of the same type are present in the identity or principal.` raised by OpenIddict (even though when I print all claims returned by `GetClaims`, there is just one claim) --- backend/src/GraphQl/Users/UserQueries.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index a2bb48e0..c1cefd9a 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -29,7 +29,7 @@ CancellationToken cancellationToken return await context.Users.AsQueryable() .SingleOrDefaultAsync( - u => u.Subject == claimsPrincipal.GetClaim(ClaimTypes.NameIdentifier), + u => claimsPrincipal.GetClaims(ClaimTypes.NameIdentifier).Contains(u.Subject), cancellationToken ).ConfigureAwait(false); } From 52a445d1f574b3a18283f99823f384c44a55ac5d Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Wed, 7 Feb 2024 16:53:18 +0100 Subject: [PATCH 155/878] Resolving conflicts --- backend/src/Data/ApplicationDbContext.cs | 1 + backend/src/Startup.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index a8a324be..b7cd4dfb 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -38,6 +38,7 @@ private static void CreateEnumerations(ModelBuilder builder) // Create enumerations in public schema because that is where // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. builder.HasPostgresEnum("public"); + builder.HasPostgresEnum("public"); } private static diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 5e54a44e..6e8d190b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -122,6 +122,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum dataSourceBuilder.MapEnum(); + dataSourceBuilder.MapEnum(); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) From cefb0407c206272d845c5c55b5269d0b16f6d8dd Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Wed, 7 Feb 2024 16:59:00 +0100 Subject: [PATCH 156/878] Adding statement to data approval --- backend/src/Data/DataApproval.cs | 6 +- backend/src/Data/IReference.cs | 9 +++ backend/src/Data/Numeration.cs | 30 +++++++++ backend/src/Data/Publication.cs | 65 +++++++++++++++++++ backend/src/Data/Standard.cs | 54 +++++++++++++++ backend/src/Enumerations/Standardizer.cs | 31 +++++++++ backend/src/GraphQl/DataApprovalInput.cs | 4 +- .../Numerations/CreateNumerationInput.cs | 8 +++ .../src/GraphQl/Numerations/NumerationType.cs | 14 ++++ .../Numerations/UpdateNumerationInput.cs | 8 +++ .../Publications/CreatePublicationInput.cs | 15 +++++ .../GraphQl/Publications/PublicationType.cs | 14 ++++ .../Publications/UpdatePublicationInput.cs | 15 +++++ .../src/GraphQl/References/ReferenceType.cs | 13 ++++ .../GraphQl/Standards/CreateStandardInput.cs | 15 +++++ backend/src/GraphQl/Standards/StandardType.cs | 14 ++++ .../GraphQl/Standards/UpdateStandardInput.cs | 15 +++++ 17 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 backend/src/Data/IReference.cs create mode 100644 backend/src/Data/Numeration.cs create mode 100644 backend/src/Data/Publication.cs create mode 100644 backend/src/Data/Standard.cs create mode 100644 backend/src/Enumerations/Standardizer.cs create mode 100644 backend/src/GraphQl/Numerations/CreateNumerationInput.cs create mode 100644 backend/src/GraphQl/Numerations/NumerationType.cs create mode 100644 backend/src/GraphQl/Numerations/UpdateNumerationInput.cs create mode 100644 backend/src/GraphQl/Publications/CreatePublicationInput.cs create mode 100644 backend/src/GraphQl/Publications/PublicationType.cs create mode 100644 backend/src/GraphQl/Publications/UpdatePublicationInput.cs create mode 100644 backend/src/GraphQl/References/ReferenceType.cs create mode 100644 backend/src/GraphQl/Standards/CreateStandardInput.cs create mode 100644 backend/src/GraphQl/Standards/StandardType.cs create mode 100644 backend/src/GraphQl/Standards/UpdateStandardInput.cs diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 98e5d891..f1e0080a 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore; +using Database.Data; namespace Database.Data; @@ -13,7 +14,8 @@ public DataApproval( string keyFingerprint, string query, string response, - Guid approverId + Guid approverId, + IReference statement ) { Timestamp = timestamp; @@ -22,6 +24,7 @@ Guid approverId Query = query; Response = response; ApproverId = approverId; + Statement = statement; } public Guid ApproverId { get; private set; } @@ -30,4 +33,5 @@ Guid approverId public string KeyFingerprint { get; private set; } public string Query { get; private set; } public string Response { get; private set; } + public IReference Statement { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/IReference.cs b/backend/src/Data/IReference.cs new file mode 100644 index 00000000..85c5d022 --- /dev/null +++ b/backend/src/Data/IReference.cs @@ -0,0 +1,9 @@ +namespace Database.Data +{ + public interface IReference + { + public string? Title { get; } + public string? Abstract { get; } + public string? Section { get; } + } +} \ No newline at end of file diff --git a/backend/src/Data/Numeration.cs b/backend/src/Data/Numeration.cs new file mode 100644 index 00000000..1916afdd --- /dev/null +++ b/backend/src/Data/Numeration.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data +{ + [Owned] + public sealed class Numeration + { + [MinLength(1)] + public string? Prefix { get; private set; } + + [Required] + [MinLength(1)] + public string MainNumber { get; private set; } + + [MinLength(1)] + public string? Suffix { get; private set; } + + public Numeration( + string? prefix, + string mainNumber, + string? suffix + ) + { + Prefix = prefix; + MainNumber = mainNumber; + Suffix = suffix; + } + } +} \ No newline at end of file diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs new file mode 100644 index 00000000..27d77da0 --- /dev/null +++ b/backend/src/Data/Publication.cs @@ -0,0 +1,65 @@ +using System; +using System.ComponentModel.DataAnnotations; +using HotChocolate; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data +{ + [Owned] + public sealed class Publication + : IReference + { + [MinLength(1)] + public string? Title { get; private set; } + + [MinLength(1)] + public string? Abstract { get; private set; } + + [MinLength(1)] + [GraphQLDescription("Referenced section")] + public string? Section { get; private set; } + + [MinLength(1)] + public string[]? Authors { get; private set; } + + [MinLength(1)] + [RegularExpression("^(10[.][0-z/.]*)$")] + [GraphQLDescription("The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] + public string? Doi { get; private set; } + + [MinLength(1)] + [RegularExpression("^(arXiv:)[0-z./]*$")] + [GraphQLDescription("The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] + public string? ArXiv { get; private set; } + + [MinLength(1)] + [RegularExpression("^(urn:)[0-z:./-]*$")] + [GraphQLDescription("A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string.")] + public string? Urn { get; private set; } + + [Url] + [GraphQLDescription("If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] + public Uri? WebAddress { get; private set; } + + public Publication( + string? title, + string? @abstract, + string? section, + string[]? authors, + string? doi, + string? arXiv, + string? urn, + Uri? webAddress + ) + { + Title = title; + Abstract = @abstract; + Section = section; + Authors = authors; + Doi = doi; + ArXiv = arXiv; + Urn = urn; + WebAddress = webAddress; + } + } +} \ No newline at end of file diff --git a/backend/src/Data/Standard.cs b/backend/src/Data/Standard.cs new file mode 100644 index 00000000..0275693c --- /dev/null +++ b/backend/src/Data/Standard.cs @@ -0,0 +1,54 @@ +using System; +using System.ComponentModel.DataAnnotations; +using HotChocolate; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data +{ + [Owned] + [GraphQLDescription("`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier.")] + public sealed class Standard + : IReference + { + [MinLength(1)] + public string? Title { get; private set; } + + [MinLength(1)] + public string? Abstract { get; private set; } + + [MinLength(1)] + [GraphQLDescription("The section of the standard to which the reference refers to.")] + public string? Section { get; private set; } + + [Range(0, 3000)] + [GraphQLDescription("It is important to define the year in which the standard was issued because there can be relevant updates of one standard.")] + public int? Year { get; private set; } + + // Numeration, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types + [Required] + public Numeration Numeration { get; set; } = default!; + + [Required] + public Enumerations.Standardizer[] Standardizers { get; private set; } + + [Url] + public Uri? Locator { get; private set; } + + public Standard( + string? title, + string? @abstract, + string? section, + int? year, + Enumerations.Standardizer[] standardizers, + Uri? locator + ) + { + Title = title; + Abstract = @abstract; + Section = section; + Year = year; + Standardizers = standardizers; + Locator = locator; + } + } +} \ No newline at end of file diff --git a/backend/src/Enumerations/Standardizer.cs b/backend/src/Enumerations/Standardizer.cs new file mode 100644 index 00000000..279b3128 --- /dev/null +++ b/backend/src/Enumerations/Standardizer.cs @@ -0,0 +1,31 @@ + +namespace Database.Enumerations +{ + public enum Standardizer + { + AERC, + AGI, + ASHRAE, + BREEAM, + BS, + BSI, + CEN, + CIE, + DGNB, + DIN, + DVWG, + IEC, + IES, + IFT, + ISO, + JIS, + LEED, + NFRC, + RIBA, + UL, + UNECE, + VDI, + VFF, + WELL + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs index 5381ec69..78688c6e 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovalInput.cs @@ -1,4 +1,5 @@ using System; +using Database.Data; namespace Database.GraphQl; @@ -9,4 +10,5 @@ public sealed record DataApprovalInput( string Query, string Response, Guid ApproverId -); \ No newline at end of file + IReference Statement +); diff --git a/backend/src/GraphQl/Numerations/CreateNumerationInput.cs b/backend/src/GraphQl/Numerations/CreateNumerationInput.cs new file mode 100644 index 00000000..3e0af7f0 --- /dev/null +++ b/backend/src/GraphQl/Numerations/CreateNumerationInput.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl.Numerations +{ + public sealed record CreateNumerationInput( + string? Prefix, + string MainNumber, + string? Suffix + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Numerations/NumerationType.cs b/backend/src/GraphQl/Numerations/NumerationType.cs new file mode 100644 index 00000000..cbe9ed53 --- /dev/null +++ b/backend/src/GraphQl/Numerations/NumerationType.cs @@ -0,0 +1,14 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.Numerations +{ + public sealed class NumerationType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs b/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs new file mode 100644 index 00000000..e24443cf --- /dev/null +++ b/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl.Numerations +{ + public sealed record UpdateNumerationInput( + string? Prefix, + string MainNumber, + string? Suffix + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/CreatePublicationInput.cs b/backend/src/GraphQl/Publications/CreatePublicationInput.cs new file mode 100644 index 00000000..3662958d --- /dev/null +++ b/backend/src/GraphQl/Publications/CreatePublicationInput.cs @@ -0,0 +1,15 @@ +using System; + +namespace Database.GraphQl.Publications +{ + public sealed record CreatePublicationInput( + string? Title, + string? Abstract, + string? Section, + string[]? Authors, + string? Doi, + string? ArXiv, + string? Urn, + Uri? WebAddress + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs new file mode 100644 index 00000000..68b6c5ee --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -0,0 +1,14 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.Publications +{ + public sealed class PublicationType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/UpdatePublicationInput.cs b/backend/src/GraphQl/Publications/UpdatePublicationInput.cs new file mode 100644 index 00000000..a29e751a --- /dev/null +++ b/backend/src/GraphQl/Publications/UpdatePublicationInput.cs @@ -0,0 +1,15 @@ +using System; + +namespace Database.GraphQl.Publications +{ + public sealed record UpdatePublicationInput( + string? Title, + string? Abstract, + string? Section, + string[]? Authors, + string? Doi, + string? ArXiv, + string? Urn, + Uri? WebAddress + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs new file mode 100644 index 00000000..f6f644c9 --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -0,0 +1,13 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.References +{ + public sealed class ReferenceType + : InterfaceType + { + protected override void Configure(IInterfaceTypeDescriptor descriptor) + { + descriptor.Name(nameof(Data.IReference)[1..]); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/CreateStandardInput.cs b/backend/src/GraphQl/Standards/CreateStandardInput.cs new file mode 100644 index 00000000..7eee2e5f --- /dev/null +++ b/backend/src/GraphQl/Standards/CreateStandardInput.cs @@ -0,0 +1,15 @@ +using System; +using Database.GraphQl.Numerations; + +namespace Database.GraphQl.Standards +{ + public sealed record CreateStandardInput( + string? Title, + string? Abstract, + string? Section, + int? Year, + CreateNumerationInput Numeration, + Enumerations.Standardizer[] Standardizers, + Uri? Locator + ); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/StandardType.cs b/backend/src/GraphQl/Standards/StandardType.cs new file mode 100644 index 00000000..0d745670 --- /dev/null +++ b/backend/src/GraphQl/Standards/StandardType.cs @@ -0,0 +1,14 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.Standards +{ + public sealed class StandardType + : ObjectType + { + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/UpdateStandardInput.cs b/backend/src/GraphQl/Standards/UpdateStandardInput.cs new file mode 100644 index 00000000..a43a33bd --- /dev/null +++ b/backend/src/GraphQl/Standards/UpdateStandardInput.cs @@ -0,0 +1,15 @@ +using System; +using Database.GraphQl.Numerations; + +namespace Database.GraphQl.Standards +{ + public sealed record UpdateStandardInput( + string? Title, + string? Abstract, + string? Section, + int? Year, + UpdateNumerationInput Numeration, + Enumerations.Standardizer[] Standardizers, + Uri? Locator + ); +} \ No newline at end of file From 091a0c6166bc994d179aa16ef108fe31f3058789 Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Wed, 6 Mar 2024 09:25:55 +0100 Subject: [PATCH 157/878] Modified constructor --- backend/src/Data/DataApproval.cs | 15 +++++++++---- .../CalorimetricDataMutations.cs | 22 +++++++++++++++++++ backend/src/GraphQl/DataApprovalInput.cs | 7 ++++-- .../HygrothermalDataMutations.cs | 22 +++++++++++++++++++ .../OpticalDataX/OpticalDataMutations.cs | 22 +++++++++++++++++++ .../PhotovoltaicDataMutations.cs | 22 +++++++++++++++++++ 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index f1e0080a..8a2adb83 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,6 +1,8 @@ using System; using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations; using Database.Data; +using System.ComponentModel.DataAnnotations.Schema; namespace Database.Data; @@ -14,8 +16,7 @@ public DataApproval( string keyFingerprint, string query, string response, - Guid approverId, - IReference statement + Guid approverId ) { Timestamp = timestamp; @@ -24,7 +25,6 @@ IReference statement Query = query; Response = response; ApproverId = approverId; - Statement = statement; } public Guid ApproverId { get; private set; } @@ -33,5 +33,12 @@ IReference statement public string KeyFingerprint { get; private set; } public string Query { get; private set; } public string Response { get; private set; } - public IReference Statement { get; private set; } + public Publication? Publication { get; set; } + public Standard? Standard { get; set; } + [NotMapped] + public IReference? Statement + { + get => Standard is not null ? Standard : Publication; + set {if(value is Standard) Standard = (Standard)value; else Publication = (Publication)value;} + } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index a6a7358e..d4761e99 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -84,6 +84,28 @@ CancellationToken cancellationToken a.Response, a.ApproverId ) + { + Publication = a.Publication is null ? null : + new Data.Publication( + title: a.Publication.Title, + @abstract: a.Publication.Abstract, + section: a.Publication.Section, + authors: a.Publication.Authors, + doi: a.Publication.Doi, + arXiv: a.Publication.ArXiv, + urn: a.Publication.Urn, + webAddress: a.Publication.WebAddress + ), + Standard = a.Standard is null ? null : + new Data.Standard( + title: a.Standard.Title, + @abstract: a.Standard.Abstract, + section: a.Standard.Section, + year: a.Standard.Year, + standardizers: a.Standard.Standardizers, + locator: a.Standard.Locator + ) + } ).ToList(), // approval: input.Approval input.GValues, diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs index 78688c6e..d4d7478a 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovalInput.cs @@ -1,5 +1,7 @@ using System; using Database.Data; +using Database.GraphQl.Publications; +using Database.GraphQl.Standards; namespace Database.GraphQl; @@ -9,6 +11,7 @@ public sealed record DataApprovalInput( string KeyFingerprint, string Query, string Response, - Guid ApproverId - IReference Statement + Guid ApproverId, + CreateStandardInput? Standard, + CreatePublicationInput? Publication ); diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 1c140eac..983b7dfd 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -84,6 +84,28 @@ CancellationToken cancellationToken a.Response, a.ApproverId ) + { + Publication = a.Publication is null ? null : + new Data.Publication( + title: a.Publication.Title, + @abstract: a.Publication.Abstract, + section: a.Publication.Section, + authors: a.Publication.Authors, + doi: a.Publication.Doi, + arXiv: a.Publication.ArXiv, + urn: a.Publication.Urn, + webAddress: a.Publication.WebAddress + ), + Standard = a.Standard is null ? null : + new Data.Standard( + title: a.Standard.Title, + @abstract: a.Standard.Abstract, + section: a.Standard.Section, + year: a.Standard.Year, + standardizers: a.Standard.Standardizers, + locator: a.Standard.Locator + ) + } ).ToList() // approval: input.Approval ); diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 69311fc7..80258b8d 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -84,6 +84,28 @@ CancellationToken cancellationToken a.Response, a.ApproverId ) + { + Publication = a.Publication is null ? null : + new Data.Publication( + title: a.Publication.Title, + @abstract: a.Publication.Abstract, + section: a.Publication.Section, + authors: a.Publication.Authors, + doi: a.Publication.Doi, + arXiv: a.Publication.ArXiv, + urn: a.Publication.Urn, + webAddress: a.Publication.WebAddress + ), + Standard = a.Standard is null ? null : + new Data.Standard( + title: a.Standard.Title, + @abstract: a.Standard.Abstract, + section: a.Standard.Section, + year: a.Standard.Year, + standardizers: a.Standard.Standardizers, + locator: a.Standard.Locator + ) + } ).ToList(), // approval: input.Approval input.NearnormalHemisphericalVisibleTransmittances, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 70d73693..a07897f6 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -84,6 +84,28 @@ CancellationToken cancellationToken a.Response, a.ApproverId ) + { + Publication = a.Publication is null ? null : + new Data.Publication( + title: a.Publication.Title, + @abstract: a.Publication.Abstract, + section: a.Publication.Section, + authors: a.Publication.Authors, + doi: a.Publication.Doi, + arXiv: a.Publication.ArXiv, + urn: a.Publication.Urn, + webAddress: a.Publication.WebAddress + ), + Standard = a.Standard is null ? null : + new Data.Standard( + title: a.Standard.Title, + @abstract: a.Standard.Abstract, + section: a.Standard.Section, + year: a.Standard.Year, + standardizers: a.Standard.Standardizers, + locator: a.Standard.Locator + ) + } ).ToList() // approval: input.Approval ); From 47120e33d2b9ae1a4a17684627c9414224a1bfbe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Apr 2024 18:20:47 +0200 Subject: [PATCH 158/878] Apply `make cleanup`, that is, JetBrain's cleanupcode --- backend/src/Data/ApplicationDbContext.cs | 2 +- backend/src/Data/DataApproval.cs | 10 +- backend/src/Data/IReference.cs | 13 ++- backend/src/Data/Numeration.cs | 37 +++---- backend/src/Data/Publication.cs | 98 +++++++++---------- backend/src/Data/Standard.cs | 87 ++++++++-------- backend/src/Enumerations/Standardizer.cs | 54 +++++----- .../CalorimetricDataMutations.cs | 42 ++++---- backend/src/GraphQl/DataApprovalInput.cs | 3 +- .../HygrothermalDataMutations.cs | 42 ++++---- .../Numerations/CreateNumerationInput.cs | 15 ++- .../src/GraphQl/Numerations/NumerationType.cs | 16 +-- .../Numerations/UpdateNumerationInput.cs | 15 ++- .../OpticalDataX/OpticalDataMutations.cs | 42 ++++---- .../PhotovoltaicDataMutations.cs | 42 ++++---- .../Publications/CreatePublicationInput.cs | 25 +++-- .../GraphQl/Publications/PublicationType.cs | 16 +-- .../Publications/UpdatePublicationInput.cs | 25 +++-- .../src/GraphQl/References/ReferenceType.cs | 14 +-- .../GraphQl/Standards/CreateStandardInput.cs | 24 ++--- backend/src/GraphQl/Standards/StandardType.cs | 16 +-- .../GraphQl/Standards/UpdateStandardInput.cs | 24 ++--- backend/src/Startup.cs | 2 +- 23 files changed, 329 insertions(+), 335 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b7cd4dfb..b477df77 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -38,7 +38,7 @@ private static void CreateEnumerations(ModelBuilder builder) // Create enumerations in public schema because that is where // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. builder.HasPostgresEnum("public"); - builder.HasPostgresEnum("public"); + builder.HasPostgresEnum("public"); } private static diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 8a2adb83..5c3dc675 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,8 +1,6 @@ using System; -using Microsoft.EntityFrameworkCore; -using System.ComponentModel.DataAnnotations; -using Database.Data; using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -39,6 +37,10 @@ Guid approverId public IReference? Statement { get => Standard is not null ? Standard : Publication; - set {if(value is Standard) Standard = (Standard)value; else Publication = (Publication)value;} + set + { + if (value is Standard) Standard = (Standard)value; + else Publication = (Publication)value; + } } } \ No newline at end of file diff --git a/backend/src/Data/IReference.cs b/backend/src/Data/IReference.cs index 85c5d022..e0cb9080 100644 --- a/backend/src/Data/IReference.cs +++ b/backend/src/Data/IReference.cs @@ -1,9 +1,8 @@ -namespace Database.Data +namespace Database.Data; + +public interface IReference { - public interface IReference - { - public string? Title { get; } - public string? Abstract { get; } - public string? Section { get; } - } + public string? Title { get; } + public string? Abstract { get; } + public string? Section { get; } } \ No newline at end of file diff --git a/backend/src/Data/Numeration.cs b/backend/src/Data/Numeration.cs index 1916afdd..fc0f2033 100644 --- a/backend/src/Data/Numeration.cs +++ b/backend/src/Data/Numeration.cs @@ -1,30 +1,25 @@ using System.ComponentModel.DataAnnotations; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class Numeration { - [Owned] - public sealed class Numeration + public Numeration( + string? prefix, + string mainNumber, + string? suffix + ) { - [MinLength(1)] - public string? Prefix { get; private set; } + Prefix = prefix; + MainNumber = mainNumber; + Suffix = suffix; + } - [Required] - [MinLength(1)] - public string MainNumber { get; private set; } + [MinLength(1)] public string? Prefix { get; private set; } - [MinLength(1)] - public string? Suffix { get; private set; } + [Required] [MinLength(1)] public string MainNumber { get; private set; } - public Numeration( - string? prefix, - string mainNumber, - string? suffix - ) - { - Prefix = prefix; - MainNumber = mainNumber; - Suffix = suffix; - } - } + [MinLength(1)] public string? Suffix { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs index 27d77da0..21a1f2ad 100644 --- a/backend/src/Data/Publication.cs +++ b/backend/src/Data/Publication.cs @@ -3,63 +3,63 @@ using HotChocolate; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - public sealed class Publication +namespace Database.Data; + +[Owned] +public sealed class Publication : IReference +{ + public Publication( + string? title, + string? @abstract, + string? section, + string[]? authors, + string? doi, + string? arXiv, + string? urn, + Uri? webAddress + ) { - [MinLength(1)] - public string? Title { get; private set; } + Title = title; + Abstract = @abstract; + Section = section; + Authors = authors; + Doi = doi; + ArXiv = arXiv; + Urn = urn; + WebAddress = webAddress; + } - [MinLength(1)] - public string? Abstract { get; private set; } + [MinLength(1)] public string[]? Authors { get; private set; } - [MinLength(1)] - [GraphQLDescription("Referenced section")] - public string? Section { get; private set; } + [MinLength(1)] + [RegularExpression("^(10[.][0-z/.]*)$")] + [GraphQLDescription( + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] + public string? Doi { get; private set; } - [MinLength(1)] - public string[]? Authors { get; private set; } + [MinLength(1)] + [RegularExpression("^(arXiv:)[0-z./]*$")] + [GraphQLDescription( + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] + public string? ArXiv { get; private set; } - [MinLength(1)] - [RegularExpression("^(10[.][0-z/.]*)$")] - [GraphQLDescription("The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] - public string? Doi { get; private set; } + [MinLength(1)] + [RegularExpression("^(urn:)[0-z:./-]*$")] + [GraphQLDescription( + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string.")] + public string? Urn { get; private set; } - [MinLength(1)] - [RegularExpression("^(arXiv:)[0-z./]*$")] - [GraphQLDescription("The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] - public string? ArXiv { get; private set; } + [Url] + [GraphQLDescription( + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] + public Uri? WebAddress { get; private set; } - [MinLength(1)] - [RegularExpression("^(urn:)[0-z:./-]*$")] - [GraphQLDescription("A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string.")] - public string? Urn { get; private set; } + [MinLength(1)] public string? Title { get; } - [Url] - [GraphQLDescription("If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] - public Uri? WebAddress { get; private set; } + [MinLength(1)] public string? Abstract { get; } - public Publication( - string? title, - string? @abstract, - string? section, - string[]? authors, - string? doi, - string? arXiv, - string? urn, - Uri? webAddress - ) - { - Title = title; - Abstract = @abstract; - Section = section; - Authors = authors; - Doi = doi; - ArXiv = arXiv; - Urn = urn; - WebAddress = webAddress; - } - } + [MinLength(1)] + [GraphQLDescription("Referenced section")] + public string? Section { get; } } \ No newline at end of file diff --git a/backend/src/Data/Standard.cs b/backend/src/Data/Standard.cs index 0275693c..f6944e6b 100644 --- a/backend/src/Data/Standard.cs +++ b/backend/src/Data/Standard.cs @@ -1,54 +1,51 @@ using System; using System.ComponentModel.DataAnnotations; +using Database.Enumerations; using HotChocolate; using Microsoft.EntityFrameworkCore; -namespace Database.Data -{ - [Owned] - [GraphQLDescription("`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier.")] - public sealed class Standard +namespace Database.Data; + +[Owned] +[GraphQLDescription( + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier.")] +public sealed class Standard : IReference +{ + public Standard( + string? title, + string? @abstract, + string? section, + int? year, + Standardizer[] standardizers, + Uri? locator + ) { - [MinLength(1)] - public string? Title { get; private set; } - - [MinLength(1)] - public string? Abstract { get; private set; } - - [MinLength(1)] - [GraphQLDescription("The section of the standard to which the reference refers to.")] - public string? Section { get; private set; } - - [Range(0, 3000)] - [GraphQLDescription("It is important to define the year in which the standard was issued because there can be relevant updates of one standard.")] - public int? Year { get; private set; } - - // Numeration, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types - [Required] - public Numeration Numeration { get; set; } = default!; - - [Required] - public Enumerations.Standardizer[] Standardizers { get; private set; } - - [Url] - public Uri? Locator { get; private set; } - - public Standard( - string? title, - string? @abstract, - string? section, - int? year, - Enumerations.Standardizer[] standardizers, - Uri? locator - ) - { - Title = title; - Abstract = @abstract; - Section = section; - Year = year; - Standardizers = standardizers; - Locator = locator; - } + Title = title; + Abstract = @abstract; + Section = section; + Year = year; + Standardizers = standardizers; + Locator = locator; } + + [Range(0, 3000)] + [GraphQLDescription( + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard.")] + public int? Year { get; private set; } + + // Numeration, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types + [Required] public Numeration Numeration { get; set; } = default!; + + [Required] public Standardizer[] Standardizers { get; private set; } + + [Url] public Uri? Locator { get; private set; } + + [MinLength(1)] public string? Title { get; } + + [MinLength(1)] public string? Abstract { get; } + + [MinLength(1)] + [GraphQLDescription("The section of the standard to which the reference refers to.")] + public string? Section { get; } } \ No newline at end of file diff --git a/backend/src/Enumerations/Standardizer.cs b/backend/src/Enumerations/Standardizer.cs index 279b3128..02affdd5 100644 --- a/backend/src/Enumerations/Standardizer.cs +++ b/backend/src/Enumerations/Standardizer.cs @@ -1,31 +1,29 @@ +namespace Database.Enumerations; -namespace Database.Enumerations +public enum Standardizer { - public enum Standardizer - { - AERC, - AGI, - ASHRAE, - BREEAM, - BS, - BSI, - CEN, - CIE, - DGNB, - DIN, - DVWG, - IEC, - IES, - IFT, - ISO, - JIS, - LEED, - NFRC, - RIBA, - UL, - UNECE, - VDI, - VFF, - WELL - } + AERC, + AGI, + ASHRAE, + BREEAM, + BS, + BSI, + CEN, + CIE, + DGNB, + DIN, + DVWG, + IEC, + IES, + IFT, + ISO, + JIS, + LEED, + NFRC, + RIBA, + UL, + UNECE, + VDI, + VFF, + WELL } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index d4761e99..89d964c1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -85,26 +85,28 @@ CancellationToken cancellationToken a.ApproverId ) { - Publication = a.Publication is null ? null : - new Data.Publication( - title: a.Publication.Title, - @abstract: a.Publication.Abstract, - section: a.Publication.Section, - authors: a.Publication.Authors, - doi: a.Publication.Doi, - arXiv: a.Publication.ArXiv, - urn: a.Publication.Urn, - webAddress: a.Publication.WebAddress - ), - Standard = a.Standard is null ? null : - new Data.Standard( - title: a.Standard.Title, - @abstract: a.Standard.Abstract, - section: a.Standard.Section, - year: a.Standard.Year, - standardizers: a.Standard.Standardizers, - locator: a.Standard.Locator - ) + Publication = a.Publication is null + ? null + : new Publication( + a.Publication.Title, + a.Publication.Abstract, + a.Publication.Section, + a.Publication.Authors, + a.Publication.Doi, + a.Publication.ArXiv, + a.Publication.Urn, + a.Publication.WebAddress + ), + Standard = a.Standard is null + ? null + : new Standard( + a.Standard.Title, + a.Standard.Abstract, + a.Standard.Section, + a.Standard.Year, + a.Standard.Standardizers, + a.Standard.Locator + ) } ).ToList(), // approval: input.Approval diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovalInput.cs index d4d7478a..277aa58f 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovalInput.cs @@ -1,5 +1,4 @@ using System; -using Database.Data; using Database.GraphQl.Publications; using Database.GraphQl.Standards; @@ -14,4 +13,4 @@ public sealed record DataApprovalInput( Guid ApproverId, CreateStandardInput? Standard, CreatePublicationInput? Publication -); +); \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 983b7dfd..0ce5444d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -85,26 +85,28 @@ CancellationToken cancellationToken a.ApproverId ) { - Publication = a.Publication is null ? null : - new Data.Publication( - title: a.Publication.Title, - @abstract: a.Publication.Abstract, - section: a.Publication.Section, - authors: a.Publication.Authors, - doi: a.Publication.Doi, - arXiv: a.Publication.ArXiv, - urn: a.Publication.Urn, - webAddress: a.Publication.WebAddress - ), - Standard = a.Standard is null ? null : - new Data.Standard( - title: a.Standard.Title, - @abstract: a.Standard.Abstract, - section: a.Standard.Section, - year: a.Standard.Year, - standardizers: a.Standard.Standardizers, - locator: a.Standard.Locator - ) + Publication = a.Publication is null + ? null + : new Publication( + a.Publication.Title, + a.Publication.Abstract, + a.Publication.Section, + a.Publication.Authors, + a.Publication.Doi, + a.Publication.ArXiv, + a.Publication.Urn, + a.Publication.WebAddress + ), + Standard = a.Standard is null + ? null + : new Standard( + a.Standard.Title, + a.Standard.Abstract, + a.Standard.Section, + a.Standard.Year, + a.Standard.Standardizers, + a.Standard.Locator + ) } ).ToList() // approval: input.Approval diff --git a/backend/src/GraphQl/Numerations/CreateNumerationInput.cs b/backend/src/GraphQl/Numerations/CreateNumerationInput.cs index 3e0af7f0..39695820 100644 --- a/backend/src/GraphQl/Numerations/CreateNumerationInput.cs +++ b/backend/src/GraphQl/Numerations/CreateNumerationInput.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.Numerations -{ - public sealed record CreateNumerationInput( - string? Prefix, - string MainNumber, - string? Suffix - ); -} \ No newline at end of file +namespace Database.GraphQl.Numerations; + +public sealed record CreateNumerationInput( + string? Prefix, + string MainNumber, + string? Suffix +); \ No newline at end of file diff --git a/backend/src/GraphQl/Numerations/NumerationType.cs b/backend/src/GraphQl/Numerations/NumerationType.cs index cbe9ed53..ffa58620 100644 --- a/backend/src/GraphQl/Numerations/NumerationType.cs +++ b/backend/src/GraphQl/Numerations/NumerationType.cs @@ -1,14 +1,14 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.Numerations +namespace Database.GraphQl.Numerations; + +public sealed class NumerationType + : ObjectType { - public sealed class NumerationType - : ObjectType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs b/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs index e24443cf..44990194 100644 --- a/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs +++ b/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs @@ -1,8 +1,7 @@ -namespace Database.GraphQl.Numerations -{ - public sealed record UpdateNumerationInput( - string? Prefix, - string MainNumber, - string? Suffix - ); -} \ No newline at end of file +namespace Database.GraphQl.Numerations; + +public sealed record UpdateNumerationInput( + string? Prefix, + string MainNumber, + string? Suffix +); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 80258b8d..e5323a3c 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -85,26 +85,28 @@ CancellationToken cancellationToken a.ApproverId ) { - Publication = a.Publication is null ? null : - new Data.Publication( - title: a.Publication.Title, - @abstract: a.Publication.Abstract, - section: a.Publication.Section, - authors: a.Publication.Authors, - doi: a.Publication.Doi, - arXiv: a.Publication.ArXiv, - urn: a.Publication.Urn, - webAddress: a.Publication.WebAddress - ), - Standard = a.Standard is null ? null : - new Data.Standard( - title: a.Standard.Title, - @abstract: a.Standard.Abstract, - section: a.Standard.Section, - year: a.Standard.Year, - standardizers: a.Standard.Standardizers, - locator: a.Standard.Locator - ) + Publication = a.Publication is null + ? null + : new Publication( + a.Publication.Title, + a.Publication.Abstract, + a.Publication.Section, + a.Publication.Authors, + a.Publication.Doi, + a.Publication.ArXiv, + a.Publication.Urn, + a.Publication.WebAddress + ), + Standard = a.Standard is null + ? null + : new Standard( + a.Standard.Title, + a.Standard.Abstract, + a.Standard.Section, + a.Standard.Year, + a.Standard.Standardizers, + a.Standard.Locator + ) } ).ToList(), // approval: input.Approval diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index a07897f6..f1a22eca 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -85,26 +85,28 @@ CancellationToken cancellationToken a.ApproverId ) { - Publication = a.Publication is null ? null : - new Data.Publication( - title: a.Publication.Title, - @abstract: a.Publication.Abstract, - section: a.Publication.Section, - authors: a.Publication.Authors, - doi: a.Publication.Doi, - arXiv: a.Publication.ArXiv, - urn: a.Publication.Urn, - webAddress: a.Publication.WebAddress - ), - Standard = a.Standard is null ? null : - new Data.Standard( - title: a.Standard.Title, - @abstract: a.Standard.Abstract, - section: a.Standard.Section, - year: a.Standard.Year, - standardizers: a.Standard.Standardizers, - locator: a.Standard.Locator - ) + Publication = a.Publication is null + ? null + : new Publication( + a.Publication.Title, + a.Publication.Abstract, + a.Publication.Section, + a.Publication.Authors, + a.Publication.Doi, + a.Publication.ArXiv, + a.Publication.Urn, + a.Publication.WebAddress + ), + Standard = a.Standard is null + ? null + : new Standard( + a.Standard.Title, + a.Standard.Abstract, + a.Standard.Section, + a.Standard.Year, + a.Standard.Standardizers, + a.Standard.Locator + ) } ).ToList() // approval: input.Approval diff --git a/backend/src/GraphQl/Publications/CreatePublicationInput.cs b/backend/src/GraphQl/Publications/CreatePublicationInput.cs index 3662958d..25d59a13 100644 --- a/backend/src/GraphQl/Publications/CreatePublicationInput.cs +++ b/backend/src/GraphQl/Publications/CreatePublicationInput.cs @@ -1,15 +1,14 @@ using System; -namespace Database.GraphQl.Publications -{ - public sealed record CreatePublicationInput( - string? Title, - string? Abstract, - string? Section, - string[]? Authors, - string? Doi, - string? ArXiv, - string? Urn, - Uri? WebAddress - ); -} \ No newline at end of file +namespace Database.GraphQl.Publications; + +public sealed record CreatePublicationInput( + string? Title, + string? Abstract, + string? Section, + string[]? Authors, + string? Doi, + string? ArXiv, + string? Urn, + Uri? WebAddress +); \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs index 68b6c5ee..0d7dc210 100644 --- a/backend/src/GraphQl/Publications/PublicationType.cs +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -1,14 +1,14 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.Publications +namespace Database.GraphQl.Publications; + +public sealed class PublicationType + : ObjectType { - public sealed class PublicationType - : ObjectType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/UpdatePublicationInput.cs b/backend/src/GraphQl/Publications/UpdatePublicationInput.cs index a29e751a..d1bf0c2b 100644 --- a/backend/src/GraphQl/Publications/UpdatePublicationInput.cs +++ b/backend/src/GraphQl/Publications/UpdatePublicationInput.cs @@ -1,15 +1,14 @@ using System; -namespace Database.GraphQl.Publications -{ - public sealed record UpdatePublicationInput( - string? Title, - string? Abstract, - string? Section, - string[]? Authors, - string? Doi, - string? ArXiv, - string? Urn, - Uri? WebAddress - ); -} \ No newline at end of file +namespace Database.GraphQl.Publications; + +public sealed record UpdatePublicationInput( + string? Title, + string? Abstract, + string? Section, + string[]? Authors, + string? Doi, + string? ArXiv, + string? Urn, + Uri? WebAddress +); \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs index f6f644c9..3489564e 100644 --- a/backend/src/GraphQl/References/ReferenceType.cs +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -1,13 +1,13 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.References +namespace Database.GraphQl.References; + +public sealed class ReferenceType + : InterfaceType { - public sealed class ReferenceType - : InterfaceType + protected override void Configure(IInterfaceTypeDescriptor descriptor) { - protected override void Configure(IInterfaceTypeDescriptor descriptor) - { - descriptor.Name(nameof(Data.IReference)[1..]); - } + descriptor.Name(nameof(IReference)[1..]); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/CreateStandardInput.cs b/backend/src/GraphQl/Standards/CreateStandardInput.cs index 7eee2e5f..7323bb2a 100644 --- a/backend/src/GraphQl/Standards/CreateStandardInput.cs +++ b/backend/src/GraphQl/Standards/CreateStandardInput.cs @@ -1,15 +1,15 @@ using System; +using Database.Enumerations; using Database.GraphQl.Numerations; -namespace Database.GraphQl.Standards -{ - public sealed record CreateStandardInput( - string? Title, - string? Abstract, - string? Section, - int? Year, - CreateNumerationInput Numeration, - Enumerations.Standardizer[] Standardizers, - Uri? Locator - ); -} \ No newline at end of file +namespace Database.GraphQl.Standards; + +public sealed record CreateStandardInput( + string? Title, + string? Abstract, + string? Section, + int? Year, + CreateNumerationInput Numeration, + Standardizer[] Standardizers, + Uri? Locator +); \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/StandardType.cs b/backend/src/GraphQl/Standards/StandardType.cs index 0d745670..8865bf78 100644 --- a/backend/src/GraphQl/Standards/StandardType.cs +++ b/backend/src/GraphQl/Standards/StandardType.cs @@ -1,14 +1,14 @@ +using Database.Data; using HotChocolate.Types; -namespace Database.GraphQl.Standards +namespace Database.GraphQl.Standards; + +public sealed class StandardType + : ObjectType { - public sealed class StandardType - : ObjectType + protected override void Configure( + IObjectTypeDescriptor descriptor + ) { - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/UpdateStandardInput.cs b/backend/src/GraphQl/Standards/UpdateStandardInput.cs index a43a33bd..0990987c 100644 --- a/backend/src/GraphQl/Standards/UpdateStandardInput.cs +++ b/backend/src/GraphQl/Standards/UpdateStandardInput.cs @@ -1,15 +1,15 @@ using System; +using Database.Enumerations; using Database.GraphQl.Numerations; -namespace Database.GraphQl.Standards -{ - public sealed record UpdateStandardInput( - string? Title, - string? Abstract, - string? Section, - int? Year, - UpdateNumerationInput Numeration, - Enumerations.Standardizer[] Standardizers, - Uri? Locator - ); -} \ No newline at end of file +namespace Database.GraphQl.Standards; + +public sealed record UpdateStandardInput( + string? Title, + string? Abstract, + string? Section, + int? Year, + UpdateNumerationInput Numeration, + Standardizer[] Standardizers, + Uri? Locator +); \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 6e8d190b..baf2ba0b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -122,7 +122,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum dataSourceBuilder.MapEnum(); - dataSourceBuilder.MapEnum(); + dataSourceBuilder.MapEnum(); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) From eeaea0219454ddf268884cf92e1db9b0c1d579a7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Apr 2024 18:25:39 +0200 Subject: [PATCH 159/878] Remove setter for `Statement` (casting like this is error-prone as the compiler does not check that an `IReference` is either `Standard` or `Publication` so it may happen in the future that it is something else resulting in a runtime error) --- backend/src/Data/DataApproval.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 5c3dc675..6e2f583f 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -33,14 +33,5 @@ Guid approverId public string Response { get; private set; } public Publication? Publication { get; set; } public Standard? Standard { get; set; } - [NotMapped] - public IReference? Statement - { - get => Standard is not null ? Standard : Publication; - set - { - if (value is Standard) Standard = (Standard)value; - else Publication = (Publication)value; - } - } + [NotMapped] public IReference? Statement => Standard is not null ? Standard : Publication; } \ No newline at end of file From b127d99f92a3f7bc08fdb13d179d5f86093f2282 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 16:51:12 +0200 Subject: [PATCH 160/878] Add missing setters to map properties to PostgreSQL table columns --- backend/src/Data/Publication.cs | 6 +++--- backend/src/Data/Standard.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs index 21a1f2ad..f5ea3b80 100644 --- a/backend/src/Data/Publication.cs +++ b/backend/src/Data/Publication.cs @@ -55,11 +55,11 @@ public Publication( "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] public Uri? WebAddress { get; private set; } - [MinLength(1)] public string? Title { get; } + [MinLength(1)] public string? Title { get; private set; } - [MinLength(1)] public string? Abstract { get; } + [MinLength(1)] public string? Abstract { get; private set; } [MinLength(1)] [GraphQLDescription("Referenced section")] - public string? Section { get; } + public string? Section { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/Standard.cs b/backend/src/Data/Standard.cs index f6944e6b..003518f0 100644 --- a/backend/src/Data/Standard.cs +++ b/backend/src/Data/Standard.cs @@ -41,11 +41,11 @@ public Standard( [Url] public Uri? Locator { get; private set; } - [MinLength(1)] public string? Title { get; } + [MinLength(1)] public string? Title { get; private set; } - [MinLength(1)] public string? Abstract { get; } + [MinLength(1)] public string? Abstract { get; private set; } [MinLength(1)] [GraphQLDescription("The section of the standard to which the reference refers to.")] - public string? Section { get; } + public string? Section { get; private set; } } \ No newline at end of file From 5e80c07cd8ab2af3e83a754961f01025226108e1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 16:51:49 +0200 Subject: [PATCH 161/878] Add migration that adds statement to data approval --- ...935_AddStatementToDataApproval.Designer.cs | 1774 +++++++++++++++++ ...240709144935_AddStatementToDataApproval.cs | 849 ++++++++ .../ApplicationDbContextModelSnapshot.cs | 435 +++- 3 files changed, 3057 insertions(+), 1 deletion(-) create mode 100644 backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs create mode 100644 backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs new file mode 100644 index 00000000..05aa0a18 --- /dev/null +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs @@ -0,0 +1,1774 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240709144935_AddStatementToDataApproval")] + partial class AddStatementToDataApproval + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs new file mode 100644 index 00000000..e048825a --- /dev/null +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs @@ -0,0 +1,849 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddStatementToDataApproval : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index e15db1ce..2019298e 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -20,10 +20,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -672,6 +673,114 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); }); b.Navigation("AppliedMethod") @@ -934,6 +1043,114 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); }); b.Navigation("AppliedMethod") @@ -1112,6 +1329,114 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); }); b.Navigation("AppliedMethod") @@ -1264,6 +1589,114 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); }); b.Navigation("AppliedMethod") From 04f69310a45941726e80e37178aa6dd2316ea5aa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Jul 2024 17:03:39 +0200 Subject: [PATCH 162/878] Configure data approvals, standards, publications, and references for GraphQL usage --- .../src/Configuration/GraphQlConfiguration.cs | 7 + .../GraphQl/DataApprovals/DataApprovalType.cs | 19 ++ frontend/type-defs.graphqls | 241 ++++++++++++++++++ 3 files changed, 267 insertions(+) create mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalType.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 61957d96..214aa24a 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -12,6 +12,9 @@ using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; +using Database.GraphQl.Publications; +using Database.GraphQl.References; +using Database.GraphQl.Standards; using Database.GraphQl.Users; using HotChocolate.Data; using HotChocolate.Data.Filters; @@ -145,12 +148,16 @@ AppSettings appSettings // Object Types .AddType() .AddType() + .AddType() .AddType() .AddType() .AddType() .AddType() .AddType() .AddType() + .AddType() + .AddType() + .AddType() .AddType() // Data Loaders /* .AddDataLoader() */ diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs new file mode 100644 index 00000000..dd17443e --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs @@ -0,0 +1,19 @@ +using Database.Data; +using HotChocolate.Types; + +namespace Database.GraphQl.References; + +public sealed class DataApprovalType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Standard) + .Ignore(); + descriptor + .Field(t => t.Publication) + .Ignore(); + + } +} \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 84a62036..86d60b81 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -24,6 +24,12 @@ interface Node { id: ID! } +interface Reference { + abstract: String + section: String + title: String +} + type Address { formatted: String! } @@ -152,6 +158,7 @@ type DataApproval { query: String! response: String! signature: String! + statement: Reference timestamp: DateTime! } @@ -298,6 +305,12 @@ type NamedMethodSource { value: CrossDatabaseDataReference! } +type Numeration { + mainNumber: String! + prefix: String + suffix: String +} + type OpenEndedDateTimeRange { from: DateTime until: DateTime @@ -396,6 +409,22 @@ type PhotovoltaicDataEdge { node: PhotovoltaicData! } +type Publication implements Reference { + abstract: String + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: String + authors: [String!] + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: String + "Referenced section" + section: String + title: String + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: String + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: Url +} + type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] timestamp: DateTime where: CalorimetricDataPropositionInput): CalorimetricDataConnection allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] timestamp: DateTime where: DataPropositionInput): DataConnection @@ -419,6 +448,19 @@ type Query { verificationCode: String! } +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +type Standard implements Reference { + abstract: String + locator: Url + numeration: Numeration! + "The section of the standard to which the reference refers to." + section: String + standardizers: [Standardizer!]! + title: String + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: Int +} + type ToTreeVertexAppliedConversionMethod { arguments: [NamedMethodArgument!]! methodId: Uuid! @@ -474,6 +516,11 @@ input AppliedMethodSortInput { methodId: SortEnumType } +input BooleanPropositionInput { + equalTo: Boolean + notEqualTo: Boolean +} + input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] @@ -560,6 +607,12 @@ input CreateHygrothermalDataInput { warnings: [String!]! } +input CreateNumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + input CreateOpticalDataInput { accessToken: String! appliedMethod: AppliedMethodInput! @@ -595,6 +648,27 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } +input CreatePublicationInput { + abstract: String + arXiv: String + authors: [String!] + doi: String + section: String + title: String + urn: String + webAddress: Url +} + +input CreateStandardInput { + abstract: String + locator: Url + numeration: CreateNumerationInput! + section: String + standardizers: [Standardizer!]! + title: String + year: Int +} + input CrossDatabaseDataReferenceInput { databaseId: Uuid! dataId: Uuid! @@ -614,9 +688,11 @@ input CrossDatabaseDataReferencePropositionInput { input DataApprovalInput { approverId: Uuid! keyFingerprint: String! + publication: CreatePublicationInput query: String! response: String! signature: String! + standard: CreateStandardInput timestamp: DateTime! } @@ -629,6 +705,9 @@ input DataApprovalPropositionInput { keyFingerprint: StringPropositionInput query: StringPropositionInput response: StringPropositionInput + publication: PublicationPropositionInput + standard: StandardPropositionInput + statement: IReferencePropositionInput } input DataKindPropositionInput { @@ -775,6 +854,29 @@ input IDataSortInput { locale: SortEnumType } +input IReferencePropositionInput { + and: [IReferencePropositionInput!] + or: [IReferencePropositionInput!] + title: StringPropositionInput + abstract: StringPropositionInput + section: StringPropositionInput +} + +input Int32PropositionInput { + equalTo: Int + notEqualTo: Int + in: [Int!] + notIn: [Int!] + greaterThan: Int + notGreaterThan: Int + greaterThanOrEqualTo: Int + notGreaterThanOrEqualTo: Int + lessThan: Int + notLessThan: Int + lessThanOrEqualTo: Int + notLessThanOrEqualTo: Int +} + input ListFilterInputTypeOfCielabColorPropositionInput { all: CielabColorPropositionInput none: CielabColorPropositionInput @@ -817,6 +919,13 @@ input ListNamedMethodArgumentFilterTypePropositionInput { any: Boolean } +input ListStandardizerPropositionInput { + all: StandardizerPropositionInput + none: StandardizerPropositionInput + some: StandardizerPropositionInput + any: Boolean +} + input NamedMethodArgumentInput { name: String! value: Any! @@ -840,6 +949,21 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NullableOfInt32PropositionInput { + equalTo: Int + notEqualTo: Int + in: [Int] + notIn: [Int] + greaterThan: Int + notGreaterThan: Int + greaterThanOrEqualTo: Int + notGreaterThanOrEqualTo: Int + lessThan: Int + notLessThan: Int + lessThanOrEqualTo: Int + notLessThanOrEqualTo: Int +} + input NullableOfUuidPropositionInput { equalTo: Uuid notEqualTo: Uuid @@ -855,6 +979,14 @@ input NullableOfUuidPropositionInput { notLessThanOrEqualTo: Uuid } +input NumerationPropositionInput { + and: [NumerationPropositionInput!] + or: [NumerationPropositionInput!] + prefix: StringPropositionInput + mainNumber: StringPropositionInput + suffix: StringPropositionInput +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] @@ -918,6 +1050,24 @@ input PhotovoltaicDataSortInput { version: SortEnumType } +input PublicationPropositionInput { + and: [PublicationPropositionInput!] + or: [PublicationPropositionInput!] + authors: StringsPropositionInput + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: StringPropositionInput + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: StringPropositionInput + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: StringPropositionInput + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "Referenced section" + section: StringPropositionInput +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -926,6 +1076,28 @@ input RootGetHttpsResourceInput { hashValue: String! } +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardPropositionInput { + and: [StandardPropositionInput!] + or: [StandardPropositionInput!] + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: NullableOfInt32PropositionInput + numeration: NumerationPropositionInput + standardizers: ListStandardizerPropositionInput + locator: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "The section of the standard to which the reference refers to." + section: StringPropositionInput +} + +input StandardizerPropositionInput { + equalTo: Standardizer + notEqualTo: Standardizer + in: [Standardizer!] + notIn: [Standardizer!] +} + input StringPropositionInput { and: [StringPropositionInput!] or: [StringPropositionInput!] @@ -974,6 +1146,40 @@ input UpdateDatabaseInput { name: String! } +input UriHostNameTypePropositionInput { + equalTo: UriHostNameType + notEqualTo: UriHostNameType + in: [UriHostNameType!] + notIn: [UriHostNameType!] +} + +input UriPropositionInput { + and: [UriPropositionInput!] + or: [UriPropositionInput!] + absolutePath: StringPropositionInput + absoluteUri: StringPropositionInput + localPath: StringPropositionInput + authority: StringPropositionInput + hostNameType: UriHostNameTypePropositionInput + isDefaultPort: BooleanPropositionInput + isFile: BooleanPropositionInput + isLoopback: BooleanPropositionInput + pathAndQuery: StringPropositionInput + segments: StringsPropositionInput + isUnc: BooleanPropositionInput + host: StringPropositionInput + port: Int32PropositionInput + query: StringPropositionInput + fragment: StringPropositionInput + scheme: StringPropositionInput + originalString: StringPropositionInput + dnsSafeHost: StringPropositionInput + idnHost: StringPropositionInput + isAbsoluteUri: BooleanPropositionInput + userEscaped: BooleanPropositionInput + userInfo: StringPropositionInput +} + input UuidPropositionInput { equalTo: Uuid notEqualTo: Uuid @@ -1037,12 +1243,47 @@ enum SortEnumType { DESC } +enum Standardizer { + AERC + AGI + ASHRAE + BREEAM + BS + BSI + CEN + CIE + DGNB + DIN + DVWG + IEC + IES + IFT + ISO + JIS + LEED + NFRC + RIBA + UL + UNECE + VDI + VFF + WELL +} + enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED UNKNOWN_DATABASE } +enum UriHostNameType { + UNKNOWN + BASIC + DNS + I_PV4 + I_PV6 +} + scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." From 579cca1c409b6d83ff4ad13dc61ce804264b992a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 22 Jul 2024 15:35:36 +0200 Subject: [PATCH 163/878] Move custom types from the schema `public` to the schema `metabase` --- backend/src/Data/ApplicationDbContext.cs | 22 +- ..._MoveEnumTypesToMetabaseSchema.Designer.cs | 1774 +++++++++++++++++ ...722133619_MoveEnumTypesToMetabaseSchema.cs | 187 ++ .../ApplicationDbContextModelSnapshot.cs | 20 +- backend/src/Startup.cs | 5 +- 5 files changed, 1988 insertions(+), 20 deletions(-) create mode 100644 backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs create mode 100644 backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b477df77..3736c1a1 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -12,15 +12,21 @@ namespace Database.Data; public sealed class ApplicationDbContext : DbContext, IDataProtectionKeyContext { + private const string DefaultSchemaName = "database"; private readonly string _schemaName; + internal const string DataKindTypeName = "data_kind"; + internal const string StandardizerTypeName = "standardizer"; + public ApplicationDbContext( DbContextOptions options ) : base(options) { + // The schema-name option is set in `Metabase.Startup` by an invocation of + // `UseSchemaName` on a `DbContextOptionsBuilder` instance. var schemaNameOptions = options.FindExtension(); - _schemaName = schemaNameOptions is null ? "database" : schemaNameOptions.SchemaName; + _schemaName = schemaNameOptions is null ? DefaultSchemaName : schemaNameOptions.SchemaName; } // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset @@ -32,13 +38,13 @@ DbContextOptions options public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; - private static void CreateEnumerations(ModelBuilder builder) + private static void CreateEnumerations(ModelBuilder builder, string schemaName) { - // https://www.npgsql.org/efcore/mapping/enum.html#creating-your-database-enum - // Create enumerations in public schema because that is where - // `NpgsqlDataSourceBuilder.MapEnum` expects them to be by default. - builder.HasPostgresEnum("public"); - builder.HasPostgresEnum("public"); + // https://www.npgsql.org/efcore/mapping/enum.html?tabs=with-datasource#mapping-your-enum + // Create enumerations in the same schema used by + // `NpgsqlDataSourceBuilder.MapEnum` in `Startup`. + builder.HasPostgresEnum(schemaName, DataKindTypeName); + builder.HasPostgresEnum(schemaName, StandardizerTypeName); } private static @@ -72,7 +78,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.HasDefaultSchema(_schemaName); modelBuilder.HasPostgresExtension( "pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - CreateEnumerations(modelBuilder); + CreateEnumerations(modelBuilder, _schemaName); ConfigureIdentityEntities(modelBuilder); ConfigureEntity( modelBuilder.Entity() diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs new file mode 100644 index 00000000..5b997247 --- /dev/null +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs @@ -0,0 +1,1774 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240722133619_MoveEnumTypesToMetabaseSchema")] + partial class MoveEnumTypesToMetabaseSchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs new file mode 100644 index 00000000..96e6e566 --- /dev/null +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs @@ -0,0 +1,187 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MoveEnumTypesToMetabaseSchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "photovoltaic_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "optical_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "hygrothermal_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "calorimetric_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "photovoltaic_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "optical_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "hygrothermal_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "calorimetric_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 2019298e..2e83f4d1 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -23,8 +23,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -609,7 +609,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); @@ -733,7 +733,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Standardizers") .IsRequired() - .HasColumnType("standardizer[]"); + .HasColumnType("database.standardizer[]"); b2.Property("Title") .HasColumnType("text"); @@ -979,7 +979,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); @@ -1103,7 +1103,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Standardizers") .IsRequired() - .HasColumnType("standardizer[]"); + .HasColumnType("database.standardizer[]"); b2.Property("Title") .HasColumnType("text"); @@ -1265,7 +1265,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); @@ -1389,7 +1389,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Standardizers") .IsRequired() - .HasColumnType("standardizer[]"); + .HasColumnType("database.standardizer[]"); b2.Property("Title") .HasColumnType("text"); @@ -1525,7 +1525,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); @@ -1649,7 +1649,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Standardizers") .IsRequired() - .HasColumnType("standardizer[]"); + .HasColumnType("database.standardizer[]"); b2.Property("Title") .HasColumnType("text"); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index baf2ba0b..ee12964b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -121,8 +121,9 @@ private void ConfigureDatabaseServices(IServiceCollection services) { var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - dataSourceBuilder.MapEnum(); - dataSourceBuilder.MapEnum(); + // Keep in sync with `ApplicationDbContext.CreateEnumerations`. + dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); + dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) From 6f5a7165afad270c290b41c5cae045d9ce0c4f90 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 22 Jul 2024 15:57:20 +0200 Subject: [PATCH 164/878] Add a comment regarding the different ways the type name is specified --- backend/src/Data/ApplicationDbContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 3736c1a1..ba67d2f7 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -42,7 +42,10 @@ private static void CreateEnumerations(ModelBuilder builder, string schemaName) { // https://www.npgsql.org/efcore/mapping/enum.html?tabs=with-datasource#mapping-your-enum // Create enumerations in the same schema used by - // `NpgsqlDataSourceBuilder.MapEnum` in `Startup`. + // `NpgsqlDataSourceBuilder.MapEnum` in `Startup`. The format of how to + // specify the type name here and in `Startup` differ slightly. This + // was complained about in + // https://github.com/npgsql/efcore.pg/issues/2963#issuecomment-1818866360 builder.HasPostgresEnum(schemaName, DataKindTypeName); builder.HasPostgresEnum(schemaName, StandardizerTypeName); } From 24cfe5bc721e9e0e07a84d8d48463aade662f010 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 22 Jul 2024 17:06:31 +0200 Subject: [PATCH 165/878] Create migration and rollback SQL scripts --- backend/src/Migrations/current | 2 +- ...22133619_MoveEnumTypesToMetabaseSchema.sql | 319 ++++++++++++++++++ backend/src/Migrations/previous | 2 +- ...22133619_MoveEnumTypesToMetabaseSchema.sql | 232 +++++++++++++ 4 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql create mode 100644 backend/src/Migrations/rollback_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index 89cc0f5e..b0f8604f 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20230210135413_AddDataProtectionKeys +20240722133619_MoveEnumTypesToMetabaseSchema diff --git a/backend/src/Migrations/migrate_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql b/backend/src/Migrations/migrate_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql new file mode 100644 index 00000000..69abc8c5 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql @@ -0,0 +1,319 @@ +START TRANSACTION; + +CREATE TABLE database."OpenIddictApplications" ( + "Id" text NOT NULL, + "ClientId" character varying(100), + "ClientSecret" text, + "ConcurrencyToken" character varying(50), + "ConsentType" character varying(50), + "DisplayName" text, + "DisplayNames" text, + "Permissions" text, + "PostLogoutRedirectUris" text, + "Properties" text, + "RedirectUris" text, + "Requirements" text, + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictApplications" PRIMARY KEY ("Id") +); + +CREATE TABLE database."OpenIddictScopes" ( + "Id" text NOT NULL, + "ConcurrencyToken" character varying(50), + "Description" text, + "Descriptions" text, + "DisplayName" text, + "DisplayNames" text, + "Name" character varying(200), + "Properties" text, + "Resources" text, + CONSTRAINT "PK_OpenIddictScopes" PRIMARY KEY ("Id") +); + +CREATE TABLE database."OpenIddictAuthorizations" ( + "Id" text NOT NULL, + "ApplicationId" text, + "ConcurrencyToken" character varying(50), + "CreationDate" timestamp with time zone, + "Properties" text, + "Scopes" text, + "Status" character varying(50), + "Subject" character varying(400), + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictAuthorizations" PRIMARY KEY ("Id"), + CONSTRAINT "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~" FOREIGN KEY ("ApplicationId") REFERENCES database."OpenIddictApplications" ("Id") +); + +CREATE TABLE database."OpenIddictTokens" ( + "Id" text NOT NULL, + "ApplicationId" text, + "AuthorizationId" text, + "ConcurrencyToken" character varying(50), + "CreationDate" timestamp with time zone, + "ExpirationDate" timestamp with time zone, + "Payload" text, + "Properties" text, + "RedemptionDate" timestamp with time zone, + "ReferenceId" character varying(100), + "Status" character varying(50), + "Subject" character varying(400), + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictTokens" PRIMARY KEY ("Id"), + CONSTRAINT "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId" FOREIGN KEY ("ApplicationId") REFERENCES database."OpenIddictApplications" ("Id"), + CONSTRAINT "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId" FOREIGN KEY ("AuthorizationId") REFERENCES database."OpenIddictAuthorizations" ("Id") +); + +CREATE UNIQUE INDEX "IX_OpenIddictApplications_ClientId" ON database."OpenIddictApplications" ("ClientId"); + +CREATE INDEX "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type" ON database."OpenIddictAuthorizations" ("ApplicationId", "Status", "Subject", "Type"); + +CREATE UNIQUE INDEX "IX_OpenIddictScopes_Name" ON database."OpenIddictScopes" ("Name"); + +CREATE INDEX "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type" ON database."OpenIddictTokens" ("ApplicationId", "Status", "Subject", "Type"); + +CREATE INDEX "IX_OpenIddictTokens_AuthorizationId" ON database."OpenIddictTokens" ("AuthorizationId"); + +CREATE UNIQUE INDEX "IX_OpenIddictTokens_ReferenceId" ON database."OpenIddictTokens" ("ReferenceId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230420143838_OpenIddict', '8.0.6'); + +COMMIT; + +START TRANSACTION; + +CREATE TABLE database."user" ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Subject" text NOT NULL, + "Email" text NOT NULL, + "Name" text NOT NULL, + CONSTRAINT "PK_user" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230427133946_CreateUserTable', '8.0.6'); + +COMMIT; + +START TRANSACTION; + +ALTER TABLE database."user" DROP COLUMN "Email"; + +ALTER TABLE database."OpenIddictApplications" RENAME COLUMN "Type" TO "ClientType"; + +ALTER TABLE database."OpenIddictApplications" ADD "ApplicationType" character varying(50); + +ALTER TABLE database."OpenIddictApplications" ADD "JsonWebKeySet" text; + +ALTER TABLE database."OpenIddictApplications" ADD "Settings" text; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240522120225_UpgradeOpenIddict', '8.0.6'); + +COMMIT; + +START TRANSACTION; + +CREATE TYPE public.standardizer AS ENUM ('aerc', 'agi', 'ashrae', 'breeam', 'bs', 'bsi', 'cen', 'cie', 'dgnb', 'din', 'dvwg', 'iec', 'ies', 'ift', 'iso', 'jis', 'leed', 'nfrc', 'riba', 'ul', 'unece', 'vdi', 'vff', 'well'); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Year" integer; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240709144935_AddStatementToDataApproval', '8.0.6'); + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE TYPE database.data_kind AS ENUM ('calorimetric_data', 'hygrothermal_data', 'optical_data', 'photovoltaic_data'); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE TYPE database.standardizer AS ENUM ('aerc', 'agi', 'ashrae', 'breeam', 'bs', 'bsi', 'cen', 'cie', 'dgnb', 'din', 'dvwg', 'iec', 'ies', 'ift', 'iso', 'jis', 'leed', 'nfrc', 'riba', 'ul', 'unece', 'vdi', 'vff', 'well'); + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +DROP TYPE public.data_kind; +DROP TYPE public.standardizer; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240722133619_MoveEnumTypesToMetabaseSchema', '8.0.6'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index eda70dee..89cc0f5e 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20230126153325_RenameXminToVersion +20230210135413_AddDataProtectionKeys diff --git a/backend/src/Migrations/rollback_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql b/backend/src/Migrations/rollback_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql new file mode 100644 index 00000000..69cd5bdd --- /dev/null +++ b/backend/src/Migrations/rollback_from_20230210135413_AddDataProtectionKeys_to_20240722133619_MoveEnumTypesToMetabaseSchema.sql @@ -0,0 +1,232 @@ +START TRANSACTION; + +CREATE TYPE public.data_kind AS ENUM ('calorimetric_data', 'hygrothermal_data', 'optical_data', 'photovoltaic_data'); +CREATE TYPE public.standardizer AS ENUM ('aerc', 'agi', 'ashrae', 'breeam', 'bs', 'bsi', 'cen', 'cie', 'dgnb', 'din', 'dvwg', 'iec', 'ies', 'ift', 'iso', 'jis', 'leed', 'nfrc', 'riba', 'ul', 'unece', 'vdi', 'vff', 'well'); + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataKind" TYPE data_kind USING "Value_DataKind"::text::data_kind; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE standardizer[] USING "Standard_Standardizers"::text::standardizer[]; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataKind" TYPE data_kind USING "Value_DataKind"::text::data_kind; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE standardizer[] USING "Standard_Standardizers"::text::standardizer[]; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataKind" TYPE data_kind USING "Value_DataKind"::text::data_kind; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE standardizer[] USING "Standard_Standardizers"::text::standardizer[]; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE data_kind USING "Value_DataKind"::text::data_kind; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE standardizer[] USING "Standard_Standardizers"::text::standardizer[]; + +DROP TYPE database.data_kind; +DROP TYPE database.standardizer; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20240722133619_MoveEnumTypesToMetabaseSchema'; + +COMMIT; + +START TRANSACTION; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_ArXiv"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Authors"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Doi"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_Urn"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Publication_WebAddress"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Locator"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Numeration_MainNumber"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Numeration_Prefix"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Numeration_Suffix"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Standardizers"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Standard_Year"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Abstract"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_ArXiv"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Authors"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Doi"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Section"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Title"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_Urn"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Publication_WebAddress"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Abstract"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Locator"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Numeration_MainNumber"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Numeration_Prefix"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Numeration_Suffix"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Section"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Standardizers"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Title"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Standard_Year"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_ArXiv"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Authors"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Doi"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_Urn"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Publication_WebAddress"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Locator"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Numeration_MainNumber"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Numeration_Prefix"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Numeration_Suffix"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Standardizers"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Standard_Year"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_ArXiv"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Authors"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Doi"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_Urn"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Publication_WebAddress"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Locator"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Numeration_MainNumber"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Numeration_Prefix"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Numeration_Suffix"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Standardizers"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Standard_Year"; + +DROP TYPE public.standardizer; +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20240709144935_AddStatementToDataApproval'; + +COMMIT; + +START TRANSACTION; + +ALTER TABLE database."OpenIddictApplications" DROP COLUMN "ApplicationType"; + +ALTER TABLE database."OpenIddictApplications" DROP COLUMN "JsonWebKeySet"; + +ALTER TABLE database."OpenIddictApplications" DROP COLUMN "Settings"; + +ALTER TABLE database."OpenIddictApplications" RENAME COLUMN "ClientType" TO "Type"; + +ALTER TABLE database."user" ADD "Email" text NOT NULL DEFAULT ''; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20240522120225_UpgradeOpenIddict'; + +COMMIT; + +START TRANSACTION; + +DROP TABLE database."user"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20230427133946_CreateUserTable'; + +COMMIT; + +START TRANSACTION; + +DROP TABLE database."OpenIddictScopes"; + +DROP TABLE database."OpenIddictTokens"; + +DROP TABLE database."OpenIddictAuthorizations"; + +DROP TABLE database."OpenIddictApplications"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20230420143838_OpenIddict'; + +COMMIT; + From 65ea40eac416bb1e23e553ee06bdaf0ac2ab466a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Jul 2024 13:09:56 +0200 Subject: [PATCH 166/878] Add deployment step in which the JSON Web Token is generated --- Makefile.production | 24 ++++++++++++++++++++++++ README.md | 10 ++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Makefile.production b/Makefile.production index 0269c000..f8969607 100644 --- a/Makefile.production +++ b/Makefile.production @@ -317,3 +317,27 @@ deploy-services : ## Deploy services run-tests : COMMAND = true run-tests : execb ## Run tests .PHONY : run-tests + +# TODO Pass passwords in a more secure way! +jwt-certificates : ## Create JWT encryption and signing certificates if necessary + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + --tag ${NAME}_bootstrap \ + --file ./backend/Dockerfile-bootstrap \ + ./backend + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -cx " \ + dotnet-script \ + ./create-certificates.csx \ + -- \ + ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ + ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ + " +.PHONY : jwt-certificates diff --git a/README.md b/README.md index e3d5cd0a..eb400cd8 100644 --- a/README.md +++ b/README.md @@ -93,9 +93,11 @@ and the pages following it. dummy passwords in the copies by newly generated ones, where random passwords may be generated running `openssl rand -base64 32`. 1. Prepare PostgreSQL by generating new password files by running - `make --file Makefile.production postgres_passwords` + `make --file=Makefile.production postgres_passwords` and creating the database by running - `make --file Makefile.production createdb`. + `make --file=Makefile.production createdb`. + 1. Generate JSON Web Token (JWT) encryption and signing certificates by running + `make --file=Makefile.production jwt-certificates`. ### Creating a release @@ -127,10 +129,10 @@ and the pages following it. 1. Enter a shell on the production machine using `ssh`. 1. Change to the staging envrionment by running `cd /app/staging`. 1. Deploy the new release in the staging environment by running - `make --file Makefile.production deploy`. + `make --file=Makefile.production deploy`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running - `make --file Makefile.production rollback`, + `make --file=Makefile.production rollback`, figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by From f4b709fec7b50bab7ac21c1e509640c2785edd40 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Jul 2024 13:10:11 +0200 Subject: [PATCH 167/878] Prepend relative path with `./` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d0c04a71..d9ffa293 100644 --- a/Makefile +++ b/Makefile @@ -224,7 +224,7 @@ jwt-certificates : ## Create JWT encryption and signing certificates if necessar ${NAME}_bootstrap \ bash -cx " \ dotnet-script \ - create-certificates.csx \ + ./create-certificates.csx \ -- \ ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ From 5c0aeaf57b32ff2c375c5ef3987daa74abb63484 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Jul 2024 14:20:17 +0200 Subject: [PATCH 168/878] Add the strict same-site policy to the antiforgery cookie --- backend/src/Controllers/AntiforgeryController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 67fc3788..941d5431 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -18,7 +18,8 @@ public sealed class AntiforgeryController : Controller private readonly CookieOptions XsrfCookieOptions = new() { - HttpOnly = false + HttpOnly = false, + SameSite = SameSiteMode.Strict }; public AntiforgeryController(IAntiforgery antiforgeryService) From bc5a183cc2a5e6e320f397ee8fa29bb21ea7fdd3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Jul 2024 15:11:33 +0200 Subject: [PATCH 169/878] Correct comment regarding usage of `ApplicationDbContext` service --- backend/src/Startup.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index ee12964b..c4ce9016 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -134,7 +134,8 @@ private void ConfigureDatabaseServices(IServiceCollection services) .EnableDetailedErrors(); } ); - // Database context as services are used by `Identity`. + // Database context as services are used by `OpenIddict`, see in + // particular `AuthConfiguration`. services.AddDbContext( (serviceProvider, options) => { From 2b5d7c56ffc5b428425de85695da13423c7ed84d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 15:51:39 +0200 Subject: [PATCH 170/878] Install the frontend package `sharp` asked for by next.js --- see https://nextjs.org/docs/messages/sharp-missing-in-production --- frontend/package.json | 1 + frontend/yarn.lock | 189 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index 97684382..948fccf6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,6 +53,7 @@ "license-checker": "^25.0.1", "prettier": "^3.3.2", "react-test-renderer": "^18.2.0", + "sharp": "^0.33.4", "ts-node": "^10.9.1", "typescript": "^5.0.4", "yaml-loader": "^0.8" diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 06f76da6..72b8b4fa 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -763,6 +763,13 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== +"@emnapi/runtime@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.2.0.tgz#71d018546c3a91f3b51106530edbc056b9f2f2e3" + integrity sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ== + dependencies: + tslib "^2.4.0" + "@emotion/hash@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" @@ -1381,6 +1388,119 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== +"@img/sharp-darwin-arm64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz#a1cf4a7febece334f16e0328b9689f05797d7aec" + integrity sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA== + optionalDependencies: + "@img/sharp-libvips-darwin-arm64" "1.0.2" + +"@img/sharp-darwin-x64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz#f77be2d7c3609d3e77cd337b199a772e07b87bd2" + integrity sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw== + optionalDependencies: + "@img/sharp-libvips-darwin-x64" "1.0.2" + +"@img/sharp-libvips-darwin-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz#b69f49fecbe9572378675769b189410721b0fa53" + integrity sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA== + +"@img/sharp-libvips-darwin-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz#5665da7360d8e5ed7bee314491c8fe736b6a3c39" + integrity sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw== + +"@img/sharp-libvips-linux-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz#8a05e5e9e9b760ff46561e32f19bd5e035fa881c" + integrity sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw== + +"@img/sharp-libvips-linux-arm@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz#0fd33b9bf3221948ce0ca7a5a725942626577a03" + integrity sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw== + +"@img/sharp-libvips-linux-s390x@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz#4b89150ec91b256ee2cbb5bb125321bf029a4770" + integrity sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog== + +"@img/sharp-libvips-linux-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz#947ccc22ca5bc8c8cfe921b39a5fdaebc5e39f3f" + integrity sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ== + +"@img/sharp-libvips-linuxmusl-arm64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz#821d58ce774f0f8bed065b69913a62f65d512f2f" + integrity sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ== + +"@img/sharp-libvips-linuxmusl-x64@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz#4309474bd8b728a61af0b3b4fad0c476b5f3ccbe" + integrity sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw== + +"@img/sharp-linux-arm64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz#bd390113e256487041411b988ded13a26cfc5f95" + integrity sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.0.2" + +"@img/sharp-linux-arm@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz#14ecc81f38f75fb4cd7571bc83311746d6745fca" + integrity sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.0.2" + +"@img/sharp-linux-s390x@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz#119e8081e2c6741b5ac908fe02244e4c559e525f" + integrity sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ== + optionalDependencies: + "@img/sharp-libvips-linux-s390x" "1.0.2" + +"@img/sharp-linux-x64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz#21d4c137b8da9a313b069ff5c920ded709f853d7" + integrity sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw== + optionalDependencies: + "@img/sharp-libvips-linux-x64" "1.0.2" + +"@img/sharp-linuxmusl-arm64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz#f3fde68fd67b85a32da6f1155818c3b58b8e7ae0" + integrity sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" + +"@img/sharp-linuxmusl-x64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz#44373724aecd7b69900e0578228144e181db7892" + integrity sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw== + optionalDependencies: + "@img/sharp-libvips-linuxmusl-x64" "1.0.2" + +"@img/sharp-wasm32@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz#88e3f18d7e7cd8cfe1af98e9963db4d7b6491435" + integrity sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ== + dependencies: + "@emnapi/runtime" "^1.1.1" + +"@img/sharp-win32-ia32@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz#b1c772dd2952e983980b1eb85808fa8129484d46" + integrity sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw== + +"@img/sharp-win32-x64@0.33.4": + version "0.33.4" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz#106f911134035b4157ec92a0c154a6b6f88fa4c1" + integrity sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw== + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -3053,11 +3173,27 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + colorette@^2.0.16: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" @@ -3329,6 +3465,11 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== +detect-libc@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" + integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4438,6 +4579,11 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-async-function@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" @@ -6749,6 +6895,11 @@ semver@^7.5.3, semver@^7.5.4: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + sentence-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" @@ -6790,6 +6941,35 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== +sharp@^0.33.4: + version "0.33.4" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.4.tgz#b88e6e843e095c6ab5e1a0c59c4885e580cd8405" + integrity sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q== + dependencies: + color "^4.2.3" + detect-libc "^2.0.3" + semver "^7.6.0" + optionalDependencies: + "@img/sharp-darwin-arm64" "0.33.4" + "@img/sharp-darwin-x64" "0.33.4" + "@img/sharp-libvips-darwin-arm64" "1.0.2" + "@img/sharp-libvips-darwin-x64" "1.0.2" + "@img/sharp-libvips-linux-arm" "1.0.2" + "@img/sharp-libvips-linux-arm64" "1.0.2" + "@img/sharp-libvips-linux-s390x" "1.0.2" + "@img/sharp-libvips-linux-x64" "1.0.2" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" + "@img/sharp-libvips-linuxmusl-x64" "1.0.2" + "@img/sharp-linux-arm" "0.33.4" + "@img/sharp-linux-arm64" "0.33.4" + "@img/sharp-linux-s390x" "0.33.4" + "@img/sharp-linux-x64" "0.33.4" + "@img/sharp-linuxmusl-arm64" "0.33.4" + "@img/sharp-linuxmusl-x64" "0.33.4" + "@img/sharp-wasm32" "0.33.4" + "@img/sharp-win32-ia32" "0.33.4" + "@img/sharp-win32-x64" "0.33.4" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6827,6 +7007,13 @@ signedsource@^1.0.0: resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" From 4a00129dfd2ab7cbd0e915309b0d84b88239c044 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 15:58:40 +0200 Subject: [PATCH 171/878] Forward the client and protocol/scheme forwarded from the machine NGINX proxy in production --- nginx/templates/production/default.conf.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index ef13d2c0..3c4a2d72 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -4,8 +4,8 @@ proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-Forwarded-For $http_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection keep-alive; From a3238a7b419ba9d31cc10ff41c9aab4b1b722c37 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 16:23:19 +0200 Subject: [PATCH 172/878] Make frontend package sharp a non-dev package --- frontend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 948fccf6..f9d170b3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,7 +22,8 @@ "react": "^18.3.1", "react-cookie": "^7.1.4", "react-dom": "^18.3.1", - "react-highlight-words": "^0.20.0" + "react-highlight-words": "^0.20.0", + "sharp": "^0.33.4" }, "devDependencies": { "@babel/core": "^7.0", @@ -53,7 +54,6 @@ "license-checker": "^25.0.1", "prettier": "^3.3.2", "react-test-renderer": "^18.2.0", - "sharp": "^0.33.4", "ts-node": "^10.9.1", "typescript": "^5.0.4", "yaml-loader": "^0.8" From 371a7438b7b57fd37e22ff3122461225dee7e801 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 17:05:29 +0200 Subject: [PATCH 173/878] Add information and configuration options that me help debugging problems with PostgreSQL --- docker-compose.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1b4e3a8c..88180d2f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,12 @@ services: # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see # https://www.postgresql.org/docs/current/runtime-config.html + # For an introduction to logging see + # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: [ "-c", "max_connections=100" - # "-c", "log_statement=all" + # "-c", "log_statement=all", + # "-c", "log_duration=1" ] healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html From 76b0230010394c18ca8d9aa432800d157a5d8ee8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 17:06:51 +0200 Subject: [PATCH 174/878] Include exception details of GraphQL requests in test environment --- .../src/Configuration/GraphQlConfiguration.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 214aa24a..5ed4a2be 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -84,23 +84,26 @@ AppSettings appSettings { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs /* options.ExecutionTimeout = ...; */ - options.IncludeExceptionDetails = environment.IsDevelopment(); // Default is `Debugger.IsAttached`. + options.IncludeExceptionDetails = + environment.IsDevelopment() + || environment.IsEnvironment(Program.TestEnvironment); // Default is `Debugger.IsAttached`. /* options.QueryCacheSize = ...; */ /* options.UseComplexityMultipliers = ...; */ } ) - // TODO Configure `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. But how? + // Configure `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. But how? // Subscriptions /* .AddInMemorySubscriptions() */ - // TODO Persisted queries + // Persisted queries /* .AddFileSystemQueryStorage("./persisted_queries") */ /* .UsePersistedQueryPipeline(); */ + // HotChocolate uses the default authentication scheme, + // which we set to `null` in `AuthConfiguration` to force + // users to be explicit about what scheme to use when + // making it easier to grasp the various authentication + // flows. .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => { - // HotChocolate uses the default authentication scheme, which we - // set to `null` in `AuthConfiguration` to force users to be - // explicit about what scheme to use when making it easier to - // grasp the various authentication flows. try { await HttpContextAuthentication.Authenticate(httpContext); From bca7df20f6268f2909eea3ac3f75abe9ac10b37d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 17:52:48 +0200 Subject: [PATCH 175/878] Explain in more detail how to deploy a release testing it beforehand in staging --- README.md | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index eb400cd8..21101c05 100644 --- a/README.md +++ b/README.md @@ -127,9 +127,16 @@ and the pages following it. ### Deploying a release 1. Enter a shell on the production machine using `ssh`. -1. Change to the staging envrionment by running `cd /app/staging`. +1. Back up the production database by running + `make --directory /app/production --file=Makefile.production BACKUP_DIRECTORY=/app/production/backup backup`. +1. Change to the staging environment by running `cd /app/staging`. +1. Restore the staging database from the production backup by running + `make --file=Makefile.production BACKUP_DIRECTORY=/app/production/backup restore`. +1. Adapt the environment file `./.env` if necessary by comparing it with the + `./.env.staging.sample` file of the release to be deployed. 1. Deploy the new release in the staging environment by running - `make --file=Makefile.production deploy`. + `make --file=Makefile.production TARGET=${TAG} deploy`, where `${TAG}` is + the release tag to be deployed, for example, `v1.0.0`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running `make --file=Makefile.production rollback`, @@ -137,13 +144,23 @@ and the pages following it. create a new release, and try to deploy that release instead. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by running `cd /app/machine && make deploy` and test whether everything works - as expected and if that is the case, repeat all stages but this one in the - directory `/app/production` (instead of `/app/staging`). Note that in the + as expected and if that is the case, continue. Note that in the staging environment sent emails can be viewed in the web browser under - `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses - in the variable `RELAY_ALLOWED_EMAILS` in the `.env` file are delivered to - the respective inboxes (the variable's value is a comma separated list of - email addresses). + `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses in + the variable `RELAY_ALLOWED_EMAILS` in the `.env` file are delivered to the + respective inboxes (the variable's value is a comma separated list of email + addresses). +1. Change to the production environment by running `cd /app/production`. +1. Adapt the environment file `./.env` if necessary by comparing it with the + `./.env.staging.sample` file of the release to be deployed. +1. Deploy the new release in the production environment by running + `make --file=Makefile.production TARGET=${TAG} deploy`, where `${TAG}` is + the release tag to be deployed, for example, `v1.0.0`. +1. If it fails _after_ the database backup was made, rollback to the previous + state by running + `make --file=Makefile.production rollback`, + figure out what went wrong, apply the necessary fixes to the codebase, + create a new release, and try to deploy that release instead. ## Useful Resources From 8885b5d34f88491c04a9fd21c642cb7472c1c282 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 18:35:47 +0200 Subject: [PATCH 176/878] Improve usage info --- Makefile.production | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.production b/Makefile.production index f8969607..95f88dd3 100644 --- a/Makefile.production +++ b/Makefile.production @@ -135,7 +135,7 @@ postgres_passwords : postgres_password ## Generate PostgreSQL passwords file who # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database backup : FILES_CONTAINER_NAME = backup_${NAME}_files -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` mkdir --parents ${BACKUP_DIRECTORY} -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} @@ -194,7 +194,7 @@ prune-backups : ## Keep the most recent 7 backups, delete the rest restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore` +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore` -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} ${docker_compose} run \ @@ -253,7 +253,7 @@ end-maintenance : ## End maintenance .PHONY : end-maintenance deploy : BACKUP_DIRECTORY = $(shell pwd)/backup -deploy : begin-maintenance store-commit backup fetch-all checkout-target migrate deploy-services run-tests end-maintenance ## Deploy tag, branch, or commit `${TARGET}`, for example, `make --file Makefile.production TARGET=v1.0.0 deploy` +deploy : begin-maintenance store-commit backup fetch-all checkout-target migrate deploy-services run-tests end-maintenance ## Deploy tag, branch, or commit `${TARGET}`, for example, `make --file=Makefile.production TARGET=v1.0.0 deploy` .PHONY : deploy rollback : TARGET = $(shell cat ./commit) From d98e6a2b80531161d17d6ae422e3ecf069f96a6a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 7 Aug 2024 19:03:14 +0200 Subject: [PATCH 177/878] Include developer exception details in GraphQL responses in all environments other than production --- backend/src/Configuration/GraphQlConfiguration.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 5ed4a2be..49c65898 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -84,9 +84,7 @@ AppSettings appSettings { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs /* options.ExecutionTimeout = ...; */ - options.IncludeExceptionDetails = - environment.IsDevelopment() - || environment.IsEnvironment(Program.TestEnvironment); // Default is `Debugger.IsAttached`. + options.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. /* options.QueryCacheSize = ...; */ /* options.UseComplexityMultipliers = ...; */ } From ef378eb037fba7d0cd61d82594664d8da4f2f6f3 Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Tue, 16 Jul 2024 11:14:12 +0200 Subject: [PATCH 178/878] add geometric data --- .../src/Configuration/GraphQlConfiguration.cs | 5 + backend/src/Data/ApplicationDbContext.cs | 5 + backend/src/Data/GeometricData.cs | 55 ++++++++++ backend/src/Data/GetHttpsResource.cs | 11 +- backend/src/Enumerations/DataKind.cs | 3 +- backend/src/GraphQl/DataX/DataQueries.cs | 9 +- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 2 +- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 2 +- .../GetHttpsResourcesByDataIdDataLoader.cs | 2 +- .../CreateGeometricDataError.cs | 16 +++ .../CreateGeometricDataErrorCode.cs | 7 ++ .../CreateGeometricDataInput.cs | 22 ++++ .../CreateGeometricDataPayload.cs | 21 ++++ .../GeometricDataByIdDataLoader.cs | 24 +++++ .../GeometricDataX/GeometricDataFilterType.cs | 18 ++++ .../GeometricDataX/GeometricDataMutations.cs | 100 ++++++++++++++++++ .../GeometricDataX/GeometricDataPayload.cs | 53 ++++++++++ .../GeometricDataX/GeometricDataQueries.cs | 41 +++++++ .../GeometricDataX/GeometricDataResolvers.cs | 32 ++++++ .../GeometricDataX/GeometricDataType.cs | 28 +++++ .../GetHttpsResourceMutations.cs | 1 + .../GetHttpsResourceResolvers.cs | 7 ++ .../GetHttpsResources/GetHttpsResourceType.cs | 6 ++ 23 files changed, 463 insertions(+), 7 deletions(-) create mode 100644 backend/src/Data/GeometricData.cs create mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs create mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs create mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs create mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataType.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 49c65898..32949270 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -15,6 +15,7 @@ using Database.GraphQl.Publications; using Database.GraphQl.References; using Database.GraphQl.Standards; +using Database.GraphQl.GeometricDataX; using Database.GraphQl.Users; using HotChocolate.Data; using HotChocolate.Data.Filters; @@ -136,6 +137,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(Mutation))) .AddType() @@ -144,6 +146,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types @@ -159,6 +162,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() .AddType() // Data Loaders /* .AddDataLoader() */ @@ -209,6 +213,7 @@ protected override void Configure(IFilterConventionDescriptor descriptor) descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); } diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index ba67d2f7..7cf7da9d 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -37,6 +37,7 @@ DbContextOptions options public DbSet PhotovoltaicData { get; private set; } = default!; public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; + public DbSet GeometricData { get; private set; } = default!; private static void CreateEnumerations(ModelBuilder builder, string schemaName) { @@ -115,5 +116,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() ) .ToTable("user"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("geometric_data"); } } \ No newline at end of file diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs new file mode 100644 index 00000000..838745dd --- /dev/null +++ b/backend/src/Data/GeometricData.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Database.Data; + +public sealed class GeometricData + : DataX +{ + public GeometricData( + string id, + Guid uuid, + DateTime timestamp, + string locale, + Guid databaseId, + Guid componentId, + string? name, + string? description, + IReadOnlyList warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod, + IReadOnlyList resources, + GetHttpsResourceTree resourceTree, + IReadOnlyList approvals, + ResponseApproval approval, + IReadOnlyList thicknesses + ) : base ( + id, + uuid, + timestamp, + locale, + databaseId, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + resources, + resourceTree, + approvals, + approval + ) + { + Thicknesses = thicknesses; + + } + + [InverseProperty(nameof(GetHttpsResource.GeometricData))] + public override ICollection Resources { get; } = new List(); + public IReadOnlyList Thicknesses { get; private set;} + +} \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 0d1d934a..3aef1f12 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -16,6 +16,7 @@ public GetHttpsResource( Guid? hygrothermalDataId, Guid? opticalDataId, Guid? photovoltaicDataId, + Guid? geometricDataId, Guid? parentId, ICollection archivedFilesMetaInformation, ToTreeVertexAppliedConversionMethod? appliedConversionMethod @@ -33,6 +34,7 @@ public GetHttpsResource( HygrothermalDataId = hygrothermalDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; + GeometricDataId = geometricDataId; } public GetHttpsResource( @@ -63,6 +65,7 @@ public GetHttpsResource( Guid? hygrothermalDataId, Guid? opticalDataId, Guid? photovoltaicDataId, + Guid? geometricDataId, Guid? parentId ) : this( @@ -76,6 +79,7 @@ public GetHttpsResource( HygrothermalDataId = hygrothermalDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; + GeometricDataId = geometricDataId; } public GetHttpsResource( @@ -100,9 +104,9 @@ public GetHttpsResource( // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! [NotMapped] - public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; + public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? CalorimetricDataId ?? Guid.Empty; - [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? PhotovoltaicData as IData; + [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? CalorimetricDataId ?? PhotovoltaicData as IData; public Guid? CalorimetricDataId { get; private set; } @@ -125,6 +129,9 @@ public GetHttpsResource( public PhotovoltaicData? PhotovoltaicData { get; set; } + [InverseProperty(nameof(Database.Data.GeometricData.Resources))] + public GeometricData? GeometricData { get; set; } + public Guid? ParentId { get; private set; } // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. diff --git a/backend/src/Enumerations/DataKind.cs b/backend/src/Enumerations/DataKind.cs index f64d6884..2dfe834d 100644 --- a/backend/src/Enumerations/DataKind.cs +++ b/backend/src/Enumerations/DataKind.cs @@ -8,5 +8,6 @@ public enum DataKind CALORIMETRIC_DATA, HYGROTHERMAL_DATA, OPTICAL_DATA, - PHOTOVOLTAIC_DATA + PHOTOVOLTAIC_DATA, + GEOMETRIC_DATA } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index c8780edf..3de9817a 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -8,6 +8,7 @@ using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; +using Database.GraphQl.GeometricDataX; using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; @@ -38,7 +39,8 @@ ApplicationDbContext context return context.CalorimetricData.AsQueryable().AsEnumerable() .Concat(context.HygrothermalData.AsQueryable().AsEnumerable()) .Concat(context.OpticalData.AsQueryable().AsEnumerable()) - .Concat(context.PhotovoltaicData.AsQueryable().AsEnumerable()); + .Concat(context.PhotovoltaicData.AsQueryable().AsEnumerable()) + .Concat(context.GeometricData.AsQueryable().AsEnumerable()); } public async Task GetDataAsync( @@ -49,6 +51,7 @@ ApplicationDbContext context HygrothermalDataByIdDataLoader hygrothermalDataById, OpticalDataByIdDataLoader opticalDataById, PhotovoltaicDataByIdDataLoader photovoltaicDataById, + GeometricDataByIdDataLoader geometricDataById, CancellationToken cancellationToken ) { @@ -66,6 +69,10 @@ await opticalDataById.LoadAsync( id, cancellationToken ).ConfigureAwait(false) ?? + await geometricDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? await photovoltaicDataById.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 2e51f695..9c6714c5 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -23,7 +23,7 @@ IDbContextFactory dbContextFactory dbContext.GetHttpsResources.AsQueryable().Where(x => x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? - x.PhotovoltaicDataId ?? Guid.Empty) + x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), x => x.DataId ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 7d7e22da..6e2c575a 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -23,7 +23,7 @@ IDbContextFactory dbContextFactory dbContext.GetHttpsResources.AsQueryable().Where(x => x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? - x.PhotovoltaicDataId ?? Guid.Empty) + x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), x => x.DataId ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index d7c9dbc4..833f7c4d 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -22,7 +22,7 @@ IDbContextFactory dbContextFactory (dbContext, ids) => dbContext.GetHttpsResources.AsQueryable().Where(x => ids.Contains(x.CalorimetricDataId ?? - x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? Guid.Empty) + x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), x => x.DataId ) diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs new file mode 100644 index 00000000..83c2712e --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class CreateGeometricDataError + : UserErrorBase +{ + public CreateGeometricDataError( + CreateGeometricDataErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs new file mode 100644 index 00000000..fb4e0deb --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -0,0 +1,7 @@ +namespace Database.GraphQl.GeometricDataX; + +public enum CreateGeometricDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs new file mode 100644 index 00000000..bf16fc45 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.GeometricDataX; + +public sealed record CreateGeometricDataInput( + string AccessToken, + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + IReadOnlyList Approvals, + ResponseApproval Approval, + RootGetHttpsResourceInput RootResource, + double[] Thicknesses +); \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs new file mode 100644 index 00000000..c4047182 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs @@ -0,0 +1,21 @@ +using Database.Data; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class CreateGeometricDataPayload + : GeometricDataPayload +{ + public CreateGeometricDataPayload( + GeometricData geometricData + ) + : base(geometricData) + { + } + + public CreateGeometricDataPayload( + CreateGeometricDataError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs new file mode 100644 index 00000000..8fb62d84 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs @@ -0,0 +1,24 @@ +using Database.Data; +using Database.GraphQl.Entities; +using GreenDonut; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataByIdDataLoader + : EntityByIdDataLoader +{ + public GeometricDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : base( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.GeometricData + ) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs new file mode 100644 index 00000000..fb973368 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -0,0 +1,18 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataFilterType + : DataFilterTypeBase +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + descriptor.Field(x => x.Thicknesses); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs new file mode 100644 index 00000000..8f90f07c --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -0,0 +1,100 @@ +using System.Linq; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Extensions; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.GeometricDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class GeometricDataMutations +{ + public async Task CreateGeometricDataAsync( + CreateGeometricDataInput input, + ApplicationDbContext context, + [Service] AppSettings appSettings, + CancellationToken cancellationToken + ) + { + if (input.AccessToken != appSettings.AccessToken) + return new CreateGeometricDataPayload( + new CreateGeometricDataError( + CreateGeometricDataErrorCode.UNAUTHORIZED, + $"The access token {input.AccessToken} is invalid.", + new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + ) + ); + var geometricData = new GeometricData( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + new AppliedMethod( + input.AppliedMethod.MethodId, + input.AppliedMethod.Arguments + .Select(a => new NamedMethodArgument( + a.Name, + JsonDocument.Parse(@"""TODO""") + )) + .ToList(), + input.AppliedMethod.Sources + .Select(s => new NamedMethodSource( + s.Name, + new CrossDatabaseDataReference( + s.Value.DataId, + s.Value.DataTimestamp, + s.Value.DataKind, + s.Value.DatabaseId + ) + )) + .ToList() + ), + input.Approvals.Select(a => + new DataApproval( + a.Timestamp, + a.Signature, + a.KeyFingerprint, + a.Query, + a.Response, + a.ApproverId + ) + ).ToList(), + approval: input.Approval, + input.Thicknesses + ); + var resource = new GetHttpsResource( + input.RootResource.Description, + input.RootResource.HashValue, + input.RootResource.DataFormatId, + null, + input.RootResource.ArchivedFilesMetaInformation.Select(i => + new FileMetaInformation( + i.Path, + i.DataFormatId + ) + ).ToList(), + input.RootResource.AppliedConversionMethod is null + ? null + : new ToTreeVertexAppliedConversionMethod( + input.RootResource.AppliedConversionMethod.MethodId, + input.RootResource.AppliedConversionMethod.Arguments.Select(a => + new NamedMethodArgument( + a.Name, + JsonDocument.Parse(@"""TODO""") + ) + ).ToList(), + input.RootResource.AppliedConversionMethod.SourceName + ) + ); + geometricData.Resources.Add(resource); + context.GeometricData.Add(geometricData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateGeometricDataPayload(geometricData); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs new file mode 100644 index 00000000..3194b727 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.GeometricDataX; + +public abstract class GeometricDataPayload + : Payload + where TGeometricDataError : IUserError +{ + protected GeometricDataPayload( + GeometricData geometricData + ) + { + GeometricData = geometricData; + } + + protected GeometricDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected GeometricDataPayload( + TGeometricDataError error + ) + : this(new[] { error }) + { + } + + protected GeometricDataPayload( + GeometricData geometricData, + IReadOnlyCollection errors + ) + { + GeometricData = geometricData; + Errors = errors; + } + + protected GeometricDataPayload( + GeometricData geometricData, + TGeometricDataError error + ) + : this( + geometricData, + new[] { error } + ) + { + } + + public GeometricData? GeometricData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs new file mode 100644 index 00000000..5d74c38a --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Types; +using Guid = System.Guid; + +namespace Database.GraphQl.GeometricDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class GeometricDataQueries +{ + [UsePaging] + [UseFiltering] + [UseSorting] + public IQueryable GetAllGeometricData( + DateTime? timestamp, + [GraphQLType] string? locale, + ApplicationDbContext context + ) + { + return context.GeometricData; + } + + public Task GetGeometricDataAsync( + Guid id, + DateTime? timestamp, + [GraphQLType] string? locale, + GeometricDataByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync( + id, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs new file mode 100644 index 00000000..b1ead8f5 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataResolvers +{ + public Task GetGetHttpsResources( + [Parent] GeometricData geometricData, + GetHttpsResourcesByDataIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(geometricData.Id, cancellationToken); + } + + public GetHttpsResourceTree GetGetHttpsResourceTree( + [Parent] GeometricData geometricData + ) + { + return new GetHttpsResourceTree(geometricData); + } + + public DateTime GetTimestamp() + { + return DateTime.UtcNow; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs new file mode 100644 index 00000000..22ade938 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs @@ -0,0 +1,28 @@ +using Database.Data; +using HotChocolate.Types; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataType + : EntityType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field("resourceTree") + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field("timestamp") + .Type>() + .ResolveWith(t => t.GetTimestamp()); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index c3bc7039..75cf5a32 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -48,6 +48,7 @@ CancellationToken cancellationToken input.DataKind == DataKind.HYGROTHERMAL_DATA ? input.DataId : null, input.DataKind == DataKind.OPTICAL_DATA ? input.DataId : null, input.DataKind == DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, + input.DataKind == DataKind.GEOMETRIC_DATA ? input.DataId : null, input.ParentId, input.ArchivedFilesMetaInformation.Select(i => new FileMetaInformation( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 31bdf065..5b9b10d3 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -5,6 +5,7 @@ using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; +using Database.GraphQl.GeometricDataX; using Database.GraphQl.PhotovoltaicDataX; using HotChocolate; @@ -18,6 +19,7 @@ public sealed class GetHttpsResourceResolvers HygrothermalDataByIdDataLoader hygrothermalDataById, OpticalDataByIdDataLoader opticalDataById, PhotovoltaicDataByIdDataLoader photovoltaicDataById, + GeometricDataByIdDataLoader geometricDataById, CancellationToken cancellationToken ) { @@ -41,6 +43,11 @@ CancellationToken cancellationToken getHttpsResource.PhotovoltaicDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + if (getHttpsResource.GeometricDataId is not null) + return await geometricDataById.LoadAsync( + getHttpsResource.GeometricDataId ?? throw new ArgumentException("Impossible!"), + cancellationToken + ).ConfigureAwait(false); return null; } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index c18a3658..b36c1a1f 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -50,6 +50,12 @@ IObjectTypeDescriptor descriptor descriptor .Field(x => x.PhotovoltaicData) .Ignore(); + descriptor + .Field(x => x.GeometricDataId) + .Ignore(); + descriptor + .Field(x => x.GeometricData) + .Ignore(); descriptor .Field(x => x.Data) .ResolveWith(t => From 8edf87bdf091785b3a4b6779e7c19992319974c7 Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Tue, 17 Sep 2024 10:17:37 +0200 Subject: [PATCH 179/878] Add geometric data frontend --- backend/src/Data/GeometricData.cs | 48 ++-- backend/src/Data/GetHttpsResource.cs | 6 +- .../CreateGeometricDataInput.cs | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 2 +- .../GetHttpsResources/GetHttpsResourceType.cs | 2 +- frontend/components/Layout.tsx | 4 + .../data/geometric/GeometricData.tsx | 70 +++++ frontend/pages/data/geometric/[uuid].tsx | 20 ++ frontend/pages/data/geometric/index.tsx | 269 ++++++++++++++++++ frontend/pages/index.tsx | 1 + frontend/paths.ts | 4 + frontend/queries/data.graphql | 19 ++ frontend/type-defs.graphqls | 105 +++++++ 13 files changed, 528 insertions(+), 24 deletions(-) create mode 100644 frontend/components/data/geometric/GeometricData.tsx create mode 100644 frontend/pages/data/geometric/[uuid].tsx create mode 100644 frontend/pages/data/geometric/index.tsx diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 838745dd..c7587bdc 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -8,29 +8,19 @@ public sealed class GeometricData : DataX { public GeometricData( - string id, - Guid uuid, - DateTime timestamp, string locale, - Guid databaseId, Guid componentId, string? name, string? description, - IReadOnlyList warnings, + string[] warnings, Guid creatorId, DateTime createdAt, AppliedMethod appliedMethod, - IReadOnlyList resources, - GetHttpsResourceTree resourceTree, - IReadOnlyList approvals, - ResponseApproval approval, - IReadOnlyList thicknesses + ICollection approvals, + // ResponseApproval approval, + double[] thicknesses ) : base ( - id, - uuid, - timestamp, locale, - databaseId, componentId, name, description, @@ -38,18 +28,38 @@ IReadOnlyList thicknesses creatorId, createdAt, appliedMethod, - resources, - resourceTree, - approvals, - approval + approvals + // approval ) { Thicknesses = thicknesses; } + public GeometricData( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + // ResponseApproval approval, + double[] thicknesses + ) : base( + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + Thicknesses = thicknesses; + } [InverseProperty(nameof(GetHttpsResource.GeometricData))] public override ICollection Resources { get; } = new List(); - public IReadOnlyList Thicknesses { get; private set;} + public double[] Thicknesses { get; private set;} } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 3aef1f12..a7bf1545 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Database.Extensions; +using Database.GraphQl.GeometricDataX; namespace Database.Data; @@ -104,9 +105,9 @@ public GetHttpsResource( // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! [NotMapped] - public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? CalorimetricDataId ?? Guid.Empty; + public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; - [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? CalorimetricDataId ?? PhotovoltaicData as IData; + [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; public Guid? CalorimetricDataId { get; private set; } @@ -128,6 +129,7 @@ public GetHttpsResource( [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] public PhotovoltaicData? PhotovoltaicData { get; set; } + public Guid? GeometricDataId { get; private set; } [InverseProperty(nameof(Database.Data.GeometricData.Resources))] public GeometricData? GeometricData { get; set; } diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index bf16fc45..29ebd12c 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -16,7 +16,7 @@ public sealed record CreateGeometricDataInput( Guid CreatorId, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, - ResponseApproval Approval, + // ResponseApproval Approval, RootGetHttpsResourceInput RootResource, double[] Thicknesses ); \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 8f90f07c..276ec449 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -65,7 +65,7 @@ CancellationToken cancellationToken a.ApproverId ) ).ToList(), - approval: input.Approval, + // approval: input.Approval, input.Thicknesses ); var resource = new GetHttpsResource( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index b36c1a1f..c719e5df 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -59,6 +59,6 @@ IObjectTypeDescriptor descriptor descriptor .Field(x => x.Data) .ResolveWith(t => - t.GetData(default!, default!, default!, default!, default!, default!)); + t.GetData(default!, default!, default!, default!, default!, default!, default!)); } } \ No newline at end of file diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 213be874..dfffb38d 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -35,6 +35,10 @@ const navItems = [ path: paths.database, label: "Database", }, + { + path: paths.geometricData, + label: "Geometric Data", + }, ]; export type LayoutProps = { diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx new file mode 100644 index 00000000..b9e4b9e9 --- /dev/null +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -0,0 +1,70 @@ +import { Scalars } from "../../../__generated__/__types__"; +import { useGeometricDataQuery } from "../../../queries/data.graphql"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import { useEffect } from "react"; +import Link from "next/link"; +import paths from "../../../paths"; +import { messageApolloError } from "../../../lib/apollo"; + +export type GeometricDataProps = { + geometricDataId: Scalars["Uuid"]; +}; + +export default function GeometricData({ geometricDataId }: GeometricDataProps) { + const { loading, error, data } = useGeometricDataQuery({ + variables: { + uuid: geometricDataId, + }, + }); + + const geometricData = data?.geometricData; + + + useEffect(() => { + if (error) { + messageApolloError(error); + } + }, [error]); + + if (loading) { + return ; + } + + if (!geometricData) { + return ( + + ); + } + + return ( + + + {geometricData.uuid} + + + {geometricData.componentId} + + + + + {geometricData.creatorId} + + + + + ); +} \ No newline at end of file diff --git a/frontend/pages/data/geometric/[uuid].tsx b/frontend/pages/data/geometric/[uuid].tsx new file mode 100644 index 00000000..cd86778f --- /dev/null +++ b/frontend/pages/data/geometric/[uuid].tsx @@ -0,0 +1,20 @@ +import { useRouter } from "next/router"; +import GeometricData from "../../../components/data/geometric/GeometricData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} +export default Page; \ No newline at end of file diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx new file mode 100644 index 00000000..eefd53d8 --- /dev/null +++ b/frontend/pages/data/geometric/index.tsx @@ -0,0 +1,269 @@ +import Layout from "../../../components/Layout"; +import { + Table, + message, + Form, + Button, + Alert, + Typography, + Descriptions, +} from "antd"; +import { useAllGeometricDataQuery } from "../../../queries/data.graphql"; +import { + GeometricData, + Scalars, + GeometricDataPropositionInput, +} from "../../../__generated__/__types__"; +import { useState } from "react"; +import { setMapValue } from "../../../lib/freeTextFilter"; +import { + getAppliedMethodColumnProps, + getComponentUuidColumnProps, + getDescriptionColumnProps, + getNameColumnProps, + getResourceTreeColumnProps, + getTimestampColumnProps, + getUuidColumnProps, +} from "../../../lib/table"; +import { + FloatPropositionComparator, + FloatPropositionFormList, +} from "../../../components/FloatPropositionFormList"; +import { + UuidPropositionComparator, + UuidPropositionFormList, +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; + +const layout = { + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, +}; + +const tailLayout = { + wrapperCol: { offset: 8, span: 16 }, +}; + +enum Negator { + Is = "is", + IsNot = "isNot", +} + +const negateIfNecessary = ( + negator: Negator, + proposition: GeometricDataPropositionInput +): GeometricDataPropositionInput => { + switch (negator) { + case Negator.Is: + return proposition; + case Negator.IsNot: + return proposition; + } +}; + +const conjunct = ( + propositions: GeometricDataPropositionInput[] +): GeometricDataPropositionInput => { + if (propositions.length == 0) { + return {}; + } + if (propositions.length == 1) { + return propositions[0]; + } + return { and: propositions }; +}; + +function Page() { + const [form] = Form.useForm(); + const [filtering, setFiltering] = useState(false); + const [globalErrorMessages, setGlobalErrorMessages] = useState( + new Array() + ); + const [messageApi, contextHolder] = message.useMessage(); + const [data, setData] = useState([]); + + const allGeometricDataQuery = useAllGeometricDataQuery({ + skip: true, + errorPolicy: "all", + }); + + const [filterText, setFilterText] = useState(() => new Map()); + const onFilterTextChange = setMapValue(filterText, setFilterText); + + const onFinish = ({ + componentIds, + dataFormatIds, + thicknesses, + }: { + componentIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"] | undefined; + }[] + | undefined; + dataFormatIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"] | undefined; + }[] + | undefined; + thicknesses: + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; + }) => { + const filter = async () => { + try { + setFiltering(true); + const propositions: GeometricDataPropositionInput[] = []; + if (componentIds) { + for (let { negator, comparator, value } of componentIds) { + propositions.push( + negateIfNecessary(negator, { + componentId: { [comparator]: value }, + }) + ); + } + } + if (dataFormatIds) { + for (let { negator, comparator, value } of dataFormatIds) { + propositions.push( + negateIfNecessary(negator, { + resources: { + some: { + dataFormatId: { [comparator]: value }, + }, + }, + }) + ); + } + } + if (thicknesses) { + for (let { negator, comparator, value } of thicknesses) { + if (value !== undefined && value !== null) { + propositions.push( + negateIfNecessary(negator, { + thicknesses: { + some: { + [comparator]: value, + }, + }, + }) + ); + } + } + } + const { error, data } = await allGeometricDataQuery.refetch( + propositions.length == 0 + ? {} + : { + where: conjunct(propositions), + } + ); + if (error) { + console.log(error); + messageApi.error(error.graphQLErrors.map((error) => error.message)); + } + setData((data?.allGeometricData?.nodes || []) as GeometricData[]); + } catch (error) { + console.log("Failed:", error); + } finally { + setFiltering(false); + } + }; + filter(); + }; + const onFinishFailed = () => { + setGlobalErrorMessages(["Fix the errors below."]); + }; + + return ( + + {contextHolder} + Geometric Data + {globalErrorMessages.length > 0 && ( + + )} + +
+ + + + + + + + + ( + onFilterTextChange, + (x) => filterText.get(x), + paths.geometricDatum + ), + }, + { + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => + filterText.get(x) + ), + }, + { + ...getDescriptionColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x) + ), + }, + { + ...getTimestampColumnProps<(typeof data)[0]>(), + }, + { + ...getComponentUuidColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x) + ), + }, + { + ...getAppliedMethodColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x) + ), + }, + { + ...getResourceTreeColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x) + ), + }, + { + title: "Thicknesses", + key: "thicknesses", + render: (_text, record, _index) => ( + + + {record.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} + + + ), + }, + ]} + dataSource={data} + /> + + ); +} +export default Page; \ No newline at end of file diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index f0f6c5f9..d706c5be 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -46,6 +46,7 @@ function Page() { . You can use this website to search this product data server for{" "} optical data,{" "} + optical data,{" "} calorimetric data and{" "} photovoltaic data. If you would like to search the entire Product Data Network{" "} diff --git a/frontend/paths.ts b/frontend/paths.ts index 50c4af48..9fac4d6e 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -22,6 +22,10 @@ export default { photovoltaicDatum(uuid: string) { return `/data/photovoltaic/${encodeURIComponent(uuid)}`; }, + geometricData: "/data/geometric", + geometricDatum(uuid: string) { + return `/data/geometric/${encodeURIComponent(uuid)}`; + }, createData: "/data/create", uploadFile: "/upload-file", login: "/connect/login", diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 33b75bb1..01e8d2d8 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -186,3 +186,22 @@ query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { # query HasPhotovoltaicData($where: DataPropositionInput!) { # hasPhotovoltaicData(where: $where) # } + +fragment GeometricDataPartial on GeometricData { + ...DataPartial + thicknesses +} + +query GeometricData($uuid: Uuid!) { + geometricData(id: $uuid) { + ...GeometricDataPartial + } +} + +query AllGeometricData($where: GeometricDataPropositionInput) { + allGeometricData(where: $where) { + nodes { + ...GeometricDataPartial + } + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 86d60b81..58ad9acb 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -145,6 +145,18 @@ type CreatePhotovoltaicDataPayload { query: Query! } +type CreateGeometricDataError { + code: CreateGeometricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateGeometricDataPayload { + geometricData: GeometricData + errors: [CreateGeometricDataError!] + query: Query! +} + type CrossDatabaseDataReference { databaseId: Uuid! dataId: Uuid! @@ -182,6 +194,44 @@ type DataEdge { node: Data! } +type GeometricData implements Node & Data { + appliedMethod: AppliedMethod! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + id: ID! + locale: Locale! + name: String + resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + uuid: Uuid! + thicknesses: [Float!]! + warnings: [String!]! +} + +"A connection to a list of items." +type GeometricDataConnection { + "A list of edges." + edges: [GeometricDataEdge!] + "A flattened list of the nodes." + nodes: [GeometricData!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! +} + +"An edge in a connection." +type GeometricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GeometricData! +} + type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -293,6 +343,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! } type NamedMethodArgument { @@ -431,6 +482,7 @@ type Query { allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] timestamp: DateTime where: HygrothermalDataPropositionInput): HygrothermalDataConnection allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] timestamp: DateTime where: OpticalDataPropositionInput): OpticalDataConnection allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] timestamp: DateTime where: GeometricDataPropositionInput): GeometricDataConnection calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData currentUser: User currentUserInfo: UserInfo @@ -446,6 +498,7 @@ type Query { opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData verificationCode: String! + geometricData(id: Uuid! locale: Locale timestamp: DateTime): GeometricData } "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." @@ -565,6 +618,23 @@ input CielabColorPropositionInput { bStar: FloatPropositionInput } +input GeometricDataPropositionInput { + and: [GeometricDataPropositionInput!] + or: [GeometricDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput + thicknesses: FloatsPropositionInput +} + input CreateCalorimetricDataInput { accessToken: String! appliedMethod: AppliedMethodInput! @@ -581,6 +651,33 @@ input CreateCalorimetricDataInput { warnings: [String!]! } +input GeometricDataSortInput { + locale: SortEnumType + componentId: SortEnumType + name: SortEnumType + description: SortEnumType + creatorId: SortEnumType + createdAt: SortEnumType + appliedMethod: AppliedMethodSortInput + id: SortEnumType + version: SortEnumType +} + +input CreateGeometricDataInput { + accessToken: String! + appliedMethod: AppliedMethodInput! + approvals: [DataApprovalInput!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: String! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! + thicknesses: [Float!]! +} + input CreateGetHttpsResourceInput { accessToken: String! appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput @@ -809,6 +906,8 @@ input GetHttpsResourceSortInput { opticalData: OpticalDataSortInput photovoltaicDataId: SortEnumType photovoltaicData: PhotovoltaicDataSortInput + geometricDataId: SortEnumType + geometricData: GeometricDataSortInput parentId: SortEnumType appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput parent: GetHttpsResourceSortInput @@ -1226,11 +1325,17 @@ enum CreatePhotovoltaicDataErrorCode { UNAUTHORIZED } +enum CreateGeometricDataErrorCode { + UNKNOWN + UNAUTHORIZED +} + enum DataKind { CALORIMETRIC_DATA HYGROTHERMAL_DATA OPTICAL_DATA PHOTOVOLTAIC_DATA + GEOMETRIC_DATA } enum DatabaseVerificationState { From df1604905f42670424df7486544eac40f00fda62 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Sep 2024 16:04:52 +0200 Subject: [PATCH 180/878] Keep appending variables even if variables coming before fail to format, do not pad and truncate variables as all information may be needed for debugging, and do not format the value's string representation as it may contain the characters `{` and `}` which have a special meaning in format strings and would need to be escaped by doubling them `{{` and `}}` --- .../GraphQl/LoggingDiagnosticEventListener.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index f464a602..b043b30d 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -202,30 +202,23 @@ public void Dispose() { stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Variables {Environment.NewLine}"); - try + foreach (var variableValue in _context.Variables!) { - foreach (var variableValue in _context.Variables!) + try { - static string PadRightHelper(string existingString, int lengthToPadTo) - { - if (string.IsNullOrEmpty(existingString)) return "".PadRight(lengthToPadTo); - - if (existingString.Length > lengthToPadTo) return existingString[..lengthToPadTo]; - - return existingString + " ".PadRight(lengthToPadTo - existingString.Length); - } - stringBuilder.AppendFormat( CultureInfo.InvariantCulture, - $" {PadRightHelper(variableValue.Name, 20)} : {PadRightHelper(variableValue.Value.ToString(), 20)}: {variableValue.Type}"); + $" {variableValue.Name} : "); + stringBuilder.Append(variableValue.Value.ToString()); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" : {variableValue.Type}"); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + catch (Exception exception) + { + // all input type records will land here. + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" Formatting the variable '{variableValue.Name}' failed with the exception '{exception}'."); stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); } - } - catch - { - // all input type records will land here. - stringBuilder.Append(" Formatting Variables Error. Continuing..."); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); } } } From a020883c68c1a28b2322734db7e093e46101d33d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Sep 2024 16:10:18 +0200 Subject: [PATCH 181/878] Install `procps` to make the command `ps` available that is needed by Visual Studio Code to attach to a process for debugging --- see https://code.visualstudio.com/docs/csharp/debugging#_attaching-to-a-process --- backend/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 95cb4add..12547edb 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -48,7 +48,8 @@ RUN \ lldb \ make \ # npm \ - openssl && \ + openssl \ + procps && \ rm \ --recursive \ --force \ From 1e34670920eda79538d27e9d33c269a9eeaab178 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Sep 2024 16:56:39 +0200 Subject: [PATCH 182/878] Fix fetching the database meta information from the metabase (essentially the setter of the `Databases` field of the type `DatabaseData` was missing) --- backend/src/GraphQl/Databases/DatabaseEdge.cs | 13 ++++++++++++ .../src/GraphQl/Databases/DatabaseQueries.cs | 15 ++++++-------- .../GraphQl/Databases/DatabasesConnection.cs | 10 +++------- .../src/Metabase/Queries/Databases.graphql | 20 ++++++++++--------- backend/src/Metabase/QueryingMetabase.cs | 1 + 5 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 backend/src/GraphQl/Databases/DatabaseEdge.cs diff --git a/backend/src/GraphQl/Databases/DatabaseEdge.cs b/backend/src/GraphQl/Databases/DatabaseEdge.cs new file mode 100644 index 00000000..f4183ab9 --- /dev/null +++ b/backend/src/GraphQl/Databases/DatabaseEdge.cs @@ -0,0 +1,13 @@ +namespace Database.GraphQl.Databases; + +public sealed class DatabaseEdge +{ + public DatabaseEdge( + Database node + ) + { + Node = node; + } + + public Database Node { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 22b28279..37b9a4ad 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -64,15 +64,15 @@ await QueryingMetabase.ConstructGraphQlQuery( .SetMessage("Response is null.") .Build() ); - if (response.Data.Databases.Nodes is null) + if (response.Data.Databases.Edges is null) throw new QueryException( ErrorBuilder.New() - .SetCode("NULL_NODES") + .SetCode("NULL_EDGES") .SetPath(resolverContext.Path) .SetMessage("The supposed list of databases is null.") .Build() ); - if (response.Data.Databases.Nodes.Count == 0) + if (response.Data.Databases.Edges.Count == 0) throw new QueryException( ErrorBuilder.New() .SetCode("NO_DATABASE") @@ -80,7 +80,7 @@ await QueryingMetabase.ConstructGraphQlQuery( .SetMessage("The list of databases is empty.") .Build() ); - if (response.Data.Databases.Nodes.Count >= 2) + if (response.Data.Databases.Edges.Count >= 2) throw new QueryException( ErrorBuilder.New() .SetCode("AMBIGUOUS_DATABASE") @@ -88,7 +88,7 @@ await QueryingMetabase.ConstructGraphQlQuery( .SetMessage("The list of databases has more than one entry.") .Build() ); - return response.Data.Databases.Nodes[0]; + return response.Data.Databases.Edges[0].Node; } catch (HttpRequestException e) { @@ -116,8 +116,5 @@ await QueryingMetabase.ConstructGraphQlQuery( } } - private sealed class DatabasesData - { - public DatabasesConnection Databases { get; } = default!; - } + private sealed record DatabasesData(DatabasesConnection Databases); } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasesConnection.cs b/backend/src/GraphQl/Databases/DatabasesConnection.cs index 3e487b91..9b3e3d47 100644 --- a/backend/src/GraphQl/Databases/DatabasesConnection.cs +++ b/backend/src/GraphQl/Databases/DatabasesConnection.cs @@ -4,16 +4,12 @@ namespace Database.GraphQl.Databases; public sealed class DatabasesConnection { - // pageInfo: PageInfo! - // totalCount: Int! - public DatabasesConnection( - IReadOnlyList? nodes + IReadOnlyList? edges ) { - Nodes = nodes; + Edges = edges; } - // edges: [DatabasesEdge!] - public IReadOnlyList? Nodes { get; } + public IReadOnlyList? Edges { get; } } \ No newline at end of file diff --git a/backend/src/Metabase/Queries/Databases.graphql b/backend/src/Metabase/Queries/Databases.graphql index 32a9c3c7..bf21e0c0 100644 --- a/backend/src/Metabase/Queries/Databases.graphql +++ b/backend/src/Metabase/Queries/Databases.graphql @@ -2,15 +2,17 @@ query Databases( $where: DatabaseFilterInput ) { databases(where: $where) { - nodes { - uuid - name - description - locator - verificationState - verificationCode - canCurrentUserUpdateNode - canCurrentUserVerifyNode + edges { + node { + uuid + name + description + locator + verificationState + verificationCode + canCurrentUserUpdateNode + canCurrentUserVerifyNode + } } } } \ No newline at end of file diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs index 34f5e9ed..e024567c 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -161,6 +161,7 @@ CancellationToken cancellationToken await httpResponseMessage.Content .ReadAsStreamAsync(cancellationToken) .ConfigureAwait(false); + // For debugging, the following lines of code write the response to standard output. // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); var deserializedResponse = await JsonSerializer.DeserializeAsync( From 73d86781744a4ad67bb87a857062a11b5cb927c5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Sep 2024 18:40:34 +0200 Subject: [PATCH 183/878] Upgrade C# dependencies --- backend/dotnet-tools.json | 20 ++++++++-------- backend/src/Database.csproj | 38 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 14 +++++------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 118653a9..3995b59c 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,28 +3,28 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "8.0.2", + "version": "8.0.5", "commands": [ "dotnet-aspnet-codegenerator" ], "rollForward": false }, "dotnet-counters": { - "version": "8.0.510501", + "version": "8.0.532401", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "8.0.510501", + "version": "8.0.532401", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "8.0.6", + "version": "8.0.8", "commands": [ "dotnet-ef" ], @@ -38,35 +38,35 @@ "rollForward": false }, "dotnet-trace": { - "version": "8.0.510501", + "version": "8.0.532401", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "8.0.510501", + "version": "8.0.532401", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "8.0.510501", + "version": "8.0.532401", "commands": [ "dotnet-sos" ], "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.8.9", + "version": "0.9.0", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.1.4", + "version": "2024.2.4", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "13.9.6", + "version": "13.9.12", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 2fd20fd9..b0c30155 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -23,34 +23,34 @@ - - - - - - - - - - - - + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - - - + + + + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index d038d28e..c5cec3b8 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,14 +16,14 @@ - + - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive From dbb585d5677dbec215e0ceb0991195fa4916d7a1 Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Tue, 24 Sep 2024 14:49:00 +0200 Subject: [PATCH 184/878] merge changes --- .../ApplicationDbContextModelSnapshot.cs | 321 +++++++++++++++++- 1 file changed, 320 insertions(+), 1 deletion(-) diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 2e83f4d1..70ba3351 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -23,7 +23,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data", "geometric_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -77,6 +77,51 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("calorimetric_data", "database"); }); + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + modelBuilder.Entity("Database.Data.GetHttpsResource", b => { b.Property("Id") @@ -94,6 +139,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); + b.Property("GeometricDataId") + .HasColumnType("uuid"); + b.Property("HashValue") .IsRequired() .HasColumnType("text"); @@ -120,6 +168,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CalorimetricDataId"); + b.HasIndex("GeometricDataId"); + b.HasIndex("HygrothermalDataId"); b.HasIndex("OpticalDataId"); @@ -789,12 +839,274 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Approvals"); }); + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + modelBuilder.Entity("Database.Data.GetHttpsResource", b => { b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") .WithMany("Resources") .HasForeignKey("CalorimetricDataId"); + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") .WithMany("Resources") .HasForeignKey("HygrothermalDataId"); @@ -892,6 +1204,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("CalorimetricData"); + b.Navigation("GeometricData"); + b.Navigation("HygrothermalData"); b.Navigation("OpticalData"); @@ -1734,6 +2048,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Resources"); }); + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + modelBuilder.Entity("Database.Data.GetHttpsResource", b => { b.Navigation("Children"); From 0ef3e7477bae8744c880848090e555836e5dbf45 Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Tue, 24 Sep 2024 15:24:54 +0200 Subject: [PATCH 185/878] migration --- ...0240917073732_AddGeometricData.Designer.cs | 2093 +++++++++++++++++ .../20240917073732_AddGeometricData.cs | 206 ++ 2 files changed, 2299 insertions(+) create mode 100644 backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs create mode 100644 backend/src/Migrations/20240917073732_AddGeometricData.cs diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs b/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs new file mode 100644 index 00000000..d5c7c75e --- /dev/null +++ b/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs @@ -0,0 +1,2093 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240917073732_AddGeometricData")] + partial class AddGeometricData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data", "geometric_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.Property("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.Property("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.Property("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Standardizers") + .IsRequired() + .HasColumnType("standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs new file mode 100644 index 00000000..ecd61803 --- /dev/null +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -0,0 +1,206 @@ +using System; +using System.Text.Json; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddGeometricData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AddColumn( + name: "GeometricDataId", + schema: "database", + table: "get_https_resource", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "geometric_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + Thicknesses = table.Column(type: "double precision[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Approvals", + schema: "database", + columns: table => new + { + GeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ApproverId = table.Column(type: "uuid", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + Publication_Authors = table.Column(type: "text[]", nullable: true), + Publication_Doi = table.Column(type: "text", nullable: true), + Publication_ArXiv = table.Column(type: "text", nullable: true), + Publication_Urn = table.Column(type: "text", nullable: true), + Publication_WebAddress = table.Column(type: "text", nullable: true), + Publication_Title = table.Column(type: "text", nullable: true), + Publication_Abstract = table.Column(type: "text", nullable: true), + Publication_Section = table.Column(type: "text", nullable: true), + Standard_Year = table.Column(type: "integer", nullable: true), + Standard_Numeration_Prefix = table.Column(type: "text", nullable: true), + Standard_Numeration_MainNumber = table.Column(type: "text", nullable: true), + Standard_Numeration_Suffix = table.Column(type: "text", nullable: true), + Standard_Standardizers = table.Column(type: "standardizer[]", nullable: true), + Standard_Locator = table.Column(type: "text", nullable: true), + Standard_Title = table.Column(type: "text", nullable: true), + Standard_Abstract = table.Column(type: "text", nullable: true), + Standard_Section = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Approvals", x => new { x.GeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Approvals_geometric_data_GeometricDataId", + column: x => x.GeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Arguments", x => new { x.AppliedMethodGeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Arguments_geometric_data_AppliedMethodGeomet~", + column: x => x.AppliedMethodGeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp with time zone", nullable: false), + Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Sources", x => new { x.AppliedMethodGeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~", + column: x => x.AppliedMethodGeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId", + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropTable( + name: "geometric_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data", + schema: "database"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropColumn( + name: "GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + } + } +} From 05410fb169ce415ddaecadef71ca70b05491466d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Sep 2024 15:54:58 +0200 Subject: [PATCH 186/878] Make `data_kind` live in the schema `database` instead of `public` --- .../20240917073732_AddGeometricData.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index ecd61803..48c4e063 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -15,12 +15,8 @@ public partial class AddGeometricData : Migration protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") - .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") migrationBuilder.AddColumn( name: "GeometricDataId", @@ -195,12 +191,8 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "get_https_resource"); migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") - .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") } } } From efc6b9cea0ef169a9bb6c28514f07698456564a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Sep 2024 16:16:03 +0200 Subject: [PATCH 187/878] Generate SQL script for the next release --- .../20240917073732_AddGeometricData.cs | 4 +- ...ema_to_20240917073732_AddGeometricData.sql | 80 +++++++++++++++++++ backend/src/Migrations/new | 1 + 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql create mode 100644 backend/src/Migrations/new diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index 48c4e063..60b4cc1b 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -16,7 +16,7 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data"); migrationBuilder.AddColumn( name: "GeometricDataId", @@ -192,7 +192,7 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); } } } diff --git a/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql b/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql new file mode 100644 index 00000000..1a8ea3aa --- /dev/null +++ b/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql @@ -0,0 +1,80 @@ +START TRANSACTION; + +ALTER TYPE database.data_kind ADD VALUE 'geometric_data'; + +ALTER TABLE database.get_https_resource ADD "GeometricDataId" uuid; + +CREATE TABLE database.geometric_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Thicknesses" double precision[] NOT NULL, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text, + "Description" text, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_geometric_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database."geometric_data_Approvals" ( + "GeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "ApproverId" uuid NOT NULL, + "Timestamp" timestamp with time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "Publication_Authors" text[], + "Publication_Doi" text, + "Publication_ArXiv" text, + "Publication_Urn" text, + "Publication_WebAddress" text, + "Publication_Title" text, + "Publication_Abstract" text, + "Publication_Section" text, + "Standard_Year" integer, + "Standard_Numeration_Prefix" text, + "Standard_Numeration_MainNumber" text, + "Standard_Numeration_Suffix" text, + "Standard_Standardizers" standardizer[], + "Standard_Locator" text, + "Standard_Title" text, + "Standard_Abstract" text, + "Standard_Section" text, + CONSTRAINT "PK_geometric_data_Approvals" PRIMARY KEY ("GeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Approvals_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."geometric_data_Arguments" ( + "AppliedMethodGeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_geometric_data_Arguments" PRIMARY KEY ("AppliedMethodGeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Arguments_geometric_data_AppliedMethodGeomet~" FOREIGN KEY ("AppliedMethodGeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."geometric_data_Sources" ( + "AppliedMethodGeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp with time zone NOT NULL, + "Value_DataKind" data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_geometric_data_Sources" PRIMARY KEY ("AppliedMethodGeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~" FOREIGN KEY ("AppliedMethodGeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE INDEX "IX_get_https_resource_GeometricDataId" ON database.get_https_resource ("GeometricDataId"); + +ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240917073732_AddGeometricData', '8.0.8'); + +COMMIT; + diff --git a/backend/src/Migrations/new b/backend/src/Migrations/new new file mode 100644 index 00000000..3a8fbc5b --- /dev/null +++ b/backend/src/Migrations/new @@ -0,0 +1 @@ +20240917073732_AddGeometricData From d19e9b8a6994ab717be58313860dfdce4fa202bd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Sep 2024 16:25:06 +0200 Subject: [PATCH 188/878] Switch migrations --- backend/src/Migrations/current | 2 +- backend/src/Migrations/new | 1 - backend/src/Migrations/previous | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) delete mode 100644 backend/src/Migrations/new diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index b0f8604f..3a8fbc5b 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20240722133619_MoveEnumTypesToMetabaseSchema +20240917073732_AddGeometricData diff --git a/backend/src/Migrations/new b/backend/src/Migrations/new deleted file mode 100644 index 3a8fbc5b..00000000 --- a/backend/src/Migrations/new +++ /dev/null @@ -1 +0,0 @@ -20240917073732_AddGeometricData diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index 89cc0f5e..b0f8604f 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20230210135413_AddDataProtectionKeys +20240722133619_MoveEnumTypesToMetabaseSchema From 38778cdff6f6cd050caca845216c4d93a71cd88d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Sep 2024 16:28:25 +0200 Subject: [PATCH 189/878] Reference custom enumerations data kind and standardizer in schema `database` --- backend/src/Migrations/20240917073732_AddGeometricData.cs | 4 ++-- ...pesToMetabaseSchema_to_20240917073732_AddGeometricData.sql | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index 60b4cc1b..2a746686 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -73,7 +73,7 @@ protected override void Up(MigrationBuilder migrationBuilder) Standard_Numeration_Prefix = table.Column(type: "text", nullable: true), Standard_Numeration_MainNumber = table.Column(type: "text", nullable: true), Standard_Numeration_Suffix = table.Column(type: "text", nullable: true), - Standard_Standardizers = table.Column(type: "standardizer[]", nullable: true), + Standard_Standardizers = table.Column(type: "database.standardizer[]", nullable: true), Standard_Locator = table.Column(type: "text", nullable: true), Standard_Title = table.Column(type: "text", nullable: true), Standard_Abstract = table.Column(type: "text", nullable: true), @@ -125,7 +125,7 @@ protected override void Up(MigrationBuilder migrationBuilder) Name = table.Column(type: "text", nullable: false), Value_DataId = table.Column(type: "uuid", nullable: false), Value_DataTimestamp = table.Column(type: "timestamp with time zone", nullable: false), - Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DataKind = table.Column(type: "database.data_kind", nullable: false), Value_DatabaseId = table.Column(type: "uuid", nullable: false) }, constraints: table => diff --git a/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql b/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql index 1a8ea3aa..2186dc7f 100644 --- a/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql +++ b/backend/src/Migrations/migrate_from_20240722133619_MoveEnumTypesToMetabaseSchema_to_20240917073732_AddGeometricData.sql @@ -39,7 +39,7 @@ CREATE TABLE database."geometric_data_Approvals" ( "Standard_Numeration_Prefix" text, "Standard_Numeration_MainNumber" text, "Standard_Numeration_Suffix" text, - "Standard_Standardizers" standardizer[], + "Standard_Standardizers" database.standardizer[], "Standard_Locator" text, "Standard_Title" text, "Standard_Abstract" text, @@ -63,7 +63,7 @@ CREATE TABLE database."geometric_data_Sources" ( "Name" text NOT NULL, "Value_DataId" uuid NOT NULL, "Value_DataTimestamp" timestamp with time zone NOT NULL, - "Value_DataKind" data_kind NOT NULL, + "Value_DataKind" database.data_kind NOT NULL, "Value_DatabaseId" uuid NOT NULL, CONSTRAINT "PK_geometric_data_Sources" PRIMARY KEY ("AppliedMethodGeometricDataId", "Id"), CONSTRAINT "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~" FOREIGN KEY ("AppliedMethodGeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE From fd6b134c30e8eb7884469496649d2d771fa553c5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Sep 2024 12:43:47 +0200 Subject: [PATCH 190/878] Only show logs of the last 24 hours --- Makefile.production | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.production b/Makefile.production index 95f88dd3..e030ec27 100644 --- a/Makefile.production +++ b/Makefile.production @@ -54,6 +54,7 @@ restart : ## Restart all stopped and running containers logs : ## Follow logs ${docker_compose} logs \ + --since=24h \ --follow .PHONY : logs From f86e1017c5a39d564c6b961932cbfbde83745b6f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Sep 2024 12:44:12 +0200 Subject: [PATCH 191/878] Use buildkit for service deployment --- Makefile.production | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile.production b/Makefile.production index e030ec27..a9afd286 100644 --- a/Makefile.production +++ b/Makefile.production @@ -305,9 +305,13 @@ migrate : ## Migrate database (note that other PostgreSQL instances using the sa # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. deploy-services : ## Deploy services - ${docker_compose} pull - ${docker_compose} build \ - --pull + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull ${docker_compose} up \ --force-recreate \ --renew-anon-volumes \ From 34a55253a2aecd4087c2aa8584b3e3cd6eabf387 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Sep 2024 12:47:52 +0200 Subject: [PATCH 192/878] Align README with the one of the metabase --- README.md | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 181 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 21101c05..05651321 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,12 @@ If you have a question for which you don't find the answer in this repository, p [Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). 1. Clone the source code by running `git clone git@github.com:building-envelope-data/database.git` and navigate - into the new directory `database` by running `cd database`. + into the new directory `database` by running `cd ./database`. 1. Initialize, fetch, and checkout possibly-nested submodules by running `git submodule update --init --recursive`. An alternative would have been passing `--recurse-submodules` to `git clone` above. -1. Prepare your environment by running `cp .env.sample .env`, - `cp frontend/.env.local.sample frontend/.env.local`, and adding the line +1. Prepare your environment by running `cp ./.env.sample ./.env`, + `cp ./frontend/.env.local.sample ./frontend/.env.local`, and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and [GNU Make](https://www.gnu.org/software/make/). @@ -53,7 +53,10 @@ If you have a question for which you don't find the answer in this repository, p the GraphQL API navigate to `https://local.solarbuildingenvelopes.com:5051/graphql/`, and to see sent emails navigate to - `https://local.solarbuildingenvelopes.com:5051/email/`. + `https://local.solarbuildingenvelopes.com:5051/email/`. Note that the port is + `5051` by default. If you set the variable `HTTPS_PORT` within the `./.env` + to some other value though, you need to use that value instead within the + URL. In another shell @@ -67,6 +70,83 @@ In another shell The same works for frontend containers by running `make shellf`. +### Developing with Visual Studio Code + +On the very first usage: + +1. Install [Visual Studio Code](https://code.visualstudio.com) and open it. + Navigate to the Extensions pane (`Ctrl+Shift+X`). Add the extension + [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). +1. Navigate to the + [Remote Explorer](https://code.visualstudio.com/docs/devcontainers/containers#_managing-containers) + pane. Hover over the running `metabase-backend-*` container (if it is not + running, then run `make up` in a shell inside the project directory) and + click on the "Attach in Current Window" icon. In the Explorer pane, open the + directory `/app`, which is mounted to the host's `./backend` directory. + Navigate to the Extensions pane. Add the extensions + [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit), + [IntelliCode for C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.vscodeintellicode-csharp), + [GraphQL: Language Feature Support](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql), + and + [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens). +1. Navigate to the + [Remote Explorer](https://code.visualstudio.com/docs/devcontainers/containers#_managing-containers) + pane. Hover over the running `metabase-frontend-*` container and click on + the "Attach in New Window" icon. In the Explorer pane, open the directory + `/app`, which is mounted to the host's `./frontend` directory. Navigate to + the Extensions pane. Add the extensions + [GraphQL: Language Feature Support](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql), + and + [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens). + +Note that the Docker containers are configured in `./docker-compose.yml` in +such a way that Visual Studio Code extensions installed within containers are +retained in Docker volumes and thus remain installed across `make down` and +`make up` cycles. + +On subsequent usages: Open Visual Studio Code, navigate to the "Remote +Explorer" pane, and attach to the container(s) you want to work in. + +The following Visual Studio Code docs may be of interest for productivity and +debugging + +- [Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers) +- [Git](https://code.visualstudio.com/docs/sourcecontrol/overview) +- [C#](https://code.visualstudio.com/docs/csharp/navigate-edit) +- [TypeScript](https://code.visualstudio.com/docs/typescript/typescript-tutorial) + +#### Debugging + +To debug the +[ASP.NET Core web application](https://learn.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core), +attach Visual Studio Code to the `metabase-backend-*` container, +[press `Ctrl+Shift+P`, select "Debug: Attach to a .NET 5+ or .NET Core process"](https://code.visualstudio.com/docs/csharp/debugging#_attaching-to-a-process), +and choose the process `/app/src/bin/Debug/net8.0/Metabase run` titled +`Metabase` or alternatively navigate to the "Run and Debug" pane +(`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the +"Start Debugging" icon (`F5`), and select the same process as above. Then, for +example, open some source files to set breakpoints, navigate through the +website https://local.buildingenvelopedata.org:4041, which will stop at +breakpoints, and inspect the information provided by the debugger at the +breakpoints. For details on debugging C# in Visual Studio Code, see +[Debugging](https://code.visualstudio.com/docs/csharp/debugging). + +Note that the debugger detaches after the +[polling file watcher](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables) +restarts the process, which happens for example after editing a source file +because `dotnet watch` is configured in `./docker-compose.yml` with +`DOTNET_USE_POLLING_FILE_WATCHER` set to `true`. As of this writing, there is +an +[open feature request to reattach the debugger automatically](https://github.com/dotnet/vscode-csharp/issues/4822). +There also are multiple extensions like +[.NET Watch Attach](https://marketplace.visualstudio.com/items?itemName=Trottero.dotnetwatchattach) +and +[.NET Stalker Debugger](https://marketplace.visualstudio.com/items?itemName=spencerjames.stalker-debugger) +that attempt to solve that. Those extensions don't work in our case though, as +they try to restart `dotnet watch` themselves, instead of waiting for the +polling file watcher of `dotnet watch` to restart +`/app/src/bin/Debug/net8.0/Metabase run` and attach to that process. + ## Deployment For information on using Docker in production see @@ -88,8 +168,8 @@ and the pages following it. 1. For each of the two environments staging and production referred to by `${environment}` below: 1. Change into the clone `${environment}` by running `cd /app/${environment}`. - 1. Prepare the environment by running `cp .env.${environment}.sample .env`, - `cp frontend/.env.local.sample frontend/.env.local`, and by replacing + 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env`, + `cp ./frontend/.env.local.sample ./frontend/.env.local`, and by replacing dummy passwords in the copies by newly generated ones, where random passwords may be generated running `openssl rand -base64 32`. 1. Prepare PostgreSQL by generating new password files by running @@ -162,6 +242,101 @@ and the pages following it. figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. +### Troubleshooting + +The file `Makefile.production` contains GNU Make targets to manage Docker +containers like `up` and `down`, to follow Docker container logs with `logs`, +to drop into shells inside running Docker containers like `shellb` for the +backend service and `shellf` for the frontend service and `psql` for the +databse service, and to list information about Docker like `list` and +`list-services`. + +And the file contains GNU Make targets to deploy a new release or rollback it +back as mentioned above. These targets depend on several smaller targets like +`begin-maintenance` and `end-maintenance` to begin or end displaying +maintenance information to end users that try to interact with the website, and +`backup` to backup all data before deploying a new version, `migrate` to +migrate the database, and `run-tests` to run tests. + +If for some reason the website displays the maintenance page without +maintenance happening at the moment, then drop into a shell on the production +machine, check all logs for information on what happened, fix issues if +necessary, and end maintenance. It could for example happen that a cron job +set-up by [machine](https://github.com/building-envelope-data/machine) begins +maintenance, fails to do its actual job, and does not end maintenance +afterwards. Whether failing to do its job is a problem for the inner workings +of the website needs to be decided by some developer. If it for example backing +up the database fails because the machine is out of memory at the time of doing +the backup, the website itself should still working. + +If the database container restarts indefinitely and its logs say + +``` +PANIC: could not locate a valid checkpoint record +``` + +for example preceded by `LOG: invalid resource manager ID in primary +checkpoint record` or `LOG: invalid primary checkpoint record`, then the +database is corrupt. For example, the write-ahead log (WAL) may be corrupt +because the database was not shut down cleanly. One solution is to restore the +database from a backup by running + +``` +make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/20XX-XX-XX_XX_XX_XX/ restore +``` + +where the `X`s need to be replaced by proper values. Another solution is to +reset the transaction log by entering the database container with + +``` +docker compose --file=docker-compose.production.yml --project-name metabase_production run database bash +``` + +and dry-running + +``` +gosu postgres pg_resetwal --dry-run /var/lib/postgresql/data +``` + +and, depending on the output, also running + +``` +gosu postgres pg_resetwal /var/lib/postgresql/data +``` + +Note that both solutions may cause data to be lost. + +## Original Idea + +The product identifier service should provide the following endpoints: + +- Obtain a new product identifier possibly associating internal meta information with it, like a custom string or a JSON blob +- Update the meta information of one of your product identifiers +- Get meta information of one of your product identifiers +- Get the owner of a product identifier (needed, for example, by the IGSDB to check that the user adding product data with a product identifier owns the identifier) +- List all your product identifiers +- Request the transferal of the ownership of one or all of your product identifiers to another (once the receiving user agrees, the transferal is made) +- Respond to a transferal request + +How to obtain a unique product identifier and add product data to some database: + +1. Create an account at a central authentication service, that is, a domain specific and lightweight service like [Auth0](https://auth0.com) managed by us (the details of how users prove to be a certain manufacturer are still open) +2. Authenticate yourself at the authentication service receiving a [JSON web token](https://jwt.io) (this could be a simple username/password authentication scheme) +3. Obtain a new product identifier from the respective service passing your JSON web token as means of authentication +4. Add product data to some database like IGSDB passing the product identifier and your JSON web token + +JSON web tokens are used for authentication across different requests, services, and domains. + +Product identifiers are randomly chosen and verified to be unique 32, 48, or 64 bit numbers, which can be communicated for easy human use as [proquints](https://arxiv.org/html/0901.4016) [there are implementations in various languages](https://github.com/dsw/proquint). We could alternatively use [version 4 universally-unique identifiers](https://tools.ietf.org/html/rfc4122); I consider this to be overkill as it comes with a performance penalty and our identifiers do not need to be universally unique. Either way, [those identifiers do _not_ replace primary keys](https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439). + +Randomness of identifiers ensures that + +- the product identifier does not encode any information regarding the product, like its manufacturer, which would, for example, be problematic when one manufacturer is bought by another +- a user cannot run out of product identifiers, because there is no fixed range of possible identifiers per user +- it's unlikely that flipping one bit or replacing one letter in the proquint representation by another results in a valid identifier owned by the same user + +We may add some error detection and correction capabilities by, for example, generating all but the last 4 bits randomly and using the last 4 bits as [some sort of checksum](https://en.wikipedia.org/wiki/Checksum). + ## Useful Resources - [Set up a GraphQL client with Apollo](https://hasura.io/learn/graphql/typescript-react-apollo/apollo-client/) From 9508ccdcfa24347f9e0d0ac465f274839b9c8464 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Sep 2024 14:39:01 +0200 Subject: [PATCH 193/878] Seed database also in production (seeding is supposed to be idempotent and can be used to bootstrap the production setup) --- backend/src/Program.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 522cb49a..11d8fdc1 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -55,9 +55,8 @@ string[] commandLineArguments startup.Configure(application); using (var scope = application.Services.CreateScope()) { - if (application.Environment.IsDevelopment()) - // https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDbIfNotExists(scope.ServiceProvider).ConfigureAwait(false); + // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data + await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); } application.Run(); @@ -104,7 +103,7 @@ string environment if (environment != "production") configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); } - private static async Task CreateAndSeedDbIfNotExists( + private static async Task CreateAndSeedDb( IServiceProvider services ) { @@ -113,7 +112,8 @@ IServiceProvider services using var dbContext = services.GetRequiredService>() .CreateDbContext(); - if (dbContext.Database.EnsureCreated()) await DbSeeder.DoAsync(services).ConfigureAwait(false); + dbContext.Database.EnsureCreated(); + await DbSeeder.DoAsync(services).ConfigureAwait(false); } catch (Exception exception) { @@ -131,7 +131,7 @@ string[] commandLineArguments new WebApplicationOptions { Args = commandLineArguments, - ContentRootPath = Directory.GetCurrentDirectory() + ContentRootPath = Directory.GetCurrentDirectory() // PlatformServices.Default.Application.ApplicationBasePath } ); // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/ From b6c7d8ee2331308e194dfa76e1a3d7fdf9e3babe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Sep 2024 14:41:29 +0200 Subject: [PATCH 194/878] Use constant `Program.TestEnvironment` instead of string "test" --- backend/src/Configuration/AuthConfiguration.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index ca242308..348e1d03 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -17,8 +17,8 @@ namespace Database.Configuration; public abstract class AuthConfiguration { public const string ClientId = "testlab-solar-facades"; - public static string ReadApiScope { get; } = "api:read"; - public static string WriteApiScope { get; } = "api:write"; + public const string ReadApiScope = "api:read"; + public const string WriteApiScope = "api:write"; public static void ConfigureServices( IServiceCollection services, @@ -103,7 +103,7 @@ IWebHostEnvironment environment _.UseDefaultThreadPool(_ => _.MaxConcurrency = 10 ); - if (environment.IsEnvironment("test")) + if (environment.IsEnvironment(Program.TestEnvironment)) // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 _.SchedulerName = Guid.NewGuid().ToString(); }); From 92206bce93afb46b8e34c4b1f2e94a6330d76cb4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Sep 2024 14:42:08 +0200 Subject: [PATCH 195/878] Bring startup configuration inline with the one of the metabase --- backend/src/Startup.cs | 96 +++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 20 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index c4ce9016..17f2e8c6 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -3,12 +3,12 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; +using HotChocolate.AspNetCore; using Database.Configuration; using Database.Data; using Database.Data.Extensions; using Database.Enumerations; using Database.Services; -using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Diagnostics.HealthChecks; @@ -29,6 +30,7 @@ namespace Database; public sealed class Startup { private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; + private const string AntiforgeryHeaderName = "X-XSRF-TOKEN"; private readonly AppSettings _appSettings; private readonly IWebHostEnvironment _environment; @@ -40,7 +42,8 @@ IConfiguration configuration { _environment = environment; _appSettings = configuration.Get() ?? - throw new ArgumentException("Failed to get application settings from configuration."); + throw new InvalidOperationException( + "Failed to get application settings from configuration."); } public void ConfigureServices(IServiceCollection services) @@ -50,12 +53,12 @@ public void ConfigureServices(IServiceCollection services) ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); - ConfigureSessionServices(services); - services.AddAntiforgery(_ => { _.HeaderName = "X-XSRF-TOKEN"; }); + ConfigureSessionServices(services, _environment); + services.AddAntiforgery(_ => { _.HeaderName = AntiforgeryHeaderName; }); services .AddDataProtection() .PersistKeysToDbContext(); - services.AddHttpClient(); + ConfigureHttpClientServices(services); services.AddHttpContextAccessor(); services .AddHealthChecks() @@ -87,7 +90,34 @@ private static void ConfigureRequestResponseServices(IServiceCollection services .AllowAnyMethod() ) ); - services.AddControllersWithViews(); + services.AddControllersWithViews() + .AddRazorOptions(_ => + { + // The default location formarts are `/Views/{1}/{0}.cshtml` and `/Views/Shared/{0}.cshtml`. + // Add the format that ignores the directory structure, that is, + // leave out `/Views` and the controller name `{1}`. For some + // reason compiled views land in the root directory. The + // debugging output of `make logs` when navigating to + // https://local.buildingenvelopedata.org:4041/connect/logout?... + // is: + // Initializing Razor view compiler with compiled view: '/Authorize.cshtml'. + // Initializing Razor view compiler with compiled view: '/Logout.cshtml'. + // Initializing Razor view compiler with compiled view: '/Verify.cshtml'. + // Initializing Razor view compiler with compiled view: '/Error.cshtml'. + // Initializing Razor view compiler with compiled view: '/_Layout.cshtml'. + // Initializing Razor view compiler with compiled view: '/_ViewImports.cshtml'. + // Initializing Razor view compiler with compiled view: '/_ViewStart.cshtml'. + // View lookup cache miss for view 'Logout' in controller 'Authorization'. + // Could not find a file for view at path '/Views/Authorization/Logout.cshtml'. + // Could not find a file for view at path '/Views/Shared/Logout.cshtml'. + // Located compiled view for view at path '/Logout.cshtml'. + // Located compiled view for view at path '/_ViewStart.cshtml'. + // Executing ViewResult, running view Logout. + // The view path '/Logout.cshtml' was found in 5.0269ms. + _.ViewLocationFormats.Add("/{0}.cshtml"); + // TODO I consider the flattened structure a bug. How can we solve this? + } + ); } private void ConfigureMessageSenderServices(IServiceCollection services) @@ -101,20 +131,42 @@ private void ConfigureMessageSenderServices(IServiceCollection services) ); } - private static void ConfigureSessionServices(IServiceCollection services) + private static void ConfigureSessionServices( + IServiceCollection services, + IWebHostEnvironment environment + ) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state services.AddDistributedMemoryCache(); services.AddSession(options => { - // Set a short timeout for easy testing. - options.IdleTimeout = TimeSpan.FromSeconds(10); + // Set a short timeout for easy testing in development and a long + // one otherwise. + options.IdleTimeout = + environment.IsDevelopment() + ? TimeSpan.FromSeconds(10) + : TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; // Make the session cookie essential options.Cookie.IsEssential = true; }); } + private static void ConfigureDatabaseContext( + DbContextOptionsBuilder options, + IWebHostEnvironment environment + ) + { + if (!environment.IsProduction()) + options + .EnableSensitiveDataLogging() + .EnableDetailedErrors(); + if (environment.IsEnvironment(Program.TestEnvironment)) + options.ConfigureWarnings(x => + x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) + ); + } + private void ConfigureDatabaseServices(IServiceCollection services) { services.AddPooledDbContextFactory(options => @@ -128,22 +180,16 @@ private void ConfigureDatabaseServices(IServiceCollection services) .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(_appSettings.Database.SchemaName) .UseOpenIddict(); - if (!_environment.IsProduction()) - options - .EnableSensitiveDataLogging() - .EnableDetailedErrors(); + ConfigureDatabaseContext(options, _environment); } ); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. services.AddDbContext( - (serviceProvider, options) => + (services, options) => { - if (!_environment.IsProduction()) - options - .EnableSensitiveDataLogging() - .EnableDetailedErrors(); - serviceProvider + ConfigureDatabaseContext(options, _environment); + services .GetRequiredService>() .CreateDbContext(); }, @@ -151,10 +197,15 @@ private void ConfigureDatabaseServices(IServiceCollection services) ); } + private static void ConfigureHttpClientServices(IServiceCollection services) + { + services.AddHttpClient(); + } + public void Configure(WebApplication app) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ - if (_environment.IsDevelopment() || _environment.IsEnvironment("test")) + if (_environment.IsDevelopment() || _environment.IsEnvironment(Program.TestEnvironment)) { app.UseDeveloperExceptionPage(); // app.UseMigrationsEndPoint(); @@ -248,6 +299,7 @@ private static Task WriteJsonResponse(HttpContext context, HealthReport healthRe healthReportEntry.Value.Duration.ToString()); jsonWriter.WriteStartArray("tags"); foreach (var tag in healthReportEntry.Value.Tags) jsonWriter.WriteStringValue(tag); + jsonWriter.WriteEndArray(); var exception = healthReportEntry.Value.Exception; if (exception is not null) @@ -255,11 +307,15 @@ private static Task WriteJsonResponse(HttpContext context, HealthReport healthRe jsonWriter.WriteStartObject("exception"); jsonWriter.WriteString("message", exception.Message); if (exception.StackTrace is not null) jsonWriter.WriteString("stackTrace", exception.StackTrace); + if (exception.InnerException is not null) jsonWriter.WriteString("innerException", exception.InnerException.ToString()); + if (exception.Source is not null) jsonWriter.WriteString("source", exception.Source); + if (exception.TargetSite is not null) jsonWriter.WriteString("targetSite", exception.TargetSite.ToString()); + jsonWriter.WriteEndObject(); } From e59f8f806fd3a37e7c81d87764003e4c310c1fd0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 23 Oct 2024 19:46:35 +0200 Subject: [PATCH 196/878] Add the latest changes from the metabase project to these Makefiles --- Makefile | 25 +++++++++--------- Makefile.production | 64 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index d9ffa293..d1603371 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,7 @@ restart : ## Restart all stopped and running containers logs : ## Follow logs ${docker_compose} logs \ + --since=24h \ --follow .PHONY : logs @@ -144,10 +145,9 @@ traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for examp " .PHONY : traceb -shelln : up ## Enter shell in an existing `nginx` container (after starting all containers if necessary) - ${docker_compose} exec \ - nginx \ - bash +shelln : CONTAINER = nginx +shelln : COMMAND = bash +shelln : exec ## Enter shell in an existing `nginx` container (after starting all containers if necessary) .PHONY : shelln psql : ## Enter PostgreSQL interactive terminal in the running `database` container @@ -158,10 +158,9 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` contai --dbname xbase_development .PHONY : psql -shelld : up ## Enter shell in an existing `database` container (after starting all containers if necessary) - ${docker_compose} exec \ - database \ - bash +shelld : CONTAINER = database +shelld : COMMAND = bash +shelld : exec ## Enter shell in an existing `database` container (after starting all containers if necessary) .PHONY : shelld list : ## List all containers with health status @@ -170,19 +169,21 @@ list : ## List all containers with health status --all .PHONY : list -createdb : ## Create database +createdb : DBNAME = xbase_development +createdb : ## Create database with name `${DBNAME}` defaulting to `xbase_development` ${docker_compose} exec \ database \ bash -c " \ - createdb --username postgres xbase_development ; \ + createdb --username postgres ${DBNAME} ; \ " .PHONY : createdb -dropdb : ## Drop database +dropdb : DBNAME = xbase_development +dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase_development` ${docker_compose} exec \ database \ bash -c " \ - dropdb --username postgres xbase_development ; \ + dropdb --username postgres ${DBNAME} ; \ " .PHONY : dropdb diff --git a/Makefile.production b/Makefile.production index a9afd286..04b3dbc4 100644 --- a/Makefile.production +++ b/Makefile.production @@ -3,6 +3,16 @@ include .env +# Use bash with pipefail to have backup and restore fail when whatever comes +# before the pipe fails. The options given to bash below are described in +# https://explainshell.com/explain?cmd=set+-eux+-o+pipefail +# and +# https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425 +# These settings could just be applied where needed instead of globally as +# described in +# https://stackoverflow.com/questions/23079651/equivalent-of-pipefail-in-gnu-make/23080721#23080721 +SHELL=/bin/bash -eux -o pipefail + docker_compose = \ docker compose \ --file docker-compose.production.yml \ @@ -66,11 +76,30 @@ exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CO execb : CONTAINER = backend execb : exec ## Execute the one-time command `${COMMAND}` against an existing `backend` container (after starting all containers if necessary) +.PHONY : execb + +execf : CONTAINER = frontend +execf : exec ## Execute the one-time command `${COMMAND}` against an existing `frontend` container (after starting all containers if necessary) +.PHONY : execf shellb : COMMAND = bash shellb : execb ## Enter shell in a running `backend` container (to test database access from within the shell run `apk add postgresql-client` and `psql "host=database port=5432 user=postgres passfile=/run/secrets/postgres_passwords"`) .PHONY : shellb +shellf : COMMAND = bash +shellf : execf ## Enter shell in a running `frontend` container (after starting all containers if necessary) +.PHONY : shellf + +shelln : CONTAINER = nginx +shelln : COMMAND = bash +shelln : exec ## Enter shell in a running `nginx` container (after starting all containers if necessary) +.PHONY : shelln + +shelld : CONTAINER = database +shelld : COMMAND = bash +shelld : exec ## Enter shell in a running `database` container (after starting all containers if necessary) +.PHONY : shelld + list : ## List all containers with health status ${docker_compose} ps \ --no-trunc \ @@ -136,8 +165,11 @@ postgres_passwords : postgres_password ## Generate PostgreSQL passwords file who # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database backup : FILES_CONTAINER_NAME = backup_${NAME}_files -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` mkdir --parents ${BACKUP_DIRECTORY} + ${docker_compose} down \ + --remove-orphans \ + database -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} ${docker_compose} run \ @@ -174,6 +206,10 @@ backup : ## Backup database and related data to directory with absolute path `${ ./files docker container stop ${FILES_CONTAINER_NAME} docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database .PHONY : backup # Inspired by https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash/34862475#34862475 @@ -193,9 +229,20 @@ prune-backups : ## Keep the most recent 7 backups, delete the rest rm --recursive --dir -- .PHONY : prune-backups +remove-data : BACKUP_DIRECTORY = $(shell pwd)/backup +remove-data : backup ## Remove data volumes (after creating a backup in ./backup) + docker volume rm \ + ${NAME}_data +.PHONY : remove-data + restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}`, for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore` +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make --file=Makefile.production restart`)` + ${docker_compose} down \ + --remove-orphans \ + database + docker volume rm \ + ${NAME}_data -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} ${docker_compose} run \ @@ -241,6 +288,10 @@ restore : ## Restore database and related data from directory with absolute path " docker container stop ${FILES_CONTAINER_NAME} docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database .PHONY : restore begin-maintenance : ## Begin maintenance @@ -281,7 +332,10 @@ checkout-target : ## Fetch and checkout `${TARGET}` migrate : FROM = $(file < ./backend/src/Migrations/previous) migrate : TO = $(file < ./backend/src/Migrations/current) migrate : CONTAINER_NAME = migrate_${NAME}_database -migrate : ## Migrate database (note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +migrate : ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) + ${docker_compose} down \ + --remove-orphans \ + database -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} run \ @@ -300,6 +354,10 @@ migrate : ## Migrate database (note that other PostgreSQL instances using the sa --dbname=xbase docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database .PHONY : migrate # Note that NGINX is because of its dependencies taken down and up last and in From bea130549a322e7f966c347649f8f29699c331ae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 24 Oct 2024 17:50:59 +0200 Subject: [PATCH 197/878] Update backend packages (in particular, upgrade to hotchocolate 14) --- backend/dotnet-tools.json | 20 ++++---- .../src/Configuration/GraphQlConfiguration.cs | 33 ++++++------- .../src/Controllers/FileUploadController.cs | 2 + backend/src/Data/DbSeeder.cs | 1 + backend/src/Database.csproj | 30 ++++++------ .../CalorimetricDataResolvers.cs | 4 +- backend/src/GraphQl/Connection.cs | 3 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 4 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 13 +++-- backend/src/GraphQl/ForkingConnection.cs | 3 +- .../GeometricDataX/GeometricDataResolvers.cs | 4 +- .../HygrothermalDataResolvers.cs | 4 +- .../GraphQl/LoggingDiagnosticEventListener.cs | 49 ++++++++++--------- .../OpticalDataX/OpticalDataResolvers.cs | 4 +- .../PhotovoltaicDataResolvers.cs | 4 +- backend/test/Database.Tests.csproj | 4 +- backend/test/Play.cs | 1 + frontend/components/Layout.tsx | 8 +-- 18 files changed, 97 insertions(+), 94 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 3995b59c..4bb5257d 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,28 +3,28 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "8.0.5", + "version": "8.0.6", "commands": [ "dotnet-aspnet-codegenerator" ], "rollForward": false }, "dotnet-counters": { - "version": "8.0.532401", + "version": "8.0.547301", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "8.0.532401", + "version": "8.0.547301", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "8.0.8", + "version": "8.0.10", "commands": [ "dotnet-ef" ], @@ -38,35 +38,35 @@ "rollForward": false }, "dotnet-trace": { - "version": "8.0.532401", + "version": "8.0.547301", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "8.0.532401", + "version": "8.0.547301", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "8.0.532401", + "version": "8.0.547301", "commands": [ "dotnet-sos" ], "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.9.0", + "version": "0.9.2", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.2.4", + "version": "2024.2.7", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "13.9.12", + "version": "14.0.0", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 32949270..db45c938 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,4 +1,9 @@ using System; +using HotChocolate.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Language; +using HotChocolate.Types; +using HotChocolate.Types.Pagination; using System.Net.Http; using System.Text.RegularExpressions; using Database.Data; @@ -17,11 +22,6 @@ using Database.GraphQl.Standards; using Database.GraphQl.GeometricDataX; using Database.GraphQl.Users; -using HotChocolate.Data; -using HotChocolate.Data.Filters; -using HotChocolate.Language; -using HotChocolate.Types; -using HotChocolate.Types.Pagination; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -61,13 +61,13 @@ AppSettings appSettings services .AddGraphQLServer() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext - .RegisterDbContext(DbContextKind.Pooled) + .RegisterDbContextFactory() + .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) // Extensions .AddProjections() .AddFiltering() .AddSorting() .AddAuthorization() - .AddApolloTracing() // TODO Do we want or need this? .AddGlobalObjectIdentification() .AddQueryFieldToMutationPayloads() .ModifyOptions(options => @@ -94,8 +94,8 @@ AppSettings appSettings // Subscriptions /* .AddInMemorySubscriptions() */ // Persisted queries - /* .AddFileSystemQueryStorage("./persisted_queries") */ - /* .UsePersistedQueryPipeline(); */ + /* .AddFileSystemOperationDocumentStorage("./persisted_operations") */ + /* .UsePersistedOperationPipeline(); */ // HotChocolate uses the default authentication scheme, // which we set to `null` in `AuthConfiguration` to force // users to be explicit about what scheme to use when @@ -167,14 +167,13 @@ AppSettings appSettings // Data Loaders /* .AddDataLoader() */ // Paging - .SetPagingOptions( - new PagingOptions + .ModifyPagingOptions(options => { - MaxPageSize = int.MaxValue, - DefaultPageSize = int.MaxValue, - IncludeTotalCount = true, + options.MaxPageSize = int.MaxValue; + options.DefaultPageSize = int.MaxValue; + options.IncludeTotalCount = true; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. - InferConnectionNameFromField = false + options.InferConnectionNameFromField = false; } ) // Stitching @@ -185,8 +184,8 @@ AppSettings appSettings // } // ") // Automatic Peristed Queries - .UseAutomaticPersistedQueryPipeline() - .AddInMemoryQueryStorage(); + .UseAutomaticPersistedOperationPipeline() + .AddInMemoryOperationDocumentStorage(); // Needed by the automatic persisted operation pipeline } public static class WellKnownSchemaNames diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 3cf32483..2b2e8164 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -215,10 +215,12 @@ private static Encoding GetEncoding(MultipartSection section) // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in // most cases. +#pragma warning disable SYSLIB0001 if (!hasMediaTypeHeader || Encoding.UTF7.Equals( mediaType?.Encoding ?? throw new ArgumentException("Impossible (because `hasMediaTypeHeader` is `true`)!")) ) +#pragma warning restore SYSLIB0001 return Encoding.UTF8; return mediaType.Encoding; diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index 115489b6..a6db34e1 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -26,5 +26,6 @@ IServiceProvider services logger.SeedingDatabase(); // var environment = services.GetRequiredService(); // var appSettings = services.GetRequiredService(); + await Task.FromResult(0); } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index b0c30155..dca9b878 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -23,23 +23,23 @@ - - - - - - - - - - - - + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + @@ -50,7 +50,7 @@ - + diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs index 7d575ba7..8e8f803e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs @@ -9,13 +9,13 @@ namespace Database.GraphQl.CalorimetricDataX; public sealed class CalorimetricDataResolvers { - public Task GetGetHttpsResources( + public async Task GetGetHttpsResources( [Parent] CalorimetricData calorimetricData, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync(calorimetricData.Id, cancellationToken); + return await byId.LoadAsync(calorimetricData.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index b1a31832..31bbf56c 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -31,8 +31,7 @@ CancellationToken cancellationToken ) { return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) + await dataLoader.LoadAsync(Subject.Id, cancellationToken).ConfigureAwait(false) ?? [] ) .Select(_createEdge); } diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 05f207cf..7577e821 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -22,7 +22,7 @@ CancellationToken cancellationToken ) { return new GetHttpsResourceTreeRoot( - (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)).Single() + (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false) ?? []).Single() ); } @@ -31,7 +31,7 @@ public async Task> GetNonRootVe CancellationToken cancellationToken ) { - return (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false)) + return (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false) ?? []) .Select(v => new GetHttpsResourceTreeNonRootVertex(v) ) diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 37b9a4ad..91b2fd30 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -7,7 +7,6 @@ using Database.Metabase; using GraphQL; using HotChocolate; -using HotChocolate.Execution; using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -57,7 +56,7 @@ await QueryingMetabase.ConstructGraphQlQuery( cancellationToken ).ConfigureAwait(false); if (response is null) - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_RESPONSE") .SetPath(resolverContext.Path) @@ -65,7 +64,7 @@ await QueryingMetabase.ConstructGraphQlQuery( .Build() ); if (response.Data.Databases.Edges is null) - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_EDGES") .SetPath(resolverContext.Path) @@ -73,7 +72,7 @@ await QueryingMetabase.ConstructGraphQlQuery( .Build() ); if (response.Data.Databases.Edges.Count == 0) - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("NO_DATABASE") .SetPath(resolverContext.Path) @@ -81,7 +80,7 @@ await QueryingMetabase.ConstructGraphQlQuery( .Build() ); if (response.Data.Databases.Edges.Count >= 2) - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("AMBIGUOUS_DATABASE") .SetPath(resolverContext.Path) @@ -92,7 +91,7 @@ await QueryingMetabase.ConstructGraphQlQuery( } catch (HttpRequestException e) { - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("METABASE_REQUEST_FAILED") .SetPath(resolverContext.Path) @@ -103,7 +102,7 @@ await QueryingMetabase.ConstructGraphQlQuery( } catch (JsonException e) { - throw new QueryException( + throw new GraphQLException( ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index 1fd9d149..8a38a2cc 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -48,8 +48,7 @@ CancellationToken cancellationToken where TDataLoader : IDataLoader { return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) - .ConfigureAwait(false) + await dataLoader.LoadAsync(Subject.Id, cancellationToken).ConfigureAwait(false) ?? [] ) .Select(_createEdge); } diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs index b1ead8f5..fab9b797 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs @@ -9,13 +9,13 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataResolvers { - public Task GetGetHttpsResources( + public async Task GetGetHttpsResources( [Parent] GeometricData geometricData, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync(geometricData.Id, cancellationToken); + return await byId.LoadAsync(geometricData.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs index 2145366f..36c7bdbb 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs @@ -9,13 +9,13 @@ namespace Database.GraphQl.HygrothermalDataX; public sealed class HygrothermalDataResolvers { - public Task GetGetHttpsResources( + public async Task GetGetHttpsResources( [Parent] HygrothermalData hygrothermalData, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync(hygrothermalData.Id, cancellationToken); + return await byId.LoadAsync(hygrothermalData.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index b043b30d..e78b533a 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -18,11 +18,11 @@ public static partial class Log [LoggerMessage( EventId = 0, Level = LogLevel.Error, - Message = "Failed executing the query {Query}.")] + Message = "Failed executing the document {Document}.")] public static partial void FailedQueryExecution( this ILogger logger, Exception exception, - IQuery? query + IOperationDocument? document ); [LoggerMessage( @@ -59,11 +59,11 @@ IOperation operation [LoggerMessage( EventId = 4, Level = LogLevel.Error, - Message = "The query {Query} has the syntax error {Error}.")] + Message = "The query {Document} has the syntax error {Error}.")] public static partial void FailedSyntax( this ILogger logger, Exception? exception, - IQuery? query, + IOperationDocument? document, string error ); @@ -82,11 +82,11 @@ string error [LoggerMessage( EventId = 6, Level = LogLevel.Error, - Message = "Failed validating the query {Query} with the error {Error}.")] + Message = "Failed validating the query {Document} with the error {Error}.")] public static partial void FailedValidation( this ILogger logger, Exception? exception, - IQuery? query, + IOperationDocument? document, string error ); } @@ -118,7 +118,7 @@ public override void RequestError( Exception exception ) { - _logger.FailedQueryExecution(exception, context.Request.Query); + _logger.FailedQueryExecution(exception, context.Request.Document); } public override void ResolverError( @@ -144,7 +144,7 @@ public override void SyntaxError( IError error ) { - _logger.FailedSyntax(error.Exception, context.Request.Query, ConvertErrorToString(error)); + _logger.FailedSyntax(error.Exception, context.Request.Document, ConvertErrorToString(error)); } public override void TaskError( @@ -161,7 +161,7 @@ IReadOnlyList errors ) { foreach (var error in errors) - _logger.FailedValidation(error.Exception, context.Request.Query, ConvertErrorToString(error)); + _logger.FailedValidation(error.Exception, context.Request.Document, ConvertErrorToString(error)); } private static string ConvertErrorToString( @@ -202,22 +202,25 @@ public void Dispose() { stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Variables {Environment.NewLine}"); - foreach (var variableValue in _context.Variables!) + foreach (var variableValueCollection in _context.Variables) { - try + foreach (var variableValue in variableValueCollection) { - stringBuilder.AppendFormat( - CultureInfo.InvariantCulture, - $" {variableValue.Name} : "); - stringBuilder.Append(variableValue.Value.ToString()); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" : {variableValue.Type}"); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - } - catch (Exception exception) - { - // all input type records will land here. - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" Formatting the variable '{variableValue.Name}' failed with the exception '{exception}'."); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + try + { + stringBuilder.AppendFormat( + CultureInfo.InvariantCulture, + $" {variableValue.Name} : "); + stringBuilder.Append(variableValue.Value.ToString()); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" : {variableValue.Type}"); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + catch (Exception exception) + { + // all input type records will land here. + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" Formatting the variable '{variableValue.Name}' failed with the exception '{exception}'."); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } } } } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs index 35374bb4..d26f938b 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs @@ -9,13 +9,13 @@ namespace Database.GraphQl.OpticalDataX; public sealed class OpticalDataResolvers { - public Task GetGetHttpsResources( + public async Task GetGetHttpsResources( [Parent] OpticalData opticalData, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync(opticalData.Id, cancellationToken); + return await byId.LoadAsync(opticalData.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs index 4a0f3f2f..07b5a4ce 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs @@ -9,13 +9,13 @@ namespace Database.GraphQl.PhotovoltaicDataX; public sealed class PhotovoltaicDataResolvers { - public Task GetGetHttpsResources( + public async Task GetGetHttpsResources( [Parent] PhotovoltaicData photovoltaicData, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync(photovoltaicData.Id, cancellationToken); + return await byId.LoadAsync(photovoltaicData.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index c5cec3b8..b01b5389 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -18,8 +18,8 @@ - - + + diff --git a/backend/test/Play.cs b/backend/test/Play.cs index be721d28..bcbcda76 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -10,5 +10,6 @@ public sealed class Play public async Task Do() { Console.WriteLine("Do play!"); + await Task.FromResult(0); } } \ No newline at end of file diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index dfffb38d..bf13d102 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -19,6 +19,10 @@ const navItems = [ path: paths.calorimetricData, label: "Calorimetric Data", }, + { + path: paths.geometricData, + label: "Geometric Data", + }, { path: paths.hygrothermalData, label: "Hygrothermal Data", @@ -35,10 +39,6 @@ const navItems = [ path: paths.database, label: "Database", }, - { - path: paths.geometricData, - label: "Geometric Data", - }, ]; export type LayoutProps = { From 9d8baf751a5d4d4075d229f6300f15e7784b95c6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 24 Oct 2024 18:15:11 +0200 Subject: [PATCH 198/878] Remove unnecessary attribute `Service` --- .../GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs | 2 +- backend/src/GraphQl/Databases/DatabaseMutations.cs | 6 +++--- backend/src/GraphQl/Databases/DatabaseQueries.cs | 6 +++--- .../src/GraphQl/GeometricDataX/GeometricDataMutations.cs | 2 +- .../GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs | 2 +- .../GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs | 2 +- .../GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs | 2 +- .../GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 6 +++--- backend/src/GraphQl/VerificationCodeQueries.cs | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 89d964c1..bb3b7587 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -19,7 +19,7 @@ public async Task CreateCalorimetricDataAsync( // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index bb20bfb7..24c4d04c 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -20,9 +20,9 @@ public sealed class DatabaseMutations public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 91b2fd30..cda86c41 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -22,9 +22,9 @@ public sealed class DatabaseQueries }; public async Task GetDatabaseAsync( - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 276ec449..27e96c9c 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -15,7 +15,7 @@ public sealed class GeometricDataMutations public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 75cf5a32..d309850e 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -20,7 +20,7 @@ public async Task CreateGetHttpsResourceAsync( // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 5b9b10d3..02c78b07 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -53,7 +53,7 @@ CancellationToken cancellationToken public Uri GetLocator( [Parent] GetHttpsResource getHttpsResource, - [Service] AppSettings appSettings + AppSettings appSettings ) { return new Uri($"{appSettings.Host}/api/resources/{getHttpsResource.Id}"); diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 0ce5444d..8d1e541b 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -19,7 +19,7 @@ public async Task CreateHygrothermalDataAsync( // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index e5323a3c..38d1f4c7 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -19,7 +19,7 @@ public async Task CreateOpticalDataAsync( // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index f1a22eca..7fc742ed 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -19,7 +19,7 @@ public async Task CreatePhotovoltaicDataAsync( // ClaimsPrincipal claimsPrincipal, // [ScopedService] UserManager userManager, ApplicationDbContext context, - [Service] AppSettings appSettings, + AppSettings appSettings, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index c1cefd9a..a05f0286 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -35,9 +35,9 @@ await context.Users.AsQueryable() } public async Task GetCurrentUserInfoAsync( - [Service] AppSettings appSettings, - [Service] IHttpClientFactory httpClientFactory, - [Service] IHttpContextAccessor httpContextAccessor, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/VerificationCodeQueries.cs b/backend/src/GraphQl/VerificationCodeQueries.cs index 44f5f9aa..17d8b334 100644 --- a/backend/src/GraphQl/VerificationCodeQueries.cs +++ b/backend/src/GraphQl/VerificationCodeQueries.cs @@ -7,7 +7,7 @@ namespace Database.GraphQl; public sealed class VerificationCodeQueries { public string GetVerificationCode( - [Service] AppSettings appSettings + AppSettings appSettings ) { return appSettings.VerificationCode; From 80dc2e31e64cad4604d3a8579532456688ae0989 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Oct 2024 13:42:34 +0200 Subject: [PATCH 199/878] Stop tracking entities unnecessarily in queries --- .../src/Controllers/FileUploadController.cs | 2 +- backend/src/GraphQl/DataX/DataQueries.cs | 22 +++++++++---------- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 2 +- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 2 +- .../GetHttpsResourcesByDataIdDataLoader.cs | 2 +- .../GraphQl/Entities/EntityByIdDataLoader.cs | 3 +-- ...eChildrenByGetHttpsResourceIdDataLoader.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- 8 files changed, 18 insertions(+), 19 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 2b2e8164..e1fcc9c0 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -95,7 +95,7 @@ CancellationToken cancellationToken return BadRequest(ModelState); } - if (!await _context.GetHttpsResources.AsQueryable() + if (!await _context.GetHttpsResources.AsNoTracking() .Where(u => u.Id == getHttpsResourceUuid) .AnyAsync(cancellationToken) .ConfigureAwait(false) diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 3de9817a..8e0591df 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -12,7 +12,7 @@ using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; -using Guid = System.Guid; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.DataX; @@ -30,17 +30,17 @@ ApplicationDbContext context ) { // TODO Use `timestamp` and `locale`. - // return context.CalorimetricData.AsQueryable(); + // return context.CalorimetricData.AsNoTracking(); // The union below does sadly not work because the different kinds of data have different include operations. - // return context.CalorimetricData.AsQueryable() - // .Union(context.HygrothermalData.AsQueryable()) - // .Union(context.OpticalData.AsQueryable()) - // .Union(context.PhotovoltaicData.AsQueryable()); - return context.CalorimetricData.AsQueryable().AsEnumerable() - .Concat(context.HygrothermalData.AsQueryable().AsEnumerable()) - .Concat(context.OpticalData.AsQueryable().AsEnumerable()) - .Concat(context.PhotovoltaicData.AsQueryable().AsEnumerable()) - .Concat(context.GeometricData.AsQueryable().AsEnumerable()); + // return context.CalorimetricData.AsNoTracking() + // .Union(context.HygrothermalData.AsNoTracking()) + // .Union(context.OpticalData.AsNoTracking()) + // .Union(context.PhotovoltaicData.AsNoTracking()); + return context.CalorimetricData.AsNoTracking().AsEnumerable() + .Concat(context.HygrothermalData.AsNoTracking().AsEnumerable()) + .Concat(context.OpticalData.AsNoTracking().AsEnumerable()) + .Concat(context.PhotovoltaicData.AsNoTracking().AsEnumerable()) + .Concat(context.GeometricData.AsNoTracking().AsEnumerable()); } public async Task GetDataAsync( diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 9c6714c5..9d21a64a 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -20,7 +20,7 @@ IDbContextFactory dbContextFactory options, dbContextFactory, (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => + dbContext.GetHttpsResources.AsNoTracking().Where(x => x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 6e2c575a..09d80fcc 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -20,7 +20,7 @@ IDbContextFactory dbContextFactory options, dbContextFactory, (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => + dbContext.GetHttpsResources.AsNoTracking().Where(x => x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index 833f7c4d..ade0cec6 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -20,7 +20,7 @@ IDbContextFactory dbContextFactory options, dbContextFactory, (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => + dbContext.GetHttpsResources.AsNoTracking().Where(x => ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs index b9726e4f..8532b92e 100644 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs @@ -6,7 +6,6 @@ using Database.Data; using GreenDonut; using Microsoft.EntityFrameworkCore; -using Guid = System.Guid; namespace Database.GraphQl.Entities; @@ -36,7 +35,7 @@ CancellationToken cancellationToken { await using var dbContext = _dbContextFactory.CreateDbContext(); - return await _getQueryable(dbContext).AsQueryable() + return await _getQueryable(dbContext).AsNoTrackingWithIdentityResolution() .Where(entity => keys.Contains(entity.Id)) .ToDictionaryAsync( entity => entity.Id, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs index 7d324df0..3ee732b4 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs @@ -20,7 +20,7 @@ IDbContextFactory dbContextFactory options, dbContextFactory, (dbContext, ids) => - dbContext.GetHttpsResources.AsQueryable().Where(x => + dbContext.GetHttpsResources.AsNoTracking().Where(x => ids.Contains(x.ParentId ?? Guid.Empty) ), x => x.Id diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index a05f0286..f6d647c3 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -27,7 +27,7 @@ CancellationToken cancellationToken { if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) return null; return - await context.Users.AsQueryable() + await context.Users.AsNoTracking() .SingleOrDefaultAsync( u => claimsPrincipal.GetClaims(ClaimTypes.NameIdentifier).Contains(u.Subject), cancellationToken From 6ef2cf5a3fdf9d81e329ead0867883ddf9f361e6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Oct 2024 14:18:19 +0200 Subject: [PATCH 200/878] Subtract 1 from the maximum int value to avoid overflow (HotChocolate adds 1 to the page size and uses the resulting integer as limit for the SQL query) --- backend/src/Configuration/GraphQlConfiguration.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index db45c938..e14d3094 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -167,10 +167,11 @@ AppSettings appSettings // Data Loaders /* .AddDataLoader() */ // Paging + .AddDbContextCursorPagingProvider() .ModifyPagingOptions(options => { - options.MaxPageSize = int.MaxValue; - options.DefaultPageSize = int.MaxValue; + options.MaxPageSize = int.MaxValue - 1; + options.DefaultPageSize = int.MaxValue - 1; options.IncludeTotalCount = true; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. options.InferConnectionNameFromField = false; From 79c2b7f9f3af4d1e5a5b9b9650fa877b7fb52e90 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Oct 2024 14:37:19 +0200 Subject: [PATCH 201/878] Stabilize sorting order for paged and sorted queries --- inspired by https://chillicream.com/blog/2024/08/30/hot-chocolate-14/#pagination --- backend/src/Data/IData.cs | 3 ++- .../CalorimetricDataQueries.cs | 12 ++++++---- backend/src/GraphQl/DataX/DataQueries.cs | 14 +++++++---- backend/src/GraphQl/DataX/DataType.cs | 2 +- .../Extensions/SortingContextExtensions.cs | 24 +++++++++++++++++++ .../GeometricDataX/GeometricDataQueries.cs | 13 ++++++---- .../GetHttpsResourceQueries.cs | 13 ++++++---- .../HygrothermalDataQueries.cs | 12 ++++++---- .../OpticalDataX/OpticalDataQueries.cs | 12 ++++++---- .../PhotovoltaicDataQueries.cs | 12 ++++++---- 10 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 backend/src/GraphQl/Extensions/SortingContextExtensions.cs diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 4758df04..ec857a48 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using Database.Data; namespace Database.Data; -public interface IData +public interface IData : IEntity { Guid ComponentId { get; } string? Name { get; } diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index e0a5680e..27cf1fbc 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.CalorimetricDataX; @@ -20,11 +22,13 @@ public sealed class CalorimetricDataQueries public IQueryable GetAllCalorimetricData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { + sorting.StabilizeOrder(); // TODO Use `timestamp` and `locale`. - return context.CalorimetricData; + return context.CalorimetricData.AsNoTracking(); } public Task GetCalorimetricDataAsync( diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 8e0591df..8409faee 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -3,16 +3,18 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Data.Sorting; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; using Database.GraphQl.GeometricDataX; -using HotChocolate; -using HotChocolate.Data; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; +using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; @@ -26,9 +28,11 @@ public sealed class DataQueries public IEnumerable GetAllData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { + sorting.StabilizeOrder(); // TODO Use `timestamp` and `locale`. // return context.CalorimetricData.AsNoTracking(); // The union below does sadly not work because the different kinds of data have different include operations. diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 2d4d7a2a..af7d4d3d 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,5 +1,5 @@ -using Database.Data; using HotChocolate.Types; +using Database.Data; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs new file mode 100644 index 00000000..faf3c950 --- /dev/null +++ b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs @@ -0,0 +1,24 @@ +using System.Linq; +using HotChocolate.Data.Sorting; +using Database.Data; + +namespace Database.GraphQl.Extensions; + +public static class SortingContextExtensions +{ + public static void StabilizeOrder(this ISortingContext sorting) where T : IEntity + { + // this signals that the expression was not handled within the resolver + // and the sorting middleware should take over. + sorting.Handled(false); + sorting.OnAfterSortingApplied>( + static (sortingApplied, query) => + { + if (sortingApplied && query is IOrderedQueryable ordered) + { + return ordered.ThenBy(_ => _.Id); + } + return query.OrderBy(_ => _.Id); + }); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 5d74c38a..471566fb 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.GeometricDataX; @@ -19,10 +21,13 @@ public sealed class GeometricDataQueries public IQueryable GetAllGeometricData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { - return context.GeometricData; + sorting.StabilizeOrder(); + // TODO Use `timestamp` and `locale`. + return context.GeometricData.AsNoTracking(); } public Task GetGeometricDataAsync( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 629824d9..bd9781da 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -1,10 +1,13 @@ +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.GetHttpsResources; @@ -16,10 +19,12 @@ public sealed class GetHttpsResourceQueries [UseFiltering] [UseSorting] public IQueryable GetGetHttpsResources( - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { - return context.GetHttpsResources; + sorting.StabilizeOrder(); + return context.GetHttpsResources.AsNoTracking(); } public Task GetGetHttpsResourceAsync( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 5e066406..5d720249 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.HygrothermalDataX; @@ -20,11 +22,13 @@ public sealed class HygrothermalDataQueries public IQueryable GetAllHygrothermalData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { + sorting.StabilizeOrder(); // TODO Use `timestamp` and `locale`. - return context.HygrothermalData; + return context.HygrothermalData.AsNoTracking(); } public Task GetHygrothermalDataAsync( diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 278829db..0a05544d 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.OpticalDataX; @@ -20,11 +22,13 @@ public sealed class OpticalDataQueries public IQueryable GetAllOpticalData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { + sorting.StabilizeOrder(); // TODO Use `timestamp` and `locale`. - return context.OpticalData; + return context.OpticalData.AsNoTracking(); } public Task GetOpticalDataAsync( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index f26f383b..940c6dac 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -2,11 +2,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; +using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Guid = System.Guid; +using Database.Data; +using Database.GraphQl.Extensions; namespace Database.GraphQl.PhotovoltaicDataX; @@ -20,11 +22,13 @@ public sealed class PhotovoltaicDataQueries public IQueryable GetAllPhotovoltaicData( DateTime? timestamp, [GraphQLType] string? locale, - ApplicationDbContext context + ApplicationDbContext context, + ISortingContext sorting ) { + sorting.StabilizeOrder(); // TODO Use `timestamp` and `locale`. - return context.PhotovoltaicData; + return context.PhotovoltaicData.AsNoTracking(); } public Task GetPhotovoltaicDataAsync( From 955270705bed47a006ff2187ec407463d051209d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 28 Oct 2024 14:22:21 +0100 Subject: [PATCH 202/878] Remove flattened list of nodes from GraphQL connections in accordance with the Relay standard --- backend/src/Configuration/GraphQlConfiguration.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index e14d3094..f3e7f5e8 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -168,13 +168,14 @@ AppSettings appSettings /* .AddDataLoader() */ // Paging .AddDbContextCursorPagingProvider() - .ModifyPagingOptions(options => + .ModifyPagingOptions(_ => { - options.MaxPageSize = int.MaxValue - 1; - options.DefaultPageSize = int.MaxValue - 1; - options.IncludeTotalCount = true; + _.MaxPageSize = int.MaxValue - 1; + _.DefaultPageSize = int.MaxValue - 1; + _.IncludeTotalCount = true; + _.IncludeNodesField = false; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. - options.InferConnectionNameFromField = false; + _.InferConnectionNameFromField = false; } ) // Stitching From 481c230c5cb9868fa4c83b4ca4dd290ea92b9fa5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 6 Nov 2024 15:58:39 +0100 Subject: [PATCH 203/878] Use file polling when `NEXT_WEBPACK_USEPOLLING` is set to `true` in ./frontend/.env.local (needed for fast refresh to work on Windows) --- frontend/.env.development | 0 frontend/.env.local.sample | 1 + frontend/.env.test | 8 -------- frontend/environment.d.ts | 1 + frontend/next.config.js | 20 +++++++++++++++++--- 5 files changed, 19 insertions(+), 11 deletions(-) delete mode 100644 frontend/.env.development delete mode 100644 frontend/.env.test diff --git a/frontend/.env.development b/frontend/.env.development deleted file mode 100644 index e69de29b..00000000 diff --git a/frontend/.env.local.sample b/frontend/.env.local.sample index 15dc565d..d4a836e8 100644 --- a/frontend/.env.local.sample +++ b/frontend/.env.local.sample @@ -1,6 +1,7 @@ NEXT_PUBLIC_DATABASE_URL=https://local.solarbuildingenvelopes.com:5051 NEXT_PUBLIC_METABASE_URL=https://local.buildingenvelopedata.org:4041 NEXTAUTH_URL=https://local.solarbuildingenvelopes.com:5051 +NEXT_WEBPACK_USEPOLLING=false AUTH_CLIENT_ID=testlab-solar-facades AUTH_CLIENT_SECRET=secret diff --git a/frontend/.env.test b/frontend/.env.test deleted file mode 100644 index eb30bf27..00000000 --- a/frontend/.env.test +++ /dev/null @@ -1,8 +0,0 @@ -NEXT_PUBLIC_DATABASE_URL=https://local.solarbuildingenvelopes.com:5051 -NEXT_PUBLIC_METABASE_URL=https://local.buildingenvelopedata.org:4041 -NEXTAUTH_URL=https://local.solarbuildingenvelopes.com:5051 - -AUTH_CLIENT_ID=testlab-solar-facades -AUTH_CLIENT_SECRET=secret -AUTH_SECRET=secret -AUTH_JWT_SECRET=INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnw \ No newline at end of file diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index f0381185..8755fd84 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -4,6 +4,7 @@ declare global { NEXT_PUBLIC_DATABASE_URL: string; NEXT_PUBLIC_METABASE_URL: string; NEXTAUTH_URL: string; + NEXT_WEBPACK_USEPOLLING: string; NODE_ENV: "test" | "development" | "production"; AUTH_CLIENT_ID: string; AUTH_CLIENT_SECRET: string; diff --git a/frontend/next.config.js b/frontend/next.config.js index d2690548..e4ad2f97 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -1,5 +1,8 @@ -module.exports = { - webpack(config, options) { +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + swcMinify: true, + webpack: (config, options) => { config.module.rules.push({ test: /\.graphql$/, exclude: /node_modules/, @@ -18,6 +21,17 @@ module.exports = { use: "yaml-loader", }); + // Enable polling based on env variable being set + // Inspired by https://medium.com/mikkotikkanen/solving-next-js-fast-refresh-on-docker-windows-71dfdb3ee785 + if (process.env.NEXT_WEBPACK_USEPOLLING == "true" || false) { + config.watchOptions = { + poll: 500, + aggregateTimeout: 300 + } + } + return config; }, -}; +} + +module.exports = nextConfig; From 0b34cca473e749e2d9a042c0b3a29e34bf423436 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 6 Nov 2024 18:41:22 +0100 Subject: [PATCH 204/878] Upgrade frontend dependencies and node.js --- frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 6 +- frontend/lib/table.tsx | 2 +- frontend/package.json | 4 +- frontend/yarn.lock | 2412 ++++++++++++++++---------------- 5 files changed, 1215 insertions(+), 1211 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 81e1c4b4..87f9b209 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -6,7 +6,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:20.10-bookworm-slim +FROM node:22.11-bookworm-slim ARG GROUP_ID ARG USER_ID diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 294e1e5f..8031c923 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -5,7 +5,7 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:20.10-bookworm-slim AS dependencies +FROM node:22.11-bookworm-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -17,7 +17,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:20.10-bookworm-slim AS build +FROM node:22.11-bookworm-slim AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -30,7 +30,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:20.10-bookworm-slim +FROM node:22.11-bookworm-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index 1ca1de9f..66542f55 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -146,7 +146,7 @@ export function getInternallyLinkedFilterableStringColumnProps( (record, _highlightedValue, value) => value ? ( // TODO Why does this not work with `_highlightedValue`? An error is raised saying "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?": https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-function-component or https://reactjs.org/docs/forwarding-refs.html or https://deepscan.io/docs/rules/react-func-component-invalid-ref-prop or https://www.carlrippon.com/react-forwardref-typescript/ - {value} + ({value}) ) : ( <> ) diff --git a/frontend/package.json b/frontend/package.json index f9d170b3..e4729189 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,10 +6,10 @@ "license": "MIT", "scripts": { "codegen": "graphql-let && graphql-codegen --config codegen.yml", - "dev": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' next -p 8080", + "dev": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' next dev --port 8080", "build": "yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", - "start": "next start -p 8080" + "start": "next start --port 8080" }, "dependencies": { "@ant-design/pro-layout": "^7.10.3", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 72b8b4fa..3f98f8ae 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -10,17 +10,26 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.0.2.tgz#c5c753a467ce8d86ba7ca4736d2c01f599bb5492" - integrity sha512-7KJkhTiPiLHSu+LmMJnehfJ6242OCxSlR3xHVBecYxnMW8MS/878NXct1GqYARyL59fyeFdKRxXTfvR9SnDgJg== +"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" + integrity sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg== dependencies: "@ctrl/tinycolor" "^3.6.1" -"@ant-design/cssinjs@^1.11.1", "@ant-design/cssinjs@^1.21.0": - version "1.21.0" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.0.tgz#de7289bfd71c7a494a28b96569ad88f999619105" - integrity sha512-gIilraPl+9EoKdYxnupxjHB/Q6IHNRjEXszKbDxZdsgv4sAZ9pjkCq8yanDWNvyfjp4leir2OVAJm0vxwKK8YA== +"@ant-design/cssinjs-utils@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz#57abb43671023f937348bd33442862c60ac8e8b2" + integrity sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ== + dependencies: + "@ant-design/cssinjs" "^1.21.0" + "@babel/runtime" "^7.23.2" + rc-util "^5.38.0" + +"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": + version "1.21.1" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz#7320813c5f747e0cde52c388eff5198d78d57230" + integrity sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" @@ -28,66 +37,77 @@ classnames "^2.3.1" csstype "^3.1.3" rc-util "^5.35.0" - stylis "^4.0.13" + stylis "^4.3.3" + +"@ant-design/fast-color@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" + integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== + dependencies: + "@babel/runtime" "^7.24.7" "@ant-design/icons-svg@^4.4.0": version "4.4.2" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== -"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.3.7": - version "5.3.7" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.3.7.tgz#d9f3654bf7934ee5faba43f91b5a187f5309ec68" - integrity sha512-bCPXTAg66f5bdccM4TT21SQBDO1Ek2gho9h3nO9DAKXJP4sq+5VBjrQMSxMVXSB3HyEz+cUbHQ5+6ogxCOpaew== +"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.5.1": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.5.1.tgz#4ff57b2a0d3bafae3d990c2781fd857ead36c935" + integrity sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w== dependencies: "@ant-design/colors" "^7.0.0" "@ant-design/icons-svg" "^4.4.0" - "@babel/runtime" "^7.11.2" + "@babel/runtime" "^7.24.8" classnames "^2.2.6" rc-util "^5.31.1" "@ant-design/pro-layout@^7.10.3": - version "7.19.8" - resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.19.8.tgz#5d070ae2d74a232a657dca6fb207d097b9e8a621" - integrity sha512-f/vC83jmCjEDP7rrLBxRBqCK8298keSRrXqpd2RqigYlJpGqeKVGcevmk5ngBjDxz+UqjoNfOokG7cU3t3eIvQ== + version "7.21.1" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.21.1.tgz#1e686e14c6ee03f4d8b2bfa06b0954c032cc745a" + integrity sha512-9B+MBPTYZbhe5xdUxgfikoUZtB/77R3hAve8iI4fVNBoc4KAm/wpejasvKgWZn/8gRht8KOXfmfe2ngjz/BWOA== dependencies: + "@ant-design/cssinjs" "^1.21.1" "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.14.7" - "@ant-design/pro-utils" "2.15.14" + "@ant-design/pro-provider" "2.15.1" + "@ant-design/pro-utils" "2.16.1" "@babel/runtime" "^7.18.0" "@umijs/route-utils" "^4.0.0" "@umijs/use-params" "^1.0.9" classnames "^2.3.2" - lodash.merge "^4.6.2" + lodash "^4.17.21" + lodash-es "^4.17.21" omit.js "^2.0.2" - path-to-regexp "2.4.0" + path-to-regexp "8.0.0" rc-resize-observer "^1.1.0" rc-util "^5.0.6" swr "^2.0.0" warning "^4.0.3" -"@ant-design/pro-provider@2.14.7": - version "2.14.7" - resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.14.7.tgz#911dac05cd9c501ddb0800fd0141c29102a9c100" - integrity sha512-yEAOph/fTzIhOWMtYAbcsjffyYq8tL6IgMQqRlYZmIisVMRDZZ32F+Ab18a4S18foZiDChEr0e6NZuuY7du5rA== +"@ant-design/pro-provider@2.15.1": + version "2.15.1" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.15.1.tgz#5937a6a91d490792de372bdfa838889b3177537c" + integrity sha512-UiNhQ3Yl+h8liC24LLWpqbcdhC9ggiXvHeCYrqBHjOx2WNFWpWO5JP0327k3RQjShprt+jZQ/cdNHx6s7BToXA== dependencies: - "@ant-design/cssinjs" "^1.11.1" + "@ant-design/cssinjs" "^1.21.1" "@babel/runtime" "^7.18.0" "@ctrl/tinycolor" "^3.4.0" + dayjs "^1.11.10" rc-util "^5.0.1" swr "^2.0.0" -"@ant-design/pro-utils@2.15.14": - version "2.15.14" - resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.15.14.tgz#852c3727aa0c0058924fd539cc375022901c1cea" - integrity sha512-h/GdYDN19dTnyWvbfoEF5DyeTwkB+iRUXlhQ1O1NMkD2eOUpKTjVKF8J+Kekb7Bp7Fkf1dBVktVrcY8sioTkrw== +"@ant-design/pro-utils@2.16.1": + version "2.16.1" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.16.1.tgz#810b30feb2cde58abe9d0b07929b89724dde73c2" + integrity sha512-GaBEgP638fvddO0YqevuIT3O8pQIrqfHhlQ4UDnf1JkhVxuVxP3XdwDituQWnyRoxLPSjNBZBOBghBBMb8nWsg== dependencies: "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.14.7" + "@ant-design/pro-provider" "2.15.1" "@babel/runtime" "^7.18.0" classnames "^2.3.2" dayjs "^1.11.10" - lodash.merge "^4.6.2" + lodash "^4.17.21" + lodash-es "^4.17.21" rc-util "^5.0.6" safe-stable-stringify "^2.4.3" swr "^2.0.0" @@ -104,9 +124,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^3.7.8": - version "3.10.6" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.10.6.tgz#cf0bf034504c7b3d9984c985172d8af87f0ef639" - integrity sha512-3lLFGJtzC1/mEnK11BRf+Bf8536kBQUSB1G9yMtcRsxmY+tCKdTPzsP3fMUKy10BPIE0sDUY1pux3iMPIn2vow== + version "3.11.8" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.11.8.tgz#f6bacdc7e1b243807c1387113e1d445a53471a9c" + integrity sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -160,18 +180,19 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" - integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: - "@babel/highlight" "^7.24.7" + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.7.tgz#d23bbea508c3883ba8251fb4164982c36ea577ed" - integrity sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.25.9": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e" + integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== "@babel/core@7.15.5": version "7.15.5" @@ -195,77 +216,69 @@ source-map "^0.5.0" "@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.23.9": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.7.tgz#b676450141e0b52a3d43bc91da86aa608f950ac4" - integrity sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g== + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" + integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.24.7" - "@babel/generator" "^7.24.7" - "@babel/helper-compilation-targets" "^7.24.7" - "@babel/helper-module-transforms" "^7.24.7" - "@babel/helpers" "^7.24.7" - "@babel/parser" "^7.24.7" - "@babel/template" "^7.24.7" - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/code-frame" "^7.26.0" + "@babel/generator" "^7.26.0" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.0" + "@babel/parser" "^7.26.0" + "@babel/template" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.26.0" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.24.7", "@babel/generator@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.7.tgz#1654d01de20ad66b4b4d99c135471bc654c55e6d" - integrity sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA== +"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f" + integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw== dependencies: - "@babel/types" "^7.24.7" + "@babel/parser" "^7.26.2" + "@babel/types" "^7.26.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^2.5.1" + jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz#5373c7bc8366b12a033b4be1ac13a206c6656aab" - integrity sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg== +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== dependencies: - "@babel/types" "^7.24.7" + "@babel/types" "^7.25.9" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz#4eb6c4a80d6ffeac25ab8cd9a21b5dfa48d503a9" - integrity sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg== +"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" + integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== dependencies: - "@babel/compat-data" "^7.24.7" - "@babel/helper-validator-option" "^7.24.7" - browserslist "^4.22.2" + "@babel/compat-data" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz#2eaed36b3a1c11c53bdf80d53838b293c52f5b3b" - integrity sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.24.7" - "@babel/helper-environment-visitor" "^7.24.7" - "@babel/helper-function-name" "^7.24.7" - "@babel/helper-member-expression-to-functions" "^7.24.7" - "@babel/helper-optimise-call-expression" "^7.24.7" - "@babel/helper-replace-supers" "^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" - "@babel/helper-split-export-declaration" "^7.24.7" + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" + integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.25.9" semver "^6.3.1" -"@babel/helper-environment-visitor@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz#4b31ba9551d1f90781ba83491dd59cf9b269f7d9" - integrity sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ== - dependencies: - "@babel/types" "^7.24.7" - -"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.24.7": +"@babel/helper-function-name@^7.15.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== @@ -273,131 +286,121 @@ "@babel/template" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.24.7": +"@babel/helper-hoist-variables@^7.15.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== dependencies: "@babel/types" "^7.24.7" -"@babel/helper-member-expression-to-functions@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz#67613d068615a70e4ed5101099affc7a41c5225f" - integrity sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w== +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-module-imports@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" - integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== +"@babel/helper-module-imports@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz#31b6c9a2930679498db65b685b1698bfd6c7daf8" - integrity sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ== +"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" + integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== dependencies: - "@babel/helper-environment-visitor" "^7.24.7" - "@babel/helper-module-imports" "^7.24.7" - "@babel/helper-simple-access" "^7.24.7" - "@babel/helper-split-export-declaration" "^7.24.7" - "@babel/helper-validator-identifier" "^7.24.7" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-optimise-call-expression@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz#8b0a0456c92f6b323d27cfd00d1d664e76692a0f" - integrity sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A== +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== dependencies: - "@babel/types" "^7.24.7" + "@babel/types" "^7.25.9" "@babel/helper-plugin-utils@7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.8.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz#98c84fe6fe3d0d3ae7bfc3a5e166a46844feb2a0" - integrity sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" + integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== -"@babel/helper-replace-supers@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz#f933b7eed81a1c0265740edc91491ce51250f765" - integrity sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg== +"@babel/helper-replace-supers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" + integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== dependencies: - "@babel/helper-environment-visitor" "^7.24.7" - "@babel/helper-member-expression-to-functions" "^7.24.7" - "@babel/helper-optimise-call-expression" "^7.24.7" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.25.9" -"@babel/helper-simple-access@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" - integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== +"@babel/helper-simple-access@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" + integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-skip-transparent-expression-wrappers@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz#5f8fa83b69ed5c27adc56044f8be2b3ea96669d9" - integrity sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ== +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" -"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.24.7": +"@babel/helper-split-export-declaration@^7.15.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== dependencies: "@babel/types" "^7.24.7" -"@babel/helper-string-parser@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz#4d2d0f14820ede3b9807ea5fc36dfc8cd7da07f2" - integrity sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg== - -"@babel/helper-validator-identifier@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" - integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== -"@babel/helper-validator-option@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz#24c3bb77c7a425d1742eec8fb433b5a1b38e62f6" - integrity sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== -"@babel/helpers@^7.15.4", "@babel/helpers@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.7.tgz#aa2ccda29f62185acb5d42fb4a3a1b1082107416" - integrity sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg== - dependencies: - "@babel/template" "^7.24.7" - "@babel/types" "^7.24.7" +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" + integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== -"@babel/highlight@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" - integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== +"@babel/helpers@^7.15.4", "@babel/helpers@^7.26.0": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" + integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== dependencies: - "@babel/helper-validator-identifier" "^7.24.7" - chalk "^2.4.2" - js-tokens "^4.0.0" - picocolors "^1.0.0" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.0" "@babel/parser@7.15.5": version "7.15.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.7.tgz#9a5226f92f0c5c8ead550b750f5608e766c8ce85" - integrity sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11" + integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== + dependencies: + "@babel/types" "^7.26.0" "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -432,28 +435,42 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.8.3": +"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.7.tgz#d1759e84dd4b437cf9fae69b4c06c41d7625bfb7" - integrity sha512-9G8GYT/dxn/D1IIKOUBmGX0mnmj46mGH9NnZyJLwtCpgh5f7D2VbuKodb+2s9m1Yavh1s7ASQN8lf0eqrb1LTw== +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.25.9": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz#96507595c21b45fccfc2bc758d5c45452e6164fa" + integrity sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz#2a0b406b5871a20a841240586b1300ce2088a778" - integrity sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg== + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" + integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + +"@babel/plugin-syntax-import-attributes@^7.24.7": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" + integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -467,14 +484,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.24.7", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz#39a1fa4a7e3d3d7f34e2acc6be585b718d30e02d" - integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.25.9", "@babel/plugin-syntax-jsx@^7.7.2": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== @@ -488,7 +505,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.8.3": +"@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== @@ -516,7 +533,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-top-level-await@^7.8.3": +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== @@ -524,187 +548,185 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz#58d458271b4d3b6bb27ee6ac9525acbb259bad1c" - integrity sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz#4f6886c11e423bd69f3ce51dbf42424a5f275514" - integrity sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" + integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz#a4251d98ea0c0f399dafe1a35801eaba455bbf1f" - integrity sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" + integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz#42063e4deb850c7bd7c55e626bf4e7ab48e6ce02" - integrity sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" + integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-classes@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz#4ae6ef43a12492134138c1e45913f7c46c41b4bf" - integrity sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.24.7" - "@babel/helper-compilation-targets" "^7.24.7" - "@babel/helper-environment-visitor" "^7.24.7" - "@babel/helper-function-name" "^7.24.7" - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-replace-supers" "^7.24.7" - "@babel/helper-split-export-declaration" "^7.24.7" + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" + integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" + "@babel/traverse" "^7.25.9" globals "^11.1.0" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz#4cab3214e80bc71fae3853238d13d097b004c707" - integrity sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" + integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/template" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/template" "^7.25.9" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz#a097f25292defb6e6cc16d6333a4cfc1e3c72d9e" - integrity sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" + integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.7.tgz#ae454e62219288fbb734541ab00389bfb13c063e" - integrity sha512-cjRKJ7FobOH2eakx7Ja+KpJRj8+y+/SiB3ooYm/n2UJfxu0oEaOoxOinitkJcPqv9KxS0kxTGPUaR7L2XcXDXA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz#85879b42a8f5948fd6317069978e98f23ef8aec1" + integrity sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-flow" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-flow" "^7.25.9" "@babel/plugin-transform-for-of@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz#f25b33f72df1d8be76399e1b8f3f9d366eb5bc70" - integrity sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" + integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" "@babel/plugin-transform-function-name@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz#6d8601fbffe665c894440ab4470bc721dd9131d6" - integrity sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" + integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== dependencies: - "@babel/helper-compilation-targets" "^7.24.7" - "@babel/helper-function-name" "^7.24.7" - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-compilation-targets" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/traverse" "^7.25.9" "@babel/plugin-transform-literals@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz#36b505c1e655151a9d7607799a9988fc5467d06c" - integrity sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" + integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz#3b4454fb0e302e18ba4945ba3246acb1248315df" - integrity sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" + integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz#9fd5f7fdadee9085886b183f1ad13d1ab260f4ab" - integrity sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686" + integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg== dependencies: - "@babel/helper-module-transforms" "^7.24.7" - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-module-transforms" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-simple-access" "^7.25.9" "@babel/plugin-transform-object-super@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz#66eeaff7830bba945dd8989b632a40c04ed625be" - integrity sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" + integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-replace-supers" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-replace-supers" "^7.25.9" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz#5881f0ae21018400e320fc7eb817e529d1254b68" - integrity sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" + integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz#f0d2ed8380dfbed949c42d4d790266525d63bbdc" - integrity sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" + integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz#9caff79836803bc666bcfe210aeb6626230c293b" - integrity sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz#4b79746b59efa1f38c8695065a92a9f5afb24f7d" + integrity sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz#17cd06b75a9f0e2bd076503400e7c4b99beedac4" - integrity sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166" + integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw== dependencies: - "@babel/helper-annotate-as-pure" "^7.24.7" - "@babel/helper-module-imports" "^7.24.7" - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/plugin-syntax-jsx" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/types" "^7.25.9" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz#85448c6b996e122fa9e289746140aaa99da64e73" - integrity sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" + integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-spread@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz#e8a38c0fde7882e0fb8f160378f74bd885cc7bb3" - integrity sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" + integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" - "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz#a05debb4a9072ae8f985bcf77f3f215434c8f8c8" - integrity sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw== + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" + integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== dependencies: - "@babel/helper-plugin-utils" "^7.24.7" + "@babel/helper-plugin-utils" "^7.25.9" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" - integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" + integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.24.7", "@babel/template@^7.3.3": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315" - integrity sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig== +"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.24.7", "@babel/template@^7.25.9", "@babel/template@^7.3.3": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/parser" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" "@babel/traverse@7.15.4": version "7.15.4" @@ -721,30 +743,26 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.24.7": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.7.tgz#de2b900163fa741721ba382163fe46a936c40cf5" - integrity sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA== - dependencies: - "@babel/code-frame" "^7.24.7" - "@babel/generator" "^7.24.7" - "@babel/helper-environment-visitor" "^7.24.7" - "@babel/helper-function-name" "^7.24.7" - "@babel/helper-hoist-variables" "^7.24.7" - "@babel/helper-split-export-declaration" "^7.24.7" - "@babel/parser" "^7.24.7" - "@babel/types" "^7.24.7" +"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" + integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== + dependencies: + "@babel/code-frame" "^7.25.9" + "@babel/generator" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/template" "^7.25.9" + "@babel/types" "^7.25.9" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.3.3": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.7.tgz#6027fe12bc1aa724cd32ab113fb7f1988f1f66f2" - integrity sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q== +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" + integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== dependencies: - "@babel/helper-string-parser" "^7.24.7" - "@babel/helper-validator-identifier" "^7.24.7" - to-fast-properties "^2.0.0" + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" @@ -763,10 +781,10 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== -"@emnapi/runtime@^1.1.1": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.2.0.tgz#71d018546c3a91f3b51106530edbc056b9f2f2e3" - integrity sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ== +"@emnapi/runtime@^1.2.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" + integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== dependencies: tslib "^2.4.0" @@ -781,25 +799,30 @@ integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== "@eslint-community/eslint-utils@^4.2.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" - integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== dependencies: - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.6.1": - version "4.10.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" - integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== -"@eslint/config-array@^0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.16.0.tgz#bb3364fc39ee84ec3a62abdc4b8d988d99dfd706" - integrity sha512-/jmuSd74i4Czf1XXn7wGRWZCuyaUZ330NH1Bek0Pplatt4Sy1S5haN21SCLLdbeKslQ+S0wEJ+++v5YibSi+Lg== +"@eslint/config-array@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" + integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== dependencies: "@eslint/object-schema" "^2.1.4" debug "^4.3.1" - minimatch "^3.0.5" + minimatch "^3.1.2" + +"@eslint/core@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" + integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== "@eslint/eslintrc@^3.1.0": version "3.1.0" @@ -816,16 +839,23 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.5.0.tgz#0e9c24a670b8a5c86bff97b40be13d8d8f238045" - integrity sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w== +"@eslint/js@9.14.0": + version "9.14.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" + integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== "@eslint/object-schema@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== +"@eslint/plugin-kit@^0.2.0": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz#5eff371953bc13e3f4d88150e2c53959f64f74f6" + integrity sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw== + dependencies: + levn "^0.4.1" + "@graphql-codegen/add@^3.2.1": version "3.2.3" resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.2.3.tgz#f1ecee085987e7c21841edc4b1fd48877c663e1a" @@ -1378,128 +1408,146 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/retry@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.0.tgz#6d86b8cb322660f03d3f0aa94b99bdd8e172d570" - integrity sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew== - -"@img/sharp-darwin-arm64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.4.tgz#a1cf4a7febece334f16e0328b9689f05797d7aec" - integrity sha512-p0suNqXufJs9t3RqLBO6vvrgr5OhgbWp76s5gTRvdmxmuv9E1rcaqGUsl3l4mKVmXPkTkTErXediAui4x+8PSA== + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.0": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" + integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== + +"@img/sharp-darwin-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" + integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== optionalDependencies: - "@img/sharp-libvips-darwin-arm64" "1.0.2" + "@img/sharp-libvips-darwin-arm64" "1.0.4" -"@img/sharp-darwin-x64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.4.tgz#f77be2d7c3609d3e77cd337b199a772e07b87bd2" - integrity sha512-0l7yRObwtTi82Z6ebVI2PnHT8EB2NxBgpK2MiKJZJ7cz32R4lxd001ecMhzzsZig3Yv9oclvqqdV93jo9hy+Dw== +"@img/sharp-darwin-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" + integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== optionalDependencies: - "@img/sharp-libvips-darwin-x64" "1.0.2" + "@img/sharp-libvips-darwin-x64" "1.0.4" -"@img/sharp-libvips-darwin-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.2.tgz#b69f49fecbe9572378675769b189410721b0fa53" - integrity sha512-tcK/41Rq8IKlSaKRCCAuuY3lDJjQnYIW1UXU1kxcEKrfL8WR7N6+rzNoOxoQRJWTAECuKwgAHnPvqXGN8XfkHA== +"@img/sharp-libvips-darwin-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" + integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== -"@img/sharp-libvips-darwin-x64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.2.tgz#5665da7360d8e5ed7bee314491c8fe736b6a3c39" - integrity sha512-Ofw+7oaWa0HiiMiKWqqaZbaYV3/UGL2wAPeLuJTx+9cXpCRdvQhCLG0IH8YGwM0yGWGLpsF4Su9vM1o6aer+Fw== +"@img/sharp-libvips-darwin-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" + integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== -"@img/sharp-libvips-linux-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.2.tgz#8a05e5e9e9b760ff46561e32f19bd5e035fa881c" - integrity sha512-x7kCt3N00ofFmmkkdshwj3vGPCnmiDh7Gwnd4nUwZln2YjqPxV1NlTyZOvoDWdKQVDL911487HOueBvrpflagw== +"@img/sharp-libvips-linux-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" + integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== -"@img/sharp-libvips-linux-arm@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.2.tgz#0fd33b9bf3221948ce0ca7a5a725942626577a03" - integrity sha512-iLWCvrKgeFoglQxdEwzu1eQV04o8YeYGFXtfWU26Zr2wWT3q3MTzC+QTCO3ZQfWd3doKHT4Pm2kRmLbupT+sZw== +"@img/sharp-libvips-linux-arm@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" + integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== -"@img/sharp-libvips-linux-s390x@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.2.tgz#4b89150ec91b256ee2cbb5bb125321bf029a4770" - integrity sha512-cmhQ1J4qVhfmS6szYW7RT+gLJq9dH2i4maq+qyXayUSn9/3iY2ZeWpbAgSpSVbV2E1JUL2Gg7pwnYQ1h8rQIog== +"@img/sharp-libvips-linux-s390x@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" + integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== -"@img/sharp-libvips-linux-x64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.2.tgz#947ccc22ca5bc8c8cfe921b39a5fdaebc5e39f3f" - integrity sha512-E441q4Qdb+7yuyiADVi5J+44x8ctlrqn8XgkDTwr4qPJzWkaHwD489iZ4nGDgcuya4iMN3ULV6NwbhRZJ9Z7SQ== +"@img/sharp-libvips-linux-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" + integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== -"@img/sharp-libvips-linuxmusl-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.2.tgz#821d58ce774f0f8bed065b69913a62f65d512f2f" - integrity sha512-3CAkndNpYUrlDqkCM5qhksfE+qSIREVpyoeHIU6jd48SJZViAmznoQQLAv4hVXF7xyUB9zf+G++e2v1ABjCbEQ== +"@img/sharp-libvips-linuxmusl-arm64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" + integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== -"@img/sharp-libvips-linuxmusl-x64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.2.tgz#4309474bd8b728a61af0b3b4fad0c476b5f3ccbe" - integrity sha512-VI94Q6khIHqHWNOh6LLdm9s2Ry4zdjWJwH56WoiJU7NTeDwyApdZZ8c+SADC8OH98KWNQXnE01UdJ9CSfZvwZw== +"@img/sharp-libvips-linuxmusl-x64@1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" + integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== -"@img/sharp-linux-arm64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.4.tgz#bd390113e256487041411b988ded13a26cfc5f95" - integrity sha512-2800clwVg1ZQtxwSoTlHvtm9ObgAax7V6MTAB/hDT945Tfyy3hVkmiHpeLPCKYqYR1Gcmv1uDZ3a4OFwkdBL7Q== +"@img/sharp-linux-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" + integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== optionalDependencies: - "@img/sharp-libvips-linux-arm64" "1.0.2" + "@img/sharp-libvips-linux-arm64" "1.0.4" -"@img/sharp-linux-arm@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.4.tgz#14ecc81f38f75fb4cd7571bc83311746d6745fca" - integrity sha512-RUgBD1c0+gCYZGCCe6mMdTiOFS0Zc/XrN0fYd6hISIKcDUbAW5NtSQW9g/powkrXYm6Vzwd6y+fqmExDuCdHNQ== +"@img/sharp-linux-arm@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" + integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== optionalDependencies: - "@img/sharp-libvips-linux-arm" "1.0.2" + "@img/sharp-libvips-linux-arm" "1.0.5" -"@img/sharp-linux-s390x@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.4.tgz#119e8081e2c6741b5ac908fe02244e4c559e525f" - integrity sha512-h3RAL3siQoyzSoH36tUeS0PDmb5wINKGYzcLB5C6DIiAn2F3udeFAum+gj8IbA/82+8RGCTn7XW8WTFnqag4tQ== +"@img/sharp-linux-s390x@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" + integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== optionalDependencies: - "@img/sharp-libvips-linux-s390x" "1.0.2" + "@img/sharp-libvips-linux-s390x" "1.0.4" -"@img/sharp-linux-x64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.4.tgz#21d4c137b8da9a313b069ff5c920ded709f853d7" - integrity sha512-GoR++s0XW9DGVi8SUGQ/U4AeIzLdNjHka6jidVwapQ/JebGVQIpi52OdyxCNVRE++n1FCLzjDovJNozif7w/Aw== +"@img/sharp-linux-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" + integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== optionalDependencies: - "@img/sharp-libvips-linux-x64" "1.0.2" + "@img/sharp-libvips-linux-x64" "1.0.4" -"@img/sharp-linuxmusl-arm64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.4.tgz#f3fde68fd67b85a32da6f1155818c3b58b8e7ae0" - integrity sha512-nhr1yC3BlVrKDTl6cO12gTpXMl4ITBUZieehFvMntlCXFzH2bvKG76tBL2Y/OqhupZt81pR7R+Q5YhJxW0rGgQ== +"@img/sharp-linuxmusl-arm64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" + integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" -"@img/sharp-linuxmusl-x64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.4.tgz#44373724aecd7b69900e0578228144e181db7892" - integrity sha512-uCPTku0zwqDmZEOi4ILyGdmW76tH7dm8kKlOIV1XC5cLyJ71ENAAqarOHQh0RLfpIpbV5KOpXzdU6XkJtS0daw== +"@img/sharp-linuxmusl-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" + integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64" "1.0.2" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" -"@img/sharp-wasm32@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.4.tgz#88e3f18d7e7cd8cfe1af98e9963db4d7b6491435" - integrity sha512-Bmmauh4sXUsUqkleQahpdNXKvo+wa1V9KhT2pDA4VJGKwnKMJXiSTGphn0gnJrlooda0QxCtXc6RX1XAU6hMnQ== +"@img/sharp-wasm32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" + integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== dependencies: - "@emnapi/runtime" "^1.1.1" + "@emnapi/runtime" "^1.2.0" -"@img/sharp-win32-ia32@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.4.tgz#b1c772dd2952e983980b1eb85808fa8129484d46" - integrity sha512-99SJ91XzUhYHbx7uhK3+9Lf7+LjwMGQZMDlO/E/YVJ7Nc3lyDFZPGhjwiYdctoH2BOzW9+TnfqcaMKt0jHLdqw== +"@img/sharp-win32-ia32@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" + integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== -"@img/sharp-win32-x64@0.33.4": - version "0.33.4" - resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.4.tgz#106f911134035b4157ec92a0c154a6b6f88fa4c1" - integrity sha512-3QLocdTRVIrFNye5YocZl+KKpYKP+fksi1QhmOArgx7GyhIbQp/WrJRu176jm8IxromS7RIkzMiMINVdBtC8Aw== +"@img/sharp-win32-x64@0.33.5": + version "0.33.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" + integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1761,9 +1809,9 @@ integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -1781,62 +1829,62 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@next/env@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.6.tgz#c1148e2e1aa166614f05161ee8f77ded467062bc" - integrity sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw== +"@next/env@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.7.tgz#5006f4460a7fa598a03e1c2aa4e59e45c71082d3" + integrity sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA== -"@next/eslint-plugin-next@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.6.tgz#cf279b94ddc7de49af8e8957f0c3b7349bc489bf" - integrity sha512-ng7pU/DDsxPgT6ZPvuprxrkeew3XaRf4LAT4FabaEO/hAbvVx4P7wqnqdbTdDn1kgTvsI4tpIgT4Awn/m0bGbg== +"@next/eslint-plugin-next@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.7.tgz#9a8cd86a7a27b8f370ec3b130e598688c869bdc6" + integrity sha512-c4vuEOOXeib4js5gDq+zFqAAdRGXX6T0d+zFETiQkRwy7vyj5lBov1dW0Z09nDst2lvxo7VEcKrQMUBH5Vgx7Q== dependencies: glob "7.1.7" -"@next/swc-darwin-arm64@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.6.tgz#b15d139d8971360fca29be3bdd703c108c9a45fb" - integrity sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA== - -"@next/swc-darwin-x64@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz#9c72ee31cc356cb65ce6860b658d807ff39f1578" - integrity sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA== - -"@next/swc-linux-arm64-gnu@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz#59f5f66155e85380ffa26ee3d95b687a770cfeab" - integrity sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg== - -"@next/swc-linux-arm64-musl@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz#f012518228017052736a87d69bae73e587c76ce2" - integrity sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q== - -"@next/swc-linux-x64-gnu@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz#339b867a7e9e7ee727a700b496b269033d820df4" - integrity sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw== - -"@next/swc-linux-x64-musl@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz#ae0ae84d058df758675830bcf70ca1846f1028f2" - integrity sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ== - -"@next/swc-win32-arm64-msvc@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz#a5cc0c16920485a929a17495064671374fdbc661" - integrity sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg== - -"@next/swc-win32-ia32-msvc@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz#6a2409b84a2cbf34bf92fe714896455efb4191e4" - integrity sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg== - -"@next/swc-win32-x64-msvc@13.5.6": - version "13.5.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz#4a3e2a206251abc729339ba85f60bc0433c2865d" - integrity sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ== +"@next/swc-darwin-arm64@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.7.tgz#b99b91c04a884ba1272a3bd5db2b6f47a5bb10c2" + integrity sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g== + +"@next/swc-darwin-x64@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.7.tgz#0a30d1c40430ed09ef4384bd90148e68badbf5e5" + integrity sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g== + +"@next/swc-linux-arm64-gnu@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.7.tgz#f6464e423186494d44ae9544c76b50e661682bc0" + integrity sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg== + +"@next/swc-linux-arm64-musl@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.7.tgz#88b62e006d00fc31359723f96cbd601132812873" + integrity sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg== + +"@next/swc-linux-x64-gnu@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.7.tgz#68d31a7c75f1b0dbc408f9fe49ac878c6723446c" + integrity sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw== + +"@next/swc-linux-x64-musl@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.7.tgz#09c9c3c667abd55f2af0b4e464342350baeabdb3" + integrity sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA== + +"@next/swc-win32-arm64-msvc@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.7.tgz#3314966f960a22ee95adb8285e0927c9e4ba7205" + integrity sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA== + +"@next/swc-win32-ia32-msvc@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.7.tgz#fd118f3bd5a87453b252eb3c5fae54ddce035026" + integrity sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA== + +"@next/swc-win32-x64-msvc@13.5.7": + version "13.5.7" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.7.tgz#3eff03a5a80281449c58fece85a780c1e6e3594b" + integrity sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1851,7 +1899,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -1859,93 +1907,104 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@parcel/watcher-android-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz#c2c19a3c442313ff007d2d7a9c2c1dd3e1c9ca84" - integrity sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg== +"@nolyfill/is-core-module@1.0.39": + version "1.0.39" + resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" + integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@parcel/watcher-darwin-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz#c817c7a3b4f3a79c1535bfe54a1c2818d9ffdc34" - integrity sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA== +"@parcel/watcher-android-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" + integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== -"@parcel/watcher-darwin-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz#1a3f69d9323eae4f1c61a5f480a59c478d2cb020" - integrity sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg== +"@parcel/watcher-darwin-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f" + integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== -"@parcel/watcher-freebsd-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz#0d67fef1609f90ba6a8a662bc76a55fc93706fc8" - integrity sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w== +"@parcel/watcher-darwin-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb" + integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== -"@parcel/watcher-linux-arm-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz#ce5b340da5829b8e546bd00f752ae5292e1c702d" - integrity sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA== +"@parcel/watcher-freebsd-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82" + integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== -"@parcel/watcher-linux-arm64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz#6d7c00dde6d40608f9554e73998db11b2b1ff7c7" - integrity sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA== +"@parcel/watcher-linux-arm-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42" + integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== -"@parcel/watcher-linux-arm64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz#bd39bc71015f08a4a31a47cd89c236b9d6a7f635" - integrity sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA== +"@parcel/watcher-linux-arm-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4" + integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== -"@parcel/watcher-linux-x64-glibc@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz#0ce29966b082fb6cdd3de44f2f74057eef2c9e39" - integrity sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg== +"@parcel/watcher-linux-arm64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03" + integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== -"@parcel/watcher-linux-x64-musl@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz#d2ebbf60e407170bb647cd6e447f4f2bab19ad16" - integrity sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ== +"@parcel/watcher-linux-arm64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732" + integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== -"@parcel/watcher-win32-arm64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz#eb4deef37e80f0b5e2f215dd6d7a6d40a85f8adc" - integrity sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg== +"@parcel/watcher-linux-x64-glibc@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== -"@parcel/watcher-win32-ia32@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz#94fbd4b497be39fd5c8c71ba05436927842c9df7" - integrity sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw== +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== -"@parcel/watcher-win32-x64@2.4.1": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz#4bf920912f67cae5f2d264f58df81abfea68dadf" - integrity sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A== +"@parcel/watcher-win32-arm64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154" + integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== + +"@parcel/watcher-win32-ia32@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220" + integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== + +"@parcel/watcher-win32-x64@2.5.0": + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7" + integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== "@parcel/watcher@^2.1.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.4.1.tgz#a50275151a1bb110879c6123589dba90c19f1bf8" - integrity sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA== + version "2.5.0" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10" + integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.4.1" - "@parcel/watcher-darwin-arm64" "2.4.1" - "@parcel/watcher-darwin-x64" "2.4.1" - "@parcel/watcher-freebsd-x64" "2.4.1" - "@parcel/watcher-linux-arm-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-glibc" "2.4.1" - "@parcel/watcher-linux-arm64-musl" "2.4.1" - "@parcel/watcher-linux-x64-glibc" "2.4.1" - "@parcel/watcher-linux-x64-musl" "2.4.1" - "@parcel/watcher-win32-arm64" "2.4.1" - "@parcel/watcher-win32-ia32" "2.4.1" - "@parcel/watcher-win32-x64" "2.4.1" - -"@peculiar/asn1-schema@^2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" - integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== + "@parcel/watcher-android-arm64" "2.5.0" + "@parcel/watcher-darwin-arm64" "2.5.0" + "@parcel/watcher-darwin-x64" "2.5.0" + "@parcel/watcher-freebsd-x64" "2.5.0" + "@parcel/watcher-linux-arm-glibc" "2.5.0" + "@parcel/watcher-linux-arm-musl" "2.5.0" + "@parcel/watcher-linux-arm64-glibc" "2.5.0" + "@parcel/watcher-linux-arm64-musl" "2.5.0" + "@parcel/watcher-linux-x64-glibc" "2.5.0" + "@parcel/watcher-linux-x64-musl" "2.5.0" + "@parcel/watcher-win32-arm64" "2.5.0" + "@parcel/watcher-win32-ia32" "2.5.0" + "@parcel/watcher-win32-x64" "2.5.0" + +"@peculiar/asn1-schema@^2.3.13", "@peculiar/asn1-schema@^2.3.8": + version "2.3.13" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz#ec8509cdcbc0da3abe73fd7e690556b57a61b8f4" + integrity sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g== dependencies: asn1js "^3.0.5" pvtsutils "^1.3.5" @@ -1976,13 +2035,13 @@ dependencies: "@babel/runtime" "^7.24.4" -"@rc-component/color-picker@~1.5.3": - version "1.5.3" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-1.5.3.tgz#f3b0e14bb67ec5ee77d1fd5d261f63dd4fd00449" - integrity sha512-+tGGH3nLmYXTalVe0L8hSZNs73VTP5ueSHwUlDC77KKRaN7G4DS4wcpG5DTDzdcV/Yas+rzA6UGgIyzd8fS4cw== +"@rc-component/color-picker@~2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" + integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== dependencies: + "@ant-design/fast-color" "^2.0.6" "@babel/runtime" "^7.23.6" - "@ctrl/tinycolor" "^3.6.1" classnames "^2.2.6" rc-util "^5.38.1" @@ -2019,10 +2078,19 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/tour@~1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.0.tgz#37a66ae5af8eefaf0ab0e22ddd8e6fecdbdc14a7" - integrity sha512-h6hyILDwL+In9GAgRobwRWihLqqsD7Uft3fZGrJ7L4EiyCoxbnNYwzPXDfz7vNDhWeVyvAWQJj9fJCzpI4+b4g== +"@rc-component/qrcode@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.0.0.tgz#48a8de5eb11d0e65926f1377c4b1ef4c888997f5" + integrity sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== + dependencies: + "@babel/runtime" "^7.24.7" + classnames "^2.3.2" + rc-util "^5.38.0" + +"@rc-component/tour@~1.15.1": + version "1.15.1" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" + integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/portal" "^1.0.0-9" @@ -2030,10 +2098,10 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.0.tgz#503a48b0895a2cfddee0a5b7b11492c3df2a493d" - integrity sha512-QarBCji02YE9aRFhZgRZmOpXBj0IZutRippsVBv85sxvG4FGk/vRxwAlkn3MS9zK5mwbETd86mAVg2tKqTkdJA== +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.3": + version "2.2.3" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" + integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== dependencies: "@babel/runtime" "^7.23.2" "@rc-component/portal" "^1.1.0" @@ -2052,10 +2120,15 @@ resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== +"@rtsao/scc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== + "@rushstack/eslint-patch@^1.3.3": - version "1.10.3" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz#391d528054f758f81e53210f1a1eebcf1a8b1d20" - integrity sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg== + version "1.10.4" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1" + integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA== "@sinclair/typebox@^0.27.8": version "0.27.8" @@ -2141,6 +2214,11 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + "@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" @@ -2180,27 +2258,34 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== -"@types/json-schema@^7.0.6": +"@types/json-schema@^7.0.15", "@types/json-schema@^7.0.6": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json-stable-stringify@^1.0.32": - version "1.0.36" - resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz#fe6c6001a69ff8160a772da08779448a333c7ddd" - integrity sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz#41393e6b7a9a67221607346af4a79783aeb28aea" + integrity sha512-ESTsHWB72QQq+pjUFIbEz9uSCZppD31YrVkbt2rnUciTYEvcwN6uZIhX5JZeBHqRlFJ41x/7MewCs7E2Qux6Cg== "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*", "@types/node@^20.14.9": - version "20.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" - integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== +"@types/node@*": + version "22.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + dependencies: + undici-types "~6.19.8" + +"@types/node@^20.14.9": + version "20.17.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.6.tgz#6e4073230c180d3579e8c60141f99efdf5df0081" + integrity sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ== dependencies: - undici-types "~5.26.4" + undici-types "~6.19.2" "@types/parse-json@^4.0.0": version "4.0.2" @@ -2208,9 +2293,9 @@ integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== "@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== "@types/qrcode.react@^1.0": version "1.0.5" @@ -2220,9 +2305,9 @@ "@types/react" "*" "@types/react-dom@^18.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== + version "18.3.1" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" + integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== dependencies: "@types/react" "*" @@ -2241,9 +2326,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^18.0": - version "18.3.3" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.3.tgz#9679020895318b0915d7a3ab004d92d33375c45f" - integrity sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw== + version "18.3.12" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" + integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== dependencies: "@types/prop-types" "*" csstype "^3.0.2" @@ -2254,9 +2339,9 @@ integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/ws@^8.0.0": - version "8.5.10" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + version "8.5.13" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" + integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== dependencies: "@types/node" "*" @@ -2273,9 +2358,9 @@ "@types/yargs-parser" "*" "@types/yargs@^17.0.8": - version "17.0.32" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229" - integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog== + version "17.0.33" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" + integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== dependencies: "@types/yargs-parser" "*" @@ -2408,16 +2493,16 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.3.3" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" - integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + version "8.3.4" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.12.0, acorn@^8.4.1: - version "8.12.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" - integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== +acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.1" @@ -2481,58 +2566,59 @@ ansi-styles@^5.0.0: integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== antd@^5.0: - version "5.18.3" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.18.3.tgz#71455216af07da202bddfa8cd16976e29dcf575c" - integrity sha512-Dm3P8HBxoo/DiR/QZLj5Mk+rQZsSXxCCArSZACHGiklkkjW6klzlebAElOUr9NyDeFX7UnQ6LVk7vznXlnjTqQ== - dependencies: - "@ant-design/colors" "^7.0.2" - "@ant-design/cssinjs" "^1.21.0" - "@ant-design/icons" "^5.3.7" + version "5.21.6" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.21.6.tgz#60d8eb442f7f8019bab198e30ea5a8d6d224a6ce" + integrity sha512-EviOde/VEu+OsIKH5t6YXTMmmNeg9R85m0W5zXAo+Np8Latg9q10691JvAqOTMpnrRmbdeKUQL1Krp69Bzbe/g== + dependencies: + "@ant-design/colors" "^7.1.0" + "@ant-design/cssinjs" "^1.21.1" + "@ant-design/cssinjs-utils" "^1.1.1" + "@ant-design/icons" "^5.5.1" "@ant-design/react-slick" "~1.1.2" - "@babel/runtime" "^7.24.7" + "@babel/runtime" "^7.25.6" "@ctrl/tinycolor" "^3.6.1" - "@rc-component/color-picker" "~1.5.3" + "@rc-component/color-picker" "~2.0.1" "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/tour" "~1.15.0" - "@rc-component/trigger" "^2.2.0" + "@rc-component/qrcode" "~1.0.0" + "@rc-component/tour" "~1.15.1" + "@rc-component/trigger" "^2.2.3" classnames "^2.5.1" copy-to-clipboard "^3.3.3" dayjs "^1.11.11" - qrcode.react "^3.1.0" - rc-cascader "~3.26.0" + rc-cascader "~3.28.2" rc-checkbox "~3.3.0" - rc-collapse "~3.7.3" - rc-dialog "~9.5.2" + rc-collapse "~3.8.0" + rc-dialog "~9.6.0" rc-drawer "~7.2.0" rc-dropdown "~4.2.0" - rc-field-form "~2.2.1" - rc-image "~7.9.0" - rc-input "~1.5.1" - rc-input-number "~9.1.0" - rc-mentions "~2.14.0" - rc-menu "~9.14.0" - rc-motion "^2.9.2" - rc-notification "~5.6.0" - rc-pagination "~4.0.4" - rc-picker "~4.5.0" + rc-field-form "~2.4.0" + rc-image "~7.11.0" + rc-input "~1.6.3" + rc-input-number "~9.2.0" + rc-mentions "~2.16.1" + rc-menu "~9.15.1" + rc-motion "^2.9.3" + rc-notification "~5.6.2" + rc-pagination "~4.3.0" + rc-picker "~4.6.15" rc-progress "~4.0.0" rc-rate "~2.13.0" rc-resize-observer "^1.4.0" - rc-segmented "~2.3.0" - rc-select "~14.14.0" - rc-slider "~10.6.2" + rc-segmented "~2.5.0" + rc-select "~14.15.2" + rc-slider "~11.1.7" rc-steps "~6.0.1" rc-switch "~4.1.0" - rc-table "~7.45.7" - rc-tabs "~15.1.1" - rc-textarea "~1.7.0" - rc-tooltip "~6.2.0" - rc-tree "~5.8.8" - rc-tree-select "~5.21.0" - rc-upload "~4.5.2" + rc-table "~7.47.5" + rc-tabs "~15.3.0" + rc-textarea "~1.8.2" + rc-tooltip "~6.2.1" + rc-tree "~5.9.0" + rc-tree-select "~5.23.0" + rc-upload "~4.8.1" rc-util "^5.43.0" scroll-into-view-if-needed "^3.1.0" - throttle-debounce "^5.0.0" + throttle-debounce "^5.0.2" anymatch@^3.0.3: version "3.1.3" @@ -2559,14 +2645,12 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@~5.1.3: - version "5.1.3" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== - dependencies: - deep-equal "^2.0.5" +aria-query@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== -array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: +array-buffer-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== @@ -2579,7 +2663,7 @@ array-find-index@^1.0.2: resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== -array-includes@^3.1.6, array-includes@^3.1.7, array-includes@^3.1.8: +array-includes@^3.1.6, array-includes@^3.1.8: version "3.1.8" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== @@ -2613,7 +2697,7 @@ array.prototype.findlast@^1.2.5: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.findlastindex@^1.2.3: +array.prototype.findlastindex@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== @@ -2645,16 +2729,6 @@ array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.toreversed@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" - integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - array.prototype.tosorted@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" @@ -2685,7 +2759,7 @@ asap@^2.0.0, asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -asn1js@^3.0.1, asn1js@^3.0.5: +asn1js@^3.0.5: version "3.0.5" resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== @@ -2716,17 +2790,15 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axe-core@^4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" - integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== +axe-core@^4.10.0: + version "4.10.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" + integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== -axobject-query@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1" - integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg== - dependencies: - deep-equal "^2.0.5" +axobject-query@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" + integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== babel-jest@27.1.0: version "27.1.0" @@ -2792,22 +2864,25 @@ babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + version "1.1.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" + integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-import-attributes" "^7.24.7" + "@babel/plugin-syntax-import-meta" "^7.10.4" "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" babel-preset-fbjs@^3.4.0: version "3.4.0" @@ -2904,15 +2979,15 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.22.2: - version "4.23.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.1.tgz#ce4af0534b3d37db5c1a4ca98b9080f985041e96" - integrity sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw== +browserslist@^4.24.0: + version "4.24.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" + integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== dependencies: - caniuse-lite "^1.0.30001629" - electron-to-chromium "^1.4.796" - node-releases "^2.0.14" - update-browserslist-db "^1.0.16" + caniuse-lite "^1.0.30001669" + electron-to-chromium "^1.5.41" + node-releases "^2.0.18" + update-browserslist-db "^1.1.1" bser@2.1.1: version "2.1.1" @@ -2980,10 +3055,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001629: - version "1.0.30001637" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001637.tgz#d9fab531493d9ef46a8ff305e9812190ac463f21" - integrity sha512-1x0qRI1mD1o9e+7mBI7XtzFAP4XszbHaVWsMiGbSPLYekKTJF7K+FNk6AsXH4sUpc+qrsI3pVgf1Jdl/uGkuSQ== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001669: + version "1.0.30001677" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz#27c2e2c637e007cfa864a16f7dfe7cde66b38b5f" + integrity sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog== capital-case@^1.0.4: version "1.0.4" @@ -2994,7 +3069,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -3077,9 +3152,9 @@ ci-info@^3.2.0: integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" - integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + version "1.4.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" + integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: version "2.5.1" @@ -3238,10 +3313,10 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== +cookie@^0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== copy-to-clipboard@^3.3.3: version "3.3.3" @@ -3306,9 +3381,9 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" crypto-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-3.0.0.tgz#1f217b7134c5e05adc013d1edce67c64fa459f7f" - integrity sha512-5l5xGtzuvGTU28GXxGV1JYVFou68buZWpkV1Fx5hIDRPnfbQ8KzabTlNIuDIeSCYGVPFehupzDqlnbXm2IXmdQ== + version "3.1.0" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-3.1.0.tgz#8be4497e1feee8e75dfc545aa5348fb11ae167c5" + integrity sha512-HR8JlZ+Dn54Lc5gGWZJxJitWbOCUzWb9/AlyONGecBnYZ+n/ONvt0gQnEzDNpJMYeRrYO7KogQ4qwyTPKnWKEw== csstype@^3.0.2, csstype@^3.1.3: version "3.1.3" @@ -3353,21 +3428,21 @@ dataloader@^2.2.2: integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: - version "1.11.11" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e" - integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg== + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== debounce@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@^3.1.0, debug@^3.2.7: version "3.2.7" @@ -3391,30 +3466,6 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== -deep-equal@^2.0.5: - version "2.2.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" - integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.5" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.2" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.13" - deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3521,14 +3572,14 @@ dotenv@^16.0.0: integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== dset@^3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" - integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== + version "3.1.4" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" + integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== -electron-to-chromium@^1.4.796: - version "1.4.812" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.812.tgz#21b78709c5a13af5d5c688d135a22dcea7617acf" - integrity sha512-7L8fC2Ey/b6SePDFKR2zHAy4mbdp1/38Yk5TsARO66W3hC5KEaeKMMHoxwtuH+jcu2AYLSn9QX04i95t6Fl1Hg== +electron-to-chromium@^1.5.41: + version "1.5.52" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz#2bed832c95a56a195504f918150e548474687da8" + integrity sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ== emittery@^0.13.1: version "0.13.1" @@ -3550,10 +3601,10 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enhanced-resolve@^5.12.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== +enhanced-resolve@^5.15.0: + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3629,25 +3680,10 @@ es-errors@^1.2.1, es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - -es-iterator-helpers@^1.0.19: - version "1.0.19" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz#117003d0e5fec237b4b5c08aded722e0c6d50ca8" - integrity sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw== +es-iterator-helpers@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" + integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== dependencies: call-bind "^1.0.7" define-properties "^1.2.1" @@ -3656,12 +3692,13 @@ es-iterator-helpers@^1.0.19: es-set-tostringtag "^2.0.3" function-bind "^1.1.2" get-intrinsic "^1.2.4" - globalthis "^1.0.3" + globalthis "^1.0.4" + gopd "^1.0.1" has-property-descriptors "^1.0.2" has-proto "^1.0.3" has-symbols "^1.0.3" internal-slot "^1.0.7" - iterator.prototype "^1.1.2" + iterator.prototype "^1.1.3" safe-array-concat "^1.1.2" es-object-atoms@^1.0.0: @@ -3696,10 +3733,10 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -escalade@^3.1.1, escalade@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.5: version "1.0.5" @@ -3717,11 +3754,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^13.0.0: - version "13.5.6" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.5.6.tgz#3a5a6222d5cb32256760ad68ab8e976e866a08c8" - integrity sha512-o8pQsUHTo9aHqJ2YiZDym5gQAMRf7O2HndHo/JZeY7TDD+W4hk6Ma8Vw54RHiBeb7OWWO5dPirQB+Is/aVQ7Kg== + version "13.5.7" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.5.7.tgz#fc5d86b22364c93d9279acab2a6f4848c4dbccaf" + integrity sha512-pdeUuL9KZ8qFzzKqCbxk6FXwG9dNEnot/3+qSFJqxdSGgkFUH8cgZus/meyCi2S0cTAsDbBEE030E6zvL9pUYQ== dependencies: - "@next/eslint-plugin-next" "13.5.6" + "@next/eslint-plugin-next" "13.5.7" "@rushstack/eslint-patch" "^1.3.3" "@typescript-eslint/parser" "^5.4.2 || ^6.0.0" eslint-import-resolver-node "^0.3.6" @@ -3741,138 +3778,144 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: resolve "^1.22.4" eslint-import-resolver-typescript@^3.5.2: - version "3.6.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz#7b983680edd3f1c5bce1a5829ae0bc2d57fe9efa" - integrity sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg== - dependencies: - debug "^4.3.4" - enhanced-resolve "^5.12.0" - eslint-module-utils "^2.7.4" - fast-glob "^3.3.1" - get-tsconfig "^4.5.0" - is-core-module "^2.11.0" + version "3.6.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e" + integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA== + dependencies: + "@nolyfill/is-core-module" "1.0.39" + debug "^4.3.5" + enhanced-resolve "^5.15.0" + eslint-module-utils "^2.8.1" + fast-glob "^3.3.2" + get-tsconfig "^4.7.5" + is-bun-module "^1.0.2" is-glob "^4.0.3" -eslint-module-utils@^2.7.4, eslint-module-utils@^2.8.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz#52f2404300c3bd33deece9d7372fb337cc1d7c34" - integrity sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q== +eslint-module-utils@^2.12.0, eslint-module-utils@^2.8.1: + version "2.12.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" + integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== dependencies: debug "^3.2.7" eslint-plugin-import@^2.28.1: - version "2.29.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" - integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== + version "2.31.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" + integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== dependencies: - array-includes "^3.1.7" - array.prototype.findlastindex "^1.2.3" + "@rtsao/scc" "^1.1.0" + array-includes "^3.1.8" + array.prototype.findlastindex "^1.2.5" array.prototype.flat "^1.3.2" array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.8.0" - hasown "^2.0.0" - is-core-module "^2.13.1" + eslint-module-utils "^2.12.0" + hasown "^2.0.2" + is-core-module "^2.15.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.7" - object.groupby "^1.0.1" - object.values "^1.1.7" + object.fromentries "^2.0.8" + object.groupby "^1.0.3" + object.values "^1.2.0" semver "^6.3.1" + string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" eslint-plugin-jsx-a11y@^6.7.1: - version "6.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz#67ab8ff460d4d3d6a0b4a570e9c1670a0a8245c8" - integrity sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g== + version "6.10.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" + integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== dependencies: - aria-query "~5.1.3" + aria-query "^5.3.2" array-includes "^3.1.8" array.prototype.flatmap "^1.3.2" ast-types-flow "^0.0.8" - axe-core "^4.9.1" - axobject-query "~3.1.1" + axe-core "^4.10.0" + axobject-query "^4.1.0" damerau-levenshtein "^1.0.8" emoji-regex "^9.2.2" - es-iterator-helpers "^1.0.19" hasown "^2.0.2" jsx-ast-utils "^3.3.5" language-tags "^1.0.9" minimatch "^3.1.2" object.fromentries "^2.0.8" safe-regex-test "^1.0.3" - string.prototype.includes "^2.0.0" + string.prototype.includes "^2.0.1" "eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": - version "4.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" - integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== + version "5.0.0-canary-7118f5dd7-20230705" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" + integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== eslint-plugin-react@^7.33.2: - version "7.34.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz#9965f27bd1250a787b5d4cfcc765e5a5d58dcb7b" - integrity sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA== + version "7.37.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" + integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" - array.prototype.toreversed "^1.1.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.0.19" + es-iterator-helpers "^1.1.0" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" object.entries "^1.1.8" object.fromentries "^2.0.8" - object.hasown "^1.1.4" object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" -eslint-scope@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.0.1.tgz#a9601e4b81a0b9171657c343fb13111688963cfc" - integrity sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og== +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: +eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" - integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== eslint@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.5.0.tgz#11856034b94a9e1a02cfcc7e96a9f0956963cd2f" - integrity sha512-+NAOZFrW/jFTS3dASCGBxX1pkFD0/fsO+hfAkJ4TyYKwgsXZbqzrw+seCYFCcPCYXvnD67tAnglU7GQTz6kcVw== + version "9.14.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" + integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/config-array" "^0.16.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.18.0" + "@eslint/core" "^0.7.0" "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.5.0" + "@eslint/js" "9.14.0" + "@eslint/plugin-kit" "^0.2.0" + "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.3.0" - "@nodelib/fs.walk" "^1.2.8" + "@humanwhocodes/retry" "^0.4.0" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.0.1" - eslint-visitor-keys "^4.0.0" - espree "^10.0.1" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3882,24 +3925,21 @@ eslint@^9.5.0: ignore "^5.2.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - is-path-inside "^3.0.3" json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - strip-ansi "^6.0.1" text-table "^0.2.0" -espree@^10.0.1: - version "10.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" - integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== dependencies: - acorn "^8.12.0" + acorn "^8.14.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.0.0" + eslint-visitor-keys "^4.2.0" esprima@^4.0.0: version "4.0.1" @@ -3907,9 +3947,9 @@ esprima@^4.0.0: integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -3985,7 +4025,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.9, fast-glob@^3.3.1: +fast-glob@^3.1.1, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -4124,7 +4164,7 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -4149,7 +4189,7 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -4179,10 +4219,10 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -get-tsconfig@^4.5.0: - version "4.7.5" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" - integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== +get-tsconfig@^4.7.5: + version "4.8.1" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" + integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== dependencies: resolve-pkg-maps "^1.0.0" @@ -4239,7 +4279,7 @@ globals@^14.0.0: resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -globalthis@^1.0.3: +globalthis@^1.0.3, globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== @@ -4405,9 +4445,9 @@ header-case@^2.0.4: tslib "^2.0.3" highlight-words-core@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.2.tgz#1eff6d7d9f0a22f155042a00791237791b1eeaaa" - integrity sha512-BXUKIkUuh6cmmxzi5OIbUJxrG8OAk2MqoL1DtO3Wo9D2faJg2ph5ntyuQeLqaHJmzER6H5tllCDA9ZnNe9BVGg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" + integrity sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ== hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" @@ -4460,9 +4500,9 @@ ieee754@^1.1.13: integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore@^5.1.4, ignore@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" - integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== immutable@~3.7.6: version "3.7.6" @@ -4483,9 +4523,9 @@ import-from@4.0.0: integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" @@ -4534,7 +4574,7 @@ inquirer@^8.0.0: through "^2.3.6" wrap-ansi "^6.0.1" -internal-slot@^1.0.4, internal-slot@^1.0.7: +internal-slot@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== @@ -4558,15 +4598,7 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: +is-array-buffer@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== @@ -4606,15 +4638,22 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-bun-module@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-1.2.1.tgz#495e706f42e29f086fd5fe1ac3c51f106062b9fc" + integrity sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q== + dependencies: + semver "^7.6.3" + is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" - integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== +is-core-module@^2.13.0, is-core-module@^2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" + integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== dependencies: hasown "^2.0.2" @@ -4680,7 +4719,7 @@ is-lower-case@^2.0.2: dependencies: tslib "^2.0.3" -is-map@^2.0.2, is-map@^2.0.3: +is-map@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== @@ -4702,11 +4741,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -4722,7 +4756,7 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-set@^2.0.2, is-set@^2.0.3: +is-set@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== @@ -4841,9 +4875,9 @@ istanbul-lib-instrument@^5.0.4: semver "^6.3.0" istanbul-lib-instrument@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz#91655936cf7380e4e473383081e38478b69993b1" - integrity sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw== + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" "@babel/parser" "^7.23.9" @@ -4877,10 +4911,10 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterator.prototype@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" - integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== +iterator.prototype@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" + integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== dependencies: define-properties "^1.2.1" get-intrinsic "^1.2.1" @@ -5316,9 +5350,9 @@ jiti@^1.17.1: integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== jose@^4.11.4: - version "4.15.7" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.7.tgz#96ad68d786632bd03c9068aa281810dbbe1b60d8" - integrity sha512-L7ioP+JAuZe8v+T5+zVI9Tx8LtU8BL7NxkyDFVMv+Qr3JW0jSoYDedLtodaXwfqMpeCyx4WXFNyu9tJt4WvC1A== + version "4.15.9" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" + integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5340,10 +5374,10 @@ js-yaml@^4.0.0, js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== json-buffer@3.0.1: version "3.0.1" @@ -5521,6 +5555,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -5629,9 +5668,9 @@ meros@^1.2.1: integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.7" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5" - integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q== + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" picomatch "^2.3.1" @@ -5655,7 +5694,7 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -5679,12 +5718,7 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: +ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -5705,11 +5739,11 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^13.0.0: - version "13.5.6" - resolved "https://registry.yarnpkg.com/next/-/next-13.5.6.tgz#e964b5853272236c37ce0dd2c68302973cf010b1" - integrity sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw== + version "13.5.7" + resolved "https://registry.yarnpkg.com/next/-/next-13.5.7.tgz#deddbb6644b235f0f6be2bbb6facce9ce004fd8e" + integrity sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg== dependencies: - "@next/env" "13.5.6" + "@next/env" "13.5.7" "@swc/helpers" "0.5.2" busboy "1.6.0" caniuse-lite "^1.0.30001406" @@ -5717,15 +5751,15 @@ next@^13.0.0: styled-jsx "5.1.1" watchpack "2.4.0" optionalDependencies: - "@next/swc-darwin-arm64" "13.5.6" - "@next/swc-darwin-x64" "13.5.6" - "@next/swc-linux-arm64-gnu" "13.5.6" - "@next/swc-linux-arm64-musl" "13.5.6" - "@next/swc-linux-x64-gnu" "13.5.6" - "@next/swc-linux-x64-musl" "13.5.6" - "@next/swc-win32-arm64-msvc" "13.5.6" - "@next/swc-win32-ia32-msvc" "13.5.6" - "@next/swc-win32-x64-msvc" "13.5.6" + "@next/swc-darwin-arm64" "13.5.7" + "@next/swc-darwin-x64" "13.5.7" + "@next/swc-linux-arm64-gnu" "13.5.7" + "@next/swc-linux-arm64-musl" "13.5.7" + "@next/swc-linux-x64-gnu" "13.5.7" + "@next/swc-linux-x64-musl" "13.5.7" + "@next/swc-win32-arm64-msvc" "13.5.7" + "@next/swc-win32-ia32-msvc" "13.5.7" + "@next/swc-win32-x64-msvc" "13.5.7" no-case@^3.0.4: version "3.0.4" @@ -5736,9 +5770,9 @@ no-case@^3.0.4: tslib "^2.0.3" node-addon-api@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.0.tgz#71f609369379c08e251c558527a107107b5e0fdb" - integrity sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g== + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-fetch@^2.6.1, node-fetch@^2.6.12: version "2.7.0" @@ -5752,10 +5786,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== nopt@^4.0.1: version "4.0.3" @@ -5814,14 +5848,6 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== -object-is@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" - integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== - dependencies: - call-bind "^1.0.7" - define-properties "^1.2.1" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5846,7 +5872,7 @@ object.entries@^1.1.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -object.fromentries@^2.0.7, object.fromentries@^2.0.8: +object.fromentries@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== @@ -5856,7 +5882,7 @@ object.fromentries@^2.0.7, object.fromentries@^2.0.8: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.groupby@^1.0.1: +object.groupby@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== @@ -5865,16 +5891,7 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" - integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== - dependencies: - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - -object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: +object.values@^1.1.6, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -6079,20 +6096,20 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-to-regexp@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" - integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== +path-to-regexp@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.0.0.tgz#92076ec6b2eaf08be7c3233484142c05e8866cf5" + integrity sha512-GAWaqWlTjYK/7SVpIUA6CTxmcg65SP30sbjdCvyYReosRkk7Z/LyHWwkK3Vu0FcIi0FNTADUs4eh1AsU5s10cg== path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0, picocolors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" - integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== +picocolors@^1.0.0, picocolors@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -6131,9 +6148,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" - integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== pretty-format@^29.7.0: version "29.7.0" @@ -6195,26 +6212,21 @@ pvutils@^1.1.3: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== -qrcode.react@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/qrcode.react/-/qrcode.react-3.1.0.tgz#5c91ddc0340f768316fbdb8fff2765134c2aecd8" - integrity sha512-oyF+Urr3oAMUG/OiOuONL3HXM+53wvuH3mtIWQrYmsXoAq0DkvZp2RYUWFSMFtbdOpuS++9v+WAkzNVkMlNW6Q== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -rc-cascader@~3.26.0: - version "3.26.0" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.26.0.tgz#1bcc9c29451047dc99e28fdd125c94828d7ddf76" - integrity sha512-L1dml383TPSJD1I11YwxuVbmqaJY64psZqFp1ETlgl3LEOwDu76Cyl11fw5dmjJhMlUWwM5dECQfqJgfebhUjg== +rc-cascader@~3.28.2: + version "3.28.2" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.28.2.tgz#91720d3498261a7bff9fffc953501a8830f601fb" + integrity sha512-8f+JgM83iLTvjgdkgU7GfI4qY8icXOBP0cGZjOdx2iJAkEe8ucobxDQAVE69UD/c3ehCxZlcgEHeD5hFmypbUw== dependencies: "@babel/runtime" "^7.12.5" array-tree-filter "^2.1.0" classnames "^2.3.1" - rc-select "~14.14.0" - rc-tree "~5.8.1" + rc-select "~14.15.0" + rc-tree "~5.9.0" rc-util "^5.37.0" rc-checkbox@~3.3.0: @@ -6226,20 +6238,20 @@ rc-checkbox@~3.3.0: classnames "^2.3.2" rc-util "^5.25.2" -rc-collapse@~3.7.3: - version "3.7.3" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.7.3.tgz#68161683d8fd1004bef4eb281fc106f3c8dc16eb" - integrity sha512-60FJcdTRn0X5sELF18TANwtVi7FtModq649H11mYF1jh83DniMoM4MqY627sEKRCTm4+WXfGDcB7hY5oW6xhyw== +rc-collapse@~3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.8.0.tgz#02bcf81e1601aa185cd3b9fab0ceefd8dc11aefb" + integrity sha512-YVBkssrKPBG09TGfcWWGj8zJBYD9G3XuTy89t5iUmSXrIXEAnO1M+qjUxRW6b4Qi0+wNWG6MHJF/+US+nmIlzA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" rc-motion "^2.3.4" rc-util "^5.27.0" -rc-dialog@~9.5.2: - version "9.5.2" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.5.2.tgz#4cf7cca23aedb6fd3d9344ea8ffd14daa94ee3a0" - integrity sha512-qVUjc8JukG+j/pNaHVSRa2GO2/KbV2thm7yO4hepQ902eGdYK913sGkwg/fh9yhKYV1ql3BKIN2xnud3rEXAPw== +rc-dialog@~9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" + integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/portal" "^1.0.0-8" @@ -6268,64 +6280,64 @@ rc-dropdown@~4.2.0: classnames "^2.2.6" rc-util "^5.17.0" -rc-field-form@~2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.2.1.tgz#0a8c76a0103535c229311dac7f91d32ea13cd3bc" - integrity sha512-uoNqDoR7A4tn4QTSqoWPAzrR7ZwOK5I+vuZ/qdcHtbKx+ZjEsTg7QXm2wk/jalDiSksAQmATxL0T5LJkRREdIA== +rc-field-form@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.4.0.tgz#26997160d12ae43a94c356c1290bfc011c69b3ca" + integrity sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/async-validator" "^5.0.3" rc-util "^5.32.2" -rc-image@~7.9.0: - version "7.9.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.9.0.tgz#2d700a5cf891bb3d0d800b7c38348927ebb9f49b" - integrity sha512-l4zqO5E0quuLMCtdKfBgj4Suv8tIS011F5k1zBBlK25iMjjiNHxA0VeTzGFtUZERSA45gvpXDg8/P6qNLjR25g== +rc-image@~7.11.0: + version "7.11.0" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.0.tgz#18c77ea557a6fdbe26856c688a9aace1505c0e77" + integrity sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" classnames "^2.2.6" - rc-dialog "~9.5.2" + rc-dialog "~9.6.0" rc-motion "^2.6.2" rc-util "^5.34.1" -rc-input-number@~9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.1.0.tgz#fd577db284b65548c156500322a2feaa04321565" - integrity sha512-NqJ6i25Xn/AgYfVxynlevIhX3FuKlMwIFpucGG1h98SlK32wQwDK0zhN9VY32McOmuaqzftduNYWWooWz8pXQA== +rc-input-number@~9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.2.0.tgz#7e9344ff054421d2bfff0eebd7c1b8ef22d12220" + integrity sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" - rc-input "~1.5.0" + rc-input "~1.6.0" rc-util "^5.40.1" -rc-input@~1.5.0, rc-input@~1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.5.1.tgz#36d37eb045f1fa17de7da1a3fab94edfa331ab92" - integrity sha512-+nOzQJDeIfIpNP/SgY45LXSKbuMlp4Yap2y8c+ZpU7XbLmNzUd6+d5/S75sA/52jsVE6S/AkhkkDEAOjIu7i6g== +rc-input@~1.6.0, rc-input@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.6.3.tgz#f1708fc3d5e68f95cb20faeb3eed1df8543cd444" + integrity sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.14.0.tgz#7cad133de2e95b7478c0ebb5d2a4315dbe9bda29" - integrity sha512-qKR59FMuF8PK4ZqsbWX3UuA5P1M/snzyqV6Yt3y1DCFbCEdqUGIBgQp6vEfLCO6Z0RoRFlzXtCeSlBTcDDpg1A== +rc-mentions@~2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.16.1.tgz#5e54ebe3ce6cd79838846ff1c8cfaf2e7aa15cec" + integrity sha512-GnhSTGP9Mtv6pqFFGQze44LlrtWOjHNrUUAcsdo9DnNAhN4pwVPEWy4z+2jpjkiGlJ3VoXdvMHcNDQdfI9fEaw== dependencies: "@babel/runtime" "^7.22.5" "@rc-component/trigger" "^2.0.0" classnames "^2.2.6" - rc-input "~1.5.0" - rc-menu "~9.14.0" - rc-textarea "~1.7.0" + rc-input "~1.6.0" + rc-menu "~9.15.1" + rc-textarea "~1.8.0" rc-util "^5.34.1" -rc-menu@~9.14.0: - version "9.14.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.14.1.tgz#5c2aea72bdce421e9d50bf721ad8b76c154ae66f" - integrity sha512-5wlRb3M8S4yGlWhSoEYJ7ZVRElyScdcpUHxgiLxkeig1tEdyKrnED3B2fhpN0Rrpdp9jyhnmZR/Lwq2fH5VvDQ== +rc-menu@~9.15.1: + version "9.15.1" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.15.1.tgz#d8b38ea534a7f596a8da063881519e7eaafca698" + integrity sha512-UKporqU6LPfHnpPmtP6hdEK4iO5Q+b7BRv/uRpxdIyDGplZy9jwUjsnpev5bs3PQKB0H0n34WAPDfjAfn3kAPA== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/trigger" "^2.0.0" @@ -6334,19 +6346,19 @@ rc-menu@~9.14.0: rc-overflow "^1.3.1" rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.2.tgz#f7c6d480250df8a512d0cfdce07ff3da906958cf" - integrity sha512-fUAhHKLDdkAXIDLH0GYwof3raS58dtNUmzLF2MeiR8o6n4thNpSDQhOqQzWE4WfFZDCi9VEN8n7tiB7czREcyw== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.3: + version "2.9.3" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.3.tgz#b1bdaf816f1ccb3e4b3b0c531c3037a59286379e" + integrity sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.43.0" -rc-notification@~5.6.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.0.tgz#1639aa30686d79ee4bb8ace05a698a5a104aaa74" - integrity sha512-TGQW5T7waOxLwgJG7fXcw8l7AQiFOjaZ7ISF5PrU526nunHRNcTMuzKihQHaF4E/h/KfOCDk3Mv8eqzbu2e28w== +rc-notification@~5.6.2: + version "5.6.2" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.2.tgz#8525b32d49dd96ec974acae61d1d1eabde61463a" + integrity sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6363,26 +6375,26 @@ rc-overflow@^1.3.1, rc-overflow@^1.3.2: rc-resize-observer "^1.0.0" rc-util "^5.37.0" -rc-pagination@~4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.0.4.tgz#ea401388ae86eac17ed5b41212d487f12b65b773" - integrity sha512-GGrLT4NgG6wgJpT/hHIpL9nELv27A1XbSZzECIuQBQTVSf4xGKxWr6I/jhpRPauYEWEbWVw22ObG6tJQqwJqWQ== +rc-pagination@~4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.3.0.tgz#c6022f820aa3a45fd734ae33a2915d39597dce1d" + integrity sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.5.0.tgz#ae7a028ed6184e0ef40a2c8aaeb9d5dbef89d4b8" - integrity sha512-suqz9bzuhBQlf7u+bZd1bJLPzhXpk12w6AjQ9BTPTiFwexVZgUKViG1KNLyfFvW6tCUZZK0HmCCX7JAyM+JnCg== +rc-picker@~4.6.15: + version "4.6.15" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.6.15.tgz#1531c9c382a295e2d1f1f38440d6678b09cd0468" + integrity sha512-OWZ1yrMie+KN2uEUfYCfS4b2Vu6RC1FWwNI0s+qypsc3wRt7g+peuZKVIzXCTaJwyyZruo80+akPg2+GmyiJjw== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.24.7" "@rc-component/trigger" "^2.0.0" classnames "^2.2.1" rc-overflow "^1.3.2" rc-resize-observer "^1.4.0" - rc-util "^5.38.1" + rc-util "^5.43.0" rc-progress@~4.0.0: version "4.0.0" @@ -6412,20 +6424,20 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-util "^5.38.0" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.3.0.tgz#b3fe080fb434a266c02e30bb62a47d2c6e094341" - integrity sha512-I3FtM5Smua/ESXutFfb8gJ8ZPcvFR+qUgeeGFQHBOvRiRKyAk4aBE5nfqrxXx+h8/vn60DQjOt6i4RNtrbOobg== +rc-segmented@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.5.0.tgz#3b5423adf57459345c77c39c7581fde786a16c11" + integrity sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.14.0: - version "14.14.0" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.14.0.tgz#110771fad72496843b245236bcb0007553b9fe0d" - integrity sha512-Uo2wulrjoPPRLCPd7zlK4ZFVJxlTN//yp1xWP/U+TUOQCyXrT+Duvq/Si5OzVcmQyWAUSbsplc2OwNNhvbOeKQ== +rc-select@~14.15.0, rc-select@~14.15.2: + version "14.15.2" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.15.2.tgz#d85fcf3a708bdf837b003feeed653347b8980ad0" + integrity sha512-oNoXlaFmpqXYcQDzcPVLrEqS2J9c+/+oJuGrlXeVVX/gVgrbHa5YcyiRUXRydFjyuA7GP3elRuLF7Y3Tfwltlw== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/trigger" "^2.1.1" @@ -6435,10 +6447,10 @@ rc-select@~14.14.0: rc-util "^5.16.1" rc-virtual-list "^3.5.2" -rc-slider@~10.6.2: - version "10.6.2" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.6.2.tgz#8bd3b63b24f2f3682ea1bf86d021073189cf33eb" - integrity sha512-FjkoFjyvUQWcBo1F3RgSglky3ar0+qHLM41PlFVYB4Bj3RD8E/Mv7kqMouLFBU+3aFglMzzctAIWRwajEuueSw== +rc-slider@~11.1.7: + version "11.1.7" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.7.tgz#3de333b1ec84d53a7bda2f816bb4779423628f09" + integrity sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" @@ -6462,66 +6474,66 @@ rc-switch@~4.1.0: classnames "^2.2.1" rc-util "^5.30.0" -rc-table@~7.45.7: - version "7.45.7" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.45.7.tgz#f7c509e05c677a30ad5b212750122da6f5318004" - integrity sha512-wi9LetBL1t1csxyGkMB2p3mCiMt+NDexMlPbXHvQFmBBAsMxrgNSAPwUci2zDLUq9m8QdWc1Nh8suvrpy9mXrg== +rc-table@~7.47.5: + version "7.47.5" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.47.5.tgz#3c530200baa82346c7e72fe9b1dbd47d4aa15838" + integrity sha512-fzq+V9j/atbPIcvs3emuclaEoXulwQpIiJA6/7ey52j8+9cJ4P8DGmp4YzfUVDrb3qhgedcVeD6eRgUrokwVEQ== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/context" "^1.4.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.37.0" + rc-util "^5.41.0" rc-virtual-list "^3.14.2" -rc-tabs@~15.1.1: - version "15.1.1" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.1.1.tgz#99f4c7647e01d3e22216d94222d717e928ed98d0" - integrity sha512-Tc7bJvpEdkWIVCUL7yQrMNBJY3j44NcyWS48jF/UKMXuUlzaXK+Z/pEL5LjGcTadtPvVmNqA40yv7hmr+tCOAw== +rc-tabs@~15.3.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.3.0.tgz#3fcc332fbb9307d5eb147e0404daca871fb92a89" + integrity sha512-lzE18r+zppT/jZWOAWS6ntdkDUKHOLJzqMi5UAij1LeKwOaQaupupAoI9Srn73GRzVpmGznkECMRrzkRusC40A== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" rc-dropdown "~4.2.0" - rc-menu "~9.14.0" + rc-menu "~9.15.1" rc-motion "^2.6.2" rc-resize-observer "^1.0.0" rc-util "^5.34.1" -rc-textarea@~1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.7.0.tgz#115c421359dddee58c601008ec2209b41cb8f8df" - integrity sha512-UxizYJkWkmxP3zofXgc487QiGyDmhhheDLLjIWbFtDmiru1ls30KpO8odDaPyqNUIy9ugj5djxTEuezIn6t3Jg== +rc-textarea@~1.8.0, rc-textarea@~1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.8.2.tgz#57a6847304551c1883fc3fb0c5076d587f70bf7f" + integrity sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" - rc-input "~1.5.0" + rc-input "~1.6.0" rc-resize-observer "^1.0.0" rc-util "^5.27.0" -rc-tooltip@~6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.0.tgz#4dd7575674137a5b14f118a5c16435d3f5e4a9c9" - integrity sha512-iS/3iOAvtDh9GIx1ulY7EFUXUtktFccNLsARo3NPgLf0QW9oT0w3dA9cYWlhqAKmD+uriEwdWz1kH0Qs4zk2Aw== +rc-tooltip@~6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.1.tgz#9a8f0335c86443a0c20c2557933205f645a381b7" + integrity sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/trigger" "^2.0.0" classnames "^2.3.1" -rc-tree-select@~5.21.0: - version "5.21.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.21.0.tgz#4ff94d5c28cba9810c4970d1f12ab7248c3b6e55" - integrity sha512-w+9qEu6zh0G3wt9N/hzWNSnqYH1i9mH1Nqxo0caxLRRFXF5yZWYmpCDoDTMdQM1Y4z3Q5yj08qyrPH/d4AtumA== +rc-tree-select@~5.23.0: + version "5.23.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.23.0.tgz#e56da0923c7c11dea98d4e14bb76969283c94468" + integrity sha512-aQGi2tFSRw1WbXv0UVXPzHm09E0cSvUVZMLxQtMv3rnZZpNmdRXWrnd9QkLNlVH31F+X5rgghmdSFF3yZW0N9A== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" - rc-select "~14.14.0" - rc-tree "~5.8.1" + rc-select "~14.15.0" + rc-tree "~5.9.0" rc-util "^5.16.1" -rc-tree@~5.8.1, rc-tree@~5.8.8: - version "5.8.8" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.8.8.tgz#650a13ec825a5a4feec6bbaf6a380465986ee0db" - integrity sha512-S+mCMWo91m5AJqjz3PdzKilGgbFm7fFJRFiTDOcoRbD7UfMOPnerXwMworiga0O2XIo383UoWuEfeHs1WOltag== +rc-tree@~5.9.0: + version "5.9.0" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.9.0.tgz#1835b2bef36cfeb4ec15d62e0319fc503aa485f1" + integrity sha512-CPrgOvm9d/9E+izTONKSngNzQdIEjMox2PBufWjS1wf7vxtvmCWzK1SlpHbRY6IaBfJIeZ+88RkcIevf729cRg== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6529,16 +6541,16 @@ rc-tree@~5.8.1, rc-tree@~5.8.8: rc-util "^5.16.1" rc-virtual-list "^3.5.1" -rc-upload@~4.5.2: - version "4.5.2" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.5.2.tgz#ea493fbaaf57d9369ee954b20e1d8bc35c818a1a" - integrity sha512-QO3ne77DwnAPKFn0bA5qJM81QBjQi0e0NHdkvpFyY73Bea2NfITiotqJqVjHgeYPOJu5lLVR32TNGP084aSoXA== +rc-upload@~4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.8.1.tgz#ac55f2bc101b95b52a6e47f3c18f0f55b54e16d2" + integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw== dependencies: "@babel/runtime" "^7.18.3" classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0: +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0: version "5.43.0" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== @@ -6547,9 +6559,9 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18. react-is "^18.2.0" rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.14.5" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.14.5.tgz#593cd13fe05eabf4ad098329704a30c77701869e" - integrity sha512-ZMOnkCLv2wUN8Jz7yI4XiSLa9THlYvf00LuMhb1JlsQCewuU7ydPuHw1rGVPhe9VZYl/5UqODtNd7QKJ2DMGfg== + version "3.15.0" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz#45c5b1ef1363287214e0a3c17af29ee0c3764764" + integrity sha512-dF2YQztqrU3ijAeWOqscTshCEr7vpimzSqAVjO1AyAmaqcHulaXpnGR0ptK5PXfxTUy48VkJOiglMIxlkYGs0w== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" @@ -6557,9 +6569,9 @@ rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: rc-util "^5.36.0" react-cookie@^7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.1.4.tgz#1e35c9f11394b44fbbc30c5eba1287d89258e993" - integrity sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw== + version "7.2.2" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84" + integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg== dependencies: "@types/hoist-non-react-statics" "^3.3.5" hoist-non-react-statics "^3.3.2" @@ -6677,15 +6689,15 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== +regexp.prototype.flags@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" + integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== dependencies: - call-bind "^1.0.6" + call-bind "^1.0.7" define-properties "^1.2.1" es-errors "^1.3.0" - set-function-name "^2.0.1" + set-function-name "^2.0.2" rehackt@^0.1.0: version "0.1.0" @@ -6843,9 +6855,9 @@ safe-regex-test@^1.0.3: is-regex "^1.1.4" safe-stable-stringify@^2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" - integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -6890,12 +6902,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - -semver@^7.6.0: +semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== @@ -6942,33 +6949,33 @@ setimmediate@^1.0.5: integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== sharp@^0.33.4: - version "0.33.4" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.4.tgz#b88e6e843e095c6ab5e1a0c59c4885e580cd8405" - integrity sha512-7i/dt5kGl7qR4gwPRD2biwD2/SvBn3O04J77XKFgL2OnZtQw+AG9wnuS/csmu80nPRHLYE9E41fyEiG8nhH6/Q== + version "0.33.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" + integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== dependencies: color "^4.2.3" detect-libc "^2.0.3" - semver "^7.6.0" + semver "^7.6.3" optionalDependencies: - "@img/sharp-darwin-arm64" "0.33.4" - "@img/sharp-darwin-x64" "0.33.4" - "@img/sharp-libvips-darwin-arm64" "1.0.2" - "@img/sharp-libvips-darwin-x64" "1.0.2" - "@img/sharp-libvips-linux-arm" "1.0.2" - "@img/sharp-libvips-linux-arm64" "1.0.2" - "@img/sharp-libvips-linux-s390x" "1.0.2" - "@img/sharp-libvips-linux-x64" "1.0.2" - "@img/sharp-libvips-linuxmusl-arm64" "1.0.2" - "@img/sharp-libvips-linuxmusl-x64" "1.0.2" - "@img/sharp-linux-arm" "0.33.4" - "@img/sharp-linux-arm64" "0.33.4" - "@img/sharp-linux-s390x" "0.33.4" - "@img/sharp-linux-x64" "0.33.4" - "@img/sharp-linuxmusl-arm64" "0.33.4" - "@img/sharp-linuxmusl-x64" "0.33.4" - "@img/sharp-wasm32" "0.33.4" - "@img/sharp-win32-ia32" "0.33.4" - "@img/sharp-win32-x64" "0.33.4" + "@img/sharp-darwin-arm64" "0.33.5" + "@img/sharp-darwin-x64" "0.33.5" + "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-linux-arm" "0.33.5" + "@img/sharp-linux-arm64" "0.33.5" + "@img/sharp-linux-s390x" "0.33.5" + "@img/sharp-linux-x64" "0.33.5" + "@img/sharp-linuxmusl-arm64" "0.33.5" + "@img/sharp-linuxmusl-x64" "0.33.5" + "@img/sharp-wasm32" "0.33.5" + "@img/sharp-win32-ia32" "0.33.5" + "@img/sharp-win32-x64" "0.33.5" shebang-command@^2.0.0: version "2.0.0" @@ -7056,9 +7063,9 @@ snake-case@^3.0.4: tslib "^2.0.3" source-map-js@^1.0.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@0.5.13: version "0.5.13" @@ -7109,9 +7116,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.18" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" - integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== + version "3.0.20" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" + integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== spdx-ranges@^2.0.0: version "2.1.1" @@ -7146,13 +7153,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - streamsearch@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" @@ -7185,13 +7185,14 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.includes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz#8986d57aee66d5460c144620a6d873778ad7289f" - integrity sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg== +string.prototype.includes@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" + integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.5" + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.3" string.prototype.matchall@^4.0.11: version "4.0.11" @@ -7211,6 +7212,14 @@ string.prototype.matchall@^4.0.11: set-function-name "^2.0.2" side-channel "^1.0.6" +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -7280,10 +7289,10 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -stylis@^4.0.13: - version "4.3.2" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.2.tgz#8f76b70777dd53eb669c6f58c997bf0a9972e444" - integrity sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg== +stylis@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" + integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== supports-color@^5.3.0: version "5.5.0" @@ -7350,7 +7359,7 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -throttle-debounce@^5.0.0: +throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== @@ -7379,11 +7388,6 @@ tmpl@1.0.5: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -7407,9 +7411,9 @@ treeify@^1.1.0: integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-api-utils@^1.0.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" - integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + version "1.4.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" + integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== ts-invariant@^0.10.3: version "0.10.3" @@ -7419,9 +7423,9 @@ ts-invariant@^0.10.3: tslib "^2.1.0" ts-log@^2.2.3: - version "2.2.5" - resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.5.tgz#aef3252f1143d11047e2cb6f7cfaac7408d96623" - integrity sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA== + version "2.2.7" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" + integrity sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg== ts-node@^10.9.1: version "10.9.2" @@ -7452,10 +7456,10 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1, tslib@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.7.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tslib@~2.0.1: version "2.0.3" @@ -7551,14 +7555,14 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^5.0.4: - version "5.5.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" - integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== ua-parser-js@^1.0.35: - version "1.0.38" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.38.tgz#66bb0c4c0e322fe48edfe6d446df6042e62f25e2" - integrity sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ== + version "1.0.39" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" + integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== unbox-primitive@^1.0.2: version "1.0.2" @@ -7575,18 +7579,18 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.19.2, undici-types@~6.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== universal-cookie@^7.0.0: - version "7.1.4" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.1.4.tgz#d11bb95e405639c0ff0b467a64a5ccc5ce97dfc6" - integrity sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g== + version "7.2.2" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c" + integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ== dependencies: "@types/cookie" "^0.6.0" - cookie "^0.6.0" + cookie "^0.7.2" unixify@1.0.0, unixify@^1.0.0: version "1.0.0" @@ -7595,13 +7599,13 @@ unixify@1.0.0, unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.0.16: - version "1.0.16" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz#f6d489ed90fb2f07d67784eb3f53d7891f736356" - integrity sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ== +update-browserslist-db@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" + integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== dependencies: - escalade "^3.1.2" - picocolors "^1.0.1" + escalade "^3.2.0" + picocolors "^1.1.0" upper-case-first@^2.0.2: version "2.0.2" @@ -7706,15 +7710,15 @@ web-streams-polyfill@^3.2.1: integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== webcrypto-core@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.0.tgz#aaea17f3dd9c77c304e3c494eb27ca07cc72ca37" - integrity sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw== + version "1.8.1" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.1.tgz#09d5bd8a9c48e9fbcaf412e06b1ff1a57514ce86" + integrity sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A== dependencies: - "@peculiar/asn1-schema" "^2.3.8" + "@peculiar/asn1-schema" "^2.3.13" "@peculiar/json-schema" "^1.1.12" - asn1js "^3.0.1" + asn1js "^3.0.5" pvtsutils "^1.3.5" - tslib "^2.6.2" + tslib "^2.7.0" webidl-conversions@^3.0.0: version "3.0.1" @@ -7741,12 +7745,12 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" is-async-function "^2.0.0" is-date-object "^1.0.5" is-finalizationregistry "^1.0.2" @@ -7755,10 +7759,10 @@ which-builtin-type@^1.1.3: is-weakref "^1.0.2" isarray "^2.0.5" which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" + which-collection "^1.0.2" + which-typed-array "^1.1.15" -which-collection@^1.0.1: +which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -7773,7 +7777,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -7843,9 +7847,9 @@ ws@8.13.0: integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== ws@^8.12.0: - version "8.17.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" - integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== y18n@^4.0.0: version "4.0.3" @@ -7882,9 +7886,9 @@ yaml@1.10.2, yaml@^1.10.0: integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^2.0.0: - version "2.4.5" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.4.5.tgz#60630b206dd6d84df97003d33fc1ddf6296cca5e" - integrity sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" + integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== yargs-parser@^18.1.2: version "18.1.3" From c6369325cd05d6bb5567a748b2559454596b6a09 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 12:06:39 +0100 Subject: [PATCH 205/878] Configure sort input types explicitely to make sure that through sorting no implementation details and sensitive data is leaked --- .../src/Configuration/GraphQlConfiguration.cs | 22 +- backend/src/Data/GetHttpsResource.cs | 2 +- .../CalorimetricDataSortType.cs | 17 + backend/src/GraphQl/DataX/DataSortType.cs | 15 + backend/src/GraphQl/DataX/DataSortTypeBase.cs | 23 + .../GeometricDataX/GeometricDataSortType.cs | 17 + .../GetHttpsResourceFilterType.cs | 3 +- .../GetHttpsResourceSortType.cs | 22 + .../HygrothermalDataSortType.cs | 17 + .../GraphQl/NamedMethodArgumentSortType.cs | 17 + .../OpticalDataX/OpticalDataSortType.cs | 17 + .../PhotovoltaicDataSortType.cs | 17 + frontend/type-defs.graphqls | 637 +++++++++--------- 13 files changed, 485 insertions(+), 341 deletions(-) create mode 100644 backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs create mode 100644 backend/src/GraphQl/DataX/DataSortType.cs create mode 100644 backend/src/GraphQl/DataX/DataSortTypeBase.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs create mode 100644 backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs create mode 100644 backend/src/GraphQl/NamedMethodArgumentSortType.cs create mode 100644 backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs create mode 100644 backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index f3e7f5e8..61ccd512 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,9 +1,9 @@ using System; using HotChocolate.Data; using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; using HotChocolate.Language; using HotChocolate.Types; -using HotChocolate.Types.Pagination; using System.Net.Http; using System.Text.RegularExpressions; using Database.Data; @@ -66,7 +66,7 @@ AppSettings appSettings // Extensions .AddProjections() .AddFiltering() - .AddSorting() + .AddSorting() .AddAuthorization() .AddGlobalObjectIdentification() .AddQueryFieldToMutationPayloads() @@ -377,4 +377,22 @@ private static IFilterConventionDescriptor BindComparableType( // } return descriptor; } +} + +// See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sort-conventions +public partial class CustomSortConvention : SortConvention +{ + protected override void Configure(ISortConventionDescriptor descriptor) + { + descriptor.AddDefaults(); + // Bind custom types + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + } } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index a7bf1545..df2ea2ca 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -133,7 +133,7 @@ public GetHttpsResource( [InverseProperty(nameof(Database.Data.GeometricData.Resources))] public GeometricData? GeometricData { get; set; } - + public Guid? ParentId { get; private set; } // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs new file mode 100644 index 00000000..4b1f08c0 --- /dev/null +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataSortType.cs b/backend/src/GraphQl/DataX/DataSortType.cs new file mode 100644 index 00000000..c401ebcf --- /dev/null +++ b/backend/src/GraphQl/DataX/DataSortType.cs @@ -0,0 +1,15 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.DataX; + +public class DataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataSortTypeBase.cs b/backend/src/GraphQl/DataX/DataSortTypeBase.cs new file mode 100644 index 00000000..36ea1080 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -0,0 +1,23 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.DataX; + +public class DataSortTypeBase + : SortInputType + where TData : IData +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.AppliedMethod); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs new file mode 100644 index 00000000..13da19b4 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index fa884dff..e5885df4 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -17,8 +17,7 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.DataFormatId); descriptor.Field(x => x.AppliedConversionMethod); descriptor.Field(x => x.ArchivedFilesMetaInformation); - descriptor.Field(x => x.DataId); - descriptor.Field(x => x.ParentId); + descriptor.Field(x => x.Data); descriptor.Field(x => x.Parent); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs new file mode 100644 index 00000000..b5b1ccb4 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -0,0 +1,22 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Id); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.HashValue); + descriptor.Field(x => x.DataFormatId); + descriptor.Field(x => x.AppliedConversionMethod); + descriptor.Field(x => x.Data); + descriptor.Field(x => x.Parent); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs new file mode 100644 index 00000000..7e32a867 --- /dev/null +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentSortType.cs b/backend/src/GraphQl/NamedMethodArgumentSortType.cs new file mode 100644 index 00000000..133285a3 --- /dev/null +++ b/backend/src/GraphQl/NamedMethodArgumentSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl; + +public sealed class NamedMethodArgumentSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Name); + // TODO descriptor.Field(x => x.Value); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs new file mode 100644 index 00000000..0746cd41 --- /dev/null +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs new file mode 100644 index 00000000..35395876 --- /dev/null +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 58ad9acb..3d5fcb9b 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -51,10 +51,10 @@ type CalorimetricData implements Node & Data { id: ID! locale: Locale! name: String - resources: [GetHttpsResource!]! + resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - uuid: Uuid! + uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! } @@ -63,12 +63,10 @@ type CalorimetricData implements Node & Data { type CalorimetricDataConnection { "A list of edges." edges: [CalorimetricDataEdge!] - "A flattened list of the nodes." - nodes: [CalorimetricData!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -97,6 +95,18 @@ type CreateCalorimetricDataPayload { query: Query! } +type CreateGeometricDataError { + code: CreateGeometricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateGeometricDataPayload { + errors: [CreateGeometricDataError!] + geometricData: GeometricData + query: Query! +} + type CreateGetHttpsResourceError { code: CreateGetHttpsResourceErrorCode! message: String! @@ -145,18 +155,6 @@ type CreatePhotovoltaicDataPayload { query: Query! } -type CreateGeometricDataError { - code: CreateGeometricDataErrorCode! - message: String! - path: [String!]! -} - -type CreateGeometricDataPayload { - geometricData: GeometricData - errors: [CreateGeometricDataError!] - query: Query! -} - type CrossDatabaseDataReference { databaseId: Uuid! dataId: Uuid! @@ -178,12 +176,10 @@ type DataApproval { type DataConnection { "A list of edges." edges: [DataEdge!] - "A flattened list of the nodes." - nodes: [Data!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -194,6 +190,22 @@ type DataEdge { node: Data! } +type Database { + canCurrentUserUpdateNode: Boolean! + canCurrentUserVerifyNode: Boolean! + description: String! + locator: Url! + name: String! + uuid: Uuid! + verificationCode: String! + verificationState: DatabaseVerificationState! +} + +type FileMetaInformation { + dataFormatId: Uuid! + path: [String!]! +} + type GeometricData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! @@ -204,11 +216,11 @@ type GeometricData implements Node & Data { id: ID! locale: Locale! name: String - resources: [GetHttpsResource!]! + resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - uuid: Uuid! thicknesses: [Float!]! + timestamp: DateTime! + uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -216,12 +228,10 @@ type GeometricData implements Node & Data { type GeometricDataConnection { "A list of edges." edges: [GeometricDataEdge!] - "A flattened list of the nodes." - nodes: [GeometricData!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -232,46 +242,28 @@ type GeometricDataEdge { node: GeometricData! } -type Database { - canCurrentUserUpdateNode: Boolean! - canCurrentUserVerifyNode: Boolean! - description: String! - locator: Url! - name: String! - uuid: Uuid! - verificationCode: String! - verificationState: DatabaseVerificationState! -} - -type FileMetaInformation { - dataFormatId: Uuid! - path: [String!]! -} - type GetHttpsResource implements Node { appliedConversionMethod: ToTreeVertexAppliedConversionMethod archivedFilesMetaInformation: [FileMetaInformation!]! - children: [GetHttpsResource!]! - data: Data + children: [GetHttpsResource!]! @cost(weight: "10") + data: Data @cost(weight: "10") dataFormatId: Uuid! description: String! hashValue: String! id: ID! locator: Url! - parent: GetHttpsResource - uuid: Uuid! + parent: GetHttpsResource @cost(weight: "10") + uuid: Uuid! @cost(weight: "10") } "A connection to a list of items." type GetHttpsResourceConnection { "A list of edges." edges: [GetHttpsResourceEdge!] - "A flattened list of the nodes." - nodes: [GetHttpsResource!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -283,8 +275,8 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices: [GetHttpsResourceTreeNonRootVertex!]! - root: GetHttpsResourceTreeRoot! + nonRootVertices: [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + root: GetHttpsResourceTreeRoot! @cost(weight: "10") } type GetHttpsResourceTreeNonRootVertex { @@ -309,10 +301,10 @@ type HygrothermalData implements Node & Data { id: ID! locale: Locale! name: String - resources: [GetHttpsResource!]! + resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - uuid: Uuid! + uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -320,12 +312,10 @@ type HygrothermalData implements Node & Data { type HygrothermalDataConnection { "A list of edges." edges: [HygrothermalDataEdge!] - "A flattened list of the nodes." - nodes: [HygrothermalData!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -337,18 +327,18 @@ type HygrothermalDataEdge { } type Mutation { - createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! - createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! - createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! - createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! - createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") } type NamedMethodArgument { name: String! - value: Any! + value: Any! @cost(weight: "10") } type NamedMethodSource { @@ -363,8 +353,8 @@ type Numeration { } type OpenEndedDateTimeRange { - from: DateTime - until: DateTime + from: DateTime @cost(weight: "10") + until: DateTime @cost(weight: "10") } type OpticalData implements Node & Data { @@ -384,10 +374,10 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources: [GetHttpsResource!]! + resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - uuid: Uuid! + uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -395,12 +385,10 @@ type OpticalData implements Node & Data { type OpticalDataConnection { "A list of edges." edges: [OpticalDataEdge!] - "A flattened list of the nodes." - nodes: [OpticalData!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -433,10 +421,10 @@ type PhotovoltaicData implements Node & Data { id: ID! locale: Locale! name: String - resources: [GetHttpsResource!]! + resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - uuid: Uuid! + uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -444,12 +432,10 @@ type PhotovoltaicData implements Node & Data { type PhotovoltaicDataConnection { "A list of edges." edges: [PhotovoltaicDataEdge!] - "A flattened list of the nodes." - nodes: [PhotovoltaicData!] "Information to aid in pagination." pageInfo: PageInfo! "Identifies the total count of items in the connection." - totalCount: Int! + totalCount: Int! @cost(weight: "10") } "An edge in a connection." @@ -477,28 +463,28 @@ type Publication implements Reference { } type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] timestamp: DateTime where: CalorimetricDataPropositionInput): CalorimetricDataConnection - allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] timestamp: DateTime where: DataPropositionInput): DataConnection - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] timestamp: DateTime where: HygrothermalDataPropositionInput): HygrothermalDataConnection - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] timestamp: DateTime where: OpticalDataPropositionInput): OpticalDataConnection - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] timestamp: DateTime where: PhotovoltaicDataPropositionInput): PhotovoltaicDataConnection - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] timestamp: DateTime where: GeometricDataPropositionInput): GeometricDataConnection - calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData - currentUser: User - currentUserInfo: UserInfo - data(id: Uuid! locale: Locale timestamp: DateTime): Data - database: Database! - getHttpsResource(id: Uuid!): GetHttpsResource - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput): GetHttpsResourceConnection - hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") timestamp: DateTime where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] @cost(weight: "10") timestamp: DateTime where: DataPropositionInput @cost(weight: "10")): DataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") timestamp: DateTime where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") timestamp: DateTime where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") timestamp: DateTime where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") timestamp: DateTime where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData @cost(weight: "10") + currentUser: User @cost(weight: "10") + currentUserInfo: UserInfo @cost(weight: "10") + data(id: Uuid! locale: Locale timestamp: DateTime): Data @cost(weight: "10") + database: Database! @cost(weight: "10") + geometricData(id: Uuid! locale: Locale timestamp: DateTime): GeometricData @cost(weight: "10") + getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") + hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData @cost(weight: "10") "Fetches an object given its ID." - node("ID of the object." id: ID!): Node + node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." - nodes("The list of node IDs." ids: [ID!]!): [Node]! - opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData - photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData + nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData @cost(weight: "10") + photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData @cost(weight: "10") verificationCode: String! - geometricData(id: Uuid! locale: Locale timestamp: DateTime): GeometricData } "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." @@ -536,7 +522,7 @@ type User implements Node { id: ID! name: String! subject: String! - uuid: Uuid! + uuid: Uuid! @cost(weight: "10") } type UserInfo { @@ -566,12 +552,12 @@ input AppliedMethodPropositionInput { } input AppliedMethodSortInput { - methodId: SortEnumType + methodId: SortEnumType @cost(weight: "10") } input BooleanPropositionInput { - equalTo: Boolean - notEqualTo: Boolean + equalTo: Boolean @cost(weight: "10") + notEqualTo: Boolean @cost(weight: "10") } input CalorimetricDataPropositionInput { @@ -593,15 +579,14 @@ input CalorimetricDataPropositionInput { } input CalorimetricDataSortInput { - locale: SortEnumType - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - id: SortEnumType - version: SortEnumType + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") } input CielabColorInput { @@ -618,23 +603,6 @@ input CielabColorPropositionInput { bStar: FloatPropositionInput } -input GeometricDataPropositionInput { - and: [GeometricDataPropositionInput!] - or: [GeometricDataPropositionInput!] - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput - warnings: StringsPropositionInput - id: UuidPropositionInput - thicknesses: FloatsPropositionInput -} - input CreateCalorimetricDataInput { accessToken: String! appliedMethod: AppliedMethodInput! @@ -651,18 +619,6 @@ input CreateCalorimetricDataInput { warnings: [String!]! } -input GeometricDataSortInput { - locale: SortEnumType - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - id: SortEnumType - version: SortEnumType -} - input CreateGeometricDataInput { accessToken: String! appliedMethod: AppliedMethodInput! @@ -674,8 +630,8 @@ input CreateGeometricDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! - warnings: [String!]! thicknesses: [Float!]! + warnings: [String!]! } input CreateGetHttpsResourceInput { @@ -808,10 +764,10 @@ input DataApprovalPropositionInput { } input DataKindPropositionInput { - equalTo: DataKind - notEqualTo: DataKind - in: [DataKind!] - notIn: [DataKind!] + equalTo: DataKind @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + in: [DataKind!] @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") } input DataPropositionInput { @@ -830,18 +786,18 @@ input DataPropositionInput { } input DateTimePropositionInput { - equalTo: DateTime - notEqualTo: DateTime - in: [DateTime!] - notIn: [DateTime!] - greaterThan: DateTime - notGreaterThan: DateTime - greaterThanOrEqualTo: DateTime - notGreaterThanOrEqualTo: DateTime - lessThan: DateTime - notLessThan: DateTime - lessThanOrEqualTo: DateTime - notLessThanOrEqualTo: DateTime + equalTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + in: [DateTime!] @cost(weight: "10") + notIn: [DateTime!] @cost(weight: "10") + greaterThan: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + greaterThanOrEqualTo: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + lessThan: DateTime @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") + lessThanOrEqualTo: DateTime @cost(weight: "10") + notLessThanOrEqualTo: DateTime @cost(weight: "10") } input FileMetaInformationInput { @@ -857,25 +813,53 @@ input FileMetaInformationPropositionInput { } input FloatPropositionInput { - equalTo: Float - notEqualTo: Float - in: [Float!] - notIn: [Float!] - greaterThan: Float - notGreaterThan: Float - greaterThanOrEqualTo: Float - notGreaterThanOrEqualTo: Float - lessThan: Float - notLessThan: Float - lessThanOrEqualTo: Float - notLessThanOrEqualTo: Float + equalTo: Float @cost(weight: "10") + notEqualTo: Float @cost(weight: "10") + in: [Float!] @cost(weight: "10") + notIn: [Float!] @cost(weight: "10") + greaterThan: Float @cost(weight: "10") + notGreaterThan: Float @cost(weight: "10") + greaterThanOrEqualTo: Float @cost(weight: "10") + notGreaterThanOrEqualTo: Float @cost(weight: "10") + lessThan: Float @cost(weight: "10") + notLessThan: Float @cost(weight: "10") + lessThanOrEqualTo: Float @cost(weight: "10") + notLessThanOrEqualTo: Float @cost(weight: "10") } input FloatsPropositionInput { - all: FloatPropositionInput - none: FloatPropositionInput - some: FloatPropositionInput - any: Boolean + all: FloatPropositionInput @cost(weight: "10") + none: FloatPropositionInput @cost(weight: "10") + some: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input GeometricDataPropositionInput { + and: [GeometricDataPropositionInput!] + or: [GeometricDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput + thicknesses: FloatsPropositionInput +} + +input GeometricDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") } input GetHttpsResourcePropositionInput { @@ -887,32 +871,18 @@ input GetHttpsResourcePropositionInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - dataId: UuidPropositionInput - parentId: NullableOfUuidPropositionInput + data: DataPropositionInput parent: GetHttpsResourcePropositionInput } input GetHttpsResourceSortInput { - description: SortEnumType - hashValue: SortEnumType - dataFormatId: SortEnumType - dataId: SortEnumType - data: IDataSortInput - calorimetricDataId: SortEnumType - calorimetricData: CalorimetricDataSortInput - hygrothermalDataId: SortEnumType - hygrothermalData: HygrothermalDataSortInput - opticalDataId: SortEnumType - opticalData: OpticalDataSortInput - photovoltaicDataId: SortEnumType - photovoltaicData: PhotovoltaicDataSortInput - geometricDataId: SortEnumType - geometricData: GeometricDataSortInput - parentId: SortEnumType - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput - parent: GetHttpsResourceSortInput - id: SortEnumType - version: SortEnumType + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + data: IDataSortInput @cost(weight: "10") + parent: GetHttpsResourceSortInput @cost(weight: "10") } input HygrothermalDataPropositionInput { @@ -932,25 +902,24 @@ input HygrothermalDataPropositionInput { } input HygrothermalDataSortInput { - locale: SortEnumType - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - id: SortEnumType - version: SortEnumType + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") } input IDataSortInput { - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - locale: SortEnumType + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") } input IReferencePropositionInput { @@ -962,67 +931,67 @@ input IReferencePropositionInput { } input Int32PropositionInput { - equalTo: Int - notEqualTo: Int - in: [Int!] - notIn: [Int!] - greaterThan: Int - notGreaterThan: Int - greaterThanOrEqualTo: Int - notGreaterThanOrEqualTo: Int - lessThan: Int - notLessThan: Int - lessThanOrEqualTo: Int - notLessThanOrEqualTo: Int + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int!] @cost(weight: "10") + notIn: [Int!] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") } input ListFilterInputTypeOfCielabColorPropositionInput { - all: CielabColorPropositionInput - none: CielabColorPropositionInput - some: CielabColorPropositionInput - any: Boolean + all: CielabColorPropositionInput @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfDataApprovalPropositionInput { - all: DataApprovalPropositionInput - none: DataApprovalPropositionInput - some: DataApprovalPropositionInput - any: Boolean + all: DataApprovalPropositionInput @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfFileMetaInformationPropositionInput { - all: FileMetaInformationPropositionInput - none: FileMetaInformationPropositionInput - some: FileMetaInformationPropositionInput - any: Boolean + all: FileMetaInformationPropositionInput @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput - none: NamedMethodSourcePropositionInput - some: NamedMethodSourcePropositionInput - any: Boolean + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListGetHttpsResourceFilterTypePropositionInput { - all: GetHttpsResourcePropositionInput - none: GetHttpsResourcePropositionInput - some: GetHttpsResourcePropositionInput - any: Boolean + all: GetHttpsResourcePropositionInput @cost(weight: "10") + none: GetHttpsResourcePropositionInput @cost(weight: "10") + some: GetHttpsResourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListNamedMethodArgumentFilterTypePropositionInput { - all: NamedMethodArgumentPropositionInput - none: NamedMethodArgumentPropositionInput - some: NamedMethodArgumentPropositionInput - any: Boolean + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ListStandardizerPropositionInput { - all: StandardizerPropositionInput - none: StandardizerPropositionInput - some: StandardizerPropositionInput - any: Boolean + all: StandardizerPropositionInput @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input NamedMethodArgumentInput { @@ -1049,33 +1018,18 @@ input NamedMethodSourcePropositionInput { } input NullableOfInt32PropositionInput { - equalTo: Int - notEqualTo: Int - in: [Int] - notIn: [Int] - greaterThan: Int - notGreaterThan: Int - greaterThanOrEqualTo: Int - notGreaterThanOrEqualTo: Int - lessThan: Int - notLessThan: Int - lessThanOrEqualTo: Int - notLessThanOrEqualTo: Int -} - -input NullableOfUuidPropositionInput { - equalTo: Uuid - notEqualTo: Uuid - in: [Uuid] - notIn: [Uuid] - greaterThan: Uuid - notGreaterThan: Uuid - greaterThanOrEqualTo: Uuid - notGreaterThanOrEqualTo: Uuid - lessThan: Uuid - notLessThan: Uuid - lessThanOrEqualTo: Uuid - notLessThanOrEqualTo: Uuid + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + notIn: [Int] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") } input NumerationPropositionInput { @@ -1110,15 +1064,14 @@ input OpticalDataPropositionInput { } input OpticalDataSortInput { - locale: SortEnumType - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - id: SortEnumType - version: SortEnumType + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") } input PhotovoltaicDataPropositionInput { @@ -1138,15 +1091,14 @@ input PhotovoltaicDataPropositionInput { } input PhotovoltaicDataSortInput { - locale: SortEnumType - componentId: SortEnumType - name: SortEnumType - description: SortEnumType - creatorId: SortEnumType - createdAt: SortEnumType - appliedMethod: AppliedMethodSortInput - id: SortEnumType - version: SortEnumType + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") } input PublicationPropositionInput { @@ -1191,32 +1143,32 @@ input StandardPropositionInput { } input StandardizerPropositionInput { - equalTo: Standardizer - notEqualTo: Standardizer - in: [Standardizer!] - notIn: [Standardizer!] + equalTo: Standardizer @cost(weight: "10") + notEqualTo: Standardizer @cost(weight: "10") + in: [Standardizer!] @cost(weight: "10") + notIn: [Standardizer!] @cost(weight: "10") } input StringPropositionInput { and: [StringPropositionInput!] or: [StringPropositionInput!] - equalTo: String - notEqualTo: String - contains: String - doesNotContain: String - in: [String] - notIn: [String] - startsWith: String - doesNotStartWith: String - endsWith: String - doesNotEndWith: String + equalTo: String @cost(weight: "10") + notEqualTo: String @cost(weight: "10") + contains: String @cost(weight: "20") + doesNotContain: String @cost(weight: "20") + in: [String] @cost(weight: "10") + notIn: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + doesNotStartWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + doesNotEndWith: String @cost(weight: "20") } input StringsPropositionInput { - all: StringPropositionInput - none: StringPropositionInput - some: StringPropositionInput - any: Boolean + all: StringPropositionInput @cost(weight: "10") + none: StringPropositionInput @cost(weight: "10") + some: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") } input ToTreeVertexAppliedConversionMethodInput { @@ -1234,8 +1186,8 @@ input ToTreeVertexAppliedConversionMethodPropositionInput { } input ToTreeVertexAppliedConversionMethodSortInput { - methodId: SortEnumType - sourceName: SortEnumType + methodId: SortEnumType @cost(weight: "10") + sourceName: SortEnumType @cost(weight: "10") } input UpdateDatabaseInput { @@ -1246,10 +1198,10 @@ input UpdateDatabaseInput { } input UriHostNameTypePropositionInput { - equalTo: UriHostNameType - notEqualTo: UriHostNameType - in: [UriHostNameType!] - notIn: [UriHostNameType!] + equalTo: UriHostNameType @cost(weight: "10") + notEqualTo: UriHostNameType @cost(weight: "10") + in: [UriHostNameType!] @cost(weight: "10") + notIn: [UriHostNameType!] @cost(weight: "10") } input UriPropositionInput { @@ -1280,23 +1232,27 @@ input UriPropositionInput { } input UuidPropositionInput { - equalTo: Uuid - notEqualTo: Uuid - in: [Uuid!] - notIn: [Uuid!] - greaterThan: Uuid - notGreaterThan: Uuid - greaterThanOrEqualTo: Uuid - notGreaterThanOrEqualTo: Uuid - lessThan: Uuid - notLessThan: Uuid - lessThanOrEqualTo: Uuid - notLessThanOrEqualTo: Uuid -} - + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid!] @cost(weight: "10") + notIn: [Uuid!] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + +"Defines when a policy shall be executed." enum ApplyPolicy { + "Before the resolver was executed." BEFORE_RESOLVER + "After the resolver was executed." AFTER_RESOLVER + "The policy is applied in the validation step before the execution." VALIDATION } @@ -1305,27 +1261,27 @@ enum CreateCalorimetricDataErrorCode { UNAUTHORIZED } -enum CreateGetHttpsResourceErrorCode { +enum CreateGeometricDataErrorCode { UNKNOWN UNAUTHORIZED } -enum CreateHygrothermalDataErrorCode { +enum CreateGetHttpsResourceErrorCode { UNKNOWN UNAUTHORIZED } -enum CreateOpticalDataErrorCode { +enum CreateHygrothermalDataErrorCode { UNKNOWN UNAUTHORIZED } -enum CreatePhotovoltaicDataErrorCode { +enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED } -enum CreateGeometricDataErrorCode { +enum CreatePhotovoltaicDataErrorCode { UNKNOWN UNAUTHORIZED } @@ -1389,6 +1345,15 @@ enum UriHostNameType { I_PV6 } +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION + +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." From e0823cf654e43f1fbfe128bf54cad59d957cdced Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 12:12:15 +0100 Subject: [PATCH 206/878] Navigate to nodes via edges (the shortcut `nodes` does not exist anymore in accordance with the latest Relay standard) --- frontend/pages/data/calorimetric/index.tsx | 2 +- frontend/pages/data/geometric/index.tsx | 2 +- frontend/pages/data/hygrothermal/index.tsx | 2 +- frontend/pages/data/index.tsx | 2 +- frontend/pages/data/optical/index.tsx | 2 +- frontend/pages/data/photovoltaic/index.tsx | 2 +- frontend/queries/data.graphql | 36 ++++++++++++++-------- 7 files changed, 30 insertions(+), 18 deletions(-) diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index 4531a8f4..c02f83e8 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -212,7 +212,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `CalorimetricData` is wrong and error prone! - setData((data?.allCalorimetricData?.nodes || []) as CalorimetricData[]); + setData((data?.allCalorimetricData?.edges.map((x) => x.node) || []) as CalorimetricData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index eefd53d8..adf168c3 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -169,7 +169,7 @@ function Page() { console.log(error); messageApi.error(error.graphQLErrors.map((error) => error.message)); } - setData((data?.allGeometricData?.nodes || []) as GeometricData[]); + setData((data?.allGeometricData?.edges.map((x) => x.node) || []) as GeometricData[]); } catch (error) { console.log("Failed:", error); } finally { diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 23fe5bb5..8ef63c9b 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -152,7 +152,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `HygrothermalData` is wrong and error prone! - setData((data?.allHygrothermalData?.nodes || []) as HygrothermalData[]); + setData((data?.allHygrothermalData?.edges.map((x) => x.node) || []) as HygrothermalData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/index.tsx b/frontend/pages/data/index.tsx index 22a0fdb0..728fa542 100644 --- a/frontend/pages/data/index.tsx +++ b/frontend/pages/data/index.tsx @@ -152,7 +152,7 @@ function Page() { ); } // TODO Casting to `Data` is wrong and error prone! - setData((dataX?.allData?.nodes || []) as Data[]); + setData((dataX?.allData?.edges.map((x) => x.node) || []) as Data[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index 4571597b..0006f8ab 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -297,7 +297,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `OpticalData` is wrong and error prone! - setData((data?.allOpticalData?.nodes || []) as OpticalData[]); + setData((data?.allOpticalData?.edges.map((x) => x.node) || []) as OpticalData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index 4001ee3e..cc3d8cc4 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -152,7 +152,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! - setData((data?.allPhotovoltaicData?.nodes || []) as PhotovoltaicData[]); + setData((data?.allPhotovoltaicData?.edges.map((x) => x.node) || []) as PhotovoltaicData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 01e8d2d8..6caa6c16 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -82,8 +82,10 @@ query Data($uuid: Uuid!) { query AllData($where: DataPropositionInput) { allData(where: $where) { - nodes { - ...DataPartial + edges { + node { + ...DataPartial + } } } } @@ -106,8 +108,10 @@ query CalorimetricData($uuid: Uuid!) { query AllCalorimetricData($where: CalorimetricDataPropositionInput) { allCalorimetricData(where: $where) { - nodes { - ...CalorimetricDataPartial + edges { + node { + ...CalorimetricDataPartial + } } } } @@ -128,8 +132,10 @@ query HygrothermalData($uuid: Uuid!) { query AllHygrothermalData($where: HygrothermalDataPropositionInput) { allHygrothermalData(where: $where) { - nodes { - ...HygrothermalDataPartial + edges { + node { + ...HygrothermalDataPartial + } } } } @@ -155,8 +161,10 @@ query OpticalData($uuid: Uuid!) { query AllOpticalData($where: OpticalDataPropositionInput) { allOpticalData(where: $where) { - nodes { - ...OpticalDataPartial + edges { + node { + ...OpticalDataPartial + } } } } @@ -177,8 +185,10 @@ query PhotovoltaicData($uuid: Uuid!) { query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { allPhotovoltaicData(where: $where) { - nodes { - ...PhotovoltaicDataPartial + edges { + node { + ...PhotovoltaicDataPartial + } } } } @@ -200,8 +210,10 @@ query GeometricData($uuid: Uuid!) { query AllGeometricData($where: GeometricDataPropositionInput) { allGeometricData(where: $where) { - nodes { - ...GeometricDataPartial + edges { + node { + ...GeometricDataPartial + } } } } From 1caf4890a5869dd7bd8763c24e8c8174482bd658 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 12:41:37 +0100 Subject: [PATCH 207/878] Fix spelling error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05651321..059c49d4 100644 --- a/README.md +++ b/README.md @@ -248,7 +248,7 @@ The file `Makefile.production` contains GNU Make targets to manage Docker containers like `up` and `down`, to follow Docker container logs with `logs`, to drop into shells inside running Docker containers like `shellb` for the backend service and `shellf` for the frontend service and `psql` for the -databse service, and to list information about Docker like `list` and +database service, and to list information about Docker like `list` and `list-services`. And the file contains GNU Make targets to deploy a new release or rollback it From f1b0ceee557edda1ad236485937dc99d2fc5da66 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 12:42:01 +0100 Subject: [PATCH 208/878] Correct error type --- frontend/lib/apollo.ts | 2 +- frontend/lib/form.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index dddb6dcd..e687c1ed 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -114,7 +114,7 @@ export function messageApolloError(error: ApolloError) { `Name(${error.name}); Message(${ error.message }); GraphQL Errors(${error.graphQLErrors - .map((e) => `[Name(${e.name}); Message(${e.message})]`) + .map((e) => `[Message(${e.message}); Path(${e.path?.join(".")})]`) .join(", ")}); Network Error(Name(${error.networkError?.name}); Message(${ error.networkError?.message }))` diff --git a/frontend/lib/form.ts b/frontend/lib/form.ts index 44367b40..3ea26554 100644 --- a/frontend/lib/form.ts +++ b/frontend/lib/form.ts @@ -1,9 +1,9 @@ -import { GraphQLError } from "graphql"; +import { GraphQLFormattedError } from "graphql"; import { FormInstance } from "antd"; import { Dispatch, SetStateAction } from "react"; export function handleFormErrors( - graphQlErrors: readonly GraphQLError[] | undefined, + graphQlErrors: readonly GraphQLFormattedError[] | undefined, userErrors: { code: string; message: string; path: string[] }[] | undefined, setGlobalErrorMessages: Dispatch>, form: FormInstance From 051383fff6893be701f1d5c3200aff7898af0574 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 12:42:30 +0100 Subject: [PATCH 209/878] Fix possible null reference --- frontend/pages/data/calorimetric/index.tsx | 2 +- frontend/pages/data/geometric/index.tsx | 2 +- frontend/pages/data/hygrothermal/index.tsx | 2 +- frontend/pages/data/index.tsx | 2 +- frontend/pages/data/optical/index.tsx | 2 +- frontend/pages/data/photovoltaic/index.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index c02f83e8..c6f84448 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -212,7 +212,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `CalorimetricData` is wrong and error prone! - setData((data?.allCalorimetricData?.edges.map((x) => x.node) || []) as CalorimetricData[]); + setData((data?.allCalorimetricData?.edges?.map((x) => x.node) || []) as CalorimetricData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index adf168c3..1fb137d3 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -169,7 +169,7 @@ function Page() { console.log(error); messageApi.error(error.graphQLErrors.map((error) => error.message)); } - setData((data?.allGeometricData?.edges.map((x) => x.node) || []) as GeometricData[]); + setData((data?.allGeometricData?.edges?.map((x) => x.node) || []) as GeometricData[]); } catch (error) { console.log("Failed:", error); } finally { diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 8ef63c9b..66eb6027 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -152,7 +152,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `HygrothermalData` is wrong and error prone! - setData((data?.allHygrothermalData?.edges.map((x) => x.node) || []) as HygrothermalData[]); + setData((data?.allHygrothermalData?.edges?.map((x) => x.node) || []) as HygrothermalData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/index.tsx b/frontend/pages/data/index.tsx index 728fa542..d04cceb2 100644 --- a/frontend/pages/data/index.tsx +++ b/frontend/pages/data/index.tsx @@ -152,7 +152,7 @@ function Page() { ); } // TODO Casting to `Data` is wrong and error prone! - setData((dataX?.allData?.edges.map((x) => x.node) || []) as Data[]); + setData((dataX?.allData?.edges?.map((x) => x.node) || []) as Data[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index 0006f8ab..38be023b 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -297,7 +297,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `OpticalData` is wrong and error prone! - setData((data?.allOpticalData?.edges.map((x) => x.node) || []) as OpticalData[]); + setData((data?.allOpticalData?.edges?.map((x) => x.node) || []) as OpticalData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index cc3d8cc4..3b116c4b 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -152,7 +152,7 @@ function Page() { messageApi.error(error.graphQLErrors.map((error) => error.message)); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! - setData((data?.allPhotovoltaicData?.edges.map((x) => x.node) || []) as PhotovoltaicData[]); + setData((data?.allPhotovoltaicData?.edges?.map((x) => x.node) || []) as PhotovoltaicData[]); } catch (error) { // TODO Handle properly. console.log("Failed:", error); From 46c6dc0afad7a688cf9f4138a53c4c5d2f4b8b24 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 14:25:46 +0100 Subject: [PATCH 210/878] Move editor config for C# into ./backend directory to make it available to dotnet CLI and vscode inside the `make shellb` --- .editorconfig => backend/.editorconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename .editorconfig => backend/.editorconfig (98%) diff --git a/.editorconfig b/backend/.editorconfig similarity index 98% rename from .editorconfig rename to backend/.editorconfig index e3a4b5a2..cc880f4b 100644 --- a/.editorconfig +++ b/backend/.editorconfig @@ -206,6 +206,9 @@ csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Blocks are allowed -csharp_prefer_braces = true:silent +csharp_prefer_braces = true:suggestion csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true + +# Namespace preferences +csharp_style_namespace_declarations = file_scoped:suggestion From cc1f223e0431719dac7227baf6d860ff732781bf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 14:26:06 +0100 Subject: [PATCH 211/878] Upgrade to ASP.NET Core 9.0 --- README.md | 4 ++-- backend/Dockerfile | 2 +- backend/Dockerfile-bootstrap | 2 +- backend/Dockerfile-production | 6 +++--- backend/dotnet-certificate-tool | 2 +- backend/dotnet-tools.json | 20 ++++++++++---------- backend/src/Database.csproj | 27 ++++++++++++++------------- backend/src/Startup.cs | 4 ++++ backend/test/Database.Tests.csproj | 6 +++--- 9 files changed, 39 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 059c49d4..2cfeae75 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ To debug the [ASP.NET Core web application](https://learn.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core), attach Visual Studio Code to the `metabase-backend-*` container, [press `Ctrl+Shift+P`, select "Debug: Attach to a .NET 5+ or .NET Core process"](https://code.visualstudio.com/docs/csharp/debugging#_attaching-to-a-process), -and choose the process `/app/src/bin/Debug/net8.0/Metabase run` titled +and choose the process `/app/src/bin/Debug/net9.0/Metabase run` titled `Metabase` or alternatively navigate to the "Run and Debug" pane (`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the "Start Debugging" icon (`F5`), and select the same process as above. Then, for @@ -145,7 +145,7 @@ and that attempt to solve that. Those extensions don't work in our case though, as they try to restart `dotnet watch` themselves, instead of waiting for the polling file watcher of `dotnet watch` to restart -`/app/src/bin/Debug/net8.0/Metabase run` and attach to that process. +`/app/src/bin/Debug/net9.0/Metabase run` and attach to that process. ## Deployment diff --git a/backend/Dockerfile b/backend/Dockerfile index 12547edb..7365a447 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -6,7 +6,7 @@ # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 ARG GROUP_ID ARG USER_ID diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 45efe9ef..15998e55 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -4,7 +4,7 @@ # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 ARG GROUP_ID ARG USER_ID diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 69a90010..f7ac6dd2 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -13,7 +13,7 @@ ######### # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build RUN \ apt-get update && \ @@ -46,7 +46,7 @@ RUN \ ########### # https://hub.docker.com/_/microsoft-dotnet-aspnet -FROM mcr.microsoft.com/dotnet/aspnet:8.0-bookworm-slim-amd64 +FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim-amd64 RUN \ apt-get update && \ @@ -64,7 +64,7 @@ WORKDIR /app COPY \ --from=build \ - /app/src/bin/Release/net8.0/publish \ + /app/src/bin/Release/net9.0/publish \ ./ RUN \ diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index db992a18..ca78f86a 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit db992a18fbe01b97315316af669c61cbf1eb63f1 +Subproject commit ca78f86ac1a99c97e2caea43aeb6106a02aef8cb diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 4bb5257d..db6c1105 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,28 +3,28 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "8.0.6", + "version": "9.0.0", "commands": [ "dotnet-aspnet-codegenerator" ], "rollForward": false }, "dotnet-counters": { - "version": "8.0.547301", + "version": "9.0.553101", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "8.0.547301", + "version": "9.0.553101", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "8.0.10", + "version": "9.0.0", "commands": [ "dotnet-ef" ], @@ -38,35 +38,35 @@ "rollForward": false }, "dotnet-trace": { - "version": "8.0.547301", + "version": "9.0.553101", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "8.0.547301", + "version": "9.0.553101", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "8.0.547301", + "version": "9.0.553101", "commands": [ "dotnet-sos" ], "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.9.2", + "version": "0.9.3", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.2.7", + "version": "2024.3.0", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "14.0.0", + "version": "14.1.0", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index dca9b878..019b0a12 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 12.0 31666837686487542159 @@ -23,22 +23,23 @@ - - - - - - + + + + + + - + - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -49,7 +50,7 @@ - + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 17f2e8c6..7099c2ad 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -118,6 +118,9 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO I consider the flattened structure a bug. How can we solve this? } ); + services.AddOpenApi(_ => { + _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; + }); } private void ConfigureMessageSenderServices(IServiceCollection services) @@ -246,6 +249,7 @@ public void Configure(WebApplication app) // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx /* app.UseWebSockets(); */ + app.MapOpenApi("/openapi/{documentName}.json"); app.MapGraphQL() .WithOptions( // https://chillicream.com/docs/hotchocolate/server/middleware diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index b01b5389..74e86804 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 false 12.0 false @@ -16,10 +16,10 @@ - + - + From ec89e746ca79b7a68884b87070d19b81ea321bb8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Nov 2024 15:04:29 +0100 Subject: [PATCH 212/878] Do not prefer conditional expressions over if-then-else --- backend/.editorconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/.editorconfig b/backend/.editorconfig index cc880f4b..3eb2414c 100644 --- a/backend/.editorconfig +++ b/backend/.editorconfig @@ -49,6 +49,9 @@ dotnet_style_qualification_for_property = false:refactoring dotnet_style_qualification_for_method = false:refactoring dotnet_style_qualification_for_event = false:refactoring +# Do not use conditional expression for return +dotnet_style_prefer_conditional_expression_over_return = false + # Use language keywords instead of framework type names for type references dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion From 6071702a843df4e63093c386af56b4bf5201e1ea Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Mon, 28 Oct 2024 16:56:13 +0100 Subject: [PATCH 213/878] Initial commit --- .../components/data/calorimetric/CalorimetricData.tsx | 2 ++ frontend/components/data/geometric/GeometricData.tsx | 3 +++ .../components/data/hygrothermal/HygrothermalData.tsx | 2 ++ frontend/components/data/optical/OpticalData.tsx | 8 ++++++++ .../components/data/photovoltaic/PhotovoltaicData.tsx | 2 ++ 5 files changed, 17 insertions(+) diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 183956cb..5cbf5a4a 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -67,6 +67,8 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat {calorimetricData.creatorId} + {calorimetricData.description} + {calorimetricData.createdAt} ); diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index b9e4b9e9..20f76e42 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -64,6 +64,9 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { {geometricData.creatorId} + {geometricData.description} + {geometricData.createdAt} + {geometricData.Thicknesses} ); diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index 315c2d34..ab14692e 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -67,6 +67,8 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat {hygrothermalData.creatorId} + {hygrothermalData.description} + {hygrothermalData.createdAt} ); diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 1feaf647..90f0a1cc 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -65,6 +65,14 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { {opticalData.creatorId} + {opticalData.description} + {opticalData.createdAt} + {opticalData.nearnormalHemisphericalVisibleTransmittances} + {opticalData.nearnormalHemisphericalVisibleReflectances} + {opticalData.nearnormalHemisphericalSolarTransmittances} + {opticalData.nearnormalHemisphericalSolarReflectances} + {opticalData.infraredEmittances} + {opticalData.colorRenderingIndices} ); diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index f5a36c33..414aed59 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -67,6 +67,8 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat {photovoltaicData.creatorId} + {photovoltaicData.description} + {photovoltaicData.createdAt} ); From 52abc744d3d8f681d8bc79be0c7ba679d9ef2b26 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 25 Nov 2024 18:25:45 +0100 Subject: [PATCH 214/878] Update C# language version to 13.0 --- backend/src/Database.csproj | 2 +- backend/test/Database.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 019b0a12..94706cf6 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -2,7 +2,7 @@ net9.0 - 12.0 + 13.0 31666837686487542159 enable diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 74e86804..aa3186d1 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -3,7 +3,7 @@ net9.0 false - 12.0 + 13.0 false enable From 33457f16ec8dfeb0aa644b44ae8804fd15f5c2ae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 25 Nov 2024 18:41:40 +0100 Subject: [PATCH 215/878] Disable HTTP metric for health checks --- see https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-9.0?view=aspnetcore-9.0#opt-out-of-http-metrics-on-certain-endpoints-and-requests --- backend/src/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7099c2ad..14bc8430 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -277,7 +277,7 @@ public void Configure(WebApplication app) { ResponseWriter = WriteJsonResponse } - ); + ).DisableHttpMetrics(); } // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output From 09d951ccb64860ad9cfa3f6d068e96b474da5eaa Mon Sep 17 00:00:00 2001 From: Shreya Ponnamma Date: Mon, 25 Nov 2024 18:46:54 +0100 Subject: [PATCH 216/878] modification --- .../data/calorimetric/CalorimetricData.tsx | 15 ++++++++- .../data/geometric/GeometricData.tsx | 13 ++++++-- .../data/hygrothermal/HygrothermalData.tsx | 2 +- .../components/data/optical/OpticalData.tsx | 33 +++++++++++++++---- .../data/photovoltaic/PhotovoltaicData.tsx | 2 +- 5 files changed, 53 insertions(+), 12 deletions(-) diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 5cbf5a4a..4bbc0159 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -68,7 +68,20 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat {calorimetricData.description} - {calorimetricData.createdAt} + {calorimetricData.createdAt} + + + {calorimetricData.appliedMethod.methodId} + + + + {calorimetricData.gValues.map((x) => x.toLocaleString("en")).join(", ")} + + + {calorimetricData.uValues.map((x) => x.toLocaleString("en")).join(", ")} + ); diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 20f76e42..5d9fc930 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -65,8 +65,17 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { {geometricData.description} - {geometricData.createdAt} - {geometricData.Thicknesses} + {geometricData.createdAt} + + + {geometricData.appliedMethod.methodId} + + + + {geometricData.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} + ); diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index ab14692e..1621307f 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -68,7 +68,7 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat {hygrothermalData.description} - {hygrothermalData.createdAt} + {hygrothermalData.createdAt} ); diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 90f0a1cc..874f4b6b 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -66,13 +66,32 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { {opticalData.description} - {opticalData.createdAt} - {opticalData.nearnormalHemisphericalVisibleTransmittances} - {opticalData.nearnormalHemisphericalVisibleReflectances} - {opticalData.nearnormalHemisphericalSolarTransmittances} - {opticalData.nearnormalHemisphericalSolarReflectances} - {opticalData.infraredEmittances} - {opticalData.colorRenderingIndices} + {opticalData.createdAt} + + + {opticalData.appliedMethod.methodId} + + + + {opticalData.nearnormalHemisphericalVisibleTransmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalVisibleReflectances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalSolarTransmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalSolarReflectances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.infraredEmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.colorRenderingIndices.map((x) => x.toLocaleString("en")).join(", ")} + ); diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 414aed59..35a2e7a9 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -68,7 +68,7 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat {photovoltaicData.description} - {photovoltaicData.createdAt} + {photovoltaicData.createdAt} ); From 07f03ad62f2c5a69b83302603bd74003742b8910 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Dec 2024 11:07:59 +0100 Subject: [PATCH 217/878] Debug ./backend/.editorconfig with `editorconfig` command --- backend/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/Dockerfile b/backend/Dockerfile index 7365a447..cad070c6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,6 +22,10 @@ RUN \ # As `root` # ############# +# `editorconfig` can be used to debug ./backend/.editorconfig used for example +# by `dotnet format` and analyzers run by `dotnet build`. See +# https://github.com/editorconfig/editorconfig/issues/358 +# # `lldb` is needed by dotnet tool `dotnet-sos`, see # https://github.com/dotnet/diagnostics#net-core-diagnostics-repo # `openssl` is needed to debug SSL certificates @@ -44,6 +48,7 @@ RUN \ --no-install-recommends \ curl \ dumb-init \ + editorconfig \ gawk \ lldb \ make \ From c55e03782a632ddb2539d074bdf210ee5700141d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Dec 2024 11:11:18 +0100 Subject: [PATCH 218/878] Format code with `dotnet format` and remove JetBrain ReSharper GNU Make targets (as they introduce errors) --- backend/Makefile | 56 ++++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index 8faed0ee..2c30a322 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -15,20 +15,9 @@ help : ## Print this help # List of dotnet commands # https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet -# TODO Shortcuts for `dotnet aspnet-codegenerator`? # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test -# Sadly `--list-tests` ignores `--filter` which was reported in 2017 and has -# not yet been fixed, see https://github.com/dotnet/sdk/issues/8643 -# This makes the following code snippet obsolete -# ``` -# echo "- - - - - - - - - - - - - - - - - - - - - - - - - " && \ -# echo "Test list for file $${x}" && \ -# echo "- - - - - - - - - - - - - - - - - - - - - - - - - " && \ -# dotnet test \ -# --filter "FullyQualifiedName~$${x}" \ -# --list-tests -# ``` +# dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal test : ## Run tests @echo "--------------------------------------------------" @echo "Complete test list" @@ -38,15 +27,8 @@ test : ## Run tests @echo "--------------------------------------------------" @echo "All test runs" @echo "--------------------------------------------------" - for x in $$(find ./test/ -type f -name "*Tests.cs" -exec basename {} '.cs' \;) ; do \ - echo "- - - - - - - - - - - - - - - - - - - - - - - - - " && \ - echo "Test runs for file '$${x}'" && \ - echo "- - - - - - - - - - - - - - - - - - - - - - - - - " && \ - dotnet test \ - --filter "FullyQualifiedName~$${x}" \ - --collect:"XPlat Code Coverage" \ - --verbosity minimal ; \ - done + dotnet test \ + --collect:"XPlat Code Coverage" .PHONY : test assets : ## Build assets @@ -73,6 +55,9 @@ trace : ## Trace the dotnet process `Database` --providers Microsoft-Windows-DotNETRuntime .PHONY : trace +# If `graphqurl` were installed, the schema could be introspected with +# `gq http://backend:8080/graphql/ --introspect` +# See https://github.com/hasura/graphqurl#export-schema update-schema : ## Update GraphQl schema wget \ --output-document ./test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap \ @@ -89,6 +74,10 @@ inspect : ## Inspect with ReSharper, for example, `make OUTPUT=./inspection.xml dotnet tool run jb inspectcode --build ./Database.sln -o ${OUTPUT} .PHONY : inspect +editorconfig : ## Debug editorconfig + editorconfig ./.editorconfig +.PHONY : editorconfig + play : ## Run tests in `Database.Tests.Play` (semi-alternative to the buggy `dotnet script` REPL) dotnet test --filter "FullyQualifiedName~Database.Tests.Play" .PHONY : play @@ -109,19 +98,20 @@ dos2unix : ## Strip the byte-order mark, also known as, BOM, and remove carriage # find . -type f -exec dos2unix {} \; .PHONY : dos2unix -format : ## Format code - dotnet tool run jb cleanupcode --profile="Built-in: Reformat Code" ./Database.sln -.PHONY : format - -syntax : ## Apply syntax style - dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./Database.sln -.PHONY : syntax - +# See https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-format +# We do not run JetBrains ReSharper cleanupcode because it does wrong things +# like removing private setters needed by EntityFramework Core. We used to run +# it as follows: For formatting +# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat Code" ./Database.sln` +# For syntax styling +# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./Database.sln` +# For cleanup +# `dotnet tool run jb cleanupcode ./Database.sln` # The profile `Built-in: Full Cleanup` does not update file headers. # See https://www.jetbrains.com/help/resharper/Code_Cleanup__Index.html#profiles -cleanup : ## Clean-up code - dotnet tool run jb cleanupcode ./Database.sln -.PHONY : cleanup +format : ## Format code + dotnet format ./Database.sln +.PHONY : format update-packages : ## Update packages for subproject in src test ; do ( \ @@ -170,7 +160,7 @@ diagrams : ## Draw images from textual UML diagrams # See https://github.com/hasura/graphqurl#export-schema schema : ## Fetch GraphQL schema using `hasura/graphqurl` - gq http://backend:8080/graphql/ --introspect > schema.graphql + gq http://backend:8080/graphql/ --introspect > ./schema.graphql .PHONY : schema add-migration : ## Add migration with name `${NAME}`, for example, `make NAME=InitialCreate add-migration` From fb3f6ae8d08c98979665d5200c5fb3142b11ff74 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Dec 2024 11:18:45 +0100 Subject: [PATCH 219/878] Configure database context centrally once --- backend/src/Startup.cs | 80 ++++++++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 14bc8430..24cb26fd 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -27,24 +27,18 @@ namespace Database; -public sealed class Startup +public sealed class Startup( + IWebHostEnvironment environment, + IConfiguration configuration + ) { private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; private const string AntiforgeryHeaderName = "X-XSRF-TOKEN"; - private readonly AppSettings _appSettings; - - private readonly IWebHostEnvironment _environment; - - public Startup( - IWebHostEnvironment environment, - IConfiguration configuration - ) - { - _environment = environment; - _appSettings = configuration.Get() ?? + private readonly AppSettings _appSettings = configuration.Get() ?? throw new InvalidOperationException( "Failed to get application settings from configuration."); - } + + private readonly IWebHostEnvironment _environment = environment; public void ConfigureServices(IServiceCollection services) { @@ -157,47 +151,50 @@ IWebHostEnvironment environment private static void ConfigureDatabaseContext( DbContextOptionsBuilder options, - IWebHostEnvironment environment + IWebHostEnvironment environment, + AppSettings appSettings ) { + var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); + // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum + // Keep in sync with `ApplicationDbContext.CreateEnumerations`. + dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); + dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); + options + .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) + .UseSchemaName(appSettings.Database.SchemaName) + .UseOpenIddict(); if (!environment.IsProduction()) + { options .EnableSensitiveDataLogging() .EnableDetailedErrors(); + } + if (environment.IsEnvironment(Program.TestEnvironment)) + { options.ConfigureWarnings(x => x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) ); + } } private void ConfigureDatabaseServices(IServiceCollection services) { - services.AddPooledDbContextFactory(options => - { - var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - // Keep in sync with `ApplicationDbContext.CreateEnumerations`. - dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); - dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); - options - .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) - .UseSchemaName(_appSettings.Database.SchemaName) - .UseOpenIddict(); - ConfigureDatabaseContext(options, _environment); - } - ); + services.AddPooledDbContextFactory(options => {}); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. services.AddDbContext( (services, options) => - { - ConfigureDatabaseContext(options, _environment); services .GetRequiredService>() - .CreateDbContext(); - }, + .CreateDbContext(), ServiceLifetime.Transient ); + services.ConfigureDbContext(options => + ConfigureDatabaseContext(options, _environment, _appSettings), + ServiceLifetime.Singleton + ); } private static void ConfigureHttpClientServices(IServiceCollection services) @@ -302,7 +299,10 @@ private static Task WriteJsonResponse(HttpContext context, HealthReport healthRe jsonWriter.WriteString("duration", healthReportEntry.Value.Duration.ToString()); jsonWriter.WriteStartArray("tags"); - foreach (var tag in healthReportEntry.Value.Tags) jsonWriter.WriteStringValue(tag); + foreach (var tag in healthReportEntry.Value.Tags) + { + jsonWriter.WriteStringValue(tag); + } jsonWriter.WriteEndArray(); var exception = healthReportEntry.Value.Exception; @@ -310,15 +310,25 @@ private static Task WriteJsonResponse(HttpContext context, HealthReport healthRe { jsonWriter.WriteStartObject("exception"); jsonWriter.WriteString("message", exception.Message); - if (exception.StackTrace is not null) jsonWriter.WriteString("stackTrace", exception.StackTrace); + if (exception.StackTrace is not null) + { + jsonWriter.WriteString("stackTrace", exception.StackTrace); + } if (exception.InnerException is not null) + { jsonWriter.WriteString("innerException", exception.InnerException.ToString()); + } - if (exception.Source is not null) jsonWriter.WriteString("source", exception.Source); + if (exception.Source is not null) + { + jsonWriter.WriteString("source", exception.Source); + } if (exception.TargetSite is not null) + { jsonWriter.WriteString("targetSite", exception.TargetSite.ToString()); + } jsonWriter.WriteEndObject(); } From 5fe13cbc6f4234430e2c726aa0ecffcc0bbd94bb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 9 Dec 2024 14:12:27 +0100 Subject: [PATCH 220/878] Use the generic GetCustomAttribute --- backend/src/Utilities/FileHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 6fb8eadd..c5b28cd6 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -78,7 +78,7 @@ public static async Task ProcessFormFile(IFormFile formFile, formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); if (property != null) - if (property.GetCustomAttribute(typeof(DisplayAttribute)) is + if (property.GetCustomAttribute() is DisplayAttribute displayAttribute) fieldDisplayName = $"{displayAttribute.Name} "; From 95e6ff76d7809a4ad0eb70f7975b27f093258a84 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 9 Dec 2024 14:13:13 +0100 Subject: [PATCH 221/878] Correct variable name of AppSettings --- backend/src/Startup.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 24cb26fd..32d1d557 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.Logging; using Npgsql; using Serilog; +using Microsoft.OpenApi; namespace Database; @@ -155,11 +156,11 @@ private static void ConfigureDatabaseContext( AppSettings appSettings ) { - var dataSourceBuilder = new NpgsqlDataSourceBuilder(_appSettings.Database.ConnectionString); + var dataSourceBuilder = new NpgsqlDataSourceBuilder(appSettings.Database.ConnectionString); // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum // Keep in sync with `ApplicationDbContext.CreateEnumerations`. - dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); - dataSourceBuilder.MapEnum($"{_appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); + dataSourceBuilder.MapEnum($"{appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); + dataSourceBuilder.MapEnum($"{appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); options .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) .UseSchemaName(appSettings.Database.SchemaName) From 7f5db46625e008873c284629718aae0754c37c49 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 9 Dec 2024 14:25:19 +0100 Subject: [PATCH 222/878] Upgrade HotChocolate and Npgsql to .NET 9 compatible versions (the latest ones actually) --- backend/dotnet-tools.json | 4 ++-- backend/src/Database.csproj | 18 +++++++++--------- backend/test/Database.Tests.csproj | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index db6c1105..8a09824d 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -31,7 +31,7 @@ "rollForward": false }, "dotnet-script": { - "version": "1.5.0", + "version": "1.6.0", "commands": [ "dotnet-script" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "14.1.0", + "version": "14.2.0", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 94706cf6..a76166cf 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -23,14 +23,14 @@ - - - - - - + + + + + + - + @@ -40,7 +40,7 @@ all - + @@ -51,7 +51,7 @@ - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index aa3186d1..da64db1f 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,11 +16,11 @@ - + - + - + From fd52d8d3437ae142b8464c3448c8e5033c7b9fbe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 10 Dec 2024 17:15:38 +0100 Subject: [PATCH 223/878] Set default page size to 10 --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 61ccd512..a7366240 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -171,7 +171,7 @@ AppSettings appSettings .ModifyPagingOptions(_ => { _.MaxPageSize = int.MaxValue - 1; - _.DefaultPageSize = int.MaxValue - 1; + _.DefaultPageSize = 10; _.IncludeTotalCount = true; _.IncludeNodesField = false; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. From c23005ebd63331b4dff2f3cbafb9bcad05487ebc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 10 Dec 2024 19:12:05 +0100 Subject: [PATCH 224/878] Set maximum page size to 100 (this is also the maximum used in GitHub's GraphQL API https://docs.github.com/en/graphql/guides/using-pagination-in-the-graphql-api) --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index a7366240..51946a77 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -170,7 +170,7 @@ AppSettings appSettings .AddDbContextCursorPagingProvider() .ModifyPagingOptions(_ => { - _.MaxPageSize = int.MaxValue - 1; + _.MaxPageSize = 100; _.DefaultPageSize = 10; _.IncludeTotalCount = true; _.IncludeNodesField = false; From 3270f97b28b1d7be6b404f01bf2a6eb44ee338f6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 19 Dec 2024 16:08:26 +0100 Subject: [PATCH 225/878] Improve Visual Studio Code integration --- backend/Dockerfile | 4 ++-- docker-compose.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 7365a447..e3d5b301 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -304,8 +304,8 @@ RUN \ ./test/bin \ ./test/obj && \ mkdir --parents \ - ${HOME}/.vscode-server/extensions \ - ${HOME}/.vscode-server-insiders/extensions && \ + ${HOME}/.vscode-server \ + ${HOME}/.vscode-server-insiders && \ mkdir --parents \ ${HOME}/.nuget \ ${HOME}/.dotnet diff --git a/docker-compose.yml b/docker-compose.yml index 88180d2f..dd7f2127 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,8 +81,8 @@ services: volumes: - ./backend:/app - files:/app/src/files - - backend_vscode_server_extensions:/home/me/.vscode-server/extensions - - backend_vscode_server_insiders_extensions:/home/me/.vscode-server-insiders/extensions + - backend_vscode_server:/home/me/.vscode-server + - backend_vscode_server_insiders:/home/me/.vscode-server-insiders # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -189,5 +189,5 @@ volumes: data: emails: files: - backend_vscode_server_extensions: - backend_vscode_server_insiders_extensions: + backend_vscode_server: + backend_vscode_server_insiders: From 7fdb138faa0e13d5f41946dfa3fb3aac4a26d0a0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Dec 2024 15:02:42 +0100 Subject: [PATCH 226/878] Raise default page size to 100 --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 51946a77..db86391c 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -171,7 +171,7 @@ AppSettings appSettings .ModifyPagingOptions(_ => { _.MaxPageSize = 100; - _.DefaultPageSize = 10; + _.DefaultPageSize = 100; _.IncludeTotalCount = true; _.IncludeNodesField = false; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. From 8773ed0b0f6d6152bdf24b359bb5d18e7f985924 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Jan 2025 11:30:39 +0100 Subject: [PATCH 227/878] Configure pooled database context factory, database context, and the options lifetime properly --- see https://github.com/npgsql/efcore.pg/issues/3394#issuecomment-2565546090 --- backend/src/Startup.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 32d1d557..2f0343a0 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -182,20 +182,20 @@ AppSettings appSettings private void ConfigureDatabaseServices(IServiceCollection services) { - services.AddPooledDbContextFactory(options => {}); + services.AddPooledDbContextFactory(options => + ConfigureDatabaseContext(options, _environment, _appSettings) + ); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. - services.AddDbContext( - (services, options) => - services - .GetRequiredService>() - .CreateDbContext(), - ServiceLifetime.Transient - ); - services.ConfigureDbContext(options => + services.AddDbContext(options => ConfigureDatabaseContext(options, _environment, _appSettings), - ServiceLifetime.Singleton + contextLifetime: ServiceLifetime.Transient, + optionsLifetime: ServiceLifetime.Singleton ); + // services.ConfigureDbContext(options => + // ConfigureDatabaseContext(options, _environment, _appSettings), + // optionsLifetime: ServiceLifetime.Singleton + // ); } private static void ConfigureHttpClientServices(IServiceCollection services) From 5fd1c41bee64fc1dd584ef6c6d2af3891fbe1288 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Jan 2025 11:31:42 +0100 Subject: [PATCH 228/878] Remove flattened view lookup (the views are compiled into the correct directory structure by now) --- backend/src/Startup.cs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2f0343a0..2c4c9f2c 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -85,34 +85,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services .AllowAnyMethod() ) ); - services.AddControllersWithViews() - .AddRazorOptions(_ => - { - // The default location formarts are `/Views/{1}/{0}.cshtml` and `/Views/Shared/{0}.cshtml`. - // Add the format that ignores the directory structure, that is, - // leave out `/Views` and the controller name `{1}`. For some - // reason compiled views land in the root directory. The - // debugging output of `make logs` when navigating to - // https://local.buildingenvelopedata.org:4041/connect/logout?... - // is: - // Initializing Razor view compiler with compiled view: '/Authorize.cshtml'. - // Initializing Razor view compiler with compiled view: '/Logout.cshtml'. - // Initializing Razor view compiler with compiled view: '/Verify.cshtml'. - // Initializing Razor view compiler with compiled view: '/Error.cshtml'. - // Initializing Razor view compiler with compiled view: '/_Layout.cshtml'. - // Initializing Razor view compiler with compiled view: '/_ViewImports.cshtml'. - // Initializing Razor view compiler with compiled view: '/_ViewStart.cshtml'. - // View lookup cache miss for view 'Logout' in controller 'Authorization'. - // Could not find a file for view at path '/Views/Authorization/Logout.cshtml'. - // Could not find a file for view at path '/Views/Shared/Logout.cshtml'. - // Located compiled view for view at path '/Logout.cshtml'. - // Located compiled view for view at path '/_ViewStart.cshtml'. - // Executing ViewResult, running view Logout. - // The view path '/Logout.cshtml' was found in 5.0269ms. - _.ViewLocationFormats.Add("/{0}.cshtml"); - // TODO I consider the flattened structure a bug. How can we solve this? - } - ); + services.AddControllersWithViews(); services.AddOpenApi(_ => { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; }); From 964432865931b997f73309f7f780a9c2fc61f21c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Jan 2025 15:28:03 +0100 Subject: [PATCH 229/878] Fix language error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2cfeae75..73e6c4bd 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ If you have a question for which you don't find the answer in this repository, p [GNU Make](https://www.gnu.org/software/make/). 1. List all GNU Make targets by running `make help`. 1. Generate and trust a self-signed certificate authority and SSL certificates - by running `make ssl`. If you are locally working the the metabase and the + by running `make ssl`. If you are locally working on the metabase and the database and if you need them to communicate over HTTPS, then instead of running `make ssl`, make the `CERTIFICATE_AUTHORITY_*` variable values in the `.env` file match the ones from the metabase, copy the certificate From 07e4a0322fa18d2984c7ad307eaf0e1270c99cde Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Jan 2025 16:24:19 +0100 Subject: [PATCH 230/878] Allow introspection explicitly (although this should be the default) --- backend/src/Configuration/GraphQlConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 61ccd512..334481ae 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -60,6 +60,7 @@ AppSettings appSettings // GraphQL Server services .AddGraphQLServer() + .AllowIntrospection(true) // this can be fine tuned if needed: https://chillicream.com/docs/hotchocolate/v14/server/introspection/#allowlisting-requests // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContextFactory() .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) From a56218c2c8924d3d467bc751859daaf9462b5fb7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Jan 2025 12:57:24 +0100 Subject: [PATCH 231/878] Remove obsolete introspection allowance --- backend/src/Configuration/GraphQlConfiguration.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 334481ae..61ccd512 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -60,7 +60,6 @@ AppSettings appSettings // GraphQL Server services .AddGraphQLServer() - .AllowIntrospection(true) // this can be fine tuned if needed: https://chillicream.com/docs/hotchocolate/v14/server/introspection/#allowlisting-requests // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContextFactory() .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) From 446c01c7b6242a8fffb800f30fcf2bfae1952697 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Jan 2025 18:01:03 +0100 Subject: [PATCH 232/878] Upgrade certificate tool --- backend/dotnet-certificate-tool | 2 +- backend/dotnet-tools.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index ca78f86a..d3d0454b 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit ca78f86ac1a99c97e2caea43aeb6106a02aef8cb +Subproject commit d3d0454b971a7bd509b55bbdc882bcd166023c52 diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 8a09824d..889d64a1 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -87,7 +87,7 @@ "rollForward": false }, "dotnet-certificate-tool": { - "version": "3.1.0", + "version": "3.2.0", "commands": [ "certificate-tool" ], From 8d6a7be8943fce70b7f7749ad095427921497bc4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Jan 2025 18:03:13 +0100 Subject: [PATCH 233/878] Use non-obsolete certificate loader --- see https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0057 --- backend/src/Configuration/AuthConfiguration.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 348e1d03..0be86773 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -40,13 +40,17 @@ private static X509Certificate2 LoadCertificate( string password ) { - if (string.IsNullOrEmpty(password)) throw new ArgumentException($"Empty password for certificate {fileName}."); + if (string.IsNullOrEmpty(password)) + { + throw new ArgumentException($"Empty password for certificate {fileName}."); + } + var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream($"Database.{fileName}") ?? throw new ArgumentException($"Missing certificate {fileName}."); using var buffer = new MemoryStream(); stream.CopyTo(buffer); - return new X509Certificate2( + return X509CertificateLoader.LoadPkcs12( buffer.ToArray(), password, X509KeyStorageFlags.EphemeralKeySet From b8c878c2822ac9c99b4b1c972f966ce3875afb73 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 Jan 2025 19:18:49 +0100 Subject: [PATCH 234/878] Lookup views in the flattened structure again (because it is still flat) --- backend/src/Startup.cs | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2c4c9f2c..2f0343a0 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -85,7 +85,34 @@ private static void ConfigureRequestResponseServices(IServiceCollection services .AllowAnyMethod() ) ); - services.AddControllersWithViews(); + services.AddControllersWithViews() + .AddRazorOptions(_ => + { + // The default location formarts are `/Views/{1}/{0}.cshtml` and `/Views/Shared/{0}.cshtml`. + // Add the format that ignores the directory structure, that is, + // leave out `/Views` and the controller name `{1}`. For some + // reason compiled views land in the root directory. The + // debugging output of `make logs` when navigating to + // https://local.buildingenvelopedata.org:4041/connect/logout?... + // is: + // Initializing Razor view compiler with compiled view: '/Authorize.cshtml'. + // Initializing Razor view compiler with compiled view: '/Logout.cshtml'. + // Initializing Razor view compiler with compiled view: '/Verify.cshtml'. + // Initializing Razor view compiler with compiled view: '/Error.cshtml'. + // Initializing Razor view compiler with compiled view: '/_Layout.cshtml'. + // Initializing Razor view compiler with compiled view: '/_ViewImports.cshtml'. + // Initializing Razor view compiler with compiled view: '/_ViewStart.cshtml'. + // View lookup cache miss for view 'Logout' in controller 'Authorization'. + // Could not find a file for view at path '/Views/Authorization/Logout.cshtml'. + // Could not find a file for view at path '/Views/Shared/Logout.cshtml'. + // Located compiled view for view at path '/Logout.cshtml'. + // Located compiled view for view at path '/_ViewStart.cshtml'. + // Executing ViewResult, running view Logout. + // The view path '/Logout.cshtml' was found in 5.0269ms. + _.ViewLocationFormats.Add("/{0}.cshtml"); + // TODO I consider the flattened structure a bug. How can we solve this? + } + ); services.AddOpenApi(_ => { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; }); From 1b18162423fde7a61e2067cf287b3ad9c7f08fe0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 Jan 2025 19:20:15 +0100 Subject: [PATCH 235/878] Dangerously accept any server certificates in development (as for some reason the HTTP clients issued by `HttpClientFactory` which are used by us when quering the metabase and by OpenIddict do not recognize our certificate authority although it is installed in the system and .Net Core's Root `X509Store` of the current user (code snippets below) awk -v cmd=\'openssl x509 -noout -subject' ' /BEGIN/{close(cmd)};{print | cmd}' < /etc/ssl/certs/ca-certificates.crt var store = new X509Store(StoreName.Root, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly); var certificates = store.Certificates; foreach (var certificate in certificates) { var xname = certificate.Subject; Console.WriteLine(xname); } store.Close(); --- .../src/Configuration/AuthConfiguration.cs | 20 ++++++++++++++++--- backend/src/Metabase/QueryingMetabase.cs | 8 ++++---- backend/src/Startup.cs | 16 +++++++++++++-- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 0be86773..6b13275d 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Reflection; +using System.Net.Http; using System.Security.Cryptography.X509Certificates; using Database.Data; using Microsoft.AspNetCore.Hosting; @@ -32,7 +33,7 @@ AppSettings appSettings appSettings.JsonWebToken.SigningCertificatePassword); ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); - ConfigureOpenIddictServices(services, appSettings, encryptionCertificate, signingCertificate); + ConfigureOpenIddictServices(services, environment, appSettings, encryptionCertificate, signingCertificate); } private static X509Certificate2 LoadCertificate( @@ -119,6 +120,7 @@ IWebHostEnvironment environment private static void ConfigureOpenIddictServices( IServiceCollection services, + IWebHostEnvironment environment, AppSettings appSettings, X509Certificate2 encryptionCertificate, X509Certificate2 signingCertificate @@ -152,7 +154,13 @@ X509Certificate2 signingCertificate // Register the ASP.NET Core host. _.UseAspNetCore(); // Register the System.Net.Http integration. - _.UseSystemNetHttp(); + _.UseSystemNetHttp() + .ConfigureHttpClientHandler(handler => { + if (environment.IsDevelopment()) { + // https://documentation.openiddict.com/integrations/system-net-http#register-a-custom-httpclienthandler-configuration-delegate + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + } + }); }) .AddClient(_ => { @@ -174,7 +182,13 @@ X509Certificate2 signingCertificate // assembly as a more specific user agent, which can be useful when dealing with // providers that use the user agent as a way to throttle requests (e.g Reddit). _.UseSystemNetHttp() - .SetProductInformation(typeof(Startup).Assembly); + .SetProductInformation(typeof(Startup).Assembly) + .ConfigureHttpClientHandler(handler => { + if (environment.IsDevelopment()) { + // https://documentation.openiddict.com/integrations/system-net-http#register-a-custom-httpclienthandler-configuration-delegate + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + } + }); // Add a client registration matching the client application definition in the server project. _.AddRegistration( diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs index e024567c..0448c06b 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -14,12 +14,13 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using OpenIddict.Client.AspNetCore; -using Yoh.Text.Json.NamingPolicies; namespace Database.Metabase; public sealed class QueryingMetabase { + public const string MetabaseHttpClient = "Metabase"; + private static readonly JsonSerializerOptions GraphQlSerializerOptions = new() { @@ -40,8 +41,7 @@ public sealed class QueryingMetabase Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, NumberHandling = JsonNumberHandling.Strict, PropertyNameCaseInsensitive = false, - // TODO When we run .NET 8, remove [Yoh.Text.Json.NamingPolicies](https://github.com/YohDeadfall/Yoh.Text.Json.NamingPolicies) and use [.NET's inbuilt snake-case support](https://github.com/dotnet/runtime/pull/69613). - PropertyNamingPolicy = JsonNamingPolicies.SnakeCaseLower, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, ReadCommentHandling = JsonCommentHandling.Disallow, IncludeFields = false, IgnoreReadOnlyProperties = false, @@ -142,7 +142,7 @@ CancellationToken cancellationToken ) where TResponse : class { - using var httpClient = httpClientFactory.CreateClient(); + using var httpClient = httpClientFactory.CreateClient(MetabaseHttpClient); var bearerToken = await ExtractBearerToken(httpContextAccessor).ConfigureAwait(false); using var httpRequestMessage = new HttpRequestMessage( httpMethod, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2f0343a0..4b0a31b0 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -3,12 +3,14 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; +using System.Net.Http; using HotChocolate.AspNetCore; using Database.Configuration; using Database.Data; using Database.Data.Extensions; using Database.Enumerations; using Database.Services; +using Database.Metabase; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -53,7 +55,7 @@ public void ConfigureServices(IServiceCollection services) services .AddDataProtection() .PersistKeysToDbContext(); - ConfigureHttpClientServices(services); + ConfigureHttpClientServices(services, _environment); services.AddHttpContextAccessor(); services .AddHealthChecks() @@ -198,9 +200,19 @@ private void ConfigureDatabaseServices(IServiceCollection services) // ); } - private static void ConfigureHttpClientServices(IServiceCollection services) + private static void ConfigureHttpClientServices(IServiceCollection services, IWebHostEnvironment environment) { services.AddHttpClient(); + var metabasesHttpClientBuilder = services.AddHttpClient(QueryingMetabase.MetabaseHttpClient); + if (environment.IsDevelopment()) + { + metabasesHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } + ); + } } public void Configure(WebApplication app) From fa1bf3d30362ccb74ea3735ff20d60156976278a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 23 Jan 2025 17:12:38 +0100 Subject: [PATCH 236/878] Upgrade to latest package versions --- backend/dotnet-tools.json | 6 ++--- backend/src/Database.csproj | 37 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 14 +++++------ 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 889d64a1..cf683ba0 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "9.0.0", + "version": "9.0.1", "commands": [ "dotnet-ef" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.3.0", + "version": "2024.3.3", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "14.2.0", + "version": "14.3.0", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index a76166cf..55f2276b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -23,40 +23,39 @@ - - - - - - + + + + + + - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - + + + + - diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index da64db1f..f45dc2f6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,16 +16,16 @@ - + - - + + - + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all From b3e19b5a2c7081b6fe7ae2318f55daa440b05cc2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:36:46 +0100 Subject: [PATCH 237/878] Generate runtime configuration files to make using `docker run --project ...csproj` possible --- backend/src/Database.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 55f2276b..b228421d 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -12,6 +12,7 @@ true latest-Recommended true + true From 58acf98d95eef9e964e09ed3b170b6cc3c4ffafb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:37:54 +0100 Subject: [PATCH 238/878] Use `RunAsync` to run application --- backend/src/Program.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 11d8fdc1..015a498a 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -59,7 +59,7 @@ string[] commandLineArguments await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); } - application.Run(); + await application.RunAsync().ConfigureAwait(false); return 0; } catch (Exception ex) when (ex is not HostAbortedException && ex.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 @@ -100,7 +100,10 @@ string environment rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, retainedFileCountLimit: 7); - if (environment != "production") configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); + if (environment != "production") + { + configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); + } } private static async Task CreateAndSeedDb( From a6d3667bbbc083e4256593d79029f5a8c4e55da7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:38:42 +0100 Subject: [PATCH 239/878] Put database name into a variable --- Makefile | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index d1603371..f8c49968 100644 --- a/Makefile +++ b/Makefile @@ -8,6 +8,8 @@ docker_compose = \ --file docker-compose.yml \ --project-name ${NAME} +database_name = xbase_development + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -62,7 +64,8 @@ frontend-build-context : ## Show the build context configured by `./frontend/.do .PHONY : frontend-build-context remove : ## Remove stopped containers - ${docker_compose} rm + ${docker_compose} rm \ + --volumes .PHONY : remove remove-data : ## Remove data volumes @@ -155,7 +158,7 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` contai database \ psql \ --username postgres \ - --dbname xbase_development + --dbname ${database_name} .PHONY : psql shelld : CONTAINER = database @@ -169,7 +172,7 @@ list : ## List all containers with health status --all .PHONY : list -createdb : DBNAME = xbase_development +createdb : DBNAME = ${database_name} createdb : ## Create database with name `${DBNAME}` defaulting to `xbase_development` ${docker_compose} exec \ database \ @@ -178,7 +181,7 @@ createdb : ## Create database with name `${DBNAME}` defaulting to `xbase_develop " .PHONY : createdb -dropdb : DBNAME = xbase_development +dropdb : DBNAME = ${database_name} dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase_development` ${docker_compose} exec \ database \ @@ -187,6 +190,18 @@ dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase_development " .PHONY : dropdb +sql : ## Run the SQL script in the file `${SQL}` in the running `database` service, for example, `make SQL=./my.sql sql` + cat ${SQL} \ + | ${docker_compose} exec \ + --no-TTY \ + database \ + psql \ + --echo-all \ + --file=- \ + --username=postgres \ + --dbname=${database_name} +.PHONY : sql + begin-maintenance : ## Begin maintenance cp \ ./nginx/html/maintenance.off.html \ @@ -243,6 +258,7 @@ ssl : ## Generate and trust certificate authority, and generate SSL certificates # See also https://gist.github.com/Soarez/9688998 # OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm # X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 +# Which file extensions are meant for which file layout? https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file/9717#9717 generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate mkdir --parents ./ssl/ docker run \ From 5208697aefece8d8d6a3bfde28b5f0623f1c7b33 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:38:59 +0100 Subject: [PATCH 240/878] Link Docker image tags --- backend/Dockerfile-production | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index f7ac6dd2..9d4e3764 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -13,6 +13,7 @@ ######### # https://hub.docker.com/_/microsoft-dotnet-sdk +# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build RUN \ @@ -46,6 +47,7 @@ RUN \ ########### # https://hub.docker.com/_/microsoft-dotnet-aspnet +# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim-amd64 RUN \ From 29fefbeb702d68f927d803347ca6db52cedb1384 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:39:11 +0100 Subject: [PATCH 241/878] Improve Visual Studio Code integration --- backend/Dockerfile | 5 +++-- docker-compose.yml | 16 ++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index cad070c6..14c2e3ad 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -6,6 +6,7 @@ # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp # https://hub.docker.com/_/microsoft-dotnet-sdk +# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 ARG GROUP_ID @@ -309,8 +310,8 @@ RUN \ ./test/bin \ ./test/obj && \ mkdir --parents \ - ${HOME}/.vscode-server/extensions \ - ${HOME}/.vscode-server-insiders/extensions && \ + ${HOME}/.vscode-server \ + ${HOME}/.vscode-server-insiders && \ mkdir --parents \ ${HOME}/.nuget \ ${HOME}/.dotnet diff --git a/docker-compose.yml b/docker-compose.yml index 88180d2f..2b31a403 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -81,8 +81,8 @@ services: volumes: - ./backend:/app - files:/app/src/files - - backend_vscode_server_extensions:/home/me/.vscode-server/extensions - - backend_vscode_server_insiders_extensions:/home/me/.vscode-server-insiders/extensions + - backend_vscode_server:/home/me/.vscode-server + - backend_vscode_server_insiders:/home/me/.vscode-server-insiders # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -105,17 +105,21 @@ services: - ASPNETCORE_ENVIRONMENT=development - XBASE_Host=https://${HOST}:${HTTPS_PORT} - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} + - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_AccessToken=${ACCESS_TOKEN} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} - - DOTNET_USE_POLLING_FILE_WATCHER=1 + # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables + - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose # - ASPNETCORE_Kestrel__Certificates__Default__Password=${SSL_CERTIFICATE_PASSWORD} # - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx + # - SSL_CERT_FILE=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx + # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING=1 + # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER=all extra_hosts: - ${HOST}:host-gateway - ${METABASE_HOST}:host-gateway @@ -189,5 +193,5 @@ volumes: data: emails: files: - backend_vscode_server_extensions: - backend_vscode_server_insiders_extensions: + backend_vscode_server: + backend_vscode_server_insiders: From 271bbe27b2e9e32bb362ea8d47861a59a5cd2455 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 14:41:27 +0100 Subject: [PATCH 242/878] Turn SSL stapling off as it does not work for self-sigend certificates (removes the warning when starting up NGINX) --- nginx/templates/development/default.conf.template | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index fcff280e..34969a9a 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -64,8 +64,7 @@ server { ssl_ecdh_curve secp384r1; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; - ssl_stapling on; #ensure your cert is capable - ssl_stapling_verify on; #ensure your cert is capable + ssl_stapling off; # Stapling does not work for self-signed certificates add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always; add_header X-Frame-Options DENY; From ff30461c8a663447093feac4110168d29c230693 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 15:22:18 +0100 Subject: [PATCH 243/878] Use simplified constructor --- backend/src/Controllers/AntiforgeryController.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 941d5431..dc58f4ee 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -9,24 +9,19 @@ namespace Database.Controllers; // For gotchas regarding antiforgery tokens read // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) -public sealed class AntiforgeryController : Controller +public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Controller { private const string XsrfCookieKey = "XSRF-TOKEN"; - private readonly IAntiforgery _antiforgeryService; + private readonly IAntiforgery _antiforgeryService = antiforgeryService; - private readonly CookieOptions XsrfCookieOptions = + private readonly CookieOptions _xsrfCookieOptions = new() { HttpOnly = false, SameSite = SameSiteMode.Strict }; - public AntiforgeryController(IAntiforgery antiforgeryService) - { - _antiforgeryService = antiforgeryService; - } - [HttpGet("~/antiforgery/token")] public async Task Token() { @@ -37,7 +32,7 @@ public async Task Token() tokens.RequestToken ?? throw new InvalidOperationException( "The request token supposed to be generated by the antiforgery service is null."), - XsrfCookieOptions + _xsrfCookieOptions ); return new StatusCodeResult(StatusCodes.Status200OK); } From 4dcb4bb0dfb50a6d507f01ed2638121fb63f9944 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 15:33:10 +0100 Subject: [PATCH 244/878] Make file and controller name match and add claims differently --- ...rollers.cs => AuthenticationController.cs} | 115 ++++++++++-------- 1 file changed, 63 insertions(+), 52 deletions(-) rename backend/src/Controllers/{AuthenticationControllers.cs => AuthenticationController.cs} (78%) diff --git a/backend/src/Controllers/AuthenticationControllers.cs b/backend/src/Controllers/AuthenticationController.cs similarity index 78% rename from backend/src/Controllers/AuthenticationControllers.cs rename to backend/src/Controllers/AuthenticationController.cs index b291b136..29ae44be 100644 --- a/backend/src/Controllers/AuthenticationControllers.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Security.Claims; using System.Threading; @@ -8,8 +9,11 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; using Microsoft.EntityFrameworkCore; +using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; @@ -17,18 +21,33 @@ namespace Database.Controllers; // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs -public class AuthenticationController : Controller +public sealed class AuthenticationController( + AppSettings appSettings, + IOptions identityOptions, + ApplicationDbContext context + ) : Controller { - private readonly ApplicationDbContext _context; - private readonly string _issuer; + private readonly IdentityOptions _identityOptions = identityOptions.Value ?? + throw new InvalidOperationException("There are no identity options."); + private readonly ApplicationDbContext _context = context; + private readonly string _issuer = appSettings.MetabaseHost; + private bool _disposed; - public AuthenticationController( - AppSettings appSettings, - ApplicationDbContext context - ) + protected override void Dispose(bool disposing) { - _issuer = appSettings.MetabaseHost; - _context = context; + base.Dispose(disposing); + if (!_disposed) + { + // Dispose of resources held by this instance. + _context.Dispose(); + _disposed = true; + } + } + + // Disposable types implement a finalizer. + ~AuthenticationController() + { + Dispose(false); } [HttpGet("~/connect/login")] @@ -61,11 +80,14 @@ public async Task LogOut(string? returnUrl) // this indicate that the user is already logged out locally (or has not logged in yet). var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); if (result is not { Succeeded: true }) + { // Only allow local return URLs to prevent open redirect attacks. // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects return LocalRedirect(SanitizeReturnUrl(returnUrl)); + } + // Remove the local authentication cookie before triggering a redirection to the remote server. - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. return SignOut( new AuthenticationProperties( @@ -95,6 +117,7 @@ public async Task LogOut(string? returnUrl) [HttpGet("~/connect/callback/login/{provider}")] [HttpPost("~/connect/callback/login/{provider}")] [IgnoreAntiforgeryToken] + [SuppressMessage("Style", "IDE0060")] public async Task LogInCallback(string provider, CancellationToken cancellationToken) { // Retrieve the authorization data validated by OpenIddict as part of the callback handling. @@ -119,49 +142,41 @@ public async Task LogInCallback(string provider, CancellationToken // // Note: this is the approach used here, but the external claims are first filtered to only persist // a few claims like the user identifier. The same approach is used to store the access/refresh tokens. - // + // Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint, - // result.Principal.Identity will represent an unauthenticated identity and won't contain any claim. + // result.Principal.Identity will represent an unauthenticated identity and won't contain any user claim. // // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls. - if (result.Principal?.Identity is not ClaimsIdentity { IsAuthenticated: true }) - throw new InvalidOperationException("The external authorization data cannot be used for authentication."); + if (result.Principal is not ClaimsPrincipal { Identity.IsAuthenticated: true }) + { + throw new InvalidOperationException( + "The external authorization data cannot be used for authentication."); + } + // Build an identity based on the external claims and that will be used to create the authentication cookie. - // - // By default, all claims extracted during the authorization dance are available. The claims collection stored - // in the cookie can be filtered out or mapped to different names depending on the claim name or its issuer. - // - // The claims are fetched from the userinfo endpoint of the - // authorization provider. - var claims = new List( - result.Principal.Claims - .Select(claim => claim switch - { - // https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims - // Map the standard "sub" and custom "id" claims to ClaimTypes.NameIdentifier, which is - // the default claim type used by .NET and is required by the antiforgery components. - { Type: Claims.Subject } - => new Claim(ClaimTypes.NameIdentifier, claim.Value, claim.ValueType, claim.Issuer), - // Map the standard "name" claim to ClaimTypes.Name. - { Type: Claims.Name } - => new Claim(ClaimTypes.Name, claim.Value, claim.ValueType, claim.Issuer), - _ => claim - }) - .Where(claim => claim switch - { - // Preserve the basic claims that are necessary for the application to work correctly. - { Type: ClaimTypes.NameIdentifier or ClaimTypes.Name } => true, - // Don't preserve the other claims. - _ => false - })); var identity = new ClaimsIdentity( - claims, CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role ); + + // By default, OpenIddict will automatically try to map the email/name and name identifier claims from + // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional + // claims can be resolved from the external identity and copied to the final authentication cookie. For example + // .SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email)) + // The claims are fetched from the userinfo endpoint of the authorization provider. + // We add the claim `IdentityOptions.ClaimsIdentity.UserIdClaimType` to make + // `UserManager.GetUserAsync(claimsPrincipal)` return the authenticated user. + identity.SetClaim(_identityOptions.ClaimsIdentity.UserIdClaimType, result.Principal.GetClaim(ClaimTypes.NameIdentifier)) + .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name)) + .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier)); + + // Preserve the registration details to be able to resolve them later. + identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId)) + .SetClaim(Claims.Private.ProviderName, result.Principal.GetClaim(Claims.Private.ProviderName)); + AuthenticationProperties properties; if (result.Properties is null) { @@ -173,17 +188,12 @@ public async Task LogInCallback(string provider, CancellationToken properties = new AuthenticationProperties(result.Properties.Items); // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. - properties.StoreTokens(result.Properties.GetTokens().Where(token => token switch - { + properties.StoreTokens(result.Properties.GetTokens().Where(token => token.Name is // Preserve the access, identity and refresh tokens returned in the token response, if available. - { - Name: OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or - OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken - } => true, - // Ignore the other tokens. - _ => false - })); + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or + OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken + )); } // Add the user to the database if he/she does not exist. @@ -226,6 +236,7 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or [HttpGet("~/connect/callback/logout/{provider}")] [HttpPost("~/connect/callback/logout/{provider}")] [IgnoreAntiforgeryToken] + [SuppressMessage("Style", "IDE0060")] public async Task LogOutCallback(string provider) { // Retrieve the data stored by OpenIddict in the state token created when the logout was triggered. From d70363d5ea8d8c4766b4932bc1e4433c3e918a53 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 15:35:20 +0100 Subject: [PATCH 245/878] Use non-obsolete certificate loader --- see https://learn.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0057 --- backend/create-certificates.csx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/create-certificates.csx b/backend/create-certificates.csx index dee68b2e..1f92e4c0 100644 --- a/backend/create-certificates.csx +++ b/backend/create-certificates.csx @@ -13,7 +13,7 @@ foreach (var (fileName, name, flags, password) in new[] { var path = Path.Join("src", fileName); var certificate = File.Exists(path) - ? new X509Certificate2(path, password) + ? X509CertificateLoader.LoadPkcs12FromFile(path, password) : null; if (certificate is null || certificate.NotAfter <= DateTime.Now) { From 42a3e0250785f95e5f63994ab6b3b608defd0e92 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 9 Dec 2024 16:07:53 +0100 Subject: [PATCH 246/878] Check whether currrent user is authorized to create data through information in the metabase --- .../CalorimetricDataAuthorization.cs | 21 +++++++ .../src/Authorization/CommonAuthorization.cs | 36 ++++++++++++ .../GeometricDataAuthorization.cs | 21 +++++++ .../HygrothermalDataAuthorization.cs | 21 +++++++ .../Authorization/OpticalDataAuthorization.cs | 21 +++++++ .../PhotovoltaicDataAuthorization.cs | 21 +++++++ .../CalorimetricDataMutations.cs | 32 ++++++---- .../GraphQl/Databases/DatabaseMutations.cs | 1 - .../GeometricDataX/GeometricDataMutations.cs | 22 ++++++- .../HygrothermalDataMutations.cs | 30 +++++----- .../OpticalDataX/OpticalDataMutations.cs | 30 +++++----- .../PhotovoltaicDataMutations.cs | 32 ++++++---- ...presentedInstitutionsByCurrentUser.graphql | 12 ++++ ...ingRepresentedInstitutionsByCurrentUser.cs | 58 +++++++++++++++++++ 14 files changed, 308 insertions(+), 50 deletions(-) create mode 100644 backend/src/Authorization/CalorimetricDataAuthorization.cs create mode 100644 backend/src/Authorization/CommonAuthorization.cs create mode 100644 backend/src/Authorization/GeometricDataAuthorization.cs create mode 100644 backend/src/Authorization/HygrothermalDataAuthorization.cs create mode 100644 backend/src/Authorization/OpticalDataAuthorization.cs create mode 100644 backend/src/Authorization/PhotovoltaicDataAuthorization.cs create mode 100644 backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql create mode 100644 backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs new file mode 100644 index 00000000..1a879626 --- /dev/null +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class CalorimetricDataAuthorization +{ + public static Task IsAuthorizedToCreateCalorimetricDataForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs new file mode 100644 index 00000000..75196d5d --- /dev/null +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -0,0 +1,36 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Metabase; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class CommonAuthorization +{ + public static async Task IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await QueryingRepresentedInstitutionsByCurrentUser.Query( + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false)) + .Any( + t => + t.Id == institutionId + && ( + t.Role == QueryingRepresentedInstitutionsByCurrentUser.InstitutionRepresentativeRole.ASSISTANT + || t.Role == QueryingRepresentedInstitutionsByCurrentUser.InstitutionRepresentativeRole.OWNER + ) + ); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs new file mode 100644 index 00000000..c38d0c3c --- /dev/null +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class GeometricDataAuthorization +{ + public static Task IsAuthorizedToCreateGeometricDataForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs new file mode 100644 index 00000000..9288718b --- /dev/null +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class HygrothermalDataAuthorization +{ + public static Task IsAuthorizedToCreateHygrothermalDataForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs new file mode 100644 index 00000000..20cbdf7f --- /dev/null +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class OpticalDataAuthorization +{ + public static Task IsAuthorizedToCreateOpticalDataForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs new file mode 100644 index 00000000..7d84d60e --- /dev/null +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class PhotovoltaicDataAuthorization +{ + public static Task IsAuthorizedToCreatePhotovoltaicDataForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index bb3b7587..2f740734 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -1,11 +1,16 @@ using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.CalorimetricDataX; @@ -16,21 +21,28 @@ public sealed class CalorimetricDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - // if (!await CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) + if (!await CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) + return new CreateCalorimetricDataPayload( + new CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode.UNAUTHORIZED, + $"The current user is not authorized to create calorimetric data for the institution.", + [] + ) + ); if (input.AccessToken != appSettings.AccessToken) return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 24c4d04c..2e124c3d 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Database.Metabase; using GraphQL; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 27e96c9c..5661dd83 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,30 +1,46 @@ using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.GeometricDataX; [ExtendObjectType(nameof(Mutation))] public sealed class GeometricDataMutations { + // [UseUserManager] + // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (input.AccessToken != appSettings.AccessToken) + if (!await GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) return new CreateGeometricDataPayload( new CreateGeometricDataError( CreateGeometricDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + $"The current user is not authorized to create geometric data for the institution.", + [] ) ); var geometricData = new GeometricData( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 8d1e541b..f4a0c1d1 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -1,11 +1,16 @@ using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.HygrothermalDataX; @@ -16,27 +21,26 @@ public sealed class HygrothermalDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - // if (!await HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) + if (!await HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( CreateHygrothermalDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + $"The current user is not authorized to create hygrothermal data for the institution.", + [] ) ); var hygrothermalData = new HygrothermalData( diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 38d1f4c7..b852d1b9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,11 +1,16 @@ using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.OpticalDataX; @@ -16,27 +21,26 @@ public sealed class OpticalDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - // if (!await OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) + if (!await OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) return new CreateOpticalDataPayload( new CreateOpticalDataError( CreateOpticalDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + $"The current user is not authorized to create optical data for the institution.", + [] ) ); var opticalData = new OpticalData( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 7fc742ed..aba98787 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -1,11 +1,16 @@ using System.Linq; +using System.Net.Http; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.PhotovoltaicDataX; @@ -16,21 +21,28 @@ public sealed class PhotovoltaicDataMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - // if (!await PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) + if (!await PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) + return new CreatePhotovoltaicDataPayload( + new CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, + $"The current user is not authorized to create photovoltaic data for the institution.", + [] + ) + ); if (input.AccessToken != appSettings.AccessToken) return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( diff --git a/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql b/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql new file mode 100644 index 00000000..0b2511de --- /dev/null +++ b/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql @@ -0,0 +1,12 @@ +query RepresentedInstitutionsByCurrentUser { + currentUser { + representedInstitutions { + edges { + node { + id + } + role + } + } + } +} \ No newline at end of file diff --git a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs new file mode 100644 index 00000000..41f09bef --- /dev/null +++ b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.Metabase; + +public sealed class QueryingRepresentedInstitutionsByCurrentUser +{ + private static readonly string[] _representedInstitutionsByCurrentUserFileNames = + { + "RepresentedInstitutionsByCurrentUser.graphql" + }; + + private sealed record Institution(Guid Id); + internal enum InstitutionRepresentativeRole + { + OWNER, + ASSISTANT + } + private sealed record UserRepresentedInstitutionEdge(Institution Node, InstitutionRepresentativeRole Role); + private sealed record UserRepresentedInstitutionConnection(IReadOnlyList Edges); + private sealed record User(UserRepresentedInstitutionConnection RepresentedInstitutions); + private sealed record RepresentedInstitutionsByCurrentUser(User CurrentUser); + + internal static async Task> Query( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await QueryingMetabase.QueryGraphQl( + appSettings, + new GraphQLRequest( + await QueryingMetabase.ConstructGraphQlQuery( + _representedInstitutionsByCurrentUserFileNames + ).ConfigureAwait(false), + new {}, + "RepresentedInstitutionsByCurrentUser" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false)) + ?.Data + ?.CurrentUser + ?.RepresentedInstitutions + ?.Edges + ?.Select(edge => (edge.Node.Id, edge.Role)) + .ToList().AsReadOnly() + ?? Array.Empty<(Guid Id, InstitutionRepresentativeRole Role)>().AsReadOnly(); + } +} \ No newline at end of file From 79112abff6b02dfce3d51e0451ee6b6c269954c6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 16:58:55 +0100 Subject: [PATCH 247/878] Fetch the UUID instead of the opaque node Id of an institution --- .../Authorization/FileUploadAuthorization.cs | 21 ++++ .../src/Controllers/FileUploadController.cs | 103 +++++++++--------- ...presentedInstitutionsByCurrentUser.graphql | 2 +- ...ingRepresentedInstitutionsByCurrentUser.cs | 4 +- 4 files changed, 75 insertions(+), 55 deletions(-) create mode 100644 backend/src/Authorization/FileUploadAuthorization.cs diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs new file mode 100644 index 00000000..46084829 --- /dev/null +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class FileUploadDataAuthorization +{ + public static Task IsAuthorizedToUploadFilesForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index e1fcc9c0..e57fca10 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -2,10 +2,12 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; using Database.Data; +using Database.Authorization; using Database.Filters; using Database.Utilities; using Microsoft.AspNetCore.Http.Features; @@ -14,6 +16,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using Microsoft.AspNetCore.Http; namespace Database.Controllers; @@ -41,12 +44,14 @@ private const long private const string _targetDirectoryPath = "./files/"; - // Get the default form options so that we can use them to set the default + // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions _defaultFormOptions = new(); - private readonly string _accessToken; - private readonly ApplicationDbContext _context; private readonly ILogger _logger; + private readonly ApplicationDbContext _context; + private readonly AppSettings _appSettings; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IHttpContextAccessor _httpContextAccessor; private readonly string[] _permittedExtensions = { ".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png" }; @@ -54,25 +59,29 @@ private const long public FileUploadController( ILogger logger, ApplicationDbContext context, - AppSettings appSettings + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor ) { _logger = logger; _context = context; - _accessToken = appSettings.AccessToken; + _appSettings = appSettings; + _httpClientFactory = httpClientFactory; + _httpContextAccessor = httpContextAccessor; // To save physical files to the temporary files folder, use: //_targetDirectoryPath = Path.GetTempPath(); } // The following upload methods: // - // 1. Disable the form value model binding to take control of handling + // 1. Disable the form value model binding to take control of handling // potentially large files. // - // 2. Typically, antiforgery tokens are sent in request body. Since we - // don't want to read the request body early, the tokens are sent via - // headers. The antiforgery token filter first looks for tokens in - // the request header and then falls back to reading the body. + // 2. Typically, antiforgery tokens are sent in request body. Since we + // don't want to read the request body early, the tokens are sent via + // headers. The antiforgery token filter first looks for tokens in the + // request header and then falls back to reading the body. [HttpPost("~/api/upload-file")] [DisableFormValueModelBinding] @@ -95,56 +104,47 @@ CancellationToken cancellationToken return BadRequest(ModelState); } - if (!await _context.GetHttpsResources.AsNoTracking() - .Where(u => u.Id == getHttpsResourceUuid) - .AnyAsync(cancellationToken) - .ConfigureAwait(false) - ) + var getHttpsResource = await _context.GetHttpsResources.AsNoTracking() + .Include(e => e.CalorimetricData) + .Include(e => e.HygrothermalData) + .Include(e => e.OpticalData) + .Include(e => e.PhotovoltaicData) + .Include(e => e.GeometricData) + .Where(e => e.Id == getHttpsResourceUuid) + .SingleOrDefaultAsync(cancellationToken) + .ConfigureAwait(false); + if (getHttpsResource is null) { ModelState.AddModelError("GetHttpsResourceUuid", $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); return BadRequest(ModelState); } - - var boundary = MultipartRequestHelper.GetBoundary( - MediaTypeHeaderValue.Parse(Request.ContentType), - _defaultFormOptions.MultipartBoundaryLengthLimit); - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); - - if (section is null) - { - ModelState.AddModelError("AccessToken", "Cannot read access token."); - return BadRequest(ModelState); - } - - var hasFirstContentDispositionHeader = - ContentDispositionHeaderValue.TryParse( - section.ContentDisposition, out var firstContentDisposition); - if (!hasFirstContentDispositionHeader) + if (getHttpsResource.Data is null) { - ModelState.AddModelError("AccessToken", "Cannot parse content disposition header value."); - return BadRequest(ModelState); - } - - if (!firstContentDisposition?.Name.Equals("accessToken") ?? - throw new ArgumentException("Impossible (because `hasFirstContentDispositionHeader` is `true`)")) - { - ModelState.AddModelError("AccessToken", - $"Access token must come first, got {firstContentDisposition?.Name} instead."); + ModelState.AddModelError("GetHttpsResourceUuid", + $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); return BadRequest(ModelState); } - - var accessToken = await new StreamReader(section.Body).ReadToEndAsync(cancellationToken).ConfigureAwait(false); - if (accessToken != _accessToken) + if (!await FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( + getHttpsResource.Data.CreatorId, + _appSettings, + _httpClientFactory, + _httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) { - ModelState.AddModelError("AccessToken", $"The access token {accessToken} is invalid."); - return BadRequest(ModelState); + return Unauthorized(); } Directory.CreateDirectory(_targetDirectoryPath); - section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + var boundary = MultipartRequestHelper.GetBoundary( + MediaTypeHeaderValue.Parse(Request.ContentType), + _defaultFormOptions.MultipartBoundaryLengthLimit); + var reader = new MultipartReader(boundary, HttpContext.Request.Body); + var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + while (section != null) { var hasContentDispositionHeader = @@ -178,9 +178,8 @@ CancellationToken cancellationToken // scanning the file's contents. In most production // scenarios, an anti-virus/anti-malware scanner API // is used on the file before making the file available - // for download or for use by other systems. - // For more information, see the topic that accompanies - // this sample. + // for download or for use by other systems. For more + // information, see the topic that accompanies this sample. var streamedFileContent = await FileHelpers.ProcessStreamedFile( section, @@ -213,8 +212,8 @@ private static Encoding GetEncoding(MultipartSection section) var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out var mediaType); - // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in - // most cases. + // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in most + // cases. #pragma warning disable SYSLIB0001 if (!hasMediaTypeHeader || Encoding.UTF7.Equals( mediaType?.Encoding ?? diff --git a/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql b/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql index 0b2511de..43fd32a4 100644 --- a/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql +++ b/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql @@ -3,7 +3,7 @@ query RepresentedInstitutionsByCurrentUser { representedInstitutions { edges { node { - id + uuid } role } diff --git a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs index 41f09bef..beade8e5 100644 --- a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs +++ b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs @@ -16,7 +16,7 @@ public sealed class QueryingRepresentedInstitutionsByCurrentUser "RepresentedInstitutionsByCurrentUser.graphql" }; - private sealed record Institution(Guid Id); + private sealed record Institution(Guid Uuid); internal enum InstitutionRepresentativeRole { OWNER, @@ -51,7 +51,7 @@ await QueryingMetabase.ConstructGraphQlQuery( ?.CurrentUser ?.RepresentedInstitutions ?.Edges - ?.Select(edge => (edge.Node.Id, edge.Role)) + ?.Select(edge => (edge.Node.Uuid, edge.Role)) .ToList().AsReadOnly() ?? Array.Empty<(Guid Id, InstitutionRepresentativeRole Role)>().AsReadOnly(); } From 74397a0632dae27d5967653b9e2da1d9484422a4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 17:34:41 +0100 Subject: [PATCH 248/878] Remove access token, remove timestamp, authorize creating HTTPS resources and uploading files through metabase information, and update frontend schema --- .env.production.sample | 1 - .env.sample | 1 - .env.staging.sample | 1 - backend/src/AppSettings.cs | 3 -- .../GetHttpsResourceAuthorization.cs | 21 ++++++++ .../src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/GetHttpsResource.cs | 4 +- backend/src/Enumerations/DataKind.cs | 4 +- .../CalorimetricDataMutations.cs | 8 --- .../CalorimetricDataQueries.cs | 6 +-- .../CreateCalorimetricDataInput.cs | 1 - backend/src/GraphQl/DataX/DataQueries.cs | 22 ++++---- .../CreateGeometricDataInput.cs | 1 - .../GeometricDataX/GeometricDataQueries.cs | 5 +- .../CreateGetHttpsResourceErrorCode.cs | 4 ++ .../CreateGetHttpsResourceInput.cs | 1 - .../GetHttpsResourceMutations.cs | 51 ++++++++++++++----- .../CreateHygrothermalDataInput.cs | 1 - .../HygrothermalDataQueries.cs | 6 +-- .../OpticalDataX/CreateOpticalDataInput.cs | 1 - .../OpticalDataX/OpticalDataQueries.cs | 6 +-- .../CreatePhotovoltaicDataInput.cs | 1 - .../PhotovoltaicDataMutations.cs | 8 --- .../PhotovoltaicDataQueries.cs | 6 +-- docker-compose.production.yml | 1 - docker-compose.yml | 1 - frontend/pages/data/create.tsx | 4 +- frontend/pages/upload-file.tsx | 17 ------- frontend/queries/data.graphql | 4 +- frontend/type-defs.graphqls | 37 ++++++-------- 30 files changed, 107 insertions(+), 122 deletions(-) create mode 100644 backend/src/Authorization/GetHttpsResourceAuthorization.cs diff --git a/.env.production.sample b/.env.production.sample index 5cd1498b..7600d7fe 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -5,7 +5,6 @@ HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org -ACCESS_TOKEN= OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= diff --git a/.env.sample b/.env.sample index ce7166c2..2c31f2cc 100644 --- a/.env.sample +++ b/.env.sample @@ -7,7 +7,6 @@ HTTPS_PORT=5051 METABASE_HOST=local.buildingenvelopedata.org METABASE_HTTPS_PORT=4041 -ACCESS_TOKEN=secret OPEN_ID_CONNECT_CLIENT_SECRET=secret VERIFICATION_CODE=secret diff --git a/.env.staging.sample b/.env.staging.sample index 85145cab..026f30db 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -5,7 +5,6 @@ HTTP_PORT=9000 METABASE_HOST=staging.buildingenvelopedata.org -ACCESS_TOKEN= OPEN_ID_CONNECT_CLIENT_SECRET= SMTP_HOST=email diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 0def98d9..1d7012ae 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -10,9 +10,6 @@ public sealed class AppSettings public string MetabaseHost { get; set; } = ""; - public string AccessToken { get; set; } - = ""; - public string VerificationCode { get; set; } = ""; diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs new file mode 100644 index 00000000..958a0469 --- /dev/null +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -0,0 +1,21 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace Database.Authorization; + +public static class GetHttpsResourceAuthorization +{ + public static Task IsAuthorizedToCreateGetHttpsResourceForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index e57fca10..bb6290c7 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -106,10 +106,10 @@ CancellationToken cancellationToken var getHttpsResource = await _context.GetHttpsResources.AsNoTracking() .Include(e => e.CalorimetricData) + .Include(e => e.GeometricData) .Include(e => e.HygrothermalData) .Include(e => e.OpticalData) .Include(e => e.PhotovoltaicData) - .Include(e => e.GeometricData) .Where(e => e.Id == getHttpsResourceUuid) .SingleOrDefaultAsync(cancellationToken) .ConfigureAwait(false); diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index df2ea2ca..cda1c88d 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -14,10 +14,10 @@ public GetHttpsResource( string hashValue, Guid dataFormatId, Guid? calorimetricDataId, + Guid? geometricDataId, Guid? hygrothermalDataId, Guid? opticalDataId, Guid? photovoltaicDataId, - Guid? geometricDataId, Guid? parentId, ICollection archivedFilesMetaInformation, ToTreeVertexAppliedConversionMethod? appliedConversionMethod @@ -32,10 +32,10 @@ public GetHttpsResource( ) { CalorimetricDataId = calorimetricDataId; + GeometricDataId = geometricDataId; HygrothermalDataId = hygrothermalDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; - GeometricDataId = geometricDataId; } public GetHttpsResource( diff --git a/backend/src/Enumerations/DataKind.cs b/backend/src/Enumerations/DataKind.cs index 2dfe834d..3f626f64 100644 --- a/backend/src/Enumerations/DataKind.cs +++ b/backend/src/Enumerations/DataKind.cs @@ -6,8 +6,8 @@ namespace Database.Enumerations; public enum DataKind { CALORIMETRIC_DATA, + GEOMETRIC_DATA, HYGROTHERMAL_DATA, OPTICAL_DATA, - PHOTOVOLTAIC_DATA, - GEOMETRIC_DATA + PHOTOVOLTAIC_DATA } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 2f740734..75c236e6 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -43,14 +43,6 @@ CancellationToken cancellationToken [] ) ); - if (input.AccessToken != appSettings.AccessToken) - return new CreateCalorimetricDataPayload( - new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); var calorimetricData = new CalorimetricData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 27cf1fbc..cbaaa2fb 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -20,26 +20,24 @@ public sealed class CalorimetricDataQueries [UseFiltering] [UseSorting] public IQueryable GetAllCalorimetricData( - DateTime? timestamp, [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return context.CalorimetricData.AsNoTracking(); } public Task GetCalorimetricDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, CalorimetricDataByIdDataLoader byId, CancellationToken cancellationToken ) { - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return byId.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 7d09961c..1090841c 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.CalorimetricDataX; public sealed record CreateCalorimetricDataInput( - string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, Guid ComponentId, diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 8409faee..756308c6 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -21,35 +21,33 @@ namespace Database.GraphQl.DataX; [ExtendObjectType(nameof(Query))] public sealed class DataQueries { - [UsePaging] + [UsePaging] // TODO Paging does not work with this data source. // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] // TODO Filtering does not work with unions. + // [UseFiltering] // TODO Filtering does not work with unions. [UseSorting] - public IEnumerable GetAllData( - DateTime? timestamp, + public IAsyncEnumerable GetAllData( [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. // return context.CalorimetricData.AsNoTracking(); // The union below does sadly not work because the different kinds of data have different include operations. // return context.CalorimetricData.AsNoTracking() // .Union(context.HygrothermalData.AsNoTracking()) // .Union(context.OpticalData.AsNoTracking()) // .Union(context.PhotovoltaicData.AsNoTracking()); - return context.CalorimetricData.AsNoTracking().AsEnumerable() - .Concat(context.HygrothermalData.AsNoTracking().AsEnumerable()) - .Concat(context.OpticalData.AsNoTracking().AsEnumerable()) - .Concat(context.PhotovoltaicData.AsNoTracking().AsEnumerable()) - .Concat(context.GeometricData.AsNoTracking().AsEnumerable()); + return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() + .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); } public async Task GetDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, CalorimetricDataByIdDataLoader calorimetricDataById, HygrothermalDataByIdDataLoader hygrothermalDataById, @@ -59,7 +57,7 @@ ISortingContext sorting CancellationToken cancellationToken ) { - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return await calorimetricDataById.LoadAsync( id, diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index 29ebd12c..b907ae9f 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.GeometricDataX; public sealed record CreateGeometricDataInput( - string AccessToken, [GraphQLType>] string Locale, Guid ComponentId, string? Name, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 471566fb..13b4f766 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -19,25 +19,24 @@ public sealed class GeometricDataQueries [UseFiltering] [UseSorting] public IQueryable GetAllGeometricData( - DateTime? timestamp, [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return context.GeometricData.AsNoTracking(); } public Task GetGeometricDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, GeometricDataByIdDataLoader byId, CancellationToken cancellationToken ) { + // TODO Use `locale`. return byId.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs index 85d591c0..e3333e23 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs @@ -1,7 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + namespace Database.GraphQl.GetHttpsResources; +[SuppressMessage("Naming", "CA1707")] public enum CreateGetHttpsResourceErrorCode { UNKNOWN, + UNKNOWN_DATA, UNAUTHORIZED } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs index fb3d62bf..7cbde64d 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs @@ -5,7 +5,6 @@ namespace Database.GraphQl.GetHttpsResources; public sealed record CreateGetHttpsResourceInput( - string AccessToken, string Description, string HashValue, Guid DataFormatId, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index d309850e..4102c07d 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -1,12 +1,17 @@ +using System; using System.Linq; +using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Data; +using Database.Authorization; using Database.Enumerations; using Database.Extensions; using HotChocolate; using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -17,27 +22,45 @@ public sealed class GetHttpsResourceMutations // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, - // ClaimsPrincipal claimsPrincipal, - // [ScopedService] UserManager userManager, ApplicationDbContext context, AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - // if (!await GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( - // claimsPrincipal, - // input.CreatorId, - // userManager, - // context, - // cancellationToken - // ).ConfigureAwait(false) - // ) - if (input.AccessToken != appSettings.AccessToken) + IData? data = input.DataKind switch + { + DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + _ => throw new ArgumentException($"The data kind {input.DataKind} is not supported.") + }; + if (data is null) + { + return new CreateGetHttpsResourcePayload( + new CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, + $"There is no data of kind {input.DataKind} with identifier {input.DataId}.", + [nameof(input), nameof(input.DataId).FirstCharToLower()] + ) + ); + } + if (!await GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( + data.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( CreateGetHttpsResourceErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } + $"The current user is not authorized to create GET HTTPS resource for the institution.", + [] ) ); var getHttpsResource = new GetHttpsResource( @@ -45,10 +68,10 @@ CancellationToken cancellationToken input.HashValue, input.DataFormatId, input.DataKind == DataKind.CALORIMETRIC_DATA ? input.DataId : null, + input.DataKind == DataKind.GEOMETRIC_DATA ? input.DataId : null, input.DataKind == DataKind.HYGROTHERMAL_DATA ? input.DataId : null, input.DataKind == DataKind.OPTICAL_DATA ? input.DataId : null, input.DataKind == DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, - input.DataKind == DataKind.GEOMETRIC_DATA ? input.DataId : null, input.ParentId, input.ArchivedFilesMetaInformation.Select(i => new FileMetaInformation( diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 61b0d09e..1aa49125 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.HygrothermalDataX; public sealed record CreateHygrothermalDataInput( - string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, Guid ComponentId, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 5d720249..0efcb67c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -20,26 +20,24 @@ public sealed class HygrothermalDataQueries [UseFiltering] [UseSorting] public IQueryable GetAllHygrothermalData( - DateTime? timestamp, [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return context.HygrothermalData.AsNoTracking(); } public Task GetHygrothermalDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, HygrothermalDataByIdDataLoader byId, CancellationToken cancellationToken ) { - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return byId.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index 96a021ed..aec4124b 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.OpticalDataX; public sealed record CreateOpticalDataInput( - string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, Guid ComponentId, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 0a05544d..aa684c98 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -20,26 +20,24 @@ public sealed class OpticalDataQueries [UseFiltering] [UseSorting] public IQueryable GetAllOpticalData( - DateTime? timestamp, [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return context.OpticalData.AsNoTracking(); } public Task GetOpticalDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, OpticalDataByIdDataLoader byId, CancellationToken cancellationToken ) { - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return byId.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index e73d0ffc..a1845620 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.PhotovoltaicDataX; public sealed record CreatePhotovoltaicDataInput( - string AccessToken, // TODO Why does specifying the type with an attribute not work here? [GraphQLType>] string Locale, Guid ComponentId, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index aba98787..857485b3 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -43,14 +43,6 @@ CancellationToken cancellationToken [] ) ); - if (input.AccessToken != appSettings.AccessToken) - return new CreatePhotovoltaicDataPayload( - new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, - $"The access token {input.AccessToken} is invalid.", - new[] { nameof(input), nameof(input.AccessToken).FirstCharToLower() } - ) - ); var photovoltaicData = new PhotovoltaicData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 940c6dac..70411eb2 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -20,26 +20,24 @@ public sealed class PhotovoltaicDataQueries [UseFiltering] [UseSorting] public IQueryable GetAllPhotovoltaicData( - DateTime? timestamp, [GraphQLType] string? locale, ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return context.PhotovoltaicData.AsNoTracking(); } public Task GetPhotovoltaicDataAsync( Guid id, - DateTime? timestamp, [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, CancellationToken cancellationToken ) { - // TODO Use `timestamp` and `locale`. + // TODO Use `locale`. return byId.LoadAsync( id, cancellationToken diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 4e7fa0c5..6b0742ba 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -71,7 +71,6 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_AccessToken=${ACCESS_TOKEN} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 2b31a403..d688ee56 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,7 +110,6 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_AccessToken=${ACCESS_TOKEN} - XBASE_VerificationCode=${VERIFICATION_CODE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true diff --git a/frontend/pages/data/create.tsx b/frontend/pages/data/create.tsx index f57f6bc5..b8b520f9 100644 --- a/frontend/pages/data/create.tsx +++ b/frontend/pages/data/create.tsx @@ -18,8 +18,8 @@ function Page() { {/*
) => ({ - accessToken: form.getFieldValue("accessToken"), - }); - return ( @@ -37,18 +33,6 @@ function Page() { file with the content for that resource. - - - - diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 6caa6c16..8386f98e 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -80,8 +80,8 @@ query Data($uuid: Uuid!) { } } -query AllData($where: DataPropositionInput) { - allData(where: $where) { +query AllData { + allData { edges { node { ...DataPartial diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 3d5fcb9b..e2bbab17 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -463,27 +463,27 @@ type Publication implements Reference { } type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") timestamp: DateTime where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] @cost(weight: "10") timestamp: DateTime where: DataPropositionInput @cost(weight: "10")): DataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") timestamp: DateTime where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") timestamp: DateTime where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") timestamp: DateTime where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") timestamp: DateTime where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - calorimetricData(id: Uuid! locale: Locale timestamp: DateTime): CalorimetricData @cost(weight: "10") + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] @cost(weight: "10")): DataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo @cost(weight: "10") - data(id: Uuid! locale: Locale timestamp: DateTime): Data @cost(weight: "10") + data(id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") - geometricData(id: Uuid! locale: Locale timestamp: DateTime): GeometricData @cost(weight: "10") + geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], sizedFields: [ "edges", "nodes" ]) @cost(weight: "10") - hygrothermalData(id: Uuid! locale: Locale timestamp: DateTime): HygrothermalData @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - opticalData(id: Uuid! locale: Locale timestamp: DateTime): OpticalData @cost(weight: "10") - photovoltaicData(id: Uuid! locale: Locale timestamp: DateTime): PhotovoltaicData @cost(weight: "10") + opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") verificationCode: String! } @@ -604,7 +604,6 @@ input CielabColorPropositionInput { } input CreateCalorimetricDataInput { - accessToken: String! appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! componentId: Uuid! @@ -620,7 +619,6 @@ input CreateCalorimetricDataInput { } input CreateGeometricDataInput { - accessToken: String! appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! componentId: Uuid! @@ -635,7 +633,6 @@ input CreateGeometricDataInput { } input CreateGetHttpsResourceInput { - accessToken: String! appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! dataFormatId: Uuid! @@ -647,7 +644,6 @@ input CreateGetHttpsResourceInput { } input CreateHygrothermalDataInput { - accessToken: String! appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! componentId: Uuid! @@ -667,7 +663,6 @@ input CreateNumerationInput { } input CreateOpticalDataInput { - accessToken: String! appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! cielabColors: [CielabColorInput!]! @@ -688,7 +683,6 @@ input CreateOpticalDataInput { } input CreatePhotovoltaicDataInput { - accessToken: String! appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! componentId: Uuid! @@ -1268,6 +1262,7 @@ enum CreateGeometricDataErrorCode { enum CreateGetHttpsResourceErrorCode { UNKNOWN + UNKNOWN_DATA UNAUTHORIZED } @@ -1288,10 +1283,10 @@ enum CreatePhotovoltaicDataErrorCode { enum DataKind { CALORIMETRIC_DATA + GEOMETRIC_DATA HYGROTHERMAL_DATA OPTICAL_DATA PHOTOVOLTAIC_DATA - GEOMETRIC_DATA } enum DatabaseVerificationState { @@ -1349,7 +1344,7 @@ enum UriHostNameType { directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR From 5e3eb456b71de8e2ec667d7ec52786b18b5ee9f5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Jan 2025 17:35:17 +0100 Subject: [PATCH 249/878] Upgrade mailkit and resharper tools --- backend/dotnet-tools.json | 2 +- backend/src/Database.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index cf683ba0..4067cb2b 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.3.3", + "version": "2024.3.4", "commands": [ "jb" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index b228421d..cf334cdd 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -32,7 +32,7 @@ - + From 7e78008f049ff0b3e61a7d053ed0eb1b13454664 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Jan 2025 17:24:26 +0100 Subject: [PATCH 250/878] Forward requests to /openapi to the backend --- backend/src/Startup.cs | 2 +- nginx/templates/development/default.conf.template | 5 +++++ nginx/templates/production/default.conf.template | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 4b0a31b0..076814df 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -258,7 +258,7 @@ public void Configure(WebApplication app) app.UseSession(); // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx - /* app.UseWebSockets(); */ + // app.UseWebSockets(); app.MapOpenApi("/openapi/{documentName}.json"); app.MapGraphQL() .WithOptions( diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index 34969a9a..4e77c310 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -121,6 +121,11 @@ server { keepalive_timeout 65; } + location /openapi { + proxy_pass http://backend; + limit_req zone=one burst=10 nodelay; + } + location /health { proxy_pass http://backend; limit_req zone=one burst=20 nodelay; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index 3c4a2d72..6394d611 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -105,6 +105,11 @@ server { keepalive_timeout 65; } + location /openapi { + proxy_pass http://backend; + limit_req zone=one burst=20 nodelay; + } + location /health { proxy_pass http://backend; limit_req zone=one burst=20 nodelay; From 0e6706c13271fa7b95200dfc64fb3c5bc69c1e80 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Jan 2025 17:29:38 +0100 Subject: [PATCH 251/878] Drop into NGINX shell with default user (to be allowed to use the `nginx` command) --- Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index f8c49968..661f6660 100644 --- a/Makefile +++ b/Makefile @@ -148,9 +148,10 @@ traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for examp " .PHONY : traceb -shelln : CONTAINER = nginx -shelln : COMMAND = bash -shelln : exec ## Enter shell in an existing `nginx` container (after starting all containers if necessary) +shelln : up ## Enter shell in an existing `nginx` container (after starting all containers if necessary) + ${docker_compose} exec \ + nginx \ + bash .PHONY : shelln psql : ## Enter PostgreSQL interactive terminal in the running `database` container From d13128129fb481c8738462596de2f533164e3292 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 17:05:28 +0100 Subject: [PATCH 252/878] Fix Npgsql enumeration mapping --- see https://www.npgsql.org/efcore/mapping/enum.html --- backend/src/Startup.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 076814df..663b0c2d 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -158,13 +158,16 @@ private static void ConfigureDatabaseContext( AppSettings appSettings ) { - var dataSourceBuilder = new NpgsqlDataSourceBuilder(appSettings.Database.ConnectionString); - // https://www.npgsql.org/efcore/mapping/enum.html#mapping-your-enum - // Keep in sync with `ApplicationDbContext.CreateEnumerations`. - dataSourceBuilder.MapEnum($"{appSettings.Database.SchemaName}.{ApplicationDbContext.DataKindTypeName}"); - dataSourceBuilder.MapEnum($"{appSettings.Database.SchemaName}.{ApplicationDbContext.StandardizerTypeName}"); + // https://www.npgsql.org/efcore/mapping/enum.html options - .UseNpgsql(dataSourceBuilder.Build() /*, optionsBuilder => optionsBuilder.UseNodaTime() */) + .UseNpgsql( + appSettings.Database.ConnectionString, + options => { + options.MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName); + options.MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName); + // options.UseNodaTime(); + } + ) .UseSchemaName(appSettings.Database.SchemaName) .UseOpenIddict(); if (!environment.IsProduction()) From 51410b7508d461b3ae00b41092fd7ca2e9dc98e6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 17:14:22 +0100 Subject: [PATCH 253/878] Use fluent API to configure Npqsql and fix PostgreSQL version for Npgsql --- backend/src/Startup.cs | 10 +++++----- docker-compose.production.yml | 2 ++ docker-compose.yml | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 663b0c2d..4492d800 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -162,11 +162,11 @@ AppSettings appSettings options .UseNpgsql( appSettings.Database.ConnectionString, - options => { - options.MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName); - options.MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName); - // options.UseNodaTime(); - } + _ => _ + .SetPostgresVersion(13, 13) + .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) + // .UseNodaTime(); ) .UseSchemaName(appSettings.Database.SchemaName) .UseOpenIddict(); diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 6b0742ba..c902062c 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,6 +1,8 @@ +# Keep setup in sync with the one in ./docker-compose.yml services: database: # https://hub.docker.com/_/postgres restart: always + # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` image: postgres:13.13-bookworm # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html diff --git a/docker-compose.yml b/docker-compose.yml index d688ee56..87a19ccf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,7 @@ +# Keep setup in sync with the one in ./docker-compose.production.yml services: database: # https://hub.docker.com/_/postgres + # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` image: postgres:13.13-bookworm # ports: # - 5432:5432 From 4b6d24acf4cde5b89dcb973e49aa7efe1d6b1630 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 17:34:13 +0100 Subject: [PATCH 254/878] Configure the database-context options only once as suggested in https://github.com/npgsql/efcore.pg/issues/3375#issuecomment-2509746639 --- backend/src/Startup.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 4492d800..4d83d2ba 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -187,13 +187,15 @@ AppSettings appSettings private void ConfigureDatabaseServices(IServiceCollection services) { + // Configure the database-context options only once as suggested in + // https://github.com/npgsql/efcore.pg/issues/3375#issuecomment-2509746639 services.AddPooledDbContextFactory(options => ConfigureDatabaseContext(options, _environment, _appSettings) ); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. services.AddDbContext(options => - ConfigureDatabaseContext(options, _environment, _appSettings), + {}, contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton ); From bb6f6bf51a8e5845abb255cc8c380d0fb42094e2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 17:40:30 +0100 Subject: [PATCH 255/878] Add migration for upgrade to .NET 9 Core --- .../20250129163501_UpgradeToNet9.Designer.cs | 2093 +++++++++++++++++ .../20250129163501_UpgradeToNet9.cs | 65 + .../ApplicationDbContextModelSnapshot.cs | 58 +- 3 files changed, 2187 insertions(+), 29 deletions(-) create mode 100644 backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs create mode 100644 backend/src/Migrations/20250129163501_UpgradeToNet9.cs diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs new file mode 100644 index 00000000..b53d128e --- /dev/null +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs @@ -0,0 +1,2093 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250129163501_UpgradeToNet9")] + partial class UpgradeToNet9 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs new file mode 100644 index 00000000..77a3886a --- /dev/null +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs @@ -0,0 +1,65 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class UpgradeToNet9 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "geometric_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "geometric_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 70ba3351..7083ce09 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -20,10 +20,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("ProductVersion", "9.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "hygrothermal_data", "optical_data", "photovoltaic_data", "geometric_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -47,7 +47,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); - b.Property("GValues") + b.PrimitiveCollection("GValues") .IsRequired() .HasColumnType("double precision[]"); @@ -58,7 +58,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("UValues") + b.PrimitiveCollection("UValues") .IsRequired() .HasColumnType("double precision[]"); @@ -68,7 +68,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("xid") .HasColumnName("xmin"); - b.Property("Warnings") + b.PrimitiveCollection("Warnings") .IsRequired() .HasColumnType("text[]"); @@ -103,7 +103,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Thicknesses") + b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); @@ -113,7 +113,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("xid") .HasColumnName("xmin"); - b.Property("Warnings") + b.PrimitiveCollection("Warnings") .IsRequired() .HasColumnType("text[]"); @@ -213,7 +213,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("xid") .HasColumnName("xmin"); - b.Property("Warnings") + b.PrimitiveCollection("Warnings") .IsRequired() .HasColumnType("text[]"); @@ -229,7 +229,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("ColorRenderingIndices") + b.PrimitiveCollection("ColorRenderingIndices") .IsRequired() .HasColumnType("double precision[]"); @@ -245,7 +245,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); - b.Property("InfraredEmittances") + b.PrimitiveCollection("InfraredEmittances") .IsRequired() .HasColumnType("double precision[]"); @@ -256,19 +256,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("NearnormalHemisphericalSolarReflectances") + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") .IsRequired() .HasColumnType("double precision[]"); - b.Property("NearnormalHemisphericalSolarTransmittances") + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") .IsRequired() .HasColumnType("double precision[]"); - b.Property("NearnormalHemisphericalVisibleReflectances") + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") .IsRequired() .HasColumnType("double precision[]"); - b.Property("NearnormalHemisphericalVisibleTransmittances") + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") .IsRequired() .HasColumnType("double precision[]"); @@ -278,7 +278,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("xid") .HasColumnName("xmin"); - b.Property("Warnings") + b.PrimitiveCollection("Warnings") .IsRequired() .HasColumnType("text[]"); @@ -319,7 +319,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("xid") .HasColumnName("xmin"); - b.Property("Warnings") + b.PrimitiveCollection("Warnings") .IsRequired() .HasColumnType("text[]"); @@ -738,7 +738,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("ArXiv") .HasColumnType("text"); - b2.Property("Authors") + b2.PrimitiveCollection("Authors") .HasColumnType("text[]"); b2.Property("Doi") @@ -781,7 +781,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Section") .HasColumnType("text"); - b2.Property("Standardizers") + b2.PrimitiveCollection("Standardizers") .IsRequired() .HasColumnType("database.standardizer[]"); @@ -917,7 +917,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b3.Property("DataKind") - .HasColumnType("data_kind"); + .HasColumnType("database.data_kind"); b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); @@ -996,7 +996,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("ArXiv") .HasColumnType("text"); - b2.Property("Authors") + b2.PrimitiveCollection("Authors") .HasColumnType("text[]"); b2.Property("Doi") @@ -1039,9 +1039,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Section") .HasColumnType("text"); - b2.Property("Standardizers") + b2.PrimitiveCollection("Standardizers") .IsRequired() - .HasColumnType("standardizer[]"); + .HasColumnType("database.standardizer[]"); b2.Property("Title") .HasColumnType("text"); @@ -1137,7 +1137,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("DataFormatId") .HasColumnType("uuid"); - b1.Property("Path") + b1.PrimitiveCollection("Path") .IsRequired() .HasColumnType("text[]"); @@ -1372,7 +1372,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("ArXiv") .HasColumnType("text"); - b2.Property("Authors") + b2.PrimitiveCollection("Authors") .HasColumnType("text[]"); b2.Property("Doi") @@ -1415,7 +1415,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Section") .HasColumnType("text"); - b2.Property("Standardizers") + b2.PrimitiveCollection("Standardizers") .IsRequired() .HasColumnType("database.standardizer[]"); @@ -1658,7 +1658,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("ArXiv") .HasColumnType("text"); - b2.Property("Authors") + b2.PrimitiveCollection("Authors") .HasColumnType("text[]"); b2.Property("Doi") @@ -1701,7 +1701,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Section") .HasColumnType("text"); - b2.Property("Standardizers") + b2.PrimitiveCollection("Standardizers") .IsRequired() .HasColumnType("database.standardizer[]"); @@ -1918,7 +1918,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("ArXiv") .HasColumnType("text"); - b2.Property("Authors") + b2.PrimitiveCollection("Authors") .HasColumnType("text[]"); b2.Property("Doi") @@ -1961,7 +1961,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("Section") .HasColumnType("text"); - b2.Property("Standardizers") + b2.PrimitiveCollection("Standardizers") .IsRequired() .HasColumnType("database.standardizer[]"); From a6d97f7fb566765be5d0b44f746bed23a836123a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 18:11:04 +0100 Subject: [PATCH 256/878] Name OpenAPI version explicitly (it corresponds to the OpenAPI document name) --- backend/src/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 4d83d2ba..223baef6 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -115,7 +115,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO I consider the flattened structure a bug. How can we solve this? } ); - services.AddOpenApi(_ => { + services.AddOpenApi("v1", _ => { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; }); } From b31aae8e820299f9d4cca50ce1993283b9869379 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 5 Feb 2025 10:24:26 +0100 Subject: [PATCH 257/878] Approval adding for one data set --- .../src/Authorization/CommonAuthorization.cs | 20 ++++++ backend/src/Data/DataX.cs | 2 +- backend/src/Data/ResponseApproval.cs | 58 ++++++++--------- .../src/GraphQl/Approvals/AddApprovalError.cs | 17 +++++ .../GraphQl/Approvals/AddApprovalErrorCode.cs | 7 +++ .../GraphQl/Approvals/AddApprovalPayload.cs | 21 +++++++ .../src/GraphQl/Approvals/ApprovalInput.cs | 11 ++++ .../src/GraphQl/Approvals/ApprovalPayload.cs | 53 ++++++++++++++++ .../{ => Approvals}/DataApprovalInput.cs | 4 +- .../src/GraphQl/Approvals/DataApprovalType.cs | 18 ++++++ .../Approvals/ResponseApprovalInput.cs | 12 ++++ .../CreateCalorimetricDataInput.cs | 1 + .../CreateGeometricDataInput.cs | 1 + .../GeometricDataX/GeometricDataMutations.cs | 62 +++++++++++++++++-- .../CreateHygrothermalDataInput.cs | 1 + .../OpticalDataX/CreateOpticalDataInput.cs | 1 + .../CreatePhotovoltaicDataInput.cs | 1 + ...ingRepresentedInstitutionsByCurrentUser.cs | 16 +++-- 18 files changed, 266 insertions(+), 40 deletions(-) create mode 100644 backend/src/GraphQl/Approvals/AddApprovalError.cs create mode 100644 backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs create mode 100644 backend/src/GraphQl/Approvals/AddApprovalPayload.cs create mode 100644 backend/src/GraphQl/Approvals/ApprovalInput.cs create mode 100644 backend/src/GraphQl/Approvals/ApprovalPayload.cs rename backend/src/GraphQl/{ => Approvals}/DataApprovalInput.cs (85%) create mode 100644 backend/src/GraphQl/Approvals/DataApprovalType.cs create mode 100644 backend/src/GraphQl/Approvals/ResponseApprovalInput.cs diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 75196d5d..cf922482 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -33,4 +33,24 @@ CancellationToken cancellationToken ) ); } + + public static async Task IsAuthorizedToAddApprovalForInstitution( + Guid institutionId, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await QueryingRepresentedInstitutionsByCurrentUser.Query( + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false)) + .Any(t => + t.Id == institutionId + && t.Permission == QueryingRepresentedInstitutionsByCurrentUser.DataSigningPermission.GRANTED + ); + } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index b7fd6c1e..38a352d0 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -63,7 +63,7 @@ DateTime createdAt public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); - // public ResponseApproval Approval { get; private set; } + public ResponseApproval Approval { get; set; } // TODO Exactly one resource must not have a parent and each other resource must have one from this list and the graph must be connected. In other words, the resources must form a tree. public virtual ICollection Resources { get; } = new List(); diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 0c7c4a56..e668472d 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,31 +1,31 @@ -// using System; +using System; +using Microsoft.EntityFrameworkCore; -// namespace Database.Data -// { -// [Owned] -// public sealed class ResponseApproval -// : IApproval -// { -// public ResponseApproval( -// DateTime timestamp, -// string signature, -// string keyFingerprint, -// string query, -// string response -// ) -// { -// Timestamp = timestamp; -// Signature = signature; -// KeyFingerprint = keyFingerprint; -// Query = query; -// Response = response; -// } - -// public DateTime Timestamp { get; private set; } -// public string Signature { get; private set; } -// public string KeyFingerprint { get; private set; } -// public string Query { get; private set; } -// public string Response { get; private set; } -// } -// } +namespace Database.Data +{ + [Owned] + public sealed class ResponseApproval + : IApproval + { + public ResponseApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + string response + ) + { + Timestamp = timestamp; + Signature = signature; + KeyFingerprint = keyFingerprint; + Query = query; + Response = response; + } + public DateTime Timestamp { get; private set; } + public string Signature { get; private set; } + public string KeyFingerprint { get; private set; } + public string Query { get; private set; } + public string Response { get; private set; } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalError.cs b/backend/src/GraphQl/Approvals/AddApprovalError.cs new file mode 100644 index 00000000..048032c1 --- /dev/null +++ b/backend/src/GraphQl/Approvals/AddApprovalError.cs @@ -0,0 +1,17 @@ +using Database.GraphQl.GeometricDataX; +using System.Collections.Generic; + +namespace Database.GraphQl.Approvals; + +public class AddApprovalError + : UserErrorBase +{ + public AddApprovalError( + AddApprovalErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs new file mode 100644 index 00000000..79cdfccd --- /dev/null +++ b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs @@ -0,0 +1,7 @@ +namespace Database.GraphQl.Approvals; + +public enum AddApprovalErrorCode +{ + UNKNOWN, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs new file mode 100644 index 00000000..ec299534 --- /dev/null +++ b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs @@ -0,0 +1,21 @@ +using Database.Data; + +namespace Database.GraphQl.Approvals; + +public class AddApprovalPayload + : ApprovalPayload +{ + public AddApprovalPayload( + DataApproval dataApproval + ) + : base(dataApproval) + { + } + + public AddApprovalPayload( + AddApprovalError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalInput.cs b/backend/src/GraphQl/Approvals/ApprovalInput.cs new file mode 100644 index 00000000..d95a330b --- /dev/null +++ b/backend/src/GraphQl/Approvals/ApprovalInput.cs @@ -0,0 +1,11 @@ +using System; + +namespace Database.GraphQl.Approvals; + +public sealed record ApprovalInput +( + Guid DataId, + Guid CreatorId, + DataApprovalInput Approval, + ResponseApprovalInput ResponseApproval +); \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalPayload.cs b/backend/src/GraphQl/Approvals/ApprovalPayload.cs new file mode 100644 index 00000000..ef4c67ab --- /dev/null +++ b/backend/src/GraphQl/Approvals/ApprovalPayload.cs @@ -0,0 +1,53 @@ +using Database.Data; +using System.Collections.Generic; + +namespace Database.GraphQl.Approvals; + +public class ApprovalPayload + : Payload + where TApprovalError : IUserError +{ + protected ApprovalPayload( + DataApproval dataApproval + ) + { + DataApproval = dataApproval; + } + + protected ApprovalPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected ApprovalPayload( + TApprovalError error + ) + : this(new[] { error }) + { + } + + protected ApprovalPayload( + DataApproval dataApproval, + IReadOnlyCollection errors + ) + { + DataApproval = dataApproval; + Errors = errors; + } + + protected ApprovalPayload( + DataApproval dataApproval, + TApprovalError error + ) + : this( + dataApproval, + new[] { error } + ) + { + } + + public DataApproval? DataApproval { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovalInput.cs b/backend/src/GraphQl/Approvals/DataApprovalInput.cs similarity index 85% rename from backend/src/GraphQl/DataApprovalInput.cs rename to backend/src/GraphQl/Approvals/DataApprovalInput.cs index 277aa58f..78ab9b44 100644 --- a/backend/src/GraphQl/DataApprovalInput.cs +++ b/backend/src/GraphQl/Approvals/DataApprovalInput.cs @@ -1,8 +1,8 @@ -using System; +using System; using Database.GraphQl.Publications; using Database.GraphQl.Standards; -namespace Database.GraphQl; +namespace Database.GraphQl.Approvals; public sealed record DataApprovalInput( DateTime Timestamp, diff --git a/backend/src/GraphQl/Approvals/DataApprovalType.cs b/backend/src/GraphQl/Approvals/DataApprovalType.cs new file mode 100644 index 00000000..f930cd5a --- /dev/null +++ b/backend/src/GraphQl/Approvals/DataApprovalType.cs @@ -0,0 +1,18 @@ +using Database.Data; +using HotChocolate.Types; + +namespace Database.GraphQl.Approvals; + +public sealed class DataApprovalType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Standard) + .Ignore(); + descriptor + .Field(t => t.Publication) + .Ignore(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs b/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs new file mode 100644 index 00000000..8c93161e --- /dev/null +++ b/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs @@ -0,0 +1,12 @@ +using System; + +namespace Database.GraphQl.Approvals; + +public sealed record ResponseApprovalInput( + DateTime Timestamp, + string Signature, + string KeyFingerprint, + string Query, + string Response, + Guid ApproverId +); \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 1090841c..736462b9 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index b907ae9f..4ef2fffd 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 5661dd83..a08ca380 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,16 +1,15 @@ +using System; using System.Linq; using System.Net.Http; -using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; -using HotChocolate; +using Database.GraphQl.Approvals; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GeometricDataX; @@ -113,4 +112,59 @@ input.RootResource.AppliedConversionMethod is null await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); } + + public async Task AddApprovalToGeometricDataAsync( + ApprovalInput input, + ApplicationDbContext context, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + if (!await CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false) + ) + return new AddApprovalPayload( + new AddApprovalError( + AddApprovalErrorCode.UNAUTHORIZED, + $"The current user is not authorized to approval for the institution.", + [] + ) + ); + + var data = await context.GeometricData.AsQueryable().SingleOrDefaultAsync(x => x.Id == input.DataId, cancellationToken).ConfigureAwait(false); + if (data == null) + { + return new AddApprovalPayload( + new AddApprovalError( + AddApprovalErrorCode.UNKNOWN, + $"Unknown data.", + [] + ) + ); + } + + var approval = new DataApproval( + DateTime.Now, + input.Approval.Signature, + input.Approval.KeyFingerprint, + input.Approval.Query, + input.Approval.Response, + input.CreatorId); // Should be CurrentUser Id + data.Approvals.Add(approval); + data.Approval = new ResponseApproval( + DateTime.Now, + input.ResponseApproval.Signature, + input.ResponseApproval.KeyFingerprint, + input.ResponseApproval.Query, + input.ResponseApproval.Response); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new AddApprovalPayload(approval); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 1aa49125..21f832cd 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index aec4124b..d2aa378a 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index a1845620..34a7222f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs index beade8e5..24f59bd4 100644 --- a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs +++ b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs @@ -22,12 +22,20 @@ internal enum InstitutionRepresentativeRole OWNER, ASSISTANT } - private sealed record UserRepresentedInstitutionEdge(Institution Node, InstitutionRepresentativeRole Role); + + internal enum DataSigningPermission + { + NEVER, + GRANTED, + REMOVED + } + + private sealed record UserRepresentedInstitutionEdge(Institution Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission); private sealed record UserRepresentedInstitutionConnection(IReadOnlyList Edges); private sealed record User(UserRepresentedInstitutionConnection RepresentedInstitutions); private sealed record RepresentedInstitutionsByCurrentUser(User CurrentUser); - internal static async Task> Query( + internal static async Task> Query( AppSettings appSettings, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, @@ -51,8 +59,8 @@ await QueryingMetabase.ConstructGraphQlQuery( ?.CurrentUser ?.RepresentedInstitutions ?.Edges - ?.Select(edge => (edge.Node.Uuid, edge.Role)) + ?.Select(edge => (edge.Node.Uuid, edge.Role, edge.DataSigningPermission)) .ToList().AsReadOnly() - ?? Array.Empty<(Guid Id, InstitutionRepresentativeRole Role)>().AsReadOnly(); + ?? Array.Empty<(Guid Id, InstitutionRepresentativeRole Role, DataSigningPermission Permission)>().AsReadOnly(); } } \ No newline at end of file From 091627bed6a574a99bfd141d959554d2f3ad5dea Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Feb 2025 10:37:28 +0100 Subject: [PATCH 258/878] Add services for user and data --- .../src/Authorization/CommonAuthorization.cs | 18 ++-- .../HttpContextAccessorExtensions.cs | 19 ++++ .../src/GraphQl/Approvals/ApprovalMutation.cs | 90 +++++++++++++++++++ backend/src/GraphQl/DataX/DataQueries.cs | 58 ++---------- .../GeometricDataX/GeometricDataMutations.cs | 63 +------------ backend/src/Metabase/CurrentUser.cs | 39 ++++++++ .../src/Metabase/Queries/CurrentUser.graphql | 17 ++++ backend/src/Metabase/QueryingCurrentUser.cs | 41 +++++++++ ...ingRepresentedInstitutionsByCurrentUser.cs | 13 --- backend/src/Services/DataService.cs | 45 ++++++++++ backend/src/Services/IDataService.cs | 14 +++ backend/src/Services/IUserService.cs | 11 +++ backend/src/Services/UserService.cs | 46 ++++++++++ backend/src/Startup.cs | 2 + 14 files changed, 341 insertions(+), 135 deletions(-) create mode 100644 backend/src/Extensions/HttpContextAccessorExtensions.cs create mode 100644 backend/src/GraphQl/Approvals/ApprovalMutation.cs create mode 100644 backend/src/Metabase/CurrentUser.cs create mode 100644 backend/src/Metabase/Queries/CurrentUser.graphql create mode 100644 backend/src/Metabase/QueryingCurrentUser.cs create mode 100644 backend/src/Services/DataService.cs create mode 100644 backend/src/Services/IDataService.cs create mode 100644 backend/src/Services/IUserService.cs create mode 100644 backend/src/Services/UserService.cs diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index cf922482..cc50de94 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -28,13 +28,14 @@ CancellationToken cancellationToken t => t.Id == institutionId && ( - t.Role == QueryingRepresentedInstitutionsByCurrentUser.InstitutionRepresentativeRole.ASSISTANT - || t.Role == QueryingRepresentedInstitutionsByCurrentUser.InstitutionRepresentativeRole.OWNER + t.Role == InstitutionRepresentativeRole.ASSISTANT + || t.Role == InstitutionRepresentativeRole.OWNER ) ); } - public static async Task IsAuthorizedToAddApprovalForInstitution( + public static bool IsAuthorizedToAddApprovalForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -42,15 +43,6 @@ public static async Task IsAuthorizedToAddApprovalForInstitution( CancellationToken cancellationToken ) { - return (await QueryingRepresentedInstitutionsByCurrentUser.Query( - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false)) - .Any(t => - t.Id == institutionId - && t.Permission == QueryingRepresentedInstitutionsByCurrentUser.DataSigningPermission.GRANTED - ); + return currentUser.RepresentedInstitutions.Edges.Any(t => t.Node.Uuid == institutionId && t.DataSigningPermission == DataSigningPermission.GRANTED); } } \ No newline at end of file diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs new file mode 100644 index 00000000..3ca66cd4 --- /dev/null +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using OpenIddict.Client.AspNetCore; + +namespace Database.Extensions; + +public static class HttpContextAccessorExtensions +{ + public static async Task ExtractBearerToken(this IHttpContextAccessor httpContextAccessor) + { + if (httpContextAccessor.HttpContext is null) return null; + return await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken + ).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalMutation.cs b/backend/src/GraphQl/Approvals/ApprovalMutation.cs new file mode 100644 index 00000000..8255cc71 --- /dev/null +++ b/backend/src/GraphQl/Approvals/ApprovalMutation.cs @@ -0,0 +1,90 @@ +using Database.Authorization; +using Database.Data; +using Database.Services; +using System.Net.Http; +using System.Threading.Tasks; +using System.Threading; +using System; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; +using HotChocolate.Authorization; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.Approvals; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ApprovalMutation +{ + [Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + public async Task AddApprovalToDataAsync( + ApprovalInput input, + ApplicationDbContext context, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IUserService userService, + IDataService dataService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken); + if (currentUser == null) + { + return new AddApprovalPayload( + new AddApprovalError( + AddApprovalErrorCode.UNKNOWN, + $"The current user is not known.", + [] + ) + ); + } + + if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( + currentUser, + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) + ) + return new AddApprovalPayload( + new AddApprovalError( + AddApprovalErrorCode.UNAUTHORIZED, + $"The current user is not authorized to approval for the institution.", + [] + ) + ); + + var data = await dataService.GetDataAsync(input.DataId, cancellationToken); + if (data == null) + { + return new AddApprovalPayload( + new AddApprovalError( + AddApprovalErrorCode.UNKNOWN, + $"Unknown data.", + [] + ) + ); + } + + var approval = new DataApproval( + DateTime.Now, + input.Approval.Signature, + input.Approval.KeyFingerprint, + input.Approval.Query, + input.Approval.Response, + currentUser.Id); + data.Approvals.Add(approval); + //data.Approval = new ResponseApproval( + // DateTime.Now, + // input.ResponseApproval.Signature, + // input.ResponseApproval.KeyFingerprint, + // input.ResponseApproval.Query, + // input.ResponseApproval.Response); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new AddApprovalPayload(approval); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 756308c6..02c00979 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,20 +1,14 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; -using Database.Data; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; -using Database.GraphQl.GeometricDataX; -using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; @@ -27,57 +21,23 @@ public sealed class DataQueries [UseSorting] public IAsyncEnumerable GetAllData( [GraphQLType] string? locale, - ApplicationDbContext context, + IDataService dataService, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `locale`. - // return context.CalorimetricData.AsNoTracking(); - // The union below does sadly not work because the different kinds of data have different include operations. - // return context.CalorimetricData.AsNoTracking() - // .Union(context.HygrothermalData.AsNoTracking()) - // .Union(context.OpticalData.AsNoTracking()) - // .Union(context.PhotovoltaicData.AsNoTracking()); - return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() - .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); + // TODO Use `locale`. return context.CalorimetricData.AsNoTracking(); + return dataService.GetAllData(); } public async Task GetDataAsync( Guid id, [GraphQLType] string? locale, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - GeometricDataByIdDataLoader geometricDataById, + IDataService dataService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return - await calorimetricDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await hygrothermalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await opticalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await geometricDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await photovoltaicDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) as IData; + return await dataService.GetDataAsync(id, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index a08ca380..29ff0766 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,15 +1,13 @@ -using System; -using System.Linq; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.GraphQl.Approvals; +using HotChocolate.Authorization; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GeometricDataX; @@ -17,7 +15,7 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataMutations { // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + [Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, @@ -112,59 +110,4 @@ input.RootResource.AppliedConversionMethod is null await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); } - - public async Task AddApprovalToGeometricDataAsync( - ApprovalInput input, - ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - if (!await CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) - ) - return new AddApprovalPayload( - new AddApprovalError( - AddApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to approval for the institution.", - [] - ) - ); - - var data = await context.GeometricData.AsQueryable().SingleOrDefaultAsync(x => x.Id == input.DataId, cancellationToken).ConfigureAwait(false); - if (data == null) - { - return new AddApprovalPayload( - new AddApprovalError( - AddApprovalErrorCode.UNKNOWN, - $"Unknown data.", - [] - ) - ); - } - - var approval = new DataApproval( - DateTime.Now, - input.Approval.Signature, - input.Approval.KeyFingerprint, - input.Approval.Query, - input.Approval.Response, - input.CreatorId); // Should be CurrentUser Id - data.Approvals.Add(approval); - data.Approval = new ResponseApproval( - DateTime.Now, - input.ResponseApproval.Signature, - input.ResponseApproval.KeyFingerprint, - input.ResponseApproval.Query, - input.ResponseApproval.Response); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new AddApprovalPayload(approval); - } } \ No newline at end of file diff --git a/backend/src/Metabase/CurrentUser.cs b/backend/src/Metabase/CurrentUser.cs new file mode 100644 index 00000000..b12df8cf --- /dev/null +++ b/backend/src/Metabase/CurrentUser.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace Database.Metabase; + +public enum InstitutionRepresentativeRole +{ + OWNER, + ASSISTANT +} + +public enum DataSigningPermission +{ + NEVER, + GRANTED, + REMOVED +} + +public record CurrentUser( + Guid Id, + Guid Uuid, + RepresentedInstitutions RepresentedInstitutions +); + +public record Edge( + Node Node, + InstitutionRepresentativeRole Role, + DataSigningPermission DataSigningPermission +); + +public record Node( + Guid Id, + Guid Uuid, + string Name +); + +public record RepresentedInstitutions( + IReadOnlyList Edges +); \ No newline at end of file diff --git a/backend/src/Metabase/Queries/CurrentUser.graphql b/backend/src/Metabase/Queries/CurrentUser.graphql new file mode 100644 index 00000000..69b688da --- /dev/null +++ b/backend/src/Metabase/Queries/CurrentUser.graphql @@ -0,0 +1,17 @@ +query CurrentUser { + currentUser { + id + uuid + representedInstitutions { + edges { + node { + id + uuid + name + } + role + dataSigningPermission + } + } + } +} \ No newline at end of file diff --git a/backend/src/Metabase/QueryingCurrentUser.cs b/backend/src/Metabase/QueryingCurrentUser.cs new file mode 100644 index 00000000..80a6f82c --- /dev/null +++ b/backend/src/Metabase/QueryingCurrentUser.cs @@ -0,0 +1,41 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.Metabase; + +public class QueryingCurrentUser +{ + private static readonly string[] _currentUserFileNames = + { + "CurrentUser.graphql" + }; + + private sealed record CurrentUserResponse(CurrentUser CurrentUser); + + public static async Task Query( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + return (await QueryingMetabase.QueryGraphQl( + appSettings, + new GraphQLRequest( + await QueryingMetabase.ConstructGraphQlQuery( + _currentUserFileNames + ).ConfigureAwait(false), + new { }, + "CurrentUser" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false)) + ?.Data + ?.CurrentUser + ?? null; + } +} \ No newline at end of file diff --git a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs index 24f59bd4..944e79fb 100644 --- a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs +++ b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs @@ -17,19 +17,6 @@ public sealed class QueryingRepresentedInstitutionsByCurrentUser }; private sealed record Institution(Guid Uuid); - internal enum InstitutionRepresentativeRole - { - OWNER, - ASSISTANT - } - - internal enum DataSigningPermission - { - NEVER, - GRANTED, - REMOVED - } - private sealed record UserRepresentedInstitutionEdge(Institution Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission); private sealed record UserRepresentedInstitutionConnection(IReadOnlyList Edges); private sealed record User(UserRepresentedInstitutionConnection RepresentedInstitutions); diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs new file mode 100644 index 00000000..6bf12a57 --- /dev/null +++ b/backend/src/Services/DataService.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.GraphQl.CalorimetricDataX; +using Database.GraphQl.GeometricDataX; +using Database.GraphQl.HygrothermalDataX; +using Database.GraphQl.OpticalDataX; +using Database.GraphQl.PhotovoltaicDataX; +using Microsoft.EntityFrameworkCore; + +namespace Database.Services; + +public class DataService( + ApplicationDbContext context, + CalorimetricDataByIdDataLoader calorimetricDataById, + HygrothermalDataByIdDataLoader hygrothermalDataById, + OpticalDataByIdDataLoader opticalDataById, + PhotovoltaicDataByIdDataLoader photovoltaicDataById, + GeometricDataByIdDataLoader geometricDataById) : IDataService +{ + public IAsyncEnumerable GetAllData() + { + // The union below does sadly not work because the different kinds of data have different + // include operations. return context.CalorimetricData.AsNoTracking() + // .Union(context.HygrothermalData.AsNoTracking()) + // .Union(context.OpticalData.AsNoTracking()) .Union(context.PhotovoltaicData.AsNoTracking()); + return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() + .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); + } + + public async Task GetDataAsync(Guid id, CancellationToken cancellationToken) + { + return await calorimetricDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? + await hygrothermalDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? + await opticalDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? + await geometricDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? + await photovoltaicDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) as IData; + } +} \ No newline at end of file diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/IDataService.cs new file mode 100644 index 00000000..855de133 --- /dev/null +++ b/backend/src/Services/IDataService.cs @@ -0,0 +1,14 @@ +using Database.Data; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; +using System; + +namespace Database.Services; + +public interface IDataService +{ + public IAsyncEnumerable GetAllData(); + + public Task GetDataAsync(Guid id, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs new file mode 100644 index 00000000..386f3d77 --- /dev/null +++ b/backend/src/Services/IUserService.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; +using Database.Metabase; +using Microsoft.AspNetCore.Http; + +namespace Database.Services; + +public interface IUserService +{ + public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs new file mode 100644 index 00000000..f4860cb6 --- /dev/null +++ b/backend/src/Services/UserService.cs @@ -0,0 +1,46 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Extensions; +using Database.Metabase; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Caching.Memory; + +namespace Database.Services; + +public class UserService( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IMemoryCache cache) : IUserService +{ + public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + { + var token = await httpContextAccessor.ExtractBearerToken(); + + if (token == null) + { + return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken); + } + + if (!cache.TryGetValue(token, out CurrentUser? cacheUser)) + { + cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken); + var cacheEntryOptions = new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromHours(1)); + + cache.Set(token, cacheUser, cacheEntryOptions); + } + + return cacheUser; + } + + private async Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + { + return await QueryingCurrentUser.Query( + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 4d83d2ba..f2b90429 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -63,6 +63,8 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(_appSettings); services.AddSingleton(_environment); // services.AddDatabaseDeveloperPageExceptionFilter(); + services.AddSingleton(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) From 06b3f50ae613e4f2d8f5d1bf3b2a629bd05af93f Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Feb 2025 11:04:27 +0100 Subject: [PATCH 259/878] Add missing "configureAwait" --- .../src/GraphQl/Approvals/ApprovalMutation.cs | 17 ++++++++--------- backend/src/Metabase/QueryingMetabase.cs | 14 ++------------ backend/src/Services/UserService.cs | 6 +++--- backend/src/Startup.cs | 11 +++++------ 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/backend/src/GraphQl/Approvals/ApprovalMutation.cs b/backend/src/GraphQl/Approvals/ApprovalMutation.cs index 8255cc71..61b4e1e1 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutation.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutation.cs @@ -1,14 +1,13 @@ -using Database.Authorization; -using Database.Data; -using Database.Services; +using System; using System.Net.Http; -using System.Threading.Tasks; using System.Threading; -using System; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Authorization; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using HotChocolate.Authorization; -using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.Approvals; @@ -29,7 +28,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( httpContextAccessor, - cancellationToken); + cancellationToken).ConfigureAwait(false); if (currentUser == null) { return new AddApprovalPayload( @@ -58,7 +57,7 @@ CancellationToken cancellationToken ) ); - var data = await dataService.GetDataAsync(input.DataId, cancellationToken); + var data = await dataService.GetDataAsync(input.DataId, cancellationToken).ConfigureAwait(false); if (data == null) { return new AddApprovalPayload( diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs index 0448c06b..23298ef5 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -8,6 +8,7 @@ using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Database.Extensions; using GraphQL; using GraphQL.Client.Serializer.SystemTextJson; using Microsoft.AspNetCore.Authentication; @@ -49,17 +50,6 @@ public sealed class QueryingMetabase DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; //.SetupImmutableConverter(); - private static async Task ExtractBearerToken( - IHttpContextAccessor httpContextAccessor - ) - { - if (httpContextAccessor.HttpContext is null) return null; - return await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken - ).ConfigureAwait(false); - } - public static async Task ConstructGraphQlQuery( string[] fileNames ) @@ -143,7 +133,7 @@ CancellationToken cancellationToken where TResponse : class { using var httpClient = httpClientFactory.CreateClient(MetabaseHttpClient); - var bearerToken = await ExtractBearerToken(httpContextAccessor).ConfigureAwait(false); + var bearerToken = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); using var httpRequestMessage = new HttpRequestMessage( httpMethod, uri diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index f4860cb6..126be909 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -16,16 +16,16 @@ public class UserService( { public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - var token = await httpContextAccessor.ExtractBearerToken(); + var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); if (token == null) { - return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken); + return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); } if (!cache.TryGetValue(token, out CurrentUser? cacheUser)) { - cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken); + cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index f2b90429..4cd8874d 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,16 +1,16 @@ -using System; +using System; using System.IO; +using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; -using System.Net.Http; -using HotChocolate.AspNetCore; using Database.Configuration; using Database.Data; using Database.Data.Extensions; using Database.Enumerations; -using Database.Services; using Database.Metabase; +using Database.Services; +using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -24,9 +24,8 @@ using Microsoft.Extensions.Diagnostics.HealthChecks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Npgsql; -using Serilog; using Microsoft.OpenApi; +using Serilog; namespace Database; From 0a0efa885128328cde28a976ea8bd00811e63e98 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 11 Feb 2025 16:22:53 +0100 Subject: [PATCH 260/878] Add `databaseId` to the interface `Data` and resolve it from a corresponding environment variable `DATABASE_ID` --- .env.production.sample | 2 ++ .env.sample | 2 ++ backend/src/AppSettings.cs | 4 ++++ backend/src/GraphQl/DataX/DataType.cs | 11 +++++++++++ docker-compose.production.yml | 1 + docker-compose.yml | 1 + 6 files changed, 21 insertions(+) diff --git a/.env.production.sample b/.env.production.sample index 7600d7fe..c28913aa 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -5,6 +5,8 @@ HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org +DATABASE_ID= + OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= diff --git a/.env.sample b/.env.sample index 2c31f2cc..df755758 100644 --- a/.env.sample +++ b/.env.sample @@ -7,6 +7,8 @@ HTTPS_PORT=5051 METABASE_HOST=local.buildingenvelopedata.org METABASE_HTTPS_PORT=4041 +DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 + OPEN_ID_CONNECT_CLIENT_SECRET=secret VERIFICATION_CODE=secret diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 1d7012ae..b463c5aa 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,5 +1,7 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited +using System; + namespace Database; public sealed class AppSettings @@ -10,6 +12,8 @@ public sealed class AppSettings public string MetabaseHost { get; set; } = ""; + public Guid DatabaseId { get; set; } + public string VerificationCode { get; set; } = ""; diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index af7d4d3d..4a6a63da 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -6,6 +6,13 @@ namespace Database.GraphQl.DataX; public sealed class DataType : InterfaceType { + private readonly AppSettings _appSettings; + + public DataType(AppSettings appSettings) + { + _appSettings = appSettings; + } + protected override void Configure( IInterfaceTypeDescriptor descriptor ) @@ -15,6 +22,10 @@ IInterfaceTypeDescriptor descriptor descriptor .Field("uuid") .Type>(); + descriptor + .Field("databaseId") + .Type>() + .Resolve(_ => _appSettings.DatabaseId); descriptor .Field("timestamp") .Type>(); diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c902062c..c2ae9679 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -69,6 +69,7 @@ services: - ASPNETCORE_ENVIRONMENT=production - XBASE_Host=https://${HOST} - XBASE_MetabaseHost=https://${METABASE_HOST} + - XBASE_DatabaseId=${DATABASE_ID} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} diff --git a/docker-compose.yml b/docker-compose.yml index 87a19ccf..71b2087b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -107,6 +107,7 @@ services: - ASPNETCORE_ENVIRONMENT=development - XBASE_Host=https://${HOST}:${HTTPS_PORT} - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} + - XBASE_DatabaseId=${DATABASE_ID} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} From a737cc5dc079e0c64fabffe5792abde1314f4223 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 11 Feb 2025 18:22:40 +0100 Subject: [PATCH 261/878] Remove the queries `allData`, `data`, and `hasData` conforming to the latest GraphQL specification of product-data databases --- https://github.com/building-envelope-data/api/commit/2acc08473fae04f403635f5dcd47c2b1fb332353 --- .../src/Configuration/GraphQlConfiguration.cs | 2 +- backend/src/GraphQl/DataX/DataConnection.cs | 17 ++ .../src/GraphQl/DataX/DataConnectionBase.cs | 17 ++ backend/src/GraphQl/DataX/DataEdge.cs | 14 + backend/src/GraphQl/DataX/DataEdgeBase.cs | 10 + backend/src/GraphQl/DataX/DataQueries.cs | 83 ------ frontend/components/Layout.tsx | 4 - frontend/pages/data/index.tsx | 252 ------------------ frontend/paths.ts | 1 - frontend/queries/data.graphql | 20 -- frontend/type-defs.graphqls | 18 +- 11 files changed, 66 insertions(+), 372 deletions(-) create mode 100644 backend/src/GraphQl/DataX/DataConnection.cs create mode 100644 backend/src/GraphQl/DataX/DataConnectionBase.cs create mode 100644 backend/src/GraphQl/DataX/DataEdge.cs create mode 100644 backend/src/GraphQl/DataX/DataEdgeBase.cs delete mode 100644 backend/src/GraphQl/DataX/DataQueries.cs delete mode 100644 frontend/pages/data/index.tsx diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 61ccd512..618a57b7 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -129,8 +129,8 @@ AppSettings appSettings // Query Types .AddQueryType(d => d.Name(nameof(Query))) .AddType() + .AddType() .AddType() - .AddType() .AddType() .AddType() .AddType() diff --git a/backend/src/GraphQl/DataX/DataConnection.cs b/backend/src/GraphQl/DataX/DataConnection.cs new file mode 100644 index 00000000..395d9738 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataConnection.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using HotChocolate.Types.Pagination; + +namespace Database.GraphQl.DataX; + +public sealed class DataConnection( + IReadOnlyList edges, + ConnectionPageInfo pageInfo + ) + : DataConnectionBase( + edges, + pageInfo + ) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataConnectionBase.cs b/backend/src/GraphQl/DataX/DataConnectionBase.cs new file mode 100644 index 00000000..99ca96ac --- /dev/null +++ b/backend/src/GraphQl/DataX/DataConnectionBase.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using HotChocolate; +using HotChocolate.Types; +using HotChocolate.Types.Pagination; + +namespace Database.GraphQl.DataX; + +public abstract class DataConnectionBase( + IReadOnlyList edges, + ConnectionPageInfo pageInfo + ) +{ + public IReadOnlyList Edges { get; } = edges; + + public ConnectionPageInfo PageInfo { get; } = pageInfo; +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdge.cs b/backend/src/GraphQl/DataX/DataEdge.cs new file mode 100644 index 00000000..0b60ee08 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataEdge.cs @@ -0,0 +1,14 @@ +using Database.Data; + +namespace Database.GraphQl.DataX; + +public sealed class DataEdge( + string cursor, + IData node + ) + : DataEdgeBase( + cursor, + node + ) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdgeBase.cs b/backend/src/GraphQl/DataX/DataEdgeBase.cs new file mode 100644 index 00000000..90ed6048 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataEdgeBase.cs @@ -0,0 +1,10 @@ +namespace Database.GraphQl.DataX; + +public abstract class DataEdgeBase( + string cursor, + TData node + ) +{ + public string Cursor { get; } = cursor; + public TData Node { get; } = node; +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs deleted file mode 100644 index 756308c6..00000000 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using HotChocolate; -using HotChocolate.Data; -using HotChocolate.Data.Sorting; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; -using Database.Data; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; -using Database.GraphQl.GeometricDataX; -using Database.GraphQl.Extensions; - -namespace Database.GraphQl.DataX; - -[ExtendObjectType(nameof(Query))] -public sealed class DataQueries -{ - [UsePaging] // TODO Paging does not work with this data source. - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - // [UseFiltering] // TODO Filtering does not work with unions. - [UseSorting] - public IAsyncEnumerable GetAllData( - [GraphQLType] string? locale, - ApplicationDbContext context, - ISortingContext sorting - ) - { - sorting.StabilizeOrder(); - // TODO Use `locale`. - // return context.CalorimetricData.AsNoTracking(); - // The union below does sadly not work because the different kinds of data have different include operations. - // return context.CalorimetricData.AsNoTracking() - // .Union(context.HygrothermalData.AsNoTracking()) - // .Union(context.OpticalData.AsNoTracking()) - // .Union(context.PhotovoltaicData.AsNoTracking()); - return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() - .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); - } - - public async Task GetDataAsync( - Guid id, - [GraphQLType] string? locale, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - GeometricDataByIdDataLoader geometricDataById, - CancellationToken cancellationToken - ) - { - // TODO Use `locale`. - return - await calorimetricDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await hygrothermalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await opticalDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await geometricDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) ?? - await photovoltaicDataById.LoadAsync( - id, - cancellationToken - ).ConfigureAwait(false) as IData; - } -} \ No newline at end of file diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index bf13d102..cc2917be 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -11,10 +11,6 @@ const navItems = [ path: paths.home, label: "Home", }, - { - path: paths.data, - label: "All Data", - }, { path: paths.calorimetricData, label: "Calorimetric Data", diff --git a/frontend/pages/data/index.tsx b/frontend/pages/data/index.tsx deleted file mode 100644 index d04cceb2..00000000 --- a/frontend/pages/data/index.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import Layout from "../../components/Layout"; -import { Table, message, Form, Button, Alert, Typography } from "antd"; -import { useAllDataQuery } from "../../queries/data.graphql"; -import { - Data, - Scalars, - DataPropositionInput, -} from "../../__generated__/__types__"; -import { useState } from "react"; -import { setMapValue } from "../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../lib/table"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../components/UuidPropositionFormList"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: DataPropositionInput -): DataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: DataPropositionInput[] -): DataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: DataPropositionInput[] -// ): DataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() - ); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allDataQuery = useAllDataQuery({ - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: DataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }) - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }) - ); - } - } - const { error, data: dataX } = await allDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - } - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error( - error.graphQLErrors.map((error) => error.message).join(" ") - ); - } - // TODO Casting to `Data` is wrong and error prone! - setData((dataX?.allData?.edges?.map((x) => x.node) || []) as Data[]); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; - - return ( - - Data - {/* TODO Display error messages in a list? */} - {globalErrorMessages.length > 0 && ( - - )} - - - - - - - - -
( - onFilterTextChange, - (x) => filterText.get(x), - (_uuid) => "/" // TODO Link somewhere useful! - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - ]} - dataSource={data} - /> - - ); -} - -export default Page; diff --git a/frontend/paths.ts b/frontend/paths.ts index 9fac4d6e..a1c918a8 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -5,7 +5,6 @@ export default { antiforgeryToken: "/antiforgery/token", userInfo: "/user-info", database: "/database", - data: "/data", calorimetricData: "/data/calorimetric", calorimetricDatum(uuid: string) { return `/data/calorimetric/${encodeURIComponent(uuid)}`; diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 8386f98e..87b4b453 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -74,26 +74,6 @@ fragment DataPartial on Data { warnings } -query Data($uuid: Uuid!) { - data(id: $uuid) { - ...DataPartial - } -} - -query AllData { - allData { - edges { - node { - ...DataPartial - } - } - } -} - -# query HasData($where: DataPropositionInput) { -# hasData(where: $where) -# } - fragment CalorimetricDataPartial on CalorimetricData { ...DataPartial gValues diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index e2bbab17..49a37f79 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -9,6 +9,7 @@ interface Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String locale: Locale! name: String @@ -46,6 +47,7 @@ type CalorimetricData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String gValues: [Float!]! id: ID! @@ -172,21 +174,13 @@ type DataApproval { timestamp: DateTime! } -"A connection to a list of items." type DataConnection { - "A list of edges." - edges: [DataEdge!] - "Information to aid in pagination." + edges: [DataEdge!]! pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") } -"An edge in a connection." type DataEdge { - "A cursor for use in pagination." cursor: String! - "The item at the end of the edge." node: Data! } @@ -212,6 +206,7 @@ type GeometricData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String id: ID! locale: Locale! @@ -297,6 +292,7 @@ type HygrothermalData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String id: ID! locale: Locale! @@ -365,6 +361,7 @@ type OpticalData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String id: ID! infraredEmittances: [Float!]! @@ -417,6 +414,7 @@ type PhotovoltaicData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + databaseId: Uuid! description: String id: ID! locale: Locale! @@ -464,7 +462,6 @@ type Publication implements Reference { type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [IDataSortInput!] @cost(weight: "10")): DataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") @@ -472,7 +469,6 @@ type Query { calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo @cost(weight: "10") - data(id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") From 39f5505d88d85d14e85d22f6aed0531b3f6f87f6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 11 Feb 2025 18:31:28 +0100 Subject: [PATCH 262/878] Add total count to `DataConnection` --- backend/src/GraphQl/DataX/DataConnection.cs | 2 ++ backend/src/GraphQl/DataX/DataConnectionBase.cs | 3 +++ frontend/type-defs.graphqls | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/backend/src/GraphQl/DataX/DataConnection.cs b/backend/src/GraphQl/DataX/DataConnection.cs index 395d9738..a6e4b206 100644 --- a/backend/src/GraphQl/DataX/DataConnection.cs +++ b/backend/src/GraphQl/DataX/DataConnection.cs @@ -7,10 +7,12 @@ namespace Database.GraphQl.DataX; public sealed class DataConnection( IReadOnlyList edges, + uint totalCount, ConnectionPageInfo pageInfo ) : DataConnectionBase( edges, + totalCount, pageInfo ) { diff --git a/backend/src/GraphQl/DataX/DataConnectionBase.cs b/backend/src/GraphQl/DataX/DataConnectionBase.cs index 99ca96ac..ae976dd8 100644 --- a/backend/src/GraphQl/DataX/DataConnectionBase.cs +++ b/backend/src/GraphQl/DataX/DataConnectionBase.cs @@ -8,10 +8,13 @@ namespace Database.GraphQl.DataX; public abstract class DataConnectionBase( IReadOnlyList edges, + uint totalCount, ConnectionPageInfo pageInfo ) { public IReadOnlyList Edges { get; } = edges; + [GraphQLType] public uint TotalCount { get; } = totalCount; + public ConnectionPageInfo PageInfo { get; } = pageInfo; } \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 49a37f79..4c1acbfa 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -177,6 +177,7 @@ type DataApproval { type DataConnection { edges: [DataEdge!]! pageInfo: PageInfo! + totalCount: NonNegativeInt } type DataEdge { @@ -1353,6 +1354,9 @@ scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time" "BCP 47 compliant Language Tag string" scalar Locale +"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." +scalar NonNegativeInt + scalar Url scalar Uuid \ No newline at end of file From 0dfa1203bd71a1f2a8de56d34d36fd1205d167ce Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Feb 2025 17:00:46 +0100 Subject: [PATCH 263/878] Add `type`, `subtype`, and `coatedSide` to *Data domain models --- backend/src/Data/ApplicationDbContext.cs | 3 + backend/src/Data/CalorimetricData.cs | 15 +- backend/src/Data/DataX.cs | 20 +- backend/src/Data/GeometricData.cs | 15 +- backend/src/Data/HygrothermalData.cs | 17 +- backend/src/Data/OpticalData.cs | 15 +- backend/src/Data/PhotovoltaicData.cs | 17 +- backend/src/Enumerations/CoatedSide.cs | 12 + backend/src/Enumerations/DataSubtype.cs | 30 + backend/src/Enumerations/DataType.cs | 10 + .../CalorimetricDataMutations.cs | 3 + .../CreateCalorimetricDataInput.cs | 4 + .../CreateGeometricDataInput.cs | 4 + .../GeometricDataX/GeometricDataMutations.cs | 3 + .../CreateHygrothermalDataInput.cs | 4 + .../HygrothermalDataMutations.cs | 3 + .../OpticalDataX/CreateOpticalDataInput.cs | 4 + .../OpticalDataX/OpticalDataMutations.cs | 3 + .../CreatePhotovoltaicDataInput.cs | 4 + .../PhotovoltaicDataMutations.cs | 3 + ...TypeSubtypeAndCoatedSideToData.Designer.cs | 2141 +++++++++++++++++ ...55333_AddTypeSubtypeAndCoatedSideToData.cs | 209 ++ .../ApplicationDbContextModelSnapshot.cs | 48 + backend/src/Startup.cs | 3 + 24 files changed, 2581 insertions(+), 9 deletions(-) create mode 100644 backend/src/Enumerations/CoatedSide.cs create mode 100644 backend/src/Enumerations/DataSubtype.cs create mode 100644 backend/src/Enumerations/DataType.cs create mode 100644 backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs create mode 100644 backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 7cf7da9d..b06285d7 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -15,7 +15,10 @@ public sealed class ApplicationDbContext private const string DefaultSchemaName = "database"; private readonly string _schemaName; + internal const string CoatedSideTypeName = "coated_side"; internal const string DataKindTypeName = "data_kind"; + internal const string DataSubtypeTypeName = "data_subtype"; + internal const string DataTypeTypeName = "data_type"; internal const string StandardizerTypeName = "standardizer"; public ApplicationDbContext( diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index ed3f8260..02981787 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; namespace Database.Data; @@ -15,6 +16,9 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, // ResponseApproval approval @@ -28,6 +32,9 @@ double[] uValues warnings, creatorId, createdAt, + type, + subtype, + coatedSide, appliedMethod, approvals ) @@ -45,6 +52,9 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, // ResponseApproval approval double[] gValues, double[] uValues @@ -55,7 +65,10 @@ double[] uValues description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { GValues = gValues; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index b7fd6c1e..2278cd8b 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.Enumerations; namespace Database.Data; @@ -14,6 +15,9 @@ protected DataX( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -25,7 +29,10 @@ ICollection approvals description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { AppliedMethod = appliedMethod; @@ -41,7 +48,10 @@ protected DataX( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide ) { Locale = locale; @@ -51,6 +61,9 @@ DateTime createdAt Warnings = warnings; CreatorId = creatorId; CreatedAt = createdAt; + Type = type; + Subtype = subtype; + CoatedSide = coatedSide; } public string Locale { get; private set; } @@ -60,6 +73,9 @@ DateTime createdAt public string[] Warnings { get; private set; } public Guid CreatorId { get; private set; } public DateTime CreatedAt { get; private set; } + public DataType? Type { get; private set; } + public DataSubtype? Subtype { get; private set; } + public CoatedSide? CoatedSide { get; private set; } public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index c7587bdc..44a2b075 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; namespace Database.Data; @@ -15,6 +16,9 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, // ResponseApproval approval, @@ -27,6 +31,9 @@ double[] thicknesses warnings, creatorId, createdAt, + type, + subtype, + coatedSide, appliedMethod, approvals // approval @@ -43,6 +50,9 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, // ResponseApproval approval, double[] thicknesses ) : base( @@ -52,7 +62,10 @@ double[] thicknesses description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { Thicknesses = thicknesses; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 7fe32480..85fb87b1 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; namespace Database.Data; @@ -15,6 +16,9 @@ public HygrothermalData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -26,6 +30,9 @@ ICollection approvals warnings, creatorId, createdAt, + type, + subtype, + coatedSide, appliedMethod, approvals ) @@ -40,7 +47,10 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide // ResponseApproval approval ) : base( locale, @@ -49,7 +59,10 @@ DateTime createdAt description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { } diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index e54ecb36..5a6d2bcb 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; namespace Database.Data; @@ -15,6 +16,9 @@ public OpticalData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, // ResponseApproval approval @@ -33,6 +37,9 @@ ICollection cielabColors warnings, creatorId, createdAt, + type, + subtype, + coatedSide, appliedMethod, approvals ) @@ -55,6 +62,9 @@ public OpticalData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, // ResponseApproval approval double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, @@ -69,7 +79,10 @@ double[] colorRenderingIndices description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index f0e5de9d..a0f8048b 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; namespace Database.Data; @@ -15,6 +16,9 @@ public PhotovoltaicData( string[] warnings, Guid creatorId, DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -26,6 +30,9 @@ ICollection approvals warnings, creatorId, createdAt, + type, + subtype, + coatedSide, appliedMethod, approvals ) @@ -40,7 +47,10 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + DateTime createdAt, + DataType? type, + DataSubtype? subtype, + CoatedSide? coatedSide // ResponseApproval approval ) : base( locale, @@ -49,7 +59,10 @@ DateTime createdAt description, warnings, creatorId, - createdAt + createdAt, + type, + subtype, + coatedSide ) { } diff --git a/backend/src/Enumerations/CoatedSide.cs b/backend/src/Enumerations/CoatedSide.cs new file mode 100644 index 00000000..e787051c --- /dev/null +++ b/backend/src/Enumerations/CoatedSide.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum CoatedSide +{ + FRONT, + BACK, + BOTH, + NEITHER +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataSubtype.cs b/backend/src/Enumerations/DataSubtype.cs new file mode 100644 index 00000000..e33b085d --- /dev/null +++ b/backend/src/Enumerations/DataSubtype.cs @@ -0,0 +1,30 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum DataSubtype +{ + MONOLITHIC, + LAMINATE, + INTERLAYER, + EMBEDDED_COATING, + COATED, + COATING, + APPLIED_FILM, + FILM, + VENETIAN_BLIND, + DIFFUSING_SHADE, + ROLLER_SHADE, + WOVEN_SHADE, + VERTICAL_LOUVER, + PERFORATED_SCREEN, + CELLULAR_SHADE, + PLEATED_SHADE, + ROMAN_SHADE, + SHADE_MATERIAL, + FRITTED_GLASS, + ACID_ETCHED_GLASS, + SANDBLASTED_GLASS, + CHROMOGENIC +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataType.cs b/backend/src/Enumerations/DataType.cs new file mode 100644 index 00000000..d91f17cc --- /dev/null +++ b/backend/src/Enumerations/DataType.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum DataType +{ + GLAZING, + SHADING +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 75c236e6..0d366752 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -51,6 +51,9 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 1090841c..3974cb8e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; +using Database.Enumerations; namespace Database.GraphQl.CalorimetricDataX; @@ -14,6 +15,9 @@ public sealed record CreateCalorimetricDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, + DataType? Type, + DataSubtype? Subtype, + CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index b907ae9f..06386cf8 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; +using Database.Enumerations; namespace Database.GraphQl.GeometricDataX; @@ -13,6 +14,9 @@ public sealed record CreateGeometricDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, + DataType? Type, + DataSubtype? Subtype, + CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 5661dd83..ccf1eb38 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -51,6 +51,9 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 1aa49125..b31ebff9 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; +using Database.Enumerations; namespace Database.GraphQl.HygrothermalDataX; @@ -14,6 +15,9 @@ public sealed record CreateHygrothermalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, + DataType? Type, + DataSubtype? Subtype, + CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index f4a0c1d1..09822c69 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -51,6 +51,9 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index aec4124b..8e1c8466 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; +using Database.Enumerations; namespace Database.GraphQl.OpticalDataX; @@ -14,6 +15,9 @@ public sealed record CreateOpticalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, + DataType? Type, + DataSubtype? Subtype, + CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index b852d1b9..6e221f21 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -51,6 +51,9 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index a1845620..2139d66b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; +using Database.Enumerations; namespace Database.GraphQl.PhotovoltaicDataX; @@ -14,6 +15,9 @@ public sealed record CreatePhotovoltaicDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, + DataType? Type, + DataSubtype? Subtype, + CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 857485b3..aaa174ef 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -51,6 +51,9 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs new file mode 100644 index 00000000..1cd26678 --- /dev/null +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs @@ -0,0 +1,2141 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250213155333_AddTypeSubtypeAndCoatedSideToData")] + partial class AddTypeSubtypeAndCoatedSideToData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Type") + .HasColumnType("database.data_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs new file mode 100644 index 00000000..0bfaaef6 --- /dev/null +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs @@ -0,0 +1,209 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddTypeSubtypeAndCoatedSideToData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") + .Annotation("Npgsql:Enum:database.data_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .Annotation("Npgsql:Enum:database.data_type", "glazing,shading"); + + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "photovoltaic_data", + type: "database.coated_side", + nullable: true); + + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "photovoltaic_data", + type: "database.data_subtype", + nullable: true); + + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "photovoltaic_data", + type: "database.data_type", + nullable: true); + + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data", + type: "database.coated_side", + nullable: true); + + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "optical_data", + type: "database.data_subtype", + nullable: true); + + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "optical_data", + type: "database.data_type", + nullable: true); + + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "hygrothermal_data", + type: "database.coated_side", + nullable: true); + + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "hygrothermal_data", + type: "database.data_subtype", + nullable: true); + + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "hygrothermal_data", + type: "database.data_type", + nullable: true); + + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "geometric_data", + type: "database.coated_side", + nullable: true); + + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "geometric_data", + type: "database.data_subtype", + nullable: true); + + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "geometric_data", + type: "database.data_type", + nullable: true); + + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "calorimetric_data", + type: "database.coated_side", + nullable: true); + + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "calorimetric_data", + type: "database.data_subtype", + nullable: true); + + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "calorimetric_data", + type: "database.data_type", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") + .OldAnnotation("Npgsql:Enum:database.data_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .OldAnnotation("Npgsql:Enum:database.data_type", "glazing,shading"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 7083ce09..4f2d1c84 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -23,7 +23,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "9.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 63); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -35,6 +38,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + b.Property("ComponentId") .HasColumnType("uuid"); @@ -58,6 +64,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + b.PrimitiveCollection("UValues") .IsRequired() .HasColumnType("double precision[]"); @@ -84,6 +96,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + b.Property("ComponentId") .HasColumnType("uuid"); @@ -103,10 +118,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); + b.Property("Type") + .HasColumnType("database.data_type"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -188,6 +209,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + b.Property("ComponentId") .HasColumnType("uuid"); @@ -207,6 +231,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -229,6 +259,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + b.PrimitiveCollection("ColorRenderingIndices") .IsRequired() .HasColumnType("double precision[]"); @@ -272,6 +305,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -294,6 +333,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + b.Property("ComponentId") .HasColumnType("uuid"); @@ -313,6 +355,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("Subtype") + .HasColumnType("database.data_subtype"); + + b.Property("Type") + .HasColumnType("database.data_type"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 223baef6..441c6302 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -164,7 +164,10 @@ AppSettings appSettings appSettings.Database.ConnectionString, _ => _ .SetPostgresVersion(13, 13) + .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.DataSubtypeTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.DataTypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) // .UseNodaTime(); ) From 07bd6febb8a5c03633c69a1614624ff6b30445e4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Feb 2025 17:12:29 +0100 Subject: [PATCH 264/878] Update frontend GraphQL schema --- frontend/type-defs.graphqls | 67 +++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 4c1acbfa..9361818b 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -44,6 +44,7 @@ type AppliedMethod { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -55,7 +56,9 @@ type CalorimetricData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! + subtype: DataSubtype timestamp: DateTime! + type: DataType uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -204,6 +207,7 @@ type FileMetaInformation { type GeometricData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -214,8 +218,10 @@ type GeometricData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! + subtype: DataSubtype thicknesses: [Float!]! timestamp: DateTime! + type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -290,6 +296,7 @@ type GetHttpsResourceTreeRoot { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -300,7 +307,9 @@ type HygrothermalData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! + subtype: DataSubtype timestamp: DateTime! + type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -358,6 +367,7 @@ type OpticalData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! cielabColors: [CielabColor!]! + coatedSide: CoatedSide colorRenderingIndices: [Float!]! componentId: Uuid! createdAt: DateTime! @@ -374,7 +384,9 @@ type OpticalData implements Node & Data { nearnormalHemisphericalVisibleTransmittances: [Float!]! resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! + subtype: DataSubtype timestamp: DateTime! + type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -412,6 +424,7 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -422,7 +435,9 @@ type PhotovoltaicData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! + subtype: DataSubtype timestamp: DateTime! + type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -603,6 +618,7 @@ input CielabColorPropositionInput { input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -611,6 +627,8 @@ input CreateCalorimetricDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! + subtype: DataSubtype + type: DataType uValues: [Float!]! warnings: [String!]! } @@ -618,6 +636,7 @@ input CreateCalorimetricDataInput { input CreateGeometricDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -625,7 +644,9 @@ input CreateGeometricDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! + subtype: DataSubtype thicknesses: [Float!]! + type: DataType warnings: [String!]! } @@ -643,6 +664,7 @@ input CreateGetHttpsResourceInput { input CreateHygrothermalDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -650,6 +672,8 @@ input CreateHygrothermalDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! + subtype: DataSubtype + type: DataType warnings: [String!]! } @@ -663,6 +687,7 @@ input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! cielabColors: [CielabColorInput!]! + coatedSide: CoatedSide colorRenderingIndices: [Float!]! componentId: Uuid! createdAt: DateTime! @@ -676,12 +701,15 @@ input CreateOpticalDataInput { nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! rootResource: RootGetHttpsResourceInput! + subtype: DataSubtype + type: DataType warnings: [String!]! } input CreatePhotovoltaicDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! + coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -689,6 +717,8 @@ input CreatePhotovoltaicDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! + subtype: DataSubtype + type: DataType warnings: [String!]! } @@ -1247,6 +1277,13 @@ enum ApplyPolicy { VALIDATION } +enum CoatedSide { + FRONT + BACK + BOTH + NEITHER +} + enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED @@ -1286,6 +1323,36 @@ enum DataKind { PHOTOVOLTAIC_DATA } +enum DataSubtype { + MONOLITHIC + LAMINATE + INTERLAYER + EMBEDDED_COATING + COATED + COATING + APPLIED_FILM + FILM + VENETIAN_BLIND + DIFFUSING_SHADE + ROLLER_SHADE + WOVEN_SHADE + VERTICAL_LOUVER + PERFORATED_SCREEN + CELLULAR_SHADE + PLEATED_SHADE + ROMAN_SHADE + SHADE_MATERIAL + FRITTED_GLASS + ACID_ETCHED_GLASS + SANDBLASTED_GLASS + CHROMOGENIC +} + +enum DataType { + GLAZING + SHADING +} + enum DatabaseVerificationState { PENDING VERIFIED From 114dee3040cd8f89368e5d15030abe4edb57ee81 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Feb 2025 17:27:40 +0100 Subject: [PATCH 265/878] Fetch color-rendering indices --- frontend/queries/data.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 87b4b453..91006602 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -131,6 +131,7 @@ fragment OpticalDataPartial on OpticalData { nearnormalHemisphericalSolarTransmittances nearnormalHemisphericalVisibleReflectances nearnormalHemisphericalVisibleTransmittances + colorRenderingIndices } query OpticalData($uuid: Uuid!) { From 8ec61fb372dbed753a32f6002f47dd9a930fdd5e Mon Sep 17 00:00:00 2001 From: jbinterap Date: Fri, 14 Feb 2025 09:09:38 +0100 Subject: [PATCH 266/878] Remove Approval from data mutations --- .../src/Configuration/GraphQlConfiguration.cs | 2 + backend/src/Data/CalorimetricData.cs | 8 +-- backend/src/Data/DataX.cs | 13 ++--- backend/src/Data/GeometricData.cs | 15 ++---- backend/src/Data/HygrothermalData.cs | 10 ++-- backend/src/Data/IData.cs | 7 +-- backend/src/Data/OpticalData.cs | 8 +-- backend/src/Data/PhotovoltaicData.cs | 10 ++-- .../src/GraphQl/Approvals/ApprovalInput.cs | 3 +- .../src/GraphQl/Approvals/ApprovalMutation.cs | 30 ++++++++--- .../Approvals/ResponseApprovalInput.cs | 12 ----- .../CalorimetricDataMutations.cs | 35 ------------ .../CreateCalorimetricDataInput.cs | 6 +-- .../GraphQl/DataApprovals/DataApprovalType.cs | 19 ------- backend/src/GraphQl/DataX/DataQueries.cs | 54 ++++++++++++++++--- .../CreateGeometricDataInput.cs | 6 +-- .../GeometricDataX/GeometricDataMutations.cs | 11 ---- .../CreateHygrothermalDataInput.cs | 6 +-- .../HygrothermalDataMutations.cs | 37 +------------ .../OpticalDataX/CreateOpticalDataInput.cs | 5 +- .../OpticalDataX/OpticalDataMutations.cs | 35 ------------ .../CreatePhotovoltaicDataInput.cs | 6 +-- .../PhotovoltaicDataMutations.cs | 37 +------------ backend/src/Services/DataService.cs | 40 +++----------- backend/src/Services/IDataService.cs | 11 ++-- 25 files changed, 112 insertions(+), 314 deletions(-) delete mode 100644 backend/src/GraphQl/Approvals/ResponseApprovalInput.cs delete mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalType.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 61ccd512..5b0140eb 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -27,6 +27,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; +using Database.GraphQl.Approvals; namespace Database.Configuration; @@ -147,6 +148,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index ed3f8260..131d6e66 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -16,8 +16,6 @@ public CalorimetricData( Guid creatorId, DateTime createdAt, AppliedMethod appliedMethod, - ICollection approvals, - // ResponseApproval approval double[] gValues, double[] uValues ) : base( @@ -28,8 +26,7 @@ double[] uValues warnings, creatorId, createdAt, - appliedMethod, - approvals + appliedMethod ) { GValues = gValues; @@ -45,7 +42,6 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, - // ResponseApproval approval double[] gValues, double[] uValues ) : base( diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 38a352d0..1e2232af 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Database.Data; @@ -14,9 +14,7 @@ protected DataX( string[] warnings, Guid creatorId, DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : this( locale, @@ -29,8 +27,6 @@ ICollection approvals ) { AppliedMethod = appliedMethod; - Approvals = approvals; - // Approval = approval; } // `DbContext` needs this constructor without owned entities. @@ -63,8 +59,9 @@ DateTime createdAt public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); - public ResponseApproval Approval { get; set; } + public ResponseApproval? Approval { get; set; } - // TODO Exactly one resource must not have a parent and each other resource must have one from this list and the graph must be connected. In other words, the resources must form a tree. + // TODO Exactly one resource must not have a parent and each other resource must have one from + // this list and the graph must be connected. In other words, the resources must form a tree. public virtual ICollection Resources { get; } = new List(); } \ No newline at end of file diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index c7587bdc..e6222cba 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -16,10 +16,8 @@ public GeometricData( Guid creatorId, DateTime createdAt, AppliedMethod appliedMethod, - ICollection approvals, - // ResponseApproval approval, double[] thicknesses - ) : base ( + ) : base( locale, componentId, name, @@ -27,14 +25,12 @@ double[] thicknesses warnings, creatorId, createdAt, - appliedMethod, - approvals - // approval + appliedMethod ) { Thicknesses = thicknesses; - } + public GeometricData( string locale, Guid componentId, @@ -43,7 +39,6 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, - // ResponseApproval approval, double[] thicknesses ) : base( locale, @@ -60,6 +55,6 @@ double[] thicknesses [InverseProperty(nameof(GetHttpsResource.GeometricData))] public override ICollection Resources { get; } = new List(); - public double[] Thicknesses { get; private set;} + public double[] Thicknesses { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 7fe32480..4212b1e0 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -15,9 +15,7 @@ public HygrothermalData( string[] warnings, Guid creatorId, DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : base( locale, componentId, @@ -26,8 +24,7 @@ ICollection approvals warnings, creatorId, createdAt, - appliedMethod, - approvals + appliedMethod ) { } @@ -41,7 +38,6 @@ public HygrothermalData( string[] warnings, Guid creatorId, DateTime createdAt - // ResponseApproval approval ) : base( locale, componentId, diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index ec857a48..4ecca85e 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Collections.Generic; -using Database.Data; namespace Database.Data; @@ -14,9 +13,7 @@ public interface IData : IEntity DateTime CreatedAt { get; } AppliedMethod AppliedMethod { get; } ICollection Approvals { get; } - ICollection Resources { get; } - - // ResponseApproval Approval { get; } + ResponseApproval? Approval { get; } string Locale { get; } } \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index e54ecb36..625dee5e 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -16,8 +16,6 @@ public OpticalData( Guid creatorId, DateTime createdAt, AppliedMethod appliedMethod, - ICollection approvals, - // ResponseApproval approval double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, double[] nearnormalHemisphericalSolarTransmittances, @@ -33,8 +31,7 @@ ICollection cielabColors warnings, creatorId, createdAt, - appliedMethod, - approvals + appliedMethod ) { NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; @@ -55,7 +52,6 @@ public OpticalData( string[] warnings, Guid creatorId, DateTime createdAt, - // ResponseApproval approval double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, double[] nearnormalHemisphericalSolarTransmittances, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index f0e5de9d..537e2e78 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -15,9 +15,7 @@ public PhotovoltaicData( string[] warnings, Guid creatorId, DateTime createdAt, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : base( locale, componentId, @@ -26,8 +24,7 @@ ICollection approvals warnings, creatorId, createdAt, - appliedMethod, - approvals + appliedMethod ) { } @@ -41,7 +38,6 @@ public PhotovoltaicData( string[] warnings, Guid creatorId, DateTime createdAt - // ResponseApproval approval ) : base( locale, componentId, diff --git a/backend/src/GraphQl/Approvals/ApprovalInput.cs b/backend/src/GraphQl/Approvals/ApprovalInput.cs index d95a330b..226d6081 100644 --- a/backend/src/GraphQl/Approvals/ApprovalInput.cs +++ b/backend/src/GraphQl/Approvals/ApprovalInput.cs @@ -6,6 +6,5 @@ public sealed record ApprovalInput ( Guid DataId, Guid CreatorId, - DataApprovalInput Approval, - ResponseApprovalInput ResponseApproval + DataApprovalInput Approval ); \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalMutation.cs b/backend/src/GraphQl/Approvals/ApprovalMutation.cs index 61b4e1e1..bb4dc80f 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutation.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutation.cs @@ -57,7 +57,7 @@ CancellationToken cancellationToken ) ); - var data = await dataService.GetDataAsync(input.DataId, cancellationToken).ConfigureAwait(false); + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); if (data == null) { return new AddApprovalPayload( @@ -76,13 +76,29 @@ CancellationToken cancellationToken input.Approval.Query, input.Approval.Response, currentUser.Id); + if (input.Approval.Publication != null) + { + approval.Publication = new Publication( + input.Approval.Publication.Title, + input.Approval.Publication.Abstract, + input.Approval.Publication.Section, + input.Approval.Publication.Authors, + input.Approval.Publication.Doi, + input.Approval.Publication.ArXiv, + input.Approval.Publication.Urn, + input.Approval.Publication.WebAddress); + } + if (input.Approval.Standard != null) + { + approval.Standard = new Standard( + input.Approval.Standard.Title, + input.Approval.Standard.Abstract, + input.Approval.Standard.Section, + input.Approval.Standard.Year, + input.Approval.Standard.Standardizers, + input.Approval.Standard.Locator); + } data.Approvals.Add(approval); - //data.Approval = new ResponseApproval( - // DateTime.Now, - // input.ResponseApproval.Signature, - // input.ResponseApproval.KeyFingerprint, - // input.ResponseApproval.Query, - // input.ResponseApproval.Response); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new AddApprovalPayload(approval); } diff --git a/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs b/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs deleted file mode 100644 index 8c93161e..00000000 --- a/backend/src/GraphQl/Approvals/ResponseApprovalInput.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Database.GraphQl.Approvals; - -public sealed record ResponseApprovalInput( - DateTime Timestamp, - string Signature, - string KeyFingerprint, - string Query, - string Response, - Guid ApproverId -); \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 75c236e6..9ddd74ac 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -79,41 +79,6 @@ CancellationToken cancellationToken )) .ToList() ), - input.Approvals.Select(a => - new DataApproval( - a.Timestamp, - a.Signature, - a.KeyFingerprint, - a.Query, - a.Response, - a.ApproverId - ) - { - Publication = a.Publication is null - ? null - : new Publication( - a.Publication.Title, - a.Publication.Abstract, - a.Publication.Section, - a.Publication.Authors, - a.Publication.Doi, - a.Publication.ArXiv, - a.Publication.Urn, - a.Publication.WebAddress - ), - Standard = a.Standard is null - ? null - : new Standard( - a.Standard.Title, - a.Standard.Abstract, - a.Standard.Section, - a.Standard.Year, - a.Standard.Standardizers, - a.Standard.Locator - ) - } - ).ToList(), - // approval: input.Approval input.GValues, input.UValues ); diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 736462b9..0128d13d 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.Approvals; +using System; using HotChocolate; using HotChocolate.Types; @@ -16,8 +14,6 @@ public sealed record CreateCalorimetricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval RootGetHttpsResourceInput RootResource, double[] GValues, double[] UValues diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs deleted file mode 100644 index dd17443e..00000000 --- a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Database.Data; -using HotChocolate.Types; - -namespace Database.GraphQl.References; - -public sealed class DataApprovalType - : ObjectType -{ - protected override void Configure(IObjectTypeDescriptor descriptor) - { - descriptor - .Field(t => t.Standard) - .Ignore(); - descriptor - .Field(t => t.Publication) - .Ignore(); - - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 02c00979..495cdbc2 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,14 +1,20 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.Extensions; -using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.CalorimetricDataX; +using Database.GraphQl.HygrothermalDataX; +using Database.GraphQl.OpticalDataX; +using Database.GraphQl.PhotovoltaicDataX; +using Database.GraphQl.GeometricDataX; +using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; @@ -21,23 +27,55 @@ public sealed class DataQueries [UseSorting] public IAsyncEnumerable GetAllData( [GraphQLType] string? locale, - IDataService dataService, + ApplicationDbContext context, ISortingContext sorting ) { sorting.StabilizeOrder(); - // TODO Use `locale`. return context.CalorimetricData.AsNoTracking(); - return dataService.GetAllData(); + // TODO Use `locale`. return context.CalorimetricData.AsNoTracking(); The union + // below does sadly not work because the different kinds of data have different include + // operations. return context.CalorimetricData.AsNoTracking() + // .Union(context.HygrothermalData.AsNoTracking()) + // .Union(context.OpticalData.AsNoTracking()) .Union(context.PhotovoltaicData.AsNoTracking()); + return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() + .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) + .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); } public async Task GetDataAsync( Guid id, [GraphQLType] string? locale, - IDataService dataService, + CalorimetricDataByIdDataLoader calorimetricDataById, + HygrothermalDataByIdDataLoader hygrothermalDataById, + OpticalDataByIdDataLoader opticalDataById, + PhotovoltaicDataByIdDataLoader photovoltaicDataById, + GeometricDataByIdDataLoader geometricDataById, CancellationToken cancellationToken ) { // TODO Use `locale`. - return await dataService.GetDataAsync(id, cancellationToken); + return + await calorimetricDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await hygrothermalDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await opticalDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await geometricDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) ?? + await photovoltaicDataById.LoadAsync( + id, + cancellationToken + ).ConfigureAwait(false) as IData; } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index 4ef2fffd..8bfd24b6 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.Approvals; +using System; using HotChocolate; using HotChocolate.Types; @@ -15,8 +13,6 @@ public sealed record CreateGeometricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval, RootGetHttpsResourceInput RootResource, double[] Thicknesses ); \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 29ff0766..291cc425 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -68,17 +68,6 @@ CancellationToken cancellationToken )) .ToList() ), - input.Approvals.Select(a => - new DataApproval( - a.Timestamp, - a.Signature, - a.KeyFingerprint, - a.Query, - a.Response, - a.ApproverId - ) - ).ToList(), - // approval: input.Approval, input.Thicknesses ); var resource = new GetHttpsResource( diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 21f832cd..e6f239e3 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.Approvals; +using System; using HotChocolate; using HotChocolate.Types; @@ -16,7 +14,5 @@ public sealed record CreateHygrothermalDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval RootGetHttpsResourceInput RootResource ); \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index f4a0c1d1..06d7f90e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -78,42 +78,7 @@ CancellationToken cancellationToken ) )) .ToList() - ), - input.Approvals.Select(a => - new DataApproval( - a.Timestamp, - a.Signature, - a.KeyFingerprint, - a.Query, - a.Response, - a.ApproverId - ) - { - Publication = a.Publication is null - ? null - : new Publication( - a.Publication.Title, - a.Publication.Abstract, - a.Publication.Section, - a.Publication.Authors, - a.Publication.Doi, - a.Publication.ArXiv, - a.Publication.Urn, - a.Publication.WebAddress - ), - Standard = a.Standard is null - ? null - : new Standard( - a.Standard.Title, - a.Standard.Abstract, - a.Standard.Section, - a.Standard.Year, - a.Standard.Standardizers, - a.Standard.Locator - ) - } - ).ToList() - // approval: input.Approval + ) ); var resource = new GetHttpsResource( input.RootResource.Description, diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index d2aa378a..afc38e59 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Collections.Generic; -using Database.GraphQl.Approvals; using HotChocolate; using HotChocolate.Types; @@ -16,8 +15,6 @@ public sealed record CreateOpticalDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval RootGetHttpsResourceInput RootResource, double[] NearnormalHemisphericalVisibleTransmittances, double[] NearnormalHemisphericalVisibleReflectances, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index b852d1b9..d957e2d5 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -79,41 +79,6 @@ CancellationToken cancellationToken )) .ToList() ), - input.Approvals.Select(a => - new DataApproval( - a.Timestamp, - a.Signature, - a.KeyFingerprint, - a.Query, - a.Response, - a.ApproverId - ) - { - Publication = a.Publication is null - ? null - : new Publication( - a.Publication.Title, - a.Publication.Abstract, - a.Publication.Section, - a.Publication.Authors, - a.Publication.Doi, - a.Publication.ArXiv, - a.Publication.Urn, - a.Publication.WebAddress - ), - Standard = a.Standard is null - ? null - : new Standard( - a.Standard.Title, - a.Standard.Abstract, - a.Standard.Section, - a.Standard.Year, - a.Standard.Standardizers, - a.Standard.Locator - ) - } - ).ToList(), - // approval: input.Approval input.NearnormalHemisphericalVisibleTransmittances, input.NearnormalHemisphericalVisibleReflectances, input.NearnormalHemisphericalSolarTransmittances, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index 34a7222f..616f40ee 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.Approvals; +using System; using HotChocolate; using HotChocolate.Types; @@ -16,7 +14,5 @@ public sealed record CreatePhotovoltaicDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - IReadOnlyList Approvals, - // ResponseApproval Approval RootGetHttpsResourceInput RootResource ); \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 857485b3..52bb7c6c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -78,42 +78,7 @@ CancellationToken cancellationToken ) )) .ToList() - ), - input.Approvals.Select(a => - new DataApproval( - a.Timestamp, - a.Signature, - a.KeyFingerprint, - a.Query, - a.Response, - a.ApproverId - ) - { - Publication = a.Publication is null - ? null - : new Publication( - a.Publication.Title, - a.Publication.Abstract, - a.Publication.Section, - a.Publication.Authors, - a.Publication.Doi, - a.Publication.ArXiv, - a.Publication.Urn, - a.Publication.WebAddress - ), - Standard = a.Standard is null - ? null - : new Standard( - a.Standard.Title, - a.Standard.Abstract, - a.Standard.Section, - a.Standard.Year, - a.Standard.Standardizers, - a.Standard.Locator - ) - } - ).ToList() - // approval: input.Approval + ) ); var resource = new GetHttpsResource( input.RootResource.Description, diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 6bf12a57..1c2aeb28 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -1,45 +1,19 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.GeometricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; -using Microsoft.EntityFrameworkCore; namespace Database.Services; -public class DataService( - ApplicationDbContext context, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - GeometricDataByIdDataLoader geometricDataById) : IDataService +public class DataService() : IDataService { - public IAsyncEnumerable GetAllData() + public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { - // The union below does sadly not work because the different kinds of data have different - // include operations. return context.CalorimetricData.AsNoTracking() - // .Union(context.HygrothermalData.AsNoTracking()) - // .Union(context.OpticalData.AsNoTracking()) .Union(context.PhotovoltaicData.AsNoTracking()); - return context.CalorimetricData.AsNoTracking().AsAsyncEnumerable() - .Concat(context.GeometricData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.HygrothermalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.OpticalData.AsNoTracking().AsAsyncEnumerable()) - .Concat(context.PhotovoltaicData.AsNoTracking().AsAsyncEnumerable()); - } - - public async Task GetDataAsync(Guid id, CancellationToken cancellationToken) - { - return await calorimetricDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? - await hygrothermalDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? - await opticalDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? - await geometricDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) ?? - await photovoltaicDataById.LoadAsync(id, cancellationToken).ConfigureAwait(false) as IData; + return context.CalorimetricData.FirstOrDefault(x => x.Id == id) ?? + context.HygrothermalData.FirstOrDefault(x => x.Id == id) ?? + context.OpticalData.FirstOrDefault(x => x.Id == id) ?? + context.GeometricData.FirstOrDefault(x => x.Id == id) ?? + context.PhotovoltaicData.FirstOrDefault(x => x.Id == id) as IData; } } \ No newline at end of file diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/IDataService.cs index 855de133..cd4e9945 100644 --- a/backend/src/Services/IDataService.cs +++ b/backend/src/Services/IDataService.cs @@ -1,14 +1,11 @@ -using Database.Data; -using System.Collections.Generic; -using System.Threading.Tasks; +using System; using System.Threading; -using System; +using System.Threading.Tasks; +using Database.Data; namespace Database.Services; public interface IDataService { - public IAsyncEnumerable GetAllData(); - - public Task GetDataAsync(Guid id, CancellationToken cancellationToken); + public Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken); } \ No newline at end of file From 362184b41d3cd0fbaaffb2c1cfff0570e9ff56d0 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 17 Feb 2025 16:03:31 +0100 Subject: [PATCH 267/878] Removed "Authorize" attribute --- backend/src/GraphQl/Approvals/ApprovalMutation.cs | 3 +-- backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs | 3 +-- backend/src/Metabase/Queries/CurrentUser.graphql | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/src/GraphQl/Approvals/ApprovalMutation.cs b/backend/src/GraphQl/Approvals/ApprovalMutation.cs index bb4dc80f..46777818 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutation.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutation.cs @@ -5,7 +5,6 @@ using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate.Authorization; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -14,7 +13,7 @@ namespace Database.GraphQl.Approvals; [ExtendObjectType(nameof(Mutation))] public sealed class ApprovalMutation { - [Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task AddApprovalToDataAsync( ApprovalInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 291cc425..f42a8496 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using HotChocolate.Authorization; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -15,7 +14,7 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataMutations { // [UseUserManager] - [Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, diff --git a/backend/src/Metabase/Queries/CurrentUser.graphql b/backend/src/Metabase/Queries/CurrentUser.graphql index 69b688da..338de444 100644 --- a/backend/src/Metabase/Queries/CurrentUser.graphql +++ b/backend/src/Metabase/Queries/CurrentUser.graphql @@ -1,11 +1,9 @@ query CurrentUser { currentUser { - id uuid representedInstitutions { edges { node { - id uuid name } From 0c67179f0c95369ef836b0f86011643442141e70 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 17 Feb 2025 16:09:22 +0100 Subject: [PATCH 268/878] Add check for token expiration --- .../HttpContextAccessorExtensions.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 3ca66cd4..29a1f7dc 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; @@ -11,9 +12,18 @@ public static class HttpContextAccessorExtensions public static async Task ExtractBearerToken(this IHttpContextAccessor httpContextAccessor) { if (httpContextAccessor.HttpContext is null) return null; - return await httpContextAccessor.HttpContext.GetTokenAsync( + var token = await httpContextAccessor.HttpContext.GetTokenAsync( CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken - ).ConfigureAwait(false); + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken); + var expirationDate = await httpContextAccessor.HttpContext.GetTokenAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate); + + return !String.IsNullOrEmpty(token) && !String.IsNullOrEmpty(expirationDate) && CheckExpirationDate(expirationDate) ? token : null; + } + + private static bool CheckExpirationDate(string expirationDate) + { + return DateTime.Parse(expirationDate) < DateTime.UtcNow; } } \ No newline at end of file From 11f35ebcd0e34516cdefe4da136acff1060994ef Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 17 Feb 2025 16:19:19 +0100 Subject: [PATCH 269/878] Removed approvel from data objects after merge --- backend/src/Data/CalorimetricData.cs | 4 +--- backend/src/Data/DataX.cs | 4 +--- backend/src/Data/GeometricData.cs | 5 +---- backend/src/Data/HygrothermalData.cs | 8 ++------ backend/src/Data/OpticalData.cs | 4 +--- backend/src/Data/PhotovoltaicData.cs | 8 ++------ 6 files changed, 8 insertions(+), 25 deletions(-) diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index bf945437..3ddf0c56 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -33,8 +33,7 @@ double[] uValues type, subtype, coatedSide, - appliedMethod, - approvals + appliedMethod ) { GValues = gValues; @@ -53,7 +52,6 @@ public CalorimetricData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - // ResponseApproval approval double[] gValues, double[] uValues ) : base( diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index d5d2cf70..faf7c4f1 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -18,9 +18,7 @@ protected DataX( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : this( locale, diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index a892cfd5..4bc17c8c 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -32,9 +32,7 @@ double[] thicknesses type, subtype, coatedSide, - appliedMethod, - approvals - // approval + appliedMethod ) { Thicknesses = thicknesses; @@ -51,7 +49,6 @@ public GeometricData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - // ResponseApproval approval, double[] thicknesses ) : base( locale, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 66ca7019..e21316f2 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -19,9 +19,7 @@ public HygrothermalData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : base( locale, componentId, @@ -33,8 +31,7 @@ ICollection approvals type, subtype, coatedSide, - appliedMethod, - approvals + appliedMethod ) { } @@ -51,7 +48,6 @@ public HygrothermalData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide - // ResponseApproval approval ) : base( locale, componentId, diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index b1b2492a..22a2d604 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -38,8 +38,7 @@ ICollection cielabColors type, subtype, coatedSide, - appliedMethod, - approvals + appliedMethod ) { NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; @@ -63,7 +62,6 @@ public OpticalData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - // ResponseApproval approval double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, double[] nearnormalHemisphericalSolarTransmittances, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 6507ef83..df3677e7 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -19,9 +19,7 @@ public PhotovoltaicData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide, - AppliedMethod appliedMethod, - ICollection approvals - // ResponseApproval approval + AppliedMethod appliedMethod ) : base( locale, componentId, @@ -33,8 +31,7 @@ ICollection approvals type, subtype, coatedSide, - appliedMethod, - approvals + appliedMethod ) { } @@ -51,7 +48,6 @@ public PhotovoltaicData( DataType? type, DataSubtype? subtype, CoatedSide? coatedSide - // ResponseApproval approval ) : base( locale, componentId, From 798cac44ac4d183e7fc6480aa3a740408e79b0c1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Feb 2025 18:15:43 +0100 Subject: [PATCH 270/878] Make `type`, `subtype`, and `coatedSide` fields of optical data only, name their enum types more expressively, and add corresponding propositions (and yes, one should not change migrations like I did here in general) --- backend/src/Data/ApplicationDbContext.cs | 4 +- backend/src/Data/CalorimetricData.cs | 15 +- backend/src/Data/DataX.cs | 20 +-- backend/src/Data/GeometricData.cs | 15 +- backend/src/Data/HygrothermalData.cs | 16 +- backend/src/Data/OpticalData.cs | 25 +-- backend/src/Data/PhotovoltaicData.cs | 16 +- backend/src/Enumerations/DataSubtype.cs | 2 +- .../{DataType.cs => OpticalComponentType.cs} | 2 +- .../CalorimetricDataMutations.cs | 3 - .../CreateCalorimetricDataInput.cs | 3 - .../src/GraphQl/Databases/DatabaseQueries.cs | 5 +- .../CreateGeometricDataInput.cs | 3 - .../GeometricDataX/GeometricDataMutations.cs | 3 - .../CreateHygrothermalDataInput.cs | 3 - .../HygrothermalDataMutations.cs | 3 - .../OpticalDataX/CreateOpticalDataInput.cs | 4 +- .../OpticalDataX/OpticalDataFilterType.cs | 3 + .../CreatePhotovoltaicDataInput.cs | 3 - .../PhotovoltaicDataMutations.cs | 3 - ...TypeSubtypeAndCoatedSideToData.Designer.cs | 50 +----- ...55333_AddTypeSubtypeAndCoatedSideToData.cs | 160 +----------------- .../ApplicationDbContextModelSnapshot.cs | 50 +----- backend/src/Startup.cs | 4 +- frontend/type-defs.graphqls | 82 ++++----- 25 files changed, 100 insertions(+), 397 deletions(-) rename backend/src/Enumerations/{DataType.cs => OpticalComponentType.cs} (80%) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b06285d7..92b0ac85 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -17,8 +17,8 @@ public sealed class ApplicationDbContext internal const string CoatedSideTypeName = "coated_side"; internal const string DataKindTypeName = "data_kind"; - internal const string DataSubtypeTypeName = "data_subtype"; - internal const string DataTypeTypeName = "data_type"; + internal const string OpticalComponentSubtypeTypeName = "optical_component_subtype"; + internal const string OpticalComponentTypeTypeName = "optical_component_type"; internal const string StandardizerTypeName = "standardizer"; public ApplicationDbContext( diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 02981787..ed3f8260 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Database.Enumerations; namespace Database.Data; @@ -16,9 +15,6 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, // ResponseApproval approval @@ -32,9 +28,6 @@ double[] uValues warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod, approvals ) @@ -52,9 +45,6 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, // ResponseApproval approval double[] gValues, double[] uValues @@ -65,10 +55,7 @@ double[] uValues description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { GValues = gValues; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 2278cd8b..b7fd6c1e 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Database.Enumerations; namespace Database.Data; @@ -15,9 +14,6 @@ protected DataX( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -29,10 +25,7 @@ ICollection approvals description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { AppliedMethod = appliedMethod; @@ -48,10 +41,7 @@ protected DataX( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide + DateTime createdAt ) { Locale = locale; @@ -61,9 +51,6 @@ protected DataX( Warnings = warnings; CreatorId = creatorId; CreatedAt = createdAt; - Type = type; - Subtype = subtype; - CoatedSide = coatedSide; } public string Locale { get; private set; } @@ -73,9 +60,6 @@ protected DataX( public string[] Warnings { get; private set; } public Guid CreatorId { get; private set; } public DateTime CreatedAt { get; private set; } - public DataType? Type { get; private set; } - public DataSubtype? Subtype { get; private set; } - public CoatedSide? CoatedSide { get; private set; } public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 44a2b075..c7587bdc 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Database.Enumerations; namespace Database.Data; @@ -16,9 +15,6 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, // ResponseApproval approval, @@ -31,9 +27,6 @@ double[] thicknesses warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod, approvals // approval @@ -50,9 +43,6 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, // ResponseApproval approval, double[] thicknesses ) : base( @@ -62,10 +52,7 @@ double[] thicknesses description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { Thicknesses = thicknesses; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 85fb87b1..419d5167 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -16,9 +16,6 @@ public HygrothermalData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -30,9 +27,6 @@ ICollection approvals warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod, approvals ) @@ -47,10 +41,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide + DateTime createdAt // ResponseApproval approval ) : base( locale, @@ -59,10 +50,7 @@ public HygrothermalData( description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { } diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 5a6d2bcb..6a626021 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -16,8 +16,8 @@ public OpticalData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, + OpticalComponentType? type, + OpticalComponentSubtype? subtype, CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals, @@ -37,13 +37,13 @@ ICollection cielabColors warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod, approvals ) { + Type = type; + Subtype = subtype; + CoatedSide = coatedSide; NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; @@ -62,8 +62,8 @@ public OpticalData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, + OpticalComponentType? type, + OpticalComponentSubtype? subtype, CoatedSide? coatedSide, // ResponseApproval approval double[] nearnormalHemisphericalVisibleTransmittances, @@ -79,12 +79,12 @@ double[] colorRenderingIndices description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { + Type = type; + Subtype = subtype; + CoatedSide = coatedSide; NearnormalHemisphericalVisibleTransmittances = nearnormalHemisphericalVisibleTransmittances; NearnormalHemisphericalVisibleReflectances = nearnormalHemisphericalVisibleReflectances; NearnormalHemisphericalSolarTransmittances = nearnormalHemisphericalSolarTransmittances; @@ -96,6 +96,9 @@ double[] colorRenderingIndices [InverseProperty(nameof(GetHttpsResource.OpticalData))] public override ICollection Resources { get; } = new List(); + public OpticalComponentType? Type { get; private set; } + public OpticalComponentSubtype? Subtype { get; private set; } + public CoatedSide? CoatedSide { get; private set; } public double[] NearnormalHemisphericalVisibleTransmittances { get; private set; } public double[] NearnormalHemisphericalVisibleReflectances { get; private set; } public double[] NearnormalHemisphericalSolarTransmittances { get; private set; } diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index a0f8048b..cba5dec9 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -16,9 +16,6 @@ public PhotovoltaicData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod, ICollection approvals // ResponseApproval approval @@ -30,9 +27,6 @@ ICollection approvals warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod, approvals ) @@ -47,10 +41,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide + DateTime createdAt // ResponseApproval approval ) : base( locale, @@ -59,10 +50,7 @@ public PhotovoltaicData( description, warnings, creatorId, - createdAt, - type, - subtype, - coatedSide + createdAt ) { } diff --git a/backend/src/Enumerations/DataSubtype.cs b/backend/src/Enumerations/DataSubtype.cs index e33b085d..f0751192 100644 --- a/backend/src/Enumerations/DataSubtype.cs +++ b/backend/src/Enumerations/DataSubtype.cs @@ -3,7 +3,7 @@ namespace Database.Enumerations; [SuppressMessage("Naming", "CA1707")] -public enum DataSubtype +public enum OpticalComponentSubtype { MONOLITHIC, LAMINATE, diff --git a/backend/src/Enumerations/DataType.cs b/backend/src/Enumerations/OpticalComponentType.cs similarity index 80% rename from backend/src/Enumerations/DataType.cs rename to backend/src/Enumerations/OpticalComponentType.cs index d91f17cc..0f43fd03 100644 --- a/backend/src/Enumerations/DataType.cs +++ b/backend/src/Enumerations/OpticalComponentType.cs @@ -3,7 +3,7 @@ namespace Database.Enumerations; [SuppressMessage("Naming", "CA1707")] -public enum DataType +public enum OpticalComponentType { GLAZING, SHADING diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 0d366752..75c236e6 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -51,9 +51,6 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index 3974cb8e..cc189780 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -15,9 +15,6 @@ public sealed record CreateCalorimetricDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - DataType? Type, - DataSubtype? Subtype, - CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index cda86c41..0df23c68 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -45,7 +45,10 @@ await QueryingMetabase.ConstructGraphQlQuery( locator = new { // TODO This is error-prone. - eq = new Uri(new Uri(appSettings.Host), "/graphql/") + absoluteUri = new + { + equalTo = new Uri(new Uri(appSettings.Host), "/graphql/") + } } } }, diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index 06386cf8..a072ec1d 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -14,9 +14,6 @@ public sealed record CreateGeometricDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - DataType? Type, - DataSubtype? Subtype, - CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index ccf1eb38..5661dd83 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -51,9 +51,6 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index b31ebff9..a8ea3503 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -15,9 +15,6 @@ public sealed record CreateHygrothermalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - DataType? Type, - DataSubtype? Subtype, - CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 09822c69..f4a0c1d1 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -51,9 +51,6 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index 8e1c8466..00c29006 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -15,8 +15,8 @@ public sealed record CreateOpticalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - DataType? Type, - DataSubtype? Subtype, + OpticalComponentType? Type, + OpticalComponentSubtype? Subtype, CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 8d728532..41644c6c 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -13,6 +13,9 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Field(x => x.Id); + descriptor.Field(x => x.Type); + descriptor.Field(x => x.Subtype); + descriptor.Field(x => x.CoatedSide); descriptor.Field(x => x.NearnormalHemisphericalSolarReflectances); descriptor.Field(x => x.NearnormalHemisphericalSolarTransmittances); descriptor.Field(x => x.NearnormalHemisphericalVisibleReflectances); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index 2139d66b..b3c1ca6b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -15,9 +15,6 @@ public sealed record CreatePhotovoltaicDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - DataType? Type, - DataSubtype? Subtype, - CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, IReadOnlyList Approvals, // ResponseApproval Approval diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index aaa174ef..857485b3 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -51,9 +51,6 @@ CancellationToken cancellationToken input.Warnings, input.CreatorId, input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, new AppliedMethod( input.AppliedMethod.MethodId, input.AppliedMethod.Arguments diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs index 1cd26678..49b415e5 100644 --- a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs @@ -28,8 +28,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -41,9 +41,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -67,12 +64,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.PrimitiveCollection("UValues") .IsRequired() .HasColumnType("double precision[]"); @@ -99,9 +90,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -121,16 +109,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -212,9 +194,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -234,12 +213,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -308,11 +281,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); - b.Property("Type") - .HasColumnType("database.data_type"); + b.Property("Type") + .HasColumnType("database.optical_component_type"); b.Property("Version") .IsConcurrencyToken() @@ -336,9 +309,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -358,12 +328,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -2138,4 +2102,4 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs index 0bfaaef6..5ca77b2b 100644 --- a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs @@ -13,29 +13,8 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") - .Annotation("Npgsql:Enum:database.data_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") - .Annotation("Npgsql:Enum:database.data_type", "glazing,shading"); - - migrationBuilder.AddColumn( - name: "CoatedSide", - schema: "database", - table: "photovoltaic_data", - type: "database.coated_side", - nullable: true); - - migrationBuilder.AddColumn( - name: "Subtype", - schema: "database", - table: "photovoltaic_data", - type: "database.data_subtype", - nullable: true); - - migrationBuilder.AddColumn( - name: "Type", - schema: "database", - table: "photovoltaic_data", - type: "database.data_type", - nullable: true); + .Annotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .Annotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); migrationBuilder.AddColumn( name: "CoatedSide", @@ -44,102 +23,24 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "database.coated_side", nullable: true); - migrationBuilder.AddColumn( + migrationBuilder.AddColumn( name: "Subtype", schema: "database", table: "optical_data", - type: "database.data_subtype", + type: "database.optical_component_subtype", nullable: true); - migrationBuilder.AddColumn( + migrationBuilder.AddColumn( name: "Type", schema: "database", table: "optical_data", - type: "database.data_type", - nullable: true); - - migrationBuilder.AddColumn( - name: "CoatedSide", - schema: "database", - table: "hygrothermal_data", - type: "database.coated_side", - nullable: true); - - migrationBuilder.AddColumn( - name: "Subtype", - schema: "database", - table: "hygrothermal_data", - type: "database.data_subtype", - nullable: true); - - migrationBuilder.AddColumn( - name: "Type", - schema: "database", - table: "hygrothermal_data", - type: "database.data_type", - nullable: true); - - migrationBuilder.AddColumn( - name: "CoatedSide", - schema: "database", - table: "geometric_data", - type: "database.coated_side", - nullable: true); - - migrationBuilder.AddColumn( - name: "Subtype", - schema: "database", - table: "geometric_data", - type: "database.data_subtype", - nullable: true); - - migrationBuilder.AddColumn( - name: "Type", - schema: "database", - table: "geometric_data", - type: "database.data_type", - nullable: true); - - migrationBuilder.AddColumn( - name: "CoatedSide", - schema: "database", - table: "calorimetric_data", - type: "database.coated_side", - nullable: true); - - migrationBuilder.AddColumn( - name: "Subtype", - schema: "database", - table: "calorimetric_data", - type: "database.data_subtype", - nullable: true); - - migrationBuilder.AddColumn( - name: "Type", - schema: "database", - table: "calorimetric_data", - type: "database.data_type", + type: "database.optical_component_type", nullable: true); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.DropColumn( - name: "CoatedSide", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Subtype", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Type", - schema: "database", - table: "photovoltaic_data"); - migrationBuilder.DropColumn( name: "CoatedSide", schema: "database", @@ -155,55 +56,10 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "optical_data"); - migrationBuilder.DropColumn( - name: "CoatedSide", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Subtype", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Type", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "CoatedSide", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Subtype", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Type", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "CoatedSide", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Subtype", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Type", - schema: "database", - table: "calorimetric_data"); - migrationBuilder.AlterDatabase() .OldAnnotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") - .OldAnnotation("Npgsql:Enum:database.data_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") - .OldAnnotation("Npgsql:Enum:database.data_type", "glazing,shading"); + .OldAnnotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .OldAnnotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); } } } \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 4f2d1c84..9f5691a8 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -25,8 +25,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -38,9 +38,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -64,12 +61,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.PrimitiveCollection("UValues") .IsRequired() .HasColumnType("double precision[]"); @@ -96,9 +87,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -118,16 +106,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -209,9 +191,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -231,12 +210,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -305,11 +278,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); - b.Property("Type") - .HasColumnType("database.data_type"); + b.Property("Type") + .HasColumnType("database.optical_component_type"); b.Property("Version") .IsConcurrencyToken() @@ -333,9 +306,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("CoatedSide") - .HasColumnType("database.coated_side"); - b.Property("ComponentId") .HasColumnType("uuid"); @@ -355,12 +325,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); - b.Property("Subtype") - .HasColumnType("database.data_subtype"); - - b.Property("Type") - .HasColumnType("database.data_type"); - b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -2135,4 +2099,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 441c6302..e2a67933 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -166,8 +166,8 @@ AppSettings appSettings .SetPostgresVersion(13, 13) .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.DataSubtypeTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.DataTypeTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) // .UseNodaTime(); ) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 9361818b..7769d598 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -44,7 +44,6 @@ type AppliedMethod { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -56,9 +55,7 @@ type CalorimetricData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - subtype: DataSubtype timestamp: DateTime! - type: DataType uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -207,7 +204,6 @@ type FileMetaInformation { type GeometricData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -218,10 +214,8 @@ type GeometricData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - subtype: DataSubtype thicknesses: [Float!]! timestamp: DateTime! - type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -296,7 +290,6 @@ type GetHttpsResourceTreeRoot { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -307,9 +300,7 @@ type HygrothermalData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - subtype: DataSubtype timestamp: DateTime! - type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -384,9 +375,9 @@ type OpticalData implements Node & Data { nearnormalHemisphericalVisibleTransmittances: [Float!]! resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - subtype: DataSubtype + subtype: OpticalComponentSubtype timestamp: DateTime! - type: DataType + type: OpticalComponentType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -424,7 +415,6 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! approvals: [DataApproval!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -435,9 +425,7 @@ type PhotovoltaicData implements Node & Data { name: String resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! - subtype: DataSubtype timestamp: DateTime! - type: DataType uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -477,18 +465,18 @@ type Publication implements Reference { } type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 2147483646, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 2147483646, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") @@ -618,7 +606,6 @@ input CielabColorPropositionInput { input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -627,8 +614,6 @@ input CreateCalorimetricDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! - subtype: DataSubtype - type: DataType uValues: [Float!]! warnings: [String!]! } @@ -636,7 +621,6 @@ input CreateCalorimetricDataInput { input CreateGeometricDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -644,9 +628,7 @@ input CreateGeometricDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! - subtype: DataSubtype thicknesses: [Float!]! - type: DataType warnings: [String!]! } @@ -664,7 +646,6 @@ input CreateGetHttpsResourceInput { input CreateHygrothermalDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -672,8 +653,6 @@ input CreateHygrothermalDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! - subtype: DataSubtype - type: DataType warnings: [String!]! } @@ -701,15 +680,14 @@ input CreateOpticalDataInput { nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! rootResource: RootGetHttpsResourceInput! - subtype: DataSubtype - type: DataType + subtype: OpticalComponentSubtype + type: OpticalComponentType warnings: [String!]! } input CreatePhotovoltaicDataInput { appliedMethod: AppliedMethodInput! approvals: [DataApprovalInput!]! - coatedSide: CoatedSide componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -717,8 +695,6 @@ input CreatePhotovoltaicDataInput { locale: String! name: String rootResource: RootGetHttpsResourceInput! - subtype: DataSubtype - type: DataType warnings: [String!]! } @@ -1038,6 +1014,13 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NullableOfCoatedSidePropositionInput { + equalTo: CoatedSide @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") + in: [CoatedSide] @cost(weight: "10") + notIn: [CoatedSide] @cost(weight: "10") +} + input NullableOfInt32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -1053,6 +1036,20 @@ input NullableOfInt32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } +input NullableOfOpticalComponentSubtypePropositionInput { + equalTo: OpticalComponentSubtype @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") + in: [OpticalComponentSubtype] @cost(weight: "10") + notIn: [OpticalComponentSubtype] @cost(weight: "10") +} + +input NullableOfOpticalComponentTypePropositionInput { + equalTo: OpticalComponentType @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") + in: [OpticalComponentType] @cost(weight: "10") + notIn: [OpticalComponentType] @cost(weight: "10") +} + input NumerationPropositionInput { and: [NumerationPropositionInput!] or: [NumerationPropositionInput!] @@ -1075,6 +1072,9 @@ input OpticalDataPropositionInput { resources: ListGetHttpsResourceFilterTypePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput + type: NullableOfOpticalComponentTypePropositionInput + subtype: NullableOfOpticalComponentSubtypePropositionInput + coatedSide: NullableOfCoatedSidePropositionInput nearnormalHemisphericalSolarReflectances: FloatsPropositionInput nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput @@ -1323,7 +1323,12 @@ enum DataKind { PHOTOVOLTAIC_DATA } -enum DataSubtype { +enum DatabaseVerificationState { + PENDING + VERIFIED +} + +enum OpticalComponentSubtype { MONOLITHIC LAMINATE INTERLAYER @@ -1348,16 +1353,11 @@ enum DataSubtype { CHROMOGENIC } -enum DataType { +enum OpticalComponentType { GLAZING SHADING } -enum DatabaseVerificationState { - PENDING - VERIFIED -} - enum SortEnumType { ASC DESC From 4d12f8b4df215776daced229fed5c0c7f378da02 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 18 Feb 2025 18:36:00 +0100 Subject: [PATCH 271/878] Make description optional to match the official specification --- backend/src/Data/GetHttpsResource.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index cda1c88d..6bf376bf 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -10,7 +10,7 @@ public sealed class GetHttpsResource : Entity { public GetHttpsResource( - string description, + string? description, string hashValue, Guid dataFormatId, Guid? calorimetricDataId, @@ -39,7 +39,7 @@ public GetHttpsResource( } public GetHttpsResource( - string description, + string? description, string hashValue, Guid dataFormatId, Guid? parentId, @@ -59,7 +59,7 @@ public GetHttpsResource( // `DbContext` needs this constructor without owned entities. public GetHttpsResource( - string description, + string? description, string hashValue, Guid dataFormatId, Guid? calorimetricDataId, @@ -84,7 +84,7 @@ public GetHttpsResource( } public GetHttpsResource( - string description, + string? description, string hashValue, Guid dataFormatId, Guid? parentId @@ -96,7 +96,7 @@ public GetHttpsResource( ParentId = parentId; } - public string Description { get; private set; } + public string? Description { get; private set; } public string HashValue { get; private set; } public Guid DataFormatId { get; private set; } From 095db17a5c381928663a70f9655f9521c5d5e032 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 18 Feb 2025 18:59:34 +0100 Subject: [PATCH 272/878] Remove unused import --- frontend/pages/upload-file.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/pages/upload-file.tsx b/frontend/pages/upload-file.tsx index b1c6d334..9928a0a3 100644 --- a/frontend/pages/upload-file.tsx +++ b/frontend/pages/upload-file.tsx @@ -1,7 +1,7 @@ import { Form, Input, Button, Row, Col, Card, Typography, Upload } from "antd"; import { UploadOutlined } from "@ant-design/icons"; import Layout from "../components/Layout"; -import { RcFile, UploadFile } from "antd/lib/upload/interface"; +import { RcFile } from "antd/lib/upload/interface"; const layout = { labelCol: { span: 8 }, From 814ea4506e431b394d1c8ca15ed505b6077e481d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 18 Feb 2025 19:32:45 +0100 Subject: [PATCH 273/878] Prepare release --- ...172859_MakeDescriptionOptional.Designer.cs | 2104 +++++++++++++++++ .../20250218172859_MakeDescriptionOptional.cs | 38 + backend/src/Migrations/current | 2 +- ...20250218172859_MakeDescriptionOptional.sql | 49 + backend/src/Migrations/previous | 2 +- ...20250218172859_MakeDescriptionOptional.sql | 30 + 6 files changed, 2223 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs create mode 100644 backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs create mode 100644 backend/src/Migrations/migrate_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql create mode 100644 backend/src/Migrations/rollback_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs new file mode 100644 index 00000000..c6d02297 --- /dev/null +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs @@ -0,0 +1,2104 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250218172859_MakeDescriptionOptional")] + partial class MakeDescriptionOptional + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs new file mode 100644 index 00000000..2d14e53e --- /dev/null +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MakeDescriptionOptional : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + } +} diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index 3a8fbc5b..a7b7a40d 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20240917073732_AddGeometricData +20250218172859_MakeDescriptionOptional diff --git a/backend/src/Migrations/migrate_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql b/backend/src/Migrations/migrate_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql new file mode 100644 index 00000000..7faebecd --- /dev/null +++ b/backend/src/Migrations/migrate_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql @@ -0,0 +1,49 @@ +START TRANSACTION; +ALTER TABLE database."geometric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind; + +ALTER TABLE database."geometric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[]; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250129163501_UpgradeToNet9', '9.0.1'); + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE TYPE database.optical_component_subtype AS ENUM ('acid_etched_glass', 'applied_film', 'cellular_shade', 'chromogenic', 'coated', 'coating', 'diffusing_shade', 'embedded_coating', 'film', 'fritted_glass', 'interlayer', 'laminate', 'monolithic', 'perforated_screen', 'pleated_shade', 'roller_shade', 'roman_shade', 'sandblasted_glass', 'shade_material', 'venetian_blind', 'vertical_louver', 'woven_shade'); +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $EF$; + +CREATE TYPE database.optical_component_type AS ENUM ('glazing', 'shading'); + +ALTER TABLE database.optical_data ADD "CoatedSide" database.coated_side; + +ALTER TABLE database.optical_data ADD "Subtype" database.optical_component_subtype; + +ALTER TABLE database.optical_data ADD "Type" database.optical_component_type; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250213155333_AddTypeSubtypeAndCoatedSideToData', '9.0.1'); + +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" DROP NOT NULL; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250218172859_MakeDescriptionOptional', '9.0.1'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index b0f8604f..3a8fbc5b 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20240722133619_MoveEnumTypesToMetabaseSchema +20240917073732_AddGeometricData diff --git a/backend/src/Migrations/rollback_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql b/backend/src/Migrations/rollback_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql new file mode 100644 index 00000000..8bdefaee --- /dev/null +++ b/backend/src/Migrations/rollback_from_20240917073732_AddGeometricData_to_20250218172859_MakeDescriptionOptional.sql @@ -0,0 +1,30 @@ +START TRANSACTION; +UPDATE database.get_https_resource SET "Description" = '' WHERE "Description" IS NULL; +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET NOT NULL; +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET DEFAULT ''; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250218172859_MakeDescriptionOptional'; + +ALTER TABLE database.optical_data DROP COLUMN "CoatedSide"; + +ALTER TABLE database.optical_data DROP COLUMN "Subtype"; + +ALTER TABLE database.optical_data DROP COLUMN "Type"; + +DROP TYPE database.coated_side; +DROP TYPE database.optical_component_subtype; +DROP TYPE database.optical_component_type; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250213155333_AddTypeSubtypeAndCoatedSideToData'; + +ALTER TABLE database."geometric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE data_kind; + +ALTER TABLE database."geometric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE standardizer[]; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250129163501_UpgradeToNet9'; + +COMMIT; + From 8eb09e6541bc1748bfbed48d89643706a59d97a5 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 26 Feb 2025 14:50:50 +0100 Subject: [PATCH 274/878] Address review findings --- backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs | 4 +++- .../{ApprovalMutation.cs => ApprovalMutations.cs} | 8 ++++---- backend/src/Metabase/CurrentUser.cs | 8 ++++---- backend/src/Metabase/QueryingCurrentUser.cs | 4 ++-- backend/src/Services/DataService.cs | 11 ++++++----- backend/src/Services/UserService.cs | 6 +++--- 6 files changed, 22 insertions(+), 19 deletions(-) rename backend/src/GraphQl/Approvals/{ApprovalMutation.cs => ApprovalMutations.cs} (94%) diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs index 79cdfccd..8a82e333 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs @@ -3,5 +3,7 @@ public enum AddApprovalErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA } \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalMutation.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs similarity index 94% rename from backend/src/GraphQl/Approvals/ApprovalMutation.cs rename to backend/src/GraphQl/Approvals/ApprovalMutations.cs index 46777818..d5fcc223 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutation.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -11,7 +11,7 @@ namespace Database.GraphQl.Approvals; [ExtendObjectType(nameof(Mutation))] -public sealed class ApprovalMutation +public sealed class ApprovalMutations { //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task AddApprovalToDataAsync( @@ -32,8 +32,8 @@ CancellationToken cancellationToken { return new AddApprovalPayload( new AddApprovalError( - AddApprovalErrorCode.UNKNOWN, - $"The current user is not known.", + AddApprovalErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", [] ) ); @@ -61,7 +61,7 @@ CancellationToken cancellationToken { return new AddApprovalPayload( new AddApprovalError( - AddApprovalErrorCode.UNKNOWN, + AddApprovalErrorCode.UNKNOWN_DATA, $"Unknown data.", [] ) diff --git a/backend/src/Metabase/CurrentUser.cs b/backend/src/Metabase/CurrentUser.cs index b12df8cf..ca3916be 100644 --- a/backend/src/Metabase/CurrentUser.cs +++ b/backend/src/Metabase/CurrentUser.cs @@ -16,24 +16,24 @@ public enum DataSigningPermission REMOVED } -public record CurrentUser( +public sealed record CurrentUser( Guid Id, Guid Uuid, RepresentedInstitutions RepresentedInstitutions ); -public record Edge( +public sealed record Edge( Node Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission ); -public record Node( +public sealed record Node( Guid Id, Guid Uuid, string Name ); -public record RepresentedInstitutions( +public sealed record RepresentedInstitutions( IReadOnlyList Edges ); \ No newline at end of file diff --git a/backend/src/Metabase/QueryingCurrentUser.cs b/backend/src/Metabase/QueryingCurrentUser.cs index 80a6f82c..58f61391 100644 --- a/backend/src/Metabase/QueryingCurrentUser.cs +++ b/backend/src/Metabase/QueryingCurrentUser.cs @@ -13,7 +13,7 @@ public class QueryingCurrentUser "CurrentUser.graphql" }; - private sealed record CurrentUserResponse(CurrentUser CurrentUser); + private sealed record CurrentUserData(CurrentUser CurrentUser); public static async Task Query( AppSettings appSettings, @@ -21,7 +21,7 @@ private sealed record CurrentUserResponse(CurrentUser CurrentUser); IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return (await QueryingMetabase.QueryGraphQl( + return (await QueryingMetabase.QueryGraphQl( appSettings, new GraphQLRequest( await QueryingMetabase.ConstructGraphQlQuery( diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 1c2aeb28..663a2451 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; +using Microsoft.EntityFrameworkCore; namespace Database.Services; @@ -10,10 +11,10 @@ public class DataService() : IDataService { public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { - return context.CalorimetricData.FirstOrDefault(x => x.Id == id) ?? - context.HygrothermalData.FirstOrDefault(x => x.Id == id) ?? - context.OpticalData.FirstOrDefault(x => x.Id == id) ?? - context.GeometricData.FirstOrDefault(x => x.Id == id) ?? - context.PhotovoltaicData.FirstOrDefault(x => x.Id == id) as IData; + return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? + await context.HygrothermalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? + await context.OpticalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? + await context.GeometricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? + await context.PhotovoltaicData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) as IData; } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 126be909..9bc5ca00 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -35,12 +35,12 @@ public class UserService( return cacheUser; } - private async Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + private Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return await QueryingCurrentUser.Query( + return QueryingCurrentUser.Query( appSettings, httpClientFactory, httpContextAccessor, - cancellationToken).ConfigureAwait(false); + cancellationToken); } } \ No newline at end of file From cc20412b43ded42d77680042141a4a2790374046 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Thu, 27 Feb 2025 11:19:07 +0100 Subject: [PATCH 275/878] Review findings --- .../CalorimetricDataAuthorization.cs | 9 ++-- .../src/Authorization/CommonAuthorization.cs | 21 ++------ .../GeometricDataAuthorization.cs | 9 ++-- .../GetHttpsResourceAuthorization.cs | 9 ++-- .../HygrothermalDataAuthorization.cs | 9 ++-- .../Authorization/OpticalDataAuthorization.cs | 9 ++-- .../PhotovoltaicDataAuthorization.cs | 9 ++-- .../src/Configuration/GraphQlConfiguration.cs | 2 +- .../CalorimetricDataMutations.cs | 32 ++++++++--- .../CreateCalorimetricDataErrorCode.cs | 3 +- .../CreateGeometricDataErrorCode.cs | 3 +- .../GeometricDataX/GeometricDataMutations.cs | 49 +++++++++++------ .../CreateGetHttpsResourceErrorCode.cs | 3 +- .../GetHttpsResourceMutations.cs | 32 ++++++++--- .../CreateHygrothermalDataErrorCode.cs | 3 +- .../HygrothermalDataMutations.cs | 31 ++++++++--- .../CreateOpticalDataErrorCode.cs | 3 +- .../OpticalDataX/OpticalDataMutations.cs | 31 ++++++++--- .../CreatePhotovoltaicDataErrorCode.cs | 3 +- .../PhotovoltaicDataMutations.cs | 31 ++++++++--- ...presentedInstitutionsByCurrentUser.graphql | 12 ----- backend/src/Metabase/QueryingMetabase.cs | 3 -- ...ingRepresentedInstitutionsByCurrentUser.cs | 53 ------------------- 23 files changed, 202 insertions(+), 167 deletions(-) delete mode 100644 backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql delete mode 100644 backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index 1a879626..da8fec9d 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class CalorimetricDataAuthorization { - public static Task IsAuthorizedToCreateCalorimetricDataForInstitution( + public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreateCalorimetricDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index cc50de94..737c2ab9 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,8 +1,7 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; using Database.Metabase; using Microsoft.AspNetCore.Http; @@ -10,7 +9,8 @@ namespace Database.Authorization; public static class CommonAuthorization { - public static async Task IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + public static bool IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -18,20 +18,7 @@ public static async Task IsCurrentUserAtLeastAssistantOfVerifiedInstitutio CancellationToken cancellationToken ) { - return (await QueryingRepresentedInstitutionsByCurrentUser.Query( - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false)) - .Any( - t => - t.Id == institutionId - && ( - t.Role == InstitutionRepresentativeRole.ASSISTANT - || t.Role == InstitutionRepresentativeRole.OWNER - ) - ); + return currentUser.RepresentedInstitutions.Edges.Any(t => t.Node.Uuid == institutionId && (t.Role == InstitutionRepresentativeRole.ASSISTANT || t.Role == InstitutionRepresentativeRole.OWNER)); } public static bool IsAuthorizedToAddApprovalForInstitution( diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index c38d0c3c..063b4067 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class GeometricDataAuthorization { - public static Task IsAuthorizedToCreateGeometricDataForInstitution( + public static bool IsAuthorizedToCreateGeometricDataForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreateGeometricDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index 958a0469..a457be81 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class GetHttpsResourceAuthorization { - public static Task IsAuthorizedToCreateGetHttpsResourceForInstitution( + public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreateGetHttpsResourceForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index 9288718b..d5372a56 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class HygrothermalDataAuthorization { - public static Task IsAuthorizedToCreateHygrothermalDataForInstitution( + public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreateHygrothermalDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index 20cbdf7f..b4df1b3f 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class OpticalDataAuthorization { - public static Task IsAuthorizedToCreateOpticalDataForInstitution( + public static bool IsAuthorizedToCreateOpticalDataForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreateOpticalDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 7d84d60e..9a0e6737 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class PhotovoltaicDataAuthorization { - public static Task IsAuthorizedToCreatePhotovoltaicDataForInstitution( + public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToCreatePhotovoltaicDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b0dacd2e..286e4694 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -148,7 +148,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() - .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index ff1bf5bd..652a5854 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -7,6 +7,8 @@ using Database.Authorization; using Database.Data; using Database.Extensions; +using Database.GraphQl.Approvals; +using Database.Services; using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -23,18 +25,34 @@ public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (!await CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreateCalorimetricDataPayload( + new CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + if (!CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( + currentUser, + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) ) return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs index e457400e..7bb980e9 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs @@ -3,5 +3,6 @@ namespace Database.GraphQl.CalorimetricDataX; public enum CreateCalorimetricDataErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs index fb4e0deb..a68904b1 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -3,5 +3,6 @@ namespace Database.GraphQl.GeometricDataX; public enum CreateGeometricDataErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 75d969d2..5283950a 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -3,8 +3,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Authorization; using Database.Data; +using Database.Services; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -19,26 +19,43 @@ public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (!await GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) - ) - return new CreateGeometricDataPayload( - new CreateGeometricDataError( - CreateGeometricDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create geometric data for the institution.", - [] - ) - ); + //var currentUser = await userService.GetCurrentUser( + // httpContextAccessor, + // cancellationToken).ConfigureAwait(false); + //if (currentUser == null) + //{ + // return new CreateGeometricDataPayload( + // new CreateGeometricDataError( + // CreateGeometricDataErrorCode.UNAUTHENTICATED, + // $"The user is not authenticated.", + // [] + // ) + // ); + //} + + //if (!GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( + // currentUser, + // input.CreatorId, + // appSettings, + // httpClientFactory, + // httpContextAccessor, + // cancellationToken + // ) + //) + // return new CreateGeometricDataPayload( + // new CreateGeometricDataError( + // CreateGeometricDataErrorCode.UNAUTHORIZED, + // $"The current user is not authorized to create geometric data for the institution.", + // [] + // ) + // ); + var geometricData = new GeometricData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs index e3333e23..96f48acd 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs @@ -7,5 +7,6 @@ public enum CreateGetHttpsResourceErrorCode { UNKNOWN, UNKNOWN_DATA, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED, } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 4102c07d..6c6f8600 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -12,6 +12,8 @@ using HotChocolate.Types; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; +using Database.GraphQl.Approvals; +using Database.Services; namespace Database.GraphQl.GetHttpsResources; @@ -24,6 +26,7 @@ public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken @@ -48,13 +51,28 @@ CancellationToken cancellationToken ) ); } - if (!await GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( - data.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreateGetHttpsResourcePayload( + new CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( + currentUser, + data.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) ) return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs index afbbdb2d..24ff8e3a 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs @@ -3,5 +3,6 @@ namespace Database.GraphQl.HygrothermalDataX; public enum CreateHygrothermalDataErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 47687ba2..738441f3 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -7,6 +7,8 @@ using Database.Authorization; using Database.Data; using Database.Extensions; +using Database.GraphQl.Approvals; +using Database.Services; using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -23,18 +25,33 @@ public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (!await HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreateHygrothermalDataPayload( + new CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( + currentUser, + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) ) return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs index 6dd5b4ed..99bdc110 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs @@ -3,5 +3,6 @@ namespace Database.GraphQl.OpticalDataX; public enum CreateOpticalDataErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index b7c21b05..df15b261 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -7,6 +7,8 @@ using Database.Authorization; using Database.Data; using Database.Extensions; +using Database.GraphQl.Approvals; +using Database.Services; using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -23,18 +25,33 @@ public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (!await OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreateOpticalDataPayload( + new CreateOpticalDataError( + CreateOpticalDataErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( + currentUser, + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) ) return new CreateOpticalDataPayload( new CreateOpticalDataError( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs index 9a24fac3..d23a28d6 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs @@ -3,5 +3,6 @@ namespace Database.GraphQl.PhotovoltaicDataX; public enum CreatePhotovoltaicDataErrorCode { UNKNOWN, - UNAUTHORIZED + UNAUTHORIZED, + UNAUTHENTICATED } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 96410f14..42027587 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -7,6 +7,8 @@ using Database.Authorization; using Database.Data; using Database.Extensions; +using Database.GraphQl.Approvals; +using Database.Services; using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -23,18 +25,33 @@ public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, AppSettings appSettings, + IUserService userService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - if (!await PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( - input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreatePhotovoltaicDataPayload( + new CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( + currentUser, + input.CreatorId, + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken + ) ) return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( diff --git a/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql b/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql deleted file mode 100644 index 43fd32a4..00000000 --- a/backend/src/Metabase/Queries/RepresentedInstitutionsByCurrentUser.graphql +++ /dev/null @@ -1,12 +0,0 @@ -query RepresentedInstitutionsByCurrentUser { - currentUser { - representedInstitutions { - edges { - node { - uuid - } - role - } - } - } -} \ No newline at end of file diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/Metabase/QueryingMetabase.cs index 23298ef5..94f049a2 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/Metabase/QueryingMetabase.cs @@ -11,10 +11,7 @@ using Database.Extensions; using GraphQL; using GraphQL.Client.Serializer.SystemTextJson; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; -using OpenIddict.Client.AspNetCore; namespace Database.Metabase; diff --git a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs b/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs deleted file mode 100644 index 944e79fb..00000000 --- a/backend/src/Metabase/QueryingRepresentedInstitutionsByCurrentUser.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; -using Microsoft.AspNetCore.Http; - -namespace Database.Metabase; - -public sealed class QueryingRepresentedInstitutionsByCurrentUser -{ - private static readonly string[] _representedInstitutionsByCurrentUserFileNames = - { - "RepresentedInstitutionsByCurrentUser.graphql" - }; - - private sealed record Institution(Guid Uuid); - private sealed record UserRepresentedInstitutionEdge(Institution Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission); - private sealed record UserRepresentedInstitutionConnection(IReadOnlyList Edges); - private sealed record User(UserRepresentedInstitutionConnection RepresentedInstitutions); - private sealed record RepresentedInstitutionsByCurrentUser(User CurrentUser); - - internal static async Task> Query( - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - return (await QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQLRequest( - await QueryingMetabase.ConstructGraphQlQuery( - _representedInstitutionsByCurrentUserFileNames - ).ConfigureAwait(false), - new {}, - "RepresentedInstitutionsByCurrentUser" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false)) - ?.Data - ?.CurrentUser - ?.RepresentedInstitutions - ?.Edges - ?.Select(edge => (edge.Node.Uuid, edge.Role, edge.DataSigningPermission)) - .ToList().AsReadOnly() - ?? Array.Empty<(Guid Id, InstitutionRepresentativeRole Role, DataSigningPermission Permission)>().AsReadOnly(); - } -} \ No newline at end of file From 8d149ef0cf76accb1fe0fae9bf6a84f9dc80cacd Mon Sep 17 00:00:00 2001 From: jbinterap Date: Thu, 27 Feb 2025 11:22:03 +0100 Subject: [PATCH 276/878] First implementation of data signing --- .env.production.sample | 2 + .env.sample | 2 + backend/Dockerfile | 26 +++- backend/src/AppSettings.cs | 2 + .../GeometricDataX/GeometricDataMutations.cs | 2 + backend/src/Program.cs | 10 ++ backend/src/Services/ISigningService.cs | 10 ++ backend/src/Services/SigningService.cs | 134 ++++++++++++++++++ backend/src/Startup.cs | 1 + docker-compose.yml | 3 + 10 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 backend/src/Services/ISigningService.cs create mode 100644 backend/src/Services/SigningService.cs diff --git a/.env.production.sample b/.env.production.sample index c28913aa..4b848665 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -10,6 +10,8 @@ DATABASE_ID= OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= +GNUPG_PRIVATEKEY_FILE_NAME= + JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index df755758..b8dc09e2 100644 --- a/.env.sample +++ b/.env.sample @@ -17,6 +17,8 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy +GNUPG_PRIVATEKEY_FILE_NAME=private.gpg + SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password SSL_CERTIFICATE_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy diff --git a/backend/Dockerfile b/backend/Dockerfile index 14c2e3ad..889e9d2a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -13,6 +13,7 @@ ARG GROUP_ID ARG USER_ID ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME ARG CERTIFICATE_AUTHORITY_PASSWORD +ARG GNUPG_PRIVATEKEY_FILE_NAME # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) RUN \ @@ -96,6 +97,15 @@ RUN \ ${HOME}/app && \ ln --symbolic ${HOME}/app /app + # Install GnuPG + RUN \ + apt-get update && \ + apt-get -y install gnupg + + # Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) + RUN \ + export GPG_TTY=$(tty) + ########### # As `me` # ########### @@ -138,7 +148,21 @@ COPY \ COPY \ --chown=me:us \ ./test/Database.Tests.csproj \ - ./test/ + ./test/ + +# GnuPG +# Copy keys to image +RUN \ +mkdir --parents \ +./gpg-keys +COPY \ +--chown=me:us \ +./gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} \ +./gpg-keys/ + +# Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) +RUN \ +export GPG_TTY=$(tty) # https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile.alpine-x64 # https://github.com/dotnet/dotnet-docker/blob/master/samples/run-aspnetcore-https-development.md diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index b463c5aa..1835ac8a 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -17,6 +17,8 @@ public sealed class AppSettings public string VerificationCode { get; set; } = ""; + public string GnupgPrivateKeyFilename { get; set; } = ""; + public LoggingSettings Logging { get; set; } = new(); public JsonWebTokenSettings JsonWebToken { get; set; } = new(); diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 5283950a..606955a1 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -20,6 +20,7 @@ public async Task CreateGeometricDataAsync( ApplicationDbContext context, AppSettings appSettings, IUserService userService, + ISigningService signingService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken @@ -114,6 +115,7 @@ input.RootResource.AppliedConversionMethod is null ) ); geometricData.Resources.Add(resource); + var signature = signingService.SignData(geometricData.ToString()); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 015a498a..d5623936 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading.Tasks; using Database.Data; +using Database.Services; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -57,6 +58,7 @@ string[] commandLineArguments { // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); + await ImportGnuPGKey(scope.ServiceProvider).ConfigureAwait(false); } await application.RunAsync().ConfigureAwait(false); @@ -125,6 +127,14 @@ IServiceProvider services } } + private static async Task ImportGnuPGKey( + IServiceProvider services + ) + { + var signing = services.GetRequiredService(); + await signing.ImportPrivateKey(); + } + // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host private static WebApplicationBuilder CreateWebApplicationBuilder( string[] commandLineArguments diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs new file mode 100644 index 00000000..1ee1d47e --- /dev/null +++ b/backend/src/Services/ISigningService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace Database.Services; + +public interface ISigningService +{ + public Task ImportPrivateKey(); + + public Task SignData(string data); +} \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs new file mode 100644 index 00000000..d3f877e2 --- /dev/null +++ b/backend/src/Services/SigningService.cs @@ -0,0 +1,134 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Database.Services; + +public class SigningService( + AppSettings appSettings, + ILogger logger) : ISigningService +{ + //Export private subkey: gpg --output private.pgp --armor --export-secret-key C97074097892B88EF6D23BA9E5D03DA76EDC407D + private string _fingerprint = ""; + + public async Task ImportPrivateKey() + { + //await ExecuteCommand(); + var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); + if (success) + { + _fingerprint = await GetFingerprint(); + if (!String.IsNullOrEmpty(_fingerprint)) + { + logger.LogDebug("GNUPG Fingerprint: {Fingerprint}", _fingerprint); + return true; + } + } + return false; + } + + public async Task SignData(string data) + { + //await ExecuteCommand(); + //await ExecuteGnuCommand($"echo $(tty)"); + var filepath = WriteDataToFile(data); + await ExecuteGnuCommand($"echo $(tty)"); + await ExecuteGnuCommand($"echo $GPG_TTY"); + await ExecuteGnuCommand($"gpgconf --list-dirs"); + // gpg --detach-sig --armor --local-user response.json + //var (success, outout) = await ExecuteGnuCommand($"gpg --output {Path.GetFileName(filepath)}.asc --detach-sig --armor {Path.GetFileName(filepath)}"); + //var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); + await ExecuteGnuCommand($"echo \"test\" | gpg --batch --clearsign"); + var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); + + var signature = ReadSignatureFromFile(filepath); + logger.LogDebug("GNUPG Signature: {Signature}", signature); + //RemoveFiles(filepath); + return success; + } + + private async Task<(bool, string)> ExecuteGnuCommand(string command) + { + logger.LogDebug("GNUPG: Execute command. ({Command})", command); + + var escapedArgs = command.Replace("\"", "\\\""); + ProcessStartInfo startInfo = new() + { + FileName = "bash", + Arguments = $"-c \"{escapedArgs}\"", + CreateNoWindow = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + //WorkingDirectory = "/home/me/app/src/", + //RedirectStandardInput = true, + //UseShellExecute = false, + //UserName = "me" + }; + + var proc = Process.Start(startInfo); + ArgumentNullException.ThrowIfNull(proc); + string output = proc.StandardOutput.ReadToEnd(); + string error = proc.StandardError.ReadToEnd(); + await proc.WaitForExitAsync(); + if (string.IsNullOrEmpty(output)) + { + output = error; + } + else if (!string.IsNullOrEmpty(error)) + { + logger.LogDebug("GNUPG Error: {Error}", error); + } + logger.LogDebug("GNUPG Output: {Output}", output); + logger.LogDebug("GNUPG ExitCode: {ExitCode}", proc.ExitCode); + return (proc.ExitCode == 0, output); + } + + private async Task GetFingerprint() + { + //gpg--list - keys--with - subkey - fingerprint--with - colon + //tru::1:1740060571:0:3:1:5 + //pub: -:2048:17:AF86639859F25224: 1739973559:1803045559::-:::scSC::::::23::0: + //fpr:::::::::8CC878D9614150C8278C6F0FAF86639859F25224: + //uid: -::::1739973559::90476D25C295C40CFD4D89EAB1BF3C879E39165B::James Bremer::::::::::0: + //sub: -:2048:17:E5D03DA76EDC407D: 1739973625:1803045625:::::s::::::23: + //fpr:::::::::C97074097892B88EF6D23BA9E5D03DA76EDC407D: + string fingerprint = ""; + var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); + if (success) + { + var splitedOutput = outout.Split(':'); + fingerprint = splitedOutput[splitedOutput.Length - 2]; + } + return fingerprint; + } + + private static string WriteDataToFile(string data) + { + string docPath = Path.Combine(Environment.CurrentDirectory, "dataToSign.json"); + + using (StreamWriter outputFile = new StreamWriter(docPath)) + { + outputFile.WriteLine(data); + } + + return docPath; + } + + private static string ReadSignatureFromFile(string filepath) + { + string signature = ""; + using (StreamReader file = new StreamReader(filepath + ".asc")) + { + signature = file.ReadToEnd(); + } + return signature; + } + + private static void RemoveFiles(string filepath) + { + File.Delete(filepath); + File.Delete(filepath + ".asc"); + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 61e00416..0ca58dd7 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -64,6 +64,7 @@ public void ConfigureServices(IServiceCollection services) // services.AddDatabaseDeveloperPageExceptionFilter(); services.AddSingleton(); services.AddScoped(); + services.AddSingleton(); } private static void ConfigureRequestResponseServices(IServiceCollection services) diff --git a/docker-compose.yml b/docker-compose.yml index 71b2087b..d8fc1897 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,7 @@ services: args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - CERTIFICATE_AUTHORITY_PASSWORD=${CERTIFICATE_AUTHORITY_PASSWORD} + - GNUPG_PRIVATEKEY_FILE_NAME=${GNUPG_PRIVATEKEY_FILE_NAME} # user: 1000:1000 command: [ "dotnet", "watch", @@ -114,6 +115,7 @@ services: - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_VerificationCode=${VERIFICATION_CODE} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose @@ -122,6 +124,7 @@ services: # - SSL_CERT_FILE=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING=1 # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER=all + - GPG_TTY=/dev/pts/0 extra_hosts: - ${HOST}:host-gateway - ${METABASE_HOST}:host-gateway From ca890014e83904dc758a6988d328ea04f7722d0d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:34:53 +0100 Subject: [PATCH 277/878] Create volume for GnuPG to share its data across multiple running containers and across time --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index d8fc1897..6435d819 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -86,6 +86,8 @@ services: - files:/app/src/files - backend_vscode_server:/home/me/.vscode-server - backend_vscode_server_insiders:/home/me/.vscode-server-insiders + # Create volume for GnuPG + - gnupg:/home/me/.gnupg # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -124,6 +126,8 @@ services: # - SSL_CERT_FILE=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING=1 # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER=all + # The value `/dev/pts/0` is the output of running `tty` within `shellb` + # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - GPG_TTY=/dev/pts/0 extra_hosts: - ${HOST}:host-gateway @@ -198,5 +202,6 @@ volumes: data: emails: files: + gnupg: backend_vscode_server: backend_vscode_server_insiders: From 111d06a3b95ce6bbd3f09b1f44b7add5be36ef93 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:36:48 +0100 Subject: [PATCH 278/878] Prepare the GnuPG volume and add configure the gpg-agent to work with unattended passphrases --- see https://wiki.archlinux.org/title/GnuPG#Unattended_passphrase --- backend/Dockerfile | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 889e9d2a..da1b8290 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -52,6 +52,7 @@ RUN \ dumb-init \ editorconfig \ gawk \ + gnupg \ lldb \ make \ # npm \ @@ -97,15 +98,6 @@ RUN \ ${HOME}/app && \ ln --symbolic ${HOME}/app /app - # Install GnuPG - RUN \ - apt-get update && \ - apt-get -y install gnupg - - # Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) - RUN \ - export GPG_TTY=$(tty) - ########### # As `me` # ########### @@ -148,7 +140,7 @@ COPY \ COPY \ --chown=me:us \ ./test/Database.Tests.csproj \ - ./test/ + ./test/ # GnuPG # Copy keys to image @@ -338,7 +330,13 @@ RUN \ ${HOME}/.vscode-server-insiders && \ mkdir --parents \ ${HOME}/.nuget \ - ${HOME}/.dotnet + ${HOME}/.dotnet && \ + mkdir --parents \ + ${HOME}/.gnupg && \ + chmod u=rwx,g=,o= \ + ${HOME}/.gnupg && \ + echo "allow-loopback-pinentry" \ + > ${HOME}/.gnupg/gpg-agent.conf ENV SHELL=/bin/bash From b7a9cae313714920d34aae36a2aefc30e2bc2d30 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:37:46 +0100 Subject: [PATCH 279/878] Set the pinentry mode to loopback for signing data unattendedly --- see https://wiki.archlinux.org/title/GnuPG#Unattended_passphrase --- backend/src/Services/SigningService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d3f877e2..d1f0ad59 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -40,8 +40,8 @@ public async Task SignData(string data) // gpg --detach-sig --armor --local-user response.json //var (success, outout) = await ExecuteGnuCommand($"gpg --output {Path.GetFileName(filepath)}.asc --detach-sig --armor {Path.GetFileName(filepath)}"); //var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); - await ExecuteGnuCommand($"echo \"test\" | gpg --batch --clearsign"); - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); + await ExecuteGnuCommand($"echo \"test\" | gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign"); + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); var signature = ReadSignatureFromFile(filepath); logger.LogDebug("GNUPG Signature: {Signature}", signature); From 40613c3a1b6ba94d59fd8859cd84e5d11d0f4972 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 5 Mar 2025 08:51:41 +0100 Subject: [PATCH 280/878] First implementation of access rights --- .../Authorization/FileUploadAuthorization.cs | 9 +- .../src/Controllers/FileUploadController.cs | 27 +++- backend/src/Data/AccessRights.cs | 6 + backend/src/Data/DataAccessRights.cs | 35 +++++ backend/src/Data/DataX.cs | 3 + backend/src/Data/IData.cs | 3 + backend/src/Enumerations/DataAccessMode.cs | 7 + .../GraphQl/AccessRights/AccessRightsInput.cs | 5 + .../AccessRights/AccessRightsPayload.cs | 53 ++++++++ .../AccessRights/AddAccessRightsError.cs | 16 +++ .../AccessRights/AddAccessRightsErrorCode.cs | 8 ++ .../AccessRights/AddAccessRightsPayload.cs | 19 +++ .../AccessRights/AddDataAccessRightsError.cs | 16 +++ .../AddDataAccessRightsErrorCode.cs | 9 ++ .../AddDataAccessRightsPayload.cs | 21 +++ .../AccessRights/DataAccessRightsInput.cs | 12 ++ .../AccessRights/DataAccessRightsMutations.cs | 127 ++++++++++++++++++ 17 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 backend/src/Data/AccessRights.cs create mode 100644 backend/src/Data/DataAccessRights.cs create mode 100644 backend/src/Enumerations/DataAccessMode.cs create mode 100644 backend/src/GraphQl/AccessRights/AccessRightsInput.cs create mode 100644 backend/src/GraphQl/AccessRights/AccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/AddAccessRightsError.cs create mode 100644 backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs create mode 100644 backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs create mode 100644 backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs create mode 100644 backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs create mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index 46084829..d645e55c 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -1,14 +1,15 @@ -using System; +using System; using System.Net.Http; using System.Threading; -using System.Threading.Tasks; +using Database.Metabase; using Microsoft.AspNetCore.Http; namespace Database.Authorization; public static class FileUploadDataAuthorization { - public static Task IsAuthorizedToUploadFilesForInstitution( + public static bool IsAuthorizedToUploadFilesForInstitution( + CurrentUser currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -16,6 +17,6 @@ public static Task IsAuthorizedToUploadFilesForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index bb6290c7..7028c096 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; using Microsoft.AspNetCore.Http; +using Database.Services; namespace Database.Controllers; @@ -94,9 +95,20 @@ IHttpContextAccessor httpContextAccessor [RequestSizeLimit(10737418240)] // 10 GiB public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, + IUserService userService, CancellationToken cancellationToken ) { + var currentUser = await userService.GetCurrentUser( + _httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + ModelState.AddModelError("CuurentUser", + $"User is not authenticated."); + return BadRequest(ModelState); + } + if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { ModelState.AddModelError("File", @@ -125,13 +137,14 @@ CancellationToken cancellationToken $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); return BadRequest(ModelState); } - if (!await FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( - getHttpsResource.Data.CreatorId, - _appSettings, - _httpClientFactory, - _httpContextAccessor, - cancellationToken - ).ConfigureAwait(false) + if (!FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( + currentUser, + getHttpsResource.Data.CreatorId, + _appSettings, + _httpClientFactory, + _httpContextAccessor, + cancellationToken + ) ) { return Unauthorized(); diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/AccessRights.cs new file mode 100644 index 00000000..93b51880 --- /dev/null +++ b/backend/src/Data/AccessRights.cs @@ -0,0 +1,6 @@ +namespace Database.Data; + +public class AccessRights + : Entity +{ +} \ No newline at end of file diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs new file mode 100644 index 00000000..70644b3a --- /dev/null +++ b/backend/src/Data/DataAccessRights.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +/// +/// Access rights per dataset +/// +[Owned] +public sealed class DataAccessRights +{ + /// + /// Dictionary to store allowed user with the amount of a allowed dataset calls + /// -1 meens unlimited access. + /// + public IDictionary AllowedUserAndQuantity { get; set; } + + /// + /// Allowed Institutions + /// + public ICollection AllowedInstitutions { get; set; } + + /// + /// Allowed applications + /// + public ICollection AllowedApplications { get; set; } + + public DataAccessRights() + { + AllowedUserAndQuantity = new Dictionary(); + AllowedInstitutions = new List(); + AllowedApplications = new List(); + } +} \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index faf7c4f1..89b4aef9 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -80,4 +80,7 @@ protected DataX( // TODO Exactly one resource must not have a parent and each other resource must have one from // this list and the graph must be connected. In other words, the resources must form a tree. public virtual ICollection Resources { get; } = new List(); + + public DataAccessMode DataAccess { get; set; } = DataAccessMode.UNRESTRICTED; + public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); } \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 4ecca85e..bb51e89f 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.Enumerations; namespace Database.Data; @@ -16,4 +17,6 @@ public interface IData : IEntity ICollection Resources { get; } ResponseApproval? Approval { get; } string Locale { get; } + DataAccessMode DataAccess { get; set; } + DataAccessRights DataAccessRights { get; } } \ No newline at end of file diff --git a/backend/src/Enumerations/DataAccessMode.cs b/backend/src/Enumerations/DataAccessMode.cs new file mode 100644 index 00000000..a642246f --- /dev/null +++ b/backend/src/Enumerations/DataAccessMode.cs @@ -0,0 +1,7 @@ +namespace Database.Enumerations; + +public enum DataAccessMode +{ + UNRESTRICTED, + RESTRICTED +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs new file mode 100644 index 00000000..35bd7003 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs @@ -0,0 +1,5 @@ +namespace Database.GraphQl.AccessRights; + +public sealed record AccessRightsInput +( +); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs new file mode 100644 index 00000000..032a1026 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.AccessRights; + +public class AccessRightsPayload + : Payload + where TDataAccessRightsError : IUserError +{ + protected AccessRightsPayload( + DataAccessRights dataAccessRights + ) + { + DataAccessRights = dataAccessRights; + } + + protected AccessRightsPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected AccessRightsPayload( + TDataAccessRightsError error + ) + : this(new[] { error }) + { + } + + protected AccessRightsPayload( + DataAccessRights dataAccessRights, + IReadOnlyCollection errors + ) + { + DataAccessRights = dataAccessRights; + Errors = errors; + } + + protected AccessRightsPayload( + DataAccessRights dataAccessRights, + TDataAccessRightsError error + ) + : this( + dataAccessRights, + new[] { error } + ) + { + } + + public DataAccessRights? DataAccessRights { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsError.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsError.cs new file mode 100644 index 00000000..020d1197 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddAccessRightsError.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public class AddAccessRightsError + : UserErrorBase +{ + public AddAccessRightsError( + AddAccessRightsErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs new file mode 100644 index 00000000..765eae29 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl.AccessRights; + +public enum AddAccessRightsErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs new file mode 100644 index 00000000..636d9a7d --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs @@ -0,0 +1,19 @@ +namespace Database.GraphQl.AccessRights; + +public class AddAccessRightsPayload + : AccessRightsPayload +{ + public AddAccessRightsPayload( + Data.AccessRights accessRights + ) + : base(accessRights) + { + } + + public AddAccessRightsPayload( + AddAccessRightsError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs new file mode 100644 index 00000000..850200aa --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public class AddDataAccessRightsError + : UserErrorBase +{ + public AddDataAccessRightsError( + AddDataAccessRightsErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs new file mode 100644 index 00000000..b7cef8fb --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs @@ -0,0 +1,9 @@ +namespace Database.GraphQl.AccessRights; + +public enum AddDataAccessRightsErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs new file mode 100644 index 00000000..82026378 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs @@ -0,0 +1,21 @@ +using Database.Data; + +namespace Database.GraphQl.AccessRights; + +public class AddDataAccessRightsPayload + : AccessRightsPayload +{ + public AddDataAccessRightsPayload( + DataAccessRights dataAccessRights + ) + : base(dataAccessRights) + { + } + + public AddDataAccessRightsPayload( + AddDataAccessRightsError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs new file mode 100644 index 00000000..c17e6c3e --- /dev/null +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs @@ -0,0 +1,12 @@ +using System; +using Database.Data; +using Database.Enumerations; + +namespace Database.GraphQl.AccessRights; + +public sealed record DataAccessRightsInput +( + Guid DataId, + DataAccessMode DataAccessMode, + DataAccessRights DataAccessRights +); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs new file mode 100644 index 00000000..d2df4eb9 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -0,0 +1,127 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Services; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; + +namespace Database.GraphQl.AccessRights; + +[ExtendObjectType(nameof(Mutation))] +public class DataAccessRightsMutations +{ + public async Task AddAccessRightsToDataAsync( + DataAccessRightsInput input, + ApplicationDbContext context, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IUserService userService, + IDataService dataService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new AddDataAccessRightsPayload( + new AddDataAccessRightsError( + AddDataAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + //if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( + // currentUser, + // input.CreatorId, + // appSettings, + // httpClientFactory, + // httpContextAccessor, + // cancellationToken + // ) + //) + // return new AddDataAccessRightsPayload( + // new AddDataAccessRightsError( + // AddDataAccessRightsErrorCode.UNAUTHORIZED, + // $"The current user is not authorized to approval for the institution.", + // [] + // ) + // ); + + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + if (data == null) + { + return new AddDataAccessRightsPayload( + new AddDataAccessRightsError( + AddDataAccessRightsErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [] + ) + ); + } + + data.DataAccess = input.DataAccessMode; + if (data.DataAccess == Enumerations.DataAccessMode.RESTRICTED) + { + data.DataAccessRights.AllowedInstitutions = input.DataAccessRights.AllowedInstitutions; + + data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; + + data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; + } + + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new AddDataAccessRightsPayload(data.DataAccessRights); + } + + public async Task AddAccessRightsAsync( + AccessRightsInput input, + ApplicationDbContext context, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IUserService userService, + IDataService dataService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new AddAccessRightsPayload( + new AddAccessRightsError( + AddAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + //if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( + // currentUser, + // input.CreatorId, + // appSettings, + // httpClientFactory, + // httpContextAccessor, + // cancellationToken + // ) + //) + // return new AddDataAccessRightsPayload( + // new AddDataAccessRightsError( + // AddDataAccessRightsErrorCode.UNAUTHORIZED, + // $"The current user is not authorized to approval for the institution.", + // [] + // ) + // ); + + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new AddAccessRightsPayload(data.DataAccessRights); + } +} \ No newline at end of file From ee0481d703f4d0f51233f36aad66783e40ee5ada Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 5 Mar 2025 10:25:58 +0100 Subject: [PATCH 281/878] Upgrade .NET dependencies and Node.JS version --- backend/src/Database.csproj | 34 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 12 +++++------ frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 6 +++--- frontend/type-defs.graphqls | 2 +- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index cf334cdd..cb88458a 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -24,33 +24,33 @@ - - - - - - + + + + + + - + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - + + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index f45dc2f6..7c2e70d6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,15 +16,15 @@ - + - - - + + + - + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 87f9b209..f43211cf 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -6,7 +6,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:22.11-bookworm-slim +FROM node:22.14-bookworm-slim ARG GROUP_ID ARG USER_ID diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 8031c923..790ceb4e 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -5,7 +5,7 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:22.11-bookworm-slim AS dependencies +FROM node:22.14-bookworm-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -17,7 +17,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:22.11-bookworm-slim AS build +FROM node:22.14-bookworm-slim AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -30,7 +30,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:22.11-bookworm-slim +FROM node:22.14-bookworm-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 7769d598..40ddafd1 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -244,7 +244,7 @@ type GetHttpsResource implements Node { children: [GetHttpsResource!]! @cost(weight: "10") data: Data @cost(weight: "10") dataFormatId: Uuid! - description: String! + description: String hashValue: String! id: ID! locator: Url! From b8519b1d18f6294d61aad9abc5e75670cd4677b2 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 5 Mar 2025 13:51:11 +0100 Subject: [PATCH 282/878] Clean up code --- .env.production.sample | 1 + .env.sample | 3 +- backend/src/AppSettings.cs | 1 + .../GeometricDataX/GeometricDataMutations.cs | 9 ++- backend/src/Services/ISigningService.cs | 4 +- backend/src/Services/SigningService.cs | 72 ++++++++++--------- docker-compose.yml | 3 +- 7 files changed, 53 insertions(+), 40 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 4b848665..7f3e3375 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -11,6 +11,7 @@ OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= GNUPG_PRIVATEKEY_FILE_NAME= +GNUPG_PRIVATEKEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index b8dc09e2..4dfc424b 100644 --- a/.env.sample +++ b/.env.sample @@ -17,7 +17,8 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_PRIVATEKEY_FILE_NAME=private.gpg +GNUPG_PRIVATEKEY_FILENAME=private.gpg +GNUPG_PRIVATEKEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 1835ac8a..1dc51bc0 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -18,6 +18,7 @@ public sealed class AppSettings = ""; public string GnupgPrivateKeyFilename { get; set; } = ""; + public string GnupgPrivateKeyPassphrase { get; set; } = ""; public LoggingSettings Logging { get; set; } = new(); diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 606955a1..515ff97f 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -115,7 +116,11 @@ input.RootResource.AppliedConversionMethod is null ) ); geometricData.Resources.Add(resource); - var signature = signingService.SignData(geometricData.ToString()); + var (success, signature) = await signingService.SignData(JsonSerializer.Serialize(geometricData)); + if (success) + { + geometricData.Approval = new ResponseApproval(DateTime.Now, signature, signingService.GetFingerprint(), "", ""); + } context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index 1ee1d47e..bd2cef08 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -6,5 +6,7 @@ public interface ISigningService { public Task ImportPrivateKey(); - public Task SignData(string data); + public string GetFingerprint(); + + public Task<(bool, string)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d1f0ad59..f4849fd1 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -10,12 +10,10 @@ public class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { - //Export private subkey: gpg --output private.pgp --armor --export-secret-key C97074097892B88EF6D23BA9E5D03DA76EDC407D private string _fingerprint = ""; public async Task ImportPrivateKey() { - //await ExecuteCommand(); var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { @@ -23,30 +21,38 @@ public async Task ImportPrivateKey() if (!String.IsNullOrEmpty(_fingerprint)) { logger.LogDebug("GNUPG Fingerprint: {Fingerprint}", _fingerprint); - return true; + } + else + { + logger.LogError("GNUPG Error: Failed to get fingerprint."); + success = false; } } - return false; + return success; } - public async Task SignData(string data) + public string GetFingerprint() + { + return _fingerprint; + } + + public async Task<(bool, string)> SignData(string data) { - //await ExecuteCommand(); - //await ExecuteGnuCommand($"echo $(tty)"); var filepath = WriteDataToFile(data); - await ExecuteGnuCommand($"echo $(tty)"); - await ExecuteGnuCommand($"echo $GPG_TTY"); - await ExecuteGnuCommand($"gpgconf --list-dirs"); - // gpg --detach-sig --armor --local-user response.json - //var (success, outout) = await ExecuteGnuCommand($"gpg --output {Path.GetFileName(filepath)}.asc --detach-sig --armor {Path.GetFileName(filepath)}"); - //var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); - await ExecuteGnuCommand($"echo \"test\" | gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign"); - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); + string signature = ""; - var signature = ReadSignatureFromFile(filepath); - logger.LogDebug("GNUPG Signature: {Signature}", signature); - //RemoveFiles(filepath); - return success; + if (success) + { + success = ReadSignatureFromFile(filepath, out signature); + logger.LogDebug("GNUPG Signature: {Signature}", signature); + } + else + { + logger.LogError("GNUPG Error: {Outout}", outout); + } + RemoveFiles(filepath); + return (success, signature); } private async Task<(bool, string)> ExecuteGnuCommand(string command) @@ -61,10 +67,6 @@ public async Task SignData(string data) CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, - //WorkingDirectory = "/home/me/app/src/", - //RedirectStandardInput = true, - //UseShellExecute = false, - //UserName = "me" }; var proc = Process.Start(startInfo); @@ -87,13 +89,6 @@ public async Task SignData(string data) private async Task GetFingerprint() { - //gpg--list - keys--with - subkey - fingerprint--with - colon - //tru::1:1740060571:0:3:1:5 - //pub: -:2048:17:AF86639859F25224: 1739973559:1803045559::-:::scSC::::::23::0: - //fpr:::::::::8CC878D9614150C8278C6F0FAF86639859F25224: - //uid: -::::1739973559::90476D25C295C40CFD4D89EAB1BF3C879E39165B::James Bremer::::::::::0: - //sub: -:2048:17:E5D03DA76EDC407D: 1739973625:1803045625:::::s::::::23: - //fpr:::::::::C97074097892B88EF6D23BA9E5D03DA76EDC407D: string fingerprint = ""; var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); if (success) @@ -116,14 +111,21 @@ private static string WriteDataToFile(string data) return docPath; } - private static string ReadSignatureFromFile(string filepath) + private static bool ReadSignatureFromFile(string filepath, out string signature) { - string signature = ""; - using (StreamReader file = new StreamReader(filepath + ".asc")) + try + { + using (StreamReader file = new StreamReader(filepath + ".asc")) + { + signature = file.ReadToEnd(); + } + return true; + } + catch { - signature = file.ReadToEnd(); + signature = ""; + return false; } - return signature; } private static void RemoveFiles(string filepath) diff --git a/docker-compose.yml b/docker-compose.yml index 6435d819..1d50f832 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,7 +117,8 @@ services: - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILENAME} + - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose From 8e8f114826d6033363b8ba08cf88357fe3922256 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Mar 2025 09:44:59 +0100 Subject: [PATCH 283/878] Cleanup Code --- .env.sample | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 4 +- backend/src/Logging/SigningServiceLogging.cs | 57 ++++++++++++ backend/src/Logging/UserServiceLogging.cs | 12 +++ backend/src/Program.cs | 6 +- backend/src/Services/DataService.cs | 4 + backend/src/Services/EmailSender.cs | 6 +- backend/src/Services/IDataService.cs | 10 +++ backend/src/Services/ISigningService.cs | 18 +++- backend/src/Services/IUserService.cs | 9 ++ backend/src/Services/SigningService.cs | 87 +++++++++++++------ backend/src/Services/UserService.cs | 18 +++- docker-compose.yml | 2 +- 13 files changed, 195 insertions(+), 40 deletions(-) create mode 100644 backend/src/Logging/SigningServiceLogging.cs create mode 100644 backend/src/Logging/UserServiceLogging.cs diff --git a/.env.sample b/.env.sample index 4dfc424b..9d9454d3 100644 --- a/.env.sample +++ b/.env.sample @@ -17,7 +17,7 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_PRIVATEKEY_FILENAME=private.gpg +GNUPG_PRIVATEKEY_FILE_NAME=private.gpg GNUPG_PRIVATEKEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 515ff97f..70cd209e 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -116,12 +116,14 @@ input.RootResource.AppliedConversionMethod is null ) ); geometricData.Resources.Add(resource); + var (success, signature) = await signingService.SignData(JsonSerializer.Serialize(geometricData)); if (success) { geometricData.Approval = new ResponseApproval(DateTime.Now, signature, signingService.GetFingerprint(), "", ""); } - context.GeometricData.Add(geometricData); + + //context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); } diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs new file mode 100644 index 00000000..b8d8cb37 --- /dev/null +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -0,0 +1,57 @@ +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class SigningServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Fingerprint: {Fingerprint}")] + public static partial void Fingerprint(this ILogger logger, string fingerprint); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Failed to get fingerprint.")] + public static partial void FingerprintError(this ILogger logger); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Signature: {Signature}")] + public static partial void Signature(this ILogger logger, string signature); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Failed to create signature: {Output}")] + public static partial void SignatureError(this ILogger logger, string output); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command: {Command}")] + public static partial void ExecuteCommand(this ILogger logger, string command); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG Execute Command: {Error}")] + public static partial void ExecuteCommandError(this ILogger logger, string error); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Output: {Output}")] + public static partial void ExecuteCommandOutput(this ILogger logger, string output); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Result: {result}")] + public static partial void ExecuteCommandResult(this ILogger logger, int result); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Write file exception: {Exception}")] + public static partial void WriteFileException(this ILogger logger, string exception); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Read file exception: {Exception}")] + public static partial void ReadFileException(this ILogger logger, string exception); +} \ No newline at end of file diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs new file mode 100644 index 00000000..24ddb7c2 --- /dev/null +++ b/backend/src/Logging/UserServiceLogging.cs @@ -0,0 +1,12 @@ +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class UserServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Extracted Bearer Token: {Token}")] + public static partial void ExtractedToken(this ILogger logger, string? token); +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index d5623936..ab6be9f0 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -58,7 +58,7 @@ string[] commandLineArguments { // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); - await ImportGnuPGKey(scope.ServiceProvider).ConfigureAwait(false); + await InitializeSigningService(scope.ServiceProvider).ConfigureAwait(false); } await application.RunAsync().ConfigureAwait(false); @@ -127,12 +127,12 @@ IServiceProvider services } } - private static async Task ImportGnuPGKey( + private static async Task InitializeSigningService( IServiceProvider services ) { var signing = services.GetRequiredService(); - await signing.ImportPrivateKey(); + await signing.Initialize(); } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 663a2451..afc5f91f 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -7,8 +7,12 @@ namespace Database.Services; +/// +/// Implementation of +/// public class DataService() : IDataService { + /// public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 6717d9ed..27af95ec 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Logging; @@ -13,11 +13,11 @@ public static partial class Log Level = LogLevel.Debug, Message = "About to send email to `{Recipient}` with subject `{Subject}` and body `{Body}`")] public static partial void AboutToSendEmail( - this ILogger logger, + this ILogger logger, (string name, string address) Recipient, string Subject, string Body - ); + ); } public sealed class EmailSender diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/IDataService.cs index cd4e9945..6800ec47 100644 --- a/backend/src/Services/IDataService.cs +++ b/backend/src/Services/IDataService.cs @@ -5,7 +5,17 @@ namespace Database.Services; +/// +/// Service to get data from +/// public interface IDataService { + /// + /// Get dataset with passed id from all datasets + /// + /// Id of dataset to get. + /// + /// + /// Dataset with passed id or null. public Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index bd2cef08..6acec2b1 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -2,11 +2,27 @@ namespace Database.Services; +/// +/// Service to sign data string with GNUPG. +/// public interface ISigningService { - public Task ImportPrivateKey(); + /// + /// Initialize service by importing private key from file passed into container. + /// + /// True, if privat key was successfully imported and fingerprint set. + public Task Initialize(); + /// + /// Get fingerprint + /// + /// Fingerprint as string public string GetFingerprint(); + /// + /// Sign passed data string and return signature. + /// + /// Data string to create signature from. + /// True and generated signature, if successful. Otherwise false. public Task<(bool, string)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index 386f3d77..5659baaa 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -5,7 +5,16 @@ namespace Database.Services; +/// +/// Service to get current user from Metabase +/// public interface IUserService { + /// + /// Get current user from Mewtabase by extraxting token from . + /// + /// + /// + /// public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index f4849fd1..72273a0a 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -2,62 +2,84 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; +using Database.Logging; using Microsoft.Extensions.Logging; namespace Database.Services; +/// +/// Implementation of +/// +/// +/// Instance of public class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { private string _fingerprint = ""; + private const string FILENAME = "dataToSign"; - public async Task ImportPrivateKey() + /// + public async Task Initialize() { - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); + // Execute command to import private key + var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { - _fingerprint = await GetFingerprint(); + // Extract fingerprint + _fingerprint = await GetFingerprintFromKeyList(); if (!String.IsNullOrEmpty(_fingerprint)) { - logger.LogDebug("GNUPG Fingerprint: {Fingerprint}", _fingerprint); + logger.Fingerprint(_fingerprint); } else { - logger.LogError("GNUPG Error: Failed to get fingerprint."); + logger.FingerprintError(); success = false; } } return success; } + /// public string GetFingerprint() { return _fingerprint; } + /// public async Task<(bool, string)> SignData(string data) { - var filepath = WriteDataToFile(data); - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); string signature = ""; + // Write data string to file + if (!WriteDataToFile(data)) + { + return (false, ""); + } + + // Execute command to generate detached signature file Creates file with .asc ending + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {FILENAME}"); + if (success) { - success = ReadSignatureFromFile(filepath, out signature); - logger.LogDebug("GNUPG Signature: {Signature}", signature); + // Read signature from file + success = ReadSignatureFromFile(out signature); + logger.Signature(signature); } else { - logger.LogError("GNUPG Error: {Outout}", outout); + logger.SignatureError(outout); } - RemoveFiles(filepath); + + // Remove created files + RemoveFiles(); return (success, signature); } private async Task<(bool, string)> ExecuteGnuCommand(string command) { - logger.LogDebug("GNUPG: Execute command. ({Command})", command); + logger.ExecuteCommand(command); var escapedArgs = command.Replace("\"", "\\\""); ProcessStartInfo startInfo = new() @@ -80,16 +102,18 @@ public string GetFingerprint() } else if (!string.IsNullOrEmpty(error)) { - logger.LogDebug("GNUPG Error: {Error}", error); + logger.ExecuteCommandError(error); } - logger.LogDebug("GNUPG Output: {Output}", output); - logger.LogDebug("GNUPG ExitCode: {ExitCode}", proc.ExitCode); + logger.ExecuteCommandOutput(output); + logger.ExecuteCommandResult(proc.ExitCode); return (proc.ExitCode == 0, output); } - private async Task GetFingerprint() + private async Task GetFingerprintFromKeyList() { string fingerprint = ""; + + // Execute command to list keys with fingerprint, use 'with-colon' to split later var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); if (success) { @@ -99,38 +123,45 @@ private async Task GetFingerprint() return fingerprint; } - private static string WriteDataToFile(string data) + private bool WriteDataToFile(string data) { - string docPath = Path.Combine(Environment.CurrentDirectory, "dataToSign.json"); + try + { + using (StreamWriter outputFile = new StreamWriter(FILENAME)) + { + outputFile.WriteLine(data); + } - using (StreamWriter outputFile = new StreamWriter(docPath)) + return true; + } + catch (Exception ex) { - outputFile.WriteLine(data); + logger.WriteFileException(ex.ToString()); + return false; } - - return docPath; } - private static bool ReadSignatureFromFile(string filepath, out string signature) + private bool ReadSignatureFromFile(out string signature) { try { - using (StreamReader file = new StreamReader(filepath + ".asc")) + using (StreamReader file = new StreamReader(FILENAME + ".asc")) { signature = file.ReadToEnd(); } return true; } - catch + catch (Exception ex) { + logger.ReadFileException(ex.ToString()); signature = ""; return false; } } - private static void RemoveFiles(string filepath) + private static void RemoveFiles() { - File.Delete(filepath); - File.Delete(filepath + ".asc"); + File.Delete(FILENAME); + File.Delete(FILENAME + ".asc"); } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 9bc5ca00..49fa45f5 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -3,32 +3,46 @@ using System.Threading; using System.Threading.Tasks; using Database.Extensions; +using Database.Logging; using Database.Metabase; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; namespace Database.Services; +/// +/// Implementation of +/// +/// +/// +/// to store already known users. public class UserService( AppSettings appSettings, IHttpClientFactory httpClientFactory, - IMemoryCache cache) : IUserService + IMemoryCache cache, + ILogger logger) : IUserService { + /// public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { + // Extract token var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); + logger.ExtractedToken(token); if (token == null) { return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); } + // Check if there is already a user for token if (!cache.TryGetValue(token, out CurrentUser? cacheUser)) { + // Get user from Metabase cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - + // Store user in cache cache.Set(token, cacheUser, cacheEntryOptions); } diff --git a/docker-compose.yml b/docker-compose.yml index 1d50f832..a8762d98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,7 +117,7 @@ services: - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILENAME} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true From 09f22cc1147cb66629c28bdfce7252fd6e660f16 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:34:53 +0100 Subject: [PATCH 284/878] Create volume for GnuPG to share its data across multiple running containers and across time --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index d8fc1897..6435d819 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -86,6 +86,8 @@ services: - files:/app/src/files - backend_vscode_server:/home/me/.vscode-server - backend_vscode_server_insiders:/home/me/.vscode-server-insiders + # Create volume for GnuPG + - gnupg:/home/me/.gnupg # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -124,6 +126,8 @@ services: # - SSL_CERT_FILE=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING=1 # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER=all + # The value `/dev/pts/0` is the output of running `tty` within `shellb` + # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - GPG_TTY=/dev/pts/0 extra_hosts: - ${HOST}:host-gateway @@ -198,5 +202,6 @@ volumes: data: emails: files: + gnupg: backend_vscode_server: backend_vscode_server_insiders: From 91cd3d2c844877a28714c1023892ccd5990f1dc5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:36:48 +0100 Subject: [PATCH 285/878] Prepare the GnuPG volume and add configure the gpg-agent to work with unattended passphrases --- see https://wiki.archlinux.org/title/GnuPG#Unattended_passphrase --- backend/Dockerfile | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 889e9d2a..da1b8290 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -52,6 +52,7 @@ RUN \ dumb-init \ editorconfig \ gawk \ + gnupg \ lldb \ make \ # npm \ @@ -97,15 +98,6 @@ RUN \ ${HOME}/app && \ ln --symbolic ${HOME}/app /app - # Install GnuPG - RUN \ - apt-get update && \ - apt-get -y install gnupg - - # Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) - RUN \ - export GPG_TTY=$(tty) - ########### # As `me` # ########### @@ -148,7 +140,7 @@ COPY \ COPY \ --chown=me:us \ ./test/Database.Tests.csproj \ - ./test/ + ./test/ # GnuPG # Copy keys to image @@ -338,7 +330,13 @@ RUN \ ${HOME}/.vscode-server-insiders && \ mkdir --parents \ ${HOME}/.nuget \ - ${HOME}/.dotnet + ${HOME}/.dotnet && \ + mkdir --parents \ + ${HOME}/.gnupg && \ + chmod u=rwx,g=,o= \ + ${HOME}/.gnupg && \ + echo "allow-loopback-pinentry" \ + > ${HOME}/.gnupg/gpg-agent.conf ENV SHELL=/bin/bash From 2287edecf5d325dd24c121bda96b1c510fd464a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 4 Mar 2025 17:37:46 +0100 Subject: [PATCH 286/878] Set the pinentry mode to loopback for signing data unattendedly --- see https://wiki.archlinux.org/title/GnuPG#Unattended_passphrase --- backend/src/Services/SigningService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d3f877e2..d1f0ad59 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -40,8 +40,8 @@ public async Task SignData(string data) // gpg --detach-sig --armor --local-user response.json //var (success, outout) = await ExecuteGnuCommand($"gpg --output {Path.GetFileName(filepath)}.asc --detach-sig --armor {Path.GetFileName(filepath)}"); //var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); - await ExecuteGnuCommand($"echo \"test\" | gpg --batch --clearsign"); - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); + await ExecuteGnuCommand($"echo \"test\" | gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign"); + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); var signature = ReadSignatureFromFile(filepath); logger.LogDebug("GNUPG Signature: {Signature}", signature); From 5e7381c2145a9ec5ace954643d300b6123eda4f6 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 5 Mar 2025 13:51:11 +0100 Subject: [PATCH 287/878] Clean up code --- .env.production.sample | 1 + .env.sample | 3 +- backend/src/AppSettings.cs | 1 + .../GeometricDataX/GeometricDataMutations.cs | 9 ++- backend/src/Services/ISigningService.cs | 4 +- backend/src/Services/SigningService.cs | 72 ++++++++++--------- docker-compose.yml | 3 +- 7 files changed, 53 insertions(+), 40 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 4b848665..7f3e3375 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -11,6 +11,7 @@ OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= GNUPG_PRIVATEKEY_FILE_NAME= +GNUPG_PRIVATEKEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index b8dc09e2..4dfc424b 100644 --- a/.env.sample +++ b/.env.sample @@ -17,7 +17,8 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_PRIVATEKEY_FILE_NAME=private.gpg +GNUPG_PRIVATEKEY_FILENAME=private.gpg +GNUPG_PRIVATEKEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 1835ac8a..1dc51bc0 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -18,6 +18,7 @@ public sealed class AppSettings = ""; public string GnupgPrivateKeyFilename { get; set; } = ""; + public string GnupgPrivateKeyPassphrase { get; set; } = ""; public LoggingSettings Logging { get; set; } = new(); diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 606955a1..515ff97f 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -115,7 +116,11 @@ input.RootResource.AppliedConversionMethod is null ) ); geometricData.Resources.Add(resource); - var signature = signingService.SignData(geometricData.ToString()); + var (success, signature) = await signingService.SignData(JsonSerializer.Serialize(geometricData)); + if (success) + { + geometricData.Approval = new ResponseApproval(DateTime.Now, signature, signingService.GetFingerprint(), "", ""); + } context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index 1ee1d47e..bd2cef08 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -6,5 +6,7 @@ public interface ISigningService { public Task ImportPrivateKey(); - public Task SignData(string data); + public string GetFingerprint(); + + public Task<(bool, string)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d1f0ad59..f4849fd1 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -10,12 +10,10 @@ public class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { - //Export private subkey: gpg --output private.pgp --armor --export-secret-key C97074097892B88EF6D23BA9E5D03DA76EDC407D private string _fingerprint = ""; public async Task ImportPrivateKey() { - //await ExecuteCommand(); var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { @@ -23,30 +21,38 @@ public async Task ImportPrivateKey() if (!String.IsNullOrEmpty(_fingerprint)) { logger.LogDebug("GNUPG Fingerprint: {Fingerprint}", _fingerprint); - return true; + } + else + { + logger.LogError("GNUPG Error: Failed to get fingerprint."); + success = false; } } - return false; + return success; } - public async Task SignData(string data) + public string GetFingerprint() + { + return _fingerprint; + } + + public async Task<(bool, string)> SignData(string data) { - //await ExecuteCommand(); - //await ExecuteGnuCommand($"echo $(tty)"); var filepath = WriteDataToFile(data); - await ExecuteGnuCommand($"echo $(tty)"); - await ExecuteGnuCommand($"echo $GPG_TTY"); - await ExecuteGnuCommand($"gpgconf --list-dirs"); - // gpg --detach-sig --armor --local-user response.json - //var (success, outout) = await ExecuteGnuCommand($"gpg --output {Path.GetFileName(filepath)}.asc --detach-sig --armor {Path.GetFileName(filepath)}"); - //var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); - await ExecuteGnuCommand($"echo \"test\" | gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign"); - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase Freiburg2005 --clearsign {Path.GetFileName(filepath)}"); + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); + string signature = ""; - var signature = ReadSignatureFromFile(filepath); - logger.LogDebug("GNUPG Signature: {Signature}", signature); - //RemoveFiles(filepath); - return success; + if (success) + { + success = ReadSignatureFromFile(filepath, out signature); + logger.LogDebug("GNUPG Signature: {Signature}", signature); + } + else + { + logger.LogError("GNUPG Error: {Outout}", outout); + } + RemoveFiles(filepath); + return (success, signature); } private async Task<(bool, string)> ExecuteGnuCommand(string command) @@ -61,10 +67,6 @@ public async Task SignData(string data) CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, - //WorkingDirectory = "/home/me/app/src/", - //RedirectStandardInput = true, - //UseShellExecute = false, - //UserName = "me" }; var proc = Process.Start(startInfo); @@ -87,13 +89,6 @@ public async Task SignData(string data) private async Task GetFingerprint() { - //gpg--list - keys--with - subkey - fingerprint--with - colon - //tru::1:1740060571:0:3:1:5 - //pub: -:2048:17:AF86639859F25224: 1739973559:1803045559::-:::scSC::::::23::0: - //fpr:::::::::8CC878D9614150C8278C6F0FAF86639859F25224: - //uid: -::::1739973559::90476D25C295C40CFD4D89EAB1BF3C879E39165B::James Bremer::::::::::0: - //sub: -:2048:17:E5D03DA76EDC407D: 1739973625:1803045625:::::s::::::23: - //fpr:::::::::C97074097892B88EF6D23BA9E5D03DA76EDC407D: string fingerprint = ""; var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); if (success) @@ -116,14 +111,21 @@ private static string WriteDataToFile(string data) return docPath; } - private static string ReadSignatureFromFile(string filepath) + private static bool ReadSignatureFromFile(string filepath, out string signature) { - string signature = ""; - using (StreamReader file = new StreamReader(filepath + ".asc")) + try + { + using (StreamReader file = new StreamReader(filepath + ".asc")) + { + signature = file.ReadToEnd(); + } + return true; + } + catch { - signature = file.ReadToEnd(); + signature = ""; + return false; } - return signature; } private static void RemoveFiles(string filepath) diff --git a/docker-compose.yml b/docker-compose.yml index 6435d819..1d50f832 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,7 +117,8 @@ services: - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILENAME} + - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose From 5e29b46d732f07cf87c90764952b947253a2f0c0 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Mar 2025 09:44:59 +0100 Subject: [PATCH 288/878] Cleanup Code --- .env.sample | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 4 +- backend/src/Logging/SigningServiceLogging.cs | 57 ++++++++++++ backend/src/Logging/UserServiceLogging.cs | 12 +++ backend/src/Program.cs | 6 +- backend/src/Services/DataService.cs | 4 + backend/src/Services/EmailSender.cs | 6 +- backend/src/Services/IDataService.cs | 10 +++ backend/src/Services/ISigningService.cs | 18 +++- backend/src/Services/IUserService.cs | 9 ++ backend/src/Services/SigningService.cs | 87 +++++++++++++------ backend/src/Services/UserService.cs | 18 +++- docker-compose.yml | 2 +- 13 files changed, 195 insertions(+), 40 deletions(-) create mode 100644 backend/src/Logging/SigningServiceLogging.cs create mode 100644 backend/src/Logging/UserServiceLogging.cs diff --git a/.env.sample b/.env.sample index 4dfc424b..9d9454d3 100644 --- a/.env.sample +++ b/.env.sample @@ -17,7 +17,7 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_PRIVATEKEY_FILENAME=private.gpg +GNUPG_PRIVATEKEY_FILE_NAME=private.gpg GNUPG_PRIVATEKEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 515ff97f..70cd209e 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -116,12 +116,14 @@ input.RootResource.AppliedConversionMethod is null ) ); geometricData.Resources.Add(resource); + var (success, signature) = await signingService.SignData(JsonSerializer.Serialize(geometricData)); if (success) { geometricData.Approval = new ResponseApproval(DateTime.Now, signature, signingService.GetFingerprint(), "", ""); } - context.GeometricData.Add(geometricData); + + //context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); } diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs new file mode 100644 index 00000000..b8d8cb37 --- /dev/null +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -0,0 +1,57 @@ +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class SigningServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Fingerprint: {Fingerprint}")] + public static partial void Fingerprint(this ILogger logger, string fingerprint); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Failed to get fingerprint.")] + public static partial void FingerprintError(this ILogger logger); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Signature: {Signature}")] + public static partial void Signature(this ILogger logger, string signature); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Failed to create signature: {Output}")] + public static partial void SignatureError(this ILogger logger, string output); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command: {Command}")] + public static partial void ExecuteCommand(this ILogger logger, string command); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG Execute Command: {Error}")] + public static partial void ExecuteCommandError(this ILogger logger, string error); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Output: {Output}")] + public static partial void ExecuteCommandOutput(this ILogger logger, string output); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Result: {result}")] + public static partial void ExecuteCommandResult(this ILogger logger, int result); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Write file exception: {Exception}")] + public static partial void WriteFileException(this ILogger logger, string exception); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG: Read file exception: {Exception}")] + public static partial void ReadFileException(this ILogger logger, string exception); +} \ No newline at end of file diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs new file mode 100644 index 00000000..24ddb7c2 --- /dev/null +++ b/backend/src/Logging/UserServiceLogging.cs @@ -0,0 +1,12 @@ +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class UserServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Extracted Bearer Token: {Token}")] + public static partial void ExtractedToken(this ILogger logger, string? token); +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index d5623936..ab6be9f0 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -58,7 +58,7 @@ string[] commandLineArguments { // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); - await ImportGnuPGKey(scope.ServiceProvider).ConfigureAwait(false); + await InitializeSigningService(scope.ServiceProvider).ConfigureAwait(false); } await application.RunAsync().ConfigureAwait(false); @@ -127,12 +127,12 @@ IServiceProvider services } } - private static async Task ImportGnuPGKey( + private static async Task InitializeSigningService( IServiceProvider services ) { var signing = services.GetRequiredService(); - await signing.ImportPrivateKey(); + await signing.Initialize(); } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 663a2451..afc5f91f 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -7,8 +7,12 @@ namespace Database.Services; +/// +/// Implementation of +/// public class DataService() : IDataService { + /// public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 6717d9ed..27af95ec 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Logging; @@ -13,11 +13,11 @@ public static partial class Log Level = LogLevel.Debug, Message = "About to send email to `{Recipient}` with subject `{Subject}` and body `{Body}`")] public static partial void AboutToSendEmail( - this ILogger logger, + this ILogger logger, (string name, string address) Recipient, string Subject, string Body - ); + ); } public sealed class EmailSender diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/IDataService.cs index cd4e9945..6800ec47 100644 --- a/backend/src/Services/IDataService.cs +++ b/backend/src/Services/IDataService.cs @@ -5,7 +5,17 @@ namespace Database.Services; +/// +/// Service to get data from +/// public interface IDataService { + /// + /// Get dataset with passed id from all datasets + /// + /// Id of dataset to get. + /// + /// + /// Dataset with passed id or null. public Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index bd2cef08..6acec2b1 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -2,11 +2,27 @@ namespace Database.Services; +/// +/// Service to sign data string with GNUPG. +/// public interface ISigningService { - public Task ImportPrivateKey(); + /// + /// Initialize service by importing private key from file passed into container. + /// + /// True, if privat key was successfully imported and fingerprint set. + public Task Initialize(); + /// + /// Get fingerprint + /// + /// Fingerprint as string public string GetFingerprint(); + /// + /// Sign passed data string and return signature. + /// + /// Data string to create signature from. + /// True and generated signature, if successful. Otherwise false. public Task<(bool, string)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index 386f3d77..5659baaa 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -5,7 +5,16 @@ namespace Database.Services; +/// +/// Service to get current user from Metabase +/// public interface IUserService { + /// + /// Get current user from Mewtabase by extraxting token from . + /// + /// + /// + /// public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index f4849fd1..72273a0a 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -2,62 +2,84 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; +using Database.Logging; using Microsoft.Extensions.Logging; namespace Database.Services; +/// +/// Implementation of +/// +/// +/// Instance of public class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { private string _fingerprint = ""; + private const string FILENAME = "dataToSign"; - public async Task ImportPrivateKey() + /// + public async Task Initialize() { - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase Freiburg2005 --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); + // Execute command to import private key + var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { - _fingerprint = await GetFingerprint(); + // Extract fingerprint + _fingerprint = await GetFingerprintFromKeyList(); if (!String.IsNullOrEmpty(_fingerprint)) { - logger.LogDebug("GNUPG Fingerprint: {Fingerprint}", _fingerprint); + logger.Fingerprint(_fingerprint); } else { - logger.LogError("GNUPG Error: Failed to get fingerprint."); + logger.FingerprintError(); success = false; } } return success; } + /// public string GetFingerprint() { return _fingerprint; } + /// public async Task<(bool, string)> SignData(string data) { - var filepath = WriteDataToFile(data); - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {Path.GetFileName(filepath)}"); string signature = ""; + // Write data string to file + if (!WriteDataToFile(data)) + { + return (false, ""); + } + + // Execute command to generate detached signature file Creates file with .asc ending + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {FILENAME}"); + if (success) { - success = ReadSignatureFromFile(filepath, out signature); - logger.LogDebug("GNUPG Signature: {Signature}", signature); + // Read signature from file + success = ReadSignatureFromFile(out signature); + logger.Signature(signature); } else { - logger.LogError("GNUPG Error: {Outout}", outout); + logger.SignatureError(outout); } - RemoveFiles(filepath); + + // Remove created files + RemoveFiles(); return (success, signature); } private async Task<(bool, string)> ExecuteGnuCommand(string command) { - logger.LogDebug("GNUPG: Execute command. ({Command})", command); + logger.ExecuteCommand(command); var escapedArgs = command.Replace("\"", "\\\""); ProcessStartInfo startInfo = new() @@ -80,16 +102,18 @@ public string GetFingerprint() } else if (!string.IsNullOrEmpty(error)) { - logger.LogDebug("GNUPG Error: {Error}", error); + logger.ExecuteCommandError(error); } - logger.LogDebug("GNUPG Output: {Output}", output); - logger.LogDebug("GNUPG ExitCode: {ExitCode}", proc.ExitCode); + logger.ExecuteCommandOutput(output); + logger.ExecuteCommandResult(proc.ExitCode); return (proc.ExitCode == 0, output); } - private async Task GetFingerprint() + private async Task GetFingerprintFromKeyList() { string fingerprint = ""; + + // Execute command to list keys with fingerprint, use 'with-colon' to split later var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); if (success) { @@ -99,38 +123,45 @@ private async Task GetFingerprint() return fingerprint; } - private static string WriteDataToFile(string data) + private bool WriteDataToFile(string data) { - string docPath = Path.Combine(Environment.CurrentDirectory, "dataToSign.json"); + try + { + using (StreamWriter outputFile = new StreamWriter(FILENAME)) + { + outputFile.WriteLine(data); + } - using (StreamWriter outputFile = new StreamWriter(docPath)) + return true; + } + catch (Exception ex) { - outputFile.WriteLine(data); + logger.WriteFileException(ex.ToString()); + return false; } - - return docPath; } - private static bool ReadSignatureFromFile(string filepath, out string signature) + private bool ReadSignatureFromFile(out string signature) { try { - using (StreamReader file = new StreamReader(filepath + ".asc")) + using (StreamReader file = new StreamReader(FILENAME + ".asc")) { signature = file.ReadToEnd(); } return true; } - catch + catch (Exception ex) { + logger.ReadFileException(ex.ToString()); signature = ""; return false; } } - private static void RemoveFiles(string filepath) + private static void RemoveFiles() { - File.Delete(filepath); - File.Delete(filepath + ".asc"); + File.Delete(FILENAME); + File.Delete(FILENAME + ".asc"); } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 9bc5ca00..49fa45f5 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -3,32 +3,46 @@ using System.Threading; using System.Threading.Tasks; using Database.Extensions; +using Database.Logging; using Database.Metabase; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; namespace Database.Services; +/// +/// Implementation of +/// +/// +/// +/// to store already known users. public class UserService( AppSettings appSettings, IHttpClientFactory httpClientFactory, - IMemoryCache cache) : IUserService + IMemoryCache cache, + ILogger logger) : IUserService { + /// public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { + // Extract token var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); + logger.ExtractedToken(token); if (token == null) { return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); } + // Check if there is already a user for token if (!cache.TryGetValue(token, out CurrentUser? cacheUser)) { + // Get user from Metabase cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - + // Store user in cache cache.Set(token, cacheUser, cacheEntryOptions); } diff --git a/docker-compose.yml b/docker-compose.yml index 1d50f832..a8762d98 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,7 +117,7 @@ services: - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILENAME} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true From 5fb7505b3c549fb91670603326180ee9fa22148e Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Mar 2025 09:47:15 +0100 Subject: [PATCH 289/878] Fix missing authorization --- .../src/Controllers/FileUploadController.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7028c096..53efb8e4 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -6,18 +6,18 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Database.Data; using Database.Authorization; +using Database.Data; using Database.Filters; +using Database.Services; using Database.Utilities; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -using Microsoft.AspNetCore.Http; -using Database.Services; namespace Database.Controllers; @@ -139,12 +139,12 @@ CancellationToken cancellationToken } if (!FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( currentUser, - getHttpsResource.Data.CreatorId, - _appSettings, - _httpClientFactory, - _httpContextAccessor, - cancellationToken - ) + getHttpsResource.Data.CreatorId, + _appSettings, + _httpClientFactory, + _httpContextAccessor, + cancellationToken + ) ) { return Unauthorized(); From c07d436c2aa5d0d9ce68804164baa2b583a0a90e Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Mar 2025 13:05:52 +0100 Subject: [PATCH 290/878] Implement access rights --- backend/src/Data/AccessRights.cs | 24 ++++++++++++++++++- backend/src/Data/ApplicationDbContext.cs | 5 ++++ .../GraphQl/AccessRights/AccessRightsInput.cs | 8 ++++++- .../AccessRights/DataAccessRightsMutations.cs | 23 ++++++++++++++++-- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/AccessRights.cs index 93b51880..b087e88d 100644 --- a/backend/src/Data/AccessRights.cs +++ b/backend/src/Data/AccessRights.cs @@ -1,6 +1,28 @@ -namespace Database.Data; +using System; +using System.Collections.Generic; + +namespace Database.Data; public class AccessRights : Entity { + public Guid InstitutionId { get; set; } + public int AllowedUserCount { get; set; } + public List UserOfInstitution { get; set; } + public int AllowedDatasetsPerTime { get; set; } + public TimeSpan Period { get; set; } + + public AccessRights( + Guid institutionId, + int allowedUserCount, + int allowedDatasetsPerTime, + TimeSpan period + ) + { + InstitutionId = institutionId; + AllowedUserCount = allowedUserCount; + AllowedDatasetsPerTime = allowedDatasetsPerTime; + Period = period; + UserOfInstitution = new List(); + } } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b06285d7..79de0060 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -41,6 +41,7 @@ DbContextOptions options public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; public DbSet GeometricData { get; private set; } = default!; + public DbSet AccessRights { get; private set; } = default!; private static void CreateEnumerations(ModelBuilder builder, string schemaName) { @@ -123,5 +124,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() ) .ToTable("geometric_data"); + ConfigureEntity( + modelBuilder.Entity() + ) + .ToTable("access_rights"); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs index 35bd7003..946879e5 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs @@ -1,5 +1,11 @@ -namespace Database.GraphQl.AccessRights; +using System; + +namespace Database.GraphQl.AccessRights; public sealed record AccessRightsInput ( + Guid InstitutionId, + int AllowedUserCount, + int AllowedUserPerTimeSpan, + TimeSpan Period ); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index d2df4eb9..02680b3c 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -5,6 +5,7 @@ using Database.Services; using HotChocolate.Types; using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessRights; @@ -104,7 +105,7 @@ CancellationToken cancellationToken ); } - //if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( + //if (!CommonAuthorization.( // currentUser, // input.CreatorId, // appSettings, @@ -121,7 +122,25 @@ CancellationToken cancellationToken // ) // ); + var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + + if (accessRights is not null) + { + accessRights.AllowedUserCount = input.AllowedUserCount; + accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; + accessRights.Period = input.Period; + } + else + { + accessRights = new Data.AccessRights( + input.InstitutionId, + input.AllowedUserCount, + input.AllowedDatasetsPerTimeSpan, + input.Period); + context.AccessRights.Add(accessRights); + } + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new AddAccessRightsPayload(data.DataAccessRights); + return new AddAccessRightsPayload(accessRights); } } \ No newline at end of file From 9b9d36a1b07ae97787f0bfbf762bd848ced4f01e Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 10 Mar 2025 13:07:16 +0100 Subject: [PATCH 291/878] Add filtering inline --- backend/src/GraphQl/AccessRights/AccessRightsInput.cs | 7 ++++++- .../src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs index 946879e5..5d6d200e 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs @@ -4,8 +4,13 @@ namespace Database.GraphQl.AccessRights; public sealed record AccessRightsInput ( + // Id of institution Guid InstitutionId, + // Count of allowed user for institution + // -1 = unlimited 0 = no access int AllowedUserCount, - int AllowedUserPerTimeSpan, + // Count of allowed datasets for institution + // -1 = unlimited 0 = no access + int AllowedDatasetsPerTimeSpan, TimeSpan Period ); \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 13b4f766..06d6ad96 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -9,6 +9,7 @@ using HotChocolate.Types; using Database.Data; using Database.GraphQl.Extensions; +using HotChocolate.Resolvers; namespace Database.GraphQl.GeometricDataX; @@ -16,17 +17,19 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataQueries { [UsePaging] - [UseFiltering] + [UseFiltering(typeof(GeometricDataFilterType))] [UseSorting] public IQueryable GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, - ISortingContext sorting + ISortingContext sorting, + IResolverContext resolverContext ) { sorting.StabilizeOrder(); + IQueryable filteredData = context.GeometricData.Filter(resolverContext); // TODO Use `locale`. - return context.GeometricData.AsNoTracking(); + return filteredData; } public Task GetGeometricDataAsync( From 39385add43c949ec55632788b9a706c6c0ac7b1d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 10 Mar 2025 17:34:43 +0100 Subject: [PATCH 292/878] Update tooling, in particular, to update `dotnet-sos` to fix the security vulnerability reported in https://github.com/dotnet/announcements/issues/346 --- backend/dotnet-tools.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 4067cb2b..46d3e302 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -10,21 +10,21 @@ "rollForward": false }, "dotnet-counters": { - "version": "9.0.553101", + "version": "9.0.607501", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "9.0.553101", + "version": "9.0.607501", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "9.0.1", + "version": "9.0.2", "commands": [ "dotnet-ef" ], @@ -38,35 +38,35 @@ "rollForward": false }, "dotnet-trace": { - "version": "9.0.553101", + "version": "9.0.607501", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "9.0.553101", + "version": "9.0.607501", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "9.0.553101", + "version": "9.0.607501", "commands": [ "dotnet-sos" ], "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.9.3", + "version": "0.10.1", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.3.4", + "version": "2024.3.6", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "14.3.0", + "version": "15.0.3", "commands": [ "dotnet-graphql" ], From fe3c84abbb3e5ee7e72df1b4ac732f0f25267625 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Mar 2025 16:32:05 +0100 Subject: [PATCH 293/878] Upgrade to .NET 9.0.3 as it fixes the security vulnerability reported in https://github.com/dotnet/announcements/issues/348 --- backend/src/Database.csproj | 14 +++++++------- backend/test/Database.Tests.csproj | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index cb88458a..132152ad 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -32,15 +32,15 @@ - - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -51,7 +51,7 @@ - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 7c2e70d6..aa5b2a7f 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -19,7 +19,7 @@ - + From f8ea45771b2e26d7b57ee1bf91134961bbdb7ea7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Mar 2025 18:14:11 +0100 Subject: [PATCH 294/878] Document production environment variables, add a troubleshooting section, and explain how to update SQL fields manually --- README.md | 121 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 73e6c4bd..c776f4f5 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ On the very first usage: [Remote Development](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack). 1. Navigate to the [Remote Explorer](https://code.visualstudio.com/docs/devcontainers/containers#_managing-containers) - pane. Hover over the running `metabase-backend-*` container (if it is not + pane. Hover over the running `database-backend-*` container (if it is not running, then run `make up` in a shell inside the project directory) and click on the "Attach in Current Window" icon. In the Explorer pane, open the directory `/app`, which is mounted to the host's `./backend` directory. @@ -91,7 +91,7 @@ On the very first usage: [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens). 1. Navigate to the [Remote Explorer](https://code.visualstudio.com/docs/devcontainers/containers#_managing-containers) - pane. Hover over the running `metabase-frontend-*` container and click on + pane. Hover over the running `database-frontend-*` container and click on the "Attach in New Window" icon. In the Explorer pane, open the directory `/app`, which is mounted to the host's `./frontend` directory. Navigate to the Extensions pane. Add the extensions @@ -119,10 +119,10 @@ debugging To debug the [ASP.NET Core web application](https://learn.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core), -attach Visual Studio Code to the `metabase-backend-*` container, +attach Visual Studio Code to the `database-backend-*` container, [press `Ctrl+Shift+P`, select "Debug: Attach to a .NET 5+ or .NET Core process"](https://code.visualstudio.com/docs/csharp/debugging#_attaching-to-a-process), -and choose the process `/app/src/bin/Debug/net9.0/Metabase run` titled -`Metabase` or alternatively navigate to the "Run and Debug" pane +and choose the process `/app/src/bin/Debug/net9.0/Database run` titled +`Database` or alternatively navigate to the "Run and Debug" pane (`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the "Start Debugging" icon (`F5`), and select the same process as above. Then, for example, open some source files to set breakpoints, navigate through the @@ -145,7 +145,47 @@ and that attempt to solve that. Those extensions don't work in our case though, as they try to restart `dotnet watch` themselves, instead of waiting for the polling file watcher of `dotnet watch` to restart -`/app/src/bin/Debug/net9.0/Metabase run` and attach to that process. +`/app/src/bin/Debug/net9.0/Database run` and attach to that process. + +### Troubleshooting + +After migrating the PostgreSQL database or changing the `database` schema +manually or upgrading Npgsql, the service `backend` may throw exceptions +regarding the object-relational mapping (Npgsql or EF Core). In that case it +may be necessary to restart the service `backend`, for example, by running +`make down up` and it may even be necessary recreate the database from scratch +by running `make down remove-data up`. Note that the latter will remove all +data from PostgreSQL, recreate the database and its schema, and seed it +freshly. + +After changing the domain model in `./backend/src/data`, you probably need to +migrate the database by dropping into `make shellb`, adding a migration `make +NAME=${MIGRATION_NAME} add-migration`, generating a migration script `make +FROM=${PREVIOUS_MIGRATION} TO=${NEW_MIGRATION} generate-migration-script`, and +executing it `make SQL=${SCRIPT_PATH} sql`. + +When your hard-disk starts to grow full, it may be the case that Docker does +not clean-up anonymous volumes properly. You can do so manually by running +`docker system prune` potentially with the argument `--all`. Note that this may +result in loss of data. + +When the `frontend` Docker image does not build in production because of an +unused import in an automatically generated file, for example, one in the +directory `./frontend/__generated__`, then **temporarily** ignore TypeScript +build errors by adding the following lines to `./frontend/next.config.js`, for +example with `vi` or `nano` in a shell on the deployment machine: + +``` +typescript: { + ignoreBuildErrors: true, +}, +``` + +The same can happen in development when running `make build` (or `yarn run +build`) in the shell entered by `make shellf`. In that case, remove the +offending import manually in the file and try again. Do not disable build +errors in development because when you do so, build errors in non-generated +files may leak into the code base. ## Deployment @@ -169,9 +209,37 @@ and the pages following it. `${environment}` below: 1. Change into the clone `${environment}` by running `cd /app/${environment}`. 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env`, - `cp ./frontend/.env.local.sample ./frontend/.env.local`, and by replacing - dummy passwords in the copies by newly generated ones, where random - passwords may be generated running `openssl rand -base64 32`. + `cp ./frontend/.env.local.sample ./frontend/.env.local`, and by adjusting + variable values in the copies to your needs, in particular, by setting + passwords to newly generated ones, where random passwords may be + generated by running `openssl rand -base64 32`. Here is some information + on what the variables mean + - `NAME` is the name Docker project name, in particular, it is the prefix + of the Docker container names listed by `docker ps --all`; + - `HOST` is the domain name with sub-domain of the deployment, in + particular, it is used to make resource locators absolute; + - `HTTP_PORT` is the HTTP port on which the reverse proxy NGINX listens + for requests; + - `METABASE_HOST` is the domain name with sub-domain of the metabase, in + particular, to use it as OpenId Connect provider and to ask it for + information about logged-in users needed for authorization; + - `DATABASE_ID` is the UUID that was assigned to this product-data + database upon registering it at the metabase; + - `VERIFICATION_CODE` is the verification code that was generated for + this product-data database upon registering it at the metabase; + - `OPEN_ID_CONNECT_CLIENT_SECRET` is the OpenId Connect client secret of + this product-data database as a client of the metabase acting as + identity provider (the client secret is given when registering an + OpenId Connect client at the metabase); + - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and + `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to + encrypt and sign JSON web tokens (JWT) used by OpenId Connect; + - `SMTP_HOST` and `SMTP_PORT` are host and port of the message transfer + agent to be used to send emails through the Simple Mail Transfer + Protocol (SMTP); + - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are + host and port of the message transfer agent and a list of allowed + email addresses to send emails to even in the staging environment. 1. Prepare PostgreSQL by generating new password files by running `make --file=Makefile.production postgres_passwords` and creating the database by running @@ -275,11 +343,11 @@ If the database container restarts indefinitely and its logs say PANIC: could not locate a valid checkpoint record ``` -for example preceded by `LOG: invalid resource manager ID in primary -checkpoint record` or `LOG: invalid primary checkpoint record`, then the -database is corrupt. For example, the write-ahead log (WAL) may be corrupt -because the database was not shut down cleanly. One solution is to restore the -database from a backup by running +for example preceded by `LOG: invalid resource manager ID in primary checkpoint +record` or `LOG: invalid primary checkpoint record`, then the database is +corrupt. For example, the write-ahead log (WAL) may be corrupt because the +database was not shut down cleanly. One solution is to restore the database +from a backup by running ``` make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/20XX-XX-XX_XX_XX_XX/ restore @@ -289,7 +357,7 @@ where the `X`s need to be replaced by proper values. Another solution is to reset the transaction log by entering the database container with ``` -docker compose --file=docker-compose.production.yml --project-name metabase_production run database bash +docker compose --file=docker-compose.production.yml --project-name database_production run database bash ``` and dry-running @@ -306,6 +374,29 @@ gosu postgres pg_resetwal /var/lib/postgresql/data Note that both solutions may cause data to be lost. +#### Update a SQL field manually + +If one field in the SQL database needs to be updated and there is no GraphQL +mutation available, then you may update it in PostgreSQL directly as +illustrated in the following example. Test it in the `staging` environment +under /app/staging before doing it in `production` under /app/production. + +1. Drop into a shell on the server as user `cloud` by running + `ssh -CvX -A cloud@IpAdressOfCloudServer`. +1. Navigate to the production environment by running `cd /app/production`. +1. Make a database backup by running `DATE=$(date +"%Y-%m-%d_%H_%M_%S")` and + `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/${DATE} backup` +1. Navigate to the staging environment by running `cd /app/staging`. +1. Load the backup into the staging database by running + `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/${DATE} restore`. +1. Drop into `psql` by running `make --file=Makefile.production psql`. +1. List all tables in the schema `database` by running `\dt database.*`. +1. List all optical data records by running `select * from + database.optical_data;` and remember for example one identifier of a record + that you want to update. +1. Update a single field by running `update database.optical_data set "Description" = '...' where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. +1. Delete a faulty record by running `delete from database.optical_data where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. + ## Original Idea The product identifier service should provide the following endpoints: From 3c07318d9322b316f0da066343ef3026034f57d9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Mar 2025 19:12:43 +0100 Subject: [PATCH 295/878] Mention the user and group ID problem --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c776f4f5..f386ba2e 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,19 @@ In another shell 1. Drop into `bash` with the working directory `/app`, which is mounted to the host's `./backend` directory, inside a fresh Docker container based on `./backend/Dockerfile` by running `make shellb`. If necessary, the Docker - image is (re)built automatically, which takes a while the first time. + image is (re)built automatically, which takes a while the first time. Note + that the Docker image and containers try to use the same user and group IDs + as the ones on the host machine. This has the upside that files created + within containers in mounted directories are owned by the host user. It has + the downside that the Docker image may fail to build because the IDs may + already be taken by other users and groups in the base image. This happens + for example if you are `root` on the host machine with the user and group + IDs 0. If there is an ID collision, then you can either change the user and + group ID on the host machine (for example by logging in as another user) or + you can replace all occurrences of `shell id --group` and `shell id --user` + in `Makefile` and `Makefile.production` by fixed non-colliding IDs like + 1000. If you know a better way, please + [let use know on GitHub](https://github.com/building-envelope-data/database/issues/new). 1. List all backend GNU Make targets by running `make help`. 1. For example, update packages and tools by running `make update`. 1. Drop out of the container by running `exit` or pressing `Ctrl-D`. From a0454c1dbb8ac1a4e80a7c06bf381df9c639dc7f Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 17 Mar 2025 12:11:50 +0100 Subject: [PATCH 296/878] Add update method for access rights --- .../AccessRights/AccessRightsPayload.cs | 29 ++++--- .../AccessRights/AddAccessRightsErrorCode.cs | 9 ++- .../AddDataAccessRightsErrorCode.cs | 5 +- .../AddDataAccessRightsPayload.cs | 2 +- .../AccessRights/DataAccessRightsMutations.cs | 75 ++++++++++++++++++- .../AccessRights/DataAccessRightsPayload.cs | 53 +++++++++++++ .../GraphQl/Approvals/AddApprovalErrorCode.cs | 5 +- .../GeometricDataX/GeometricDataQueries.cs | 5 +- 8 files changed, 158 insertions(+), 25 deletions(-) create mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs diff --git a/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs index 032a1026..74365f78 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs @@ -1,53 +1,52 @@ using System.Collections.Generic; -using Database.Data; namespace Database.GraphQl.AccessRights; -public class AccessRightsPayload +public class AccessRightsPayload : Payload - where TDataAccessRightsError : IUserError + where TAccessRightsError : IUserError { protected AccessRightsPayload( - DataAccessRights dataAccessRights + Data.AccessRights accessRights ) { - DataAccessRights = dataAccessRights; + AccessRights = accessRights; } protected AccessRightsPayload( - IReadOnlyCollection errors + IReadOnlyCollection errors ) { Errors = errors; } protected AccessRightsPayload( - TDataAccessRightsError error + TAccessRightsError error ) : this(new[] { error }) { } protected AccessRightsPayload( - DataAccessRights dataAccessRights, - IReadOnlyCollection errors + Data.AccessRights accessRights, + IReadOnlyCollection errors ) { - DataAccessRights = dataAccessRights; + AccessRights = accessRights; Errors = errors; } protected AccessRightsPayload( - DataAccessRights dataAccessRights, - TDataAccessRightsError error + Data.AccessRights accessRights, + TAccessRightsError error ) : this( - dataAccessRights, + accessRights, new[] { error } ) { } - public DataAccessRights? DataAccessRights { get; } - public IReadOnlyCollection? Errors { get; } + public Data.AccessRights? AccessRights { get; } + public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs index 765eae29..56ad8e30 100644 --- a/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs @@ -1,8 +1,13 @@ -namespace Database.GraphQl.AccessRights; +using System.Diagnostics.CodeAnalysis; +namespace Database.GraphQl.AccessRights; + +[SuppressMessage("Naming", "CA1707")] public enum AddAccessRightsErrorCode { UNKNOWN, UNAUTHENTICATED, - UNAUTHORIZED + UNAUTHORIZED, + ALREADY_EXISTS, + UNKNOWN_ACCESSRIGHTS } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs index b7cef8fb..57e0cf22 100644 --- a/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs @@ -1,5 +1,8 @@ -namespace Database.GraphQl.AccessRights; +using System.Diagnostics.CodeAnalysis; +namespace Database.GraphQl.AccessRights; + +[SuppressMessage("Naming", "CA1707")] public enum AddDataAccessRightsErrorCode { UNKNOWN, diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs index 82026378..acf790ab 100644 --- a/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.AccessRights; public class AddDataAccessRightsPayload - : AccessRightsPayload + : DataAccessRightsPayload { public AddDataAccessRightsPayload( DataAccessRights dataAccessRights diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 02680b3c..89732a37 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -126,9 +126,13 @@ CancellationToken cancellationToken if (accessRights is not null) { - accessRights.AllowedUserCount = input.AllowedUserCount; - accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = input.Period; + return new AddAccessRightsPayload( + new AddAccessRightsError( + AddAccessRightsErrorCode.ALREADY_EXISTS, + $"The access rights for this institution already exist.", + [] + ) + ); } else { @@ -143,4 +147,69 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new AddAccessRightsPayload(accessRights); } + + public async Task UpdateAccessRightsAsync( + AccessRightsInput input, + ApplicationDbContext context, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IUserService userService, + IDataService dataService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new AddAccessRightsPayload( + new AddAccessRightsError( + AddAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + //if (!CommonAuthorization.( + // currentUser, + // input.CreatorId, + // appSettings, + // httpClientFactory, + // httpContextAccessor, + // cancellationToken + // ) + //) + // return new AddDataAccessRightsPayload( + // new AddDataAccessRightsError( + // AddDataAccessRightsErrorCode.UNAUTHORIZED, + // $"The current user is not authorized to approval for the institution.", + // [] + // ) + // ); + + var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + + if (accessRights is not null) + { + accessRights.AllowedUserCount = input.AllowedUserCount; + accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; + accessRights.Period = input.Period; + } + else + { + return new AddAccessRightsPayload( + new AddAccessRightsError( + AddAccessRightsErrorCode.UNKNOWN_ACCESSRIGHTS, + $"There are no access rights for this institution.", + [] + ) + ); + } + + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new AddAccessRightsPayload(accessRights); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs new file mode 100644 index 00000000..9e59cea7 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs @@ -0,0 +1,53 @@ +using Database.Data; +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public class DataAccessRightsPayload + : Payload + where TDataAccessRightsError : IUserError +{ + protected DataAccessRightsPayload( + DataAccessRights dataAccessRights + ) + { + DataAccessRights = dataAccessRights; + } + + protected DataAccessRightsPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected DataAccessRightsPayload( + TDataAccessRightsError error + ) + : this(new[] { error }) + { + } + + protected DataAccessRightsPayload( + DataAccessRights dataAccessRights, + IReadOnlyCollection errors + ) + { + DataAccessRights = dataAccessRights; + Errors = errors; + } + + protected DataAccessRightsPayload( + DataAccessRights dataAccessRights, + TDataAccessRightsError error + ) + : this( + dataAccessRights, + new[] { error } + ) + { + } + + public DataAccessRights? DataAccessRights { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs index 8a82e333..52f5dc2a 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs @@ -1,5 +1,8 @@ -namespace Database.GraphQl.Approvals; +using System.Diagnostics.CodeAnalysis; +namespace Database.GraphQl.Approvals; + +[SuppressMessage("Naming", "CA1707")] public enum AddApprovalErrorCode { UNKNOWN, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 06d6ad96..e8ea1f77 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -27,7 +27,8 @@ IResolverContext resolverContext ) { sorting.StabilizeOrder(); - IQueryable filteredData = context.GeometricData.Filter(resolverContext); + IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + // TODO Use `locale`. return filteredData; } From 662b348ac7ff0afac48c1e614d6d12eb245aa684 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 12 Mar 2025 13:35:26 +0100 Subject: [PATCH 297/878] Moved and reworked Metabase calls to ApiRequestService --- .../ApiRequestService.cs} | 68 ++++------ backend/src/ApiRequest/DatabaseApi.cs | 123 ++++++++++++++++++ .../Dto/CurrentUserDto.cs} | 4 +- backend/src/ApiRequest/Dto/DatabaseDto.cs | 26 ++++ .../src/ApiRequest/Dto/DatabaseRequestDto.cs | 10 ++ .../Dto/UserInfoDto.cs} | 6 +- .../src/ApiRequest/JsonSerializerOptions.cs | 36 +++++ .../Queries/CurrentUser.graphql | 0 .../Queries/Databases.graphql | 0 .../Queries/UpdateDatabase.graphql | 2 +- backend/src/ApiRequest/UserApi.cs | 59 +++++++++ .../CalorimetricDataAuthorization.cs | 4 +- .../src/Authorization/CommonAuthorization.cs | 6 +- .../Authorization/FileUploadAuthorization.cs | 4 +- .../GeometricDataAuthorization.cs | 4 +- .../GetHttpsResourceAuthorization.cs | 4 +- .../HygrothermalDataAuthorization.cs | 4 +- .../Authorization/OpticalDataAuthorization.cs | 4 +- .../PhotovoltaicDataAuthorization.cs | 4 +- .../{GraphQl/Databases => Data}/Database.cs | 19 ++- backend/src/Data/UserInfo.cs | 63 +++++++++ backend/src/GraphQl/Databases/DatabaseEdge.cs | 13 -- .../GraphQl/Databases/DatabaseMutations.cs | 40 +++--- .../src/GraphQl/Databases/DatabasePayload.cs | 6 +- .../GraphQl/Databases/DatabasesConnection.cs | 15 --- .../Databases/UpdateDatabasePayload.cs | 4 +- backend/src/GraphQl/Users/UserQueries.cs | 17 ++- backend/src/Metabase/QueryingCurrentUser.cs | 41 ------ backend/src/Services/IUserService.cs | 4 +- backend/src/Services/UserService.cs | 11 +- backend/src/Startup.cs | 4 +- 31 files changed, 423 insertions(+), 182 deletions(-) rename backend/src/{Metabase/QueryingMetabase.cs => ApiRequest/ApiRequestService.cs} (61%) create mode 100644 backend/src/ApiRequest/DatabaseApi.cs rename backend/src/{Metabase/CurrentUser.cs => ApiRequest/Dto/CurrentUserDto.cs} (88%) create mode 100644 backend/src/ApiRequest/Dto/DatabaseDto.cs create mode 100644 backend/src/ApiRequest/Dto/DatabaseRequestDto.cs rename backend/src/{GraphQl/Users/UserInfo.cs => ApiRequest/Dto/UserInfoDto.cs} (72%) create mode 100644 backend/src/ApiRequest/JsonSerializerOptions.cs rename backend/src/{Metabase => ApiRequest}/Queries/CurrentUser.graphql (100%) rename backend/src/{Metabase => ApiRequest}/Queries/Databases.graphql (100%) rename backend/src/{Metabase => ApiRequest}/Queries/UpdateDatabase.graphql (92%) create mode 100644 backend/src/ApiRequest/UserApi.cs rename backend/src/{GraphQl/Databases => Data}/Database.cs (67%) create mode 100644 backend/src/Data/UserInfo.cs delete mode 100644 backend/src/GraphQl/Databases/DatabaseEdge.cs delete mode 100644 backend/src/GraphQl/Databases/DatabasesConnection.cs delete mode 100644 backend/src/Metabase/QueryingCurrentUser.cs diff --git a/backend/src/Metabase/QueryingMetabase.cs b/backend/src/ApiRequest/ApiRequestService.cs similarity index 61% rename from backend/src/Metabase/QueryingMetabase.cs rename to backend/src/ApiRequest/ApiRequestService.cs index 94f049a2..9696dd3e 100644 --- a/backend/src/Metabase/QueryingMetabase.cs +++ b/backend/src/ApiRequest/ApiRequestService.cs @@ -1,51 +1,35 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Text.Json; -using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; using Database.Extensions; using GraphQL; -using GraphQL.Client.Serializer.SystemTextJson; using Microsoft.AspNetCore.Http; -namespace Database.Metabase; +namespace Database.ApiRequest; -public sealed class QueryingMetabase +public sealed class ApiRequestService { public const string MetabaseHttpClient = "Metabase"; + public const string DatabaseHttpClient = "Database"; + private static bool _useMetabase = true; - private static readonly JsonSerializerOptions GraphQlSerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + public ApiRequestService Metabase() + { + _useMetabase = true; + return this; + } - private static readonly JsonSerializerOptions RestSerializerOptions = - new() - { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + public ApiRequestService Database() + { + _useMetabase = false; + return this; + } public static async Task ConstructGraphQlQuery( string[] fileNames @@ -55,7 +39,7 @@ string[] fileNames Environment.NewLine, await Task.WhenAll( fileNames.Select(fileName => - File.ReadAllTextAsync($"Metabase/Queries/{fileName}") + File.ReadAllTextAsync($"ApiRequest/Queries/{fileName}") ) ).ConfigureAwait(false) ); @@ -72,10 +56,11 @@ CancellationToken cancellationToken { return Query>( HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(appSettings.MetabaseHost), "/graphql/"), + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of + // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(new Uri(_useMetabase ? appSettings.MetabaseHost : appSettings.Host), "/graphql/"), MakeJsonHttpContent(request), - GraphQlSerializerOptions, + JsonSerializerSettings.GraphQL, httpClientFactory, httpContextAccessor, cancellationToken @@ -86,12 +71,13 @@ private static ByteArrayContent MakeJsonHttpContent( TContent content ) { - // For some reason using `JsonContent.Create(content, null, SerializerOptions)` results in status code `BadRequest`. + // For some reason using `JsonContent.Create(content, null, SerializerOptions)` + // results in status code `BadRequest`. var result = new ByteArrayContent( JsonSerializer.SerializeToUtf8Bytes( content, - GraphQlSerializerOptions + JsonSerializerSettings.GraphQL ) ); result.Headers.ContentType = @@ -111,7 +97,7 @@ CancellationToken cancellationToken HttpMethod.Get, uri, null, - RestSerializerOptions, + JsonSerializerSettings.REST, httpClientFactory, httpContextAccessor, cancellationToken @@ -129,7 +115,7 @@ CancellationToken cancellationToken ) where TResponse : class { - using var httpClient = httpClientFactory.CreateClient(MetabaseHttpClient); + using var httpClient = _useMetabase ? httpClientFactory.CreateClient(MetabaseHttpClient) : httpClientFactory.CreateClient(DatabaseHttpClient); var bearerToken = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); using var httpRequestMessage = new HttpRequestMessage( httpMethod, @@ -143,7 +129,9 @@ CancellationToken cancellationToken throw new HttpRequestException( $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); - // We could use `httpResponseMessage.Content.ReadFromJsonAsync>` which would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ + // We could use + // `httpResponseMessage.Content.ReadFromJsonAsync>` which + // would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ using var responseStream = await httpResponseMessage.Content .ReadAsStreamAsync(cancellationToken) diff --git a/backend/src/ApiRequest/DatabaseApi.cs b/backend/src/ApiRequest/DatabaseApi.cs new file mode 100644 index 00000000..6eba59a7 --- /dev/null +++ b/backend/src/ApiRequest/DatabaseApi.cs @@ -0,0 +1,123 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequest.Dto; +using Database.GraphQl.Databases; +using GraphQL; +using HotChocolate; +using HotChocolate.Resolvers; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequest; + +public class DatabaseApi +{ + private static readonly string[] _databaseFileNames = + { + "Databases.graphql" + }; + + private static readonly string[] _updateDatabaseFileNames = + { + "UpdateDatabase.graphql" + }; + + private sealed record DatabasesData(DatabasesConnection Databases); + + public static async Task RequestDatabase( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken) + { + var response = await ApiRequestService.QueryGraphQl( + appSettings, + new GraphQLRequest( + await ApiRequestService.ConstructGraphQlQuery( + _databaseFileNames + ).ConfigureAwait(false), + new + { + where = new + { + locator = new + { + // TODO This is error-prone. + absoluteUri = new + { + equalTo = new Uri(new Uri(appSettings.Host), "/graphql/") + } + } + } + }, + "Databases" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + if (response is null) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NULL_RESPONSE") + .SetPath(resolverContext.Path) + .SetMessage("Response is null.") + .Build() + ); + if (response.Data.Databases.Edges is null) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NULL_EDGES") + .SetPath(resolverContext.Path) + .SetMessage("The supposed list of databases is null.") + .Build() + ); + if (response.Data.Databases.Edges.Count == 0) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NO_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases is empty.") + .Build() + ); + if (response.Data.Databases.Edges.Count >= 2) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("AMBIGUOUS_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases has more than one entry.") + .Build() + ); + + return response.Data.Databases.Edges[0].Node; + } + + public static async Task UpdateDatabase( + DatabaseRequestDto databaseRequest, + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await ApiRequestService.QueryGraphQl( + appSettings, + new GraphQLRequest( + await ApiRequestService.ConstructGraphQlQuery( + _updateDatabaseFileNames + ).ConfigureAwait(false), + new + { + databaseRequest + }, + "UpdateDatabase" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false))?.Data; + } +} \ No newline at end of file diff --git a/backend/src/Metabase/CurrentUser.cs b/backend/src/ApiRequest/Dto/CurrentUserDto.cs similarity index 88% rename from backend/src/Metabase/CurrentUser.cs rename to backend/src/ApiRequest/Dto/CurrentUserDto.cs index ca3916be..065d517e 100644 --- a/backend/src/Metabase/CurrentUser.cs +++ b/backend/src/ApiRequest/Dto/CurrentUserDto.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Database.Metabase; +namespace Database.ApiRequest.Dto; public enum InstitutionRepresentativeRole { @@ -16,7 +16,7 @@ public enum DataSigningPermission REMOVED } -public sealed record CurrentUser( +public sealed record CurrentUserDto( Guid Id, Guid Uuid, RepresentedInstitutions RepresentedInstitutions diff --git a/backend/src/ApiRequest/Dto/DatabaseDto.cs b/backend/src/ApiRequest/Dto/DatabaseDto.cs new file mode 100644 index 00000000..e526b26d --- /dev/null +++ b/backend/src/ApiRequest/Dto/DatabaseDto.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; + +namespace Database.GraphQl.Databases; + +public sealed record DatabaseDto( + + Guid Uuid, + string Name, + string Description, + Uri Locator, + DatabaseVerificationState VerificationState, + string VerificationCode, + + // public Institution? Operator { get; set; } + bool CanCurrentUserUpdateNode, + bool CanCurrentUserVerifyNode +); + +public sealed record DatabasesConnection +( + IReadOnlyList? Edges +); +public sealed record DatabaseEdge( + DatabaseDto Node +); \ No newline at end of file diff --git a/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs b/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs new file mode 100644 index 00000000..c847c2a1 --- /dev/null +++ b/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs @@ -0,0 +1,10 @@ +using System; + +namespace Database.ApiRequest.Dto; + +public sealed record DatabaseRequestDto( +Guid DatabaseId, +string Name, +string Description, +Uri Locator +); \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserInfo.cs b/backend/src/ApiRequest/Dto/UserInfoDto.cs similarity index 72% rename from backend/src/GraphQl/Users/UserInfo.cs rename to backend/src/ApiRequest/Dto/UserInfoDto.cs index 8c04ab68..5210e8a5 100644 --- a/backend/src/GraphQl/Users/UserInfo.cs +++ b/backend/src/ApiRequest/Dto/UserInfoDto.cs @@ -1,12 +1,12 @@ -using System.Collections.Generic; +using System.Collections.Generic; -namespace Database.GraphQl.Users; +namespace Database.ApiRequest.Dto; public sealed record Address( string Formatted ); -public sealed record UserInfo( +public sealed record UserInfoDto( Address? Address, string Email, bool EmailVerified, diff --git a/backend/src/ApiRequest/JsonSerializerOptions.cs b/backend/src/ApiRequest/JsonSerializerOptions.cs new file mode 100644 index 00000000..d9a0dc94 --- /dev/null +++ b/backend/src/ApiRequest/JsonSerializerOptions.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GraphQL.Client.Serializer.SystemTextJson; + +namespace Database.ApiRequest; + +public static class JsonSerializerSettings +{ + public static readonly JsonSerializerOptions GraphQL = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); + + public static readonly JsonSerializerOptions REST = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); +} \ No newline at end of file diff --git a/backend/src/Metabase/Queries/CurrentUser.graphql b/backend/src/ApiRequest/Queries/CurrentUser.graphql similarity index 100% rename from backend/src/Metabase/Queries/CurrentUser.graphql rename to backend/src/ApiRequest/Queries/CurrentUser.graphql diff --git a/backend/src/Metabase/Queries/Databases.graphql b/backend/src/ApiRequest/Queries/Databases.graphql similarity index 100% rename from backend/src/Metabase/Queries/Databases.graphql rename to backend/src/ApiRequest/Queries/Databases.graphql diff --git a/backend/src/Metabase/Queries/UpdateDatabase.graphql b/backend/src/ApiRequest/Queries/UpdateDatabase.graphql similarity index 92% rename from backend/src/Metabase/Queries/UpdateDatabase.graphql rename to backend/src/ApiRequest/Queries/UpdateDatabase.graphql index ba9eadda..ccef5e7f 100644 --- a/backend/src/Metabase/Queries/UpdateDatabase.graphql +++ b/backend/src/ApiRequest/Queries/UpdateDatabase.graphql @@ -1,5 +1,5 @@ mutation UpdateDatabase( - $input: UpdateDatabaseInput! + $input: DatabaseRequestDto! ) { updateDatabase(input: $input) { database { diff --git a/backend/src/ApiRequest/UserApi.cs b/backend/src/ApiRequest/UserApi.cs new file mode 100644 index 00000000..72529b20 --- /dev/null +++ b/backend/src/ApiRequest/UserApi.cs @@ -0,0 +1,59 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequest.Dto; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequest; + +public class UserApi +{ + private static readonly string[] _currentUserFileNames = + { + "CurrentUser.graphql" + }; + + private sealed record CurrentUserData(CurrentUserDto CurrentUser); + + public static async Task RequestCurrentUser( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + return (await ApiRequestService.QueryGraphQl( + appSettings, + new GraphQLRequest( + await ApiRequestService.ConstructGraphQlQuery( + _currentUserFileNames + ).ConfigureAwait(false), + new { }, + "CurrentUser" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false)) + ?.Data + ?.CurrentUser + ?? null; + } + + public static async Task RequestUserInfo( + AppSettings appSettings, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); + + return await ApiRequestService.QueryRest( + uri, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index da8fec9d..8bac5da6 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class CalorimetricDataAuthorization { public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 737c2ab9..102430ee 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -10,7 +10,7 @@ namespace Database.Authorization; public static class CommonAuthorization { public static bool IsCurrentUserAtLeastAssistantOfVerifiedInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -22,7 +22,7 @@ CancellationToken cancellationToken } public static bool IsAuthorizedToAddApprovalForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index d645e55c..3e09c1c8 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class FileUploadDataAuthorization { public static bool IsAuthorizedToUploadFilesForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index 063b4067..7a1bad49 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class GeometricDataAuthorization { public static bool IsAuthorizedToCreateGeometricDataForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index a457be81..31fdbcd7 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class GetHttpsResourceAuthorization { public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index d5372a56..3181af5e 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class HygrothermalDataAuthorization { public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index b4df1b3f..8d75b7b7 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class OpticalDataAuthorization { public static bool IsAuthorizedToCreateOpticalDataForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 9a0e6737..2267e8ad 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Net.Http; using System.Threading; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -9,7 +9,7 @@ namespace Database.Authorization; public static class PhotovoltaicDataAuthorization { public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( - CurrentUser currentUser, + CurrentUserDto currentUser, Guid institutionId, AppSettings appSettings, IHttpClientFactory httpClientFactory, diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/Data/Database.cs similarity index 67% rename from backend/src/GraphQl/Databases/Database.cs rename to backend/src/Data/Database.cs index 71459584..7a6a8034 100644 --- a/backend/src/GraphQl/Databases/Database.cs +++ b/backend/src/Data/Database.cs @@ -1,6 +1,7 @@ -using System; +using System; +using Database.GraphQl.Databases; -namespace Database.GraphQl.Databases; +namespace Database.Data; public sealed class Database { @@ -35,5 +36,19 @@ bool canCurrentUserVerifyNode // public Institution? Operator { get; set; } public bool CanCurrentUserUpdateNode { get; } + public bool CanCurrentUserVerifyNode { get; } + + public static Database FromDto(DatabaseDto databaseDto) + { + return new Database( + databaseDto.Uuid, + databaseDto.Name, + databaseDto.Description, + databaseDto.Locator, + databaseDto.VerificationState, + databaseDto.VerificationCode, + databaseDto.CanCurrentUserUpdateNode, + databaseDto.CanCurrentUserVerifyNode); + } } \ No newline at end of file diff --git a/backend/src/Data/UserInfo.cs b/backend/src/Data/UserInfo.cs new file mode 100644 index 00000000..8a1cdcf5 --- /dev/null +++ b/backend/src/Data/UserInfo.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using Database.ApiRequest.Dto; + +namespace Database.Data; + +public sealed class Address +{ + public string Formatted { get; private set; } + + public Address(string formatted) + { + Formatted = formatted; + } +} + +public sealed class UserInfo +{ + public Address? Address { get; private set; } + public string Email { get; private set; } + public bool EmailVerified { get; private set; } + public string Name { get; private set; } + public string? PhoneNumber { get; private set; } + public bool PhoneNumberVerified { get; private set; } + public IReadOnlyList? Roles { get; private set; } + public string Sub { get; private set; } // Subject + public string? Website { get; private set; } + + public UserInfo( + Address? address, + string email, + bool emailVerified, + string name, + string? phonenumber, + bool phonenumberVerified, + IReadOnlyList? roles, + string subject, + string? website) + { + Address = address; + Email = email; + EmailVerified = emailVerified; + Name = name; + PhoneNumber = phonenumber; + PhoneNumberVerified = phonenumberVerified; + Roles = roles; + Sub = subject; + Website = website; + } + + public static UserInfo? FromDto(UserInfoDto? userInfoDto) + { + return userInfoDto is not null ? new UserInfo( + userInfoDto.Address is not null ? new Address(userInfoDto.Address.Formatted) : null, + userInfoDto.Email, + userInfoDto.EmailVerified, + userInfoDto.Name, + userInfoDto.PhoneNumber, + userInfoDto.PhoneNumberVerified, + userInfoDto.Roles, + userInfoDto.Sub, + userInfoDto.Website) : null; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseEdge.cs b/backend/src/GraphQl/Databases/DatabaseEdge.cs deleted file mode 100644 index f4183ab9..00000000 --- a/backend/src/GraphQl/Databases/DatabaseEdge.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Database.GraphQl.Databases; - -public sealed class DatabaseEdge -{ - public DatabaseEdge( - Database node - ) - { - Node = node; - } - - public Database Node { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 2e124c3d..e1e6b4bd 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -1,9 +1,8 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.Metabase; -using GraphQL; +using Database.ApiRequest; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -12,11 +11,6 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Mutation))] public sealed class DatabaseMutations { - private static readonly string[] _updateDatabaseFileNames = - { - "UpdateDatabase.graphql" - }; - public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, AppSettings appSettings, @@ -25,23 +19,19 @@ public async Task UpdateDatabaseAsync( CancellationToken cancellationToken ) { - return (await QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQLRequest( - await QueryingMetabase.ConstructGraphQlQuery( - _updateDatabaseFileNames - ).ConfigureAwait(false), - new - { - input - }, - "UpdateDatabase" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false))?.Data - ?? new UpdateDatabasePayload( + var database = await DatabaseApi.UpdateDatabase( + new ApiRequest.Dto.DatabaseRequestDto( + input.DatabaseId, + input.Description, + input.Name, + input.Locator), + appSettings, + httpClientFactory, + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + return database is not null ? new UpdateDatabasePayload( + Data.Database.FromDto(database), + null) : new UpdateDatabasePayload( null, new[] { diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index 14f27274..046fbc07 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.Databases; @@ -7,7 +7,7 @@ public abstract class DatabasePayload where TDatabaseError : IUserError { protected DatabasePayload( - Database? database, + Data.Database? database, IReadOnlyCollection? errors ) { @@ -15,6 +15,6 @@ protected DatabasePayload( Errors = errors; } - public Database? Database { get; } + public Data.Database? Database { get; } public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasesConnection.cs b/backend/src/GraphQl/Databases/DatabasesConnection.cs deleted file mode 100644 index 9b3e3d47..00000000 --- a/backend/src/GraphQl/Databases/DatabasesConnection.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.Databases; - -public sealed class DatabasesConnection -{ - public DatabasesConnection( - IReadOnlyList? edges - ) - { - Edges = edges; - } - - public IReadOnlyList? Edges { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index 4463e551..0c939725 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.Databases; @@ -6,7 +6,7 @@ public sealed class UpdateDatabasePayload : DatabasePayload { public UpdateDatabasePayload( - Database? database, + Data.Database? database, IReadOnlyCollection? errors ) : base(database, errors) diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index f6d647c3..f875c1f3 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,12 +1,12 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequest; using Database.Data; -using Database.Metabase; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -42,15 +42,14 @@ await context.Users.AsNoTracking() CancellationToken cancellationToken ) { - var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); try { - return await QueryingMetabase.QueryRest( - uri, + return UserInfo.FromDto(await UserApi.RequestUserInfo( + appSettings, httpClientFactory, httpContextAccessor, cancellationToken - ); + )); } catch (HttpRequestException e) { @@ -58,7 +57,7 @@ CancellationToken cancellationToken ErrorBuilder.New() .SetCode("METABASE_REQUEST_FAILED") .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request {uri}.") + .SetMessage($"Failed with status code {e.StatusCode} to request {appSettings.MetabaseHost}.") .SetException(e) .Build() ); @@ -68,11 +67,11 @@ CancellationToken cancellationToken { resolverContext.ReportError( ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") + .SetCode("JSON_DESERIALIZATION_FAILED") .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) .ToList()) // TODO Splitting the path at '.' is wrong in general. .SetMessage( - $"Failed to deserialize GraphQL response of request to {uri}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + $"Failed to deserialize GraphQL response of request to {appSettings.MetabaseHost}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") .SetException(e) .Build() ); diff --git a/backend/src/Metabase/QueryingCurrentUser.cs b/backend/src/Metabase/QueryingCurrentUser.cs deleted file mode 100644 index 58f61391..00000000 --- a/backend/src/Metabase/QueryingCurrentUser.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; -using Microsoft.AspNetCore.Http; - -namespace Database.Metabase; - -public class QueryingCurrentUser -{ - private static readonly string[] _currentUserFileNames = - { - "CurrentUser.graphql" - }; - - private sealed record CurrentUserData(CurrentUser CurrentUser); - - public static async Task Query( - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - { - return (await QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQLRequest( - await QueryingMetabase.ConstructGraphQlQuery( - _currentUserFileNames - ).ConfigureAwait(false), - new { }, - "CurrentUser" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false)) - ?.Data - ?.CurrentUser - ?? null; - } -} \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index 5659baaa..f7e6e9a8 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Database.Metabase; +using Database.ApiRequest.Dto; using Microsoft.AspNetCore.Http; namespace Database.Services; @@ -16,5 +16,5 @@ public interface IUserService /// /// /// - public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); + public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 49fa45f5..aa2a641c 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -2,9 +2,10 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequest; +using Database.ApiRequest.Dto; using Database.Extensions; using Database.Logging; -using Database.Metabase; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; @@ -24,7 +25,7 @@ public class UserService( ILogger logger) : IUserService { /// - public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { // Extract token var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); @@ -36,7 +37,7 @@ public class UserService( } // Check if there is already a user for token - if (!cache.TryGetValue(token, out CurrentUser? cacheUser)) + if (!cache.TryGetValue(token, out CurrentUserDto? cacheUser)) { // Get user from Metabase cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); @@ -49,9 +50,9 @@ public class UserService( return cacheUser; } - private Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + private Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return QueryingCurrentUser.Query( + return UserApi.RequestCurrentUser( appSettings, httpClientFactory, httpContextAccessor, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 30159655..aeb3ab83 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -4,11 +4,11 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Database.ApiRequest; using Database.Configuration; using Database.Data; using Database.Data.Extensions; using Database.Enumerations; -using Database.Metabase; using Database.Services; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; @@ -213,7 +213,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) private static void ConfigureHttpClientServices(IServiceCollection services, IWebHostEnvironment environment) { services.AddHttpClient(); - var metabasesHttpClientBuilder = services.AddHttpClient(QueryingMetabase.MetabaseHttpClient); + var metabasesHttpClientBuilder = services.AddHttpClient(ApiRequestService.MetabaseHttpClient); if (environment.IsDevelopment()) { metabasesHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => From 1d966a4079a97734ddce5176427d6e91a9a4bf69 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Fri, 14 Mar 2025 14:01:31 +0100 Subject: [PATCH 298/878] Rework Api calls --- backend/src/ApiRequest/DataApi.cs | 169 ++++++++++++++++++ backend/src/ApiRequest/DatabaseApi.cs | 14 +- .../Queries/CalorimetricData.graphql | 79 ++++++++ .../ApiRequest/Queries/GeometricData.graphql | 78 ++++++++ .../Queries/HygrothermalData.graphql | 77 ++++++++ .../ApiRequest/Queries/OpticalData.graphql | 83 +++++++++ .../Queries/PhotovoltaicData.graphql | 77 ++++++++ backend/src/ApiRequest/UserApi.cs | 14 +- .../CalorimetricDataAuthorization.cs | 7 +- .../src/Authorization/CommonAuthorization.cs | 8 - .../Authorization/FileUploadAuthorization.cs | 7 +- .../GeometricDataAuthorization.cs | 7 +- .../GetHttpsResourceAuthorization.cs | 7 +- .../HygrothermalDataAuthorization.cs | 7 +- .../Authorization/OpticalDataAuthorization.cs | 7 +- .../PhotovoltaicDataAuthorization.cs | 7 +- backend/src/Data/GetHttpsResource.cs | 11 +- .../GraphQl/Approvals/ApprovalMutations.cs | 6 - .../CalorimetricDataMutations.cs | 16 +- .../GraphQl/Databases/DatabaseMutations.cs | 6 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 84 ++------- .../CreateGeometricDataErrorCode.cs | 5 +- .../CreateGeometricDataPayload.cs | 10 +- .../GeometricDataX/GeometricDataMutations.cs | 84 +++++---- .../GetHttpsResourceMutations.cs | 13 +- .../HygrothermalDataMutations.cs | 16 +- .../OpticalDataX/OpticalDataMutations.cs | 16 +- .../PhotovoltaicDataMutations.cs | 14 +- .../Logging/ResponseApprovalServiceLogging.cs | 17 ++ .../ApiRequestService.cs | 67 +++---- backend/src/Services/IApiRequestService.cs | 34 ++++ .../src/Services/IResponseApprovalService.cs | 11 ++ backend/src/Services/ISigningService.cs | 2 +- backend/src/Services/IUserService.cs | 2 +- .../src/Services/ResponseApprovalService.cs | 70 ++++++++ backend/src/Services/SigningService.cs | 2 +- backend/src/Services/UserService.cs | 3 + backend/src/Startup.cs | 13 +- 38 files changed, 859 insertions(+), 291 deletions(-) create mode 100644 backend/src/ApiRequest/DataApi.cs create mode 100644 backend/src/ApiRequest/Queries/CalorimetricData.graphql create mode 100644 backend/src/ApiRequest/Queries/GeometricData.graphql create mode 100644 backend/src/ApiRequest/Queries/HygrothermalData.graphql create mode 100644 backend/src/ApiRequest/Queries/OpticalData.graphql create mode 100644 backend/src/ApiRequest/Queries/PhotovoltaicData.graphql create mode 100644 backend/src/Logging/ResponseApprovalServiceLogging.cs rename backend/src/{ApiRequest => Services}/ApiRequestService.cs (88%) create mode 100644 backend/src/Services/IApiRequestService.cs create mode 100644 backend/src/Services/IResponseApprovalService.cs create mode 100644 backend/src/Services/ResponseApprovalService.cs diff --git a/backend/src/ApiRequest/DataApi.cs b/backend/src/ApiRequest/DataApi.cs new file mode 100644 index 00000000..010300a9 --- /dev/null +++ b/backend/src/ApiRequest/DataApi.cs @@ -0,0 +1,169 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Services; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequest; + +public class DataApi +{ + private static readonly string[] _calorimetricDataFileNames = + { + "CalorimetricData.graphql" + }; + + private static readonly string[] _geometricDataFileNames = + { + "GeometricData.graphql" + }; + + private static readonly string[] _hygrothermalDataFileNames = + { + "HygrothermalData.graphql" + }; + + private static readonly string[] _photovoltaicDataFileNames = + { + "PhotovoltaicData.graphql" + }; + + private static readonly string[] _opticalDataFileNames = + { + "OpticalData.graphql" + }; + + private sealed record CalorimetricDataResponse(CalorimetricData CalorimetricData); + private sealed record GeometricDataResponse(GeometricData GeometricData); + private sealed record HygrothermalDataResponse(HygrothermalData HygrothermalData); + private sealed record PhotovoltaicDataResponse(PhotovoltaicData PhotovoltaicData); + private sealed record OpticalDataResponse(OpticalData OpticalData); + + public static async Task<(string? Query, string? Response)> GetQueryAndResponceCalorimetricData( + Guid dataId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(_calorimetricDataFileNames).ConfigureAwait(false), + new { uuid = dataId }, + "Data" + ); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return (request.Query, response.ToString()); + } + + public static async Task<(string? Query, string? Response)> GetQueryAndResponceGeometricData( + Guid dataId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(_geometricDataFileNames).ConfigureAwait(false), + new { uuid = dataId }, + "Data" + ); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return (Query: request.Query, response.ToString()); + } + + public static async Task<(string? Query, string? Response)> GetQueryAndResponceHygrothermalData( + Guid dataId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(_hygrothermalDataFileNames).ConfigureAwait(false), + new { uuid = dataId }, + "Data" + ); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return (request.Query, response.ToString()); + } + + public static async Task<(string? Query, string? Response)> GetQueryAndResponcePhotovoltaicData( + Guid dataId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(_photovoltaicDataFileNames).ConfigureAwait(false), + new { uuid = dataId }, + "Data" + ); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return (request.Query, response.ToString()); + } + + public static async Task<(string? Query, string? Response)> GetQueryAndResponceOpticalData( + Guid dataId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(_opticalDataFileNames).ConfigureAwait(false), + new { uuid = dataId }, + "Data" + ); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return (request.Query, response.ToString()); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/DatabaseApi.cs b/backend/src/ApiRequest/DatabaseApi.cs index 6eba59a7..cd64085c 100644 --- a/backend/src/ApiRequest/DatabaseApi.cs +++ b/backend/src/ApiRequest/DatabaseApi.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.ApiRequest.Dto; using Database.GraphQl.Databases; +using Database.Services; using GraphQL; using HotChocolate; using HotChocolate.Resolvers; @@ -27,17 +28,15 @@ private sealed record DatabasesData(DatabasesConnection Databases); public static async Task RequestDatabase( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken) { - var response = await ApiRequestService.QueryGraphQl( + var response = await apiRequestService.Metabase().QueryGraphQl( appSettings, - new GraphQLRequest( - await ApiRequestService.ConstructGraphQlQuery( - _databaseFileNames - ).ConfigureAwait(false), + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames).ConfigureAwait(false), new { where = new @@ -97,16 +96,17 @@ await ApiRequestService.ConstructGraphQlQuery( public static async Task UpdateDatabase( DatabaseRequestDto databaseRequest, + IApiRequestService apiRequestService, AppSettings appSettings, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return (await ApiRequestService.QueryGraphQl( + return (await apiRequestService.Metabase().QueryGraphQl( appSettings, new GraphQLRequest( - await ApiRequestService.ConstructGraphQlQuery( + await apiRequestService.ConstructGraphQlQuery( _updateDatabaseFileNames ).ConfigureAwait(false), new diff --git a/backend/src/ApiRequest/Queries/CalorimetricData.graphql b/backend/src/ApiRequest/Queries/CalorimetricData.graphql new file mode 100644 index 00000000..70dd3bd8 --- /dev/null +++ b/backend/src/ApiRequest/Queries/CalorimetricData.graphql @@ -0,0 +1,79 @@ +query CalorimetricData($uuid: Uuid!) { + calorimetricData(id: $uuid) { + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + approverId + keyFingerprint + query + response + signature + timestamp + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + gValues + uValues + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/GeometricData.graphql b/backend/src/ApiRequest/Queries/GeometricData.graphql new file mode 100644 index 00000000..b02d8d20 --- /dev/null +++ b/backend/src/ApiRequest/Queries/GeometricData.graphql @@ -0,0 +1,78 @@ +query GeometricData($uuid: Uuid!) { + geometricData(id: $uuid) { + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + approverId + keyFingerprint + query + response + signature + timestamp + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + thicknesses + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/HygrothermalData.graphql b/backend/src/ApiRequest/Queries/HygrothermalData.graphql new file mode 100644 index 00000000..d8cd331a --- /dev/null +++ b/backend/src/ApiRequest/Queries/HygrothermalData.graphql @@ -0,0 +1,77 @@ +query HygrothermalData($uuid: Uuid!) { + hygrothermalData(id: $uuid) { + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + approverId + keyFingerprint + query + response + signature + timestamp + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/OpticalData.graphql b/backend/src/ApiRequest/Queries/OpticalData.graphql new file mode 100644 index 00000000..f06ee4be --- /dev/null +++ b/backend/src/ApiRequest/Queries/OpticalData.graphql @@ -0,0 +1,83 @@ +query OpticalData($uuid: Uuid!) { + opticalData(id: $uuid) { + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + approverId + keyFingerprint + query + response + signature + timestamp + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + infraredEmittances + nearnormalHemisphericalSolarReflectances + nearnormalHemisphericalSolarTransmittances + nearnormalHemisphericalVisibleReflectances + nearnormalHemisphericalVisibleTransmittances + colorRenderingIndices + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql b/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql new file mode 100644 index 00000000..30a51bc1 --- /dev/null +++ b/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql @@ -0,0 +1,77 @@ +query PhotovoltaicData($uuid: Uuid!) { + photovoltaicData(id: $uuid) { + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + approverId + keyFingerprint + query + response + signature + timestamp + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/UserApi.cs b/backend/src/ApiRequest/UserApi.cs index 72529b20..052f25e4 100644 --- a/backend/src/ApiRequest/UserApi.cs +++ b/backend/src/ApiRequest/UserApi.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequest.Dto; +using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; @@ -19,16 +20,14 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); public static async Task RequestCurrentUser( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return (await ApiRequestService.QueryGraphQl( + return (await apiRequestService.Metabase().QueryGraphQl( appSettings, - new GraphQLRequest( - await ApiRequestService.ConstructGraphQlQuery( - _currentUserFileNames - ).ConfigureAwait(false), + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_currentUserFileNames).ConfigureAwait(false), new { }, "CurrentUser" ), @@ -43,17 +42,18 @@ await ApiRequestService.ConstructGraphQlQuery( public static async Task RequestUserInfo( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); - return await ApiRequestService.QueryRest( + return await apiRequestService.Metabase().QueryRest( uri, httpClientFactory, httpContextAccessor, cancellationToken - ); + ).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index 8bac5da6..9124e4aa 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class CalorimetricDataAuthorization public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 102430ee..7b6b37f6 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,9 +1,7 @@ using System; using System.Linq; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -12,9 +10,6 @@ public static class CommonAuthorization public static bool IsCurrentUserAtLeastAssistantOfVerifiedInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { @@ -24,9 +19,6 @@ CancellationToken cancellationToken public static bool IsAuthorizedToAddApprovalForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index 3e09c1c8..66ce112d 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class FileUploadDataAuthorization public static bool IsAuthorizedToUploadFilesForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index 7a1bad49..ab17e20f 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class GeometricDataAuthorization public static bool IsAuthorizedToCreateGeometricDataForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index 31fdbcd7..a14a018d 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class GetHttpsResourceAuthorization public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index 3181af5e..ce53a512 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class HygrothermalDataAuthorization public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index 8d75b7b7..980f9476 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class OpticalDataAuthorization public static bool IsAuthorizedToCreateOpticalDataForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 2267e8ad..8c6a145c 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -1,8 +1,6 @@ using System; -using System.Net.Http; using System.Threading; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Authorization; @@ -11,12 +9,9 @@ public static class PhotovoltaicDataAuthorization public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( CurrentUserDto currentUser, Guid institutionId, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, appSettings, httpClientFactory, httpContextAccessor, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 6bf376bf..06b8e837 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Database.Extensions; -using Database.GraphQl.GeometricDataX; namespace Database.Data; @@ -111,27 +110,27 @@ public GetHttpsResource( public Guid? CalorimetricDataId { get; private set; } - [InverseProperty(nameof(Database.Data.CalorimetricData.Resources))] + [InverseProperty(nameof(CalorimetricData.Resources))] public CalorimetricData? CalorimetricData { get; set; } public Guid? HygrothermalDataId { get; private set; } - [InverseProperty(nameof(Database.Data.HygrothermalData.Resources))] + [InverseProperty(nameof(HygrothermalData.Resources))] public HygrothermalData? HygrothermalData { get; set; } public Guid? OpticalDataId { get; private set; } - [InverseProperty(nameof(Database.Data.OpticalData.Resources))] + [InverseProperty(nameof(OpticalData.Resources))] public OpticalData? OpticalData { get; set; } public Guid? PhotovoltaicDataId { get; private set; } - [InverseProperty(nameof(Database.Data.PhotovoltaicData.Resources))] + [InverseProperty(nameof(PhotovoltaicData.Resources))] public PhotovoltaicData? PhotovoltaicData { get; set; } public Guid? GeometricDataId { get; private set; } - [InverseProperty(nameof(Database.Data.GeometricData.Resources))] + [InverseProperty(nameof(GeometricData.Resources))] public GeometricData? GeometricData { get; set; } public Guid? ParentId { get; private set; } diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index d5fcc223..ef0dae2d 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -1,5 +1,4 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -17,8 +16,6 @@ public sealed class ApprovalMutations public async Task AddApprovalToDataAsync( ApprovalInput input, ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IUserService userService, IDataService dataService, @@ -42,9 +39,6 @@ CancellationToken cancellationToken if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( currentUser, input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 6ab3c375..d45fd211 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -1,32 +1,23 @@ -using System.Linq; -using System.Net.Http; -using System.Security.Claims; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; -using Database.GraphQl.Approvals; using Database.Services; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.CalorimetricDataX; [ExtendObjectType(nameof(Mutation))] public sealed class CalorimetricDataMutations { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) @@ -48,9 +39,6 @@ CancellationToken cancellationToken if (!CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( currentUser, input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index e1e6b4bd..4fa37c44 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -3,6 +3,8 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequest; +using Database.ApiRequest.Dto; +using Database.Services; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -14,17 +16,19 @@ public sealed class DatabaseMutations public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { var database = await DatabaseApi.UpdateDatabase( - new ApiRequest.Dto.DatabaseRequestDto( + new DatabaseRequestDto( input.DatabaseId, input.Description, input.Name, input.Locator), + apiRequestService, appSettings, httpClientFactory, httpContextAccessor, diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 0df23c68..c6f59eaa 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Metabase; -using GraphQL; +using Database.ApiRequest; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -16,13 +16,9 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Query))] public sealed class DatabaseQueries { - private static readonly string[] _databaseFileNames = - { - "Databases.graphql" - }; - - public async Task GetDatabaseAsync( + public async Task GetDatabaseAsync( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, @@ -31,66 +27,14 @@ CancellationToken cancellationToken { try { - var response = - await QueryingMetabase.QueryGraphQl( - appSettings, - new GraphQLRequest( - await QueryingMetabase.ConstructGraphQlQuery( - _databaseFileNames - ).ConfigureAwait(false), - new - { - where = new - { - locator = new - { - // TODO This is error-prone. - absoluteUri = new - { - equalTo = new Uri(new Uri(appSettings.Host), "/graphql/") - } - } - } - }, - "Databases" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - if (response is null) - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_RESPONSE") - .SetPath(resolverContext.Path) - .SetMessage("Response is null.") - .Build() - ); - if (response.Data.Databases.Edges is null) - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_EDGES") - .SetPath(resolverContext.Path) - .SetMessage("The supposed list of databases is null.") - .Build() - ); - if (response.Data.Databases.Edges.Count == 0) - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NO_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases is empty.") - .Build() - ); - if (response.Data.Databases.Edges.Count >= 2) - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("AMBIGUOUS_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases has more than one entry.") - .Build() - ); - return response.Data.Databases.Edges[0].Node; + var database = await DatabaseApi.RequestDatabase( + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + resolverContext, + cancellationToken); + return Data.Database.FromDto(database); } catch (HttpRequestException e) { @@ -117,6 +61,4 @@ await QueryingMetabase.ConstructGraphQlQuery( ); } } - - private sealed record DatabasesData(DatabasesConnection Databases); } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs index a68904b1..29346d03 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -1,8 +1,9 @@ -namespace Database.GraphQl.GeometricDataX; +namespace Database.GraphQl.GeometricDataX; public enum CreateGeometricDataErrorCode { UNKNOWN, UNAUTHORIZED, - UNAUTHENTICATED + UNAUTHENTICATED, + SIGNINGFAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs index c4047182..941d5a83 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.GeometricDataX; @@ -18,4 +18,12 @@ CreateGeometricDataError error : base(error) { } + + public CreateGeometricDataPayload( + GeometricData geometricData, + CreateGeometricDataError error + ) + : base(geometricData, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index c5bfee24..cd97cfd4 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,9 +1,9 @@ using System; using System.Linq; -using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Services; using HotChocolate.Types; @@ -19,44 +19,39 @@ public sealed class GeometricDataMutations public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - ISigningService signingService, - IHttpClientFactory httpClientFactory, + IResponseApprovalService responseApprovalService, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - //var currentUser = await userService.GetCurrentUser( - // httpContextAccessor, - // cancellationToken).ConfigureAwait(false); - //if (currentUser == null) - //{ - // return new CreateGeometricDataPayload( - // new CreateGeometricDataError( - // CreateGeometricDataErrorCode.UNAUTHENTICATED, - // $"The user is not authenticated.", - // [] - // ) - // ); - //} + var currentUser = await userService.GetCurrentUser( + httpContextAccessor, + cancellationToken).ConfigureAwait(false); + if (currentUser == null) + { + return new CreateGeometricDataPayload( + new CreateGeometricDataError( + CreateGeometricDataErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } - //if (!GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( - // currentUser, - // input.CreatorId, - // appSettings, - // httpClientFactory, - // httpContextAccessor, - // cancellationToken - // ) - //) - // return new CreateGeometricDataPayload( - // new CreateGeometricDataError( - // CreateGeometricDataErrorCode.UNAUTHORIZED, - // $"The current user is not authorized to create geometric data for the institution.", - // [] - // ) - // ); + if (!GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( + currentUser, + input.CreatorId, + cancellationToken + ) + ) + return new CreateGeometricDataPayload( + new CreateGeometricDataError( + CreateGeometricDataErrorCode.UNAUTHORIZED, + $"The current user is not authorized to create geometric data for the institution.", + [] + ) + ); var geometricData = new GeometricData( input.Locale, @@ -114,14 +109,25 @@ input.RootResource.AppliedConversionMethod is null ); geometricData.Resources.Add(resource); - var (success, signature) = await signingService.SignData(JsonSerializer.Serialize(geometricData)); - if (success) + context.GeometricData.Add(geometricData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try { - geometricData.Approval = new ResponseApproval(DateTime.Now, signature, signingService.GetFingerprint(), "", ""); + geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, httpContextAccessor, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + return new CreateGeometricDataPayload( + geometricData, + new CreateGeometricDataError( + CreateGeometricDataErrorCode.SIGNINGFAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); } - - //context.GeometricData.Add(geometricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new CreateGeometricDataPayload(geometricData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 6c6f8600..1ab4d34c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -1,6 +1,5 @@ -using System; +using System; using System.Linq; -using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -8,11 +7,9 @@ using Database.Authorization; using Database.Enumerations; using Database.Extensions; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; -using Database.GraphQl.Approvals; using Database.Services; namespace Database.GraphQl.GetHttpsResources; @@ -20,14 +17,11 @@ namespace Database.GraphQl.GetHttpsResources; [ExtendObjectType(nameof(Mutation))] public sealed class GetHttpsResourceMutations { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) @@ -68,9 +62,6 @@ CancellationToken cancellationToken if (!GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( currentUser, data.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 36c5998d..1e77b553 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -1,32 +1,23 @@ -using System.Linq; -using System.Net.Http; -using System.Security.Claims; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; -using Database.GraphQl.Approvals; using Database.Services; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.HygrothermalDataX; [ExtendObjectType(nameof(Mutation))] public sealed class HygrothermalDataMutations { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) @@ -47,9 +38,6 @@ CancellationToken cancellationToken if (!HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( currentUser, input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index df15b261..ab9231b5 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,32 +1,23 @@ -using System.Linq; -using System.Net.Http; -using System.Security.Claims; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; -using Database.GraphQl.Approvals; using Database.Services; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.OpticalDataX; [ExtendObjectType(nameof(Mutation))] public sealed class OpticalDataMutations { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) @@ -47,9 +38,6 @@ CancellationToken cancellationToken if (!OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( currentUser, input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 05991fd7..b0ce3e2b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Text.Json; @@ -6,27 +6,20 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; -using Database.GraphQl.Approvals; using Database.Services; -using HotChocolate; using HotChocolate.Types; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; namespace Database.GraphQl.PhotovoltaicDataX; [ExtendObjectType(nameof(Mutation))] public sealed class PhotovoltaicDataMutations { - // [UseUserManager] - // [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, - AppSettings appSettings, IUserService userService, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) @@ -47,9 +40,6 @@ CancellationToken cancellationToken if (!PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( currentUser, input.CreatorId, - appSettings, - httpClientFactory, - httpContextAccessor, cancellationToken ) ) diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs new file mode 100644 index 00000000..0c055361 --- /dev/null +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -0,0 +1,17 @@ +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class ResponseApprovalServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Getting query and response for {DataType}")] + public static partial void GetQueryAndResponse(this ILogger logger, string dataType); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Query and response: {Query} \n {Response}")] + public static partial void QueryAndResponce(this ILogger logger, string query, string response); +} \ No newline at end of file diff --git a/backend/src/ApiRequest/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs similarity index 88% rename from backend/src/ApiRequest/ApiRequestService.cs rename to backend/src/Services/ApiRequestService.cs index 9696dd3e..6a09f322 100644 --- a/backend/src/ApiRequest/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -7,13 +7,14 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequest; using Database.Extensions; using GraphQL; using Microsoft.AspNetCore.Http; -namespace Database.ApiRequest; +namespace Database.Services; -public sealed class ApiRequestService +public class ApiRequestService() : IApiRequestService { public const string MetabaseHttpClient = "Metabase"; public const string DatabaseHttpClient = "Database"; @@ -31,7 +32,7 @@ public ApiRequestService Database() return this; } - public static async Task ConstructGraphQlQuery( + public async Task ConstructGraphQlQuery( string[] fileNames ) { @@ -45,7 +46,7 @@ await Task.WhenAll( ); } - public static Task> QueryGraphQl( + public Task> QueryGraphQl( AppSettings appSettings, GraphQLRequest request, IHttpClientFactory httpClientFactory, @@ -67,25 +68,7 @@ CancellationToken cancellationToken ); } - private static ByteArrayContent MakeJsonHttpContent( - TContent content - ) - { - // For some reason using `JsonContent.Create(content, null, SerializerOptions)` - // results in status code `BadRequest`. - var result = - new ByteArrayContent( - JsonSerializer.SerializeToUtf8Bytes( - content, - JsonSerializerSettings.GraphQL - ) - ); - result.Headers.ContentType = - new MediaTypeHeaderValue("application/json"); - return result; - } - - public static Task QueryRest( + public Task QueryRest( Uri uri, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, @@ -129,22 +112,42 @@ CancellationToken cancellationToken throw new HttpRequestException( $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); + // We could use // `httpResponseMessage.Content.ReadFromJsonAsync>` which // would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ - using var responseStream = - await httpResponseMessage.Content - .ReadAsStreamAsync(cancellationToken) - .ConfigureAwait(false); + using var responseStream = await httpResponseMessage.Content + .ReadAsStreamAsync(cancellationToken) + .ConfigureAwait(false); + // For debugging, the following lines of code write the response to standard output. // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); - var deserializedResponse = - await JsonSerializer.DeserializeAsync( - responseStream, - serializerOptions, - cancellationToken + var deserializedResponse = await JsonSerializer.DeserializeAsync( + responseStream, + serializerOptions, + cancellationToken ).ConfigureAwait(false); + if (deserializedResponse is null) throw new JsonException("Failed to deserialize the GraphQL response."); + return deserializedResponse; } + + private static ByteArrayContent MakeJsonHttpContent( + TContent content + ) + { + // For some reason using `JsonContent.Create(content, null, SerializerOptions)` + // results in status code `BadRequest`. + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + JsonSerializerSettings.GraphQL + ) + ); + result.Headers.ContentType = + new MediaTypeHeaderValue("application/json"); + return result; + } } \ No newline at end of file diff --git a/backend/src/Services/IApiRequestService.cs b/backend/src/Services/IApiRequestService.cs new file mode 100644 index 00000000..d8ef690c --- /dev/null +++ b/backend/src/Services/IApiRequestService.cs @@ -0,0 +1,34 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.Services; + +public interface IApiRequestService +{ + public ApiRequestService Metabase(); + + public ApiRequestService Database(); + + public Task ConstructGraphQlQuery(string[] fileNames); + + public Task> QueryGraphQl( + AppSettings appSettings, + GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class; + + public Task QueryRest( + Uri uri, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class; +} \ No newline at end of file diff --git a/backend/src/Services/IResponseApprovalService.cs b/backend/src/Services/IResponseApprovalService.cs new file mode 100644 index 00000000..d3460d4e --- /dev/null +++ b/backend/src/Services/IResponseApprovalService.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Microsoft.AspNetCore.Http; + +namespace Database.Services; + +public interface IResponseApprovalService +{ + Task CreateResponseApproval(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index 6acec2b1..4d346eeb 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -24,5 +24,5 @@ public interface ISigningService /// /// Data string to create signature from. /// True and generated signature, if successful. Otherwise false. - public Task<(bool, string)> SignData(string data); + public Task<(bool Success, string Signature)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index f7e6e9a8..bd349f3f 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -13,7 +13,7 @@ public interface IUserService /// /// Get current user from Mewtabase by extraxting token from . /// - /// + /// /// /// public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs new file mode 100644 index 00000000..4c0169c6 --- /dev/null +++ b/backend/src/Services/ResponseApprovalService.cs @@ -0,0 +1,70 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequest; +using Database.Data; +using Database.Logging; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Database.Services; + +public class ResponseApprovalService( + AppSettings appSettings, + ISigningService signingService, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + ILogger logger) : IResponseApprovalService +{ + public async Task CreateResponseApproval(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + { + // Get dataset + logger.GetQueryAndResponse(dataObject.GetType().Name); + var queryAdnResponce = await GetQueryAndResponse(dataObject, httpContextAccessor, cancellationToken).ConfigureAwait(false); + + if (string.IsNullOrEmpty(queryAdnResponce.Query)) + { + throw new ArgumentNullException(queryAdnResponce.Query); + } + if (string.IsNullOrEmpty(queryAdnResponce.Response)) + { + throw new ArgumentNullException(queryAdnResponce.Response); + } + + logger.QueryAndResponce(queryAdnResponce.Query, queryAdnResponce.Response); + + var signatureResult = await signingService.SignData(queryAdnResponce.Response).ConfigureAwait(false); + + if (!signatureResult.Success) + { + throw new ArgumentException("Signing failed"); + } + + return new ResponseApproval(DateTime.Now, signatureResult.Signature, signingService.GetFingerprint(), queryAdnResponce.Query, queryAdnResponce.Response); + } + + private async Task<(string? Query, string? Response)> GetQueryAndResponse(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + { + switch (dataObject) + { + case GeometricData data: + return await DataApi.GetQueryAndResponceGeometricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + + case CalorimetricData data: + return await DataApi.GetQueryAndResponceCalorimetricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + + case HygrothermalData data: + return await DataApi.GetQueryAndResponceHygrothermalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + + case PhotovoltaicData data: + return await DataApi.GetQueryAndResponcePhotovoltaicData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + + case OpticalData data: + return await DataApi.GetQueryAndResponceOpticalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + + default: + return (null, null); + } + } +} \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 72273a0a..668d9123 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -48,7 +48,7 @@ public string GetFingerprint() } /// - public async Task<(bool, string)> SignData(string data) + public async Task<(bool Success, string Signature)> SignData(string data) { string signature = ""; diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index aa2a641c..38ec25ec 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -18,8 +18,10 @@ namespace Database.Services; /// /// /// to store already known users. +/// Instance of public class UserService( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IMemoryCache cache, ILogger logger) : IUserService @@ -54,6 +56,7 @@ public class UserService( { return UserApi.RequestCurrentUser( appSettings, + apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index aeb3ab83..13951249 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -4,7 +4,6 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; -using Database.ApiRequest; using Database.Configuration; using Database.Data; using Database.Data.Extensions; @@ -65,6 +64,8 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddScoped(); services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) @@ -223,6 +224,16 @@ private static void ConfigureHttpClientServices(IServiceCollection services, IWe } ); } + var databaseHttpClientBuilder = services.AddHttpClient(ApiRequestService.DatabaseHttpClient); + if (environment.IsDevelopment()) + { + databaseHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler + { + ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } + ); + } } public void Configure(WebApplication app) From 6d6e0b3cc026031f12fb255de8e64d82272c5a01 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Fri, 14 Mar 2025 14:07:58 +0100 Subject: [PATCH 299/878] Commit missing changes --- .../src/Controllers/FileUploadController.cs | 18 +++--------------- backend/src/GraphQl/Users/UserQueries.cs | 3 +++ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 53efb8e4..db0104de 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -1,8 +1,7 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; -using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -50,8 +49,6 @@ private const long private static readonly FormOptions _defaultFormOptions = new(); private readonly ILogger _logger; private readonly ApplicationDbContext _context; - private readonly AppSettings _appSettings; - private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpContextAccessor _httpContextAccessor; private readonly string[] _permittedExtensions = @@ -60,15 +57,11 @@ private const long public FileUploadController( ILogger logger, ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor ) { _logger = logger; _context = context; - _appSettings = appSettings; - _httpClientFactory = httpClientFactory; _httpContextAccessor = httpContextAccessor; // To save physical files to the temporary files folder, use: //_targetDirectoryPath = Path.GetTempPath(); @@ -139,13 +132,8 @@ CancellationToken cancellationToken } if (!FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( currentUser, - getHttpsResource.Data.CreatorId, - _appSettings, - _httpClientFactory, - _httpContextAccessor, - cancellationToken - ) - ) + getHttpsResource.Data.CreatorId, + cancellationToken)) { return Unauthorized(); } diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index f875c1f3..0b82dc21 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Database.ApiRequest; using Database.Data; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -36,6 +37,7 @@ await context.Users.AsNoTracking() public async Task GetCurrentUserInfoAsync( AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, @@ -46,6 +48,7 @@ CancellationToken cancellationToken { return UserInfo.FromDto(await UserApi.RequestUserInfo( appSettings, + apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken From a20a2284b555b77602e3a2094c08d3567bdcf0c0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Mar 2025 14:46:42 +0100 Subject: [PATCH 300/878] Upgrade HotChocolate to version 15.1.1 --- backend/src/Database.csproj | 14 +++++++------- backend/test/Database.Tests.csproj | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 132152ad..3ecd4842 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -24,14 +24,14 @@ - - - - - - + + + + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index aa5b2a7f..a8d32e41 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,7 +16,7 @@ - + From eb04e39e58cabdf73f37963e0f14f8f3b9f1640b Mon Sep 17 00:00:00 2001 From: jbinterap Date: Tue, 25 Mar 2025 13:18:54 +0100 Subject: [PATCH 301/878] ResponseApproval added in all data mutations and approval mutation --- backend/src/ApiRequest/DataApi.cs | 106 +++++++++++---- backend/src/ApiRequest/DatabaseApi.cs | 26 +++- backend/src/ApiRequest/Dto/XDataDto.cs | 121 ++++++++++++++++++ .../src/ApiRequest/JsonSerializerOptions.cs | 11 ++ .../ApiRequest/Queries/GeometricData.graphql | 4 +- backend/src/ApiRequest/UserApi.cs | 21 +++ .../src/Configuration/GraphQlConfiguration.cs | 20 ++- .../src/Controllers/FileUploadController.cs | 7 +- backend/src/Data/CalorimetricData.cs | 6 - backend/src/Data/DataX.cs | 3 - backend/src/Data/GeometricData.cs | 6 - backend/src/Data/HygrothermalData.cs | 12 +- backend/src/Data/OpticalData.cs | 3 - backend/src/Data/PhotovoltaicData.cs | 12 +- ...aSubtype.cs => OpticalComponentSubtype.cs} | 0 .../HttpContextAccessorExtensions.cs | 10 +- .../GraphQl/Approvals/AddApprovalErrorCode.cs | 3 +- .../GraphQl/Approvals/AddApprovalPayload.cs | 8 ++ .../GraphQl/Approvals/ApprovalMutations.cs | 24 +++- .../CalorimetricDataMutations.cs | 27 +++- .../CreateCalorimetricDataErrorCode.cs | 6 +- .../CreateCalorimetricDataPayload.cs | 10 +- .../GraphQl/Databases/DatabaseMutations.cs | 2 +- .../CreateGeometricDataErrorCode.cs | 7 +- .../GeometricDataX/GeometricDataMutations.cs | 15 +-- .../GeometricDataX/GeometricDataQueries.cs | 30 ++++- .../src/GraphQl/GeometricDataX/QueryError.cs | 21 +++ .../GraphQl/GeometricDataX/QueryPayload.cs | 20 +++ .../GetHttpsResourceMutations.cs | 3 - .../CreateHygrothermalDataErrorCode.cs | 6 +- .../CreateHygrothermalDataPayload.cs | 10 +- .../HygrothermalDataMutations.cs | 29 ++++- .../CreateOpticalDataErrorCode.cs | 6 +- .../OpticalDataX/CreateOpticalDataPayload.cs | 10 +- .../OpticalDataX/OpticalDataMutations.cs | 29 ++++- .../CreatePhotovoltaicDataErrorCode.cs | 6 +- .../CreatePhotovoltaicDataPayload.cs | 10 +- .../PhotovoltaicDataMutations.cs | 33 ++++- backend/src/Services/ApiRequestService.cs | 21 ++- backend/src/Services/CacheService.cs | 60 +++++++++ backend/src/Services/IApiRequestService.cs | 35 +++++ backend/src/Services/ICacheService.cs | 21 +++ .../src/Services/IResponseApprovalService.cs | 12 +- backend/src/Services/ISigningService.cs | 14 +- backend/src/Services/IUserService.cs | 14 +- .../src/Services/ResponseApprovalService.cs | 41 +++--- backend/src/Services/SigningService.cs | 28 ++-- backend/src/Services/UserService.cs | 38 +++--- backend/src/Startup.cs | 6 +- 49 files changed, 782 insertions(+), 191 deletions(-) create mode 100644 backend/src/ApiRequest/Dto/XDataDto.cs rename backend/src/Enumerations/{DataSubtype.cs => OpticalComponentSubtype.cs} (100%) create mode 100644 backend/src/GraphQl/GeometricDataX/QueryError.cs create mode 100644 backend/src/GraphQl/GeometricDataX/QueryPayload.cs create mode 100644 backend/src/Services/CacheService.cs create mode 100644 backend/src/Services/ICacheService.cs diff --git a/backend/src/ApiRequest/DataApi.cs b/backend/src/ApiRequest/DataApi.cs index 010300a9..9b142054 100644 --- a/backend/src/ApiRequest/DataApi.cs +++ b/backend/src/ApiRequest/DataApi.cs @@ -1,14 +1,18 @@ using System; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Database.ApiRequest.Dto; using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; namespace Database.ApiRequest; +/// +/// Class to request XData from Database API. +/// public class DataApi { private static readonly string[] _calorimetricDataFileNames = @@ -36,13 +40,23 @@ public class DataApi "OpticalData.graphql" }; - private sealed record CalorimetricDataResponse(CalorimetricData CalorimetricData); - private sealed record GeometricDataResponse(GeometricData GeometricData); - private sealed record HygrothermalDataResponse(HygrothermalData HygrothermalData); - private sealed record PhotovoltaicDataResponse(PhotovoltaicData PhotovoltaicData); - private sealed record OpticalDataResponse(OpticalData OpticalData); - - public static async Task<(string? Query, string? Response)> GetQueryAndResponceCalorimetricData( + private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); + private sealed record GeometricDataResponse(GeometricDataDto GeometricData); + private sealed record HygrothermalDataResponse(HygrothermalDataDto HygrothermalData); + private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicData); + private sealed record OpticalDataResponse(OpticalDataDto OpticalData); + + /// + /// Create query to request calorimetric data and get response. + /// + /// Id of data to request. + /// + /// + /// + /// + /// + /// Query and response for calorimetric data. + public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseCalorimetricData( Guid dataId, AppSettings appSettings, IApiRequestService apiRequestService, @@ -53,8 +67,8 @@ CancellationToken cancellationToken { var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(_calorimetricDataFileNames).ConfigureAwait(false), - new { uuid = dataId }, - "Data" + dataId, + "GetCalorimetricData" ); var response = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -64,10 +78,20 @@ await apiRequestService.ConstructGraphQlQuery(_calorimetricDataFileNames).Config cancellationToken ).ConfigureAwait(false); - return (request.Query, response.ToString()); + return (request.Query, JsonSerializer.Serialize(response)); } - public static async Task<(string? Query, string? Response)> GetQueryAndResponceGeometricData( + /// + /// Create query to request geometric data and get response. + /// + /// Id of data to request. + /// + /// + /// + /// + /// + /// Query and response for geometric data. + public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseGeometricData( Guid dataId, AppSettings appSettings, IApiRequestService apiRequestService, @@ -79,7 +103,7 @@ CancellationToken cancellationToken var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(_geometricDataFileNames).ConfigureAwait(false), new { uuid = dataId }, - "Data" + "GeometricData" ); var response = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -89,10 +113,20 @@ await apiRequestService.ConstructGraphQlQuery(_geometricDataFileNames).Configure cancellationToken ).ConfigureAwait(false); - return (Query: request.Query, response.ToString()); + return (Query: request.Query, JsonSerializer.Serialize(response)); } - public static async Task<(string? Query, string? Response)> GetQueryAndResponceHygrothermalData( + /// + /// Create query to request hygrothemal data and get response. + /// + /// Id of data to request. + /// + /// + /// + /// + /// + /// Query and response for hygrothermal data. + public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseHygrothermalData( Guid dataId, AppSettings appSettings, IApiRequestService apiRequestService, @@ -103,8 +137,8 @@ CancellationToken cancellationToken { var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(_hygrothermalDataFileNames).ConfigureAwait(false), - new { uuid = dataId }, - "Data" + dataId, + "GetHygrothermalData" ); var response = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -114,10 +148,20 @@ await apiRequestService.ConstructGraphQlQuery(_hygrothermalDataFileNames).Config cancellationToken ).ConfigureAwait(false); - return (request.Query, response.ToString()); + return (request.Query, JsonSerializer.Serialize(response)); } - public static async Task<(string? Query, string? Response)> GetQueryAndResponcePhotovoltaicData( + /// + /// Create query to request photovoltaic data and get response. + /// + /// Id of data to request. + /// + /// + /// + /// + /// + /// Query and response for photovoltaic data. + public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponsePhotovoltaicData( Guid dataId, AppSettings appSettings, IApiRequestService apiRequestService, @@ -128,8 +172,8 @@ CancellationToken cancellationToken { var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(_photovoltaicDataFileNames).ConfigureAwait(false), - new { uuid = dataId }, - "Data" + dataId, + "GetPhotovoltaicData" ); var response = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -139,10 +183,20 @@ await apiRequestService.ConstructGraphQlQuery(_photovoltaicDataFileNames).Config cancellationToken ).ConfigureAwait(false); - return (request.Query, response.ToString()); + return (request.Query, JsonSerializer.Serialize(response)); } - public static async Task<(string? Query, string? Response)> GetQueryAndResponceOpticalData( + /// + /// Create query to request optical data and get response. + /// + /// Id of data to request. + /// + /// + /// + /// + /// + /// Query and response for optical data. + public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseOpticalData( Guid dataId, AppSettings appSettings, IApiRequestService apiRequestService, @@ -153,8 +207,8 @@ CancellationToken cancellationToken { var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(_opticalDataFileNames).ConfigureAwait(false), - new { uuid = dataId }, - "Data" + dataId, + "GetOpticalData" ); var response = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -164,6 +218,6 @@ await apiRequestService.ConstructGraphQlQuery(_opticalDataFileNames).ConfigureAw cancellationToken ).ConfigureAwait(false); - return (request.Query, response.ToString()); + return (request.Query, JsonSerializer.Serialize(response)); } } \ No newline at end of file diff --git a/backend/src/ApiRequest/DatabaseApi.cs b/backend/src/ApiRequest/DatabaseApi.cs index cd64085c..52456265 100644 --- a/backend/src/ApiRequest/DatabaseApi.cs +++ b/backend/src/ApiRequest/DatabaseApi.cs @@ -12,6 +12,9 @@ namespace Database.ApiRequest; +/// +/// Class to request databases from Metabase API. +/// public class DatabaseApi { private static readonly string[] _databaseFileNames = @@ -26,6 +29,17 @@ public class DatabaseApi private sealed record DatabasesData(DatabasesConnection Databases); + /// + /// Request database from Metabase. + /// + /// + /// + /// + /// + /// + /// + /// if successful. + /// Throws exception if errors occur. public static async Task RequestDatabase( AppSettings appSettings, IApiRequestService apiRequestService, @@ -94,10 +108,20 @@ public static async Task RequestDatabase( return response.Data.Databases.Edges[0].Node; } + /// + /// Update database in Metabase. + /// + /// with update mutation. + /// + /// + /// + /// + /// + /// Updated public static async Task UpdateDatabase( DatabaseRequestDto databaseRequest, - IApiRequestService apiRequestService, AppSettings appSettings, + IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken diff --git a/backend/src/ApiRequest/Dto/XDataDto.cs b/backend/src/ApiRequest/Dto/XDataDto.cs new file mode 100644 index 00000000..14fbdfd6 --- /dev/null +++ b/backend/src/ApiRequest/Dto/XDataDto.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using Database.Data; +using Database.Enumerations; + +namespace Database.ApiRequest.Dto; + +public record GeometricDataDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethod AppliedMethod, + ResourceTree ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings, + IReadOnlyList Thicknesses +); + +public record CalorimetricDataDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethod AppliedMethod, + ResourceTree ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings, + IReadOnlyList GValues, + IReadOnlyList UValues +); + +public record HygrothermalDataDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethod AppliedMethod, + ResourceTree ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings +); + +public record OpticalDataDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethod AppliedMethod, + ResourceTree ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings, + OpticalComponentType? Type, + OpticalComponentSubtype? Subtype, + CoatedSide? CoatedSide, + IReadOnlyList NearnormalHemisphericalVisibleTransmittances, + IReadOnlyList NearnormalHemisphericalVisibleReflectances, + IReadOnlyList NearnormalHemisphericalSolarTransmittances, + IReadOnlyList NearnormalHemisphericalSolarReflectances, + IReadOnlyList InfraredEmittances, + IReadOnlyList ColorRenderingIndices, + ICollection CielabColors +); + +public record PhotovoltaicDataDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethod AppliedMethod, + ResourceTree ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings +); + +public record AppliedMethod( + string MethodId, + IReadOnlyList Arguments, + IReadOnlyList Sources +); + +public record Resource( + string Description, + string HashValue, + string Locator, + Guid DataFormatId +); + +public record ResourceTree( + Root Root, + IReadOnlyList NonRootVertices +); + +public record Root( + object Value +); \ No newline at end of file diff --git a/backend/src/ApiRequest/JsonSerializerOptions.cs b/backend/src/ApiRequest/JsonSerializerOptions.cs index d9a0dc94..309bfb5d 100644 --- a/backend/src/ApiRequest/JsonSerializerOptions.cs +++ b/backend/src/ApiRequest/JsonSerializerOptions.cs @@ -4,13 +4,20 @@ namespace Database.ApiRequest; +/// +/// Settings for Json serialization. +/// public static class JsonSerializerSettings { + /// + /// Settings for GraphQL Json requests. + /// public static readonly JsonSerializerOptions GraphQL = new() { Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, NumberHandling = JsonNumberHandling.Strict, + AllowOutOfOrderMetadataProperties = true, PropertyNameCaseInsensitive = false, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Disallow, @@ -20,11 +27,15 @@ public static class JsonSerializerSettings DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; //.SetupImmutableConverter(); + /// + /// Settings for REST Json requests. + /// public static readonly JsonSerializerOptions REST = new() { Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, NumberHandling = JsonNumberHandling.Strict, + AllowOutOfOrderMetadataProperties = true, PropertyNameCaseInsensitive = false, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, ReadCommentHandling = JsonCommentHandling.Disallow, diff --git a/backend/src/ApiRequest/Queries/GeometricData.graphql b/backend/src/ApiRequest/Queries/GeometricData.graphql index b02d8d20..e732d791 100644 --- a/backend/src/ApiRequest/Queries/GeometricData.graphql +++ b/backend/src/ApiRequest/Queries/GeometricData.graphql @@ -1,4 +1,6 @@ -query GeometricData($uuid: Uuid!) { +query GeometricData( + $uuid: Uuid! +) { geometricData(id: $uuid) { uuid timestamp diff --git a/backend/src/ApiRequest/UserApi.cs b/backend/src/ApiRequest/UserApi.cs index 052f25e4..a88f553e 100644 --- a/backend/src/ApiRequest/UserApi.cs +++ b/backend/src/ApiRequest/UserApi.cs @@ -9,6 +9,9 @@ namespace Database.ApiRequest; +/// +/// Class to request user info from Metabase API. +/// public class UserApi { private static readonly string[] _currentUserFileNames = @@ -18,6 +21,15 @@ public class UserApi private sealed record CurrentUserData(CurrentUserDto CurrentUser); + /// + /// Request current user from Metabase. + /// + /// + /// + /// + /// + /// + /// if successful. public static async Task RequestCurrentUser( AppSettings appSettings, IApiRequestService apiRequestService, @@ -40,6 +52,15 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); ?? null; } + /// + /// Request user info from Metabase. + /// + /// + /// + /// + /// + /// + /// if successful. public static async Task RequestUserInfo( AppSettings appSettings, IApiRequestService apiRequestService, diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 286e4694..d54658d3 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -39,14 +39,17 @@ public static void ConfigureServices( AppSettings appSettings ) { - // Stitching Services - // https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching - var httpClientBuilder = services.AddHttpClient( + // Stitching Services https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching + var httpMetaBaesClientBuilder = services.AddHttpClient( WellKnownSchemaNames.Metabase, _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") ); + var httpDatabaseClientBuilder = services.AddHttpClient( + WellKnownSchemaNames.Database, + _ => _.BaseAddress = new Uri($"{appSettings.Host}/graphql") + ); if (!environment.IsProduction()) - httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + httpMetaBaesClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { // ClientCertificateOptions = ClientCertificateOption.Manual, @@ -54,6 +57,14 @@ AppSettings appSettings HttpClientHandler.DangerousAcceptAnyServerCertificateValidator } ); + httpDatabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + new HttpClientHandler + { + // ClientCertificateOptions = ClientCertificateOption.Manual, + ServerCertificateCustomValidationCallback = + HttpClientHandler.DangerousAcceptAnyServerCertificateValidator + } + ); // Automatic-Persisted-Queries Services services .AddMemoryCache() @@ -195,6 +206,7 @@ AppSettings appSettings public static class WellKnownSchemaNames { public const string Metabase = "metabase"; + public const string Database = "database"; } } diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index db0104de..21930908 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -10,7 +10,6 @@ using Database.Filters; using Database.Services; using Database.Utilities; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.WebUtilities; @@ -49,20 +48,17 @@ private const long private static readonly FormOptions _defaultFormOptions = new(); private readonly ILogger _logger; private readonly ApplicationDbContext _context; - private readonly IHttpContextAccessor _httpContextAccessor; private readonly string[] _permittedExtensions = { ".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png" }; public FileUploadController( ILogger logger, - ApplicationDbContext context, - IHttpContextAccessor httpContextAccessor + ApplicationDbContext context ) { _logger = logger; _context = context; - _httpContextAccessor = httpContextAccessor; // To save physical files to the temporary files folder, use: //_targetDirectoryPath = Path.GetTempPath(); } @@ -93,7 +89,6 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - _httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 3cd5d6b0..131d6e66 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -26,9 +26,6 @@ double[] uValues warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod ) { @@ -45,9 +42,6 @@ public CalorimetricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, double[] gValues, double[] uValues ) : base( diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index ca1339c6..f4c1a556 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -14,9 +14,6 @@ protected DataX( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod ) : this( diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index f3711b2c..e6222cba 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -25,9 +25,6 @@ double[] thicknesses warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod ) { @@ -42,9 +39,6 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, double[] thicknesses ) : base( locale, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 9232019f..4212b1e0 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Database.Enumerations; namespace Database.Data; @@ -16,9 +15,6 @@ public HygrothermalData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod ) : base( locale, @@ -28,9 +24,6 @@ AppliedMethod appliedMethod warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod ) { @@ -44,10 +37,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide + DateTime createdAt ) : base( locale, componentId, diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 4087f1b1..c820539b 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -35,9 +35,6 @@ ICollection cielabColors warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod ) { diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 31f49002..537e2e78 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Database.Enumerations; namespace Database.Data; @@ -16,9 +15,6 @@ public PhotovoltaicData( string[] warnings, Guid creatorId, DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide, AppliedMethod appliedMethod ) : base( locale, @@ -28,9 +24,6 @@ AppliedMethod appliedMethod warnings, creatorId, createdAt, - type, - subtype, - coatedSide, appliedMethod ) { @@ -44,10 +37,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, - DataType? type, - DataSubtype? subtype, - CoatedSide? coatedSide + DateTime createdAt ) : base( locale, componentId, diff --git a/backend/src/Enumerations/DataSubtype.cs b/backend/src/Enumerations/OpticalComponentSubtype.cs similarity index 100% rename from backend/src/Enumerations/DataSubtype.cs rename to backend/src/Enumerations/OpticalComponentSubtype.cs diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 29a1f7dc..35df634e 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -7,8 +7,16 @@ namespace Database.Extensions; +/// +/// Extensions of +/// public static class HttpContextAccessorExtensions { + /// + /// Extract bearer token from . Only returns token if not expired. + /// + /// + /// Bearer token or null. public static async Task ExtractBearerToken(this IHttpContextAccessor httpContextAccessor) { if (httpContextAccessor.HttpContext is null) return null; @@ -24,6 +32,6 @@ public static class HttpContextAccessorExtensions private static bool CheckExpirationDate(string expirationDate) { - return DateTime.Parse(expirationDate) < DateTime.UtcNow; + return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate)) < DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs index 52f5dc2a..605a937a 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs @@ -8,5 +8,6 @@ public enum AddApprovalErrorCode UNKNOWN, UNAUTHENTICATED, UNAUTHORIZED, - UNKNOWN_DATA + UNKNOWN_DATA, + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs index ec299534..24286292 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs @@ -18,4 +18,12 @@ AddApprovalError error : base(error) { } + + public AddApprovalPayload( + DataApproval dataApproval, + AddApprovalError error + ) + : base(dataApproval, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index ef0dae2d..7f3ba0c0 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -5,7 +5,6 @@ using Database.Data; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; namespace Database.GraphQl.Approvals; @@ -16,14 +15,13 @@ public sealed class ApprovalMutations public async Task AddApprovalToDataAsync( ApprovalInput input, ApplicationDbContext context, - IHttpContextAccessor httpContextAccessor, IUserService userService, IDataService dataService, + IResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -93,6 +91,26 @@ CancellationToken cancellationToken } data.Approvals.Add(approval); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + ((Data.DataX)data).Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(data); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new AddApprovalPayload( + approval, + new AddApprovalError( + AddApprovalErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } return new AddApprovalPayload(approval); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index d45fd211..db831ac9 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -6,7 +7,6 @@ using Database.Data; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; namespace Database.GraphQl.CalorimetricDataX; @@ -18,12 +18,11 @@ public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, IUserService userService, - IHttpContextAccessor httpContextAccessor, + IResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -123,6 +122,26 @@ input.RootResource.AppliedConversionMethod is null calorimetricData.Resources.Add(resource); context.CalorimetricData.Add(calorimetricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + calorimetricData.Approval = await responseApprovalService.CreateResponseApproval(calorimetricData, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(calorimetricData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new CreateCalorimetricDataPayload( + calorimetricData, + new CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } return new CreateCalorimetricDataPayload(calorimetricData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs index 7bb980e9..4eb1768d 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs @@ -1,8 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Database.GraphQl.CalorimetricDataX; +[SuppressMessage("Naming", "CA1707")] public enum CreateCalorimetricDataErrorCode { UNKNOWN, UNAUTHORIZED, - UNAUTHENTICATED + UNAUTHENTICATED, + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs index e8d0bd23..3d4e2c16 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.CalorimetricDataX; @@ -18,4 +18,12 @@ CreateCalorimetricDataError error : base(error) { } + + public CreateCalorimetricDataPayload( + CalorimetricData calorimetricData, + CreateCalorimetricDataError error + ) + : base(calorimetricData, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 4fa37c44..4b9b7003 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -28,8 +28,8 @@ CancellationToken cancellationToken input.Description, input.Name, input.Locator), - apiRequestService, appSettings, + apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs index 29346d03..7371092b 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -1,9 +1,12 @@ -namespace Database.GraphQl.GeometricDataX; +using System.Diagnostics.CodeAnalysis; +namespace Database.GraphQl.GeometricDataX; + +[SuppressMessage("Naming", "CA1707")] public enum CreateGeometricDataErrorCode { UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNINGFAILED + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index cd97cfd4..dc116e2d 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -7,7 +7,6 @@ using Database.Data; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; namespace Database.GraphQl.GeometricDataX; @@ -21,14 +20,11 @@ public async Task CreateGeometricDataAsync( ApplicationDbContext context, IUserService userService, IResponseApprovalService responseApprovalService, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - httpContextAccessor, - cancellationToken).ConfigureAwait(false); - if (currentUser == null) + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + if (currentUser is null) { return new CreateGeometricDataPayload( new CreateGeometricDataError( @@ -114,15 +110,18 @@ input.RootResource.AppliedConversionMethod is null try { - geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, httpContextAccessor, cancellationToken).ConfigureAwait(false); + geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { + context.Remove(geometricData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return new CreateGeometricDataPayload( geometricData, new CreateGeometricDataError( - CreateGeometricDataErrorCode.SIGNINGFAILED, + CreateGeometricDataErrorCode.SIGNING_FAILED, $"Signing failed with message: {ex.Message}", [] ) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index e8ea1f77..82b4fd7c 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,15 +1,17 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; -using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GeometricDataX; @@ -19,18 +21,32 @@ public sealed class GeometricDataQueries [UsePaging] [UseFiltering(typeof(GeometricDataFilterType))] [UseSorting] - public IQueryable GetAllGeometricData( + public async Task> GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, + IAccessRightsService accessRightsService, ISortingContext sorting, - IResolverContext resolverContext + IResolverContext resolverContext, + CancellationToken cancellationToken ) { sorting.StabilizeOrder(); IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + + var errors = new List(); + foreach (var error in result.Errors) + { + errors.Add(new QueryError( + QueryErrorCode.RESTRICTED, + error, + [])); + } + // TODO Use `locale`. - return filteredData; + //return new QueryPayload(result.Data as List, errors); + return (result.Data as IEnumerable).AsQueryable(); } public Task GetGeometricDataAsync( diff --git a/backend/src/GraphQl/GeometricDataX/QueryError.cs b/backend/src/GraphQl/GeometricDataX/QueryError.cs new file mode 100644 index 00000000..919c3be5 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/QueryError.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.GeometricDataX; + +public class QueryError + : UserErrorBase +{ + public QueryError( + QueryErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} + +public enum QueryErrorCode +{ + RESTRICTED +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryPayload.cs b/backend/src/GraphQl/GeometricDataX/QueryPayload.cs new file mode 100644 index 00000000..d8824cde --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/QueryPayload.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.GeometricDataX; + +public class QueryPayload + : Payload +{ + public QueryPayload( + ICollection geometricData, + IReadOnlyCollection errors + ) + { + GeometricData = geometricData; + Errors = errors; + } + + public ICollection? GeometricData { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 1ab4d34c..c7596084 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -8,7 +8,6 @@ using Database.Enumerations; using Database.Extensions; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Database.Services; @@ -22,7 +21,6 @@ public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, IUserService userService, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { @@ -47,7 +45,6 @@ CancellationToken cancellationToken } var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs index 24ff8e3a..915c2070 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs @@ -1,8 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Database.GraphQl.HygrothermalDataX; +[SuppressMessage("Naming", "CA1707")] public enum CreateHygrothermalDataErrorCode { UNKNOWN, UNAUTHORIZED, - UNAUTHENTICATED + UNAUTHENTICATED, + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs index a08758c4..745d61fd 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.HygrothermalDataX; @@ -18,4 +18,12 @@ CreateHygrothermalDataError error : base(error) { } + + public CreateHygrothermalDataPayload( + HygrothermalData hygrothermalData, + CreateHygrothermalDataError error + ) + : base(hygrothermalData, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 1e77b553..6c97bd29 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -1,12 +1,14 @@ -using System.Linq; +using System; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.GraphQl.GeometricDataX; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; +using Org.BouncyCastle.Asn1.X509.Qualified; namespace Database.GraphQl.HygrothermalDataX; @@ -18,12 +20,11 @@ public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, IUserService userService, - IHttpContextAccessor httpContextAccessor, + IResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -120,6 +121,26 @@ input.RootResource.AppliedConversionMethod is null hygrothermalData.Resources.Add(resource); context.HygrothermalData.Add(hygrothermalData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + hygrothermalData.Approval = await responseApprovalService.CreateResponseApproval(hygrothermalData, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(hygrothermalData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new CreateHygrothermalDataPayload( + hygrothermalData, + new CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } return new CreateHygrothermalDataPayload(hygrothermalData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs index 99bdc110..4f1a5d28 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs @@ -1,8 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Database.GraphQl.OpticalDataX; +[SuppressMessage("Naming", "CA1707")] public enum CreateOpticalDataErrorCode { UNKNOWN, UNAUTHORIZED, - UNAUTHENTICATED + UNAUTHENTICATED, + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs index a354c283..6d8c502a 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.OpticalDataX; @@ -18,4 +18,12 @@ CreateOpticalDataError error : base(error) { } + + public CreateOpticalDataPayload( + OpticalData opticalData, + CreateOpticalDataError error + ) + : base(opticalData, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index ab9231b5..b41bccb8 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,12 +1,14 @@ -using System.Linq; +using System; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.GraphQl.GeometricDataX; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; +using Org.BouncyCastle.Asn1.X509.Qualified; namespace Database.GraphQl.OpticalDataX; @@ -18,12 +20,11 @@ public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, IUserService userService, - IHttpContextAccessor httpContextAccessor, + IResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -136,6 +137,26 @@ input.RootResource.AppliedConversionMethod is null opticalData.Resources.Add(resource); context.OpticalData.Add(opticalData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + opticalData.Approval = await responseApprovalService.CreateResponseApproval(opticalData, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(opticalData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new CreateOpticalDataPayload( + opticalData, + new CreateOpticalDataError( + CreateOpticalDataErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } return new CreateOpticalDataPayload(opticalData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs index d23a28d6..ad79dd0f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs @@ -1,8 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + namespace Database.GraphQl.PhotovoltaicDataX; +[SuppressMessage("Naming", "CA1707")] public enum CreatePhotovoltaicDataErrorCode { UNKNOWN, UNAUTHORIZED, - UNAUTHENTICATED + UNAUTHENTICATED, + SIGNING_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs index 94dc0e3d..37fafd74 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.PhotovoltaicDataX; @@ -18,4 +18,12 @@ CreatePhotovoltaicDataError error : base(error) { } + + public CreatePhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData, + CreatePhotovoltaicDataError error + ) + : base(photovoltaicData, error) + { + } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index b0ce3e2b..2b4e3041 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Text.Json; @@ -6,9 +7,10 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.GraphQl.GeometricDataX; using Database.Services; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; +using Org.BouncyCastle.Asn1.X509.Qualified; namespace Database.GraphQl.PhotovoltaicDataX; @@ -20,12 +22,11 @@ public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, IUserService userService, - IHttpContextAccessor httpContextAccessor, + IResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -37,12 +38,14 @@ CancellationToken cancellationToken ) ); } + if (!PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( currentUser, input.CreatorId, cancellationToken ) ) + { return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, @@ -50,6 +53,8 @@ CancellationToken cancellationToken [] ) ); + } + var photovoltaicData = new PhotovoltaicData( input.Locale, input.ComponentId, @@ -122,6 +127,26 @@ input.RootResource.AppliedConversionMethod is null photovoltaicData.Resources.Add(resource); context.PhotovoltaicData.Add(photovoltaicData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + photovoltaicData.Approval = await responseApprovalService.CreateResponseApproval(photovoltaicData, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(photovoltaicData); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new CreatePhotovoltaicDataPayload( + photovoltaicData, + new CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } return new CreatePhotovoltaicDataPayload(photovoltaicData); } } \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 6a09f322..da39e5df 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -14,24 +14,38 @@ namespace Database.Services; +/// +/// Implementation of +/// public class ApiRequestService() : IApiRequestService { + /// + /// Name of Metabase http client. + /// public const string MetabaseHttpClient = "Metabase"; + + /// + /// Name of Database http client. + /// public const string DatabaseHttpClient = "Database"; + private static bool _useMetabase = true; + /// public ApiRequestService Metabase() { _useMetabase = true; return this; } + /// public ApiRequestService Database() { _useMetabase = false; return this; } + /// public async Task ConstructGraphQlQuery( string[] fileNames ) @@ -46,6 +60,7 @@ await Task.WhenAll( ); } + /// public Task> QueryGraphQl( AppSettings appSettings, GraphQLRequest request, @@ -68,6 +83,7 @@ CancellationToken cancellationToken ); } + /// public Task QueryRest( Uri uri, IHttpClientFactory httpClientFactory, @@ -105,7 +121,10 @@ CancellationToken cancellationToken uri ); httpRequestMessage.Content = httpContent; - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + if (bearerToken != null) + { + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + } using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); if (httpResponseMessage.StatusCode != HttpStatusCode.OK) diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs new file mode 100644 index 00000000..3f7860aa --- /dev/null +++ b/backend/src/Services/CacheService.cs @@ -0,0 +1,60 @@ +using System; +using Database.ApiRequest.Dto; +using Microsoft.Extensions.Caching.Memory; + +namespace Database.Services; + +public class CacheService( + IMemoryCache currentUserCache, + IMemoryCache accessCountCache, + IMemoryCache timePeriodCountCache) : ICacheService +{ + public CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser) + { + var cacheEntryOptions = new MemoryCacheEntryOptions() + .SetSlidingExpiration(TimeSpan.FromHours(1)); + return currentUserCache.Set(token, cacheUser, cacheEntryOptions); + } + + public bool TryGetUser(string token, out CurrentUserDto? cacheUser) + { + return currentUserCache.TryGetValue(token, out cacheUser); + } + + public int GetAccessCountForUser(Guid userId) + { + int count; + if (!accessCountCache.TryGetValue(userId, out count)) + { + return accessCountCache.Set(userId, 0); + } + return count; + } + + public int SetAccessCountForUser(Guid userId, int count) + { + return accessCountCache.Set(userId, count); + } + + public (DateTime StartTime, int Count) GetOrCreateAccessCountForPerirod(Guid institutionId) + { + (DateTime StartTime, int Count) accessesPerPeriod; + if (!timePeriodCountCache.TryGetValue(institutionId, out accessesPerPeriod)) + { + return timePeriodCountCache.Set(institutionId, (DateTime.Now, 0)); + } + return accessesPerPeriod; + } + + public (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId) + { + var accessesPerPeriod = GetOrCreateAccessCountForPerirod(institutionId); + accessesPerPeriod.Count++; + return timePeriodCountCache.Set(institutionId, accessesPerPeriod); + } + + public (DateTime StartTime, int Count) SetNewTimePeriod(Guid institutionId) + { + return timePeriodCountCache.Set(institutionId, (DateTime.Now, 0)); + } +} \ No newline at end of file diff --git a/backend/src/Services/IApiRequestService.cs b/backend/src/Services/IApiRequestService.cs index d8ef690c..d301c85d 100644 --- a/backend/src/Services/IApiRequestService.cs +++ b/backend/src/Services/IApiRequestService.cs @@ -7,14 +7,40 @@ namespace Database.Services; +/// +/// Service to request Metabase or Database Api. GraphQL and REST. +/// public interface IApiRequestService { + /// + /// Use Metabase as target for following requests. + /// + /// public ApiRequestService Metabase(); + /// + /// Use Database as target for following requests. + /// + /// public ApiRequestService Database(); + /// + /// Construct query from passed files. + /// + /// Name of files containung queries. + /// Query from all files. public Task ConstructGraphQlQuery(string[] fileNames); + /// + /// Send GraphQL request to API and return response. + /// + /// Expected response type. + /// + /// + /// + /// + /// + /// of expected type. public Task> QueryGraphQl( AppSettings appSettings, GraphQLRequest request, @@ -24,6 +50,15 @@ CancellationToken cancellationToken ) where TGraphQlResponse : class; + /// + /// Send REST request to API and return response. + /// + /// Expected response type. + /// to send request to. + /// + /// + /// + /// public Task QueryRest( Uri uri, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Services/ICacheService.cs b/backend/src/Services/ICacheService.cs new file mode 100644 index 00000000..97bece39 --- /dev/null +++ b/backend/src/Services/ICacheService.cs @@ -0,0 +1,21 @@ +using System; +using Database.ApiRequest.Dto; + +namespace Database.Services; + +public interface ICacheService +{ + bool TryGetUser(string token, out CurrentUserDto? cacheUser); + + CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser); + + int GetAccessCountForUser(Guid userId); + + int SetAccessCountForUser(Guid userId, int count); + + (DateTime StartTime, int Count) GetOrCreateAccessCountForPerirod(Guid institutionId); + + (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId); + + (DateTime StartTime, int Count) SetNewTimePeriod(Guid institutionId); +} \ No newline at end of file diff --git a/backend/src/Services/IResponseApprovalService.cs b/backend/src/Services/IResponseApprovalService.cs index d3460d4e..6878aee1 100644 --- a/backend/src/Services/IResponseApprovalService.cs +++ b/backend/src/Services/IResponseApprovalService.cs @@ -1,11 +1,19 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; -using Microsoft.AspNetCore.Http; namespace Database.Services; +/// +/// Service to create response approval +/// public interface IResponseApprovalService { - Task CreateResponseApproval(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); + /// + /// Create response approval by calling graphql Api and signing responce. + /// + /// + /// + /// + Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index 4d346eeb..d8f866e6 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using System; namespace Database.Services; @@ -8,16 +9,17 @@ namespace Database.Services; public interface ISigningService { /// - /// Initialize service by importing private key from file passed into container. + /// Get fingerprint /// - /// True, if privat key was successfully imported and fingerprint set. - public Task Initialize(); + /// Fingerprint as string + public string Fingerprint { get; } /// - /// Get fingerprint + /// Initialize service by importing private key from file passed into container. /// - /// Fingerprint as string - public string GetFingerprint(); + /// True, if privat key was successfully imported and fingerprint set. + /// If initialization failed. + public Task Initialize(); /// /// Sign passed data string and return signature. diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index bd349f3f..022a9295 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequest.Dto; -using Microsoft.AspNetCore.Http; namespace Database.Services; @@ -11,10 +10,15 @@ namespace Database.Services; public interface IUserService { /// - /// Get current user from Mewtabase by extraxting token from . + /// Get current user from Metabase. /// - /// - /// + /// /// - public Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken); + public Task GetCurrentUser(CancellationToken cancellationToken); + + /// + /// Get application from user calims. + /// + /// ClientId from claims as applicationId. + string? GetApplicationIdFromUser(); } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 4c0169c6..3e27fae0 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -10,58 +10,69 @@ namespace Database.Services; +/// +/// Implementation of +/// +/// +/// +/// +/// +/// +/// public class ResponseApprovalService( AppSettings appSettings, ISigningService signingService, IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, ILogger logger) : IResponseApprovalService { - public async Task CreateResponseApproval(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + /// + public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset logger.GetQueryAndResponse(dataObject.GetType().Name); - var queryAdnResponce = await GetQueryAndResponse(dataObject, httpContextAccessor, cancellationToken).ConfigureAwait(false); + var queryAdnResponse = await GetQueryAndResponse(dataObject, cancellationToken).ConfigureAwait(false); - if (string.IsNullOrEmpty(queryAdnResponce.Query)) + if (string.IsNullOrEmpty(queryAdnResponse.Query)) { - throw new ArgumentNullException(queryAdnResponce.Query); + throw new ArgumentNullException(queryAdnResponse.Query); } - if (string.IsNullOrEmpty(queryAdnResponce.Response)) + if (string.IsNullOrEmpty(queryAdnResponse.Response)) { - throw new ArgumentNullException(queryAdnResponce.Response); + throw new ArgumentNullException(queryAdnResponse.Response); } - logger.QueryAndResponce(queryAdnResponce.Query, queryAdnResponce.Response); + logger.QueryAndResponce(queryAdnResponse.Query, queryAdnResponse.Response); - var signatureResult = await signingService.SignData(queryAdnResponce.Response).ConfigureAwait(false); + var signatureResult = await signingService.SignData(queryAdnResponse.Response).ConfigureAwait(false); if (!signatureResult.Success) { throw new ArgumentException("Signing failed"); } - return new ResponseApproval(DateTime.Now, signatureResult.Signature, signingService.GetFingerprint(), queryAdnResponce.Query, queryAdnResponce.Response); + return new ResponseApproval(DateTime.UtcNow, signatureResult.Signature, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); } - private async Task<(string? Query, string? Response)> GetQueryAndResponse(object dataObject, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + private async Task<(string? Query, string? Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) { switch (dataObject) { case GeometricData data: - return await DataApi.GetQueryAndResponceGeometricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponseGeometricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case CalorimetricData data: - return await DataApi.GetQueryAndResponceCalorimetricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponseCalorimetricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case HygrothermalData data: - return await DataApi.GetQueryAndResponceHygrothermalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponseHygrothermalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case PhotovoltaicData data: - return await DataApi.GetQueryAndResponcePhotovoltaicData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponsePhotovoltaicData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case OpticalData data: - return await DataApi.GetQueryAndResponceOpticalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponseOpticalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); default: return (null, null); diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 668d9123..aee7bc83 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -16,35 +16,31 @@ public class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { - private string _fingerprint = ""; private const string FILENAME = "dataToSign"; + private const string FILE_EXTENSION = ".asc"; /// - public async Task Initialize() + public string Fingerprint { get; private set; } = ""; + + /// + public async Task Initialize() { // Execute command to import private key var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { // Extract fingerprint - _fingerprint = await GetFingerprintFromKeyList(); - if (!String.IsNullOrEmpty(_fingerprint)) + Fingerprint = await GetFingerprintFromKeyList(); + if (!String.IsNullOrEmpty(Fingerprint)) { - logger.Fingerprint(_fingerprint); + logger.Fingerprint(Fingerprint); } else { logger.FingerprintError(); - success = false; + throw new InvalidOperationException("Failed to initialize GNUPG signing service."); } } - return success; - } - - /// - public string GetFingerprint() - { - return _fingerprint; } /// @@ -59,7 +55,7 @@ public string GetFingerprint() } // Execute command to generate detached signature file Creates file with .asc ending - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {_fingerprint} {FILENAME}"); + var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {Fingerprint} {FILENAME}"); if (success) { @@ -145,7 +141,7 @@ private bool ReadSignatureFromFile(out string signature) { try { - using (StreamReader file = new StreamReader(FILENAME + ".asc")) + using (StreamReader file = new StreamReader(FILENAME + FILE_EXTENSION)) { signature = file.ReadToEnd(); } @@ -162,6 +158,6 @@ private bool ReadSignatureFromFile(out string signature) private static void RemoveFiles() { File.Delete(FILENAME); - File.Delete(FILENAME + ".asc"); + File.Delete(FILENAME + FILE_EXTENSION); } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 38ec25ec..6595bcd4 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,5 +1,4 @@ -using System; -using System.Net.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.ApiRequest; @@ -7,27 +6,36 @@ using Database.Extensions; using Database.Logging; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; +using OpenIddict.Abstractions; +using static OpenIddict.Abstractions.OpenIddictConstants; namespace Database.Services; /// /// Implementation of /// -/// -/// -/// to store already known users. -/// Instance of +/// +/// +/// +/// to store already known users. +/// Instance of public class UserService( AppSettings appSettings, IApiRequestService apiRequestService, + IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory, - IMemoryCache cache, + ICacheService cacheService, ILogger logger) : IUserService { /// - public async Task GetCurrentUser(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + public string? GetApplicationIdFromUser() + { + return httpContextAccessor.HttpContext?.User.GetClaim(Claims.ClientId); + } + + /// + public async Task GetCurrentUser(CancellationToken cancellationToken) { // Extract token var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); @@ -35,24 +43,22 @@ public class UserService( if (token == null) { - return await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); + return await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); } // Check if there is already a user for token - if (!cache.TryGetValue(token, out CurrentUserDto? cacheUser)) + if (!cacheService.TryGetUser(token, out CurrentUserDto? cacheUser)) { // Get user from Metabase - cacheUser = await GetCurrentUserFromMetabase(httpContextAccessor, cancellationToken).ConfigureAwait(false); - var cacheEntryOptions = new MemoryCacheEntryOptions() - .SetSlidingExpiration(TimeSpan.FromHours(1)); + cacheUser = await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); // Store user in cache - cache.Set(token, cacheUser, cacheEntryOptions); + cacheService.SetUser(token, cacheUser); } return cacheUser; } - private Task GetCurrentUserFromMetabase(IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) + private Task GetCurrentUserFromMetabase(CancellationToken cancellationToken) { return UserApi.RequestCurrentUser( appSettings, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 13951249..b4d1152f 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -61,11 +61,13 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(_appSettings); services.AddSingleton(_environment); // services.AddDatabaseDeveloperPageExceptionFilter(); - services.AddSingleton(); - services.AddScoped(); services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) From db771c5a4df9a3f06fcb315e2d8be0fc5fb9b52a Mon Sep 17 00:00:00 2001 From: jbinterap Date: Tue, 25 Mar 2025 13:21:55 +0100 Subject: [PATCH 302/878] Start implementing access rights --- backend/src/Data/AccessRights.cs | 47 + backend/src/Data/DataX.cs | 31 + backend/src/Data/IData.cs | 6 + ...essRightsError.cs => AccessRightsError.cs} | 8 +- ...sErrorCode.cs => AccessRightsErrorCode.cs} | 2 +- .../AccessRights/AddAccessRightsPayload.cs | 4 +- .../AddDataAccessRightsPayload.cs | 21 - .../AccessRights/DataAccessRightsInput.cs | 2 - .../AccessRights/DataAccessRightsMutations.cs | 62 +- .../AccessRights/UpdateAccessRightsPayload.cs | 19 + ...rror.cs => UpdateDataAccessRightsError.cs} | 8 +- ....cs => UpdateDataAccessRightsErrorCode.cs} | 2 +- .../UpdateDataAccessRightsPayload.cs | 21 + ...20250319132121_AddAccessRights.Designer.cs | 2324 +++++++++++++++++ .../20250319132121_AddAccessRights.cs | 427 +++ .../ApplicationDbContextModelSnapshot.cs | 225 +- ...onal_to_20250319132121_AddAccessRights.sql | 78 + ...onal_to_20250319132121_AddAccessRights.sql | 72 + backend/src/Services/AccessRightsService.cs | 73 + backend/src/Services/IAccessRightsService.cs | 21 + 20 files changed, 3385 insertions(+), 68 deletions(-) rename backend/src/GraphQl/AccessRights/{AddAccessRightsError.cs => AccessRightsError.cs} (56%) rename backend/src/GraphQl/AccessRights/{AddAccessRightsErrorCode.cs => AccessRightsErrorCode.cs} (85%) delete mode 100644 backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs rename backend/src/GraphQl/AccessRights/{AddDataAccessRightsError.cs => UpdateDataAccessRightsError.cs} (52%) rename backend/src/GraphQl/AccessRights/{AddDataAccessRightsErrorCode.cs => UpdateDataAccessRightsErrorCode.cs} (81%) create mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs create mode 100644 backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs create mode 100644 backend/src/Migrations/20250319132121_AddAccessRights.cs create mode 100644 backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql create mode 100644 backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql create mode 100644 backend/src/Services/AccessRightsService.cs create mode 100644 backend/src/Services/IAccessRightsService.cs diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/AccessRights.cs index b087e88d..fdbe80c0 100644 --- a/backend/src/Data/AccessRights.cs +++ b/backend/src/Data/AccessRights.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.Services; namespace Database.Data; @@ -25,4 +26,50 @@ TimeSpan period Period = period; UserOfInstitution = new List(); } + + internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out List errors) + { + bool isRestricted = false; + errors = new List(); + if (this.AllowedDatasetsPerTime > 0) + { + var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPerirod(InstitutionId); + + if (accessesPerPeriod.StartTime.Add(Period) < DateTime.Now) + { + if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) + { + isRestricted = true; + errors.Add($"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets reached."); + } + } + else + { + cacheService.SetNewTimePeriod(InstitutionId); + } + } + + if (this.AllowedUserCount > 0) + { + if (this.UserOfInstitution.Count >= this.AllowedUserCount) + { + isRestricted = true; + errors.Add($"Id: {dataItem.Id} Reason: Maximum allowed users of institution reached."); + } + } + + if (!isRestricted) + { + if (this.AllowedDatasetsPerTime > 0) + { + cacheService.AddAccessCountToPeriod(InstitutionId); + } + if (this.AllowedUserCount > 0) + { + this.UserOfInstitution.Add(currentUserId); + } + } + + return isRestricted; + } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index f4c1a556..e167e72c 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Enumerations; namespace Database.Data; @@ -67,4 +69,33 @@ DateTime createdAt public DataAccessMode DataAccess { get; set; } = DataAccessMode.UNRESTRICTED; public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); + + public bool IsRestrictedByApplication(string applicationId) + { + if (this.DataAccess == DataAccessMode.UNRESTRICTED) + return false; + return this.DataAccessRights.AllowedApplications.Contains(applicationId); + } + + public bool IsRestrictedByInstitutions(List institutions) + { + if (this.DataAccess == DataAccessMode.UNRESTRICTED) + return false; + return this.DataAccessRights.AllowedInstitutions.Any(a => institutions.Any(b => a.Equals(b))); + } + + public bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount) + { + if (this.DataAccess == DataAccessMode.UNRESTRICTED) + return false; + int limit; + if (this.DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out limit)) + { + if (limit < 0 || limit > alreadyAccesedCount) + { + return false; + } + } + return true; + } } \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index bb51e89f..f83760e9 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -19,4 +19,10 @@ public interface IData : IEntity string Locale { get; } DataAccessMode DataAccess { get; set; } DataAccessRights DataAccessRights { get; } + + bool IsRestrictedByApplication(string applicationId); + + bool IsRestrictedByInstitutions(List institutions); + + bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount); } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsError.cs b/backend/src/GraphQl/AccessRights/AccessRightsError.cs similarity index 56% rename from backend/src/GraphQl/AccessRights/AddAccessRightsError.cs rename to backend/src/GraphQl/AccessRights/AccessRightsError.cs index 020d1197..37feb2b5 100644 --- a/backend/src/GraphQl/AccessRights/AddAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsError.cs @@ -2,11 +2,11 @@ namespace Database.GraphQl.AccessRights; -public class AddAccessRightsError - : UserErrorBase +public class AccessRightsError + : UserErrorBase { - public AddAccessRightsError( - AddAccessRightsErrorCode code, + public AccessRightsError( + AccessRightsErrorCode code, string message, IReadOnlyList path ) diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs similarity index 85% rename from backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs rename to backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs index 56ad8e30..f9193e0f 100644 --- a/backend/src/GraphQl/AccessRights/AddAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.AccessRights; [SuppressMessage("Naming", "CA1707")] -public enum AddAccessRightsErrorCode +public enum AccessRightsErrorCode { UNKNOWN, UNAUTHENTICATED, diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs index 636d9a7d..faa04623 100644 --- a/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs @@ -1,7 +1,7 @@ namespace Database.GraphQl.AccessRights; public class AddAccessRightsPayload - : AccessRightsPayload + : AccessRightsPayload { public AddAccessRightsPayload( Data.AccessRights accessRights @@ -11,7 +11,7 @@ Data.AccessRights accessRights } public AddAccessRightsPayload( - AddAccessRightsError error + AccessRightsError error ) : base(error) { diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs deleted file mode 100644 index acf790ab..00000000 --- a/backend/src/GraphQl/AccessRights/AddDataAccessRightsPayload.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.AccessRights; - -public class AddDataAccessRightsPayload - : DataAccessRightsPayload -{ - public AddDataAccessRightsPayload( - DataAccessRights dataAccessRights - ) - : base(dataAccessRights) - { - } - - public AddDataAccessRightsPayload( - AddDataAccessRightsError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs index c17e6c3e..403432ac 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs @@ -1,12 +1,10 @@ using System; using Database.Data; -using Database.Enumerations; namespace Database.GraphQl.AccessRights; public sealed record DataAccessRightsInput ( Guid DataId, - DataAccessMode DataAccessMode, DataAccessRights DataAccessRights ); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 89732a37..fe6ffbf0 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -12,7 +12,7 @@ namespace Database.GraphQl.AccessRights; [ExtendObjectType(nameof(Mutation))] public class DataAccessRightsMutations { - public async Task AddAccessRightsToDataAsync( + public async Task UpdateAccessRightsToDataAsync( DataAccessRightsInput input, ApplicationDbContext context, AppSettings appSettings, @@ -24,13 +24,12 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { - return new AddDataAccessRightsPayload( - new AddDataAccessRightsError( - AddDataAccessRightsErrorCode.UNAUTHENTICATED, + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, $"The user is not authenticated.", [] ) @@ -57,27 +56,32 @@ CancellationToken cancellationToken var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); if (data == null) { - return new AddDataAccessRightsPayload( - new AddDataAccessRightsError( - AddDataAccessRightsErrorCode.UNKNOWN_DATA, + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, $"Unknown data.", [] ) ); } - data.DataAccess = input.DataAccessMode; - if (data.DataAccess == Enumerations.DataAccessMode.RESTRICTED) - { - data.DataAccessRights.AllowedInstitutions = input.DataAccessRights.AllowedInstitutions; - - data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; + data.DataAccessRights.AllowedInstitutions = input.DataAccessRights.AllowedInstitutions; + data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; + data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; - data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; + if (data.DataAccessRights.AllowedInstitutions?.Count > 0 + || data.DataAccessRights.AllowedApplications?.Count > 0 + || data.DataAccessRights.AllowedUserAndQuantity?.Count > 0) + { + data.DataAccess = Enumerations.DataAccessMode.RESTRICTED; + } + else + { + data.DataAccess = Enumerations.DataAccessMode.UNRESTRICTED; } await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new AddDataAccessRightsPayload(data.DataAccessRights); + return new UpdateDataAccessRightsPayload(data.DataAccessRights); } public async Task AddAccessRightsAsync( @@ -92,13 +96,12 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { return new AddAccessRightsPayload( - new AddAccessRightsError( - AddAccessRightsErrorCode.UNAUTHENTICATED, + new AccessRightsError( + AccessRightsErrorCode.UNAUTHENTICATED, $"The user is not authenticated.", [] ) @@ -127,8 +130,8 @@ CancellationToken cancellationToken if (accessRights is not null) { return new AddAccessRightsPayload( - new AddAccessRightsError( - AddAccessRightsErrorCode.ALREADY_EXISTS, + new AccessRightsError( + AccessRightsErrorCode.ALREADY_EXISTS, $"The access rights for this institution already exist.", [] ) @@ -148,7 +151,7 @@ CancellationToken cancellationToken return new AddAccessRightsPayload(accessRights); } - public async Task UpdateAccessRightsAsync( + public async Task UpdateAccessRightsAsync( AccessRightsInput input, ApplicationDbContext context, AppSettings appSettings, @@ -160,13 +163,12 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { - return new AddAccessRightsPayload( - new AddAccessRightsError( - AddAccessRightsErrorCode.UNAUTHENTICATED, + return new UpdateAccessRightsPayload( + new AccessRightsError( + AccessRightsErrorCode.UNAUTHENTICATED, $"The user is not authenticated.", [] ) @@ -200,9 +202,9 @@ CancellationToken cancellationToken } else { - return new AddAccessRightsPayload( - new AddAccessRightsError( - AddAccessRightsErrorCode.UNKNOWN_ACCESSRIGHTS, + return new UpdateAccessRightsPayload( + new AccessRightsError( + AccessRightsErrorCode.UNKNOWN_ACCESSRIGHTS, $"There are no access rights for this institution.", [] ) @@ -210,6 +212,6 @@ CancellationToken cancellationToken } await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new AddAccessRightsPayload(accessRights); + return new UpdateAccessRightsPayload(accessRights); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs new file mode 100644 index 00000000..ed49a046 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs @@ -0,0 +1,19 @@ +namespace Database.GraphQl.AccessRights; + +public class UpdateAccessRightsPayload + : AccessRightsPayload +{ + public UpdateAccessRightsPayload( + Data.AccessRights accessRights + ) + : base(accessRights) + { + } + + public UpdateAccessRightsPayload( + AccessRightsError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs similarity index 52% rename from backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs rename to backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs index 850200aa..c235d264 100644 --- a/backend/src/GraphQl/AccessRights/AddDataAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs @@ -2,11 +2,11 @@ namespace Database.GraphQl.AccessRights; -public class AddDataAccessRightsError - : UserErrorBase +public class UpdateDataAccessRightsError + : UserErrorBase { - public AddDataAccessRightsError( - AddDataAccessRightsErrorCode code, + public UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode code, string message, IReadOnlyList path ) diff --git a/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs similarity index 81% rename from backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs rename to backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs index 57e0cf22..0e9d68f1 100644 --- a/backend/src/GraphQl/AccessRights/AddDataAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.AccessRights; [SuppressMessage("Naming", "CA1707")] -public enum AddDataAccessRightsErrorCode +public enum UpdateDataAccessRightsErrorCode { UNKNOWN, UNAUTHENTICATED, diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs new file mode 100644 index 00000000..ed48f464 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs @@ -0,0 +1,21 @@ +using Database.Data; + +namespace Database.GraphQl.AccessRights; + +public class UpdateDataAccessRightsPayload + : DataAccessRightsPayload +{ + public UpdateDataAccessRightsPayload( + DataAccessRights dataAccessRights + ) + : base(dataAccessRights) + { + } + + public UpdateDataAccessRightsPayload( + UpdateDataAccessRightsError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs b/backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs new file mode 100644 index 00000000..a535dd5e --- /dev/null +++ b/backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs @@ -0,0 +1,2324 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250319132121_AddAccessRights")] + partial class AddAccessRights + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("integer"); + + b.Property("AllowedUserCount") + .HasColumnType("integer"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserOfInstitution") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DataAccess") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DataAccess") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DataAccess") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DataAccess") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("DataAccess") + .HasColumnType("integer"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250319132121_AddAccessRights.cs b/backend/src/Migrations/20250319132121_AddAccessRights.cs new file mode 100644 index 00000000..49540eca --- /dev/null +++ b/backend/src/Migrations/20250319132121_AddAccessRights.cs @@ -0,0 +1,427 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddAccessRights : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccess", + schema: "database", + table: "photovoltaic_data", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccess", + schema: "database", + table: "optical_data", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccess", + schema: "database", + table: "hygrothermal_data", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "geometric_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccess", + schema: "database", + table: "geometric_data", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccess", + schema: "database", + table: "calorimetric_data", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateTable( + name: "access_rights", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + InstitutionId = table.Column(type: "uuid", nullable: false), + AllowedUserCount = table.Column(type: "integer", nullable: false), + UserOfInstitution = table.Column>(type: "uuid[]", nullable: false), + AllowedDatasetsPerTime = table.Column(type: "integer", nullable: false), + Period = table.Column(type: "interval", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_access_rights", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "access_rights", + schema: "database"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccess", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccess", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccess", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccess", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccess", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 9f5691a8..70b49773 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,5 +1,6 @@ // using System; +using System.Collections.Generic; using System.Text.Json; using Database.Data; using Database.Enumerations; @@ -20,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("ProductVersion", "9.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); @@ -31,6 +32,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Database.Data.AccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("integer"); + + b.Property("AllowedUserCount") + .HasColumnType("integer"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserOfInstitution") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("access_rights", "database"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Property("Id") @@ -47,6 +82,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); + b.Property("DataAccess") + .HasColumnType("integer"); + b.Property("Description") .HasColumnType("text"); @@ -96,6 +134,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); + b.Property("DataAccess") + .HasColumnType("integer"); + b.Property("Description") .HasColumnType("text"); @@ -139,7 +180,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid"); b.Property("Description") - .IsRequired() .HasColumnType("text"); b.Property("GeometricDataId") @@ -200,6 +240,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); + b.Property("DataAccess") + .HasColumnType("integer"); + b.Property("Description") .HasColumnType("text"); @@ -248,6 +291,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); + b.Property("DataAccess") + .HasColumnType("integer"); + b.Property("Description") .HasColumnType("text"); @@ -315,6 +361,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); + b.Property("DataAccess") + .HasColumnType("integer"); + b.Property("Description") .HasColumnType("text"); @@ -696,6 +745,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("CalorimetricDataId") @@ -848,6 +929,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AppliedMethod") .IsRequired(); + b.Navigation("Approval"); + b.Navigation("Approvals"); }); @@ -954,6 +1037,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("GeometricDataId") @@ -1106,6 +1221,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AppliedMethod") .IsRequired(); + b.Navigation("Approval"); + b.Navigation("Approvals"); }); @@ -1330,6 +1447,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("HygrothermalDataId") @@ -1482,6 +1631,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AppliedMethod") .IsRequired(); + b.Navigation("Approval"); + b.Navigation("Approvals"); }); @@ -1616,6 +1767,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("OpticalDataId") @@ -1768,6 +1951,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AppliedMethod") .IsRequired(); + b.Navigation("Approval"); + b.Navigation("Approvals"); b.Navigation("CielabColors"); @@ -1876,6 +2061,38 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Sources"); }); + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => { b1.Property("PhotovoltaicDataId") @@ -2028,6 +2245,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AppliedMethod") .IsRequired(); + b.Navigation("Approval"); + b.Navigation("Approvals"); }); @@ -2099,4 +2318,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} \ No newline at end of file +} diff --git a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql new file mode 100644 index 00000000..a5d7e9ff --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql @@ -0,0 +1,78 @@ +START TRANSACTION; +ALTER TABLE database.photovoltaic_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Query" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Response" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Signature" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.photovoltaic_data ADD "DataAccess" integer NOT NULL DEFAULT 0; + +ALTER TABLE database.optical_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.optical_data ADD "Approval_Query" text; + +ALTER TABLE database.optical_data ADD "Approval_Response" text; + +ALTER TABLE database.optical_data ADD "Approval_Signature" text; + +ALTER TABLE database.optical_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.optical_data ADD "DataAccess" integer NOT NULL DEFAULT 0; + +ALTER TABLE database.hygrothermal_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Query" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Response" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Signature" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.hygrothermal_data ADD "DataAccess" integer NOT NULL DEFAULT 0; + +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" DROP NOT NULL; + +ALTER TABLE database.geometric_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.geometric_data ADD "Approval_Query" text; + +ALTER TABLE database.geometric_data ADD "Approval_Response" text; + +ALTER TABLE database.geometric_data ADD "Approval_Signature" text; + +ALTER TABLE database.geometric_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.geometric_data ADD "DataAccess" integer NOT NULL DEFAULT 0; + +ALTER TABLE database.calorimetric_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Query" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Response" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Signature" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.calorimetric_data ADD "DataAccess" integer NOT NULL DEFAULT 0; + +CREATE TABLE database.access_rights ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "InstitutionId" uuid NOT NULL, + "AllowedUserCount" integer NOT NULL, + "UserOfInstitution" uuid[] NOT NULL, + "AllowedDatasetsPerTime" integer NOT NULL, + "Period" interval NOT NULL, + CONSTRAINT "PK_access_rights" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250319132121_AddAccessRights', '9.0.2'); + +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql new file mode 100644 index 00000000..e99794f2 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql @@ -0,0 +1,72 @@ +START TRANSACTION; +DROP TABLE database.access_rights; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_KeyFingerprint"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Query"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Response"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Signature"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Timestamp"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccess"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_KeyFingerprint"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_Query"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_Response"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_Signature"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_Timestamp"; + +ALTER TABLE database.optical_data DROP COLUMN "DataAccess"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_KeyFingerprint"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Query"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Response"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Signature"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Timestamp"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccess"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_KeyFingerprint"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_Query"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_Response"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_Signature"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_Timestamp"; + +ALTER TABLE database.geometric_data DROP COLUMN "DataAccess"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_KeyFingerprint"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Query"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Response"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Signature"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Timestamp"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccess"; + +UPDATE database.get_https_resource SET "Description" = '' WHERE "Description" IS NULL; +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET NOT NULL; +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET DEFAULT ''; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250319132121_AddAccessRights'; + +COMMIT; + diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs new file mode 100644 index 00000000..484afc07 --- /dev/null +++ b/backend/src/Services/AccessRightsService.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.Services; + +/// +/// Implementation of +/// +/// +/// +/// +public class AccessRightsService( + ApplicationDbContext context, + IUserService userService, + ICacheService cacheService) : IAccessRightsService +{ + /// + public async Task<(IEnumerable Data, IEnumerable Restrictions)> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) + { + List filteredData = new List(); + List restrictions = new List(); + var applicationId = userService.GetApplicationIdFromUser(); + List institutions = new List(); + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); + + List accessRights = new List(); + foreach (var institution in currentUser.RepresentedInstitutions.Edges) + { + institutions.Add(institution.Node.Uuid); + var accessRightOfInstitution = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == institution.Node.Uuid, cancellationToken).ConfigureAwait(false); + if (accessRightOfInstitution is not null) + { + accessRights.Add(accessRightOfInstitution); + } + } + + foreach (IData dataItem in data) + { + if (dataItem.DataAccess == Enumerations.DataAccessMode.RESTRICTED) + { + if (dataItem.IsRestrictedByApplication(applicationId)) + { + restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Application not allowed."); + continue; + } + if (dataItem.IsRestrictedByInstitutions(institutions)) + { + restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Institution not allowed."); + continue; + } + if (dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedCount)) + { + restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Allowed accesses for user reached."); + continue; + } + if (accessRights.Any(x => x.IsDataRestricted(dataItem, currentUser.Uuid, cacheService, out restrictions))) + { + continue; + } + } + + filteredData.Add(dataItem); + } + + return (filteredData, restrictions); + } +} \ No newline at end of file diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs new file mode 100644 index 00000000..eb3345cf --- /dev/null +++ b/backend/src/Services/IAccessRightsService.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; + +namespace Database.Services; + +/// +/// Service to check if requested data can be returned regarding access rights. +/// +public interface IAccessRightsService +{ + /// + /// Apply access rights to passed data list. + /// + /// Data to apply acces rights on. + /// + /// List of data that can be returned and restrictions. + Task<(IEnumerable Data, IEnumerable Restrictions)> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken); +} \ No newline at end of file From 687b87ba03a468e341410966e9c304a93ebd25c1 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Tue, 25 Mar 2025 13:25:39 +0100 Subject: [PATCH 303/878] Fix issue --- backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 82b4fd7c..b40bb201 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken var result = await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); var errors = new List(); - foreach (var error in result.Errors) + foreach (var error in result.Restrictions) { errors.Add(new QueryError( QueryErrorCode.RESTRICTED, From 442db13f9e4fbe2c4b4e187882a544a5331c6e82 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Tue, 25 Mar 2025 13:33:58 +0100 Subject: [PATCH 304/878] Removed access rights for now --- backend/src/Data/DataX.cs | 1 + .../AccessRights/DataAccessRightsMutations.cs | 3 -- .../GeometricDataX/GeometricDataQueries.cs | 28 +++++++++---------- backend/src/Startup.cs | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index f4c1a556..9b9f23c1 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Database.Enumerations; namespace Database.Data; diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 89732a37..9ab2de2d 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -24,7 +24,6 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -92,7 +91,6 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { @@ -160,7 +158,6 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - httpContextAccessor, cancellationToken).ConfigureAwait(false); if (currentUser == null) { diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index b40bb201..91493445 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -24,7 +24,7 @@ public sealed class GeometricDataQueries public async Task> GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + //IAccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -32,21 +32,21 @@ CancellationToken cancellationToken { sorting.StabilizeOrder(); IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + return filteredData; + //var result = await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + //var errors = new List(); + //foreach (var error in result.Restrictions) + //{ + // errors.Add(new QueryError( + // QueryErrorCode.RESTRICTED, + // error, + // [])); + //} - var errors = new List(); - foreach (var error in result.Restrictions) - { - errors.Add(new QueryError( - QueryErrorCode.RESTRICTED, - error, - [])); - } - - // TODO Use `locale`. - //return new QueryPayload(result.Data as List, errors); - return (result.Data as IEnumerable).AsQueryable(); + //// TODO Use `locale`. + ////return new QueryPayload(result.Data as List, errors); + //return (result.Data as IEnumerable).AsQueryable(); } public Task GetGeometricDataAsync( diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index b4d1152f..8959d567 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -67,7 +67,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + //services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) From 96bb69e799f661273d6820ab277f5eb7241ad688 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 26 Mar 2025 11:10:05 +0100 Subject: [PATCH 305/878] Address review findings --- backend/src/ApiRequest/DataApi.cs | 223 ------------------ .../src/ApiRequest/Dto/DatabaseRequestDto.cs | 10 - .../Queries/CalorimetricData.graphql | 79 ------- .../ApiRequest/Queries/GeometricData.graphql | 80 ------- .../Queries/HygrothermalData.graphql | 77 ------ .../ApiRequest/Queries/OpticalData.graphql | 83 ------- .../Queries/PhotovoltaicData.graphql | 77 ------ backend/src/ApiRequests/DataApi.cs | 96 ++++++++ .../DatabaseApi.cs | 9 +- .../Dto/CurrentUserDto.cs | 3 +- .../Dto/DatabaseDto.cs | 0 .../Dto/UserInfoDto.cs | 2 +- .../Dto/XDataDto.cs | 2 +- .../JsonSerializerOptions.cs | 2 +- .../Queries/CalorimetricData.graphql | 5 + .../Queries/CalorimetricDataFields.graphql | 6 + .../Queries/CurrentUser.graphql | 0 .../ApiRequests/Queries/DataFields.graphql | 82 +++++++ .../Queries/Databases.graphql | 0 .../ApiRequests/Queries/GeometricData.graphql | 5 + .../Queries/GeometricDataFields.graphql | 5 + .../Queries/HygrothermalData.graphql | 5 + .../Queries/HygrothermalDataFields.graphql | 4 + .../ApiRequests/Queries/OpticalData.graphql | 5 + .../Queries/OpticalDataFields.graphql | 10 + .../Queries/PhotovoltaicData.graphql | 5 + .../Queries/PhotovoltaicDataFields.graphql | 4 + .../Queries/UpdateDatabase.graphql | 2 +- .../{ApiRequest => ApiRequests}/UserApi.cs | 4 +- .../CalorimetricDataAuthorization.cs | 2 +- .../src/Authorization/CommonAuthorization.cs | 2 +- .../Authorization/FileUploadAuthorization.cs | 2 +- .../GeometricDataAuthorization.cs | 2 +- .../GetHttpsResourceAuthorization.cs | 2 +- .../HygrothermalDataAuthorization.cs | 2 +- .../Authorization/OpticalDataAuthorization.cs | 2 +- .../PhotovoltaicDataAuthorization.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 4 +- backend/src/Data/UserInfo.cs | 2 +- .../HttpContextAccessorExtensions.cs | 1 + backend/src/GlobalSuppressions.cs | 9 + .../GraphQl/Databases/DatabaseMutations.cs | 9 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- backend/src/Services/ApiRequestService.cs | 4 +- backend/src/Services/CacheService.cs | 2 +- backend/src/Services/ICacheService.cs | 2 +- .../src/Services/IResponseApprovalService.cs | 4 +- backend/src/Services/IUserService.cs | 2 +- .../src/Services/ResponseApprovalService.cs | 34 ++- backend/src/Services/UserService.cs | 4 +- backend/src/Startup.cs | 4 +- 52 files changed, 297 insertions(+), 689 deletions(-) delete mode 100644 backend/src/ApiRequest/DataApi.cs delete mode 100644 backend/src/ApiRequest/Dto/DatabaseRequestDto.cs delete mode 100644 backend/src/ApiRequest/Queries/CalorimetricData.graphql delete mode 100644 backend/src/ApiRequest/Queries/GeometricData.graphql delete mode 100644 backend/src/ApiRequest/Queries/HygrothermalData.graphql delete mode 100644 backend/src/ApiRequest/Queries/OpticalData.graphql delete mode 100644 backend/src/ApiRequest/Queries/PhotovoltaicData.graphql create mode 100644 backend/src/ApiRequests/DataApi.cs rename backend/src/{ApiRequest => ApiRequests}/DatabaseApi.cs (95%) rename backend/src/{ApiRequest => ApiRequests}/Dto/CurrentUserDto.cs (76%) rename backend/src/{ApiRequest => ApiRequests}/Dto/DatabaseDto.cs (100%) rename backend/src/{ApiRequest => ApiRequests}/Dto/UserInfoDto.cs (90%) rename backend/src/{ApiRequest => ApiRequests}/Dto/XDataDto.cs (98%) rename backend/src/{ApiRequest => ApiRequests}/JsonSerializerOptions.cs (98%) create mode 100644 backend/src/ApiRequests/Queries/CalorimetricData.graphql create mode 100644 backend/src/ApiRequests/Queries/CalorimetricDataFields.graphql rename backend/src/{ApiRequest => ApiRequests}/Queries/CurrentUser.graphql (100%) create mode 100644 backend/src/ApiRequests/Queries/DataFields.graphql rename backend/src/{ApiRequest => ApiRequests}/Queries/Databases.graphql (100%) create mode 100644 backend/src/ApiRequests/Queries/GeometricData.graphql create mode 100644 backend/src/ApiRequests/Queries/GeometricDataFields.graphql create mode 100644 backend/src/ApiRequests/Queries/HygrothermalData.graphql create mode 100644 backend/src/ApiRequests/Queries/HygrothermalDataFields.graphql create mode 100644 backend/src/ApiRequests/Queries/OpticalData.graphql create mode 100644 backend/src/ApiRequests/Queries/OpticalDataFields.graphql create mode 100644 backend/src/ApiRequests/Queries/PhotovoltaicData.graphql create mode 100644 backend/src/ApiRequests/Queries/PhotovoltaicDataFields.graphql rename backend/src/{ApiRequest => ApiRequests}/Queries/UpdateDatabase.graphql (92%) rename backend/src/{ApiRequest => ApiRequests}/UserApi.cs (97%) create mode 100644 backend/src/GlobalSuppressions.cs diff --git a/backend/src/ApiRequest/DataApi.cs b/backend/src/ApiRequest/DataApi.cs deleted file mode 100644 index 9b142054..00000000 --- a/backend/src/ApiRequest/DataApi.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Net.Http; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequest.Dto; -using Database.Services; -using GraphQL; -using Microsoft.AspNetCore.Http; - -namespace Database.ApiRequest; - -/// -/// Class to request XData from Database API. -/// -public class DataApi -{ - private static readonly string[] _calorimetricDataFileNames = - { - "CalorimetricData.graphql" - }; - - private static readonly string[] _geometricDataFileNames = - { - "GeometricData.graphql" - }; - - private static readonly string[] _hygrothermalDataFileNames = - { - "HygrothermalData.graphql" - }; - - private static readonly string[] _photovoltaicDataFileNames = - { - "PhotovoltaicData.graphql" - }; - - private static readonly string[] _opticalDataFileNames = - { - "OpticalData.graphql" - }; - - private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); - private sealed record GeometricDataResponse(GeometricDataDto GeometricData); - private sealed record HygrothermalDataResponse(HygrothermalDataDto HygrothermalData); - private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicData); - private sealed record OpticalDataResponse(OpticalDataDto OpticalData); - - /// - /// Create query to request calorimetric data and get response. - /// - /// Id of data to request. - /// - /// - /// - /// - /// - /// Query and response for calorimetric data. - public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseCalorimetricData( - Guid dataId, - AppSettings appSettings, - IApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(_calorimetricDataFileNames).ConfigureAwait(false), - dataId, - "GetCalorimetricData" - ); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - return (request.Query, JsonSerializer.Serialize(response)); - } - - /// - /// Create query to request geometric data and get response. - /// - /// Id of data to request. - /// - /// - /// - /// - /// - /// Query and response for geometric data. - public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseGeometricData( - Guid dataId, - AppSettings appSettings, - IApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(_geometricDataFileNames).ConfigureAwait(false), - new { uuid = dataId }, - "GeometricData" - ); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - return (Query: request.Query, JsonSerializer.Serialize(response)); - } - - /// - /// Create query to request hygrothemal data and get response. - /// - /// Id of data to request. - /// - /// - /// - /// - /// - /// Query and response for hygrothermal data. - public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseHygrothermalData( - Guid dataId, - AppSettings appSettings, - IApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(_hygrothermalDataFileNames).ConfigureAwait(false), - dataId, - "GetHygrothermalData" - ); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - return (request.Query, JsonSerializer.Serialize(response)); - } - - /// - /// Create query to request photovoltaic data and get response. - /// - /// Id of data to request. - /// - /// - /// - /// - /// - /// Query and response for photovoltaic data. - public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponsePhotovoltaicData( - Guid dataId, - AppSettings appSettings, - IApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(_photovoltaicDataFileNames).ConfigureAwait(false), - dataId, - "GetPhotovoltaicData" - ); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - return (request.Query, JsonSerializer.Serialize(response)); - } - - /// - /// Create query to request optical data and get response. - /// - /// Id of data to request. - /// - /// - /// - /// - /// - /// Query and response for optical data. - public static async Task<(string? Query, string? Response)> CreateQueryAndGetResponseOpticalData( - Guid dataId, - AppSettings appSettings, - IApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(_opticalDataFileNames).ConfigureAwait(false), - dataId, - "GetOpticalData" - ); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - return (request.Query, JsonSerializer.Serialize(response)); - } -} \ No newline at end of file diff --git a/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs b/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs deleted file mode 100644 index c847c2a1..00000000 --- a/backend/src/ApiRequest/Dto/DatabaseRequestDto.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Database.ApiRequest.Dto; - -public sealed record DatabaseRequestDto( -Guid DatabaseId, -string Name, -string Description, -Uri Locator -); \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/CalorimetricData.graphql b/backend/src/ApiRequest/Queries/CalorimetricData.graphql deleted file mode 100644 index 70dd3bd8..00000000 --- a/backend/src/ApiRequest/Queries/CalorimetricData.graphql +++ /dev/null @@ -1,79 +0,0 @@ -query CalorimetricData($uuid: Uuid!) { - calorimetricData(id: $uuid) { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - approverId - keyFingerprint - query - response - signature - timestamp - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - gValues - uValues - } -} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/GeometricData.graphql b/backend/src/ApiRequest/Queries/GeometricData.graphql deleted file mode 100644 index e732d791..00000000 --- a/backend/src/ApiRequest/Queries/GeometricData.graphql +++ /dev/null @@ -1,80 +0,0 @@ -query GeometricData( - $uuid: Uuid! -) { - geometricData(id: $uuid) { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - approverId - keyFingerprint - query - response - signature - timestamp - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - thicknesses - } -} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/HygrothermalData.graphql b/backend/src/ApiRequest/Queries/HygrothermalData.graphql deleted file mode 100644 index d8cd331a..00000000 --- a/backend/src/ApiRequest/Queries/HygrothermalData.graphql +++ /dev/null @@ -1,77 +0,0 @@ -query HygrothermalData($uuid: Uuid!) { - hygrothermalData(id: $uuid) { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - approverId - keyFingerprint - query - response - signature - timestamp - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - } -} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/OpticalData.graphql b/backend/src/ApiRequest/Queries/OpticalData.graphql deleted file mode 100644 index f06ee4be..00000000 --- a/backend/src/ApiRequest/Queries/OpticalData.graphql +++ /dev/null @@ -1,83 +0,0 @@ -query OpticalData($uuid: Uuid!) { - opticalData(id: $uuid) { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - approverId - keyFingerprint - query - response - signature - timestamp - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - infraredEmittances - nearnormalHemisphericalSolarReflectances - nearnormalHemisphericalSolarTransmittances - nearnormalHemisphericalVisibleReflectances - nearnormalHemisphericalVisibleTransmittances - colorRenderingIndices - } -} \ No newline at end of file diff --git a/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql b/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql deleted file mode 100644 index 30a51bc1..00000000 --- a/backend/src/ApiRequest/Queries/PhotovoltaicData.graphql +++ /dev/null @@ -1,77 +0,0 @@ -query PhotovoltaicData($uuid: Uuid!) { - photovoltaicData(id: $uuid) { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - approverId - keyFingerprint - query - response - signature - timestamp - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs new file mode 100644 index 00000000..627ae487 --- /dev/null +++ b/backend/src/ApiRequests/DataApi.cs @@ -0,0 +1,96 @@ +using System; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequests; + +/// +/// Class to request XData from Database API. +/// +public class DataApi +{ + public static readonly string[] CalorimetricDataFileNames = + { + "DataFields.grapql", + "CalorimetricDataFields.graphql", + "CalorimetricData.graphql" + }; + + public static readonly string[] GeometricDataFileNames = + { + "DataFields.graphql", + "GeometricDataFields.graphql", + "GeometricData.graphql" + }; + + public static readonly string[] HygrothermalDataFileNames = + { + "DataFields.graphql", + "HygrothermalDataFields.graphql", + "HygrothermalData.graphql" + }; + + public static readonly string[] PhotovoltaicDataFileNames = + { + "DataFields.graphql", + "PhotovoltaicDataFields.graphql", + "PhotovoltaicData.graphql" + }; + + public static readonly string[] OpticalDataFileNames = + { + "DataFields.graphql", + "OpticalDataFields.graphql", + "OpticalData.graphql" + }; + + /// + /// Create query to request data and get response. + /// + /// Id of data to request. + /// File names of query files. + /// + /// + /// + /// + /// + /// + /// Throws exception, when query could not be constructed or no response. + /// + /// Query and response for data. + public static async Task<(string Query, string Response)> CreateQueryAndGetResponse( + Guid dataId, + string[] filenames, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + + where TGraphQlResponse : class + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), + new { uuid = dataId }, + "" + ); + var query = request.Query ?? throw new Exception("Failed to construct GraphQL query."); + + var responseObject = await apiRequestService.Database().QueryGraphQl( + appSettings, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + var response = JsonSerializer.Serialize(responseObject); + if (string.IsNullOrEmpty(response)) throw new Exception("No response for query."); + + return (query, response); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequest/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs similarity index 95% rename from backend/src/ApiRequest/DatabaseApi.cs rename to backend/src/ApiRequests/DatabaseApi.cs index 52456265..8ac0c2cd 100644 --- a/backend/src/ApiRequest/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -2,7 +2,6 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest.Dto; using Database.GraphQl.Databases; using Database.Services; using GraphQL; @@ -10,7 +9,7 @@ using HotChocolate.Resolvers; using Microsoft.AspNetCore.Http; -namespace Database.ApiRequest; +namespace Database.ApiRequests; /// /// Class to request databases from Metabase API. @@ -111,7 +110,7 @@ public static async Task RequestDatabase( /// /// Update database in Metabase. /// - /// with update mutation. + /// with update mutation. /// /// /// @@ -119,7 +118,7 @@ public static async Task RequestDatabase( /// /// Updated public static async Task UpdateDatabase( - DatabaseRequestDto databaseRequest, + UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -135,7 +134,7 @@ await apiRequestService.ConstructGraphQlQuery( ).ConfigureAwait(false), new { - databaseRequest + updateDatabaseInput }, "UpdateDatabase" ), diff --git a/backend/src/ApiRequest/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs similarity index 76% rename from backend/src/ApiRequest/Dto/CurrentUserDto.cs rename to backend/src/ApiRequests/Dto/CurrentUserDto.cs index 065d517e..37055f69 100644 --- a/backend/src/ApiRequest/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace Database.ApiRequest.Dto; +namespace Database.ApiRequests.Dto; public enum InstitutionRepresentativeRole { @@ -9,6 +9,7 @@ public enum InstitutionRepresentativeRole ASSISTANT } +[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Bezeichner dürfen kein falsches Suffix aufweisen", Justification = "")] public enum DataSigningPermission { NEVER, diff --git a/backend/src/ApiRequest/Dto/DatabaseDto.cs b/backend/src/ApiRequests/Dto/DatabaseDto.cs similarity index 100% rename from backend/src/ApiRequest/Dto/DatabaseDto.cs rename to backend/src/ApiRequests/Dto/DatabaseDto.cs diff --git a/backend/src/ApiRequest/Dto/UserInfoDto.cs b/backend/src/ApiRequests/Dto/UserInfoDto.cs similarity index 90% rename from backend/src/ApiRequest/Dto/UserInfoDto.cs rename to backend/src/ApiRequests/Dto/UserInfoDto.cs index 5210e8a5..3e4df980 100644 --- a/backend/src/ApiRequest/Dto/UserInfoDto.cs +++ b/backend/src/ApiRequests/Dto/UserInfoDto.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Database.ApiRequest.Dto; +namespace Database.ApiRequests.Dto; public sealed record Address( string Formatted diff --git a/backend/src/ApiRequest/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs similarity index 98% rename from backend/src/ApiRequest/Dto/XDataDto.cs rename to backend/src/ApiRequests/Dto/XDataDto.cs index 14fbdfd6..914613a1 100644 --- a/backend/src/ApiRequest/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -3,7 +3,7 @@ using Database.Data; using Database.Enumerations; -namespace Database.ApiRequest.Dto; +namespace Database.ApiRequests.Dto; public record GeometricDataDto( string Uuid, diff --git a/backend/src/ApiRequest/JsonSerializerOptions.cs b/backend/src/ApiRequests/JsonSerializerOptions.cs similarity index 98% rename from backend/src/ApiRequest/JsonSerializerOptions.cs rename to backend/src/ApiRequests/JsonSerializerOptions.cs index 309bfb5d..47ddfd88 100644 --- a/backend/src/ApiRequest/JsonSerializerOptions.cs +++ b/backend/src/ApiRequests/JsonSerializerOptions.cs @@ -2,7 +2,7 @@ using System.Text.Json.Serialization; using GraphQL.Client.Serializer.SystemTextJson; -namespace Database.ApiRequest; +namespace Database.ApiRequests; /// /// Settings for Json serialization. diff --git a/backend/src/ApiRequests/Queries/CalorimetricData.graphql b/backend/src/ApiRequests/Queries/CalorimetricData.graphql new file mode 100644 index 00000000..451caf6e --- /dev/null +++ b/backend/src/ApiRequests/Queries/CalorimetricData.graphql @@ -0,0 +1,5 @@ +query CalorimetricData($uuid: Uuid!) { + calorimetricData(id: $uuid) { + ...CalorimetricDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/CalorimetricDataFields.graphql b/backend/src/ApiRequests/Queries/CalorimetricDataFields.graphql new file mode 100644 index 00000000..5d07c319 --- /dev/null +++ b/backend/src/ApiRequests/Queries/CalorimetricDataFields.graphql @@ -0,0 +1,6 @@ +fragment CalorimetricDataFields on CalorimetricData { + id + ...DataFields + uValues + gValues +} diff --git a/backend/src/ApiRequest/Queries/CurrentUser.graphql b/backend/src/ApiRequests/Queries/CurrentUser.graphql similarity index 100% rename from backend/src/ApiRequest/Queries/CurrentUser.graphql rename to backend/src/ApiRequests/Queries/CurrentUser.graphql diff --git a/backend/src/ApiRequests/Queries/DataFields.graphql b/backend/src/ApiRequests/Queries/DataFields.graphql new file mode 100644 index 00000000..77cb14aa --- /dev/null +++ b/backend/src/ApiRequests/Queries/DataFields.graphql @@ -0,0 +1,82 @@ +fragment DataFields on Data { + uuid + timestamp + componentId + name + description + warnings + creatorId + createdAt + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataTimestamp + dataKind + databaseId + } + } + } + approvals { + timestamp + signature + keyFingerprint + query + response + approverId + } + resources { + description + hashValue + locator + dataFormatId + archivedFilesMetaInformation { + path + dataFormatId + } + } + resourceTree { + root { + vertexId + value { + description + hashValue + locator + dataFormatId + archivedFilesMetaInformation { + path + dataFormatId + } + } + } + nonRootVertices { + vertexId + parentId + value { + description + hashValue + locator + dataFormatId + archivedFilesMetaInformation { + path + dataFormatId + } + } + appliedConversionMethod { + methodId + arguments { + name + value + } + sourceName + } + } + } + locale +} diff --git a/backend/src/ApiRequest/Queries/Databases.graphql b/backend/src/ApiRequests/Queries/Databases.graphql similarity index 100% rename from backend/src/ApiRequest/Queries/Databases.graphql rename to backend/src/ApiRequests/Queries/Databases.graphql diff --git a/backend/src/ApiRequests/Queries/GeometricData.graphql b/backend/src/ApiRequests/Queries/GeometricData.graphql new file mode 100644 index 00000000..ea303ae9 --- /dev/null +++ b/backend/src/ApiRequests/Queries/GeometricData.graphql @@ -0,0 +1,5 @@ +query GeometricData($uuid: Uuid!) { + geometricData(id: $uuid) { + ...GeometricDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/GeometricDataFields.graphql b/backend/src/ApiRequests/Queries/GeometricDataFields.graphql new file mode 100644 index 00000000..cfccf1f2 --- /dev/null +++ b/backend/src/ApiRequests/Queries/GeometricDataFields.graphql @@ -0,0 +1,5 @@ +fragment GeometricDataFields on GeometricData { + id + ...DataFields + thicknesses +} diff --git a/backend/src/ApiRequests/Queries/HygrothermalData.graphql b/backend/src/ApiRequests/Queries/HygrothermalData.graphql new file mode 100644 index 00000000..a8da4aa8 --- /dev/null +++ b/backend/src/ApiRequests/Queries/HygrothermalData.graphql @@ -0,0 +1,5 @@ +query HygrothermalData($uuid: Uuid!) { + hygrothermalData(id: $uuid) { + ...HygrothermalDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/HygrothermalDataFields.graphql b/backend/src/ApiRequests/Queries/HygrothermalDataFields.graphql new file mode 100644 index 00000000..17ec4af3 --- /dev/null +++ b/backend/src/ApiRequests/Queries/HygrothermalDataFields.graphql @@ -0,0 +1,4 @@ +fragment HygrothermalDataFields on HygrothermalData { + id + ...DataFields +} diff --git a/backend/src/ApiRequests/Queries/OpticalData.graphql b/backend/src/ApiRequests/Queries/OpticalData.graphql new file mode 100644 index 00000000..2d3ce8d8 --- /dev/null +++ b/backend/src/ApiRequests/Queries/OpticalData.graphql @@ -0,0 +1,5 @@ +query OpticalData($uuid: Uuid!) { + opticalData(id: $uuid) { + ...OpticalDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/OpticalDataFields.graphql b/backend/src/ApiRequests/Queries/OpticalDataFields.graphql new file mode 100644 index 00000000..f97f57c5 --- /dev/null +++ b/backend/src/ApiRequests/Queries/OpticalDataFields.graphql @@ -0,0 +1,10 @@ +fragment OpticalDataFields on OpticalData { + id + ...DataFields + infraredEmittances + nearnormalHemisphericalSolarReflectances + nearnormalHemisphericalSolarTransmittances + nearnormalHemisphericalVisibleReflectances + nearnormalHemisphericalVisibleTransmittances + colorRenderingIndices +} diff --git a/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql b/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql new file mode 100644 index 00000000..164795a5 --- /dev/null +++ b/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql @@ -0,0 +1,5 @@ +query PhotovoltaicData($uuid: Uuid!) { + photovoltaicData(id: $uuid) { + ...PhotovoltaicDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/PhotovoltaicDataFields.graphql b/backend/src/ApiRequests/Queries/PhotovoltaicDataFields.graphql new file mode 100644 index 00000000..fa872a5e --- /dev/null +++ b/backend/src/ApiRequests/Queries/PhotovoltaicDataFields.graphql @@ -0,0 +1,4 @@ +fragment PhotovoltaicDataFields on PhotovoltaicData { + id + ...DataFields +} diff --git a/backend/src/ApiRequest/Queries/UpdateDatabase.graphql b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql similarity index 92% rename from backend/src/ApiRequest/Queries/UpdateDatabase.graphql rename to backend/src/ApiRequests/Queries/UpdateDatabase.graphql index ccef5e7f..ba9eadda 100644 --- a/backend/src/ApiRequest/Queries/UpdateDatabase.graphql +++ b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql @@ -1,5 +1,5 @@ mutation UpdateDatabase( - $input: DatabaseRequestDto! + $input: UpdateDatabaseInput! ) { updateDatabase(input: $input) { database { diff --git a/backend/src/ApiRequest/UserApi.cs b/backend/src/ApiRequests/UserApi.cs similarity index 97% rename from backend/src/ApiRequest/UserApi.cs rename to backend/src/ApiRequests/UserApi.cs index a88f553e..f4e177a4 100644 --- a/backend/src/ApiRequest/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -2,12 +2,12 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; -namespace Database.ApiRequest; +namespace Database.ApiRequests; /// /// Class to request user info from Metabase API. diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index 9124e4aa..deabcc7d 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 7b6b37f6..4db7c9b8 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index 66ce112d..883a8e7b 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index ab17e20f..4a564d40 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index a14a018d..1c452adc 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index ce53a512..bbc0d483 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index 980f9476..82055ad4 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 8c6a145c..21c12b21 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -1,6 +1,6 @@ using System; using System.Threading; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Authorization; diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index d54658d3..ac352988 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -40,7 +40,7 @@ AppSettings appSettings ) { // Stitching Services https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching - var httpMetaBaesClientBuilder = services.AddHttpClient( + var httpMetabaseClientBuilder = services.AddHttpClient( WellKnownSchemaNames.Metabase, _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") ); @@ -49,7 +49,7 @@ AppSettings appSettings _ => _.BaseAddress = new Uri($"{appSettings.Host}/graphql") ); if (!environment.IsProduction()) - httpMetaBaesClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + httpMetabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { // ClientCertificateOptions = ClientCertificateOption.Manual, diff --git a/backend/src/Data/UserInfo.cs b/backend/src/Data/UserInfo.cs index 8a1cdcf5..95e4e050 100644 --- a/backend/src/Data/UserInfo.cs +++ b/backend/src/Data/UserInfo.cs @@ -1,5 +1,5 @@ using System.Collections.Generic; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Data; diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 35df634e..06b7216e 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -30,6 +30,7 @@ public static class HttpContextAccessorExtensions return !String.IsNullOrEmpty(token) && !String.IsNullOrEmpty(expirationDate) && CheckExpirationDate(expirationDate) ? token : null; } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:IFormatProvider angeben", Justification = "No need at this point.")] private static bool CheckExpirationDate(string expirationDate) { return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate)) < DateTime.UtcNow; diff --git a/backend/src/GlobalSuppressions.cs b/backend/src/GlobalSuppressions.cs new file mode 100644 index 00000000..5f302677 --- /dev/null +++ b/backend/src/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "member", Target = "~M:Database.ApiRequests.DataApi.CreateQueryAndGetResponse``1(System.Guid,System.String[],Database.AppSettings,Database.Services.IApiRequestService,System.Net.Http.IHttpClientFactory,Microsoft.AspNetCore.Http.IHttpContextAccessor,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.ValueTuple{System.String,System.String}}")] +[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "member", Target = "~M:Database.Services.ResponseApprovalService.CreateResponseApproval(Database.Data.IData,System.Threading.CancellationToken)~System.Threading.Tasks.Task{Database.Data.ResponseApproval}")] diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 4b9b7003..508190ae 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -2,8 +2,7 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; -using Database.ApiRequest.Dto; +using Database.ApiRequests; using Database.Services; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -23,11 +22,7 @@ CancellationToken cancellationToken ) { var database = await DatabaseApi.UpdateDatabase( - new DatabaseRequestDto( - input.DatabaseId, - input.Description, - input.Name, - input.Locator), + input, appSettings, apiRequestService, httpClientFactory, diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index c6f59eaa..9dd42807 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -4,7 +4,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; +using Database.ApiRequests; using Database.Services; using HotChocolate; using HotChocolate.Resolvers; diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 0b82dc21..4c43668a 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -5,7 +5,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; +using Database.ApiRequests; using Database.Data; using Database.Services; using HotChocolate; diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index da39e5df..5b9e28f4 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -7,7 +7,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; +using Database.ApiRequests; using Database.Extensions; using GraphQL; using Microsoft.AspNetCore.Http; @@ -54,7 +54,7 @@ string[] fileNames Environment.NewLine, await Task.WhenAll( fileNames.Select(fileName => - File.ReadAllTextAsync($"ApiRequest/Queries/{fileName}") + File.ReadAllTextAsync($"ApiRequests/Queries/{fileName}") ) ).ConfigureAwait(false) ); diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 3f7860aa..2cf64ee5 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,5 +1,5 @@ using System; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; using Microsoft.Extensions.Caching.Memory; namespace Database.Services; diff --git a/backend/src/Services/ICacheService.cs b/backend/src/Services/ICacheService.cs index 97bece39..00ffa13c 100644 --- a/backend/src/Services/ICacheService.cs +++ b/backend/src/Services/ICacheService.cs @@ -1,5 +1,5 @@ using System; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Services; diff --git a/backend/src/Services/IResponseApprovalService.cs b/backend/src/Services/IResponseApprovalService.cs index 6878aee1..656e0478 100644 --- a/backend/src/Services/IResponseApprovalService.cs +++ b/backend/src/Services/IResponseApprovalService.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System; +using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -14,6 +15,7 @@ public interface IResponseApprovalService /// /// /// + /// Thows exception, when singing of data failed. /// Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index 022a9295..715aa52a 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -1,6 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest.Dto; +using Database.ApiRequests.Dto; namespace Database.Services; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 3e27fae0..bc94bee1 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -2,7 +2,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; +using Database.ApiRequests; +using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; using Microsoft.AspNetCore.Http; @@ -27,6 +28,12 @@ public class ResponseApprovalService( IHttpContextAccessor httpContextAccessor, ILogger logger) : IResponseApprovalService { + private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); + private sealed record GeometricDataResponse(GeometricDataDto GeometricData); + private sealed record HygrothermalDataResponse(HygrothermalDataDto HygrothermalData); + private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicData); + private sealed record OpticalDataResponse(OpticalDataDto OpticalData); + /// public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { @@ -34,48 +41,39 @@ public async Task CreateResponseApproval(IData dataObject, Can logger.GetQueryAndResponse(dataObject.GetType().Name); var queryAdnResponse = await GetQueryAndResponse(dataObject, cancellationToken).ConfigureAwait(false); - if (string.IsNullOrEmpty(queryAdnResponse.Query)) - { - throw new ArgumentNullException(queryAdnResponse.Query); - } - if (string.IsNullOrEmpty(queryAdnResponse.Response)) - { - throw new ArgumentNullException(queryAdnResponse.Response); - } - logger.QueryAndResponce(queryAdnResponse.Query, queryAdnResponse.Response); var signatureResult = await signingService.SignData(queryAdnResponse.Response).ConfigureAwait(false); if (!signatureResult.Success) { - throw new ArgumentException("Signing failed"); + throw new Exception("Signing of data failed!"); } return new ResponseApproval(DateTime.UtcNow, signatureResult.Signature, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); } - private async Task<(string? Query, string? Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) + private async Task<(string Query, string Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) { switch (dataObject) { case GeometricData data: - return await DataApi.CreateQueryAndGetResponseGeometricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case CalorimetricData data: - return await DataApi.CreateQueryAndGetResponseCalorimetricData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case HygrothermalData data: - return await DataApi.CreateQueryAndGetResponseHygrothermalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case PhotovoltaicData data: - return await DataApi.CreateQueryAndGetResponsePhotovoltaicData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); case OpticalData data: - return await DataApi.CreateQueryAndGetResponseOpticalData(data.Id, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); + return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); default: - return (null, null); + throw new ArgumentException("Unknown IData object."); } } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 6595bcd4..833e283b 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,8 +1,8 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequest; -using Database.ApiRequest.Dto; +using Database.ApiRequests; +using Database.ApiRequests.Dto; using Database.Extensions; using Database.Logging; using Microsoft.AspNetCore.Http; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8959d567..5effae79 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -216,10 +216,10 @@ private void ConfigureDatabaseServices(IServiceCollection services) private static void ConfigureHttpClientServices(IServiceCollection services, IWebHostEnvironment environment) { services.AddHttpClient(); - var metabasesHttpClientBuilder = services.AddHttpClient(ApiRequestService.MetabaseHttpClient); + var metabaseHttpClientBuilder = services.AddHttpClient(ApiRequestService.MetabaseHttpClient); if (environment.IsDevelopment()) { - metabasesHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + metabaseHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator From 91c7e83e598768d95947d4ec85aa8e9397d01913 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Wed, 26 Mar 2025 14:32:12 +0100 Subject: [PATCH 306/878] Continue access rights --- backend/src/Data/AccessRights.cs | 25 ++++++++--- backend/src/Data/DataX.cs | 3 ++ backend/src/Data/IData.cs | 16 +++++++ .../GraphQl/AccessRights/AccessRightsInput.cs | 2 +- .../AccessRights/DataAccessRightsMutations.cs | 4 +- .../GeometricDataX/GeometricDataQueries.cs | 44 ++++++++++++++----- ...QueryPayload.cs => QueryAllDataPayload.cs} | 18 +++++++- .../GeometricDataX/QueryDataPayload.cs | 33 ++++++++++++++ .../src/GraphQl/GeometricDataX/QueryError.cs | 3 ++ backend/src/Services/AccessRightsService.cs | 21 ++++++--- backend/src/Services/IAccessRightsService.cs | 11 ++++- 11 files changed, 151 insertions(+), 29 deletions(-) rename backend/src/GraphQl/GeometricDataX/{QueryPayload.cs => QueryAllDataPayload.cs} (56%) create mode 100644 backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/AccessRights.cs index fdbe80c0..f7acc38f 100644 --- a/backend/src/Data/AccessRights.cs +++ b/backend/src/Data/AccessRights.cs @@ -17,20 +17,31 @@ public AccessRights( Guid institutionId, int allowedUserCount, int allowedDatasetsPerTime, - TimeSpan period + int periodInDays ) { InstitutionId = institutionId; AllowedUserCount = allowedUserCount; AllowedDatasetsPerTime = allowedDatasetsPerTime; - Period = period; + Period = TimeSpan.FromDays(periodInDays); UserOfInstitution = new List(); } - internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out List errors) + /// + /// Check if dataset is restricted by access rights for institution. Datasets per time period of + /// max user per institution. + /// + /// + /// Id of current user. + /// + /// List or restrictions + /// + internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out List restrictions) { bool isRestricted = false; - errors = new List(); + restrictions = new List(); + + // Check restriction for time period if (this.AllowedDatasetsPerTime > 0) { var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPerirod(InstitutionId); @@ -40,7 +51,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) { isRestricted = true; - errors.Add($"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets reached."); + restrictions.Add($"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets for institution {InstitutionId} reached."); } } else @@ -49,15 +60,17 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService } } + // Check restriction for users per institution if (this.AllowedUserCount > 0) { if (this.UserOfInstitution.Count >= this.AllowedUserCount) { isRestricted = true; - errors.Add($"Id: {dataItem.Id} Reason: Maximum allowed users of institution reached."); + restrictions.Add($"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."); } } + // If restricted add user or count if (!isRestricted) { if (this.AllowedDatasetsPerTime > 0) diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index e167e72c..96f1dce2 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -70,6 +70,7 @@ DateTime createdAt public DataAccessMode DataAccess { get; set; } = DataAccessMode.UNRESTRICTED; public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); + /// public bool IsRestrictedByApplication(string applicationId) { if (this.DataAccess == DataAccessMode.UNRESTRICTED) @@ -77,6 +78,7 @@ public bool IsRestrictedByApplication(string applicationId) return this.DataAccessRights.AllowedApplications.Contains(applicationId); } + /// public bool IsRestrictedByInstitutions(List institutions) { if (this.DataAccess == DataAccessMode.UNRESTRICTED) @@ -84,6 +86,7 @@ public bool IsRestrictedByInstitutions(List institutions) return this.DataAccessRights.AllowedInstitutions.Any(a => institutions.Any(b => a.Equals(b))); } + /// public bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount) { if (this.DataAccess == DataAccessMode.UNRESTRICTED) diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index f83760e9..437ce510 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -20,9 +20,25 @@ public interface IData : IEntity DataAccessMode DataAccess { get; set; } DataAccessRights DataAccessRights { get; } + /// + /// Check if dataset is restricted for passed application id. + /// + /// Id of application. + /// True, if dataset is rescricted. Otherwise false. bool IsRestrictedByApplication(string applicationId); + /// + /// Check if dataset is restricted for at least one of the passed institutions. + /// + /// List of institution ids. + /// True, if dataset is rescricted. Otherwise false. bool IsRestrictedByInstitutions(List institutions); + /// + /// Check if dataset is restricted for passed user. + /// + /// Id of user. + /// Count of already accessed datasets by user. + /// True, if dataset is rescricted. Otherwise false. bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount); } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs index 5d6d200e..b05a3c36 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs @@ -12,5 +12,5 @@ public sealed record AccessRightsInput // Count of allowed datasets for institution // -1 = unlimited 0 = no access int AllowedDatasetsPerTimeSpan, - TimeSpan Period + int PeriodInDays ); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index fe6ffbf0..7c3992dd 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -143,7 +143,7 @@ CancellationToken cancellationToken input.InstitutionId, input.AllowedUserCount, input.AllowedDatasetsPerTimeSpan, - input.Period); + input.PeriodInDays); context.AccessRights.Add(accessRights); } @@ -198,7 +198,7 @@ CancellationToken cancellationToken { accessRights.AllowedUserCount = input.AllowedUserCount; accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = input.Period; + accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); } else { diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index b40bb201..6f5a0e28 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -21,7 +21,7 @@ public sealed class GeometricDataQueries [UsePaging] [UseFiltering(typeof(GeometricDataFilterType))] [UseSorting] - public async Task> GetAllGeometricData( + public async Task GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, IAccessRightsService accessRightsService, @@ -32,34 +32,56 @@ CancellationToken cancellationToken { sorting.StabilizeOrder(); IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + { + return new QueryAllDataPayload(filteredData.ToList()); + } - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - var errors = new List(); - foreach (var error in result.Restrictions) + var restrictions = new List(); + foreach (var restriction in result.Restrictions) { - errors.Add(new QueryError( + restrictions.Add(new QueryError( QueryErrorCode.RESTRICTED, - error, + restriction, [])); } // TODO Use `locale`. - //return new QueryPayload(result.Data as List, errors); - return (result.Data as IEnumerable).AsQueryable(); + return new QueryAllDataPayload((ICollection)result.Data, restrictions); } - public Task GetGeometricDataAsync( + public async Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, GeometricDataByIdDataLoader byId, + IAccessRightsService accessRightsService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return byId.LoadAsync( + var geometricData = await byId.LoadAsync( id, cancellationToken - ); + ).ConfigureAwait(false); + + if (geometricData == null) + { + return new QueryDataPayload(new QueryError( + QueryErrorCode.NO_ELEMENTS, + "No elements", + [])); + } + + var result = await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken).ConfigureAwait(false); + if (result.Restrictions is not null) + { + return new QueryDataPayload(new QueryError( + QueryErrorCode.RESTRICTED, + result.Restrictions, + [])); + } + return new QueryDataPayload(result.Data as GeometricData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryPayload.cs b/backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs similarity index 56% rename from backend/src/GraphQl/GeometricDataX/QueryPayload.cs rename to backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs index d8824cde..d2d17be5 100644 --- a/backend/src/GraphQl/GeometricDataX/QueryPayload.cs +++ b/backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs @@ -3,10 +3,24 @@ namespace Database.GraphQl.GeometricDataX; -public class QueryPayload +public class QueryAllDataPayload : Payload { - public QueryPayload( + public QueryAllDataPayload( + ICollection geometricData + ) + { + GeometricData = geometricData; + } + + public QueryAllDataPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + public QueryAllDataPayload( ICollection geometricData, IReadOnlyCollection errors ) diff --git a/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs b/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs new file mode 100644 index 00000000..cf8cff27 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs @@ -0,0 +1,33 @@ +using Database.Data; + +namespace Database.GraphQl.GeometricDataX; + +public class QueryDataPayload + : Payload +{ + public QueryDataPayload( + GeometricData? geometricData + ) + { + GeometricData = geometricData; + } + + public QueryDataPayload( + IUserError errors + ) + { + Errors = errors; + } + + public QueryDataPayload( + GeometricData? geometricData, + IUserError errors + ) + { + GeometricData = geometricData; + Errors = errors; + } + + public GeometricData? GeometricData { get; } + public IUserError? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryError.cs b/backend/src/GraphQl/GeometricDataX/QueryError.cs index 919c3be5..c49c060e 100644 --- a/backend/src/GraphQl/GeometricDataX/QueryError.cs +++ b/backend/src/GraphQl/GeometricDataX/QueryError.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.GeometricDataX; @@ -15,7 +16,9 @@ IReadOnlyList path } } +[SuppressMessage("Naming", "CA1707")] public enum QueryErrorCode { + NO_ELEMENTS, RESTRICTED } \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 484afc07..1eb1e1cc 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -20,16 +20,17 @@ public class AccessRightsService( ICacheService cacheService) : IAccessRightsService { /// - public async Task<(IEnumerable Data, IEnumerable Restrictions)> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) + public async Task<(ICollection Data, ICollection Restrictions)> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) { List filteredData = new List(); List restrictions = new List(); - var applicationId = userService.GetApplicationIdFromUser(); List institutions = new List(); - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); - var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); - List accessRights = new List(); + + var applicationId = userService.GetApplicationIdFromUser() ?? throw new Exception("Application Id could not be aquired."); + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new Exception("Could not get current user!"); + + var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); foreach (var institution in currentUser.RepresentedInstitutions.Edges) { institutions.Add(institution.Node.Uuid); @@ -63,6 +64,8 @@ public class AccessRightsService( { continue; } + + cacheService.SetAccessCountForUser(currentUser.Uuid, ++alreadyAccessedCount); } filteredData.Add(dataItem); @@ -70,4 +73,12 @@ public class AccessRightsService( return (filteredData, restrictions); } + + /// + public async Task<(IData? Data, string? Restrictions)> ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken) + { + var result = await ApplyAccessRightsOnData(new List { data }, cancellationToken).ConfigureAwait(false); + + return (result.Data.FirstOrDefault(), result.Restrictions.FirstOrDefault()); + } } \ No newline at end of file diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs index eb3345cf..c7dae1ff 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/IAccessRightsService.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -17,5 +16,13 @@ public interface IAccessRightsService /// Data to apply acces rights on. /// /// List of data that can be returned and restrictions. - Task<(IEnumerable Data, IEnumerable Restrictions)> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken); + Task<(ICollection Data, ICollection Restrictions)> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken); + + /// + /// Apply access rights on passed data item. + /// + /// Data to apply acces rights on. + /// + /// Data item or restriction. + Task<(IData? Data, string? Restrictions)> ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken); } \ No newline at end of file From f17c01d5651f16ec43fde3316c56e3eeac0542df Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 31 Mar 2025 11:31:30 +0200 Subject: [PATCH 307/878] Address review findings --- .../src/Configuration/GraphQlConfiguration.cs | 2 + .../src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/AccessRights.cs | 28 ++--- backend/src/GlobalSuppressions.cs | 10 +- .../GraphQl/AccessRights/AccessRightsInput.cs | 10 +- .../AccessRights/DataAccessRightsMutations.cs | 117 ++++++++---------- .../CalorimetricDataQueries.cs | 45 +++++-- .../GeometricDataX/GeometricDataMutations.cs | 1 + .../GeometricDataX/GeometricDataQueries.cs | 37 ++---- .../GeometricDataX/QueryAllDataPayload.cs | 34 ----- .../GeometricDataX/QueryDataPayload.cs | 33 ----- .../src/GraphQl/GeometricDataX/QueryError.cs | 24 ---- .../HygrothermalDataQueries.cs | 46 +++++-- .../OpticalDataX/OpticalDataQueries.cs | 46 +++++-- .../PhotovoltaicDataQueries.cs | 46 +++++-- ...0250331092709_AddAccessRights.Designer.cs} | 12 +- ...s.cs => 20250331092709_AddAccessRights.cs} | 26 +--- .../ApplicationDbContextModelSnapshot.cs | 10 +- ...nal_to_20250331092709_AddAccessRights.sql} | 10 +- ...nal_to_20250331092709_AddAccessRights.sql} | 6 +- backend/src/Services/AccessRightsService.cs | 8 +- backend/src/Services/IAccessRightsService.cs | 9 +- .../src/Services/ResponseApprovalService.cs | 2 +- 23 files changed, 263 insertions(+), 301 deletions(-) delete mode 100644 backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/QueryError.cs rename backend/src/Migrations/{20250319132121_AddAccessRights.Designer.cs => 20250331092709_AddAccessRights.Designer.cs} (99%) rename backend/src/Migrations/{20250319132121_AddAccessRights.cs => 20250331092709_AddAccessRights.cs} (93%) rename backend/src/Migrations/{migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql => migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql} (90%) rename backend/src/Migrations/{rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql => rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql} (87%) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index ac352988..252cd699 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -28,6 +28,7 @@ using Microsoft.Extensions.Logging; using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; using Database.GraphQl.Approvals; +using Database.GraphQl.AccessRights; namespace Database.Configuration; @@ -160,6 +161,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 21930908..7bde0e78 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -92,7 +92,7 @@ CancellationToken cancellationToken cancellationToken).ConfigureAwait(false); if (currentUser == null) { - ModelState.AddModelError("CuurentUser", + ModelState.AddModelError("CurrentUser", $"User is not authenticated."); return BadRequest(ModelState); } diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/AccessRights.cs index f7acc38f..2e5e0fec 100644 --- a/backend/src/Data/AccessRights.cs +++ b/backend/src/Data/AccessRights.cs @@ -8,23 +8,23 @@ public class AccessRights : Entity { public Guid InstitutionId { get; set; } - public int AllowedUserCount { get; set; } - public List UserOfInstitution { get; set; } - public int AllowedDatasetsPerTime { get; set; } + public uint? AllowedUserCount { get; set; } + public uint? AllowedDatasetsPerTime { get; set; } public TimeSpan Period { get; set; } + public List UserOfInstitutionAlreadyAccessed { get; private set; } public AccessRights( Guid institutionId, - int allowedUserCount, - int allowedDatasetsPerTime, - int periodInDays + uint? allowedUserCount, + uint? allowedDatasetsPerTime, + TimeSpan period ) { InstitutionId = institutionId; AllowedUserCount = allowedUserCount; AllowedDatasetsPerTime = allowedDatasetsPerTime; - Period = TimeSpan.FromDays(periodInDays); - UserOfInstitution = new List(); + Period = period; + UserOfInstitutionAlreadyAccessed = new List(); } /// @@ -42,7 +42,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService restrictions = new List(); // Check restriction for time period - if (this.AllowedDatasetsPerTime > 0) + if (AllowedDatasetsPerTime is not null) { var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPerirod(InstitutionId); @@ -61,9 +61,9 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService } // Check restriction for users per institution - if (this.AllowedUserCount > 0) + if (this.AllowedUserCount is not null) { - if (this.UserOfInstitution.Count >= this.AllowedUserCount) + if (UserOfInstitutionAlreadyAccessed.Count >= AllowedUserCount) { isRestricted = true; restrictions.Add($"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."); @@ -73,13 +73,13 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService // If restricted add user or count if (!isRestricted) { - if (this.AllowedDatasetsPerTime > 0) + if (AllowedDatasetsPerTime is not null && AllowedDatasetsPerTime > 0) { cacheService.AddAccessCountToPeriod(InstitutionId); } - if (this.AllowedUserCount > 0) + if (AllowedUserCount is not null && AllowedUserCount > 0) { - this.UserOfInstitution.Add(currentUserId); + UserOfInstitutionAlreadyAccessed.Add(currentUserId); } } diff --git a/backend/src/GlobalSuppressions.cs b/backend/src/GlobalSuppressions.cs index 5f302677..0d5ed97c 100644 --- a/backend/src/GlobalSuppressions.cs +++ b/backend/src/GlobalSuppressions.cs @@ -1,9 +1,7 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. +// This file is used by Code Analysis to maintain SuppressMessage attributes that are applied to +// this project. Project-level suppressions either have no target or are given a specific target and +// scoped to a namespace, type, member, etc. using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "member", Target = "~M:Database.ApiRequests.DataApi.CreateQueryAndGetResponse``1(System.Guid,System.String[],Database.AppSettings,Database.Services.IApiRequestService,System.Net.Http.IHttpClientFactory,Microsoft.AspNetCore.Http.IHttpContextAccessor,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.ValueTuple{System.String,System.String}}")] -[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "member", Target = "~M:Database.Services.ResponseApprovalService.CreateResponseApproval(Database.Data.IData,System.Threading.CancellationToken)~System.Threading.Tasks.Task{Database.Data.ResponseApproval}")] +[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "module")] \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs index b05a3c36..caae7253 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/AccessRightsInput.cs @@ -6,11 +6,9 @@ public sealed record AccessRightsInput ( // Id of institution Guid InstitutionId, - // Count of allowed user for institution - // -1 = unlimited 0 = no access - int AllowedUserCount, - // Count of allowed datasets for institution - // -1 = unlimited 0 = no access - int AllowedDatasetsPerTimeSpan, + // Count of allowed user for institution. Null is unlimited + uint? AllowedUserCount, + // Count of allowed datasets for institution. Null is unlimited + uint? AllowedDatasetsPerTimeSpan, int PeriodInDays ); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 7c3992dd..a2046793 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -1,6 +1,8 @@ -using System.Net.Http; +using System; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Services; using HotChocolate.Types; @@ -12,12 +14,9 @@ namespace Database.GraphQl.AccessRights; [ExtendObjectType(nameof(Mutation))] public class DataAccessRightsMutations { - public async Task UpdateAccessRightsToDataAsync( + public async Task UpdateDataAccessRightsAsync( DataAccessRightsInput input, ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IUserService userService, IDataService dataService, CancellationToken cancellationToken @@ -36,23 +35,6 @@ CancellationToken cancellationToken ); } - //if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( - // currentUser, - // input.CreatorId, - // appSettings, - // httpClientFactory, - // httpContextAccessor, - // cancellationToken - // ) - //) - // return new AddDataAccessRightsPayload( - // new AddDataAccessRightsError( - // AddDataAccessRightsErrorCode.UNAUTHORIZED, - // $"The current user is not authorized to approval for the institution.", - // [] - // ) - // ); - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); if (data == null) { @@ -65,6 +47,22 @@ CancellationToken cancellationToken ); } + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + currentUser, + data.CreatorId, + cancellationToken + ) + ) + { + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update access rights for the data.", + [] + ) + ); + } + data.DataAccessRights.AllowedInstitutions = input.DataAccessRights.AllowedInstitutions; data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; @@ -108,22 +106,21 @@ CancellationToken cancellationToken ); } - //if (!CommonAuthorization.( - // currentUser, - // input.CreatorId, - // appSettings, - // httpClientFactory, - // httpContextAccessor, - // cancellationToken - // ) - //) - // return new AddDataAccessRightsPayload( - // new AddDataAccessRightsError( - // AddDataAccessRightsErrorCode.UNAUTHORIZED, - // $"The current user is not authorized to approval for the institution.", - // [] - // ) - // ); + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + currentUser, + input.InstitutionId, + cancellationToken + ) + ) + { + return new AddAccessRightsPayload( + new AccessRightsError( + AccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to add access rights for the institution.", + [] + ) + ); + } var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); @@ -143,7 +140,7 @@ CancellationToken cancellationToken input.InstitutionId, input.AllowedUserCount, input.AllowedDatasetsPerTimeSpan, - input.PeriodInDays); + TimeSpan.FromDays(input.PeriodInDays)); context.AccessRights.Add(accessRights); } @@ -175,32 +172,25 @@ CancellationToken cancellationToken ); } - //if (!CommonAuthorization.( - // currentUser, - // input.CreatorId, - // appSettings, - // httpClientFactory, - // httpContextAccessor, - // cancellationToken - // ) - //) - // return new AddDataAccessRightsPayload( - // new AddDataAccessRightsError( - // AddDataAccessRightsErrorCode.UNAUTHORIZED, - // $"The current user is not authorized to approval for the institution.", - // [] - // ) - // ); + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + currentUser, + input.InstitutionId, + cancellationToken + ) + ) + { + return new UpdateAccessRightsPayload( + new AccessRightsError( + AccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update access rights for the institution.", + [] + ) + ); + } var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); - if (accessRights is not null) - { - accessRights.AllowedUserCount = input.AllowedUserCount; - accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); - } - else + if (accessRights is null) { return new UpdateAccessRightsPayload( new AccessRightsError( @@ -211,6 +201,9 @@ CancellationToken cancellationToken ); } + accessRights.AllowedUserCount = input.AllowedUserCount; + accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; + accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new UpdateAccessRightsPayload(accessRights); } diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index cbaaa2fb..c679e057 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -1,14 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.CalorimetricDataX; @@ -16,31 +18,54 @@ namespace Database.GraphQl.CalorimetricDataX; public sealed class CalorimetricDataQueries { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public IQueryable GetAllCalorimetricData( + public async Task> GetAllCalorimetricData( [GraphQLType] string? locale, ApplicationDbContext context, - ISortingContext sorting + IAccessRightsService accessRightsService, + ISortingContext sorting, + IResolverContext resolverContext, + CancellationToken cancellationToken ) { sorting.StabilizeOrder(); + IQueryable filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); + + // Check if there is restricted data + if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + { + return filteredData; + } + + // Apply acces rights on data + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + // TODO Use `locale`. - return context.CalorimetricData.AsNoTracking(); + return (IQueryable)result; } - public Task GetCalorimetricDataAsync( + public async Task GetCalorimetricDataAsync( Guid id, [GraphQLType] string? locale, CalorimetricDataByIdDataLoader byId, + IAccessRightsService accessRightsService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return byId.LoadAsync( + var calorimetricData = await byId.LoadAsync( id, cancellationToken ); + if (calorimetricData == null) + { + return calorimetricData; + } + + var result = await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken).ConfigureAwait(false); + return result as CalorimetricData; } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index dc116e2d..55381694 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -127,6 +127,7 @@ input.RootResource.AppliedConversionMethod is null ) ); } + return new CreateGeometricDataPayload(geometricData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 2dc75053..ac6b52ec 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -21,10 +20,10 @@ public sealed class GeometricDataQueries [UsePaging] [UseFiltering(typeof(GeometricDataFilterType))] [UseSorting] - public async Task GetAllGeometricData( + public async Task> GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, - //IAccessRightsService accessRightsService, + IAccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -32,27 +31,21 @@ CancellationToken cancellationToken { sorting.StabilizeOrder(); IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + + // Check if there is restricted data if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) { - return new QueryAllDataPayload(filteredData.ToList()); + return filteredData; } + // Apply acces rights on data var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - var restrictions = new List(); - foreach (var restriction in result.Restrictions) - { - restrictions.Add(new QueryError( - QueryErrorCode.RESTRICTED, - restriction, - [])); - } - // TODO Use `locale`. - return new QueryAllDataPayload((ICollection)result.Data, restrictions); + return (IQueryable)result; } - public async Task GetGeometricDataAsync( + public async Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, GeometricDataByIdDataLoader byId, @@ -68,20 +61,10 @@ CancellationToken cancellationToken if (geometricData == null) { - return new QueryDataPayload(new QueryError( - QueryErrorCode.NO_ELEMENTS, - "No elements", - [])); + return geometricData; } var result = await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken).ConfigureAwait(false); - if (result.Restrictions is not null) - { - return new QueryDataPayload(new QueryError( - QueryErrorCode.RESTRICTED, - result.Restrictions, - [])); - } - return new QueryDataPayload(result.Data as GeometricData); + return result as GeometricData; } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs b/backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs deleted file mode 100644 index d2d17be5..00000000 --- a/backend/src/GraphQl/GeometricDataX/QueryAllDataPayload.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.GeometricDataX; - -public class QueryAllDataPayload - : Payload -{ - public QueryAllDataPayload( - ICollection geometricData - ) - { - GeometricData = geometricData; - } - - public QueryAllDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - public QueryAllDataPayload( - ICollection geometricData, - IReadOnlyCollection errors - ) - { - GeometricData = geometricData; - Errors = errors; - } - - public ICollection? GeometricData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs b/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs deleted file mode 100644 index cf8cff27..00000000 --- a/backend/src/GraphQl/GeometricDataX/QueryDataPayload.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.GeometricDataX; - -public class QueryDataPayload - : Payload -{ - public QueryDataPayload( - GeometricData? geometricData - ) - { - GeometricData = geometricData; - } - - public QueryDataPayload( - IUserError errors - ) - { - Errors = errors; - } - - public QueryDataPayload( - GeometricData? geometricData, - IUserError errors - ) - { - GeometricData = geometricData; - Errors = errors; - } - - public GeometricData? GeometricData { get; } - public IUserError? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/QueryError.cs b/backend/src/GraphQl/GeometricDataX/QueryError.cs deleted file mode 100644 index c49c060e..00000000 --- a/backend/src/GraphQl/GeometricDataX/QueryError.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.GeometricDataX; - -public class QueryError - : UserErrorBase -{ - public QueryError( - QueryErrorCode code, - string message, - IReadOnlyList path - ) - : base(code, message, path) - { - } -} - -[SuppressMessage("Naming", "CA1707")] -public enum QueryErrorCode -{ - NO_ELEMENTS, - RESTRICTED -} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 0efcb67c..e1bd5340 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -1,14 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.HygrothermalDataX; @@ -16,31 +18,55 @@ namespace Database.GraphQl.HygrothermalDataX; public sealed class HygrothermalDataQueries { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public IQueryable GetAllHygrothermalData( + public async Task> GetAllHygrothermalData( [GraphQLType] string? locale, ApplicationDbContext context, - ISortingContext sorting + IAccessRightsService accessRightsService, + IResolverContext resolverContext, + ISortingContext sorting, + CancellationToken cancellationToken ) { sorting.StabilizeOrder(); + IQueryable filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); + + // Check if there is restricted data + if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + { + return filteredData; + } + + // Apply acces rights on data + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + // TODO Use `locale`. - return context.HygrothermalData.AsNoTracking(); + return (IQueryable)result; } - public Task GetHygrothermalDataAsync( + public async Task GetHygrothermalDataAsync( Guid id, [GraphQLType] string? locale, HygrothermalDataByIdDataLoader byId, + IAccessRightsService accessRightsService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return byId.LoadAsync( + var hygrothermalData = await byId.LoadAsync( id, cancellationToken ); + + if (hygrothermalData == null) + { + return hygrothermalData; + } + + var result = await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken).ConfigureAwait(false); + return result as HygrothermalData; } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index aa684c98..bf04d1d5 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -1,14 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.OpticalDataX; @@ -16,31 +18,55 @@ namespace Database.GraphQl.OpticalDataX; public sealed class OpticalDataQueries { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public IQueryable GetAllOpticalData( + public async Task> GetAllOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, - ISortingContext sorting + IAccessRightsService accessRightsService, + ISortingContext sorting, + IResolverContext resolverContext, + CancellationToken cancellationToken ) { sorting.StabilizeOrder(); + IQueryable filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); + + // Check if there is restricted data + if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + { + return filteredData; + } + + // Apply acces rights on data + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + // TODO Use `locale`. - return context.OpticalData.AsNoTracking(); + return (IQueryable)result; } - public Task GetOpticalDataAsync( + public async Task GetOpticalDataAsync( Guid id, [GraphQLType] string? locale, OpticalDataByIdDataLoader byId, + IAccessRightsService accessRightsService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return byId.LoadAsync( + var opticalData = await byId.LoadAsync( id, cancellationToken ); + + if (opticalData == null) + { + return opticalData; + } + + var result = await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken).ConfigureAwait(false); + return result as OpticalData; } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 70411eb2..fa47e86a 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -1,14 +1,16 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.PhotovoltaicDataX; @@ -16,31 +18,55 @@ namespace Database.GraphQl.PhotovoltaicDataX; public sealed class PhotovoltaicDataQueries { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public IQueryable GetAllPhotovoltaicData( + public async Task> GetAllPhotovoltaicData( [GraphQLType] string? locale, ApplicationDbContext context, - ISortingContext sorting + IAccessRightsService accessRightsService, + ISortingContext sorting, + IResolverContext resolverContext, + CancellationToken cancellationToken ) { sorting.StabilizeOrder(); + IQueryable filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); + + // Check if there is restricted data + if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + { + return filteredData; + } + + // Apply acces rights on data + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + // TODO Use `locale`. - return context.PhotovoltaicData.AsNoTracking(); + return (IQueryable)result; } - public Task GetPhotovoltaicDataAsync( + public async Task GetPhotovoltaicDataAsync( Guid id, [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, + IAccessRightsService accessRightsService, CancellationToken cancellationToken ) { // TODO Use `locale`. - return byId.LoadAsync( + var photovoltaicData = await byId.LoadAsync( id, cancellationToken ); + + if (photovoltaicData == null) + { + return photovoltaicData; + } + + var result = await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken).ConfigureAwait(false); + return result as PhotovoltaicData; } } \ No newline at end of file diff --git a/backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs b/backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs similarity index 99% rename from backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs rename to backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs index a535dd5e..548ad096 100644 --- a/backend/src/Migrations/20250319132121_AddAccessRights.Designer.cs +++ b/backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs @@ -15,7 +15,7 @@ namespace Database.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20250319132121_AddAccessRights")] + [Migration("20250331092709_AddAccessRights")] partial class AddAccessRights { /// @@ -42,11 +42,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("AllowedDatasetsPerTime") - .HasColumnType("integer"); + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); - b.Property("AllowedUserCount") - .HasColumnType("integer"); + b.Property("AllowedUserCount") + .HasColumnType("bigint"); b.Property("InstitutionId") .HasColumnType("uuid"); @@ -54,7 +54,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("Period") .HasColumnType("interval"); - b.PrimitiveCollection>("UserOfInstitution") + b.PrimitiveCollection>("UserOfInstitutionAlreadyAccessed") .IsRequired() .HasColumnType("uuid[]"); diff --git a/backend/src/Migrations/20250319132121_AddAccessRights.cs b/backend/src/Migrations/20250331092709_AddAccessRights.cs similarity index 93% rename from backend/src/Migrations/20250319132121_AddAccessRights.cs rename to backend/src/Migrations/20250331092709_AddAccessRights.cs index 49540eca..673503cd 100644 --- a/backend/src/Migrations/20250319132121_AddAccessRights.cs +++ b/backend/src/Migrations/20250331092709_AddAccessRights.cs @@ -141,15 +141,6 @@ protected override void Up(MigrationBuilder migrationBuilder) nullable: false, defaultValue: 0); - migrationBuilder.AlterColumn( - name: "Description", - schema: "database", - table: "get_https_resource", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "text"); - migrationBuilder.AddColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -243,10 +234,10 @@ protected override void Up(MigrationBuilder migrationBuilder) { Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), InstitutionId = table.Column(type: "uuid", nullable: false), - AllowedUserCount = table.Column(type: "integer", nullable: false), - UserOfInstitution = table.Column>(type: "uuid[]", nullable: false), - AllowedDatasetsPerTime = table.Column(type: "integer", nullable: false), + AllowedUserCount = table.Column(type: "bigint", nullable: true), + AllowedDatasetsPerTime = table.Column(type: "bigint", nullable: true), Period = table.Column(type: "interval", nullable: false), + UserOfInstitutionAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), xmin = table.Column(type: "xid", rowVersion: true, nullable: false) }, constraints: table => @@ -411,17 +402,6 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "DataAccess", schema: "database", table: "calorimetric_data"); - - migrationBuilder.AlterColumn( - name: "Description", - schema: "database", - table: "get_https_resource", - type: "text", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); } } } diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 70b49773..1b0ddeb7 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -39,11 +39,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); - b.Property("AllowedDatasetsPerTime") - .HasColumnType("integer"); + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); - b.Property("AllowedUserCount") - .HasColumnType("integer"); + b.Property("AllowedUserCount") + .HasColumnType("bigint"); b.Property("InstitutionId") .HasColumnType("uuid"); @@ -51,7 +51,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Period") .HasColumnType("interval"); - b.PrimitiveCollection>("UserOfInstitution") + b.PrimitiveCollection>("UserOfInstitutionAlreadyAccessed") .IsRequired() .HasColumnType("uuid[]"); diff --git a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql similarity index 90% rename from backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql rename to backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql index a5d7e9ff..4ab9c6b6 100644 --- a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql +++ b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql @@ -35,8 +35,6 @@ ALTER TABLE database.hygrothermal_data ADD "Approval_Timestamp" timestamp with t ALTER TABLE database.hygrothermal_data ADD "DataAccess" integer NOT NULL DEFAULT 0; -ALTER TABLE database.get_https_resource ALTER COLUMN "Description" DROP NOT NULL; - ALTER TABLE database.geometric_data ADD "Approval_KeyFingerprint" text; ALTER TABLE database.geometric_data ADD "Approval_Query" text; @@ -64,15 +62,15 @@ ALTER TABLE database.calorimetric_data ADD "DataAccess" integer NOT NULL DEFAULT CREATE TABLE database.access_rights ( "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), "InstitutionId" uuid NOT NULL, - "AllowedUserCount" integer NOT NULL, - "UserOfInstitution" uuid[] NOT NULL, - "AllowedDatasetsPerTime" integer NOT NULL, + "AllowedUserCount" bigint, + "AllowedDatasetsPerTime" bigint, "Period" interval NOT NULL, + "UserOfInstitutionAlreadyAccessed" uuid[] NOT NULL, CONSTRAINT "PK_access_rights" PRIMARY KEY ("Id") ); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") -VALUES ('20250319132121_AddAccessRights', '9.0.2'); +VALUES ('20250331092709_AddAccessRights', '9.0.2'); COMMIT; diff --git a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql similarity index 87% rename from backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql rename to backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql index e99794f2..c4e84dbb 100644 --- a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250319132121_AddAccessRights.sql +++ b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql @@ -61,12 +61,8 @@ ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Timestamp"; ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccess"; -UPDATE database.get_https_resource SET "Description" = '' WHERE "Description" IS NULL; -ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET NOT NULL; -ALTER TABLE database.get_https_resource ALTER COLUMN "Description" SET DEFAULT ''; - DELETE FROM "__EFMigrationsHistory" -WHERE "MigrationId" = '20250319132121_AddAccessRights'; +WHERE "MigrationId" = '20250331092709_AddAccessRights'; COMMIT; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 1eb1e1cc..b0deaacc 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -20,7 +20,7 @@ public class AccessRightsService( ICacheService cacheService) : IAccessRightsService { /// - public async Task<(ICollection Data, ICollection Restrictions)> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) + public async Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) { List filteredData = new List(); List restrictions = new List(); @@ -71,14 +71,14 @@ public class AccessRightsService( filteredData.Add(dataItem); } - return (filteredData, restrictions); + return filteredData.AsQueryable(); } /// - public async Task<(IData? Data, string? Restrictions)> ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken) + public async Task ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken) { var result = await ApplyAccessRightsOnData(new List { data }, cancellationToken).ConfigureAwait(false); - return (result.Data.FirstOrDefault(), result.Restrictions.FirstOrDefault()); + return result.FirstOrDefault(); } } \ No newline at end of file diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs index c7dae1ff..6e1dc7b6 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/IAccessRightsService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -15,14 +16,14 @@ public interface IAccessRightsService /// /// Data to apply acces rights on. /// - /// List of data that can be returned and restrictions. - Task<(ICollection Data, ICollection Restrictions)> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken); + /// List of data that can be returned. + Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken); /// /// Apply access rights on passed data item. /// /// Data to apply acces rights on. /// - /// Data item or restriction. - Task<(IData? Data, string? Restrictions)> ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken); + /// Data item. + Task ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index bc94bee1..dbef88ca 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -73,7 +73,7 @@ public async Task CreateResponseApproval(IData dataObject, Can return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); default: - throw new ArgumentException("Unknown IData object."); + throw new NotImplementedException("Unknown IData object."); } } } \ No newline at end of file From d0821838a343cfb26716212947bddf21f8cfa44e Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 31 Mar 2025 16:17:18 +0200 Subject: [PATCH 308/878] Another review findings adressing --- .../src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/ApplicationDbContext.cs | 4 +- backend/src/Data/DataAccessRights.cs | 56 ++++++++++-- backend/src/Data/DataX.cs | 30 +++---- backend/src/Data/IData.cs | 2 - ...ssRights.cs => InstitutionAccessRights.cs} | 22 ++--- backend/src/Enumerations/DataAccessMode.cs | 7 -- .../AccessRights/AccessRightsPayload.cs | 52 ------------ .../AccessRights/AddAccessRightsPayload.cs | 19 ----- .../AddInstitutionAccessRightsPayload.cs | 19 +++++ .../AccessRights/DataAccessRightsMutations.cs | 70 ++++++--------- ...ror.cs => InstitutionAccessRightsError.cs} | 8 +- ...cs => InstitutionAccessRightsErrorCode.cs} | 4 +- ...put.cs => InstitutionAccessRightsInput.cs} | 2 +- .../InstitutionAccessRightsPayload.cs | 52 ++++++++++++ .../AccessRights/UpdateAccessRightsPayload.cs | 6 +- .../GraphQl/Approvals/ApprovalMutations.cs | 2 +- .../CalorimetricDataMutations.cs | 2 +- .../CalorimetricDataQueries.cs | 4 +- .../{Data => GraphQl/Databases}/Database.cs | 3 +- .../GraphQl/Databases/DatabaseMutations.cs | 2 +- .../src/GraphQl/Databases/DatabasePayload.cs | 4 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 4 +- .../Databases/UpdateDatabasePayload.cs | 2 +- .../GeometricDataX/GeometricDataQueries.cs | 4 +- .../GetHttpsResourceMutations.cs | 2 +- .../HygrothermalDataMutations.cs | 2 +- .../HygrothermalDataQueries.cs | 2 +- .../OpticalDataX/OpticalDataMutations.cs | 2 +- .../OpticalDataX/OpticalDataQueries.cs | 4 +- .../PhotovoltaicDataMutations.cs | 2 +- .../PhotovoltaicDataQueries.cs | 4 +- .../src/Logging/AccessRightServiceLogging.cs | 13 +++ ...0250331141152_AddAccessRights.Designer.cs} | 85 ++++++++----------- ...s.cs => 20250331141152_AddAccessRights.cs} | 67 +-------------- .../ApplicationDbContextModelSnapshot.cs | 83 ++++++++---------- ...nal_to_20250331141152_AddAccessRights.sql} | 14 +-- ...nal_to_20250331141152_AddAccessRights.sql} | 12 +-- backend/src/Services/AccessRightsService.cs | 62 +++++++++----- backend/src/Services/IAccessRightsService.cs | 4 +- 40 files changed, 338 insertions(+), 402 deletions(-) rename backend/src/Data/{AccessRights.cs => InstitutionAccessRights.cs} (74%) delete mode 100644 backend/src/Enumerations/DataAccessMode.cs delete mode 100644 backend/src/GraphQl/AccessRights/AccessRightsPayload.cs delete mode 100644 backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs rename backend/src/GraphQl/AccessRights/{AccessRightsError.cs => InstitutionAccessRightsError.cs} (51%) rename backend/src/GraphQl/AccessRights/{AccessRightsErrorCode.cs => InstitutionAccessRightsErrorCode.cs} (73%) rename backend/src/GraphQl/AccessRights/{AccessRightsInput.cs => InstitutionAccessRightsInput.cs} (86%) create mode 100644 backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs rename backend/src/{Data => GraphQl/Databases}/Database.cs (96%) create mode 100644 backend/src/Logging/AccessRightServiceLogging.cs rename backend/src/Migrations/{20250331092709_AddAccessRights.Designer.cs => 20250331141152_AddAccessRights.Designer.cs} (99%) rename backend/src/Migrations/{20250331092709_AddAccessRights.cs => 20250331141152_AddAccessRights.cs} (84%) rename backend/src/Migrations/{migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql => migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql} (80%) rename backend/src/Migrations/{rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql => rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql} (83%) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7bde0e78..96377979 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -90,7 +90,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { ModelState.AddModelError("CurrentUser", $"User is not authenticated."); diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 9a473b1c..08c55247 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -41,7 +41,7 @@ DbContextOptions options public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; public DbSet GeometricData { get; private set; } = default!; - public DbSet AccessRights { get; private set; } = default!; + public DbSet AccessRights { get; private set; } = default!; private static void CreateEnumerations(ModelBuilder builder, string schemaName) { @@ -125,7 +125,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ) .ToTable("geometric_data"); ConfigureEntity( - modelBuilder.Entity() + modelBuilder.Entity() ) .ToTable("access_rights"); } diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 70644b3a..2fd44fb1 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -14,22 +14,64 @@ public sealed class DataAccessRights /// Dictionary to store allowed user with the amount of a allowed dataset calls /// -1 meens unlimited access. /// - public IDictionary AllowedUserAndQuantity { get; set; } + public IDictionary? AllowedUserAndQuantity { get; set; } /// /// Allowed Institutions /// - public ICollection AllowedInstitutions { get; set; } + public ICollection? AllowedInstitutions { get; set; } /// /// Allowed applications /// - public ICollection AllowedApplications { get; set; } + public ICollection? AllowedApplications { get; set; } - public DataAccessRights() + /// + /// Is data access ristricted by data access rights. + /// + public bool HasRistrictions + { + get + { + return AllowedUserAndQuantity is not null && + AllowedInstitutions is not null && + AllowedApplications is not null; + } + } + + /// + /// Is data access ristricted by user. + /// + public bool IsRestrictedByUser + { + get + { + return AllowedUserAndQuantity is not null && + AllowedUserAndQuantity.Count > 0; + } + } + + /// + /// Is data access ristricted by institution. + /// + public bool IsRestrictedByInstitution + { + get + { + return AllowedInstitutions is not null && + AllowedInstitutions.Count > 0; + } + } + + /// + /// Is data access ristricted by application. + /// + public bool IsRestrictedByApplication { - AllowedUserAndQuantity = new Dictionary(); - AllowedInstitutions = new List(); - AllowedApplications = new List(); + get + { + return AllowedApplications is not null && + AllowedApplications.Count > 0; + } } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 96f1dce2..1ca95faf 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Database.Enumerations; namespace Database.Data; @@ -67,38 +66,39 @@ DateTime createdAt // this list and the graph must be connected. In other words, the resources must form a tree. public virtual ICollection Resources { get; } = new List(); - public DataAccessMode DataAccess { get; set; } = DataAccessMode.UNRESTRICTED; public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); /// public bool IsRestrictedByApplication(string applicationId) { - if (this.DataAccess == DataAccessMode.UNRESTRICTED) - return false; - return this.DataAccessRights.AllowedApplications.Contains(applicationId); + if (DataAccessRights.IsRestrictedByApplication) + return DataAccessRights.AllowedApplications!.Contains(applicationId); + return false; } /// public bool IsRestrictedByInstitutions(List institutions) { - if (this.DataAccess == DataAccessMode.UNRESTRICTED) - return false; - return this.DataAccessRights.AllowedInstitutions.Any(a => institutions.Any(b => a.Equals(b))); + if (DataAccessRights.IsRestrictedByInstitution) + return DataAccessRights.AllowedInstitutions!.Any(a => institutions.Any(b => a.Equals(b))); + return false; } /// public bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount) { - if (this.DataAccess == DataAccessMode.UNRESTRICTED) - return false; - int limit; - if (this.DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out limit)) + if (DataAccessRights.IsRestrictedByUser) { - if (limit < 0 || limit > alreadyAccesedCount) + int limit; + if (DataAccessRights.AllowedUserAndQuantity!.TryGetValue(uuid, out limit)) { - return false; + if (limit < 0 || limit > alreadyAccesedCount) + { + return false; + } } + return true; } - return true; + return false; } } \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 437ce510..4b1f0293 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using Database.Enumerations; namespace Database.Data; @@ -17,7 +16,6 @@ public interface IData : IEntity ICollection Resources { get; } ResponseApproval? Approval { get; } string Locale { get; } - DataAccessMode DataAccess { get; set; } DataAccessRights DataAccessRights { get; } /// diff --git a/backend/src/Data/AccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs similarity index 74% rename from backend/src/Data/AccessRights.cs rename to backend/src/Data/InstitutionAccessRights.cs index 2e5e0fec..e2d27c06 100644 --- a/backend/src/Data/AccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -4,16 +4,16 @@ namespace Database.Data; -public class AccessRights +public class InstitutionAccessRights : Entity { public Guid InstitutionId { get; set; } public uint? AllowedUserCount { get; set; } public uint? AllowedDatasetsPerTime { get; set; } public TimeSpan Period { get; set; } - public List UserOfInstitutionAlreadyAccessed { get; private set; } + public List UserAlreadyAccessed { get; private set; } - public AccessRights( + public InstitutionAccessRights( Guid institutionId, uint? allowedUserCount, uint? allowedDatasetsPerTime, @@ -24,7 +24,7 @@ TimeSpan period AllowedUserCount = allowedUserCount; AllowedDatasetsPerTime = allowedDatasetsPerTime; Period = period; - UserOfInstitutionAlreadyAccessed = new List(); + UserAlreadyAccessed = new List(); } /// @@ -34,12 +34,12 @@ TimeSpan period /// /// Id of current user. /// - /// List or restrictions + /// Reason, why data is restricted. /// - internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out List restrictions) + internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out string reason) { bool isRestricted = false; - restrictions = new List(); + reason = ""; // Check restriction for time period if (AllowedDatasetsPerTime is not null) @@ -51,7 +51,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) { isRestricted = true; - restrictions.Add($"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets for institution {InstitutionId} reached."); + reason = $"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets for institution {InstitutionId} reached."; } } else @@ -63,10 +63,10 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService // Check restriction for users per institution if (this.AllowedUserCount is not null) { - if (UserOfInstitutionAlreadyAccessed.Count >= AllowedUserCount) + if (UserAlreadyAccessed.Count >= AllowedUserCount) { isRestricted = true; - restrictions.Add($"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."); + reason = $"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."; } } @@ -79,7 +79,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService } if (AllowedUserCount is not null && AllowedUserCount > 0) { - UserOfInstitutionAlreadyAccessed.Add(currentUserId); + UserAlreadyAccessed.Add(currentUserId); } } diff --git a/backend/src/Enumerations/DataAccessMode.cs b/backend/src/Enumerations/DataAccessMode.cs deleted file mode 100644 index a642246f..00000000 --- a/backend/src/Enumerations/DataAccessMode.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Database.Enumerations; - -public enum DataAccessMode -{ - UNRESTRICTED, - RESTRICTED -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs deleted file mode 100644 index 74365f78..00000000 --- a/backend/src/GraphQl/AccessRights/AccessRightsPayload.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.AccessRights; - -public class AccessRightsPayload - : Payload - where TAccessRightsError : IUserError -{ - protected AccessRightsPayload( - Data.AccessRights accessRights - ) - { - AccessRights = accessRights; - } - - protected AccessRightsPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected AccessRightsPayload( - TAccessRightsError error - ) - : this(new[] { error }) - { - } - - protected AccessRightsPayload( - Data.AccessRights accessRights, - IReadOnlyCollection errors - ) - { - AccessRights = accessRights; - Errors = errors; - } - - protected AccessRightsPayload( - Data.AccessRights accessRights, - TAccessRightsError error - ) - : this( - accessRights, - new[] { error } - ) - { - } - - public Data.AccessRights? AccessRights { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs deleted file mode 100644 index faa04623..00000000 --- a/backend/src/GraphQl/AccessRights/AddAccessRightsPayload.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Database.GraphQl.AccessRights; - -public class AddAccessRightsPayload - : AccessRightsPayload -{ - public AddAccessRightsPayload( - Data.AccessRights accessRights - ) - : base(accessRights) - { - } - - public AddAccessRightsPayload( - AccessRightsError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs new file mode 100644 index 00000000..0dda6179 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs @@ -0,0 +1,19 @@ +namespace Database.GraphQl.AccessRights; + +public class AddInstitutionAccessRightsPayload + : InstitutionAccessRightsPayload +{ + public AddInstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights + ) + : base(accessRights) + { + } + + public AddInstitutionAccessRightsPayload( + InstitutionAccessRightsError error + ) + : base(error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index a2046793..8d7f207c 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -24,7 +24,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new UpdateDataAccessRightsPayload( new UpdateDataAccessRightsError( @@ -67,23 +67,12 @@ CancellationToken cancellationToken data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; - if (data.DataAccessRights.AllowedInstitutions?.Count > 0 - || data.DataAccessRights.AllowedApplications?.Count > 0 - || data.DataAccessRights.AllowedUserAndQuantity?.Count > 0) - { - data.DataAccess = Enumerations.DataAccessMode.RESTRICTED; - } - else - { - data.DataAccess = Enumerations.DataAccessMode.UNRESTRICTED; - } - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new UpdateDataAccessRightsPayload(data.DataAccessRights); } - public async Task AddAccessRightsAsync( - AccessRightsInput input, + public async Task AddAccessRightsAsync( + InstitutionAccessRightsInput input, ApplicationDbContext context, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -95,11 +84,11 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { - return new AddAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.UNAUTHENTICATED, + return new AddInstitutionAccessRightsPayload( + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.UNAUTHENTICATED, $"The user is not authenticated.", [] ) @@ -113,9 +102,9 @@ CancellationToken cancellationToken ) ) { - return new AddAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.UNAUTHORIZED, + return new AddInstitutionAccessRightsPayload( + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.UNAUTHORIZED, $"The current user is not authorized to add access rights for the institution.", [] ) @@ -126,30 +115,27 @@ CancellationToken cancellationToken if (accessRights is not null) { - return new AddAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.ALREADY_EXISTS, + return new AddInstitutionAccessRightsPayload( + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.ALREADY_EXISTS, $"The access rights for this institution already exist.", [] ) ); } - else - { - accessRights = new Data.AccessRights( - input.InstitutionId, - input.AllowedUserCount, - input.AllowedDatasetsPerTimeSpan, - TimeSpan.FromDays(input.PeriodInDays)); - context.AccessRights.Add(accessRights); - } + accessRights = new Data.InstitutionAccessRights( + input.InstitutionId, + input.AllowedUserCount, + input.AllowedDatasetsPerTimeSpan, + TimeSpan.FromDays(input.PeriodInDays)); + context.AccessRights.Add(accessRights); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - return new AddAccessRightsPayload(accessRights); + return new AddInstitutionAccessRightsPayload(accessRights); } public async Task UpdateAccessRightsAsync( - AccessRightsInput input, + InstitutionAccessRightsInput input, ApplicationDbContext context, AppSettings appSettings, IHttpClientFactory httpClientFactory, @@ -161,11 +147,11 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new UpdateAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.UNAUTHENTICATED, + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.UNAUTHENTICATED, $"The user is not authenticated.", [] ) @@ -180,8 +166,8 @@ CancellationToken cancellationToken ) { return new UpdateAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.UNAUTHORIZED, + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.UNAUTHORIZED, $"The current user is not authorized to update access rights for the institution.", [] ) @@ -193,8 +179,8 @@ CancellationToken cancellationToken if (accessRights is null) { return new UpdateAccessRightsPayload( - new AccessRightsError( - AccessRightsErrorCode.UNKNOWN_ACCESSRIGHTS, + new InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode.UNKNOWN_ACCESS_RIGHTS, $"There are no access rights for this institution.", [] ) diff --git a/backend/src/GraphQl/AccessRights/AccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs similarity index 51% rename from backend/src/GraphQl/AccessRights/AccessRightsError.cs rename to backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs index 37feb2b5..da211e42 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs @@ -2,11 +2,11 @@ namespace Database.GraphQl.AccessRights; -public class AccessRightsError - : UserErrorBase +public class InstitutionAccessRightsError + : UserErrorBase { - public AccessRightsError( - AccessRightsErrorCode code, + public InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode code, string message, IReadOnlyList path ) diff --git a/backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs similarity index 73% rename from backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs rename to backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs index f9193e0f..cc1aab16 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs @@ -3,11 +3,11 @@ namespace Database.GraphQl.AccessRights; [SuppressMessage("Naming", "CA1707")] -public enum AccessRightsErrorCode +public enum InstitutionAccessRightsErrorCode { UNKNOWN, UNAUTHENTICATED, UNAUTHORIZED, ALREADY_EXISTS, - UNKNOWN_ACCESSRIGHTS + UNKNOWN_ACCESS_RIGHTS } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs similarity index 86% rename from backend/src/GraphQl/AccessRights/AccessRightsInput.cs rename to backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs index caae7253..1e58e6aa 100644 --- a/backend/src/GraphQl/AccessRights/AccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.AccessRights; -public sealed record AccessRightsInput +public sealed record InstitutionAccessRightsInput ( // Id of institution Guid InstitutionId, diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs new file mode 100644 index 00000000..e52a5614 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public class InstitutionAccessRightsPayload + : Payload + where TInstitutionAccessRightsError : IUserError +{ + protected InstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights + ) + { + AccessRights = accessRights; + } + + protected InstitutionAccessRightsPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + protected InstitutionAccessRightsPayload( + TInstitutionAccessRightsError error + ) + : this(new[] { error }) + { + } + + protected InstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights, + IReadOnlyCollection errors + ) + { + AccessRights = accessRights; + Errors = errors; + } + + protected InstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights, + TInstitutionAccessRightsError error + ) + : this( + accessRights, + new[] { error } + ) + { + } + + public Data.InstitutionAccessRights? AccessRights { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs index ed49a046..c51b19b6 100644 --- a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs @@ -1,17 +1,17 @@ namespace Database.GraphQl.AccessRights; public class UpdateAccessRightsPayload - : AccessRightsPayload + : InstitutionAccessRightsPayload { public UpdateAccessRightsPayload( - Data.AccessRights accessRights + Data.InstitutionAccessRights accessRights ) : base(accessRights) { } public UpdateAccessRightsPayload( - AccessRightsError error + InstitutionAccessRightsError error ) : base(error) { diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index 7f3ba0c0..de01f595 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -23,7 +23,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new AddApprovalPayload( new AddApprovalError( diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index db831ac9..f7a43701 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -24,7 +24,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index c679e057..5db1e87e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -35,13 +35,13 @@ CancellationToken cancellationToken IQueryable filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) { return filteredData; } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); // TODO Use `locale`. return (IQueryable)result; diff --git a/backend/src/Data/Database.cs b/backend/src/GraphQl/Databases/Database.cs similarity index 96% rename from backend/src/Data/Database.cs rename to backend/src/GraphQl/Databases/Database.cs index 7a6a8034..64281713 100644 --- a/backend/src/Data/Database.cs +++ b/backend/src/GraphQl/Databases/Database.cs @@ -1,7 +1,6 @@ using System; -using Database.GraphQl.Databases; -namespace Database.Data; +namespace Database.GraphQl.Databases; public sealed class Database { diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 508190ae..1f409a2a 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -29,7 +29,7 @@ CancellationToken cancellationToken httpContextAccessor, cancellationToken).ConfigureAwait(false); return database is not null ? new UpdateDatabasePayload( - Data.Database.FromDto(database), + Database.FromDto(database), null) : new UpdateDatabasePayload( null, new[] diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index 046fbc07..238e802d 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -7,7 +7,7 @@ public abstract class DatabasePayload where TDatabaseError : IUserError { protected DatabasePayload( - Data.Database? database, + Database? database, IReadOnlyCollection? errors ) { @@ -15,6 +15,6 @@ protected DatabasePayload( Errors = errors; } - public Data.Database? Database { get; } + public Database? Database { get; } public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 9dd42807..f0191501 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -16,7 +16,7 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Query))] public sealed class DatabaseQueries { - public async Task GetDatabaseAsync( + public async Task GetDatabaseAsync( AppSettings appSettings, IApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -34,7 +34,7 @@ CancellationToken cancellationToken httpContextAccessor, resolverContext, cancellationToken); - return Data.Database.FromDto(database); + return Database.FromDto(database); } catch (HttpRequestException e) { diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index 0c939725..6c025a6b 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -6,7 +6,7 @@ public sealed class UpdateDatabasePayload : DatabasePayload { public UpdateDatabasePayload( - Data.Database? database, + Database? database, IReadOnlyCollection? errors ) : base(database, errors) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index ac6b52ec..e75db6ce 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -33,13 +33,13 @@ CancellationToken cancellationToken IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) { return filteredData; } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); // TODO Use `locale`. return (IQueryable)result; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index c7596084..37bb3c76 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -46,7 +46,7 @@ CancellationToken cancellationToken var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 6c97bd29..839de2d4 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -26,7 +26,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index e1bd5340..38f041e5 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -35,7 +35,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) { return filteredData; } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index b41bccb8..fa29957a 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -26,7 +26,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new CreateOpticalDataPayload( new CreateOpticalDataError( diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index bf04d1d5..c7f47860 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -35,13 +35,13 @@ CancellationToken cancellationToken IQueryable filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) { return filteredData; } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); // TODO Use `locale`. return (IQueryable)result; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 2b4e3041..194db6f7 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -28,7 +28,7 @@ CancellationToken cancellationToken { var currentUser = await userService.GetCurrentUser( cancellationToken).ConfigureAwait(false); - if (currentUser == null) + if (currentUser is null) { return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index fa47e86a..a1482d00 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -35,13 +35,13 @@ CancellationToken cancellationToken IQueryable filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccess == Enumerations.DataAccessMode.RESTRICTED)) + if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) { return filteredData; } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); + var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); // TODO Use `locale`. return (IQueryable)result; diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs new file mode 100644 index 00000000..fce0f183 --- /dev/null +++ b/backend/src/Logging/AccessRightServiceLogging.cs @@ -0,0 +1,13 @@ +using System; +using Database.Services; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static partial class AccessRightServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Restricted Item Id: {Id} Reason: {Reason}")] + public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); +} \ No newline at end of file diff --git a/backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs similarity index 99% rename from backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs rename to backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs index 548ad096..a3a62599 100644 --- a/backend/src/Migrations/20250331092709_AddAccessRights.Designer.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs @@ -15,7 +15,7 @@ namespace Database.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20250331092709_AddAccessRights")] + [Migration("20250331141152_AddAccessRights")] partial class AddAccessRights { /// @@ -35,40 +35,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Database.Data.AccessRights", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AllowedDatasetsPerTime") - .HasColumnType("bigint"); - - b.Property("AllowedUserCount") - .HasColumnType("bigint"); - - b.Property("InstitutionId") - .HasColumnType("uuid"); - - b.Property("Period") - .HasColumnType("interval"); - - b.PrimitiveCollection>("UserOfInstitutionAlreadyAccessed") - .IsRequired() - .HasColumnType("uuid[]"); - - b.Property("Version") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid") - .HasColumnName("xmin"); - - b.HasKey("Id"); - - b.ToTable("access_rights", "database"); - }); - modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Property("Id") @@ -85,9 +51,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -137,9 +100,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -243,9 +203,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -271,6 +228,40 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("hygrothermal_data", "database"); }); + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("access_rights", "database"); + }); + modelBuilder.Entity("Database.Data.OpticalData", b => { b.Property("Id") @@ -294,9 +285,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -364,9 +352,6 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); diff --git a/backend/src/Migrations/20250331092709_AddAccessRights.cs b/backend/src/Migrations/20250331141152_AddAccessRights.cs similarity index 84% rename from backend/src/Migrations/20250331092709_AddAccessRights.cs rename to backend/src/Migrations/20250331141152_AddAccessRights.cs index 673503cd..365b33be 100644 --- a/backend/src/Migrations/20250331092709_AddAccessRights.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.cs @@ -47,14 +47,6 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "timestamp with time zone", nullable: true); - migrationBuilder.AddColumn( - name: "DataAccess", - schema: "database", - table: "photovoltaic_data", - type: "integer", - nullable: false, - defaultValue: 0); - migrationBuilder.AddColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -90,14 +82,6 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "timestamp with time zone", nullable: true); - migrationBuilder.AddColumn( - name: "DataAccess", - schema: "database", - table: "optical_data", - type: "integer", - nullable: false, - defaultValue: 0); - migrationBuilder.AddColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -133,14 +117,6 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "timestamp with time zone", nullable: true); - migrationBuilder.AddColumn( - name: "DataAccess", - schema: "database", - table: "hygrothermal_data", - type: "integer", - nullable: false, - defaultValue: 0); - migrationBuilder.AddColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -176,14 +152,6 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "timestamp with time zone", nullable: true); - migrationBuilder.AddColumn( - name: "DataAccess", - schema: "database", - table: "geometric_data", - type: "integer", - nullable: false, - defaultValue: 0); - migrationBuilder.AddColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -219,14 +187,6 @@ protected override void Up(MigrationBuilder migrationBuilder) type: "timestamp with time zone", nullable: true); - migrationBuilder.AddColumn( - name: "DataAccess", - schema: "database", - table: "calorimetric_data", - type: "integer", - nullable: false, - defaultValue: 0); - migrationBuilder.CreateTable( name: "access_rights", schema: "database", @@ -237,7 +197,7 @@ protected override void Up(MigrationBuilder migrationBuilder) AllowedUserCount = table.Column(type: "bigint", nullable: true), AllowedDatasetsPerTime = table.Column(type: "bigint", nullable: true), Period = table.Column(type: "interval", nullable: false), - UserOfInstitutionAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), + UserAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), xmin = table.Column(type: "xid", rowVersion: true, nullable: false) }, constraints: table => @@ -278,11 +238,6 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "photovoltaic_data"); - migrationBuilder.DropColumn( - name: "DataAccess", - schema: "database", - table: "photovoltaic_data"); - migrationBuilder.DropColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -308,11 +263,6 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "optical_data"); - migrationBuilder.DropColumn( - name: "DataAccess", - schema: "database", - table: "optical_data"); - migrationBuilder.DropColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -338,11 +288,6 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "hygrothermal_data"); - migrationBuilder.DropColumn( - name: "DataAccess", - schema: "database", - table: "hygrothermal_data"); - migrationBuilder.DropColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -368,11 +313,6 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "geometric_data"); - migrationBuilder.DropColumn( - name: "DataAccess", - schema: "database", - table: "geometric_data"); - migrationBuilder.DropColumn( name: "Approval_KeyFingerprint", schema: "database", @@ -397,11 +337,6 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "Approval_Timestamp", schema: "database", table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "DataAccess", - schema: "database", - table: "calorimetric_data"); } } } diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 1b0ddeb7..a6e212d1 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -32,40 +32,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Database.Data.AccessRights", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); - - b.Property("AllowedDatasetsPerTime") - .HasColumnType("bigint"); - - b.Property("AllowedUserCount") - .HasColumnType("bigint"); - - b.Property("InstitutionId") - .HasColumnType("uuid"); - - b.Property("Period") - .HasColumnType("interval"); - - b.PrimitiveCollection>("UserOfInstitutionAlreadyAccessed") - .IsRequired() - .HasColumnType("uuid[]"); - - b.Property("Version") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid") - .HasColumnName("xmin"); - - b.HasKey("Id"); - - b.ToTable("access_rights", "database"); - }); - modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Property("Id") @@ -82,9 +48,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -134,9 +97,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -240,9 +200,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -268,6 +225,40 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("hygrothermal_data", "database"); }); + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("access_rights", "database"); + }); + modelBuilder.Entity("Database.Data.OpticalData", b => { b.Property("Id") @@ -291,9 +282,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); @@ -361,9 +349,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatorId") .HasColumnType("uuid"); - b.Property("DataAccess") - .HasColumnType("integer"); - b.Property("Description") .HasColumnType("text"); diff --git a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql similarity index 80% rename from backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql rename to backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql index 4ab9c6b6..9f37bd78 100644 --- a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql +++ b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql @@ -9,8 +9,6 @@ ALTER TABLE database.photovoltaic_data ADD "Approval_Signature" text; ALTER TABLE database.photovoltaic_data ADD "Approval_Timestamp" timestamp with time zone; -ALTER TABLE database.photovoltaic_data ADD "DataAccess" integer NOT NULL DEFAULT 0; - ALTER TABLE database.optical_data ADD "Approval_KeyFingerprint" text; ALTER TABLE database.optical_data ADD "Approval_Query" text; @@ -21,8 +19,6 @@ ALTER TABLE database.optical_data ADD "Approval_Signature" text; ALTER TABLE database.optical_data ADD "Approval_Timestamp" timestamp with time zone; -ALTER TABLE database.optical_data ADD "DataAccess" integer NOT NULL DEFAULT 0; - ALTER TABLE database.hygrothermal_data ADD "Approval_KeyFingerprint" text; ALTER TABLE database.hygrothermal_data ADD "Approval_Query" text; @@ -33,8 +29,6 @@ ALTER TABLE database.hygrothermal_data ADD "Approval_Signature" text; ALTER TABLE database.hygrothermal_data ADD "Approval_Timestamp" timestamp with time zone; -ALTER TABLE database.hygrothermal_data ADD "DataAccess" integer NOT NULL DEFAULT 0; - ALTER TABLE database.geometric_data ADD "Approval_KeyFingerprint" text; ALTER TABLE database.geometric_data ADD "Approval_Query" text; @@ -45,8 +39,6 @@ ALTER TABLE database.geometric_data ADD "Approval_Signature" text; ALTER TABLE database.geometric_data ADD "Approval_Timestamp" timestamp with time zone; -ALTER TABLE database.geometric_data ADD "DataAccess" integer NOT NULL DEFAULT 0; - ALTER TABLE database.calorimetric_data ADD "Approval_KeyFingerprint" text; ALTER TABLE database.calorimetric_data ADD "Approval_Query" text; @@ -57,20 +49,18 @@ ALTER TABLE database.calorimetric_data ADD "Approval_Signature" text; ALTER TABLE database.calorimetric_data ADD "Approval_Timestamp" timestamp with time zone; -ALTER TABLE database.calorimetric_data ADD "DataAccess" integer NOT NULL DEFAULT 0; - CREATE TABLE database.access_rights ( "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), "InstitutionId" uuid NOT NULL, "AllowedUserCount" bigint, "AllowedDatasetsPerTime" bigint, "Period" interval NOT NULL, - "UserOfInstitutionAlreadyAccessed" uuid[] NOT NULL, + "UserAlreadyAccessed" uuid[] NOT NULL, CONSTRAINT "PK_access_rights" PRIMARY KEY ("Id") ); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") -VALUES ('20250331092709_AddAccessRights', '9.0.2'); +VALUES ('20250331141152_AddAccessRights', '9.0.2'); COMMIT; diff --git a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql similarity index 83% rename from backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql rename to backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql index c4e84dbb..cadd91de 100644 --- a/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331092709_AddAccessRights.sql +++ b/backend/src/Migrations/rollback_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql @@ -11,8 +11,6 @@ ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Signature"; ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Timestamp"; -ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccess"; - ALTER TABLE database.optical_data DROP COLUMN "Approval_KeyFingerprint"; ALTER TABLE database.optical_data DROP COLUMN "Approval_Query"; @@ -23,8 +21,6 @@ ALTER TABLE database.optical_data DROP COLUMN "Approval_Signature"; ALTER TABLE database.optical_data DROP COLUMN "Approval_Timestamp"; -ALTER TABLE database.optical_data DROP COLUMN "DataAccess"; - ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_KeyFingerprint"; ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Query"; @@ -35,8 +31,6 @@ ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Signature"; ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Timestamp"; -ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccess"; - ALTER TABLE database.geometric_data DROP COLUMN "Approval_KeyFingerprint"; ALTER TABLE database.geometric_data DROP COLUMN "Approval_Query"; @@ -47,8 +41,6 @@ ALTER TABLE database.geometric_data DROP COLUMN "Approval_Signature"; ALTER TABLE database.geometric_data DROP COLUMN "Approval_Timestamp"; -ALTER TABLE database.geometric_data DROP COLUMN "DataAccess"; - ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_KeyFingerprint"; ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Query"; @@ -59,10 +51,8 @@ ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Signature"; ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Timestamp"; -ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccess"; - DELETE FROM "__EFMigrationsHistory" -WHERE "MigrationId" = '20250331092709_AddAccessRights'; +WHERE "MigrationId" = '20250331141152_AddAccessRights'; COMMIT; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index b0deaacc..5ec72860 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -3,8 +3,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests.Dto; using Database.Data; +using Database.Logging; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace Database.Services; @@ -14,18 +17,21 @@ namespace Database.Services; /// /// /// +/// public class AccessRightsService( ApplicationDbContext context, IUserService userService, - ICacheService cacheService) : IAccessRightsService + ICacheService cacheService, + ILogger logger) : IAccessRightsService { /// - public async Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) + public async Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) + where T : IData { - List filteredData = new List(); + List filteredData = new List(); List restrictions = new List(); List institutions = new List(); - List accessRights = new List(); + List accessRights = new List(); var applicationId = userService.GetApplicationIdFromUser() ?? throw new Exception("Application Id could not be aquired."); var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new Exception("Could not get current user!"); @@ -41,44 +47,58 @@ public async Task> ApplyAccessRightsOnData(ICollection } } - foreach (IData dataItem in data) + return ProcessData(data, applicationId, institutions, currentUser, alreadyAccessedCount, cacheService, accessRights).AsQueryable(); + } + + /// + public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) + where T : IData + { + var result = await ApplyAccessRightsOnData(new List { data }, cancellationToken).ConfigureAwait(false); + + return result.FirstOrDefault(); + } + + private IEnumerable ProcessData( + ICollection data, + string applicationId, + List institutions, + CurrentUserDto currentUser, + int alreadyAccessedCount, + ICacheService cacheService, + List accessRights) + where T : IData + { + string reason = ""; + foreach (T dataItem in data) { - if (dataItem.DataAccess == Enumerations.DataAccessMode.RESTRICTED) + if (dataItem.DataAccessRights.HasRistrictions) { if (dataItem.IsRestrictedByApplication(applicationId)) { - restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Application not allowed."); + logger.DataRestriction(dataItem.Id, "Application not allowed."); continue; } if (dataItem.IsRestrictedByInstitutions(institutions)) { - restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Institution not allowed."); + logger.DataRestriction(dataItem.Id, "Institution not allowed."); continue; } if (dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedCount)) { - restrictions.Add($"Restricted Item Id: {dataItem.Id} Reason: Allowed accesses for user reached."); + logger.DataRestriction(dataItem.Id, "Allowed accesses for user reached."); continue; } - if (accessRights.Any(x => x.IsDataRestricted(dataItem, currentUser.Uuid, cacheService, out restrictions))) + if (accessRights.Any(x => x.IsDataRestricted(dataItem, currentUser.Uuid, cacheService, out reason))) { + logger.DataRestriction(dataItem.Id, reason); continue; } cacheService.SetAccessCountForUser(currentUser.Uuid, ++alreadyAccessedCount); } - filteredData.Add(dataItem); + yield return dataItem; } - - return filteredData.AsQueryable(); - } - - /// - public async Task ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken) - { - var result = await ApplyAccessRightsOnData(new List { data }, cancellationToken).ConfigureAwait(false); - - return result.FirstOrDefault(); } } \ No newline at end of file diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs index 6e1dc7b6..f334a2b6 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/IAccessRightsService.cs @@ -17,7 +17,7 @@ public interface IAccessRightsService /// Data to apply acces rights on. /// /// List of data that can be returned. - Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken); + Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) where T : IData; /// /// Apply access rights on passed data item. @@ -25,5 +25,5 @@ public interface IAccessRightsService /// Data to apply acces rights on. /// /// Data item. - Task ApplyAccessRightsOnData(IData data, CancellationToken cancellationToken); + Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData; } \ No newline at end of file From 8a667583e916934feae604870fdbd6b7960c9918 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 31 Mar 2025 16:36:21 +0200 Subject: [PATCH 309/878] Again --- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 12 ++--- backend/src/ApiRequests/Dto/UserInfoDto.cs | 10 ++-- backend/src/ApiRequests/Dto/XDataDto.cs | 52 +++++++++---------- .../src/ApiRequests/JsonSerializerOptions.cs | 2 +- backend/src/GlobalSuppressions.cs | 7 --- backend/src/Services/AccessRightsService.cs | 1 + backend/src/Services/ApiRequestService.cs | 2 +- backend/src/Services/ISigningService.cs | 4 +- .../src/Services/ResponseApprovalService.cs | 5 +- backend/src/Services/SigningService.cs | 20 +++---- 10 files changed, 53 insertions(+), 62 deletions(-) delete mode 100644 backend/src/GlobalSuppressions.cs diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index 37055f69..690b36c0 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -20,21 +20,21 @@ public enum DataSigningPermission public sealed record CurrentUserDto( Guid Id, Guid Uuid, - RepresentedInstitutions RepresentedInstitutions + RepresentedInstitutionsDto RepresentedInstitutions ); -public sealed record Edge( - Node Node, +public sealed record EdgeDto( + NodeDto Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission ); -public sealed record Node( +public sealed record NodeDto( Guid Id, Guid Uuid, string Name ); -public sealed record RepresentedInstitutions( - IReadOnlyList Edges +public sealed record RepresentedInstitutionsDto( + IReadOnlyList Edges ); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/UserInfoDto.cs b/backend/src/ApiRequests/Dto/UserInfoDto.cs index 3e4df980..f73656fa 100644 --- a/backend/src/ApiRequests/Dto/UserInfoDto.cs +++ b/backend/src/ApiRequests/Dto/UserInfoDto.cs @@ -2,12 +2,8 @@ namespace Database.ApiRequests.Dto; -public sealed record Address( - string Formatted -); - public sealed record UserInfoDto( - Address? Address, + AddressDto? Address, string Email, bool EmailVerified, string Name, @@ -16,4 +12,8 @@ public sealed record UserInfoDto( IReadOnlyList? Roles, string Sub, // Subject string? Website +); + +public sealed record AddressDto( + string Formatted ); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs index 914613a1..b0441fd0 100644 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -5,70 +5,70 @@ namespace Database.ApiRequests.Dto; -public record GeometricDataDto( +public sealed record GeometricDataDto( string Uuid, DateTime Timestamp, string ComponentId, string Name, string Description, - AppliedMethod AppliedMethod, - ResourceTree ResourceTree, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, IReadOnlyList Approvals, DateTime CreatedAt, string CreatorId, string Locale, - IReadOnlyList Resources, + IReadOnlyList Resources, IReadOnlyList Warnings, IReadOnlyList Thicknesses ); -public record CalorimetricDataDto( +public sealed record CalorimetricDataDto( string Uuid, DateTime Timestamp, string ComponentId, string Name, string Description, - AppliedMethod AppliedMethod, - ResourceTree ResourceTree, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, IReadOnlyList Approvals, DateTime CreatedAt, string CreatorId, string Locale, - IReadOnlyList Resources, + IReadOnlyList Resources, IReadOnlyList Warnings, IReadOnlyList GValues, IReadOnlyList UValues ); -public record HygrothermalDataDto( +public sealed record HygrothermalDataDto( string Uuid, DateTime Timestamp, string ComponentId, string Name, string Description, - AppliedMethod AppliedMethod, - ResourceTree ResourceTree, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, IReadOnlyList Approvals, DateTime CreatedAt, string CreatorId, string Locale, - IReadOnlyList Resources, + IReadOnlyList Resources, IReadOnlyList Warnings ); -public record OpticalDataDto( +public sealed record OpticalDataDto( string Uuid, DateTime Timestamp, string ComponentId, string Name, string Description, - AppliedMethod AppliedMethod, - ResourceTree ResourceTree, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, IReadOnlyList Approvals, DateTime CreatedAt, string CreatorId, string Locale, - IReadOnlyList Resources, + IReadOnlyList Resources, IReadOnlyList Warnings, OpticalComponentType? Type, OpticalComponentSubtype? Subtype, @@ -82,40 +82,40 @@ public record OpticalDataDto( ICollection CielabColors ); -public record PhotovoltaicDataDto( +public sealed record PhotovoltaicDataDto( string Uuid, DateTime Timestamp, string ComponentId, string Name, string Description, - AppliedMethod AppliedMethod, - ResourceTree ResourceTree, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, IReadOnlyList Approvals, DateTime CreatedAt, string CreatorId, string Locale, - IReadOnlyList Resources, + IReadOnlyList Resources, IReadOnlyList Warnings ); -public record AppliedMethod( +public sealed record AppliedMethodDto( string MethodId, IReadOnlyList Arguments, IReadOnlyList Sources ); -public record Resource( +public sealed record ResourceDto( string Description, string HashValue, string Locator, Guid DataFormatId ); -public record ResourceTree( - Root Root, +public sealed record ResourceTreeDto( + RootDto Root, IReadOnlyList NonRootVertices ); -public record Root( - object Value +public sealed record RootDto( + ResourceDto Value ); \ No newline at end of file diff --git a/backend/src/ApiRequests/JsonSerializerOptions.cs b/backend/src/ApiRequests/JsonSerializerOptions.cs index 47ddfd88..a6aee170 100644 --- a/backend/src/ApiRequests/JsonSerializerOptions.cs +++ b/backend/src/ApiRequests/JsonSerializerOptions.cs @@ -30,7 +30,7 @@ public static class JsonSerializerSettings /// /// Settings for REST Json requests. /// - public static readonly JsonSerializerOptions REST = + public static readonly JsonSerializerOptions Rest = new() { Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, diff --git a/backend/src/GlobalSuppressions.cs b/backend/src/GlobalSuppressions.cs deleted file mode 100644 index 0d5ed97c..00000000 --- a/backend/src/GlobalSuppressions.cs +++ /dev/null @@ -1,7 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage attributes that are applied to -// this project. Project-level suppressions either have no target or are given a specific target and -// scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "", Scope = "module")] \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 5ec72860..d9b98991 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -25,6 +25,7 @@ public class AccessRightsService( ILogger logger) : IAccessRightsService { /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public async Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) where T : IData { diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 5b9e28f4..a1fa73a1 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -96,7 +96,7 @@ CancellationToken cancellationToken HttpMethod.Get, uri, null, - JsonSerializerSettings.REST, + JsonSerializerSettings.Rest, httpClientFactory, httpContextAccessor, cancellationToken diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index d8f866e6..f21e17a0 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -25,6 +25,6 @@ public interface ISigningService /// Sign passed data string and return signature. /// /// Data string to create signature from. - /// True and generated signature, if successful. Otherwise false. - public Task<(bool Success, string Signature)> SignData(string data); + /// True and generated signature, if successful. Otherwise false and error. + public Task<(bool Success, string Output)> SignData(string data); } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index dbef88ca..c5e3d170 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -35,6 +35,7 @@ private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicD private sealed record OpticalDataResponse(OpticalDataDto OpticalData); /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset @@ -47,10 +48,10 @@ public async Task CreateResponseApproval(IData dataObject, Can if (!signatureResult.Success) { - throw new Exception("Signing of data failed!"); + throw new Exception($"Signing of data failed! {signatureResult.Output}"); } - return new ResponseApproval(DateTime.UtcNow, signatureResult.Signature, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); + return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); } private async Task<(string Query, string Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index aee7bc83..d54e0b9c 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -44,10 +44,8 @@ public async Task Initialize() } /// - public async Task<(bool Success, string Signature)> SignData(string data) + public async Task<(bool Success, string Output)> SignData(string data) { - string signature = ""; - // Write data string to file if (!WriteDataToFile(data)) { @@ -60,8 +58,8 @@ public async Task Initialize() if (success) { // Read signature from file - success = ReadSignatureFromFile(out signature); - logger.Signature(signature); + (success, outout) = ReadSignatureFromFile(); + logger.Signature(outout); } else { @@ -70,7 +68,7 @@ public async Task Initialize() // Remove created files RemoveFiles(); - return (success, signature); + return (success, outout); } private async Task<(bool, string)> ExecuteGnuCommand(string command) @@ -137,21 +135,19 @@ private bool WriteDataToFile(string data) } } - private bool ReadSignatureFromFile(out string signature) + private (bool, string) ReadSignatureFromFile() { try { using (StreamReader file = new StreamReader(FILENAME + FILE_EXTENSION)) { - signature = file.ReadToEnd(); + return (true, file.ReadToEnd()); } - return true; } - catch (Exception ex) + catch (IOException ex) { logger.ReadFileException(ex.ToString()); - signature = ""; - return false; + return (false, ex.ToString()); } } From 8fd7b305e4c93b0feae9f6b2731eab26d3502554 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Mon, 31 Mar 2025 16:48:17 +0200 Subject: [PATCH 310/878] Fixed Dto and added comment --- backend/src/ApiRequests/DataApi.cs | 1 + backend/src/ApiRequests/DatabaseApi.cs | 2 +- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 4 ++-- backend/src/ApiRequests/Dto/DatabaseDto.cs | 2 +- backend/src/Services/IApiRequestService.cs | 4 ++-- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 627ae487..97dc1f22 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -63,6 +63,7 @@ public class DataApi /// Throws exception, when query could not be constructed or no response. /// /// Query and response for data. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public static async Task<(string Query, string Response)> CreateQueryAndGetResponse( Guid dataId, string[] filenames, diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 8ac0c2cd..0243832c 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -26,7 +26,7 @@ public class DatabaseApi "UpdateDatabase.graphql" }; - private sealed record DatabasesData(DatabasesConnection Databases); + private sealed record DatabasesData(DatabasesConnectionDto Databases); /// /// Request database from Metabase. diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index 690b36c0..c332847a 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -23,7 +23,7 @@ public sealed record CurrentUserDto( RepresentedInstitutionsDto RepresentedInstitutions ); -public sealed record EdgeDto( +public sealed record RepresentedInstitutionsEdge( NodeDto Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission @@ -36,5 +36,5 @@ string Name ); public sealed record RepresentedInstitutionsDto( - IReadOnlyList Edges + IReadOnlyList Edges ); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/DatabaseDto.cs b/backend/src/ApiRequests/Dto/DatabaseDto.cs index e526b26d..575feaba 100644 --- a/backend/src/ApiRequests/Dto/DatabaseDto.cs +++ b/backend/src/ApiRequests/Dto/DatabaseDto.cs @@ -17,7 +17,7 @@ public sealed record DatabaseDto( bool CanCurrentUserVerifyNode ); -public sealed record DatabasesConnection +public sealed record DatabasesConnectionDto ( IReadOnlyList? Edges ); diff --git a/backend/src/Services/IApiRequestService.cs b/backend/src/Services/IApiRequestService.cs index d301c85d..1203c937 100644 --- a/backend/src/Services/IApiRequestService.cs +++ b/backend/src/Services/IApiRequestService.cs @@ -13,13 +13,13 @@ namespace Database.Services; public interface IApiRequestService { /// - /// Use Metabase as target for following requests. + /// Use Metabase as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection /// /// public ApiRequestService Metabase(); /// - /// Use Database as target for following requests. + /// Use Database as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection /// /// public ApiRequestService Database(); From aa15b0d381e4a8cc6d31bb8c83847d14de4c566a Mon Sep 17 00:00:00 2001 From: jbinterap Date: Tue, 1 Apr 2025 12:01:22 +0200 Subject: [PATCH 311/878] Fixed access right --- backend/src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/DataAccessRights.cs | 13 +++++-------- backend/src/Data/DataX.cs | 12 ++++-------- backend/src/Data/IData.cs | 2 +- backend/src/Data/InstitutionAccessRights.cs | 4 ++-- .../AccessRights/DataAccessRightsMutations.cs | 2 +- backend/src/GraphQl/Approvals/ApprovalMutations.cs | 8 ++++---- .../CalorimetricDataX/CalorimetricDataQueries.cs | 11 ++++------- .../GraphQl/GeometricDataX/GeometricDataQueries.cs | 11 ++++------- .../HygrothermalDataX/HygrothermalDataQueries.cs | 11 ++++------- .../src/GraphQl/LoggingDiagnosticEventListener.cs | 2 +- .../src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 11 ++++------- .../PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 11 ++++------- backend/src/Services/AccessRightsService.cs | 12 ++++++++---- backend/src/Services/ApiRequestService.cs | 2 +- backend/src/Services/CacheService.cs | 4 ++-- backend/src/Services/IAccessRightsService.cs | 5 ++--- backend/src/Services/ICacheService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- backend/src/Utilities/FileHelpers.cs | 4 ++-- backend/src/Utilities/MultipartRequestHelper.cs | 4 ++-- 21 files changed, 58 insertions(+), 77 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 96377979..1cfcf797 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -141,7 +141,7 @@ CancellationToken cancellationToken var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); - while (section != null) + while (section is not null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse( diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 2fd44fb1..30f824e6 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -33,8 +33,8 @@ public bool HasRistrictions { get { - return AllowedUserAndQuantity is not null && - AllowedInstitutions is not null && + return AllowedUserAndQuantity is not null || + AllowedInstitutions is not null || AllowedApplications is not null; } } @@ -46,8 +46,7 @@ public bool IsRestrictedByUser { get { - return AllowedUserAndQuantity is not null && - AllowedUserAndQuantity.Count > 0; + return AllowedUserAndQuantity is not null; } } @@ -58,8 +57,7 @@ public bool IsRestrictedByInstitution { get { - return AllowedInstitutions is not null && - AllowedInstitutions.Count > 0; + return AllowedInstitutions is not null; } } @@ -70,8 +68,7 @@ public bool IsRestrictedByApplication { get { - return AllowedApplications is not null && - AllowedApplications.Count > 0; + return AllowedApplications is not null; } } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 1ca95faf..a4ffc96e 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -71,26 +71,22 @@ DateTime createdAt /// public bool IsRestrictedByApplication(string applicationId) { - if (DataAccessRights.IsRestrictedByApplication) - return DataAccessRights.AllowedApplications!.Contains(applicationId); - return false; + return DataAccessRights.AllowedApplications is not null && DataAccessRights.AllowedApplications.Contains(applicationId); } /// public bool IsRestrictedByInstitutions(List institutions) { - if (DataAccessRights.IsRestrictedByInstitution) - return DataAccessRights.AllowedInstitutions!.Any(a => institutions.Any(b => a.Equals(b))); - return false; + return DataAccessRights.AllowedInstitutions is not null && DataAccessRights.AllowedInstitutions!.Any(a => institutions.Any(b => a.Equals(b))); } /// public bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount) { - if (DataAccessRights.IsRestrictedByUser) + if (DataAccessRights.AllowedUserAndQuantity is not null) { int limit; - if (DataAccessRights.AllowedUserAndQuantity!.TryGetValue(uuid, out limit)) + if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out limit)) { if (limit < 0 || limit > alreadyAccesedCount) { diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 4b1f0293..48799ecb 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -14,7 +14,7 @@ public interface IData : IEntity AppliedMethod AppliedMethod { get; } ICollection Approvals { get; } ICollection Resources { get; } - ResponseApproval? Approval { get; } + ResponseApproval? Approval { get; set; } string Locale { get; } DataAccessRights DataAccessRights { get; } diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index e2d27c06..c90255d2 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -44,7 +44,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService // Check restriction for time period if (AllowedDatasetsPerTime is not null) { - var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPerirod(InstitutionId); + var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPeriod(InstitutionId); if (accessesPerPeriod.StartTime.Add(Period) < DateTime.Now) { @@ -61,7 +61,7 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService } // Check restriction for users per institution - if (this.AllowedUserCount is not null) + if (AllowedUserCount is not null) { if (UserAlreadyAccessed.Count >= AllowedUserCount) { diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 8d7f207c..aa0d8c8a 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken } var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); - if (data == null) + if (data is null) { return new UpdateDataAccessRightsPayload( new UpdateDataAccessRightsError( diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index de01f595..08623bac 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -49,7 +49,7 @@ CancellationToken cancellationToken ); var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); - if (data == null) + if (data is null) { return new AddApprovalPayload( new AddApprovalError( @@ -67,7 +67,7 @@ CancellationToken cancellationToken input.Approval.Query, input.Approval.Response, currentUser.Id); - if (input.Approval.Publication != null) + if (input.Approval.Publication is not null) { approval.Publication = new Publication( input.Approval.Publication.Title, @@ -79,7 +79,7 @@ CancellationToken cancellationToken input.Approval.Publication.Urn, input.Approval.Publication.WebAddress); } - if (input.Approval.Standard != null) + if (input.Approval.Standard is not null) { approval.Standard = new Standard( input.Approval.Standard.Title, @@ -94,7 +94,7 @@ CancellationToken cancellationToken try { - ((Data.DataX)data).Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (Exception ex) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 5db1e87e..99f3a51d 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -31,6 +31,7 @@ public async Task> GetAllCalorimetricData( CancellationToken cancellationToken ) { + // TODO Use `locale`. sorting.StabilizeOrder(); IQueryable filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); @@ -41,10 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - - // TODO Use `locale`. - return (IQueryable)result; + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); } public async Task GetCalorimetricDataAsync( @@ -60,12 +58,11 @@ CancellationToken cancellationToken id, cancellationToken ); - if (calorimetricData == null) + if (calorimetricData is null) { return calorimetricData; } - var result = await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken).ConfigureAwait(false); - return result as CalorimetricData; + return await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index e75db6ce..a62dcda8 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -29,6 +29,7 @@ public async Task> GetAllGeometricData( CancellationToken cancellationToken ) { + // TODO Use `locale`. sorting.StabilizeOrder(); IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); @@ -39,10 +40,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - - // TODO Use `locale`. - return (IQueryable)result; + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); } public async Task GetGeometricDataAsync( @@ -59,12 +57,11 @@ CancellationToken cancellationToken cancellationToken ).ConfigureAwait(false); - if (geometricData == null) + if (geometricData is null) { return geometricData; } - var result = await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken).ConfigureAwait(false); - return result as GeometricData; + return await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 38f041e5..ee917e6e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -31,6 +31,7 @@ public async Task> GetAllHygrothermalData( CancellationToken cancellationToken ) { + // TODO Use `locale`. sorting.StabilizeOrder(); IQueryable filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); @@ -41,10 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - - // TODO Use `locale`. - return (IQueryable)result; + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); } public async Task GetHygrothermalDataAsync( @@ -61,12 +59,11 @@ CancellationToken cancellationToken cancellationToken ); - if (hygrothermalData == null) + if (hygrothermalData is null) { return hygrothermalData; } - var result = await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken).ConfigureAwait(false); - return result as HygrothermalData; + return await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index e78b533a..39f384a6 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -195,7 +195,7 @@ public void Dispose() StringBuilder stringBuilder = new(_context.Document.ToString(true)); stringBuilder.AppendLine(); - if (_context.Variables != null) + if (_context.Variables is not null) { var variablesConcrete = _context.Variables!.ToList(); if (variablesConcrete.Count > 0) diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index c7f47860..9234aed7 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -31,6 +31,7 @@ public async Task> GetAllOpticalData( CancellationToken cancellationToken ) { + // TODO Use `locale`. sorting.StabilizeOrder(); IQueryable filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); @@ -41,10 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - - // TODO Use `locale`. - return (IQueryable)result; + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); } public async Task GetOpticalDataAsync( @@ -61,12 +59,11 @@ CancellationToken cancellationToken cancellationToken ); - if (opticalData == null) + if (opticalData is null) { return opticalData; } - var result = await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken).ConfigureAwait(false); - return result as OpticalData; + return await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index a1482d00..638a5fd0 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -31,6 +31,7 @@ public async Task> GetAllPhotovoltaicData( CancellationToken cancellationToken ) { + // TODO Use `locale`. sorting.StabilizeOrder(); IQueryable filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); @@ -41,10 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - var result = await accessRightsService.ApplyAccessRightsOnData(filteredData.ToList(), cancellationToken).ConfigureAwait(false); - - // TODO Use `locale`. - return (IQueryable)result; + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); } public async Task GetPhotovoltaicDataAsync( @@ -61,12 +59,11 @@ CancellationToken cancellationToken cancellationToken ); - if (photovoltaicData == null) + if (photovoltaicData is null) { return photovoltaicData; } - var result = await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken).ConfigureAwait(false); - return result as PhotovoltaicData; + return await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index d9b98991..9150b89a 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -26,7 +26,7 @@ public class AccessRightsService( { /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] - public async Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) + public async Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData { List filteredData = new List(); @@ -48,20 +48,24 @@ public async Task> ApplyAccessRightsOnData(ICollection data, } } - return ProcessData(data, applicationId, institutions, currentUser, alreadyAccessedCount, cacheService, accessRights).AsQueryable(); + var result = ProcessData(data, applicationId, institutions, currentUser, alreadyAccessedCount, cacheService, accessRights); + + // Save InstitutionAccessRight changes + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + return result.AsQueryable(); } /// public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { - var result = await ApplyAccessRightsOnData(new List { data }, cancellationToken).ConfigureAwait(false); + var result = await ApplyAccessRightsOnData((new List { data }).AsQueryable(), cancellationToken).ConfigureAwait(false); return result.FirstOrDefault(); } private IEnumerable ProcessData( - ICollection data, + IQueryable data, string applicationId, List institutions, CurrentUserDto currentUser, diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index a1fa73a1..95766c85 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -121,7 +121,7 @@ CancellationToken cancellationToken uri ); httpRequestMessage.Content = httpContent; - if (bearerToken != null) + if (bearerToken is not null) { httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); } diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 2cf64ee5..45f8ab84 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -36,7 +36,7 @@ public int SetAccessCountForUser(Guid userId, int count) return accessCountCache.Set(userId, count); } - public (DateTime StartTime, int Count) GetOrCreateAccessCountForPerirod(Guid institutionId) + public (DateTime StartTime, int Count) GetOrCreateAccessCountForPeriod(Guid institutionId) { (DateTime StartTime, int Count) accessesPerPeriod; if (!timePeriodCountCache.TryGetValue(institutionId, out accessesPerPeriod)) @@ -48,7 +48,7 @@ public int SetAccessCountForUser(Guid userId, int count) public (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId) { - var accessesPerPeriod = GetOrCreateAccessCountForPerirod(institutionId); + var accessesPerPeriod = GetOrCreateAccessCountForPeriod(institutionId); accessesPerPeriod.Count++; return timePeriodCountCache.Set(institutionId, accessesPerPeriod); } diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs index f334a2b6..4d0f361c 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/IAccessRightsService.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -17,7 +16,7 @@ public interface IAccessRightsService /// Data to apply acces rights on. /// /// List of data that can be returned. - Task> ApplyAccessRightsOnData(ICollection data, CancellationToken cancellationToken) where T : IData; + Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData; /// /// Apply access rights on passed data item. diff --git a/backend/src/Services/ICacheService.cs b/backend/src/Services/ICacheService.cs index 00ffa13c..6045e0a4 100644 --- a/backend/src/Services/ICacheService.cs +++ b/backend/src/Services/ICacheService.cs @@ -13,7 +13,7 @@ public interface ICacheService int SetAccessCountForUser(Guid userId, int count); - (DateTime StartTime, int Count) GetOrCreateAccessCountForPerirod(Guid institutionId); + (DateTime StartTime, int Count) GetOrCreateAccessCountForPeriod(Guid institutionId); (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId); diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 833e283b..acd928a3 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -41,7 +41,7 @@ public class UserService( var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); logger.ExtractedToken(token); - if (token == null) + if (token is null) { return await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); } diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index c5b28cd6..73f48fc4 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -77,7 +77,7 @@ public static async Task ProcessFormFile(IFormFile formFile, typeof(T).GetProperty( formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); - if (property != null) + if (property is not null) if (property.GetCustomAttribute() is DisplayAttribute displayAttribute) fieldDisplayName = $"{displayAttribute.Name} "; @@ -185,7 +185,7 @@ public static async Task ProcessStreamedFile( private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) { - if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0) return false; + if (string.IsNullOrEmpty(fileName) || data is null || data.Length == 0) return false; var ext = Path.GetExtension(fileName).ToLowerInvariant(); diff --git a/backend/src/Utilities/MultipartRequestHelper.cs b/backend/src/Utilities/MultipartRequestHelper.cs index d50eaace..711f84c2 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -30,7 +30,7 @@ public static bool IsMultipartContentType(string? contentType) public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) { // Content-Disposition: form-data; name="key"; - return contentDisposition != null + return contentDisposition is not null && contentDisposition.DispositionType.Equals("form-data") && string.IsNullOrEmpty(contentDisposition.FileName.Value) && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value); @@ -39,7 +39,7 @@ public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue c public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition) { // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg" - return contentDisposition != null + return contentDisposition is not null && contentDisposition.DispositionType.Equals("form-data") && (!string.IsNullOrEmpty(contentDisposition.FileName.Value) || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value)); From 397bf296dc87ef0393bdaf77b66319b2ddfb1ba3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 15 May 2025 16:55:56 +0200 Subject: [PATCH 312/878] Delete user or group if they already exist inside a Docker image before recreating them (this will not work in general) --- backend/Dockerfile | 4 ++++ backend/Dockerfile-bootstrap | 4 ++++ frontend/Dockerfile | 2 -- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index da1b8290..8a172a4b 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -17,6 +17,10 @@ ARG GNUPG_PRIVATEKEY_FILE_NAME # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) RUN \ + existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ + existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_user_name}"; then deluser --system "${existing_user_name}"; fi && \ + if test -n "${existing_group_name}"; then delgroup --system "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 15998e55..b57cfebe 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -11,6 +11,10 @@ ARG USER_ID # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) RUN \ + existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ + existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_user_name}"; then deluser --system "${existing_user_name}"; fi && \ + if test -n "${existing_group_name}"; then delgroup --system "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me diff --git a/frontend/Dockerfile b/frontend/Dockerfile index f43211cf..4bad00d8 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -8,8 +8,6 @@ # https://hub.docker.com/_/node FROM node:22.14-bookworm-slim -ARG GROUP_ID -ARG USER_ID ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME # TODO directory node_modules is created on host machine when container is run From 89f3cb86f019e7c5eaf391ae369eff7cfed71921 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 15 May 2025 16:56:58 +0200 Subject: [PATCH 313/878] Add sample commands that will work for reusing the certificate authority from the metabase project if both reside alongside each other --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f386ba2e..a46a4a83 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,15 @@ If you have a question for which you don't find the answer in this repository, p by running `make ssl`. If you are locally working on the metabase and the database and if you need them to communicate over HTTPS, then instead of running `make ssl`, make the `CERTIFICATE_AUTHORITY_*` variable values in - the `.env` file match the ones from the metabase, copy the certificate - authority files from the directories `./ssl`, `./backend/ssl`, and - `./frontend/ssl` of the metabase project into the respective directories in - the database project, and run the command `make generate-ssl-certificate`. + the `.env` file match the ones from the metabase (these variables match in + the `.env.sample` files), copy the certificate authority files from the + directories `./ssl`, `./backend/ssl`, and `./frontend/ssl` of the metabase + project into the respective directories in the database project (if the + repository reside alongside each other by running `mkdir ./ssl ./backend/ssl + ./frontend/ssl && cp ../metabase/ssl/ca.* ./ssl && cp + ../metabase/backend/ssl/ca.* ./backend/ssl && cp + ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command `make + generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `make jwt-certificates`. 1. Start all services and follow their logs by running `make up logs`. From c2f54e9f523cfabda0fb0897680bdf8991b9c003 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 16 May 2025 16:19:27 +0200 Subject: [PATCH 314/878] Add GNU Make taret `gpg` to generate GPG key and add that step in the readme and also explain how to export the secret key to a file --- Makefile | 12 ++++++++++++ README.md | 11 +++++++++++ backend/.gitignore | 1 + backend/Dockerfile | 11 +++++------ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 661f6660..4aac3ef4 100644 --- a/Makefile +++ b/Makefile @@ -220,6 +220,18 @@ prepare-release : ## Prepare release make prepare-release .PHONY : prepare-release +gpg : COMMENT = +gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}`, for example, `make NAME="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` + gpg \ + --quick-generate-key \ + --pinentry-mode loopback \ + --batch \ + --passphrase ${GNUPG_PRIVATEKEY_PASSPHRASE} \ + "${NAME} (${COMMENT}) <${EMAIL}>" \ + ed25519 \ + cert \ + never + # --------------------- # # Generate Certificates # # --------------------- # diff --git a/README.md b/README.md index a46a4a83..e3fe0af5 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,17 @@ If you have a question for which you don't find the answer in this repository, p generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `make jwt-certificates`. +1. Generate a GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` + given in the `.env` file by running `make NAME=${name} COMMENT=${comment} + EMAIL=${email} gpg` with your information filled in. List the secret keys + with their identifiers by running `gpg --list-secret-keys + --keyid-format=long` and copy the identifier of the new secret key to the + clipboard. Export the secret key to + `./backend/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` by running + `mkdir --parents ./backend/gpg-keys && gpg --armor --export-secret-keys ${key_id} > ./backend/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` + with `${key_id}` set to the key identifier and + `${GNUPG_PRIVATEKEY_FILE_NAME}` replaced by the value given in the `.env` + file. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see diff --git a/backend/.gitignore b/backend/.gitignore index 7893655f..044fd6ef 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -8,6 +8,7 @@ /secrets /ssl +/gpg-keys /test/**/__snapshots__/__mismatch__ diff --git a/backend/Dockerfile b/backend/Dockerfile index 8a172a4b..2595d7ff 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -149,16 +149,15 @@ COPY \ # GnuPG # Copy keys to image RUN \ -mkdir --parents \ -./gpg-keys + mkdir ./gpg-keys COPY \ ---chown=me:us \ -./gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} \ -./gpg-keys/ + --chown=me:us \ + ./gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} \ + ./gpg-keys/ # Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) RUN \ -export GPG_TTY=$(tty) + export GPG_TTY=$(tty) # https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile.alpine-x64 # https://github.com/dotnet/dotnet-docker/blob/master/samples/run-aspnetcore-https-development.md From 017c693906ae34d7f04b0c7b901477aa6a1a4b15 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 19 May 2025 18:28:26 +0200 Subject: [PATCH 315/878] Upgrade packages and tools to latest compatible versions --- backend/dotnet-tools.json | 16 ++++++------- backend/src/Database.csproj | 36 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 2 +- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 46d3e302..4b24c83a 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -10,21 +10,21 @@ "rollForward": false }, "dotnet-counters": { - "version": "9.0.607501", + "version": "9.0.621003", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "9.0.607501", + "version": "9.0.621003", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "9.0.2", + "version": "9.0.5", "commands": [ "dotnet-ef" ], @@ -38,21 +38,21 @@ "rollForward": false }, "dotnet-trace": { - "version": "9.0.607501", + "version": "9.0.621003", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "9.0.607501", + "version": "9.0.621003", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "9.0.607501", + "version": "9.0.621003", "commands": [ "dotnet-sos" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2024.3.6", + "version": "2025.1.2", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "15.0.3", + "version": "15.1.4", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 3ecd4842..e96c2ee0 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -24,38 +24,38 @@ - - - - - - + + + + + + - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - - + + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index a8d32e41..d04fe6e2 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -19,7 +19,7 @@ - + From fd80cbbdc2bf650f868086d9c1df04a94b2f27bb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 19 May 2025 18:56:41 +0200 Subject: [PATCH 316/878] Add SQL migration and rollback scripts --- backend/src/Migrations/current | 2 +- ...akeDescriptionOptional_to_20250331141152_AddAccessRights.sql | 2 +- backend/src/Migrations/previous | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index a7b7a40d..bf1ae126 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250218172859_MakeDescriptionOptional +20250331141152_AddAccessRights diff --git a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql index 9f37bd78..9630fa08 100644 --- a/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql +++ b/backend/src/Migrations/migrate_from_20250218172859_MakeDescriptionOptional_to_20250331141152_AddAccessRights.sql @@ -60,7 +60,7 @@ CREATE TABLE database.access_rights ( ); INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") -VALUES ('20250331141152_AddAccessRights', '9.0.2'); +VALUES ('20250331141152_AddAccessRights', '9.0.5'); COMMIT; diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index 3a8fbc5b..a7b7a40d 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20240917073732_AddGeometricData +20250218172859_MakeDescriptionOptional From 19d417f6c6850bba1b0a800e319a71d7c48e5f2c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 May 2025 17:58:52 +0200 Subject: [PATCH 317/878] Do not throw exceptions of unspecific type `Exception` --- backend/src/ApiRequests/DataApi.cs | 8 +++++--- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 2 +- backend/src/Extensions/HttpContextAccessorExtensions.cs | 6 +++--- backend/src/Services/AccessRightsService.cs | 5 ++--- backend/src/Services/ResponseApprovalService.cs | 3 +-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 97dc1f22..26b6e8bc 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -63,7 +63,6 @@ public class DataApi /// Throws exception, when query could not be constructed or no response. /// /// Query and response for data. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public static async Task<(string Query, string Response)> CreateQueryAndGetResponse( Guid dataId, string[] filenames, @@ -80,7 +79,7 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), new { uuid = dataId }, "" ); - var query = request.Query ?? throw new Exception("Failed to construct GraphQL query."); + var query = request.Query ?? throw new InvalidOperationException("Failed to construct GraphQL query."); var responseObject = await apiRequestService.Database().QueryGraphQl( appSettings, @@ -90,7 +89,10 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), cancellationToken ).ConfigureAwait(false); var response = JsonSerializer.Serialize(responseObject); - if (string.IsNullOrEmpty(response)) throw new Exception("No response for query."); + if (string.IsNullOrEmpty(response)) + { + throw new InvalidOperationException("No response for query."); + } return (query, response); } diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index c332847a..50063780 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -9,7 +9,7 @@ public enum InstitutionRepresentativeRole ASSISTANT } -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Bezeichner dürfen kein falsches Suffix aufweisen", Justification = "")] +[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711")] public enum DataSigningPermission { NEVER, diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 06b7216e..fb2bf42f 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; @@ -27,12 +28,11 @@ public static class HttpContextAccessorExtensions CookieAuthenticationDefaults.AuthenticationScheme, OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate); - return !String.IsNullOrEmpty(token) && !String.IsNullOrEmpty(expirationDate) && CheckExpirationDate(expirationDate) ? token : null; + return !string.IsNullOrEmpty(token) && !string.IsNullOrEmpty(expirationDate) && CheckExpirationDate(expirationDate) ? token : null; } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1305:IFormatProvider angeben", Justification = "No need at this point.")] private static bool CheckExpirationDate(string expirationDate) { - return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate)) < DateTime.UtcNow; + return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate, CultureInfo.InvariantCulture)) < DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 9150b89a..df820f01 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -25,7 +25,6 @@ public class AccessRightsService( ILogger logger) : IAccessRightsService { /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public async Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData { @@ -34,8 +33,8 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, List institutions = new List(); List accessRights = new List(); - var applicationId = userService.GetApplicationIdFromUser() ?? throw new Exception("Application Id could not be aquired."); - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new Exception("Could not get current user!"); + var applicationId = userService.GetApplicationIdFromUser() ?? throw new InvalidOperationException("Application Id could not be aquired."); + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException("Could not get current user!"); var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); foreach (var institution in currentUser.RepresentedInstitutions.Edges) diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index c5e3d170..8d5ed250 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -35,7 +35,6 @@ private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicD private sealed record OpticalDataResponse(OpticalDataDto OpticalData); /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset @@ -48,7 +47,7 @@ public async Task CreateResponseApproval(IData dataObject, Can if (!signatureResult.Success) { - throw new Exception($"Signing of data failed! {signatureResult.Output}"); + throw new InvalidOperationException($"Signing of data failed! {signatureResult.Output}"); } return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); From d47b458a3bc09db509c1115a0917e252358216d0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 May 2025 17:59:47 +0200 Subject: [PATCH 318/878] Remove byte order mark by running `make dos2unix` --- backend/src/ApiRequests/DataApi.cs | 2 +- backend/src/ApiRequests/DatabaseApi.cs | 2 +- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 2 +- backend/src/ApiRequests/Dto/DatabaseDto.cs | 2 +- backend/src/ApiRequests/Dto/UserInfoDto.cs | 2 +- backend/src/ApiRequests/Dto/XDataDto.cs | 2 +- backend/src/ApiRequests/JsonSerializerOptions.cs | 2 +- backend/src/ApiRequests/UserApi.cs | 2 +- backend/src/Authorization/CalorimetricDataAuthorization.cs | 2 +- backend/src/Authorization/CommonAuthorization.cs | 2 +- backend/src/Authorization/FileUploadAuthorization.cs | 2 +- backend/src/Authorization/GeometricDataAuthorization.cs | 2 +- backend/src/Authorization/GetHttpsResourceAuthorization.cs | 2 +- backend/src/Authorization/HygrothermalDataAuthorization.cs | 2 +- backend/src/Authorization/OpticalDataAuthorization.cs | 2 +- backend/src/Authorization/PhotovoltaicDataAuthorization.cs | 2 +- backend/src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/CalorimetricData.cs | 2 +- backend/src/Data/DataAccessRights.cs | 2 +- backend/src/Data/DataX.cs | 2 +- backend/src/Data/GeometricData.cs | 2 +- backend/src/Data/HygrothermalData.cs | 2 +- backend/src/Data/IData.cs | 2 +- backend/src/Data/InstitutionAccessRights.cs | 2 +- backend/src/Data/OpticalData.cs | 2 +- backend/src/Data/PhotovoltaicData.cs | 2 +- backend/src/Data/ResponseApproval.cs | 2 +- backend/src/Data/UserInfo.cs | 2 +- backend/src/Extensions/HttpContextAccessorExtensions.cs | 2 +- .../GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs | 2 +- backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs | 2 +- backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs | 2 +- backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs | 2 +- .../src/GraphQl/AccessRights/InstitutionAccessRightsError.cs | 2 +- .../GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs | 2 +- .../src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs | 2 +- .../src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs | 2 +- backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs | 2 +- backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs | 2 +- .../src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs | 2 +- .../src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs | 2 +- backend/src/GraphQl/Approvals/AddApprovalError.cs | 2 +- backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs | 2 +- backend/src/GraphQl/Approvals/AddApprovalPayload.cs | 2 +- backend/src/GraphQl/Approvals/ApprovalInput.cs | 2 +- backend/src/GraphQl/Approvals/ApprovalMutations.cs | 2 +- backend/src/GraphQl/Approvals/ApprovalPayload.cs | 2 +- backend/src/GraphQl/Approvals/DataApprovalInput.cs | 2 +- backend/src/GraphQl/Approvals/DataApprovalType.cs | 2 +- .../src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs | 2 +- .../src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs | 2 +- .../CalorimetricDataX/CreateCalorimetricDataErrorCode.cs | 2 +- .../GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs | 2 +- .../GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs | 2 +- backend/src/GraphQl/Databases/Database.cs | 2 +- backend/src/GraphQl/Databases/DatabaseMutations.cs | 2 +- backend/src/GraphQl/Databases/DatabasePayload.cs | 2 +- backend/src/GraphQl/Databases/DatabaseQueries.cs | 2 +- backend/src/GraphQl/Databases/UpdateDatabasePayload.cs | 2 +- .../src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs | 2 +- backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs | 2 +- .../src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs | 2 +- backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs | 2 +- backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs | 2 +- .../HygrothermalDataX/CreateHygrothermalDataErrorCode.cs | 2 +- .../GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs | 2 +- .../GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs | 2 +- .../src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs | 2 +- .../src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs | 2 +- backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs | 2 +- backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs | 2 +- backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 2 +- .../PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs | 2 +- .../GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs | 2 +- .../GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs | 2 +- .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs | 2 +- .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- backend/src/Logging/AccessRightServiceLogging.cs | 2 +- backend/src/Logging/ResponseApprovalServiceLogging.cs | 2 +- backend/src/Logging/SigningServiceLogging.cs | 2 +- backend/src/Logging/UserServiceLogging.cs | 2 +- .../Migrations/20230210135413_AddDataProtectionKeys.Designer.cs | 2 +- backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs | 2 +- backend/src/Migrations/20230420143838_OpenIddict.Designer.cs | 2 +- backend/src/Migrations/20230420143838_OpenIddict.cs | 2 +- .../src/Migrations/20230427133946_CreateUserTable.Designer.cs | 2 +- backend/src/Migrations/20230427133946_CreateUserTable.cs | 2 +- .../src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs | 2 +- backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs | 2 +- .../20240709144935_AddStatementToDataApproval.Designer.cs | 2 +- .../src/Migrations/20240709144935_AddStatementToDataApproval.cs | 2 +- .../20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs | 2 +- .../Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs | 2 +- .../src/Migrations/20240917073732_AddGeometricData.Designer.cs | 2 +- backend/src/Migrations/20240917073732_AddGeometricData.cs | 2 +- backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs | 2 +- backend/src/Migrations/20250129163501_UpgradeToNet9.cs | 2 +- ...20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs | 2 +- .../20250213155333_AddTypeSubtypeAndCoatedSideToData.cs | 2 +- .../20250218172859_MakeDescriptionOptional.Designer.cs | 2 +- .../src/Migrations/20250218172859_MakeDescriptionOptional.cs | 2 +- .../src/Migrations/20250331141152_AddAccessRights.Designer.cs | 2 +- backend/src/Migrations/20250331141152_AddAccessRights.cs | 2 +- backend/src/Migrations/ApplicationDbContextModelSnapshot.cs | 2 +- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/ApiRequestService.cs | 2 +- backend/src/Services/CacheService.cs | 2 +- backend/src/Services/DataService.cs | 2 +- backend/src/Services/EmailSender.cs | 2 +- backend/src/Services/IAccessRightsService.cs | 2 +- backend/src/Services/IApiRequestService.cs | 2 +- backend/src/Services/ICacheService.cs | 2 +- backend/src/Services/IDataService.cs | 2 +- backend/src/Services/IResponseApprovalService.cs | 2 +- backend/src/Services/ISigningService.cs | 2 +- backend/src/Services/IUserService.cs | 2 +- backend/src/Services/ResponseApprovalService.cs | 2 +- backend/src/Services/SigningService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- backend/src/Startup.cs | 2 +- 124 files changed, 124 insertions(+), 124 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 26b6e8bc..a5741889 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Text.Json; using System.Threading; diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 0243832c..b7e4d1b0 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index 50063780..25c95c96 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Database.ApiRequests.Dto; diff --git a/backend/src/ApiRequests/Dto/DatabaseDto.cs b/backend/src/ApiRequests/Dto/DatabaseDto.cs index 575feaba..92cf14e2 100644 --- a/backend/src/ApiRequests/Dto/DatabaseDto.cs +++ b/backend/src/ApiRequests/Dto/DatabaseDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Database.GraphQl.Databases; diff --git a/backend/src/ApiRequests/Dto/UserInfoDto.cs b/backend/src/ApiRequests/Dto/UserInfoDto.cs index f73656fa..4b22065d 100644 --- a/backend/src/ApiRequests/Dto/UserInfoDto.cs +++ b/backend/src/ApiRequests/Dto/UserInfoDto.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.ApiRequests.Dto; diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs index b0441fd0..279dcc10 100644 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Database.Data; using Database.Enumerations; diff --git a/backend/src/ApiRequests/JsonSerializerOptions.cs b/backend/src/ApiRequests/JsonSerializerOptions.cs index a6aee170..5bb1bc71 100644 --- a/backend/src/ApiRequests/JsonSerializerOptions.cs +++ b/backend/src/ApiRequests/JsonSerializerOptions.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; using GraphQL.Client.Serializer.SystemTextJson; diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index f4e177a4..90f98754 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index deabcc7d..a3102291 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 4db7c9b8..bed95815 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index 883a8e7b..2dc68837 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index 4a564d40..3f6d31c8 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index 1c452adc..5126276a 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index bbc0d483..982ec75b 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index 82055ad4..5152fba8 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 21c12b21..546899d7 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using Database.ApiRequests.Dto; diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 1cfcf797..5409b56b 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 131d6e66..43af5e63 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 30f824e6..8e62dbe7 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index a4ffc96e..fe1974f3 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index e6222cba..c67ccc60 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 4212b1e0..61f06a26 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 48799ecb..a6239da2 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Database.Data; diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index c90255d2..6c6b7a66 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Database.Services; diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index c820539b..ff821806 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using Database.Enumerations; diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 537e2e78..35660ce4 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index e668472d..f90dfd87 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore; namespace Database.Data diff --git a/backend/src/Data/UserInfo.cs b/backend/src/Data/UserInfo.cs index 95e4e050..3607e4c8 100644 --- a/backend/src/Data/UserInfo.cs +++ b/backend/src/Data/UserInfo.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Database.ApiRequests.Dto; namespace Database.Data; diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index fb2bf42f..67326111 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs index 0dda6179..1770bbbf 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs @@ -1,4 +1,4 @@ -namespace Database.GraphQl.AccessRights; +namespace Database.GraphQl.AccessRights; public class AddInstitutionAccessRightsPayload : InstitutionAccessRightsPayload diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs index 403432ac..4703ec9f 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.Data; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index aa0d8c8a..d3182274 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs index 9e59cea7..b6a5a7f7 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using System.Collections.Generic; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs index da211e42..c0b18cc1 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs index cc1aab16..a2b92fa0 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs index 1e58e6aa..de9269e0 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs index e52a5614..d5a14f81 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs index c51b19b6..e3b943df 100644 --- a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs @@ -1,4 +1,4 @@ -namespace Database.GraphQl.AccessRights; +namespace Database.GraphQl.AccessRights; public class UpdateAccessRightsPayload : InstitutionAccessRightsPayload diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs index c235d264..0d9e67b7 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs index 0e9d68f1..cb6842ae 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs index ed48f464..8489e9e6 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/Approvals/AddApprovalError.cs b/backend/src/GraphQl/Approvals/AddApprovalError.cs index 048032c1..608f661d 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalError.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalError.cs @@ -1,4 +1,4 @@ -using Database.GraphQl.GeometricDataX; +using Database.GraphQl.GeometricDataX; using System.Collections.Generic; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs index 605a937a..7e460b47 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs index 24286292..da65f990 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/Approvals/ApprovalInput.cs b/backend/src/GraphQl/Approvals/ApprovalInput.cs index 226d6081..92f60572 100644 --- a/backend/src/GraphQl/Approvals/ApprovalInput.cs +++ b/backend/src/GraphQl/Approvals/ApprovalInput.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index 08623bac..7ccbcb69 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Authorization; diff --git a/backend/src/GraphQl/Approvals/ApprovalPayload.cs b/backend/src/GraphQl/Approvals/ApprovalPayload.cs index ef4c67ab..010bd42c 100644 --- a/backend/src/GraphQl/Approvals/ApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/ApprovalPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using System.Collections.Generic; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/Approvals/DataApprovalInput.cs b/backend/src/GraphQl/Approvals/DataApprovalInput.cs index 78ab9b44..d22e24bf 100644 --- a/backend/src/GraphQl/Approvals/DataApprovalInput.cs +++ b/backend/src/GraphQl/Approvals/DataApprovalInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.GraphQl.Publications; using Database.GraphQl.Standards; diff --git a/backend/src/GraphQl/Approvals/DataApprovalType.cs b/backend/src/GraphQl/Approvals/DataApprovalType.cs index f930cd5a..b719d18d 100644 --- a/backend/src/GraphQl/Approvals/DataApprovalType.cs +++ b/backend/src/GraphQl/Approvals/DataApprovalType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using HotChocolate.Types; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index f7a43701..9e00bb89 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Threading; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 99f3a51d..9c6ea62a 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs index 4eb1768d..237cced2 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index ff46c8c8..fb84ef1c 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs index 3d4e2c16..197a9979 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs index 64281713..5ad268cc 100644 --- a/backend/src/GraphQl/Databases/Database.cs +++ b/backend/src/GraphQl/Databases/Database.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 1f409a2a..2adefcd3 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index 238e802d..14f27274 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index f0191501..fbd4051e 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Text.Json; diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index 6c025a6b..4463e551 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs index 7371092b..cb23d15b 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index a0327d69..46a3663e 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs index 941d5a83..b49c59ca 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 55381694..afd11ecd 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Threading; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index a62dcda8..d674a924 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 37bb3c76..828e16ec 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Threading; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs index 915c2070..86de5839 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.HygrothermalDataX; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index 3e9890d1..b6320a99 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs index 745d61fd..3f24825b 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.HygrothermalDataX; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 839de2d4..84761dac 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Threading; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index ee917e6e..0e0e05a9 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs index 4f1a5d28..8eb2a18a 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index bf1f6e95..b3e13c8d 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs index 6d8c502a..e173c816 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index fa29957a..c5fea28f 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Text.Json; using System.Threading; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 9234aed7..a5020efa 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs index ad79dd0f..88b98d84 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.PhotovoltaicDataX; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index eebf7dcf..a3db233c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -1,4 +1,4 @@ -using System; +using System; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs index 37fafd74..ca418f7f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; namespace Database.GraphQl.PhotovoltaicDataX; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 194db6f7..548b217d 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Security.Claims; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 638a5fd0..a9682a98 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 4c43668a..056a024f 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Net.Http; using System.Security.Claims; diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs index fce0f183..1e6342d4 100644 --- a/backend/src/Logging/AccessRightServiceLogging.cs +++ b/backend/src/Logging/AccessRightServiceLogging.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.Services; using Microsoft.Extensions.Logging; diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs index 0c055361..4f152de8 100644 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs index b8d8cb37..cfb81f77 100644 --- a/backend/src/Logging/SigningServiceLogging.cs +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs index 24ddb7c2..736c3209 100644 --- a/backend/src/Logging/UserServiceLogging.cs +++ b/backend/src/Logging/UserServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs index 01011be0..88112da6 100644 --- a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs index 76f93ae7..2ff935b5 100644 --- a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable diff --git a/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs b/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs index 9e0bbdcc..fa6c6a5d 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs index ce093f4c..4ece5212 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs b/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs index cfd6fd51..0a10ee95 100644 --- a/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs +++ b/backend/src/Migrations/20230427133946_CreateUserTable.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.cs b/backend/src/Migrations/20230427133946_CreateUserTable.cs index 317b3e46..362942c7 100644 --- a/backend/src/Migrations/20230427133946_CreateUserTable.cs +++ b/backend/src/Migrations/20230427133946_CreateUserTable.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs index 1c405b3a..daa5104f 100644 --- a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs index 0022bebf..52c65e9a 100644 --- a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs index 05aa0a18..3cc819ed 100644 --- a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs index e048825a..4d17959c 100644 --- a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs @@ -1,4 +1,4 @@ -using Database.Enumerations; +using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs index 5b997247..53133924 100644 --- a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs index 96e6e566..615fb132 100644 --- a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs @@ -1,4 +1,4 @@ -using Database.Enumerations; +using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs b/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs index d5c7c75e..400961f5 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index 2a746686..3157c737 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json; using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs index b53d128e..0ab91fb5 100644 --- a/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs index 77a3886a..84446de7 100644 --- a/backend/src/Migrations/20250129163501_UpgradeToNet9.cs +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs @@ -1,4 +1,4 @@ -using Database.Enumerations; +using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs index 49b415e5..3d4d502c 100644 --- a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs index 5ca77b2b..f1714e66 100644 --- a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs @@ -1,4 +1,4 @@ -using Database.Enumerations; +using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs index c6d02297..e9cb40e5 100644 --- a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs index 2d14e53e..e2df8ce1 100644 --- a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs index a3a62599..fc8bf85a 100644 --- a/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250331141152_AddAccessRights.cs b/backend/src/Migrations/20250331141152_AddAccessRights.cs index 365b33be..1cad1d68 100644 --- a/backend/src/Migrations/20250331141152_AddAccessRights.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index a6e212d1..fc98eb1a 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index df820f01..019c9364 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 95766c85..050e7ad5 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net; diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 45f8ab84..0e7fea30 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.ApiRequests.Dto; using Microsoft.Extensions.Caching.Memory; diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index afc5f91f..c9dc1956 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 27af95ec..30198d24 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Logging; diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/IAccessRightsService.cs index 4d0f361c..7a4016e6 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/IAccessRightsService.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; diff --git a/backend/src/Services/IApiRequestService.cs b/backend/src/Services/IApiRequestService.cs index 1203c937..051bec38 100644 --- a/backend/src/Services/IApiRequestService.cs +++ b/backend/src/Services/IApiRequestService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Services/ICacheService.cs b/backend/src/Services/ICacheService.cs index 6045e0a4..209832e1 100644 --- a/backend/src/Services/ICacheService.cs +++ b/backend/src/Services/ICacheService.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.ApiRequests.Dto; namespace Database.Services; diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/IDataService.cs index 6800ec47..e32da2b3 100644 --- a/backend/src/Services/IDataService.cs +++ b/backend/src/Services/IDataService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Data; diff --git a/backend/src/Services/IResponseApprovalService.cs b/backend/src/Services/IResponseApprovalService.cs index 656e0478..9054c65b 100644 --- a/backend/src/Services/IResponseApprovalService.cs +++ b/backend/src/Services/IResponseApprovalService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Data; diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/ISigningService.cs index f21e17a0..0e9557df 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/ISigningService.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using System; namespace Database.Services; diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/IUserService.cs index 715aa52a..eca84ae9 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/IUserService.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Database.ApiRequests.Dto; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 8d5ed250..220e4eff 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d54e0b9c..04beb5df 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Threading.Tasks; diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index acd928a3..7cb6b7de 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,4 +1,4 @@ -using System.Net.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 5effae79..cec69aab 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Net.Http; using System.Text; From 5c30cf9ccdaa3ace3416c5e3d95ef2eb580e47e8 Mon Sep 17 00:00:00 2001 From: jbinterap Date: Thu, 17 Apr 2025 13:11:56 +0200 Subject: [PATCH 319/878] Added Method as service --- backend/src/ApiRequests/DataApi.cs | 54 ++++++- backend/src/ApiRequests/DatabaseApi.cs | 79 +++++++++- backend/src/ApiRequests/Dto/XDataDto.cs | 16 ++ .../src/ApiRequests/JsonSerializerOptions.cs | 18 +++ backend/src/ApiRequests/Queries/DataX.graphql | 5 + backend/src/ApiRequests/UserApi.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 4 + .../src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/InstitutionAccessRights.cs | 2 +- .../AccessRights/DataAccessRightsMutations.cs | 2 +- .../GraphQl/Approvals/ApprovalMutations.cs | 2 +- .../CalorimetricDataMutations.cs | 2 +- .../CalorimetricDataQueries.cs | 2 +- backend/src/GraphQl/DataX/DataQueries.cs | 33 +++++ .../GraphQl/Databases/DatabaseMutations.cs | 2 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 2 +- .../GeometricDataX/GeometricDataQueries.cs | 2 +- .../GetHttpsResourceMutations.cs | 2 +- .../HygrothermalDataMutations.cs | 2 +- .../HygrothermalDataQueries.cs | 2 +- .../GraphQl/MethodAsService/FileInputData.cs | 70 +++++++++ .../MethodAsService/MethodAsServiceError.cs | 17 +++ .../MethodAsServiceErrorCode.cs | 10 ++ .../MethodAsService/MethodAsServiceInput.cs | 9 ++ .../MethodAsService/MethodAsServicePayload.cs | 51 +++++++ .../MethodAsService/MethodAsServiceQuaries.cs | 140 ++++++++++++++++++ .../MethodAsServiceWithFileInput.cs | 10 ++ .../OpticalDataX/OpticalDataMutations.cs | 2 +- .../OpticalDataX/OpticalDataQueries.cs | 2 +- .../PhotovoltaicDataMutations.cs | 2 +- .../PhotovoltaicDataQueries.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- .../src/Logging/AccessRightServiceLogging.cs | 2 +- .../Logging/ResponseApprovalServiceLogging.cs | 2 +- backend/src/Logging/SigningServiceLogging.cs | 2 +- backend/src/Logging/UserServiceLogging.cs | 2 +- backend/src/Methods/IMethod.cs | 13 ++ backend/src/Methods/TransmittanceSumMethod.cs | 26 ++++ backend/src/Program.cs | 2 +- backend/src/Services/AccessRightsService.cs | 1 + backend/src/Services/ApiRequestService.cs | 25 ++++ backend/src/Services/CacheService.cs | 1 + backend/src/Services/DataService.cs | 1 + backend/src/Services/EmailSender.cs | 1 + .../{ => Interfaces}/IAccessRightsService.cs | 2 +- .../{ => Interfaces}/IApiRequestService.cs | 23 ++- .../{ => Interfaces}/ICacheService.cs | 2 +- .../Services/{ => Interfaces}/IDataService.cs | 2 +- .../Services/{ => Interfaces}/IEmailSender.cs | 4 +- .../Interfaces/IMethodCalculationService.cs | 15 ++ .../IResponseApprovalService.cs | 2 +- .../{ => Interfaces}/ISigningService.cs | 4 +- .../Services/{ => Interfaces}/IUserService.cs | 2 +- .../src/Services/MethodCalculationService.cs | 30 ++++ .../src/Services/ResponseApprovalService.cs | 1 + backend/src/Services/SigningService.cs | 1 + backend/src/Services/UserService.cs | 1 + backend/src/Startup.cs | 1 + 59 files changed, 686 insertions(+), 36 deletions(-) create mode 100644 backend/src/ApiRequests/Queries/DataX.graphql create mode 100644 backend/src/GraphQl/DataX/DataQueries.cs create mode 100644 backend/src/GraphQl/MethodAsService/FileInputData.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs create mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs create mode 100644 backend/src/Methods/IMethod.cs create mode 100644 backend/src/Methods/TransmittanceSumMethod.cs rename backend/src/Services/{ => Interfaces}/IAccessRightsService.cs (96%) rename backend/src/Services/{ => Interfaces}/IApiRequestService.cs (71%) rename backend/src/Services/{ => Interfaces}/ICacheService.cs (93%) rename backend/src/Services/{ => Interfaces}/IDataService.cs (94%) rename backend/src/Services/{ => Interfaces}/IEmailSender.cs (68%) create mode 100644 backend/src/Services/Interfaces/IMethodCalculationService.cs rename backend/src/Services/{ => Interfaces}/IResponseApprovalService.cs (94%) rename backend/src/Services/{ => Interfaces}/ISigningService.cs (96%) rename backend/src/Services/{ => Interfaces}/IUserService.cs (94%) create mode 100644 backend/src/Services/MethodCalculationService.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index a5741889..a155018d 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Services; +using Database.Services.Interfaces; using GraphQL; using Microsoft.AspNetCore.Http; @@ -49,6 +49,12 @@ public class DataApi "OpticalData.graphql" }; + public static readonly string[] DataXFileNames = + { + "DataFields.graphql", + "DataX.graphql" + }; + /// /// Create query to request data and get response. /// @@ -96,4 +102,50 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), return (query, response); } + + /// + /// Create query to request data and get response. + /// + /// Id of data to request. + /// File names of query files. + /// + /// + /// + /// + /// + /// + /// Throws exception, when query could not be constructed or no response. + /// + /// Query and response for data. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] + public static async Task> GetDataFromDatabase( + Uri databaseUri, + Guid dataId, + string[] filenames, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + + where TGraphQlResponse : class + { + var request = new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), + new { uuid = dataId }, + "" + ); + if (request.Query is null) throw new Exception("Failed to construct GraphQL query."); + + var responseObject = await apiRequestService.Database().QueryGraphQlFromUrl( + appSettings, + databaseUri, + request, + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + return responseObject; + } } \ No newline at end of file diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index b7e4d1b0..2e22e13d 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.GraphQl.Databases; -using Database.Services; +using Database.Services.Interfaces; using GraphQL; using HotChocolate; using HotChocolate.Resolvers; @@ -107,6 +107,83 @@ public static async Task RequestDatabase( return response.Data.Databases.Edges[0].Node; } + /// + /// Request database from Metabase. + /// + /// Id of database. + /// + /// + /// + /// + /// + /// + /// if successful. + /// Throws exception if errors occur. + public static async Task RequestDatabaseById( + Guid databaseId, + AppSettings appSettings, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken) + { + var response = await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames).ConfigureAwait(false), + new + { + where = new + { + uuid = new + { + equalTo = databaseId.ToString(), + } + } + }, + "Databases" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ).ConfigureAwait(false); + + if (response is null) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NULL_RESPONSE") + .SetPath(resolverContext.Path) + .SetMessage("Response is null.") + .Build() + ); + if (response.Data.Databases.Edges is null) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NULL_EDGES") + .SetPath(resolverContext.Path) + .SetMessage("The supposed list of databases is null.") + .Build() + ); + if (response.Data.Databases.Edges.Count == 0) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("NO_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases is empty.") + .Build() + ); + if (response.Data.Databases.Edges.Count >= 2) + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("AMBIGUOUS_DATABASE") + .SetPath(resolverContext.Path) + .SetMessage("The list of databases has more than one entry.") + .Build() + ); + + return response.Data.Databases.Edges[0].Node; + } + /// /// Update database in Metabase. /// diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs index 279dcc10..b84429d2 100644 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -5,6 +5,22 @@ namespace Database.ApiRequests.Dto; +public record DataXDto( + string Uuid, + DateTime Timestamp, + string ComponentId, + string Name, + string Description, + AppliedMethodDto AppliedMethod, + ResourceTreeDto ResourceTree, + IReadOnlyList Approvals, + DateTime CreatedAt, + string CreatorId, + string Locale, + IReadOnlyList Resources, + IReadOnlyList Warnings +); + public sealed record GeometricDataDto( string Uuid, DateTime Timestamp, diff --git a/backend/src/ApiRequests/JsonSerializerOptions.cs b/backend/src/ApiRequests/JsonSerializerOptions.cs index 5bb1bc71..5592d8d9 100644 --- a/backend/src/ApiRequests/JsonSerializerOptions.cs +++ b/backend/src/ApiRequests/JsonSerializerOptions.cs @@ -44,4 +44,22 @@ public static class JsonSerializerSettings IgnoreReadOnlyFields = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; //.SetupImmutableConverter(); + + /// + /// Settings for file Json requests. + /// + public static readonly JsonSerializerOptions File = + new() + { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + NumberHandling = JsonNumberHandling.Strict, + AllowOutOfOrderMetadataProperties = true, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, + ReadCommentHandling = JsonCommentHandling.Disallow, + IncludeFields = false, + IgnoreReadOnlyProperties = false, + IgnoreReadOnlyFields = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; //.SetupImmutableConverter(); } \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/DataX.graphql b/backend/src/ApiRequests/Queries/DataX.graphql new file mode 100644 index 00000000..2c0a8271 --- /dev/null +++ b/backend/src/ApiRequests/Queries/DataX.graphql @@ -0,0 +1,5 @@ +query Data($uuid: Uuid!) { + data(id: $uuid) { + ...DataFields + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index 90f98754..c426f38f 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests.Dto; -using Database.Services; +using Database.Services.Interfaces; using GraphQL; using Microsoft.AspNetCore.Http; diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 252cd699..27dca75a 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -29,6 +29,7 @@ using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; using Database.GraphQl.Approvals; using Database.GraphQl.AccessRights; +using Database.GraphQl.MethodAsService; namespace Database.Configuration; @@ -151,6 +152,8 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() + .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(Mutation))) .AddType() @@ -179,6 +182,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() + .AddType() // Data Loaders /* .AddDataLoader() */ // Paging diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 5409b56b..60abfc54 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -8,7 +8,7 @@ using Database.Authorization; using Database.Data; using Database.Filters; -using Database.Services; +using Database.Services.Interfaces; using Database.Utilities; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 6c6b7a66..814d545b 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Database.Services; +using Database.Services.Interfaces; namespace Database.Data; diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index d3182274..ae2fe54d 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index 7ccbcb69..3cefcb76 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; namespace Database.GraphQl.Approvals; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 9e00bb89..3a667513 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 9c6ea62a..1062ae48 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs new file mode 100644 index 00000000..39cc8c93 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Services.Interfaces; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl.DataX; + +[ExtendObjectType(nameof(Query))] +public class DataQueries +{ + public async Task GetDataAsync( + Guid id, + [GraphQLType] string? locale, + ApplicationDbContext dbContext, + IDataService dataService, + IAccessRightsService accessRightsService, + CancellationToken cancellationToken + ) + { + // TODO Use `locale`. + var data = await dataService.GetDataAsync(id, dbContext, cancellationToken).ConfigureAwait(false); + + if (data is null) + { + return data; + } + + return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 2adefcd3..c5de13d7 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; using Microsoft.AspNetCore.Http; diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index fbd4051e..6842fddc 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index afd11ecd..1a391fb5 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index d674a924..2b2dd65d 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 828e16ec..c94be37d 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -9,7 +9,7 @@ using Database.Extensions; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.Services; +using Database.Services.Interfaces; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 84761dac..f159deb2 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -6,7 +6,7 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.GeometricDataX; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; using Org.BouncyCastle.Asn1.X509.Qualified; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 0e0e05a9..469d979a 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; diff --git a/backend/src/GraphQl/MethodAsService/FileInputData.cs b/backend/src/GraphQl/MethodAsService/FileInputData.cs new file mode 100644 index 00000000..926542ac --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/FileInputData.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.MethodAsService; + +public sealed record FileInputData( + Data Data +); + +public sealed record Data( + ComponentCharacteristics ComponentCharacteristics, + IReadOnlyList DataPoints +); + +public sealed record ComponentCharacteristics( + string Surface, + string Symmetries +); + +public sealed record DataPoint( + Incidence Incidence, + Emergence Emergence, + Results Results +); + +public sealed record Incidence( + Wavelengths Wavelengths, + Direction Direction +); + +public sealed record Emergence( + Direction Direction +); + +public sealed record Results( + double Transmittance, + double? Reflectance = null, + ColorTransmittance? ColorTransmittance = null, + ColorReflectance? ColorReflectance = null +); + +public sealed record Direction( + int Polar +); + +public sealed record Wavelengths( + int Wavelength +); + +public record ColorTransmittance( + IReadOnlyList CielabColors, + IReadOnlyList ColorRenderingIndices +); + +public record ColorReflectance( + IReadOnlyList CielabColors +); + +public record CielabColor( + int LStar, + double AStar, + double BStar, + string Observer, + string Illuminant +); + +public record ColorRenderingIndex( + int Ra, + string Observer, + string Illuminant +); \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs new file mode 100644 index 00000000..89ba6e49 --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs @@ -0,0 +1,17 @@ +using Database.GraphQl.GeometricDataX; +using System.Collections.Generic; + +namespace Database.GraphQl.MethodAsService; + +public class MethodAsServiceError + : UserErrorBase +{ + public MethodAsServiceError( + MethodAsServiceErrorCode code, + string message, + IReadOnlyList path + ) + : base(code, message, path) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs new file mode 100644 index 00000000..aa1ac41a --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.MethodAsService; + +[SuppressMessage("Naming", "CA1707")] +public enum MethodAsServiceErrorCode +{ + UNKNOWN_METHOD, + UNKNOWN_DATABASE +} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs new file mode 100644 index 00000000..f4e932e4 --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs @@ -0,0 +1,9 @@ +using System; + +namespace Database.GraphQl.MethodAsService; + +public sealed record MethodAsServiceInput( + Guid DataId, + Guid DatabaseId, + Guid MethodId + ); \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs new file mode 100644 index 00000000..b755494e --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.MethodAsService; + +public class MethodAsServicePayload + : Payload +{ + public MethodAsServicePayload( + List calculationResult + ) + { + CalculationResult = calculationResult; + } + + public MethodAsServicePayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + public MethodAsServicePayload( + MethodAsServiceError error + ) + : this(new[] { error }) + { + } + + public MethodAsServicePayload( + List calculationResult, + IReadOnlyCollection errors + ) + { + CalculationResult = calculationResult; + Errors = errors; + } + + public MethodAsServicePayload( + List calculationResult, + MethodAsServiceError error + ) + : this( + calculationResult, + new[] { error } + ) + { + } + + public List? CalculationResult { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs new file mode 100644 index 00000000..43917fc8 --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs @@ -0,0 +1,140 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.ApiRequests.Dto; +using Database.Data; +using Database.GraphQl.Databases; +using Database.Services.Interfaces; +using HotChocolate; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; + +namespace Database.GraphQl.MethodAsService; + +[ExtendObjectType(nameof(Query))] +public class MethodAsServiceQuaries +{ + private sealed record DataXResponse(DataXDto DataX); + + public async Task MethodAsServiceWithFileAsync( + MethodAsServiceWithFileInput input, + ApplicationDbContext context, + IUserService userService, + IMethodCalculationService methodCalculationService, + CancellationToken cancellationToken + ) + { + if (!methodCalculationService.MethodExists(input.MethodId)) + { + return new MethodAsServicePayload( + new MethodAsServiceError( + MethodAsServiceErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [] + ) + ); + } + + await using Stream stream = input.File.OpenReadStream(); + var fileInput = await JsonSerializer.DeserializeAsync( + stream, + JsonSerializerSettings.File, + cancellationToken + ).ConfigureAwait(false); + + if (fileInput is null) throw new JsonException("Failed to deserialize the GraphQL response."); + + var result = methodCalculationService.UseMethodToCalculate(input.MethodId, fileInput.Data.DataPoints.ToList()); + + return new MethodAsServicePayload(result); + } + + public async Task MethodAsServiceAsync( + AppSettings appSettings, + MethodAsServiceInput input, + ApplicationDbContext context, + IUserService userService, + IMethodCalculationService methodCalculationService, + IApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + if (!methodCalculationService.MethodExists(input.MethodId)) + { + return new MethodAsServicePayload( + new MethodAsServiceError( + MethodAsServiceErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [] + ) + ); + } + + DatabaseDto database; + try + { + database = await DatabaseApi.RequestDatabaseById( + input.DatabaseId, + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + resolverContext, + cancellationToken); + } + catch (HttpRequestException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("METABASE_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") + .SetException(e) + .Build() + ); + } + catch (JsonException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("JSON_DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetException(e) + .Build() + ); + } + + if (database is null) + { + return new MethodAsServicePayload( + new MethodAsServiceError( + MethodAsServiceErrorCode.UNKNOWN_DATABASE, + $"The database is unknown.", + [] + ) + ); + } + + var data = await DataApi.GetDataFromDatabase(database.Locator, input.DataId, DataApi.DataXFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); + + var locator = data.Data.DataX.ResourceTree.Root.Value.Locator; + + var filedata = await apiRequestService.QueryRest(new Uri(locator), httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); + + var result = methodCalculationService.UseMethodToCalculate(input.MethodId, filedata.Data.DataPoints.ToList()); + + return new MethodAsServicePayload(result); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs new file mode 100644 index 00000000..238da3c2 --- /dev/null +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs @@ -0,0 +1,10 @@ +using HotChocolate.Types; +using HotChocolate; +using System; + +namespace Database.GraphQl.MethodAsService; + +public sealed record MethodAsServiceWithFileInput( + [GraphQLType(typeof(NonNullType))] IFile File, + Guid MethodId + ); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index c5fea28f..34abde78 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -6,7 +6,7 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.GeometricDataX; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; using Org.BouncyCastle.Asn1.X509.Qualified; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index a5020efa..9221499c 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 548b217d..e5e40433 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -8,7 +8,7 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.GeometricDataX; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate.Types; using Org.BouncyCastle.Asn1.X509.Qualified; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index a9682a98..34d30c89 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 056a024f..5e52a10a 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs index 1e6342d4..72b13cf7 100644 --- a/backend/src/Logging/AccessRightServiceLogging.cs +++ b/backend/src/Logging/AccessRightServiceLogging.cs @@ -1,5 +1,5 @@ using System; -using Database.Services; +using Database.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs index 4f152de8..afcf06c6 100644 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs index cfb81f77..0e11fb72 100644 --- a/backend/src/Logging/SigningServiceLogging.cs +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs index 736c3209..5703d007 100644 --- a/backend/src/Logging/UserServiceLogging.cs +++ b/backend/src/Logging/UserServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services; +using Database.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Database.Logging; diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs new file mode 100644 index 00000000..5a3f0d4f --- /dev/null +++ b/backend/src/Methods/IMethod.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using Database.GraphQl.MethodAsService; + +namespace Database.Methods; + +public interface IMethod +{ + string Name { get; } + Guid Id { get; } + + List Calculate(List dataPoints); +} \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs new file mode 100644 index 00000000..34d73206 --- /dev/null +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Database.GraphQl.MethodAsService; + +namespace Database.Methods; + +public class TransmittanceSumMethod : IMethod +{ + public string Name => "TransmittanceSum"; + + public Guid Id => Guid.Parse("dfa3a7b3-a6da-444c-b43f-5ffd021c4a5c"); + + public List Calculate(List dataPoints) + { + double transmittanceSum = 0; + foreach (var dataPoint in dataPoints) + { + transmittanceSum += dataPoint.Results.Transmittance; + } + + return new List + { + new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)) + }; + } +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index ab6be9f0..da8f4b37 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading.Tasks; using Database.Data; -using Database.Services; +using Database.Services.Interfaces; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 019c9364..90ba1149 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -6,6 +6,7 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; +using Database.Services.Interfaces; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 050e7ad5..5cf879a5 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Extensions; +using Database.Services.Interfaces; using GraphQL; using Microsoft.AspNetCore.Http; @@ -83,6 +84,30 @@ CancellationToken cancellationToken ); } + /// + public Task> QueryGraphQlFromUrl( + AppSettings appSettings, + Uri uri, + GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class + { + return Query>( + HttpMethod.Post, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of + // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(uri, "/graphql/"), + MakeJsonHttpContent(request), + JsonSerializerSettings.GraphQL, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + /// public Task QueryRest( Uri uri, diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 0e7fea30..85c8de77 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,5 +1,6 @@ using System; using Database.ApiRequests.Dto; +using Database.Services.Interfaces; using Microsoft.Extensions.Caching.Memory; namespace Database.Services; diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index c9dc1956..d3b8de3c 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; +using Database.Services.Interfaces; using Microsoft.EntityFrameworkCore; namespace Database.Services; diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 30198d24..467c6320 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Database.Services.Interfaces; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Logging; diff --git a/backend/src/Services/IAccessRightsService.cs b/backend/src/Services/Interfaces/IAccessRightsService.cs similarity index 96% rename from backend/src/Services/IAccessRightsService.cs rename to backend/src/Services/Interfaces/IAccessRightsService.cs index 7a4016e6..2c4e78be 100644 --- a/backend/src/Services/IAccessRightsService.cs +++ b/backend/src/Services/Interfaces/IAccessRightsService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Data; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to check if requested data can be returned regarding access rights. diff --git a/backend/src/Services/IApiRequestService.cs b/backend/src/Services/Interfaces/IApiRequestService.cs similarity index 71% rename from backend/src/Services/IApiRequestService.cs rename to backend/src/Services/Interfaces/IApiRequestService.cs index 051bec38..d85aab84 100644 --- a/backend/src/Services/IApiRequestService.cs +++ b/backend/src/Services/Interfaces/IApiRequestService.cs @@ -5,7 +5,7 @@ using GraphQL; using Microsoft.AspNetCore.Http; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to request Metabase or Database Api. GraphQL and REST. @@ -50,6 +50,27 @@ CancellationToken cancellationToken ) where TGraphQlResponse : class; + /// + /// Send GraphQL request to API and return response. + /// + /// Expected response type. + /// + /// Uri of GraphQL Api. + /// + /// + /// + /// + /// of expected type. + public Task> QueryGraphQlFromUrl( + AppSettings appSettings, + Uri uri, + GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TGraphQlResponse : class; + /// /// Send REST request to API and return response. /// diff --git a/backend/src/Services/ICacheService.cs b/backend/src/Services/Interfaces/ICacheService.cs similarity index 93% rename from backend/src/Services/ICacheService.cs rename to backend/src/Services/Interfaces/ICacheService.cs index 209832e1..b45a9bbe 100644 --- a/backend/src/Services/ICacheService.cs +++ b/backend/src/Services/Interfaces/ICacheService.cs @@ -1,7 +1,7 @@ using System; using Database.ApiRequests.Dto; -namespace Database.Services; +namespace Database.Services.Interfaces; public interface ICacheService { diff --git a/backend/src/Services/IDataService.cs b/backend/src/Services/Interfaces/IDataService.cs similarity index 94% rename from backend/src/Services/IDataService.cs rename to backend/src/Services/Interfaces/IDataService.cs index e32da2b3..561b5d8f 100644 --- a/backend/src/Services/IDataService.cs +++ b/backend/src/Services/Interfaces/IDataService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Data; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to get data from diff --git a/backend/src/Services/IEmailSender.cs b/backend/src/Services/Interfaces/IEmailSender.cs similarity index 68% rename from backend/src/Services/IEmailSender.cs rename to backend/src/Services/Interfaces/IEmailSender.cs index 1c7c7130..d0c480d6 100644 --- a/backend/src/Services/IEmailSender.cs +++ b/backend/src/Services/Interfaces/IEmailSender.cs @@ -1,6 +1,6 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; -namespace Database.Services; +namespace Database.Services.Interfaces; public interface IEmailSender { diff --git a/backend/src/Services/Interfaces/IMethodCalculationService.cs b/backend/src/Services/Interfaces/IMethodCalculationService.cs new file mode 100644 index 00000000..92457d58 --- /dev/null +++ b/backend/src/Services/Interfaces/IMethodCalculationService.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Database.GraphQl.MethodAsService; +using Database.Methods; + +namespace Database.Services.Interfaces; + +public interface IMethodCalculationService +{ + void AddMethod(IMethod method); + + bool MethodExists(Guid methodId); + + List UseMethodToCalculate(Guid methodId, List dataPoints); +} \ No newline at end of file diff --git a/backend/src/Services/IResponseApprovalService.cs b/backend/src/Services/Interfaces/IResponseApprovalService.cs similarity index 94% rename from backend/src/Services/IResponseApprovalService.cs rename to backend/src/Services/Interfaces/IResponseApprovalService.cs index 9054c65b..27293d48 100644 --- a/backend/src/Services/IResponseApprovalService.cs +++ b/backend/src/Services/Interfaces/IResponseApprovalService.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Data; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to create response approval diff --git a/backend/src/Services/ISigningService.cs b/backend/src/Services/Interfaces/ISigningService.cs similarity index 96% rename from backend/src/Services/ISigningService.cs rename to backend/src/Services/Interfaces/ISigningService.cs index 0e9557df..bce4c5ab 100644 --- a/backend/src/Services/ISigningService.cs +++ b/backend/src/Services/Interfaces/ISigningService.cs @@ -1,7 +1,7 @@ -using System.Threading.Tasks; using System; +using System.Threading.Tasks; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to sign data string with GNUPG. diff --git a/backend/src/Services/IUserService.cs b/backend/src/Services/Interfaces/IUserService.cs similarity index 94% rename from backend/src/Services/IUserService.cs rename to backend/src/Services/Interfaces/IUserService.cs index eca84ae9..9d556cda 100644 --- a/backend/src/Services/IUserService.cs +++ b/backend/src/Services/Interfaces/IUserService.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using Database.ApiRequests.Dto; -namespace Database.Services; +namespace Database.Services.Interfaces; /// /// Service to get current user from Metabase diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs new file mode 100644 index 00000000..999f503a --- /dev/null +++ b/backend/src/Services/MethodCalculationService.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Database.GraphQl.MethodAsService; +using Database.Methods; +using Database.Services.Interfaces; + +namespace Database.Services; + +public class MethodCalculationService : IMethodCalculationService +{ + private List _methods = new List(); + + public void AddMethod(IMethod method) + { + _methods.Add(method); + } + + public bool MethodExists(Guid methodId) + { + return _methods.Exists(x => x.Id == methodId); + } + + public List UseMethodToCalculate(Guid methodId, List dataPoints) + { + var method = _methods.First(x => x.Id == methodId); + + return method.Calculate(dataPoints); + } +} \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 220e4eff..7d8176ac 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -6,6 +6,7 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; +using Database.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 04beb5df..fa7664f4 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -3,6 +3,7 @@ using System.IO; using System.Threading.Tasks; using Database.Logging; +using Database.Services.Interfaces; using Microsoft.Extensions.Logging; namespace Database.Services; diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 7cb6b7de..1a6148c4 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -5,6 +5,7 @@ using Database.ApiRequests.Dto; using Database.Extensions; using Database.Logging; +using Database.Services.Interfaces; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index cec69aab..8163bfbf 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -9,6 +9,7 @@ using Database.Data.Extensions; using Database.Enumerations; using Database.Services; +using Database.Services.Interfaces; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; From 1425f732a9c96a741722a0e31dfdc51267f2d884 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 May 2025 19:05:12 +0200 Subject: [PATCH 320/878] Allow representatives of an institution to manage data sets and files of institutions managed by the institution the user represents --- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 29 ++++++++++++++----- .../ApiRequests/Queries/CurrentUser.graphql | 8 +++++ .../CalorimetricDataAuthorization.cs | 2 +- .../src/Authorization/CommonAuthorization.cs | 21 ++++++++++++-- .../Authorization/FileUploadAuthorization.cs | 2 +- .../GeometricDataAuthorization.cs | 2 +- .../GetHttpsResourceAuthorization.cs | 2 +- .../HygrothermalDataAuthorization.cs | 2 +- .../Authorization/OpticalDataAuthorization.cs | 2 +- .../PhotovoltaicDataAuthorization.cs | 2 +- .../AccessRights/DataAccessRightsMutations.cs | 6 ++-- 11 files changed, 57 insertions(+), 21 deletions(-) diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index 25c95c96..26e568ee 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -20,21 +20,34 @@ public enum DataSigningPermission public sealed record CurrentUserDto( Guid Id, Guid Uuid, - RepresentedInstitutionsDto RepresentedInstitutions + UserRepresentedInstitutionConnection RepresentedInstitutions ); -public sealed record RepresentedInstitutionsEdge( - NodeDto Node, +public sealed record UserRepresentedInstitutionConnection( + IReadOnlyList Edges +); + +public sealed record UserRepresentedInstitutionEdge( + UserRepresentedInstitutionNode Node, InstitutionRepresentativeRole Role, DataSigningPermission DataSigningPermission ); -public sealed record NodeDto( - Guid Id, +public sealed record UserRepresentedInstitutionNode( Guid Uuid, - string Name + string Name, + InstitutionManagedInstitutionConnection ManagedInstitutions +); + +public sealed record InstitutionManagedInstitutionConnection( + IReadOnlyList Edges +); + +public sealed record InstitutionManagedInstitutionEdge( + InstitutionManagedInstitutionNode Node ); -public sealed record RepresentedInstitutionsDto( - IReadOnlyList Edges +public sealed record InstitutionManagedInstitutionNode( + Guid Uuid, + string Name ); \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/CurrentUser.graphql b/backend/src/ApiRequests/Queries/CurrentUser.graphql index 338de444..a84f4f35 100644 --- a/backend/src/ApiRequests/Queries/CurrentUser.graphql +++ b/backend/src/ApiRequests/Queries/CurrentUser.graphql @@ -6,6 +6,14 @@ query CurrentUser { node { uuid name + managedInstitutions { + edges { + node { + uuid + name + } + } + } } role dataSigningPermission diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index a3102291..69c9bb66 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index bed95815..0100c81f 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -7,13 +7,25 @@ namespace Database.Authorization; public static class CommonAuthorization { - public static bool IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + public static bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( CurrentUserDto currentUser, Guid institutionId, CancellationToken cancellationToken ) { - return currentUser.RepresentedInstitutions.Edges.Any(t => t.Node.Uuid == institutionId && (t.Role == InstitutionRepresentativeRole.ASSISTANT || t.Role == InstitutionRepresentativeRole.OWNER)); + return currentUser.RepresentedInstitutions.Edges.Any(t => + ( + t.Role is InstitutionRepresentativeRole.ASSISTANT + || t.Role is InstitutionRepresentativeRole.OWNER + ) + && + ( + t.Node.Uuid == institutionId + || t.Node.ManagedInstitutions.Edges.Any(t => + t.Node.Uuid == institutionId + ) + ) + ); } public static bool IsAuthorizedToAddApprovalForInstitution( @@ -22,6 +34,9 @@ public static bool IsAuthorizedToAddApprovalForInstitution( CancellationToken cancellationToken ) { - return currentUser.RepresentedInstitutions.Edges.Any(t => t.Node.Uuid == institutionId && t.DataSigningPermission == DataSigningPermission.GRANTED); + return currentUser.RepresentedInstitutions.Edges.Any( + t => t.Node.Uuid == institutionId + && t.DataSigningPermission == DataSigningPermission.GRANTED + ); } } \ No newline at end of file diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index 2dc68837..ae7ebd60 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToUploadFilesForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index 3f6d31c8..fc818f04 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreateGeometricDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index 5126276a..540f7682 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index 982ec75b..a4338751 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index 5152fba8..e5d4b008 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreateOpticalDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index 546899d7..ec8f2a48 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -12,6 +12,6 @@ public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( CancellationToken cancellationToken ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index ae2fe54d..8226bdb5 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -47,7 +47,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, data.CreatorId, cancellationToken @@ -95,7 +95,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, input.InstitutionId, cancellationToken @@ -158,7 +158,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantOfVerifiedInstitution( + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, input.InstitutionId, cancellationToken From 3d48ba78599fe3cb482af762bc2aa84d4a95a5e2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 10:42:27 +0200 Subject: [PATCH 321/878] Bind runtime type `uint` to `NonNegativeIntType` in HotChocolate --- backend/src/Configuration/GraphQlConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 27dca75a..f91a6134 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -74,6 +74,7 @@ AppSettings appSettings // GraphQL Server services .AddGraphQLServer() + .BindRuntimeType() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContextFactory() .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) From 586c4f1ab708dc19e0fbd7dcd295444ced8d845a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 10:48:09 +0200 Subject: [PATCH 322/878] Register access-rights service as scoped service --- backend/src/Startup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8163bfbf..ef275aa2 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -68,7 +68,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - //services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) From a59bbacb7176f06bd1b703ee2c9ee4971727aab8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:31:26 +0200 Subject: [PATCH 323/878] Represent access rights more faithfully by using unsigned integers and read-only collections (and make HotChocolate not choke on the access rights) --- backend/src/Data/DataAccessRights.cs | 11 +++++---- backend/src/Data/DataX.cs | 19 +++++++++------ backend/src/Data/IData.cs | 4 ++-- .../AccessRights/DataAccessRightsInput.cs | 6 +++-- .../AccessRights/DataAccessRightsMutations.cs | 16 +++---------- backend/src/Services/AccessRightsService.cs | 4 ++-- backend/src/Services/CacheService.cs | 24 +++++++++---------- .../src/Services/Interfaces/ICacheService.cs | 10 ++++---- 8 files changed, 45 insertions(+), 49 deletions(-) diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 8e62dbe7..1db3bec1 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -11,20 +11,21 @@ namespace Database.Data; public sealed class DataAccessRights { /// - /// Dictionary to store allowed user with the amount of a allowed dataset calls - /// -1 meens unlimited access. + /// Dictionary to store allowed user with the amount of a allowed dataset calls. + /// If the user ID is mapped to `null`, then this means that the user has unlimited + /// access. /// - public IDictionary? AllowedUserAndQuantity { get; set; } + public IReadOnlyDictionary? AllowedUserAndQuantity { get; set; } /// /// Allowed Institutions /// - public ICollection? AllowedInstitutions { get; set; } + public IReadOnlyList? AllowedInstitutions { get; set; } /// /// Allowed applications /// - public ICollection? AllowedApplications { get; set; } + public IReadOnlyList? AllowedApplications { get; set; } /// /// Is data access ristricted by data access rights. diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index fe1974f3..3631c695 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -71,24 +71,29 @@ DateTime createdAt /// public bool IsRestrictedByApplication(string applicationId) { - return DataAccessRights.AllowedApplications is not null && DataAccessRights.AllowedApplications.Contains(applicationId); + return DataAccessRights.AllowedApplications is not null + && DataAccessRights.AllowedApplications.Contains(applicationId); } /// - public bool IsRestrictedByInstitutions(List institutions) + public bool IsRestrictedByInstitutions(IEnumerable institutions) { - return DataAccessRights.AllowedInstitutions is not null && DataAccessRights.AllowedInstitutions!.Any(a => institutions.Any(b => a.Equals(b))); + return DataAccessRights.AllowedInstitutions is not null + && DataAccessRights.AllowedInstitutions.Any(a => + institutions.Any(b => + a.Equals(b) + ) + ); } /// - public bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount) + public bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount) { if (DataAccessRights.AllowedUserAndQuantity is not null) { - int limit; - if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out limit)) + if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out var limit)) { - if (limit < 0 || limit > alreadyAccesedCount) + if (limit is null || limit > alreadyAccesedCount) { return false; } diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index a6239da2..23db9341 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -30,7 +30,7 @@ public interface IData : IEntity /// /// List of institution ids. /// True, if dataset is rescricted. Otherwise false. - bool IsRestrictedByInstitutions(List institutions); + bool IsRestrictedByInstitutions(IEnumerable institutions); /// /// Check if dataset is restricted for passed user. @@ -38,5 +38,5 @@ public interface IData : IEntity /// Id of user. /// Count of already accessed datasets by user. /// True, if dataset is rescricted. Otherwise false. - bool IsRestrictedByUser(Guid uuid, int alreadyAccesedCount); + bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount); } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs index 4703ec9f..6d49cb74 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs @@ -1,10 +1,12 @@ using System; -using Database.Data; +using System.Collections.Generic; namespace Database.GraphQl.AccessRights; public sealed record DataAccessRightsInput ( Guid DataId, - DataAccessRights DataAccessRights + IReadOnlyDictionary? AllowedUserAndQuantity, + IReadOnlyList? AllowedInstitutions, + IReadOnlyList? AllowedApplications ); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 8226bdb5..d18b4e08 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -1,12 +1,10 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.Services.Interfaces; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessRights; @@ -63,9 +61,9 @@ CancellationToken cancellationToken ); } - data.DataAccessRights.AllowedInstitutions = input.DataAccessRights.AllowedInstitutions; - data.DataAccessRights.AllowedApplications = input.DataAccessRights.AllowedApplications; - data.DataAccessRights.AllowedUserAndQuantity = input.DataAccessRights.AllowedUserAndQuantity; + data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; + data.DataAccessRights.AllowedApplications = input.AllowedApplications; + data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new UpdateDataAccessRightsPayload(data.DataAccessRights); @@ -74,11 +72,7 @@ CancellationToken cancellationToken public async Task AddAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IUserService userService, - IDataService dataService, CancellationToken cancellationToken ) { @@ -137,11 +131,7 @@ CancellationToken cancellationToken public async Task UpdateAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, - AppSettings appSettings, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IUserService userService, - IDataService dataService, CancellationToken cancellationToken ) { diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 90ba1149..9e8383e9 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -69,13 +69,13 @@ private IEnumerable ProcessData( string applicationId, List institutions, CurrentUserDto currentUser, - int alreadyAccessedCount, + uint alreadyAccessedCount, ICacheService cacheService, List accessRights) where T : IData { string reason = ""; - foreach (T dataItem in data) + foreach (var dataItem in data) { if (dataItem.DataAccessRights.HasRistrictions) { diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 85c8de77..94d62978 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -5,7 +5,7 @@ namespace Database.Services; -public class CacheService( +public sealed class CacheService( IMemoryCache currentUserCache, IMemoryCache accessCountCache, IMemoryCache timePeriodCountCache) : ICacheService @@ -22,40 +22,38 @@ public bool TryGetUser(string token, out CurrentUserDto? cacheUser) return currentUserCache.TryGetValue(token, out cacheUser); } - public int GetAccessCountForUser(Guid userId) + public uint GetAccessCountForUser(Guid userId) { - int count; - if (!accessCountCache.TryGetValue(userId, out count)) + if (!accessCountCache.TryGetValue(userId, out uint count)) { - return accessCountCache.Set(userId, 0); + return accessCountCache.Set(userId, 0); } return count; } - public int SetAccessCountForUser(Guid userId, int count) + public uint SetAccessCountForUser(Guid userId, uint count) { return accessCountCache.Set(userId, count); } - public (DateTime StartTime, int Count) GetOrCreateAccessCountForPeriod(Guid institutionId) + public (DateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId) { - (DateTime StartTime, int Count) accessesPerPeriod; - if (!timePeriodCountCache.TryGetValue(institutionId, out accessesPerPeriod)) + if (!timePeriodCountCache.TryGetValue(institutionId, out (DateTime StartTime, uint Count) accessesPerPeriod)) { - return timePeriodCountCache.Set(institutionId, (DateTime.Now, 0)); + return timePeriodCountCache.Set(institutionId, (DateTime.Now, (uint)0)); } return accessesPerPeriod; } - public (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId) + public (DateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId) { var accessesPerPeriod = GetOrCreateAccessCountForPeriod(institutionId); accessesPerPeriod.Count++; return timePeriodCountCache.Set(institutionId, accessesPerPeriod); } - public (DateTime StartTime, int Count) SetNewTimePeriod(Guid institutionId) + public (DateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId) { - return timePeriodCountCache.Set(institutionId, (DateTime.Now, 0)); + return timePeriodCountCache.Set(institutionId, (DateTime.Now, (uint)0)); } } \ No newline at end of file diff --git a/backend/src/Services/Interfaces/ICacheService.cs b/backend/src/Services/Interfaces/ICacheService.cs index b45a9bbe..34a6bb97 100644 --- a/backend/src/Services/Interfaces/ICacheService.cs +++ b/backend/src/Services/Interfaces/ICacheService.cs @@ -9,13 +9,13 @@ public interface ICacheService CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser); - int GetAccessCountForUser(Guid userId); + uint GetAccessCountForUser(Guid userId); - int SetAccessCountForUser(Guid userId, int count); + uint SetAccessCountForUser(Guid userId, uint count); - (DateTime StartTime, int Count) GetOrCreateAccessCountForPeriod(Guid institutionId); + (DateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId); - (DateTime StartTime, int Count) AddAccessCountToPeriod(Guid institutionId); + (DateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId); - (DateTime StartTime, int Count) SetNewTimePeriod(Guid institutionId); + (DateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId); } \ No newline at end of file From f95798cae4413fadbece8b2500d18267ed5c6165 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:42:30 +0200 Subject: [PATCH 324/878] Register method calculation service so that it can be resolved by HotChocolate --- backend/src/Startup.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index ef275aa2..7f764b82 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -64,11 +64,12 @@ public void ConfigureServices(IServiceCollection services) // services.AddDatabaseDeveloperPageExceptionFilter(); services.AddSingleton(); services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) From 851eb14556d3cc2c40786306e74b6f73e16d39f5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:43:08 +0200 Subject: [PATCH 325/878] Use read-only collections where possible and fix spelling mistake --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- .../GraphQl/MethodAsService/MethodAsServicePayload.cs | 6 +++--- ...odAsServiceQuaries.cs => MethodAsServiceQueries.cs} | 10 +++------- backend/src/Methods/IMethod.cs | 2 +- backend/src/Methods/TransmittanceSumMethod.cs | 2 +- .../Services/Interfaces/IMethodCalculationService.cs | 2 +- backend/src/Services/MethodCalculationService.cs | 2 +- 7 files changed, 11 insertions(+), 15 deletions(-) rename backend/src/GraphQl/MethodAsService/{MethodAsServiceQuaries.cs => MethodAsServiceQueries.cs} (94%) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index f91a6134..b52aee15 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -153,7 +153,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() - .AddType() + .AddType() .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(Mutation))) diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs index b755494e..83802525 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs @@ -6,7 +6,7 @@ public class MethodAsServicePayload : Payload { public MethodAsServicePayload( - List calculationResult + IReadOnlyList calculationResult ) { CalculationResult = calculationResult; @@ -27,7 +27,7 @@ MethodAsServiceError error } public MethodAsServicePayload( - List calculationResult, + IReadOnlyList calculationResult, IReadOnlyCollection errors ) { @@ -46,6 +46,6 @@ MethodAsServiceError error { } - public List? CalculationResult { get; } + public IReadOnlyList? CalculationResult { get; } public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs similarity index 94% rename from backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs rename to backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 43917fc8..a4766d8b 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQuaries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -18,14 +18,12 @@ namespace Database.GraphQl.MethodAsService; [ExtendObjectType(nameof(Query))] -public class MethodAsServiceQuaries +public class MethodAsServiceQueries { private sealed record DataXResponse(DataXDto DataX); public async Task MethodAsServiceWithFileAsync( MethodAsServiceWithFileInput input, - ApplicationDbContext context, - IUserService userService, IMethodCalculationService methodCalculationService, CancellationToken cancellationToken ) @@ -46,11 +44,9 @@ CancellationToken cancellationToken stream, JsonSerializerSettings.File, cancellationToken - ).ConfigureAwait(false); - - if (fileInput is null) throw new JsonException("Failed to deserialize the GraphQL response."); + ).ConfigureAwait(false) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - var result = methodCalculationService.UseMethodToCalculate(input.MethodId, fileInput.Data.DataPoints.ToList()); + var result = methodCalculationService.UseMethodToCalculate(input.MethodId, fileInput.Data.DataPoints); return new MethodAsServicePayload(result); } diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 5a3f0d4f..8b4781e8 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -9,5 +9,5 @@ public interface IMethod string Name { get; } Guid Id { get; } - List Calculate(List dataPoints); + List Calculate(IReadOnlyList dataPoints); } \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 34d73206..adc6f745 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -10,7 +10,7 @@ public class TransmittanceSumMethod : IMethod public Guid Id => Guid.Parse("dfa3a7b3-a6da-444c-b43f-5ffd021c4a5c"); - public List Calculate(List dataPoints) + public List Calculate(IReadOnlyList dataPoints) { double transmittanceSum = 0; foreach (var dataPoint in dataPoints) diff --git a/backend/src/Services/Interfaces/IMethodCalculationService.cs b/backend/src/Services/Interfaces/IMethodCalculationService.cs index 92457d58..bb6f06bc 100644 --- a/backend/src/Services/Interfaces/IMethodCalculationService.cs +++ b/backend/src/Services/Interfaces/IMethodCalculationService.cs @@ -11,5 +11,5 @@ public interface IMethodCalculationService bool MethodExists(Guid methodId); - List UseMethodToCalculate(Guid methodId, List dataPoints); + List UseMethodToCalculate(Guid methodId, IReadOnlyList dataPoints); } \ No newline at end of file diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs index 999f503a..7a43067c 100644 --- a/backend/src/Services/MethodCalculationService.cs +++ b/backend/src/Services/MethodCalculationService.cs @@ -21,7 +21,7 @@ public bool MethodExists(Guid methodId) return _methods.Exists(x => x.Id == methodId); } - public List UseMethodToCalculate(Guid methodId, List dataPoints) + public List UseMethodToCalculate(Guid methodId, IReadOnlyList dataPoints) { var method = _methods.First(x => x.Id == methodId); From 4335c152c8e5eaeb1f4a1ca358c3203608189e15 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:48:01 +0200 Subject: [PATCH 326/878] Rename `CielabColor` type used by method as a service to avoid it clashing with `CielabColor` of `Data` --- backend/src/GraphQl/MethodAsService/FileInputData.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/GraphQl/MethodAsService/FileInputData.cs b/backend/src/GraphQl/MethodAsService/FileInputData.cs index 926542ac..bc3746a5 100644 --- a/backend/src/GraphQl/MethodAsService/FileInputData.cs +++ b/backend/src/GraphQl/MethodAsService/FileInputData.cs @@ -46,16 +46,16 @@ public sealed record Wavelengths( int Wavelength ); -public record ColorTransmittance( - IReadOnlyList CielabColors, +public sealed record ColorTransmittance( + IReadOnlyList CielabColors, IReadOnlyList ColorRenderingIndices ); -public record ColorReflectance( - IReadOnlyList CielabColors +public sealed record ColorReflectance( + IReadOnlyList CielabColors ); -public record CielabColor( +public sealed record MethodAsServiceCielabColor( int LStar, double AStar, double BStar, @@ -63,7 +63,7 @@ public record CielabColor( string Illuminant ); -public record ColorRenderingIndex( +public sealed record ColorRenderingIndex( int Ra, string Observer, string Illuminant From f3a2f398bfd24a294a3667ab63155005d5a9d494 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:50:54 +0200 Subject: [PATCH 327/878] Correct spelling of `restrictions` --- backend/src/Data/DataAccessRights.cs | 2 +- .../src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs | 2 +- backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 2 +- .../src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 2 +- .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 2 +- backend/src/Services/AccessRightsService.cs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 1db3bec1..028cf1e8 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -30,7 +30,7 @@ public sealed class DataAccessRights /// /// Is data access ristricted by data access rights. /// - public bool HasRistrictions + public bool HasRestrictions { get { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 1062ae48..15fb1ed6 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) + if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { return filteredData; } diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 2b2dd65d..91ee7d19 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -34,7 +34,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) + if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { return filteredData; } diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 469d979a..9f103c90 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) + if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { return filteredData; } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 9221499c..60543c06 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) + if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { return filteredData; } diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 34d30c89..5fe59e4e 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken IQueryable filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRistrictions)) + if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { return filteredData; } diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 9e8383e9..369fe29a 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -77,7 +77,7 @@ private IEnumerable ProcessData( string reason = ""; foreach (var dataItem in data) { - if (dataItem.DataAccessRights.HasRistrictions) + if (dataItem.DataAccessRights.HasRestrictions) { if (dataItem.IsRestrictedByApplication(applicationId)) { From 0f20b4138ae415294e2f57339832a4bbb6540e74 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:52:38 +0200 Subject: [PATCH 328/878] Use `var` instead of explicit type wherever possible --- backend/src/Data/InstitutionAccessRights.cs | 2 +- .../CalorimetricDataX/CalorimetricDataQueries.cs | 2 +- .../src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 2 +- .../HygrothermalDataX/HygrothermalDataQueries.cs | 2 +- .../GraphQl/MethodAsService/MethodAsServiceQueries.cs | 2 +- backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 2 +- .../PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 2 +- backend/src/Services/AccessRightsService.cs | 10 +++++----- backend/src/Services/SigningService.cs | 10 +++++----- backend/src/Services/UserService.cs | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 814d545b..a30dfb12 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -38,7 +38,7 @@ TimeSpan period /// internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out string reason) { - bool isRestricted = false; + var isRestricted = false; reason = ""; // Check restriction for time period diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 15fb1ed6..726c44a1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken { // TODO Use `locale`. sorting.StabilizeOrder(); - IQueryable filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); + var filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 91ee7d19..ded96460 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -31,7 +31,7 @@ CancellationToken cancellationToken { // TODO Use `locale`. sorting.StabilizeOrder(); - IQueryable filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); + var filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 9f103c90..7f04ed3e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken { // TODO Use `locale`. sorting.StabilizeOrder(); - IQueryable filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); + var filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index a4766d8b..d8c1ee45 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -39,7 +39,7 @@ CancellationToken cancellationToken ); } - await using Stream stream = input.File.OpenReadStream(); + await using var stream = input.File.OpenReadStream(); var fileInput = await JsonSerializer.DeserializeAsync( stream, JsonSerializerSettings.File, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 60543c06..a52da9c9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken { // TODO Use `locale`. sorting.StabilizeOrder(); - IQueryable filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); + var filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 5fe59e4e..e245a483 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken { // TODO Use `locale`. sorting.StabilizeOrder(); - IQueryable filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); + var filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 369fe29a..157a3444 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -29,10 +29,10 @@ public class AccessRightsService( public async Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData { - List filteredData = new List(); - List restrictions = new List(); - List institutions = new List(); - List accessRights = new List(); + var filteredData = new List(); + var restrictions = new List(); + var institutions = new List(); + var accessRights = new List(); var applicationId = userService.GetApplicationIdFromUser() ?? throw new InvalidOperationException("Application Id could not be aquired."); var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException("Could not get current user!"); @@ -74,7 +74,7 @@ private IEnumerable ProcessData( List accessRights) where T : IData { - string reason = ""; + var reason = ""; foreach (var dataItem in data) { if (dataItem.DataAccessRights.HasRestrictions) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index fa7664f4..38fd46ac 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -88,8 +88,8 @@ public async Task Initialize() var proc = Process.Start(startInfo); ArgumentNullException.ThrowIfNull(proc); - string output = proc.StandardOutput.ReadToEnd(); - string error = proc.StandardError.ReadToEnd(); + var output = proc.StandardOutput.ReadToEnd(); + var error = proc.StandardError.ReadToEnd(); await proc.WaitForExitAsync(); if (string.IsNullOrEmpty(output)) { @@ -106,7 +106,7 @@ public async Task Initialize() private async Task GetFingerprintFromKeyList() { - string fingerprint = ""; + var fingerprint = ""; // Execute command to list keys with fingerprint, use 'with-colon' to split later var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); @@ -122,7 +122,7 @@ private bool WriteDataToFile(string data) { try { - using (StreamWriter outputFile = new StreamWriter(FILENAME)) + using (var outputFile = new StreamWriter(FILENAME)) { outputFile.WriteLine(data); } @@ -140,7 +140,7 @@ private bool WriteDataToFile(string data) { try { - using (StreamReader file = new StreamReader(FILENAME + FILE_EXTENSION)) + using (var file = new StreamReader(FILENAME + FILE_EXTENSION)) { return (true, file.ReadToEnd()); } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 1a6148c4..37cbfdd2 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -48,7 +48,7 @@ public class UserService( } // Check if there is already a user for token - if (!cacheService.TryGetUser(token, out CurrentUserDto? cacheUser)) + if (!cacheService.TryGetUser(token, out var cacheUser)) { // Get user from Metabase cacheUser = await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); From 03b0064337e9d5a0d1e080f2a5b53b153f3feca2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 11:53:00 +0200 Subject: [PATCH 329/878] Fix spelling of `restricted` --- backend/src/Data/DataAccessRights.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 028cf1e8..d44b9b8d 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -28,7 +28,7 @@ public sealed class DataAccessRights public IReadOnlyList? AllowedApplications { get; set; } /// - /// Is data access ristricted by data access rights. + /// Is data access restricted by data access rights. /// public bool HasRestrictions { @@ -41,7 +41,7 @@ AllowedInstitutions is not null || } /// - /// Is data access ristricted by user. + /// Is data access restricted by user. /// public bool IsRestrictedByUser { @@ -52,7 +52,7 @@ public bool IsRestrictedByUser } /// - /// Is data access ristricted by institution. + /// Is data access restricted by institution. /// public bool IsRestrictedByInstitution { @@ -63,7 +63,7 @@ public bool IsRestrictedByInstitution } /// - /// Is data access ristricted by application. + /// Is data access restricted by application. /// public bool IsRestrictedByApplication { From 33da1733070f39e00b5d3ef111cfaa5388a0261c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 15:31:02 +0200 Subject: [PATCH 330/878] Mark not-mapped properties explicitly (even though leaving away the setter is sufficient) --- backend/src/Data/DataAccessRights.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index d44b9b8d..bc835331 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -30,6 +31,7 @@ public sealed class DataAccessRights /// /// Is data access restricted by data access rights. /// + [NotMapped] public bool HasRestrictions { get @@ -43,6 +45,7 @@ AllowedInstitutions is not null || /// /// Is data access restricted by user. /// + [NotMapped] public bool IsRestrictedByUser { get @@ -54,6 +57,7 @@ public bool IsRestrictedByUser /// /// Is data access restricted by institution. /// + [NotMapped] public bool IsRestrictedByInstitution { get @@ -65,6 +69,7 @@ public bool IsRestrictedByInstitution /// /// Is data access restricted by application. /// + [NotMapped] public bool IsRestrictedByApplication { get From fb420802745d405d8ee191982ac7c1b782c8a9de Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 15:35:16 +0200 Subject: [PATCH 331/878] Use collection expressions wherever possible --- backend/src/ApiRequests/DataApi.cs | 24 +++++++++---------- backend/src/ApiRequests/DatabaseApi.cs | 8 +++---- backend/src/ApiRequests/UserApi.cs | 4 ++-- .../src/Controllers/FileUploadController.cs | 2 +- .../AccessRights/DataAccessRightsPayload.cs | 4 ++-- .../InstitutionAccessRightsPayload.cs | 4 ++-- .../src/GraphQl/Approvals/ApprovalPayload.cs | 4 ++-- .../CalorimetricDataPayload.cs | 4 ++-- .../GraphQl/Databases/DatabaseMutations.cs | 5 ++-- .../GeometricDataX/GeometricDataPayload.cs | 4 ++-- .../GetHttpsResourcePayload.cs | 4 ++-- .../HygrothermalDataPayload.cs | 4 ++-- .../MethodAsService/MethodAsServicePayload.cs | 4 ++-- .../OpticalDataX/OpticalDataPayload.cs | 4 ++-- .../PhotovoltaicDataPayload.cs | 4 ++-- .../Migrations/20230420143838_OpenIddict.cs | 2 +- 16 files changed, 42 insertions(+), 43 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index a155018d..30315ef0 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -15,45 +15,45 @@ namespace Database.ApiRequests; public class DataApi { public static readonly string[] CalorimetricDataFileNames = - { + [ "DataFields.grapql", "CalorimetricDataFields.graphql", "CalorimetricData.graphql" - }; + ]; public static readonly string[] GeometricDataFileNames = - { + [ "DataFields.graphql", "GeometricDataFields.graphql", "GeometricData.graphql" - }; + ]; public static readonly string[] HygrothermalDataFileNames = - { + [ "DataFields.graphql", "HygrothermalDataFields.graphql", "HygrothermalData.graphql" - }; + ]; public static readonly string[] PhotovoltaicDataFileNames = - { + [ "DataFields.graphql", "PhotovoltaicDataFields.graphql", "PhotovoltaicData.graphql" - }; + ]; public static readonly string[] OpticalDataFileNames = - { + [ "DataFields.graphql", "OpticalDataFields.graphql", "OpticalData.graphql" - }; + ]; public static readonly string[] DataXFileNames = - { + [ "DataFields.graphql", "DataX.graphql" - }; + ]; /// /// Create query to request data and get response. diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 2e22e13d..4e34d101 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -17,14 +17,14 @@ namespace Database.ApiRequests; public class DatabaseApi { private static readonly string[] _databaseFileNames = - { + [ "Databases.graphql" - }; + ]; private static readonly string[] _updateDatabaseFileNames = - { + [ "UpdateDatabase.graphql" - }; + ]; private sealed record DatabasesData(DatabasesConnectionDto Databases); diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index c426f38f..11aad111 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -15,9 +15,9 @@ namespace Database.ApiRequests; public class UserApi { private static readonly string[] _currentUserFileNames = - { + [ "CurrentUser.graphql" - }; + ]; private sealed record CurrentUserData(CurrentUserDto CurrentUser); diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 60abfc54..2193e91c 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -50,7 +50,7 @@ private const long private readonly ApplicationDbContext _context; private readonly string[] _permittedExtensions = - { ".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png" }; + [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; public FileUploadController( ILogger logger, diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs index b6a5a7f7..0034b3ed 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected DataAccessRightsPayload( TDataAccessRightsError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TDataAccessRightsError error ) : this( dataAccessRights, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs index d5a14f81..a9de4969 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs @@ -23,7 +23,7 @@ IReadOnlyCollection errors protected InstitutionAccessRightsPayload( TInstitutionAccessRightsError error ) - : this(new[] { error }) + : this([error]) { } @@ -42,7 +42,7 @@ TInstitutionAccessRightsError error ) : this( accessRights, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/Approvals/ApprovalPayload.cs b/backend/src/GraphQl/Approvals/ApprovalPayload.cs index 010bd42c..276d13b1 100644 --- a/backend/src/GraphQl/Approvals/ApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/ApprovalPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected ApprovalPayload( TApprovalError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TApprovalError error ) : this( dataApproval, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs index 578963fa..e9636c1d 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected CalorimetricDataPayload( TCalorimetricDataError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TCalorimetricDataError error ) : this( calorimetricData, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index c5de13d7..47714406 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -32,14 +32,13 @@ CancellationToken cancellationToken Database.FromDto(database), null) : new UpdateDatabasePayload( null, - new[] - { + [ new UpdateDatabaseError( UpdateDatabaseErrorCode.UNKNOWN, "Unknown error.", Array.Empty() ) - } + ] ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs index 3194b727..ff16e1db 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected GeometricDataPayload( TGeometricDataError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TGeometricDataError error ) : this( geometricData, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs index 97fecfb4..031685e7 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected GetHttpsResourcePayload( TGetHttpsResourceError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TGetHttpsResourceError error ) : this( getHttpsResource, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs index bac1715c..c952cde0 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected HygrothermalDataPayload( THygrothermalDataError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ THygrothermalDataError error ) : this( hygrothermalData, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs index 83802525..fa53996a 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs @@ -22,7 +22,7 @@ IReadOnlyCollection errors public MethodAsServicePayload( MethodAsServiceError error ) - : this(new[] { error }) + : this([error]) { } @@ -41,7 +41,7 @@ MethodAsServiceError error ) : this( calculationResult, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs index a0eba70e..d9730a53 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected OpticalDataPayload( TOpticalDataError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TOpticalDataError error ) : this( opticalData, - new[] { error } + [error] ) { } diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs index f73670b7..22be4489 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs @@ -24,7 +24,7 @@ IReadOnlyCollection errors protected PhotovoltaicDataPayload( TPhotovoltaicDataError error ) - : this(new[] { error }) + : this([error]) { } @@ -43,7 +43,7 @@ TPhotovoltaicDataError error ) : this( photovoltaicData, - new[] { error } + [error] ) { } diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs index 4ece5212..eb81a185 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -8,7 +8,7 @@ namespace Database.Migrations /// public partial class OpenIddict : Migration { - private static readonly string[] _applicationIdStatusSubjectAndTypeIndexColumns = new[] { "ApplicationId", "Status", "Subject", "Type" }; + private static readonly string[] _applicationIdStatusSubjectAndTypeIndexColumns = ["ApplicationId", "Status", "Subject", "Type"]; /// protected override void Up(MigrationBuilder migrationBuilder) From 2ad000e963e1a6c708ac3f450ef54ab47cf3adc8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 15:59:54 +0200 Subject: [PATCH 332/878] Use primary constructors everywhere --- .../src/Controllers/FileUploadController.cs | 20 ++----- backend/src/Data/AppliedMethod.cs | 14 ++--- backend/src/Data/CielabColor.cs | 23 +++---- .../src/Data/CrossDatabaseDataReference.cs | 28 ++++----- backend/src/Data/DataApproval.cs | 40 +++++-------- backend/src/Data/DataX.cs | 46 ++++++-------- .../Extensions/SchemaNameOptionsExtension.cs | 20 ++----- backend/src/Data/FileMetaInformation.cs | 18 ++---- backend/src/Data/GetHttpsResource.cs | 30 ++++------ backend/src/Data/InstitutionAccessRights.cs | 33 ++++------ backend/src/Data/NamedMethodArgument.cs | 20 +++---- backend/src/Data/NamedMethodSource.cs | 14 ++--- backend/src/Data/Numeration.cs | 23 +++---- backend/src/Data/Publication.cs | 50 ++++++---------- backend/src/Data/ResponseApproval.cs | 35 ++++------- backend/src/Data/Standard.cs | 40 +++++-------- .../ToTreeVertexAppliedConversionMethod.cs | 19 ++---- backend/src/Data/User.cs | 18 ++---- backend/src/Data/UserInfo.cs | 60 +++++++------------ .../InstitutionAccessRightsError.cs | 16 ++--- .../UpdateDataAccessRightsError.cs | 16 ++--- .../src/GraphQl/Approvals/AddApprovalError.cs | 16 ++--- .../CalorimetricDataByIdDataLoader.cs | 24 ++++---- .../CreateCalorimetricDataError.cs | 16 ++--- backend/src/GraphQl/Connection.cs | 18 ++---- backend/src/GraphQl/DataX/DataType.cs | 11 +--- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 13 ++-- .../GetHttpsResourceTreeNonRootVertex.cs | 22 +++---- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 26 ++++---- .../GraphQl/DataX/GetHttpsResourceTreeRoot.cs | 13 ++-- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 26 ++++---- .../GetHttpsResourcesByDataIdDataLoader.cs | 26 ++++---- backend/src/GraphQl/Databases/Database.cs | 48 ++++++--------- .../src/GraphQl/Databases/DatabasePayload.cs | 18 ++---- .../GraphQl/Databases/UpdateDatabaseError.cs | 16 ++--- .../Databases/UpdateDatabasePayload.cs | 14 ++--- backend/src/GraphQl/Edge.cs | 13 ++-- .../AssociationsByAssociateIdDataLoader.cs | 30 ++++------ .../GraphQl/Entities/EntityByIdDataLoader.cs | 25 +++----- backend/src/GraphQl/ForkingConnection.cs | 23 +++---- .../CreateGeometricDataError.cs | 16 ++--- .../GeometricDataByIdDataLoader.cs | 24 ++++---- .../CreateGetHttpsResourceError.cs | 16 ++--- .../GetHttpsResourceByIdDataLoader.cs | 24 ++++---- ...eChildrenByGetHttpsResourceIdDataLoader.cs | 26 ++++---- .../CreateHygrothermalDataError.cs | 16 ++--- .../HygrothermalDataByIdDataLoader.cs | 24 ++++---- backend/src/GraphQl/LocaleType.cs | 31 ++++------ .../GraphQl/LoggingDiagnosticEventListener.cs | 15 ++--- .../MethodAsService/MethodAsServiceError.cs | 16 ++--- .../OpticalDataX/CreateOpticalDataError.cs | 16 ++--- .../OpticalDataX/OpticalDataByIdDataLoader.cs | 24 ++++---- .../CreatePhotovoltaicDataError.cs | 16 ++--- .../PhotovoltaicDataByIdDataLoader.cs | 24 ++++---- backend/src/GraphQl/UserErrorBase.cs | 23 +++---- .../src/GraphQl/Users/UserByIdDataLoader.cs | 24 ++++---- backend/src/Services/EmailSender.cs | 25 +++----- 57 files changed, 494 insertions(+), 847 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 2193e91c..03c453fb 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -35,7 +35,10 @@ string trustedFileNameForFileStorage // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ -public class FileUploadController : Controller +public class FileUploadController( + ILogger logger, + ApplicationDbContext context + ) : Controller { private const long _fileSizeLimit = @@ -46,23 +49,12 @@ private const long // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions _defaultFormOptions = new(); - private readonly ILogger _logger; - private readonly ApplicationDbContext _context; + private readonly ILogger _logger = logger; + private readonly ApplicationDbContext _context = context; private readonly string[] _permittedExtensions = [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; - public FileUploadController( - ILogger logger, - ApplicationDbContext context - ) - { - _logger = logger; - _context = context; - // To save physical files to the temporary files folder, use: - //_targetDirectoryPath = Path.GetTempPath(); - } - // The following upload methods: // // 1. Disable the form value model binding to take control of handling diff --git a/backend/src/Data/AppliedMethod.cs b/backend/src/Data/AppliedMethod.cs index ebb2d35c..b5143070 100644 --- a/backend/src/Data/AppliedMethod.cs +++ b/backend/src/Data/AppliedMethod.cs @@ -5,7 +5,9 @@ namespace Database.Data; [Owned] -public sealed class AppliedMethod +public sealed class AppliedMethod( + Guid methodId + ) { public AppliedMethod( Guid methodId, @@ -18,15 +20,7 @@ ICollection sources Sources = sources; } - // `DbContext` needs this constructor without owned entities. - public AppliedMethod( - Guid methodId - ) - { - MethodId = methodId; - } - - public Guid MethodId { get; private set; } + public Guid MethodId { get; private set; } = methodId; public ICollection Arguments { get; private set; } = new List(); public ICollection Sources { get; private set; } = new List(); } \ No newline at end of file diff --git a/backend/src/Data/CielabColor.cs b/backend/src/Data/CielabColor.cs index a8ad1ac7..8b234437 100644 --- a/backend/src/Data/CielabColor.cs +++ b/backend/src/Data/CielabColor.cs @@ -3,20 +3,13 @@ namespace Database.Data; [Owned] -public sealed class CielabColor -{ - public CielabColor( - double lStar, - double aStar, - double bStar +public sealed class CielabColor( + double lStar, + double aStar, + double bStar ) - { - LStar = lStar; - AStar = aStar; - BStar = bStar; - } - - public double LStar { get; private set; } - public double AStar { get; private set; } - public double BStar { get; private set; } +{ + public double LStar { get; private set; } = lStar; + public double AStar { get; private set; } = aStar; + public double BStar { get; private set; } = bStar; } \ No newline at end of file diff --git a/backend/src/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index 5d3d1554..35061bb4 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -5,23 +5,15 @@ namespace Database.Data; [Owned] -public sealed class CrossDatabaseDataReference -{ - public CrossDatabaseDataReference( - Guid dataId, - DateTime dataTimestamp, - DataKind dataKind, - Guid databaseId +public sealed class CrossDatabaseDataReference( + Guid dataId, + DateTime dataTimestamp, + DataKind dataKind, + Guid databaseId ) - { - DataId = dataId; - DataTimestamp = dataTimestamp; - DataKind = dataKind; - DatabaseId = databaseId; - } - - public Guid DataId { get; private set; } - public DateTime DataTimestamp { get; private set; } - public DataKind DataKind { get; private set; } - public Guid DatabaseId { get; private set; } +{ + public Guid DataId { get; private set; } = dataId; + public DateTime DataTimestamp { get; private set; } = dataTimestamp; + public DataKind DataKind { get; private set; } = dataKind; + public Guid DatabaseId { get; private set; } = databaseId; } \ No newline at end of file diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 6e2f583f..0e10607f 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -5,32 +5,22 @@ namespace Database.Data; [Owned] -public sealed class DataApproval - : IApproval -{ - public DataApproval( - DateTime timestamp, - string signature, - string keyFingerprint, - string query, - string response, - Guid approverId +public sealed class DataApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + string response, + Guid approverId ) - { - Timestamp = timestamp; - Signature = signature; - KeyFingerprint = keyFingerprint; - Query = query; - Response = response; - ApproverId = approverId; - } - - public Guid ApproverId { get; private set; } - public DateTime Timestamp { get; private set; } - public string Signature { get; private set; } - public string KeyFingerprint { get; private set; } - public string Query { get; private set; } - public string Response { get; private set; } + : IApproval +{ + public Guid ApproverId { get; private set; } = approverId; + public DateTime Timestamp { get; private set; } = timestamp; + public string Signature { get; private set; } = signature; + public string KeyFingerprint { get; private set; } = keyFingerprint; + public string Query { get; private set; } = query; + public string Response { get; private set; } = response; public Publication? Publication { get; set; } public Standard? Standard { get; set; } [NotMapped] public IReference? Statement => Standard is not null ? Standard : Publication; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 3631c695..9a27ec0e 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -4,8 +4,16 @@ namespace Database.Data; -public abstract class DataX - : Entity, IData +public abstract class DataX( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt + ) + : Entity, IData { protected DataX( string locale, @@ -30,33 +38,13 @@ AppliedMethod appliedMethod AppliedMethod = appliedMethod; } - // `DbContext` needs this constructor without owned entities. - protected DataX( - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt - ) - { - Locale = locale; - ComponentId = componentId; - Name = name; - Description = description; - Warnings = warnings; - CreatorId = creatorId; - CreatedAt = createdAt; - } - - public string Locale { get; private set; } - public Guid ComponentId { get; private set; } - public string? Name { get; private set; } - public string? Description { get; private set; } - public string[] Warnings { get; private set; } - public Guid CreatorId { get; private set; } - public DateTime CreatedAt { get; private set; } + public string Locale { get; private set; } = locale; + public Guid ComponentId { get; private set; } = componentId; + public string? Name { get; private set; } = name; + public string? Description { get; private set; } = description; + public string[] Warnings { get; private set; } = warnings; + public Guid CreatorId { get; private set; } = creatorId; + public DateTime CreatedAt { get; private set; } = createdAt; public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = new List(); diff --git a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs index 008d241a..5b86a014 100644 --- a/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs +++ b/backend/src/Data/Extensions/SchemaNameOptionsExtension.cs @@ -5,15 +5,10 @@ // Inspired by https://github.com/npgsql/efcore.pg/blob/main/src/EFCore.PG/Infrastructure/Internal/NpgsqlOptionsExtension.cs namespace Database.Data.Extensions; -public sealed class SchemaNameOptionsExtension - : IDbContextOptionsExtension +public sealed class SchemaNameOptionsExtension(string schemaName) + : IDbContextOptionsExtension { - public SchemaNameOptionsExtension(string schemaName) - { - SchemaName = schemaName; - } - - public string SchemaName { get; } + public string SchemaName { get; } = schemaName; public DbContextOptionsExtensionInfo Info => new SchemaNameExtensionInfo(this); @@ -26,14 +21,9 @@ public void Validate(IDbContextOptions options) { } - public sealed class SchemaNameExtensionInfo - : DbContextOptionsExtensionInfo + public sealed class SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) + : DbContextOptionsExtensionInfo(extension) { - public SchemaNameExtensionInfo(SchemaNameOptionsExtension extension) - : base(extension) - { - } - public override bool IsDatabaseProvider => false; diff --git a/backend/src/Data/FileMetaInformation.cs b/backend/src/Data/FileMetaInformation.cs index 586bfdd6..afa1a8ec 100644 --- a/backend/src/Data/FileMetaInformation.cs +++ b/backend/src/Data/FileMetaInformation.cs @@ -4,17 +4,11 @@ namespace Database.Data; [Owned] -public sealed class FileMetaInformation -{ - public FileMetaInformation( - string[] path, - Guid dataFormatId +public sealed class FileMetaInformation( + string[] path, + Guid dataFormatId ) - { - Path = path; - DataFormatId = dataFormatId; - } - - public string[] Path { get; private set; } - public Guid DataFormatId { get; private set; } +{ + public string[] Path { get; private set; } = path; + public Guid DataFormatId { get; private set; } = dataFormatId; } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 06b8e837..ccc74ef6 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -5,8 +5,13 @@ namespace Database.Data; -public sealed class GetHttpsResource - : Entity +public sealed class GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + Guid? parentId + ) + : Entity { public GetHttpsResource( string? description, @@ -82,22 +87,9 @@ public GetHttpsResource( GeometricDataId = geometricDataId; } - public GetHttpsResource( - string? description, - string hashValue, - Guid dataFormatId, - Guid? parentId - ) - { - Description = description; - HashValue = hashValue; - DataFormatId = dataFormatId; - ParentId = parentId; - } - - public string? Description { get; private set; } - public string HashValue { get; private set; } - public Guid DataFormatId { get; private set; } + public string? Description { get; private set; } = description; + public string HashValue { get; private set; } = hashValue; + public Guid DataFormatId { get; private set; } = dataFormatId; public ICollection ArchivedFilesMetaInformation { get; private set; } = new List(); @@ -133,7 +125,7 @@ public GetHttpsResource( [InverseProperty(nameof(GeometricData.Resources))] public GeometricData? GeometricData { get; set; } - public Guid? ParentId { get; private set; } + public Guid? ParentId { get; private set; } = parentId; // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index a30dfb12..39c90492 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -4,28 +4,19 @@ namespace Database.Data; -public class InstitutionAccessRights - : Entity -{ - public Guid InstitutionId { get; set; } - public uint? AllowedUserCount { get; set; } - public uint? AllowedDatasetsPerTime { get; set; } - public TimeSpan Period { get; set; } - public List UserAlreadyAccessed { get; private set; } - - public InstitutionAccessRights( - Guid institutionId, - uint? allowedUserCount, - uint? allowedDatasetsPerTime, - TimeSpan period +public class InstitutionAccessRights( + Guid institutionId, + uint? allowedUserCount, + uint? allowedDatasetsPerTime, + TimeSpan period ) - { - InstitutionId = institutionId; - AllowedUserCount = allowedUserCount; - AllowedDatasetsPerTime = allowedDatasetsPerTime; - Period = period; - UserAlreadyAccessed = new List(); - } + : Entity +{ + public Guid InstitutionId { get; set; } = institutionId; + public uint? AllowedUserCount { get; set; } = allowedUserCount; + public uint? AllowedDatasetsPerTime { get; set; } = allowedDatasetsPerTime; + public TimeSpan Period { get; set; } = period; + public List UserAlreadyAccessed { get; private set; } = new List(); /// /// Check if dataset is restricted by access rights for institution. Datasets per time period of diff --git a/backend/src/Data/NamedMethodArgument.cs b/backend/src/Data/NamedMethodArgument.cs index 87f53cf5..e6c05366 100644 --- a/backend/src/Data/NamedMethodArgument.cs +++ b/backend/src/Data/NamedMethodArgument.cs @@ -5,20 +5,14 @@ namespace Database.Data; [Owned] -public sealed class NamedMethodArgument - : IDisposable -{ - public NamedMethodArgument( - string name, - JsonDocument value +public sealed class NamedMethodArgument( + string name, + JsonDocument value ) - { - Name = name; - Value = value; - } - - public string Name { get; private set; } - public JsonDocument Value { get; private set; } + : IDisposable +{ + public string Name { get; private set; } = name; + public JsonDocument Value { get; private set; } = value; public void Dispose() { diff --git a/backend/src/Data/NamedMethodSource.cs b/backend/src/Data/NamedMethodSource.cs index 1a6820af..72836cd3 100644 --- a/backend/src/Data/NamedMethodSource.cs +++ b/backend/src/Data/NamedMethodSource.cs @@ -3,7 +3,9 @@ namespace Database.Data; [Owned] -public sealed class NamedMethodSource +public sealed class NamedMethodSource( + string name + ) { public NamedMethodSource( string name, @@ -15,14 +17,6 @@ CrossDatabaseDataReference value Value = value; } - // `DbContext` needs this constructor without owned entities. - public NamedMethodSource( - string name - ) - { - Name = name; - } - - public string Name { get; private set; } + public string Name { get; private set; } = name; public CrossDatabaseDataReference Value { get; private set; } = default!; } \ No newline at end of file diff --git a/backend/src/Data/Numeration.cs b/backend/src/Data/Numeration.cs index fc0f2033..973dfe86 100644 --- a/backend/src/Data/Numeration.cs +++ b/backend/src/Data/Numeration.cs @@ -4,22 +4,15 @@ namespace Database.Data; [Owned] -public sealed class Numeration -{ - public Numeration( - string? prefix, - string mainNumber, - string? suffix +public sealed class Numeration( + string? prefix, + string mainNumber, + string? suffix ) - { - Prefix = prefix; - MainNumber = mainNumber; - Suffix = suffix; - } - - [MinLength(1)] public string? Prefix { get; private set; } +{ + [MinLength(1)] public string? Prefix { get; private set; } = prefix; - [Required] [MinLength(1)] public string MainNumber { get; private set; } + [Required][MinLength(1)] public string MainNumber { get; private set; } = mainNumber; - [MinLength(1)] public string? Suffix { get; private set; } + [MinLength(1)] public string? Suffix { get; private set; } = suffix; } \ No newline at end of file diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs index f5ea3b80..15dd27fe 100644 --- a/backend/src/Data/Publication.cs +++ b/backend/src/Data/Publication.cs @@ -6,60 +6,48 @@ namespace Database.Data; [Owned] -public sealed class Publication - : IReference -{ - public Publication( - string? title, - string? @abstract, - string? section, - string[]? authors, - string? doi, - string? arXiv, - string? urn, - Uri? webAddress +public sealed class Publication( + string? title, + string? @abstract, + string? section, + string[]? authors, + string? doi, + string? arXiv, + string? urn, + Uri? webAddress ) - { - Title = title; - Abstract = @abstract; - Section = section; - Authors = authors; - Doi = doi; - ArXiv = arXiv; - Urn = urn; - WebAddress = webAddress; - } - - [MinLength(1)] public string[]? Authors { get; private set; } + : IReference +{ + [MinLength(1)] public string[]? Authors { get; private set; } = authors; [MinLength(1)] [RegularExpression("^(10[.][0-z/.]*)$")] [GraphQLDescription( "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] - public string? Doi { get; private set; } + public string? Doi { get; private set; } = doi; [MinLength(1)] [RegularExpression("^(arXiv:)[0-z./]*$")] [GraphQLDescription( "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] - public string? ArXiv { get; private set; } + public string? ArXiv { get; private set; } = arXiv; [MinLength(1)] [RegularExpression("^(urn:)[0-z:./-]*$")] [GraphQLDescription( "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string.")] - public string? Urn { get; private set; } + public string? Urn { get; private set; } = urn; [Url] [GraphQLDescription( "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] - public Uri? WebAddress { get; private set; } + public Uri? WebAddress { get; private set; } = webAddress; - [MinLength(1)] public string? Title { get; private set; } + [MinLength(1)] public string? Title { get; private set; } = title; - [MinLength(1)] public string? Abstract { get; private set; } + [MinLength(1)] public string? Abstract { get; private set; } = @abstract; [MinLength(1)] [GraphQLDescription("Referenced section")] - public string? Section { get; private set; } + public string? Section { get; private set; } = section; } \ No newline at end of file diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index f90dfd87..f878a29c 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -4,28 +4,19 @@ namespace Database.Data { [Owned] - public sealed class ResponseApproval - : IApproval - { - public ResponseApproval( - DateTime timestamp, - string signature, - string keyFingerprint, - string query, - string response + public sealed class ResponseApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + string response ) - { - Timestamp = timestamp; - Signature = signature; - KeyFingerprint = keyFingerprint; - Query = query; - Response = response; - } - - public DateTime Timestamp { get; private set; } - public string Signature { get; private set; } - public string KeyFingerprint { get; private set; } - public string Query { get; private set; } - public string Response { get; private set; } + : IApproval + { + public DateTime Timestamp { get; private set; } = timestamp; + public string Signature { get; private set; } = signature; + public string KeyFingerprint { get; private set; } = keyFingerprint; + public string Query { get; private set; } = query; + public string Response { get; private set; } = response; } } \ No newline at end of file diff --git a/backend/src/Data/Standard.cs b/backend/src/Data/Standard.cs index 003518f0..b6aa5a2a 100644 --- a/backend/src/Data/Standard.cs +++ b/backend/src/Data/Standard.cs @@ -9,43 +9,33 @@ namespace Database.Data; [Owned] [GraphQLDescription( "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier.")] -public sealed class Standard - : IReference -{ - public Standard( - string? title, - string? @abstract, - string? section, - int? year, - Standardizer[] standardizers, - Uri? locator +public sealed class Standard( + string? title, + string? @abstract, + string? section, + int? year, + Standardizer[] standardizers, + Uri? locator ) - { - Title = title; - Abstract = @abstract; - Section = section; - Year = year; - Standardizers = standardizers; - Locator = locator; - } - + : IReference +{ [Range(0, 3000)] [GraphQLDescription( "It is important to define the year in which the standard was issued because there can be relevant updates of one standard.")] - public int? Year { get; private set; } + public int? Year { get; private set; } = year; // Numeration, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types [Required] public Numeration Numeration { get; set; } = default!; - [Required] public Standardizer[] Standardizers { get; private set; } + [Required] public Standardizer[] Standardizers { get; private set; } = standardizers; - [Url] public Uri? Locator { get; private set; } + [Url] public Uri? Locator { get; private set; } = locator; - [MinLength(1)] public string? Title { get; private set; } + [MinLength(1)] public string? Title { get; private set; } = title; - [MinLength(1)] public string? Abstract { get; private set; } + [MinLength(1)] public string? Abstract { get; private set; } = @abstract; [MinLength(1)] [GraphQLDescription("The section of the standard to which the reference refers to.")] - public string? Section { get; private set; } + public string? Section { get; private set; } = section; } \ No newline at end of file diff --git a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs index c05a8183..7ac24864 100644 --- a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs +++ b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs @@ -5,7 +5,10 @@ namespace Database.Data; [Owned] -public sealed class ToTreeVertexAppliedConversionMethod +public sealed class ToTreeVertexAppliedConversionMethod( + Guid methodId, + string sourceName + ) { public ToTreeVertexAppliedConversionMethod( Guid methodId, @@ -20,17 +23,7 @@ string sourceName Arguments = arguments; } - // `DbContext` needs this constructor without owned entities. - public ToTreeVertexAppliedConversionMethod( - Guid methodId, - string sourceName - ) - { - MethodId = methodId; - SourceName = sourceName; - } - - public Guid MethodId { get; private set; } + public Guid MethodId { get; private set; } = methodId; public ICollection Arguments { get; private set; } = new List(); - public string SourceName { get; private set; } + public string SourceName { get; private set; } = sourceName; } \ No newline at end of file diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs index d1767fca..564862f4 100644 --- a/backend/src/Data/User.cs +++ b/backend/src/Data/User.cs @@ -1,18 +1,12 @@ namespace Database.Data; -public sealed class User : Entity +public sealed class User( + string subject, + string name + ) : Entity { - public User( - string subject, - string name - ) - { - Subject = subject; - Name = name; - } - - public string Subject { get; private set; } - public string Name { get; private set; } + public string Subject { get; private set; } = subject; + public string Name { get; private set; } = name; public void Update( string name diff --git a/backend/src/Data/UserInfo.cs b/backend/src/Data/UserInfo.cs index 3607e4c8..69e55b0b 100644 --- a/backend/src/Data/UserInfo.cs +++ b/backend/src/Data/UserInfo.cs @@ -3,49 +3,31 @@ namespace Database.Data; -public sealed class Address +public sealed class Address(string formatted) { - public string Formatted { get; private set; } - - public Address(string formatted) - { - Formatted = formatted; - } + public string Formatted { get; private set; } = formatted; } -public sealed class UserInfo +public sealed class UserInfo( + Address? address, + string email, + bool emailVerified, + string name, + string? phonenumber, + bool phonenumberVerified, + IReadOnlyList? roles, + string subject, + string? website) { - public Address? Address { get; private set; } - public string Email { get; private set; } - public bool EmailVerified { get; private set; } - public string Name { get; private set; } - public string? PhoneNumber { get; private set; } - public bool PhoneNumberVerified { get; private set; } - public IReadOnlyList? Roles { get; private set; } - public string Sub { get; private set; } // Subject - public string? Website { get; private set; } - - public UserInfo( - Address? address, - string email, - bool emailVerified, - string name, - string? phonenumber, - bool phonenumberVerified, - IReadOnlyList? roles, - string subject, - string? website) - { - Address = address; - Email = email; - EmailVerified = emailVerified; - Name = name; - PhoneNumber = phonenumber; - PhoneNumberVerified = phonenumberVerified; - Roles = roles; - Sub = subject; - Website = website; - } + public Address? Address { get; private set; } = address; + public string Email { get; private set; } = email; + public bool EmailVerified { get; private set; } = emailVerified; + public string Name { get; private set; } = name; + public string? PhoneNumber { get; private set; } = phonenumber; + public bool PhoneNumberVerified { get; private set; } = phonenumberVerified; + public IReadOnlyList? Roles { get; private set; } = roles; + public string Sub { get; private set; } = subject; + public string? Website { get; private set; } = website; public static UserInfo? FromDto(UserInfoDto? userInfoDto) { diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs index c0b18cc1..e28de3e5 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.AccessRights; -public class InstitutionAccessRightsError - : UserErrorBase -{ - public InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode code, - string message, - IReadOnlyList path +public class InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs index 0d9e67b7..67a43a24 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.AccessRights; -public class UpdateDataAccessRightsError - : UserErrorBase -{ - public UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode code, - string message, - IReadOnlyList path +public class UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalError.cs b/backend/src/GraphQl/Approvals/AddApprovalError.cs index 608f661d..2d28b442 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalError.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalError.cs @@ -3,15 +3,11 @@ namespace Database.GraphQl.Approvals; -public class AddApprovalError - : UserErrorBase -{ - public AddApprovalError( - AddApprovalErrorCode code, - string message, - IReadOnlyList path +public class AddApprovalError( + AddApprovalErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs index 927ba17d..52cbce26 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.CalorimetricDataX; -public sealed class CalorimetricDataByIdDataLoader - : EntityByIdDataLoader -{ - public CalorimetricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class CalorimetricDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.CalorimetricData + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.CalorimetricData ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs index 0c2ef28a..2c6cc11f 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.CalorimetricDataX; -public sealed class CreateCalorimetricDataError - : UserErrorBase -{ - public CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode code, - string message, - IReadOnlyList path +public sealed class CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index 31bbf56c..f83cb31d 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -8,22 +8,16 @@ namespace Database.GraphQl; -public abstract class Connection +public abstract class Connection( + TSubject subject, + Func createEdge + ) where TSubject : IEntity where TAssociationsByAssociateIdDataLoader : IDataLoader { - private readonly Func _createEdge; - - protected Connection( - TSubject subject, - Func createEdge - ) - { - Subject = subject; - _createEdge = createEdge; - } + private readonly Func _createEdge = createEdge; - protected TSubject Subject { get; } + protected TSubject Subject { get; } = subject; public async Task> GetEdgesAsync( TAssociationsByAssociateIdDataLoader dataLoader, diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 4a6a63da..83cb2fc0 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -3,15 +3,10 @@ namespace Database.GraphQl.DataX; -public sealed class DataType - : InterfaceType +public sealed class DataType(AppSettings appSettings) + : InterfaceType { - private readonly AppSettings _appSettings; - - public DataType(AppSettings appSettings) - { - _appSettings = appSettings; - } + private readonly AppSettings _appSettings = appSettings; protected override void Configure( IInterfaceTypeDescriptor descriptor diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 7577e821..feacccab 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -5,16 +5,11 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourceTree -{ - private readonly Data.DataX _data; - - public GetHttpsResourceTree( - Data.DataX data +public sealed class GetHttpsResourceTree( + Data.DataX data ) - { - _data = data; - } +{ + private readonly Data.DataX _data = data; public async Task GetRoot( GetHttpsResourceTreeRootByDataIdDataLoader byId, diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs index 6615de6f..49d551b0 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -5,19 +5,11 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourceTreeNonRootVertex - : IGetHttpsResourceTreeVertex -{ - public GetHttpsResourceTreeNonRootVertex( - GetHttpsResource value +public sealed class GetHttpsResourceTreeNonRootVertex( + GetHttpsResource value ) - { - Value = value; - AppliedConversionMethod = value.AppliedConversionMethod ?? - throw new InvalidOperationException( - "Each non-root vertex has an applied conversion method."); - } - + : IGetHttpsResourceTreeVertex +{ [GraphQLType>] public string ParentId => GetHttpsResource.ConstructVertexId( @@ -25,9 +17,11 @@ GetHttpsResource value ?? throw new InvalidOperationException("Impossible! Each non-root vertex has a parent.") ); - public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } + public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } = value.AppliedConversionMethod ?? + throw new InvalidOperationException( + "Each non-root vertex has an applied conversion method."); [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); - public GetHttpsResource Value { get; } + public GetHttpsResource Value { get; } = value; } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 9d21a64a..af880bed 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -7,26 +7,22 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader - : AssociationsByAssociateIdDataLoader -{ - public GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => + : AssociationsByAssociateIdDataLoader( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), - x => x.DataId + x => x.DataId ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs index 76ba74da..956377bc 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRoot.cs @@ -4,16 +4,11 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourceTreeRoot : IGetHttpsResourceTreeVertex +public sealed class GetHttpsResourceTreeRoot( + GetHttpsResource value + ) : IGetHttpsResourceTreeVertex { - public GetHttpsResourceTreeRoot( - GetHttpsResource value - ) - { - Value = value; - } - [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); - public GetHttpsResource Value { get; } + public GetHttpsResource Value { get; } = value; } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 09d80fcc..389c282c 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -7,26 +7,22 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourceTreeRootByDataIdDataLoader - : AssociationsByAssociateIdDataLoader -{ - public GetHttpsResourceTreeRootByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GetHttpsResourceTreeRootByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => + : AssociationsByAssociateIdDataLoader( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), - x => x.DataId + x => x.DataId ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index ade0cec6..1d9780df 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -7,25 +7,21 @@ namespace Database.GraphQl.DataX; -public sealed class GetHttpsResourcesByDataIdDataLoader - : AssociationsByAssociateIdDataLoader -{ - public GetHttpsResourcesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GetHttpsResourcesByDataIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => + : AssociationsByAssociateIdDataLoader( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), - x => x.DataId + x => x.DataId ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs index 5ad268cc..6dd938e3 100644 --- a/backend/src/GraphQl/Databases/Database.cs +++ b/backend/src/GraphQl/Databases/Database.cs @@ -2,41 +2,29 @@ namespace Database.GraphQl.Databases; -public sealed class Database -{ - public Database( - Guid uuid, - string name, - string description, - Uri locator, - DatabaseVerificationState verificationState, - string verificationCode, - bool canCurrentUserUpdateNode, - bool canCurrentUserVerifyNode +public sealed class Database( + Guid uuid, + string name, + string description, + Uri locator, + DatabaseVerificationState verificationState, + string verificationCode, + bool canCurrentUserUpdateNode, + bool canCurrentUserVerifyNode ) - { - Uuid = uuid; - Name = name; - Description = description; - Locator = locator; - VerificationState = verificationState; - VerificationCode = verificationCode; - CanCurrentUserUpdateNode = canCurrentUserUpdateNode; - CanCurrentUserVerifyNode = canCurrentUserVerifyNode; - } - - public Guid Uuid { get; } - public string Name { get; } - public string Description { get; } - public Uri Locator { get; } - public DatabaseVerificationState VerificationState { get; } +{ + public Guid Uuid { get; } = uuid; + public string Name { get; } = name; + public string Description { get; } = description; + public Uri Locator { get; } = locator; + public DatabaseVerificationState VerificationState { get; } = verificationState; - public string VerificationCode { get; } + public string VerificationCode { get; } = verificationCode; // public Institution? Operator { get; set; } - public bool CanCurrentUserUpdateNode { get; } + public bool CanCurrentUserUpdateNode { get; } = canCurrentUserUpdateNode; - public bool CanCurrentUserVerifyNode { get; } + public bool CanCurrentUserVerifyNode { get; } = canCurrentUserVerifyNode; public static Database FromDto(DatabaseDto databaseDto) { diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs index 14f27274..1511ac07 100644 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ b/backend/src/GraphQl/Databases/DatabasePayload.cs @@ -2,19 +2,13 @@ namespace Database.GraphQl.Databases; -public abstract class DatabasePayload +public abstract class DatabasePayload( + Database? database, + IReadOnlyCollection? errors + ) : Payload where TDatabaseError : IUserError { - protected DatabasePayload( - Database? database, - IReadOnlyCollection? errors - ) - { - Database = database; - Errors = errors; - } - - public Database? Database { get; } - public IReadOnlyCollection? Errors { get; } + public Database? Database { get; } = database; + public IReadOnlyCollection? Errors { get; } = errors; } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs index 431fec23..5e82f40f 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.Databases; -public sealed class UpdateDatabaseError - : UserErrorBase -{ - public UpdateDatabaseError( - UpdateDatabaseErrorCode code, - string message, - IReadOnlyList path +public sealed class UpdateDatabaseError( + UpdateDatabaseErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs index 4463e551..047741ca 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs @@ -2,14 +2,10 @@ namespace Database.GraphQl.Databases; -public sealed class UpdateDatabasePayload - : DatabasePayload -{ - public UpdateDatabasePayload( - Database? database, - IReadOnlyCollection? errors +public sealed class UpdateDatabasePayload( + Database? database, + IReadOnlyCollection? errors ) - : base(database, errors) - { - } + : DatabasePayload(database, errors) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/Edge.cs b/backend/src/GraphQl/Edge.cs index 33acf61b..0950b83a 100644 --- a/backend/src/GraphQl/Edge.cs +++ b/backend/src/GraphQl/Edge.cs @@ -5,17 +5,12 @@ namespace Database.GraphQl; -public abstract class Edge +public abstract class Edge( + Guid nodeId + ) where TNodeByIdDataLoader : IDataLoader { - private readonly Guid _nodeId; - - protected Edge( - Guid nodeId - ) - { - _nodeId = nodeId; - } + private readonly Guid _nodeId = nodeId; public async Task GetNodeAsync( TNodeByIdDataLoader byId, diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs index 74938d74..ba7cc9d4 100644 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs @@ -10,26 +10,18 @@ namespace Database.GraphQl.Entities; -public abstract class AssociationsByAssociateIdDataLoader - : GroupedDataLoader -{ - private readonly IDbContextFactory _dbContextFactory; - private readonly Func _getAssociateId; - private readonly Func, IQueryable> _getAssociations; - - protected AssociationsByAssociateIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func, IQueryable> getAssociations, - Func getAssociateId +public abstract class AssociationsByAssociateIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory, + Func, IQueryable> getAssociations, + Func getAssociateId ) - : base(batchScheduler, options) - { - _dbContextFactory = dbContextFactory; - _getAssociations = getAssociations; - _getAssociateId = getAssociateId; - } + : GroupedDataLoader(batchScheduler, options) +{ + private readonly IDbContextFactory _dbContextFactory = dbContextFactory; + private readonly Func _getAssociateId = getAssociateId; + private readonly Func, IQueryable> _getAssociations = getAssociations; protected override async Task> LoadGroupedBatchAsync( IReadOnlyList keys, diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs index 8532b92e..d706c2ec 100644 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs @@ -9,24 +9,17 @@ namespace Database.GraphQl.Entities; -public abstract class EntityByIdDataLoader - : BatchDataLoader +public abstract class EntityByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory, + Func> getQueryable + ) + : BatchDataLoader(batchScheduler, options) where TEntity : class, IEntity { - private readonly IDbContextFactory _dbContextFactory; - private readonly Func> _getQueryable; - - protected EntityByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func> getQueryable - ) - : base(batchScheduler, options) - { - _dbContextFactory = dbContextFactory; - _getQueryable = getQueryable; - } + private readonly IDbContextFactory _dbContextFactory = dbContextFactory; + private readonly Func> _getQueryable = getQueryable; protected override async Task> LoadBatchAsync( IReadOnlyList keys, diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index 8a38a2cc..90c0d8e8 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -9,26 +9,19 @@ namespace Database.GraphQl; public abstract class ForkingConnection + TOtherAssociationsByAssociateIdDataLoader, TEdge>( + TSubject subject, + bool useFirstDataLoader, + Func createEdge + ) where TSubject : IEntity where TSomeAssociationsByAssociateIdDataLoader : IDataLoader where TOtherAssociationsByAssociateIdDataLoader : IDataLoader { - private readonly Func _createEdge; - private readonly bool _useFirstDataLoader; - - protected ForkingConnection( - TSubject subject, - bool useFirstDataLoader, - Func createEdge - ) - { - Subject = subject; - _useFirstDataLoader = useFirstDataLoader; - _createEdge = createEdge; - } + private readonly Func _createEdge = createEdge; + private readonly bool _useFirstDataLoader = useFirstDataLoader; - protected TSubject Subject { get; } + protected TSubject Subject { get; } = subject; public Task> GetEdgesAsync( TSomeAssociationsByAssociateIdDataLoader someDataLoader, diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs index 83c2712e..0fd09e51 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.GeometricDataX; -public sealed class CreateGeometricDataError - : UserErrorBase -{ - public CreateGeometricDataError( - CreateGeometricDataErrorCode code, - string message, - IReadOnlyList path +public sealed class CreateGeometricDataError( + CreateGeometricDataErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs index 8fb62d84..ef2c94e3 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.GeometricDataX; -public sealed class GeometricDataByIdDataLoader - : EntityByIdDataLoader -{ - public GeometricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GeometricDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GeometricData + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.GeometricData ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs index 5252c6a7..b729e582 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class CreateGetHttpsResourceError - : UserErrorBase -{ - public CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode code, - string message, - IReadOnlyList path +public sealed class CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs index 80f97933..f937d1f3 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class GetHttpsResourceByIdDataLoader - : EntityByIdDataLoader -{ - public GetHttpsResourceByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GetHttpsResourceByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GetHttpsResources + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.GetHttpsResources ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs index 3ee732b4..276b992d 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs @@ -7,24 +7,20 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader - : AssociationsByAssociateIdDataLoader -{ - public GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => + : AssociationsByAssociateIdDataLoader( + batchScheduler, + options, + dbContextFactory, + (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => ids.Contains(x.ParentId ?? Guid.Empty) ), - x => x.Id + x => x.Id ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs index 625ffdf2..d55f0970 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.HygrothermalDataX; -public sealed class CreateHygrothermalDataError - : UserErrorBase -{ - public CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode code, - string message, - IReadOnlyList path +public sealed class CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs index 252cf1a9..deac4fcf 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.HygrothermalDataX; -public sealed class HygrothermalDataByIdDataLoader - : EntityByIdDataLoader -{ - public HygrothermalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class HygrothermalDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.HygrothermalData + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.HygrothermalData ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs index 7191b940..7dfb9a6b 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/LocaleType.cs @@ -20,8 +20,19 @@ namespace Database.GraphQl; /// maintains the /// [Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). /// -public class LocaleType - : RegexType +/// +/// Initializes a new instance of the class. +/// +public class LocaleType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit) + : RegexType( + name, + _validationPattern, + description, + RegexOptions.Compiled | RegexOptions.IgnoreCase, + bind) { private const string _validationPattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; @@ -36,20 +47,4 @@ public LocaleType() ) { } - - /// - /// Initializes a new instance of the class. - /// - public LocaleType( - string name, - string? description = null, - BindingBehavior bind = BindingBehavior.Explicit) - : base( - name, - _validationPattern, - description, - RegexOptions.Compiled | RegexOptions.IgnoreCase, - bind) - { - } } \ No newline at end of file diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index 39f384a6..f192ba02 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -93,18 +93,13 @@ string error // Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate // and https://chillicream.com/blog/2021/01/10/hot-chocolate-logging -public sealed class LoggingDiagnosticEventListener - : ExecutionDiagnosticEventListener +public sealed class LoggingDiagnosticEventListener( + ILogger logger + ) + : ExecutionDiagnosticEventListener { private static Stopwatch s_queryTimer = null!; - private readonly ILogger _logger; - - public LoggingDiagnosticEventListener( - ILogger logger - ) - { - _logger = logger; - } + private readonly ILogger _logger = logger; // this diagnostic event is raised when a request is executed ... public override IDisposable ExecuteRequest(IRequestContext context) diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs index 89ba6e49..e6d497b5 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs @@ -3,15 +3,11 @@ namespace Database.GraphQl.MethodAsService; -public class MethodAsServiceError - : UserErrorBase -{ - public MethodAsServiceError( - MethodAsServiceErrorCode code, - string message, - IReadOnlyList path +public class MethodAsServiceError( + MethodAsServiceErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs index cd4b0ec9..a2c3736c 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.OpticalDataX; -public sealed class CreateOpticalDataError - : UserErrorBase -{ - public CreateOpticalDataError( - CreateOpticalDataErrorCode code, - string message, - IReadOnlyList path +public sealed class CreateOpticalDataError( + CreateOpticalDataErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs index 9e9f1f00..e1a27bc1 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.OpticalDataX; -public sealed class OpticalDataByIdDataLoader - : EntityByIdDataLoader -{ - public OpticalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class OpticalDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.OpticalData + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.OpticalData ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs index 80c99ee3..1e375136 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs @@ -2,15 +2,11 @@ namespace Database.GraphQl.PhotovoltaicDataX; -public sealed class CreatePhotovoltaicDataError - : UserErrorBase -{ - public CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode code, - string message, - IReadOnlyList path +public sealed class CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode code, + string message, + IReadOnlyList path ) - : base(code, message, path) - { - } + : UserErrorBase(code, message, path) +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs index 3a682bc5..79e00ab5 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.PhotovoltaicDataX; -public sealed class PhotovoltaicDataByIdDataLoader - : EntityByIdDataLoader -{ - public PhotovoltaicDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class PhotovoltaicDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.PhotovoltaicData + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.PhotovoltaicData ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/GraphQl/UserErrorBase.cs b/backend/src/GraphQl/UserErrorBase.cs index 8751c523..253a2b53 100644 --- a/backend/src/GraphQl/UserErrorBase.cs +++ b/backend/src/GraphQl/UserErrorBase.cs @@ -3,22 +3,15 @@ namespace Database.GraphQl; -public abstract class UserErrorBase +public abstract class UserErrorBase( + TUserErrorCode code, + string message, + IReadOnlyList path + ) : IUserError where TUserErrorCode : struct, Enum { - protected UserErrorBase( - TUserErrorCode code, - string message, - IReadOnlyList path - ) - { - Code = code; - Message = message; - Path = path; - } - - public TUserErrorCode Code { get; } - public string Message { get; } - public IReadOnlyList Path { get; } + public TUserErrorCode Code { get; } = code; + public string Message { get; } = message; + public IReadOnlyList Path { get; } = path; } \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserByIdDataLoader.cs b/backend/src/GraphQl/Users/UserByIdDataLoader.cs index bb64e7a5..afc10301 100644 --- a/backend/src/GraphQl/Users/UserByIdDataLoader.cs +++ b/backend/src/GraphQl/Users/UserByIdDataLoader.cs @@ -5,20 +5,16 @@ namespace Database.GraphQl.Users; -public sealed class UserByIdDataLoader - : EntityByIdDataLoader -{ - public UserByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory +public sealed class UserByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory ) - : base( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.Users + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.Users ) - { - } +{ } \ No newline at end of file diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 467c6320..38f8e09f 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -21,23 +21,16 @@ string Body ); } -public sealed class EmailSender - : IEmailSender -{ - private readonly ILogger _logger; - private readonly string _smtpHost; - private readonly int _smtpPort; - - public EmailSender( - string smtpHost, - int smtpPort, - ILogger logger +public sealed class EmailSender( + string smtpHost, + int smtpPort, + ILogger logger ) - { - _smtpHost = smtpHost; - _smtpPort = smtpPort; - _logger = logger; - } + : IEmailSender +{ + private readonly ILogger _logger = logger; + private readonly string _smtpHost = smtpHost; + private readonly int _smtpPort = smtpPort; public Task SendAsync( (string name, string address) recipient, From 01bb804bd69983656dfb8dfc9c190f1b425fce5c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 16:20:04 +0200 Subject: [PATCH 333/878] Simplify collection initialization expressions --- backend/src/Data/AppliedMethod.cs | 4 ++-- backend/src/Data/CalorimetricData.cs | 2 +- backend/src/Data/DataX.cs | 4 ++-- backend/src/Data/GeometricData.cs | 2 +- backend/src/Data/GetHttpsResource.cs | 4 ++-- backend/src/Data/HygrothermalData.cs | 2 +- backend/src/Data/InstitutionAccessRights.cs | 2 +- backend/src/Data/OpticalData.cs | 4 ++-- backend/src/Data/PhotovoltaicData.cs | 2 +- backend/src/Data/ToTreeVertexAppliedConversionMethod.cs | 2 +- backend/src/Methods/TransmittanceSumMethod.cs | 6 +++--- backend/src/Services/MethodCalculationService.cs | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/src/Data/AppliedMethod.cs b/backend/src/Data/AppliedMethod.cs index b5143070..c9e68565 100644 --- a/backend/src/Data/AppliedMethod.cs +++ b/backend/src/Data/AppliedMethod.cs @@ -21,6 +21,6 @@ ICollection sources } public Guid MethodId { get; private set; } = methodId; - public ICollection Arguments { get; private set; } = new List(); - public ICollection Sources { get; private set; } = new List(); + public ICollection Arguments { get; private set; } = []; + public ICollection Sources { get; private set; } = []; } \ No newline at end of file diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 43af5e63..e16f6453 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -59,7 +59,7 @@ double[] uValues } [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] - public override ICollection Resources { get; } = new List(); + public override ICollection Resources { get; } = []; public double[] GValues { get; private set; } public double[] UValues { get; private set; } diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 9a27ec0e..ae5a2fbf 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -47,12 +47,12 @@ AppliedMethod appliedMethod public DateTime CreatedAt { get; private set; } = createdAt; public AppliedMethod AppliedMethod { get; private set; } = default!; - public ICollection Approvals { get; } = new List(); + public ICollection Approvals { get; } = []; public ResponseApproval? Approval { get; set; } // TODO Exactly one resource must not have a parent and each other resource must have one from // this list and the graph must be connected. In other words, the resources must form a tree. - public virtual ICollection Resources { get; } = new List(); + public virtual ICollection Resources { get; } = []; public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index c67ccc60..ff11bb7a 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -54,7 +54,7 @@ double[] thicknesses } [InverseProperty(nameof(GetHttpsResource.GeometricData))] - public override ICollection Resources { get; } = new List(); + public override ICollection Resources { get; } = []; public double[] Thicknesses { get; private set; } } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index ccc74ef6..6674ec4e 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -92,7 +92,7 @@ public GetHttpsResource( public Guid DataFormatId { get; private set; } = dataFormatId; public ICollection ArchivedFilesMetaInformation { get; private set; } = - new List(); + []; // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! [NotMapped] @@ -134,7 +134,7 @@ public GetHttpsResource( [InverseProperty(nameof(Children))] public GetHttpsResource? Parent { get; set; } [InverseProperty(nameof(Parent))] - public ICollection Children { get; } = new List(); + public ICollection Children { get; } = []; public static string ConstructVertexId(Guid id) { diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 61f06a26..aef4c23b 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -51,5 +51,5 @@ DateTime createdAt } [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] - public override ICollection Resources { get; } = new List(); + public override ICollection Resources { get; } = []; } \ No newline at end of file diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 39c90492..da387aeb 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -16,7 +16,7 @@ TimeSpan period public uint? AllowedUserCount { get; set; } = allowedUserCount; public uint? AllowedDatasetsPerTime { get; set; } = allowedDatasetsPerTime; public TimeSpan Period { get; set; } = period; - public List UserAlreadyAccessed { get; private set; } = new List(); + public List UserAlreadyAccessed { get; private set; } = []; /// /// Check if dataset is restricted by access rights for institution. Datasets per time period of diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index ff821806..650fe81d 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -90,7 +90,7 @@ double[] colorRenderingIndices } [InverseProperty(nameof(GetHttpsResource.OpticalData))] - public override ICollection Resources { get; } = new List(); + public override ICollection Resources { get; } = []; public OpticalComponentType? Type { get; private set; } public OpticalComponentSubtype? Subtype { get; private set; } @@ -101,5 +101,5 @@ double[] colorRenderingIndices public double[] NearnormalHemisphericalSolarReflectances { get; private set; } public double[] InfraredEmittances { get; private set; } public double[] ColorRenderingIndices { get; private set; } - public ICollection CielabColors { get; private set; } = new List(); + public ICollection CielabColors { get; private set; } = []; } \ No newline at end of file diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 35660ce4..019a8ab6 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -51,5 +51,5 @@ DateTime createdAt } [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] - public override ICollection Resources { get; } = new List(); + public override ICollection Resources { get; } = []; } \ No newline at end of file diff --git a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs index 7ac24864..076b1f69 100644 --- a/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs +++ b/backend/src/Data/ToTreeVertexAppliedConversionMethod.cs @@ -24,6 +24,6 @@ string sourceName } public Guid MethodId { get; private set; } = methodId; - public ICollection Arguments { get; private set; } = new List(); + public ICollection Arguments { get; private set; } = []; public string SourceName { get; private set; } = sourceName; } \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index adc6f745..04136ca3 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -18,9 +18,9 @@ public List Calculate(IReadOnlyList dataPoints) transmittanceSum += dataPoint.Results.Transmittance; } - return new List - { + return + [ new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)) - }; + ]; } } \ No newline at end of file diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs index 7a43067c..6980d9c6 100644 --- a/backend/src/Services/MethodCalculationService.cs +++ b/backend/src/Services/MethodCalculationService.cs @@ -9,7 +9,7 @@ namespace Database.Services; public class MethodCalculationService : IMethodCalculationService { - private List _methods = new List(); + private List _methods = []; public void AddMethod(IMethod method) { From 0bcc3bbc05f30c4da3daa6e655aa5a8fb230c446 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 17:12:12 +0200 Subject: [PATCH 334/878] Make non-mapped properties in `DataAccessRigths` usable in queries by make them `Projectable` and seal classes and records --- backend/src/ApiRequests/DataApi.cs | 2 +- backend/src/ApiRequests/DatabaseApi.cs | 2 +- backend/src/ApiRequests/Dto/XDataDto.cs | 2 +- backend/src/ApiRequests/UserApi.cs | 2 +- .../src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/ApplicationDbContext.cs | 4 +- backend/src/Data/DataAccessRights.cs | 43 +- backend/src/Data/DataX.cs | 19 +- backend/src/Data/InstitutionAccessRights.cs | 2 +- backend/src/Database.csproj | 1 + .../DisableFormValueModelBindingAttribute.cs | 2 +- ...GenerateAntiforgeryTokenCookieAttribute.cs | 2 +- .../AddInstitutionAccessRightsPayload.cs | 2 +- .../AccessRights/DataAccessRightsMutations.cs | 8 +- .../AccessRights/DataAccessRightsPayload.cs | 2 +- .../InstitutionAccessRightsError.cs | 2 +- .../InstitutionAccessRightsPayload.cs | 2 +- .../AccessRights/UpdateAccessRightsPayload.cs | 2 +- .../UpdateDataAccessRightsError.cs | 2 +- .../UpdateDataAccessRightsPayload.cs | 2 +- .../src/GraphQl/Approvals/AddApprovalError.cs | 2 +- .../GraphQl/Approvals/AddApprovalPayload.cs | 2 +- .../src/GraphQl/Approvals/ApprovalPayload.cs | 2 +- backend/src/GraphQl/DataX/DataFilterType.cs | 2 +- .../src/GraphQl/DataX/DataFilterTypeBase.cs | 2 +- backend/src/GraphQl/DataX/DataQueries.cs | 4 +- backend/src/GraphQl/DataX/DataSortType.cs | 2 +- backend/src/GraphQl/DataX/DataSortTypeBase.cs | 2 +- backend/src/GraphQl/LocaleType.cs | 2 +- .../GraphQl/MethodAsService/FileInputData.cs | 2 +- .../MethodAsService/MethodAsServiceError.cs | 4 +- .../MethodAsServiceErrorCode.cs | 2 +- .../MethodAsService/MethodAsServiceInput.cs | 2 +- .../MethodAsService/MethodAsServicePayload.cs | 4 +- .../MethodAsService/MethodAsServiceQueries.cs | 4 +- .../MethodAsServiceWithFileInput.cs | 2 +- backend/src/Methods/IMethod.cs | 2 +- backend/src/Methods/TransmittanceSumMethod.cs | 4 +- ...0527144412_AddDataAccessRights.Designer.cs | 2434 +++++++++++++++++ .../20250527144412_AddDataAccessRights.cs | 200 ++ ...25_MoveInstitutionAccessRights.Designer.cs | 2434 +++++++++++++++++ ...50527150325_MoveInstitutionAccessRights.cs | 52 + .../ApplicationDbContextModelSnapshot.cs | 129 +- backend/src/Services/AccessRightsService.cs | 4 +- backend/src/Services/ApiRequestService.cs | 2 +- backend/src/Services/DataService.cs | 2 +- .../src/Services/Interfaces/IEmailSender.cs | 2 +- .../Interfaces/IMethodCalculationService.cs | 2 +- .../src/Services/MethodCalculationService.cs | 4 +- .../src/Services/ResponseApprovalService.cs | 2 +- backend/src/Services/SigningService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- backend/src/Startup.cs | 5 +- 53 files changed, 5327 insertions(+), 100 deletions(-) create mode 100644 backend/src/Migrations/20250527144412_AddDataAccessRights.Designer.cs create mode 100644 backend/src/Migrations/20250527144412_AddDataAccessRights.cs create mode 100644 backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.Designer.cs create mode 100644 backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 30315ef0..2617e388 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -12,7 +12,7 @@ namespace Database.ApiRequests; /// /// Class to request XData from Database API. /// -public class DataApi +public sealed class DataApi { public static readonly string[] CalorimetricDataFileNames = [ diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 4e34d101..d509200d 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -14,7 +14,7 @@ namespace Database.ApiRequests; /// /// Class to request databases from Metabase API. /// -public class DatabaseApi +public sealed class DatabaseApi { private static readonly string[] _databaseFileNames = [ diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs index b84429d2..2aedf057 100644 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -5,7 +5,7 @@ namespace Database.ApiRequests.Dto; -public record DataXDto( +public sealed record DataXDto( string Uuid, DateTime Timestamp, string ComponentId, diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index 11aad111..4242075f 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -12,7 +12,7 @@ namespace Database.ApiRequests; /// /// Class to request user info from Metabase API. /// -public class UserApi +public sealed class UserApi { private static readonly string[] _currentUserFileNames = [ diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 03c453fb..c1504d1f 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -35,7 +35,7 @@ string trustedFileNameForFileStorage // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ -public class FileUploadController( +public sealed class FileUploadController( ILogger logger, ApplicationDbContext context ) : Controller diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 08c55247..1967a142 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -41,7 +41,7 @@ DbContextOptions options public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; public DbSet GeometricData { get; private set; } = default!; - public DbSet AccessRights { get; private set; } = default!; + public DbSet InstitutionAccessRights { get; private set; } = default!; private static void CreateEnumerations(ModelBuilder builder, string schemaName) { @@ -127,6 +127,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureEntity( modelBuilder.Entity() ) - .ToTable("access_rights"); + .ToTable("institution_access_rights"); } } \ No newline at end of file diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index bc835331..67563c64 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -16,6 +17,7 @@ public sealed class DataAccessRights /// If the user ID is mapped to `null`, then this means that the user has unlimited /// access. /// + [Column(TypeName = "jsonb")] public IReadOnlyDictionary? AllowedUserAndQuantity { get; set; } /// @@ -32,49 +34,30 @@ public sealed class DataAccessRights /// Is data access restricted by data access rights. /// [NotMapped] - public bool HasRestrictions - { - get - { - return AllowedUserAndQuantity is not null || - AllowedInstitutions is not null || - AllowedApplications is not null; - } - } + [Projectable] + public bool HasRestrictions => + AllowedUserAndQuantity != null || + AllowedInstitutions != null || + AllowedApplications != null; /// /// Is data access restricted by user. /// [NotMapped] - public bool IsRestrictedByUser - { - get - { - return AllowedUserAndQuantity is not null; - } - } + [Projectable] + public bool IsRestrictedByUser => AllowedUserAndQuantity != null; /// /// Is data access restricted by institution. /// [NotMapped] - public bool IsRestrictedByInstitution - { - get - { - return AllowedInstitutions is not null; - } - } + [Projectable] + public bool IsRestrictedByInstitution => AllowedInstitutions != null; /// /// Is data access restricted by application. /// [NotMapped] - public bool IsRestrictedByApplication - { - get - { - return AllowedApplications is not null; - } - } + [Projectable] + public bool IsRestrictedByApplication => AllowedApplications != null; } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index ae5a2fbf..a9501c76 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -54,7 +54,7 @@ AppliedMethod appliedMethod // this list and the graph must be connected. In other words, the resources must form a tree. public virtual ICollection Resources { get; } = []; - public DataAccessRights DataAccessRights { get; } = new DataAccessRights(); + public DataAccessRights DataAccessRights { get; private set; } = new DataAccessRights(); /// public bool IsRestrictedByApplication(string applicationId) @@ -77,17 +77,14 @@ public bool IsRestrictedByInstitutions(IEnumerable institutions) /// public bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount) { - if (DataAccessRights.AllowedUserAndQuantity is not null) + if (DataAccessRights.AllowedUserAndQuantity is null) { - if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out var limit)) - { - if (limit is null || limit > alreadyAccesedCount) - { - return false; - } - } - return true; + return false; } - return false; + if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out var limit)) + { + return limit is not null && alreadyAccesedCount >= limit; + } + return true; } } \ No newline at end of file diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index da387aeb..ca7aad6d 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -4,7 +4,7 @@ namespace Database.Data; -public class InstitutionAccessRights( +public sealed class InstitutionAccessRights( Guid institutionId, uint? allowedUserCount, uint? allowedDatasetsPerTime, diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index e96c2ee0..260901a7 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -22,6 +22,7 @@ + diff --git a/backend/src/Filters/DisableFormValueModelBindingAttribute.cs b/backend/src/Filters/DisableFormValueModelBindingAttribute.cs index 841254b3..9da0d1c8 100644 --- a/backend/src/Filters/DisableFormValueModelBindingAttribute.cs +++ b/backend/src/Filters/DisableFormValueModelBindingAttribute.cs @@ -5,7 +5,7 @@ namespace Database.Filters; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] -public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter +public sealed class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter { public void OnResourceExecuting(ResourceExecutingContext context) { diff --git a/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs b/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs index e81bd485..e8f5ceed 100644 --- a/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs +++ b/backend/src/Filters/GenerateAntiforgeryTokenCookieAttribute.cs @@ -6,7 +6,7 @@ namespace Database.Filters; -public class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute +public sealed class GenerateAntiforgeryTokenCookieAttribute : ResultFilterAttribute { public const string RequestVerificationTokenName = "RequestVerificationToken"; diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs index 1770bbbf..ac2d1530 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs @@ -1,6 +1,6 @@ namespace Database.GraphQl.AccessRights; -public class AddInstitutionAccessRightsPayload +public sealed class AddInstitutionAccessRightsPayload : InstitutionAccessRightsPayload { public AddInstitutionAccessRightsPayload( diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index d18b4e08..77aef126 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -10,7 +10,7 @@ namespace Database.GraphQl.AccessRights; [ExtendObjectType(nameof(Mutation))] -public class DataAccessRightsMutations +public sealed class DataAccessRightsMutations { public async Task UpdateDataAccessRightsAsync( DataAccessRightsInput input, @@ -105,7 +105,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); if (accessRights is not null) { @@ -123,7 +123,7 @@ CancellationToken cancellationToken input.AllowedUserCount, input.AllowedDatasetsPerTimeSpan, TimeSpan.FromDays(input.PeriodInDays)); - context.AccessRights.Add(accessRights); + context.InstitutionAccessRights.Add(accessRights); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return new AddInstitutionAccessRightsPayload(accessRights); } @@ -164,7 +164,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); if (accessRights is null) { diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs index 0034b3ed..b6389e86 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.AccessRights; -public class DataAccessRightsPayload +public abstract class DataAccessRightsPayload : Payload where TDataAccessRightsError : IUserError { diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs index e28de3e5..e84eb035 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.AccessRights; -public class InstitutionAccessRightsError( +public sealed class InstitutionAccessRightsError( InstitutionAccessRightsErrorCode code, string message, IReadOnlyList path diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs index a9de4969..cd295a5b 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.AccessRights; -public class InstitutionAccessRightsPayload +public abstract class InstitutionAccessRightsPayload : Payload where TInstitutionAccessRightsError : IUserError { diff --git a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs index e3b943df..6566feea 100644 --- a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs @@ -1,6 +1,6 @@ namespace Database.GraphQl.AccessRights; -public class UpdateAccessRightsPayload +public sealed class UpdateAccessRightsPayload : InstitutionAccessRightsPayload { public UpdateAccessRightsPayload( diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs index 67a43a24..9821ca3a 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.AccessRights; -public class UpdateDataAccessRightsError( +public sealed class UpdateDataAccessRightsError( UpdateDataAccessRightsErrorCode code, string message, IReadOnlyList path diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs index 8489e9e6..a864fd16 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.AccessRights; -public class UpdateDataAccessRightsPayload +public sealed class UpdateDataAccessRightsPayload : DataAccessRightsPayload { public UpdateDataAccessRightsPayload( diff --git a/backend/src/GraphQl/Approvals/AddApprovalError.cs b/backend/src/GraphQl/Approvals/AddApprovalError.cs index 2d28b442..f702c8eb 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalError.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalError.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.Approvals; -public class AddApprovalError( +public sealed class AddApprovalError( AddApprovalErrorCode code, string message, IReadOnlyList path diff --git a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs index da65f990..a2541b76 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.Approvals; -public class AddApprovalPayload +public sealed class AddApprovalPayload : ApprovalPayload { public AddApprovalPayload( diff --git a/backend/src/GraphQl/Approvals/ApprovalPayload.cs b/backend/src/GraphQl/Approvals/ApprovalPayload.cs index 276d13b1..0f18fcea 100644 --- a/backend/src/GraphQl/Approvals/ApprovalPayload.cs +++ b/backend/src/GraphQl/Approvals/ApprovalPayload.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.Approvals; -public class ApprovalPayload +public abstract class ApprovalPayload : Payload where TApprovalError : IUserError { diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index d1ffeb54..4705f142 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.DataX; -public class DataFilterType +public sealed class DataFilterType : DataFilterTypeBase { protected override void Configure( diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index 42cf2805..ceddfbcf 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.DataX; -public class DataFilterTypeBase +public abstract class DataFilterTypeBase : FilterInputType where TData : IData { diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 39cc8c93..17080ac8 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -9,7 +9,7 @@ namespace Database.GraphQl.DataX; [ExtendObjectType(nameof(Query))] -public class DataQueries +public sealed class DataQueries { public async Task GetDataAsync( Guid id, diff --git a/backend/src/GraphQl/DataX/DataSortType.cs b/backend/src/GraphQl/DataX/DataSortType.cs index c401ebcf..85b601bb 100644 --- a/backend/src/GraphQl/DataX/DataSortType.cs +++ b/backend/src/GraphQl/DataX/DataSortType.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.DataX; -public class DataSortType +public sealed class DataSortType : DataSortTypeBase { protected override void Configure( diff --git a/backend/src/GraphQl/DataX/DataSortTypeBase.cs b/backend/src/GraphQl/DataX/DataSortTypeBase.cs index 36ea1080..39d060c5 100644 --- a/backend/src/GraphQl/DataX/DataSortTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.DataX; -public class DataSortTypeBase +public abstract class DataSortTypeBase : SortInputType where TData : IData { diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs index 7dfb9a6b..58564a0f 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/LocaleType.cs @@ -23,7 +23,7 @@ namespace Database.GraphQl; /// /// Initializes a new instance of the class. /// -public class LocaleType( +public sealed class LocaleType( string name, string? description = null, BindingBehavior bind = BindingBehavior.Explicit) diff --git a/backend/src/GraphQl/MethodAsService/FileInputData.cs b/backend/src/GraphQl/MethodAsService/FileInputData.cs index bc3746a5..56f0cc5e 100644 --- a/backend/src/GraphQl/MethodAsService/FileInputData.cs +++ b/backend/src/GraphQl/MethodAsService/FileInputData.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.MethodAsService; diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs index e6d497b5..4ab42d43 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs @@ -1,9 +1,9 @@ -using Database.GraphQl.GeometricDataX; +using Database.GraphQl.GeometricDataX; using System.Collections.Generic; namespace Database.GraphQl.MethodAsService; -public class MethodAsServiceError( +public sealed class MethodAsServiceError( MethodAsServiceErrorCode code, string message, IReadOnlyList path diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs index aa1ac41a..8509c7e3 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs @@ -1,4 +1,4 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.MethodAsService; diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs index f4e932e4..a8f001b0 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Database.GraphQl.MethodAsService; diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs index fa53996a..a19cf2a7 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Database.GraphQl.MethodAsService; -public class MethodAsServicePayload +public sealed class MethodAsServicePayload : Payload { public MethodAsServicePayload( diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index d8c1ee45..b79e7a91 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Linq; using System.Net.Http; @@ -18,7 +18,7 @@ namespace Database.GraphQl.MethodAsService; [ExtendObjectType(nameof(Query))] -public class MethodAsServiceQueries +public sealed class MethodAsServiceQueries { private sealed record DataXResponse(DataXDto DataX); diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs index 238da3c2..0a636f94 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs @@ -1,4 +1,4 @@ -using HotChocolate.Types; +using HotChocolate.Types; using HotChocolate; using System; diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 8b4781e8..8b6e064e 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Database.GraphQl.MethodAsService; diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 04136ca3..8626f8b2 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections.Generic; using Database.GraphQl.MethodAsService; namespace Database.Methods; -public class TransmittanceSumMethod : IMethod +public sealed class TransmittanceSumMethod : IMethod { public string Name => "TransmittanceSum"; diff --git a/backend/src/Migrations/20250527144412_AddDataAccessRights.Designer.cs b/backend/src/Migrations/20250527144412_AddDataAccessRights.Designer.cs new file mode 100644 index 00000000..b9543096 --- /dev/null +++ b/backend/src/Migrations/20250527144412_AddDataAccessRights.Designer.cs @@ -0,0 +1,2434 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250527144412_AddDataAccessRights")] + partial class AddDataAccessRights + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250527144412_AddDataAccessRights.cs b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs new file mode 100644 index 00000000..f978e818 --- /dev/null +++ b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddDataAccessRights : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data"); + } + } +} diff --git a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.Designer.cs b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.Designer.cs new file mode 100644 index 00000000..c1447676 --- /dev/null +++ b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.Designer.cs @@ -0,0 +1,2434 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250527150325_MoveInstitutionAccessRights")] + partial class MoveInstitutionAccessRights + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("ArXiv") + .HasColumnType("text"); + + b2.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b2.Property("Doi") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Urn") + .HasColumnType("text"); + + b2.Property("WebAddress") + .HasColumnType("text"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + }); + + b1.OwnsOne("Database.Data.Standard", "Standard", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Abstract") + .HasColumnType("text"); + + b2.Property("Locator") + .HasColumnType("text"); + + b2.Property("Section") + .HasColumnType("text"); + + b2.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b2.Property("Title") + .HasColumnType("text"); + + b2.Property("Year") + .HasColumnType("integer"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + { + b3.Property("StandardDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("StandardDataApprovalId") + .HasColumnType("integer"); + + b3.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b3.Property("Prefix") + .HasColumnType("text"); + + b3.Property("Suffix") + .HasColumnType("text"); + + b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + }); + + b2.Navigation("Numeration") + .IsRequired(); + }); + + b1.Navigation("Publication"); + + b1.Navigation("Standard"); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs new file mode 100644 index 00000000..ad0a81a0 --- /dev/null +++ b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs @@ -0,0 +1,52 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MoveInstitutionAccessRights : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_access_rights", + schema: "database", + table: "access_rights"); + + migrationBuilder.RenameTable( + name: "access_rights", + schema: "database", + newName: "institution_access_rights", + newSchema: "database"); + + migrationBuilder.AddPrimaryKey( + name: "PK_institution_access_rights", + schema: "database", + table: "institution_access_rights", + column: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_institution_access_rights", + schema: "database", + table: "institution_access_rights"); + + migrationBuilder.RenameTable( + name: "institution_access_rights", + schema: "database", + newName: "access_rights", + newSchema: "database"); + + migrationBuilder.AddPrimaryKey( + name: "PK_access_rights", + schema: "database", + table: "access_rights", + column: "Id"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index fc98eb1a..84869e23 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.2") + .HasAnnotation("ProductVersion", "9.0.5") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); @@ -256,7 +256,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.ToTable("access_rights", "database"); + b.ToTable("institution_access_rights", "database"); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -911,12 +911,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Standard"); }); + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); }); modelBuilder.Entity("Database.Data.GeometricData", b => @@ -1203,12 +1228,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Standard"); }); + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); }); modelBuilder.Entity("Database.Data.GetHttpsResource", b => @@ -1613,12 +1663,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Standard"); }); + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -1933,6 +2008,28 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Standard"); }); + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + b.Navigation("AppliedMethod") .IsRequired(); @@ -1941,6 +2038,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Approvals"); b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => @@ -2227,12 +2327,37 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Navigation("Standard"); }); + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); }); modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 157a3444..8d328156 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -19,7 +19,7 @@ namespace Database.Services; /// /// /// -public class AccessRightsService( +public sealed class AccessRightsService( ApplicationDbContext context, IUserService userService, ICacheService cacheService, @@ -41,7 +41,7 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, foreach (var institution in currentUser.RepresentedInstitutions.Edges) { institutions.Add(institution.Node.Uuid); - var accessRightOfInstitution = await context.AccessRights.FirstOrDefaultAsync(x => x.InstitutionId == institution.Node.Uuid, cancellationToken).ConfigureAwait(false); + var accessRightOfInstitution = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == institution.Node.Uuid, cancellationToken).ConfigureAwait(false); if (accessRightOfInstitution is not null) { accessRights.Add(accessRightOfInstitution); diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 5cf879a5..84c99963 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -18,7 +18,7 @@ namespace Database.Services; /// /// Implementation of /// -public class ApiRequestService() : IApiRequestService +public sealed class ApiRequestService() : IApiRequestService { /// /// Name of Metabase http client. diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index d3b8de3c..1ccf0a25 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -11,7 +11,7 @@ namespace Database.Services; /// /// Implementation of /// -public class DataService() : IDataService +public sealed class DataService() : IDataService { /// public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) diff --git a/backend/src/Services/Interfaces/IEmailSender.cs b/backend/src/Services/Interfaces/IEmailSender.cs index d0c480d6..eb1ecaf2 100644 --- a/backend/src/Services/Interfaces/IEmailSender.cs +++ b/backend/src/Services/Interfaces/IEmailSender.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; namespace Database.Services.Interfaces; diff --git a/backend/src/Services/Interfaces/IMethodCalculationService.cs b/backend/src/Services/Interfaces/IMethodCalculationService.cs index bb6f06bc..301ba091 100644 --- a/backend/src/Services/Interfaces/IMethodCalculationService.cs +++ b/backend/src/Services/Interfaces/IMethodCalculationService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Database.GraphQl.MethodAsService; using Database.Methods; diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs index 6980d9c6..ecc2f71c 100644 --- a/backend/src/Services/MethodCalculationService.cs +++ b/backend/src/Services/MethodCalculationService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Database.GraphQl.MethodAsService; @@ -7,7 +7,7 @@ namespace Database.Services; -public class MethodCalculationService : IMethodCalculationService +public sealed class MethodCalculationService : IMethodCalculationService { private List _methods = []; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 7d8176ac..d593bc0a 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -21,7 +21,7 @@ namespace Database.Services; /// /// /// -public class ResponseApprovalService( +public sealed class ResponseApprovalService( AppSettings appSettings, ISigningService signingService, IApiRequestService apiRequestService, diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 38fd46ac..b904d7c4 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -13,7 +13,7 @@ namespace Database.Services; /// /// /// Instance of -public class SigningService( +public sealed class SigningService( AppSettings appSettings, ILogger logger) : ISigningService { diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 37cbfdd2..026bab46 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -21,7 +21,7 @@ namespace Database.Services; /// /// to store already known users. /// Instance of -public class UserService( +public sealed class UserService( AppSettings appSettings, IApiRequestService apiRequestService, IHttpContextAccessor httpContextAccessor, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7f764b82..9305cc82 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -176,10 +176,11 @@ AppSettings appSettings .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) - // .UseNodaTime(); + // .UseNodaTime(); ) .UseSchemaName(appSettings.Database.SchemaName) - .UseOpenIddict(); + .UseOpenIddict() + .UseProjectables(); if (!environment.IsProduction()) { options From 98ca7c9ff71720b7436f47e822c191c28ba7ddbc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 18:03:36 +0200 Subject: [PATCH 335/878] Verify expiration date only if it is not null (and assume the token is valid if the date is null) --- .../Extensions/HttpContextAccessorExtensions.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 67326111..53d4f8f9 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -20,18 +20,24 @@ public static class HttpContextAccessorExtensions /// Bearer token or null. public static async Task ExtractBearerToken(this IHttpContextAccessor httpContextAccessor) { - if (httpContextAccessor.HttpContext is null) return null; + if (httpContextAccessor.HttpContext is null) + { + return null; + } var token = await httpContextAccessor.HttpContext.GetTokenAsync( CookieAuthenticationDefaults.AuthenticationScheme, OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken); var expirationDate = await httpContextAccessor.HttpContext.GetTokenAsync( CookieAuthenticationDefaults.AuthenticationScheme, OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate); - - return !string.IsNullOrEmpty(token) && !string.IsNullOrEmpty(expirationDate) && CheckExpirationDate(expirationDate) ? token : null; + if (expirationDate is null) + { + return token; + } + return LiesInTheFuture(expirationDate) ? token : null; } - private static bool CheckExpirationDate(string expirationDate) + private static bool LiesInTheFuture(string expirationDate) { return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate, CultureInfo.InvariantCulture)) < DateTime.UtcNow; } From 0aaea908468342adbbe824e6a04716f4f38a8e0f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 18:04:29 +0200 Subject: [PATCH 336/878] Use curly braces around in if statement bodies and make sure that constructed URIs are absolute --- backend/src/ApiRequests/DataApi.cs | 5 +++- backend/src/ApiRequests/DatabaseApi.cs | 22 ++++++++++++++++ backend/src/ApiRequests/UserApi.cs | 2 +- .../src/Configuration/AuthConfiguration.cs | 2 ++ .../src/Configuration/GraphQlConfiguration.cs | 3 +++ .../Controllers/AuthenticationController.cs | 5 ++++ .../src/Controllers/FileUploadController.cs | 7 +++++- .../GraphQl/Approvals/ApprovalMutations.cs | 2 ++ .../CalorimetricDataMutations.cs | 3 +++ .../GeometricDataX/GeometricDataMutations.cs | 2 ++ .../GetHttpsResourceMutations.cs | 3 +++ .../GetHttpsResourceResolvers.cs | 15 +++++++++++ .../HygrothermalDataMutations.cs | 3 +++ .../GraphQl/LoggingDiagnosticEventListener.cs | 2 ++ .../OpticalDataX/OpticalDataMutations.cs | 3 +++ backend/src/GraphQl/Users/UserQueries.cs | 6 ++++- backend/src/Services/ApiRequestService.cs | 20 ++++++++------- backend/src/Utilities/FileHelpers.cs | 25 ++++++++++++++++--- .../src/Utilities/MultipartRequestHelper.cs | 7 +++++- 19 files changed, 120 insertions(+), 17 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 2617e388..69f8e800 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -135,7 +135,10 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), new { uuid = dataId }, "" ); - if (request.Query is null) throw new Exception("Failed to construct GraphQL query."); + if (request.Query is null) + { + throw new Exception("Failed to construct GraphQL query."); + } var responseObject = await apiRequestService.Database().QueryGraphQlFromUrl( appSettings, diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index d509200d..31d81e50 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -72,6 +72,7 @@ public static async Task RequestDatabase( ).ConfigureAwait(false); if (response is null) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_RESPONSE") @@ -79,7 +80,10 @@ public static async Task RequestDatabase( .SetMessage("Response is null.") .Build() ); + } + if (response.Data.Databases.Edges is null) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_EDGES") @@ -87,7 +91,10 @@ public static async Task RequestDatabase( .SetMessage("The supposed list of databases is null.") .Build() ); + } + if (response.Data.Databases.Edges.Count == 0) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NO_DATABASE") @@ -95,7 +102,10 @@ public static async Task RequestDatabase( .SetMessage("The list of databases is empty.") .Build() ); + } + if (response.Data.Databases.Edges.Count >= 2) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("AMBIGUOUS_DATABASE") @@ -103,6 +113,7 @@ public static async Task RequestDatabase( .SetMessage("The list of databases has more than one entry.") .Build() ); + } return response.Data.Databases.Edges[0].Node; } @@ -149,6 +160,7 @@ public static async Task RequestDatabaseById( ).ConfigureAwait(false); if (response is null) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_RESPONSE") @@ -156,7 +168,10 @@ public static async Task RequestDatabaseById( .SetMessage("Response is null.") .Build() ); + } + if (response.Data.Databases.Edges is null) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NULL_EDGES") @@ -164,7 +179,10 @@ public static async Task RequestDatabaseById( .SetMessage("The supposed list of databases is null.") .Build() ); + } + if (response.Data.Databases.Edges.Count == 0) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("NO_DATABASE") @@ -172,7 +190,10 @@ public static async Task RequestDatabaseById( .SetMessage("The list of databases is empty.") .Build() ); + } + if (response.Data.Databases.Edges.Count >= 2) + { throw new GraphQLException( ErrorBuilder.New() .SetCode("AMBIGUOUS_DATABASE") @@ -180,6 +201,7 @@ public static async Task RequestDatabaseById( .SetMessage("The list of databases has more than one entry.") .Build() ); + } return response.Data.Databases.Edges[0].Node; } diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index 4242075f..daf2f14b 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -68,7 +68,7 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - var uri = new Uri(new Uri(appSettings.MetabaseHost), "/connect/userinfo"); + var uri = new Uri(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"); return await apiRequestService.Metabase().QueryRest( uri, diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 6b13275d..a5467de8 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -109,8 +109,10 @@ IWebHostEnvironment environment _.MaxConcurrency = 10 ); if (environment.IsEnvironment(Program.TestEnvironment)) + { // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 _.SchedulerName = Guid.NewGuid().ToString(); + } }); // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. services.AddQuartzHostedService(_ => diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b52aee15..8972dec5 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -51,6 +51,7 @@ AppSettings appSettings _ => _.BaseAddress = new Uri($"{appSettings.Host}/graphql") ); if (!environment.IsProduction()) + { httpMetabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { @@ -59,6 +60,8 @@ AppSettings appSettings HttpClientHandler.DangerousAcceptAnyServerCertificateValidator } ); + } + httpDatabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 29ae44be..844fa7e0 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -212,14 +212,19 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or cancellationToken ); if (user is null) + { _context.Users.Add( new User( subject, name ) ); + } else + { user.Update(name); + } + await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); // Ask the cookie authentication handler to return a new cookie and redirect // the user agent to the return URL stored in the authentication properties. diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index c1504d1f..9beb2816 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -177,7 +177,10 @@ CancellationToken cancellationToken _fileSizeLimit ).ConfigureAwait(false); - if (!ModelState.IsValid) return BadRequest(ModelState); + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } using var targetStream = System.IO.File.Create( Path.Combine(_targetDirectoryPath, trustedFileNameForFileStorage)); @@ -207,8 +210,10 @@ private static Encoding GetEncoding(MultipartSection section) mediaType?.Encoding ?? throw new ArgumentException("Impossible (because `hasMediaTypeHeader` is `true`)!")) ) + { #pragma warning restore SYSLIB0001 return Encoding.UTF8; + } return mediaType.Encoding; } diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index 3cefcb76..5db06410 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -40,6 +40,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new AddApprovalPayload( new AddApprovalError( AddApprovalErrorCode.UNAUTHORIZED, @@ -47,6 +48,7 @@ CancellationToken cancellationToken [] ) ); + } var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); if (data is null) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 3a667513..876146f3 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -41,6 +41,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( CreateCalorimetricDataErrorCode.UNAUTHORIZED, @@ -48,6 +49,8 @@ CancellationToken cancellationToken [] ) ); + } + var calorimetricData = new CalorimetricData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 1a391fb5..e9a23766 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -41,6 +41,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new CreateGeometricDataPayload( new CreateGeometricDataError( CreateGeometricDataErrorCode.UNAUTHORIZED, @@ -48,6 +49,7 @@ CancellationToken cancellationToken [] ) ); + } var geometricData = new GeometricData( input.Locale, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index c94be37d..9dee91a7 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -62,6 +62,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( CreateGetHttpsResourceErrorCode.UNAUTHORIZED, @@ -69,6 +70,8 @@ CancellationToken cancellationToken [] ) ); + } + var getHttpsResource = new GetHttpsResource( input.Description, input.HashValue, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 02c78b07..cd14d57e 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -24,30 +24,45 @@ CancellationToken cancellationToken ) { if (getHttpsResource.CalorimetricDataId is not null) + { return await calorimetricDataById.LoadAsync( getHttpsResource.CalorimetricDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + } + if (getHttpsResource.HygrothermalDataId is not null) + { return await hygrothermalDataById.LoadAsync( getHttpsResource.HygrothermalDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + } + if (getHttpsResource.OpticalDataId is not null) + { return await opticalDataById.LoadAsync( getHttpsResource.OpticalDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + } + if (getHttpsResource.PhotovoltaicDataId is not null) + { return await photovoltaicDataById.LoadAsync( getHttpsResource.PhotovoltaicDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + } + if (getHttpsResource.GeometricDataId is not null) + { return await geometricDataById.LoadAsync( getHttpsResource.GeometricDataId ?? throw new ArgumentException("Impossible!"), cancellationToken ).ConfigureAwait(false); + } + return null; } diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index f159deb2..8ab1a66d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -42,6 +42,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( CreateHygrothermalDataErrorCode.UNAUTHORIZED, @@ -49,6 +50,8 @@ CancellationToken cancellationToken [] ) ); + } + var hygrothermalData = new HygrothermalData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index f192ba02..8f4a80ab 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -156,7 +156,9 @@ IReadOnlyList errors ) { foreach (var error in errors) + { _logger.FailedValidation(error.Exception, context.Request.Document, ConvertErrorToString(error)); + } } private static string ConvertErrorToString( diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 34abde78..8628eacb 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -42,6 +42,7 @@ CancellationToken cancellationToken cancellationToken ) ) + { return new CreateOpticalDataPayload( new CreateOpticalDataError( CreateOpticalDataErrorCode.UNAUTHORIZED, @@ -49,6 +50,8 @@ CancellationToken cancellationToken [] ) ); + } + var opticalData = new OpticalData( input.Locale, input.ComponentId, diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 5e52a10a..76785945 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -26,7 +26,11 @@ public sealed class UserQueries CancellationToken cancellationToken ) { - if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) return null; + if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) + { + return null; + } + return await context.Users.AsNoTracking() .SingleOrDefaultAsync( diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 84c99963..25afb1ea 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -30,19 +30,19 @@ public sealed class ApiRequestService() : IApiRequestService /// public const string DatabaseHttpClient = "Database"; - private static bool _useMetabase = true; + private static bool s_useMetabase = true; /// public ApiRequestService Metabase() { - _useMetabase = true; + s_useMetabase = true; return this; } /// public ApiRequestService Database() { - _useMetabase = false; + s_useMetabase = false; return this; } @@ -75,7 +75,7 @@ CancellationToken cancellationToken HttpMethod.Post, // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(_useMetabase ? appSettings.MetabaseHost : appSettings.Host), "/graphql/"), + new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), MakeJsonHttpContent(request), JsonSerializerSettings.GraphQL, httpClientFactory, @@ -139,7 +139,10 @@ CancellationToken cancellationToken ) where TResponse : class { - using var httpClient = _useMetabase ? httpClientFactory.CreateClient(MetabaseHttpClient) : httpClientFactory.CreateClient(DatabaseHttpClient); + using var httpClient = + s_useMetabase + ? httpClientFactory.CreateClient(MetabaseHttpClient) + : httpClientFactory.CreateClient(DatabaseHttpClient); var bearerToken = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); using var httpRequestMessage = new HttpRequestMessage( httpMethod, @@ -153,9 +156,11 @@ CancellationToken cancellationToken using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { throw new HttpRequestException( $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); + } // We could use // `httpResponseMessage.Content.ReadFromJsonAsync>` which @@ -170,10 +175,7 @@ CancellationToken cancellationToken responseStream, serializerOptions, cancellationToken - ).ConfigureAwait(false); - - if (deserializedResponse is null) throw new JsonException("Failed to deserialize the GraphQL response."); - + ).ConfigureAwait(false) ?? throw new JsonException("Failed to deserialize the GraphQL response."); return deserializedResponse; } diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 73f48fc4..0403af11 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -78,9 +78,13 @@ public static async Task ProcessFormFile(IFormFile formFile, formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); if (property is not null) + { if (property.GetCustomAttribute() is DisplayAttribute displayAttribute) + { fieldDisplayName = $"{displayAttribute.Name} "; + } + } // Don't trust the file name sent by the client. To display // the file name, HTML-encode the value. @@ -116,17 +120,23 @@ public static async Task ProcessFormFile(IFormFile formFile, // content was a BOM and the content is actually // empty after removing the BOM. if (memoryStream.Length == 0) + { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); + } if (!IsValidFileExtensionAndSignature( formFile.FileName, memoryStream, permittedExtensions)) + { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) file " + "type isn't permitted or the file's signature " + "doesn't match the file's extension."); + } else + { return memoryStream.ToArray(); + } } catch (Exception ex) { @@ -185,11 +195,17 @@ public static async Task ProcessStreamedFile( private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) { - if (string.IsNullOrEmpty(fileName) || data is null || data.Length == 0) return false; + if (string.IsNullOrEmpty(fileName) || data is null || data.Length == 0) + { + return false; + } var ext = Path.GetExtension(fileName).ToLowerInvariant(); - if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) return false; + if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) + { + return false; + } data.Position = 0; @@ -232,7 +248,10 @@ private static bool IsValidFileExtensionAndSignature(string? fileName, Stream da // for files (when possible) for all file types you intend // to allow on the system and perform the file signature // check. - if (!_fileSignature.TryGetValue(ext, out var value)) return true; + if (!_fileSignature.TryGetValue(ext, out var value)) + { + return true; + } // File signature check // -------------------- diff --git a/backend/src/Utilities/MultipartRequestHelper.cs b/backend/src/Utilities/MultipartRequestHelper.cs index 711f84c2..88a79f7f 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -12,11 +12,16 @@ public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLim { var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value; - if (string.IsNullOrWhiteSpace(boundary)) throw new InvalidDataException("Missing content-type boundary."); + if (string.IsNullOrWhiteSpace(boundary)) + { + throw new InvalidDataException("Missing content-type boundary."); + } if (boundary.Length > lengthLimit) + { throw new InvalidDataException( $"Multipart boundary length limit {lengthLimit} exceeded."); + } return boundary; } From 145ed501dab91c4d1e128c51b14d2225f9e3b338 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 18:34:56 +0200 Subject: [PATCH 337/878] Preserve backchannel access token expiration date to be used for token validation --- backend/src/Controllers/AuthenticationController.cs | 2 +- backend/src/Extensions/HttpContextAccessorExtensions.cs | 8 ++------ backend/src/Services/AccessRightsService.cs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 844fa7e0..8bf3c711 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -191,7 +191,7 @@ public async Task LogInCallback(string provider, CancellationToken properties.StoreTokens(result.Properties.GetTokens().Where(token => token.Name is // Preserve the access, identity and refresh tokens returned in the token response, if available. OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken or + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate or OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken )); } diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index 53d4f8f9..e8334990 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -30,15 +30,11 @@ public static class HttpContextAccessorExtensions var expirationDate = await httpContextAccessor.HttpContext.GetTokenAsync( CookieAuthenticationDefaults.AuthenticationScheme, OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate); - if (expirationDate is null) - { - return token; - } - return LiesInTheFuture(expirationDate) ? token : null; + return expirationDate is not null && LiesInTheFuture(expirationDate) ? token : null; } private static bool LiesInTheFuture(string expirationDate) { - return DateTime.UnixEpoch.AddSeconds(int.Parse(expirationDate, CultureInfo.InvariantCulture)) < DateTime.UtcNow; + return DateTime.Parse(expirationDate, CultureInfo.InvariantCulture) < DateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 8d328156..f9a85ef1 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -59,7 +59,7 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { - var result = await ApplyAccessRightsOnData((new List { data }).AsQueryable(), cancellationToken).ConfigureAwait(false); + var result = await ApplyAccessRightsOnData(new List { data }.AsQueryable(), cancellationToken).ConfigureAwait(false); return result.FirstOrDefault(); } From 548abc751f8b420c40535e8095185d67e4145281 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 18:46:18 +0200 Subject: [PATCH 338/878] Fix future check --- backend/src/Extensions/HttpContextAccessorExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs index e8334990..76fb9c59 100644 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ b/backend/src/Extensions/HttpContextAccessorExtensions.cs @@ -35,6 +35,6 @@ public static class HttpContextAccessorExtensions private static bool LiesInTheFuture(string expirationDate) { - return DateTime.Parse(expirationDate, CultureInfo.InvariantCulture) < DateTime.UtcNow; + return DateTime.Parse(expirationDate, CultureInfo.InvariantCulture) > DateTime.UtcNow; } } \ No newline at end of file From f2346c82312715b7f9fd00f67aff715593527ec9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 May 2025 19:13:02 +0200 Subject: [PATCH 339/878] Correct file extension --- backend/src/ApiRequests/DataApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 69f8e800..57d4e315 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -16,7 +16,7 @@ public sealed class DataApi { public static readonly string[] CalorimetricDataFileNames = [ - "DataFields.grapql", + "DataFields.graphql", "CalorimetricDataFields.graphql", "CalorimetricData.graphql" ]; From dde8a488271962e4baea562c9ce514c24a647f51 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 13:16:03 +0200 Subject: [PATCH 340/878] Begin relative paths with `./` to make it explicit --- backend/src/Services/SigningService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index b904d7c4..3abf5adb 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -27,7 +27,7 @@ public sealed class SigningService( public async Task Initialize() { // Execute command to import private key - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ../gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); + var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ./gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { // Extract fingerprint From a3419ffab424bf586ebc5042c7ea3d5136890d87 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 13:43:53 +0200 Subject: [PATCH 341/878] Extract and use client ID from login callback --- it is called `AuthorizedParty` and has the key `azp` and is not to be confused with `Audience` --- backend/src/Controllers/AuthenticationController.cs | 7 +++++++ backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 8bf3c711..30a11a06 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -169,10 +169,17 @@ public async Task LogInCallback(string provider, CancellationToken // The claims are fetched from the userinfo endpoint of the authorization provider. // We add the claim `IdentityOptions.ClaimsIdentity.UserIdClaimType` to make // `UserManager.GetUserAsync(claimsPrincipal)` return the authenticated user. + // You can use the debugger to see which claims are present. Just now it were: + // iss, exp, iat, aud, sub, role, name, preferred_username, email, azp, nonce, at_hash, email_verified, + // phone_number_verified, as, oi_reg_id, http://schemas.xmlsoat.org/.../emailaddress, + // http://schemas.xmlsoat.org/.../name, http://schemas.xmlsoat.org/.../nameidentifier identity.SetClaim(_identityOptions.ClaimsIdentity.UserIdClaimType, result.Principal.GetClaim(ClaimTypes.NameIdentifier)) .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name)) .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier)); + // Preserve `client_id` for usage by access-rights service. + identity.SetClaim(Claims.AuthorizedParty, result.Principal.GetClaim(Claims.AuthorizedParty)); + // Preserve the registration details to be able to resolve them later. identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId)) .SetClaim(Claims.Private.ProviderName, result.Principal.GetClaim(Claims.Private.ProviderName)); diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index f9a85ef1..35455e7a 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -34,7 +34,7 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, var institutions = new List(); var accessRights = new List(); - var applicationId = userService.GetApplicationIdFromUser() ?? throw new InvalidOperationException("Application Id could not be aquired."); + var applicationId = userService.GetApplicationIdFromUser() ?? throw new InvalidOperationException("Application identifier could not be acquired."); var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException("Could not get current user!"); var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 026bab46..7213a3b7 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -32,7 +32,7 @@ public sealed class UserService( /// public string? GetApplicationIdFromUser() { - return httpContextAccessor.HttpContext?.User.GetClaim(Claims.ClientId); + return httpContextAccessor.HttpContext?.User.GetClaim(Claims.AuthorizedParty); } /// From 5494d09b834455554055ea7f6e5c97dd1a395ed3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 13:44:09 +0200 Subject: [PATCH 342/878] Prefix relative paths with `./` to make it explicit --- backend/src/Services/ApiRequestService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 25afb1ea..4cad2978 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -55,7 +55,7 @@ string[] fileNames Environment.NewLine, await Task.WhenAll( fileNames.Select(fileName => - File.ReadAllTextAsync($"ApiRequests/Queries/{fileName}") + File.ReadAllTextAsync($"./ApiRequests/Queries/{fileName}") ) ).ConfigureAwait(false) ); From 01e26d03507dcfd84c261242fef0a5793b2ce07e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 16:15:09 +0200 Subject: [PATCH 343/878] Automate the creation and export of GnuPG keys --- Makefile | 17 ++++++++++++++++- README.md | 16 +++++----------- backend/Makefile | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 4aac3ef4..7a24f0b4 100644 --- a/Makefile +++ b/Makefile @@ -229,8 +229,23 @@ gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` --passphrase ${GNUPG_PRIVATEKEY_PASSPHRASE} \ "${NAME} (${COMMENT}) <${EMAIL}>" \ ed25519 \ - cert \ + sign \ never + fingerprint=gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + "${NAME} (${COMMENT}) <${EMAIL}>" \ + | awk -F: '$$1=="fpr" {print $$10; exit}' + mkdir --parents \ + ./backend/src/gpg-keys + gpg \ + --armor \ + --export-secret-keys $$fingerprint \ + > ./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} # --------------------- # # Generate Certificates # diff --git a/README.md b/README.md index e3fe0af5..21abbfa3 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,11 @@ If you have a question for which you don't find the answer in this repository, p generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `make jwt-certificates`. -1. Generate a GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` - given in the `.env` file by running `make NAME=${name} COMMENT=${comment} - EMAIL=${email} gpg` with your information filled in. List the secret keys - with their identifiers by running `gpg --list-secret-keys - --keyid-format=long` and copy the identifier of the new secret key to the - clipboard. Export the secret key to - `./backend/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` by running - `mkdir --parents ./backend/gpg-keys && gpg --armor --export-secret-keys ${key_id} > ./backend/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` - with `${key_id}` set to the key identifier and - `${GNUPG_PRIVATEKEY_FILE_NAME}` replaced by the value given in the `.env` - file. +1. Generate and export a GnuPG key with the passphrase + `${GNUPG_PRIVATEKEY_PASSPHRASE}` to the file + `./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` + by running `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with + your information filled in. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see diff --git a/backend/Makefile b/backend/Makefile index 2c30a322..ae8171e3 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -219,3 +219,20 @@ switch-migrations : ## Turn current into previous and new into current mv ./src/Migrations/current ./src/Migrations/previous mv ./src/Migrations/new ./src/Migrations/current .PHONY : switch-migrations + +list-keys : ## List GnuPG keys + gpg --list-keys +.PHONY : list-keys + +delete-key : ## Delete GnuPG key `${FINGERPRINT}` + gpg \ + --pinentry-mode loopback \ + --batch \ + --yes \ + --delete-secret-keys ${FINGERPRINT} + gpg \ + --pinentry-mode loopback \ + --batch \ + --yes \ + --delete-key ${FINGERPRINT} +.PHONY : delete-key From 42c1347d8f7b525125c30a1753dc7ac697bfe7b8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 16:26:12 +0200 Subject: [PATCH 344/878] Exclude newline from awk output --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7a24f0b4..a5553665 100644 --- a/Makefile +++ b/Makefile @@ -239,7 +239,7 @@ gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` | grep \ --before=3 \ "${NAME} (${COMMENT}) <${EMAIL}>" \ - | awk -F: '$$1=="fpr" {print $$10; exit}' + | awk -F: '$$1=="fpr" {printf $$10; exit}' mkdir --parents \ ./backend/src/gpg-keys gpg \ From 374b84bcaf4861407739915719d19216bc80d423 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 May 2025 16:28:46 +0200 Subject: [PATCH 345/878] Make signing service work properly --- extract fingerprint reliably, include GnuPG key file in compilation for production, do not have empty fingerprints when extraction failed --- backend/.gitignore | 8 ++--- backend/Dockerfile | 9 ----- backend/src/Database.csproj | 6 ++++ .../OpticalDataX/OpticalDataMutations.cs | 2 -- .../Services/Interfaces/ISigningService.cs | 13 +++---- .../src/Services/ResponseApprovalService.cs | 2 +- backend/src/Services/SigningService.cs | 36 ++++++++----------- 7 files changed, 32 insertions(+), 44 deletions(-) diff --git a/backend/.gitignore b/backend/.gitignore index 044fd6ef..1f38ce67 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -2,15 +2,15 @@ /src/tempkey.rsa /src/wwwroot/assets/ /src/files/ +/src/gpg-keys/ /trace.* /.secrets /.mono -/secrets -/ssl -/gpg-keys +/secrets/ +/ssl/ -/test/**/__snapshots__/__mismatch__ +/test/**/__snapshots__/__mismatch__/ # Created by https://www.gitignore.io/api/csharp # Edit at https://www.gitignore.io/?templates=csharp diff --git a/backend/Dockerfile b/backend/Dockerfile index 2595d7ff..2af24b78 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -146,15 +146,6 @@ COPY \ ./test/Database.Tests.csproj \ ./test/ -# GnuPG -# Copy keys to image -RUN \ - mkdir ./gpg-keys -COPY \ - --chown=me:us \ - ./gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} \ - ./gpg-keys/ - # Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) RUN \ export GPG_TTY=$(tty) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 260901a7..9c8566fa 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -70,4 +70,10 @@ PreserveNewest + + + + PreserveNewest + + diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 8628eacb..5c0e77c7 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -5,10 +5,8 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.GraphQl.GeometricDataX; using Database.Services.Interfaces; using HotChocolate.Types; -using Org.BouncyCastle.Asn1.X509.Qualified; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/Services/Interfaces/ISigningService.cs b/backend/src/Services/Interfaces/ISigningService.cs index bce4c5ab..6e8b4d90 100644 --- a/backend/src/Services/Interfaces/ISigningService.cs +++ b/backend/src/Services/Interfaces/ISigningService.cs @@ -8,12 +8,6 @@ namespace Database.Services.Interfaces; /// public interface ISigningService { - /// - /// Get fingerprint - /// - /// Fingerprint as string - public string Fingerprint { get; } - /// /// Initialize service by importing private key from file passed into container. /// @@ -27,4 +21,11 @@ public interface ISigningService /// Data string to create signature from. /// True and generated signature, if successful. Otherwise false and error. public Task<(bool Success, string Output)> SignData(string data); + + /// + /// Get fingerprint. + /// + /// The fingerprint. + /// If fingerprint extraction failed. + public Task GetFingerprint(); } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index d593bc0a..a464fe79 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -51,7 +51,7 @@ public async Task CreateResponseApproval(IData dataObject, Can throw new InvalidOperationException($"Signing of data failed! {signatureResult.Output}"); } - return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, signingService.Fingerprint, queryAdnResponse.Query, queryAdnResponse.Response); + return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, await signingService.GetFingerprint(), queryAdnResponse.Query, queryAdnResponse.Response); } private async Task<(string Query, string Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 3abf5adb..288dd59e 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -20,26 +20,22 @@ public sealed class SigningService( private const string FILENAME = "dataToSign"; private const string FILE_EXTENSION = ".asc"; - /// - public string Fingerprint { get; private set; } = ""; - /// public async Task Initialize() { // Execute command to import private key - var (success, outout) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ./gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); + var (success, output) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ./gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); if (success) { - // Extract fingerprint - Fingerprint = await GetFingerprintFromKeyList(); - if (!String.IsNullOrEmpty(Fingerprint)) + var fingerprint = await GetFingerprint(); + if (!string.IsNullOrEmpty(fingerprint)) { - logger.Fingerprint(Fingerprint); + logger.Fingerprint(fingerprint); } else { logger.FingerprintError(); - throw new InvalidOperationException("Failed to initialize GNUPG signing service."); + throw new InvalidOperationException("Failed to initialize GnuPG signing service."); } } } @@ -54,22 +50,22 @@ public async Task Initialize() } // Execute command to generate detached signature file Creates file with .asc ending - var (success, outout) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {Fingerprint} {FILENAME}"); + var (success, output) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {await GetFingerprint()} {FILENAME}"); if (success) { // Read signature from file - (success, outout) = ReadSignatureFromFile(); - logger.Signature(outout); + (success, output) = ReadSignatureFromFile(); + logger.Signature(output); } else { - logger.SignatureError(outout); + logger.SignatureError(output); } // Remove created files RemoveFiles(); - return (success, outout); + return (success, output); } private async Task<(bool, string)> ExecuteGnuCommand(string command) @@ -104,16 +100,12 @@ public async Task Initialize() return (proc.ExitCode == 0, output); } - private async Task GetFingerprintFromKeyList() + public async Task GetFingerprint() { - var fingerprint = ""; - - // Execute command to list keys with fingerprint, use 'with-colon' to split later - var (success, outout) = await ExecuteGnuCommand($"gpg --list-keys --with-subkey-fingerprint --with-colon"); - if (success) + var (success, fingerprint) = await ExecuteGnuCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); + if (!success) { - var splitedOutput = outout.Split(':'); - fingerprint = splitedOutput[splitedOutput.Length - 2]; + throw new InvalidOperationException("Failed to extract fingerprint."); } return fingerprint; } From f09c1e4a7320c57d5cc82398a1618e841e5a213b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 11:43:37 +0200 Subject: [PATCH 346/878] Use status code pages and its integration with OpenIddict --- backend/src/Configuration/AuthConfiguration.cs | 6 +++--- backend/src/Startup.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index a5467de8..b55e6312 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -175,10 +175,10 @@ X509Certificate2 signingCertificate // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. _.UseAspNetCore() - .EnableStatusCodePagesIntegration() - .EnableRedirectionEndpointPassthrough() + .EnableStatusCodePagesIntegration() // https://documentation.openiddict.com/integrations/aspnet-core#status-code-pages-middleware-integration + .EnableRedirectionEndpointPassthrough() // https://documentation.openiddict.com/integrations/aspnet-core#pass-through-mode .EnablePostLogoutRedirectionEndpointPassthrough(); - // .DisableTransportSecurityRequirement(); + // .DisableTransportSecurityRequirement(); // https://documentation.openiddict.com/integrations/aspnet-core#transport-security-requirement // Register the System.Net.Http integration and use the identity of the current // assembly as a more specific user agent, which can be useful when dealing with diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 9305cc82..c36ae65b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -264,7 +264,7 @@ public void Configure(WebApplication app) // app.UseHsts(); // Done by NGINX, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ } - // app.UseStatusCodePages(); + app.UseStatusCodePages(); // [UseStatusCodePages](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-9.0#usestatuscodepages) // app.UseHttpsRedirection(); // Done by NGINX app.UseSerilogRequestLogging(); app.UseStaticFiles(); From fe9eef99c0543a354c436f3b177f791226e0a0a8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 11:44:52 +0200 Subject: [PATCH 347/878] Remove `OpenIddict.Client.AspNetCore` as it is included in `OpenIddict.AspNetCore` --- backend/src/Database.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 9c8566fa..1f4679a7 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -49,7 +49,6 @@ - From 0692e82c7f2a1fb3bee0914d2f48d73e67fce45f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 11:46:07 +0200 Subject: [PATCH 348/878] Fix name violation --- backend/src/Controllers/FileUploadController.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 9beb2816..7d5e2a29 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -41,14 +41,14 @@ ApplicationDbContext context ) : Controller { private const long - _fileSizeLimit = + FileSizeLimit = 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte private const string _targetDirectoryPath = "./files/"; // Get the default form options so that we can use them to set the default // limits for request body data. - private static readonly FormOptions _defaultFormOptions = new(); + private static readonly FormOptions s_defaultFormOptions = new(); private readonly ILogger _logger = logger; private readonly ApplicationDbContext _context = context; @@ -129,7 +129,7 @@ CancellationToken cancellationToken var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), - _defaultFormOptions.MultipartBoundaryLengthLimit); + s_defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); @@ -174,7 +174,7 @@ CancellationToken cancellationToken contentDisposition, ModelState, _permittedExtensions, - _fileSizeLimit + FileSizeLimit ).ConfigureAwait(false); if (!ModelState.IsValid) From 102a59321228d2309b8662078fa57a40551c2ac7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 11:48:24 +0200 Subject: [PATCH 349/878] Dispose database context properly --- .../src/Controllers/FileUploadController.cs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7d5e2a29..b9b9454d 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -44,7 +44,7 @@ private const long FileSizeLimit = 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte - private const string _targetDirectoryPath = "./files/"; + private const string TargetDirectoryPath = "./files/"; // Get the default form options so that we can use them to set the default // limits for request body data. @@ -55,6 +55,25 @@ private const long private readonly string[] _permittedExtensions = [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; + private bool _disposed; + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!_disposed) + { + // Dispose of resources held by this instance. + _context.Dispose(); + _disposed = true; + } + } + + // Disposable types implement a finalizer. + ~FileUploadController() + { + Dispose(false); + } + // The following upload methods: // // 1. Disable the form value model binding to take control of handling @@ -125,7 +144,7 @@ CancellationToken cancellationToken return Unauthorized(); } - Directory.CreateDirectory(_targetDirectoryPath); + Directory.CreateDirectory(TargetDirectoryPath); var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), @@ -183,10 +202,10 @@ CancellationToken cancellationToken } using var targetStream = System.IO.File.Create( - Path.Combine(_targetDirectoryPath, trustedFileNameForFileStorage)); + Path.Combine(TargetDirectoryPath, trustedFileNameForFileStorage)); await targetStream.WriteAsync(streamedFileContent, cancellationToken).ConfigureAwait(false); - _logger.SavedUploadedFile(trustedFileNameForDisplay, _targetDirectoryPath, + _logger.SavedUploadedFile(trustedFileNameForDisplay, TargetDirectoryPath, trustedFileNameForFileStorage); } From e0317f6b99f418da305f621d46fd5a0ee627c1e3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 13:14:04 +0200 Subject: [PATCH 350/878] Use `is null` and `is not null` wherever possible --- ...GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs | 6 +++--- .../DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index af880bed..93e962d3 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -18,9 +18,9 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? - x.HygrothermalDataId ?? x.OpticalDataId ?? - x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) + x.ParentId is not null && ids.Contains(x.CalorimetricDataId ?? + x.HygrothermalDataId ?? x.OpticalDataId ?? + x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), x => x.DataId ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 389c282c..9d4b4dc8 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -18,7 +18,7 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? + x.ParentId is null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), From feff7a61797f1989eb76d9d80be4537096863ce5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 14:34:55 +0200 Subject: [PATCH 351/878] Remove unnecessary service interfaces --- there is and will be only one implementation of each of these services and therefore using interfaces unnecessarily hides the actual implementation and makes debugging and IDE usage unnecessarily cumbersome --- backend/src/ApiRequests/DataApi.cs | 10 +-- backend/src/ApiRequests/DatabaseApi.cs | 14 +-- backend/src/ApiRequests/UserApi.cs | 10 +-- .../src/Controllers/FileUploadController.cs | 4 +- backend/src/Data/InstitutionAccessRights.cs | 6 +- .../AccessRights/DataAccessRightsMutations.cs | 10 +-- .../GraphQl/Approvals/ApprovalMutations.cs | 8 +- .../CalorimetricDataMutations.cs | 6 +- .../CalorimetricDataQueries.cs | 6 +- backend/src/GraphQl/DataX/DataQueries.cs | 6 +- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 2 +- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 2 +- .../GraphQl/Databases/DatabaseMutations.cs | 4 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 4 +- .../GeometricDataX/GeometricDataMutations.cs | 6 +- .../GeometricDataX/GeometricDataQueries.cs | 6 +- .../GetHttpsResourceMutations.cs | 4 +- .../HygrothermalDataMutations.cs | 6 +- .../HygrothermalDataQueries.cs | 6 +- .../MethodAsService/MethodAsServiceQueries.cs | 10 +-- .../OpticalDataX/OpticalDataMutations.cs | 6 +- .../OpticalDataX/OpticalDataQueries.cs | 6 +- .../PhotovoltaicDataMutations.cs | 6 +- .../PhotovoltaicDataQueries.cs | 6 +- backend/src/GraphQl/Users/UserQueries.cs | 4 +- .../src/Logging/AccessRightServiceLogging.cs | 4 +- .../Logging/ResponseApprovalServiceLogging.cs | 6 +- backend/src/Logging/SigningServiceLogging.cs | 22 ++--- backend/src/Logging/UserServiceLogging.cs | 4 +- backend/src/Program.cs | 4 +- backend/src/Services/AccessRightsService.cs | 30 ++++--- backend/src/Services/ApiRequestService.cs | 55 ++++++++++-- backend/src/Services/CacheService.cs | 4 +- backend/src/Services/DataService.cs | 14 ++- backend/src/Services/EmailSender.cs | 2 - .../Interfaces/IAccessRightsService.cs | 28 ------ .../Services/Interfaces/IApiRequestService.cs | 90 ------------------- .../src/Services/Interfaces/ICacheService.cs | 21 ----- .../src/Services/Interfaces/IDataService.cs | 21 ----- .../src/Services/Interfaces/IEmailSender.cs | 12 --- .../Interfaces/IMethodCalculationService.cs | 15 ---- .../Interfaces/IResponseApprovalService.cs | 21 ----- .../Services/Interfaces/ISigningService.cs | 31 ------- .../src/Services/Interfaces/IUserService.cs | 24 ----- .../src/Services/MethodCalculationService.cs | 4 +- .../src/Services/ResponseApprovalService.cs | 22 +++-- backend/src/Services/SigningService.cs | 23 +++-- backend/src/Services/UserService.cs | 23 +++-- backend/src/Startup.cs | 19 ++-- 49 files changed, 235 insertions(+), 422 deletions(-) delete mode 100644 backend/src/Services/Interfaces/IAccessRightsService.cs delete mode 100644 backend/src/Services/Interfaces/IApiRequestService.cs delete mode 100644 backend/src/Services/Interfaces/ICacheService.cs delete mode 100644 backend/src/Services/Interfaces/IDataService.cs delete mode 100644 backend/src/Services/Interfaces/IEmailSender.cs delete mode 100644 backend/src/Services/Interfaces/IMethodCalculationService.cs delete mode 100644 backend/src/Services/Interfaces/IResponseApprovalService.cs delete mode 100644 backend/src/Services/Interfaces/ISigningService.cs delete mode 100644 backend/src/Services/Interfaces/IUserService.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 57d4e315..6aafcc38 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Services.Interfaces; +using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; @@ -61,7 +61,7 @@ public sealed class DataApi /// Id of data to request. /// File names of query files. /// - /// + /// /// /// /// @@ -73,7 +73,7 @@ public sealed class DataApi Guid dataId, string[] filenames, AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) @@ -109,7 +109,7 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), /// Id of data to request. /// File names of query files. /// - /// + /// /// /// /// @@ -123,7 +123,7 @@ public static async Task> GetDataFromDatabase< Guid dataId, string[] filenames, AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 31d81e50..ab6fca09 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.GraphQl.Databases; -using Database.Services.Interfaces; +using Database.Services; using GraphQL; using HotChocolate; using HotChocolate.Resolvers; @@ -32,7 +32,7 @@ private sealed record DatabasesData(DatabasesConnectionDto Databases); /// Request database from Metabase. /// /// - /// + /// /// /// /// @@ -41,7 +41,7 @@ private sealed record DatabasesData(DatabasesConnectionDto Databases); /// Throws exception if errors occur. public static async Task RequestDatabase( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, @@ -123,7 +123,7 @@ public static async Task RequestDatabase( /// /// Id of database. /// - /// + /// /// /// /// @@ -133,7 +133,7 @@ public static async Task RequestDatabase( public static async Task RequestDatabaseById( Guid databaseId, AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, @@ -211,7 +211,7 @@ public static async Task RequestDatabaseById( /// /// with update mutation. /// - /// + /// /// /// /// @@ -219,7 +219,7 @@ public static async Task RequestDatabaseById( public static async Task UpdateDatabase( UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index daf2f14b..542a1dd3 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests.Dto; -using Database.Services.Interfaces; +using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; @@ -25,14 +25,14 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); /// Request current user from Metabase. /// /// - /// + /// /// /// /// /// if successful. public static async Task RequestCurrentUser( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) @@ -56,14 +56,14 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); /// Request user info from Metabase. /// /// - /// + /// /// /// /// /// if successful. public static async Task RequestUserInfo( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index b9b9454d..8d23cc66 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -8,7 +8,7 @@ using Database.Authorization; using Database.Data; using Database.Filters; -using Database.Services.Interfaces; +using Database.Services; using Database.Utilities; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; @@ -95,7 +95,7 @@ protected override void Dispose(bool disposing) [RequestSizeLimit(10737418240)] // 10 GiB public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, - IUserService userService, + UserService userService, CancellationToken cancellationToken ) { diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index ca7aad6d..5115543d 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Database.Services.Interfaces; +using Database.Services; namespace Database.Data; @@ -24,10 +24,10 @@ TimeSpan period /// /// /// Id of current user. - /// + /// /// Reason, why data is restricted. /// - internal bool IsDataRestricted(IData dataItem, Guid currentUserId, ICacheService cacheService, out string reason) + internal bool IsDataRestricted(IData dataItem, Guid currentUserId, CacheService cacheService, out string reason) { var isRestricted = false; reason = ""; diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 77aef126..ac434eff 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -15,8 +15,8 @@ public sealed class DataAccessRightsMutations public async Task UpdateDataAccessRightsAsync( DataAccessRightsInput input, ApplicationDbContext context, - IUserService userService, - IDataService dataService, + UserService userService, + DataService dataService, CancellationToken cancellationToken ) { @@ -72,7 +72,7 @@ CancellationToken cancellationToken public async Task AddAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, - IUserService userService, + UserService userService, CancellationToken cancellationToken ) { @@ -131,7 +131,7 @@ CancellationToken cancellationToken public async Task UpdateAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, - IUserService userService, + UserService userService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs index 5db06410..d94f5cce 100644 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ b/backend/src/GraphQl/Approvals/ApprovalMutations.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; namespace Database.GraphQl.Approvals; @@ -15,9 +15,9 @@ public sealed class ApprovalMutations public async Task AddApprovalToDataAsync( ApprovalInput input, ApplicationDbContext context, - IUserService userService, - IDataService dataService, - IResponseApprovalService responseApprovalService, + UserService userService, + DataService dataService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 876146f3..41788177 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; namespace Database.GraphQl.CalorimetricDataX; @@ -17,8 +17,8 @@ public sealed class CalorimetricDataMutations public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, - IUserService userService, - IResponseApprovalService responseApprovalService, + UserService userService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 726c44a1..14945177 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; @@ -25,7 +25,7 @@ public sealed class CalorimetricDataQueries public async Task> GetAllCalorimetricData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -49,7 +49,7 @@ CancellationToken cancellationToken Guid id, [GraphQLType] string? locale, CalorimetricDataByIdDataLoader byId, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 17080ac8..cbaa5f46 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -15,8 +15,8 @@ public sealed class DataQueries Guid id, [GraphQLType] string? locale, ApplicationDbContext dbContext, - IDataService dataService, - IAccessRightsService accessRightsService, + DataService dataService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 93e962d3..ca52b426 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -18,7 +18,7 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId is not null && ids.Contains(x.CalorimetricDataId ?? + x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 9d4b4dc8..389c282c 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -18,7 +18,7 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId is null && ids.Contains(x.CalorimetricDataId ?? + x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) ), diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 47714406..a72a170d 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; using Microsoft.AspNetCore.Http; @@ -15,7 +15,7 @@ public sealed class DatabaseMutations public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 6842fddc..976c7050 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -18,7 +18,7 @@ public sealed class DatabaseQueries { public async Task GetDatabaseAsync( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index e9a23766..049c5722 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; namespace Database.GraphQl.GeometricDataX; @@ -18,8 +18,8 @@ public sealed class GeometricDataMutations public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, - IUserService userService, - IResponseApprovalService responseApprovalService, + UserService userService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index ded96460..99c3aea0 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; @@ -23,7 +23,7 @@ public sealed class GeometricDataQueries public async Task> GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -47,7 +47,7 @@ CancellationToken cancellationToken Guid id, [GraphQLType] string? locale, GeometricDataByIdDataLoader byId, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 9dee91a7..856bced6 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -9,7 +9,7 @@ using Database.Extensions; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.Services.Interfaces; +using Database.Services; namespace Database.GraphQl.GetHttpsResources; @@ -20,7 +20,7 @@ public sealed class GetHttpsResourceMutations public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, - IUserService userService, + UserService userService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 8ab1a66d..3b389b4d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -6,7 +6,7 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.GeometricDataX; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; using Org.BouncyCastle.Asn1.X509.Qualified; @@ -19,8 +19,8 @@ public sealed class HygrothermalDataMutations public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, - IUserService userService, - IResponseApprovalService responseApprovalService, + UserService userService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 7f04ed3e..932dd136 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; @@ -25,7 +25,7 @@ public sealed class HygrothermalDataQueries public async Task> GetAllHygrothermalData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, IResolverContext resolverContext, ISortingContext sorting, CancellationToken cancellationToken @@ -49,7 +49,7 @@ CancellationToken cancellationToken Guid id, [GraphQLType] string? locale, HygrothermalDataByIdDataLoader byId, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index b79e7a91..656edb67 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -9,7 +9,7 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.GraphQl.Databases; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -24,7 +24,7 @@ private sealed record DataXResponse(DataXDto DataX); public async Task MethodAsServiceWithFileAsync( MethodAsServiceWithFileInput input, - IMethodCalculationService methodCalculationService, + MethodCalculationService methodCalculationService, CancellationToken cancellationToken ) { @@ -55,9 +55,9 @@ public async Task MethodAsServiceAsync( AppSettings appSettings, MethodAsServiceInput input, ApplicationDbContext context, - IUserService userService, - IMethodCalculationService methodCalculationService, - IApiRequestService apiRequestService, + UserService userService, + MethodCalculationService methodCalculationService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 5c0e77c7..55d4b698 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; namespace Database.GraphQl.OpticalDataX; @@ -17,8 +17,8 @@ public sealed class OpticalDataMutations public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, - IUserService userService, - IResponseApprovalService responseApprovalService, + UserService userService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index a52da9c9..29715376 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; @@ -25,7 +25,7 @@ public sealed class OpticalDataQueries public async Task> GetAllOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -49,7 +49,7 @@ CancellationToken cancellationToken Guid id, [GraphQLType] string? locale, OpticalDataByIdDataLoader byId, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index e5e40433..7007d530 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -8,7 +8,7 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.GeometricDataX; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate.Types; using Org.BouncyCastle.Asn1.X509.Qualified; @@ -21,8 +21,8 @@ public sealed class PhotovoltaicDataMutations public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, - IUserService userService, - IResponseApprovalService responseApprovalService, + UserService userService, + ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index e245a483..d793b149 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -4,7 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; @@ -25,7 +25,7 @@ public sealed class PhotovoltaicDataQueries public async Task> GetAllPhotovoltaicData( [GraphQLType] string? locale, ApplicationDbContext context, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken @@ -49,7 +49,7 @@ CancellationToken cancellationToken Guid id, [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, - IAccessRightsService accessRightsService, + AccessRightsService accessRightsService, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 76785945..01802a9d 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -41,7 +41,7 @@ await context.Users.AsNoTracking() public async Task GetCurrentUserInfoAsync( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs index 72b13cf7..8eb4027e 100644 --- a/backend/src/Logging/AccessRightServiceLogging.cs +++ b/backend/src/Logging/AccessRightServiceLogging.cs @@ -1,5 +1,5 @@ using System; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; @@ -9,5 +9,5 @@ public static partial class AccessRightServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "Restricted Item Id: {Id} Reason: {Reason}")] - public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); + public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); } \ No newline at end of file diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs index afcf06c6..bb19c8c7 100644 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; @@ -8,10 +8,10 @@ public static partial class ResponseApprovalServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "Getting query and response for {DataType}")] - public static partial void GetQueryAndResponse(this ILogger logger, string dataType); + public static partial void GetQueryAndResponse(this ILogger logger, string dataType); [LoggerMessage( Level = LogLevel.Debug, Message = "Query and response: {Query} \n {Response}")] - public static partial void QueryAndResponce(this ILogger logger, string query, string response); + public static partial void QueryAndResponce(this ILogger logger, string query, string response); } \ No newline at end of file diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs index 0e11fb72..50af1124 100644 --- a/backend/src/Logging/SigningServiceLogging.cs +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; @@ -8,50 +8,50 @@ public static partial class SigningServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG Fingerprint: {Fingerprint}")] - public static partial void Fingerprint(this ILogger logger, string fingerprint); + public static partial void Fingerprint(this ILogger logger, string fingerprint); [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG: Failed to get fingerprint.")] - public static partial void FingerprintError(this ILogger logger); + public static partial void FingerprintError(this ILogger logger); [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG Signature: {Signature}")] - public static partial void Signature(this ILogger logger, string signature); + public static partial void Signature(this ILogger logger, string signature); [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG: Failed to create signature: {Output}")] - public static partial void SignatureError(this ILogger logger, string output); + public static partial void SignatureError(this ILogger logger, string output); [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG: Execute Command: {Command}")] - public static partial void ExecuteCommand(this ILogger logger, string command); + public static partial void ExecuteCommand(this ILogger logger, string command); [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG Execute Command: {Error}")] - public static partial void ExecuteCommandError(this ILogger logger, string error); + public static partial void ExecuteCommandError(this ILogger logger, string error); [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG: Execute Command Output: {Output}")] - public static partial void ExecuteCommandOutput(this ILogger logger, string output); + public static partial void ExecuteCommandOutput(this ILogger logger, string output); [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG: Execute Command Result: {result}")] - public static partial void ExecuteCommandResult(this ILogger logger, int result); + public static partial void ExecuteCommandResult(this ILogger logger, int result); [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG: Write file exception: {Exception}")] - public static partial void WriteFileException(this ILogger logger, string exception); + public static partial void WriteFileException(this ILogger logger, string exception); [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG: Read file exception: {Exception}")] - public static partial void ReadFileException(this ILogger logger, string exception); + public static partial void ReadFileException(this ILogger logger, string exception); } \ No newline at end of file diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs index 5703d007..95e8aa8a 100644 --- a/backend/src/Logging/UserServiceLogging.cs +++ b/backend/src/Logging/UserServiceLogging.cs @@ -1,4 +1,4 @@ -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Logging; @@ -8,5 +8,5 @@ public static partial class UserServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "Extracted Bearer Token: {Token}")] - public static partial void ExtractedToken(this ILogger logger, string? token); + public static partial void ExtractedToken(this ILogger logger, string? token); } \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index da8f4b37..76478db9 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,7 +3,7 @@ using System.IO; using System.Threading.Tasks; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -131,7 +131,7 @@ private static async Task InitializeSigningService( IServiceProvider services ) { - var signing = services.GetRequiredService(); + var signing = services.GetRequiredService(); await signing.Initialize(); } diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 35455e7a..1cf13c8e 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -6,26 +6,31 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace Database.Services; /// -/// Implementation of +/// Service to check if requested data can be returned regarding access rights. /// /// -/// -/// +/// +/// /// public sealed class AccessRightsService( ApplicationDbContext context, - IUserService userService, - ICacheService cacheService, - ILogger logger) : IAccessRightsService + UserService userService, + CacheService cacheService, + ILogger logger) { - /// + /// + /// Apply access rights to passed data list. + /// + /// Data to apply acces rights on. + /// + /// List of data that can be returned. public async Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData { @@ -55,7 +60,12 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, return result.AsQueryable(); } - /// + /// + /// Apply access rights on passed data item. + /// + /// Data to apply acces rights on. + /// + /// Data item. public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { @@ -70,7 +80,7 @@ private IEnumerable ProcessData( List institutions, CurrentUserDto currentUser, uint alreadyAccessedCount, - ICacheService cacheService, + CacheService cacheService, List accessRights) where T : IData { diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 4cad2978..c9764121 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -9,16 +9,16 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Extensions; -using Database.Services.Interfaces; +using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; namespace Database.Services; /// -/// Implementation of +/// Service to request Metabase or Database Api. GraphQL and REST. /// -public sealed class ApiRequestService() : IApiRequestService +public sealed class ApiRequestService { /// /// Name of Metabase http client. @@ -32,21 +32,31 @@ public sealed class ApiRequestService() : IApiRequestService private static bool s_useMetabase = true; - /// + /// + /// Use Metabase as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection + /// + /// public ApiRequestService Metabase() { s_useMetabase = true; return this; } - /// + /// + /// Use Database as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection + /// + /// public ApiRequestService Database() { s_useMetabase = false; return this; } - /// + /// + /// Construct query from passed files. + /// + /// Name of files containung queries. + /// Query from all files. public async Task ConstructGraphQlQuery( string[] fileNames ) @@ -61,7 +71,16 @@ await Task.WhenAll( ); } - /// + /// + /// Send GraphQL request to API and return response. + /// + /// Expected response type. + /// + /// + /// + /// + /// + /// of expected type. public Task> QueryGraphQl( AppSettings appSettings, GraphQLRequest request, @@ -84,7 +103,17 @@ CancellationToken cancellationToken ); } - /// + /// + /// Send GraphQL request to API and return response. + /// + /// Expected response type. + /// + /// Uri of GraphQL Api. + /// + /// + /// + /// + /// of expected type. public Task> QueryGraphQlFromUrl( AppSettings appSettings, Uri uri, @@ -108,7 +137,15 @@ CancellationToken cancellationToken ); } - /// + /// + /// Send REST request to API and return response. + /// + /// Expected response type. + /// to send request to. + /// + /// + /// + /// public Task QueryRest( Uri uri, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 94d62978..3bcd3805 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,6 +1,6 @@ using System; using Database.ApiRequests.Dto; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Caching.Memory; namespace Database.Services; @@ -8,7 +8,7 @@ namespace Database.Services; public sealed class CacheService( IMemoryCache currentUserCache, IMemoryCache accessCountCache, - IMemoryCache timePeriodCountCache) : ICacheService + IMemoryCache timePeriodCountCache) { public CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser) { diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 1ccf0a25..0823e1bf 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -3,17 +3,23 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.EntityFrameworkCore; namespace Database.Services; /// -/// Implementation of +/// Service to get data from /// -public sealed class DataService() : IDataService +public sealed class DataService { - /// + /// + /// Get dataset with passed id from all datasets + /// + /// Id of dataset to get. + /// + /// + /// Dataset with passed id or null. public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 38f8e09f..d5b8cb1b 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,5 +1,4 @@ using System.Threading.Tasks; -using Database.Services.Interfaces; using MailKit.Net.Smtp; using MailKit.Security; using Microsoft.Extensions.Logging; @@ -26,7 +25,6 @@ public sealed class EmailSender( int smtpPort, ILogger logger ) - : IEmailSender { private readonly ILogger _logger = logger; private readonly string _smtpHost = smtpHost; diff --git a/backend/src/Services/Interfaces/IAccessRightsService.cs b/backend/src/Services/Interfaces/IAccessRightsService.cs deleted file mode 100644 index 2c4e78be..00000000 --- a/backend/src/Services/Interfaces/IAccessRightsService.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; - -namespace Database.Services.Interfaces; - -/// -/// Service to check if requested data can be returned regarding access rights. -/// -public interface IAccessRightsService -{ - /// - /// Apply access rights to passed data list. - /// - /// Data to apply acces rights on. - /// - /// List of data that can be returned. - Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) where T : IData; - - /// - /// Apply access rights on passed data item. - /// - /// Data to apply acces rights on. - /// - /// Data item. - Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData; -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IApiRequestService.cs b/backend/src/Services/Interfaces/IApiRequestService.cs deleted file mode 100644 index d85aab84..00000000 --- a/backend/src/Services/Interfaces/IApiRequestService.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using GraphQL; -using Microsoft.AspNetCore.Http; - -namespace Database.Services.Interfaces; - -/// -/// Service to request Metabase or Database Api. GraphQL and REST. -/// -public interface IApiRequestService -{ - /// - /// Use Metabase as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection - /// - /// - public ApiRequestService Metabase(); - - /// - /// Use Database as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection - /// - /// - public ApiRequestService Database(); - - /// - /// Construct query from passed files. - /// - /// Name of files containung queries. - /// Query from all files. - public Task ConstructGraphQlQuery(string[] fileNames); - - /// - /// Send GraphQL request to API and return response. - /// - /// Expected response type. - /// - /// - /// - /// - /// - /// of expected type. - public Task> QueryGraphQl( - AppSettings appSettings, - GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TGraphQlResponse : class; - - /// - /// Send GraphQL request to API and return response. - /// - /// Expected response type. - /// - /// Uri of GraphQL Api. - /// - /// - /// - /// - /// of expected type. - public Task> QueryGraphQlFromUrl( - AppSettings appSettings, - Uri uri, - GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TGraphQlResponse : class; - - /// - /// Send REST request to API and return response. - /// - /// Expected response type. - /// to send request to. - /// - /// - /// - /// - public Task QueryRest( - Uri uri, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - where TResponse : class; -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/ICacheService.cs b/backend/src/Services/Interfaces/ICacheService.cs deleted file mode 100644 index 34a6bb97..00000000 --- a/backend/src/Services/Interfaces/ICacheService.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using Database.ApiRequests.Dto; - -namespace Database.Services.Interfaces; - -public interface ICacheService -{ - bool TryGetUser(string token, out CurrentUserDto? cacheUser); - - CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser); - - uint GetAccessCountForUser(Guid userId); - - uint SetAccessCountForUser(Guid userId, uint count); - - (DateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId); - - (DateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId); - - (DateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IDataService.cs b/backend/src/Services/Interfaces/IDataService.cs deleted file mode 100644 index 561b5d8f..00000000 --- a/backend/src/Services/Interfaces/IDataService.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; - -namespace Database.Services.Interfaces; - -/// -/// Service to get data from -/// -public interface IDataService -{ - /// - /// Get dataset with passed id from all datasets - /// - /// Id of dataset to get. - /// - /// - /// Dataset with passed id or null. - public Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IEmailSender.cs b/backend/src/Services/Interfaces/IEmailSender.cs deleted file mode 100644 index eb1ecaf2..00000000 --- a/backend/src/Services/Interfaces/IEmailSender.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - -namespace Database.Services.Interfaces; - -public interface IEmailSender -{ - public Task SendAsync( - (string name, string address) recipient, - string subject, - string body - ); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IMethodCalculationService.cs b/backend/src/Services/Interfaces/IMethodCalculationService.cs deleted file mode 100644 index 301ba091..00000000 --- a/backend/src/Services/Interfaces/IMethodCalculationService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.MethodAsService; -using Database.Methods; - -namespace Database.Services.Interfaces; - -public interface IMethodCalculationService -{ - void AddMethod(IMethod method); - - bool MethodExists(Guid methodId); - - List UseMethodToCalculate(Guid methodId, IReadOnlyList dataPoints); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IResponseApprovalService.cs b/backend/src/Services/Interfaces/IResponseApprovalService.cs deleted file mode 100644 index 27293d48..00000000 --- a/backend/src/Services/Interfaces/IResponseApprovalService.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; - -namespace Database.Services.Interfaces; - -/// -/// Service to create response approval -/// -public interface IResponseApprovalService -{ - /// - /// Create response approval by calling graphql Api and signing responce. - /// - /// - /// - /// Thows exception, when singing of data failed. - /// - Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/ISigningService.cs b/backend/src/Services/Interfaces/ISigningService.cs deleted file mode 100644 index 6e8b4d90..00000000 --- a/backend/src/Services/Interfaces/ISigningService.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Database.Services.Interfaces; - -/// -/// Service to sign data string with GNUPG. -/// -public interface ISigningService -{ - /// - /// Initialize service by importing private key from file passed into container. - /// - /// True, if privat key was successfully imported and fingerprint set. - /// If initialization failed. - public Task Initialize(); - - /// - /// Sign passed data string and return signature. - /// - /// Data string to create signature from. - /// True and generated signature, if successful. Otherwise false and error. - public Task<(bool Success, string Output)> SignData(string data); - - /// - /// Get fingerprint. - /// - /// The fingerprint. - /// If fingerprint extraction failed. - public Task GetFingerprint(); -} \ No newline at end of file diff --git a/backend/src/Services/Interfaces/IUserService.cs b/backend/src/Services/Interfaces/IUserService.cs deleted file mode 100644 index 9d556cda..00000000 --- a/backend/src/Services/Interfaces/IUserService.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests.Dto; - -namespace Database.Services.Interfaces; - -/// -/// Service to get current user from Metabase -/// -public interface IUserService -{ - /// - /// Get current user from Metabase. - /// - /// - /// - public Task GetCurrentUser(CancellationToken cancellationToken); - - /// - /// Get application from user calims. - /// - /// ClientId from claims as applicationId. - string? GetApplicationIdFromUser(); -} \ No newline at end of file diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs index ecc2f71c..f978940f 100644 --- a/backend/src/Services/MethodCalculationService.cs +++ b/backend/src/Services/MethodCalculationService.cs @@ -3,11 +3,11 @@ using System.Linq; using Database.GraphQl.MethodAsService; using Database.Methods; -using Database.Services.Interfaces; +using Database.Services; namespace Database.Services; -public sealed class MethodCalculationService : IMethodCalculationService +public sealed class MethodCalculationService { private List _methods = []; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index a464fe79..d586a88b 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -6,28 +6,28 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Database.Services; /// -/// Implementation of +/// Service to create response approval /// /// -/// -/// +/// +/// /// /// /// public sealed class ResponseApprovalService( AppSettings appSettings, - ISigningService signingService, - IApiRequestService apiRequestService, + SigningService signingService, + ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, - ILogger logger) : IResponseApprovalService + ILogger logger) { private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); private sealed record GeometricDataResponse(GeometricDataDto GeometricData); @@ -35,7 +35,13 @@ private sealed record HygrothermalDataResponse(HygrothermalDataDto HygrothermalD private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicData); private sealed record OpticalDataResponse(OpticalDataDto OpticalData); - /// + /// + /// Create response approval by calling graphql Api and signing responce. + /// + /// + /// + /// Thows exception, when singing of data failed. + /// public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 288dd59e..e2105a31 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -3,24 +3,28 @@ using System.IO; using System.Threading.Tasks; using Database.Logging; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.Extensions.Logging; namespace Database.Services; /// -/// Implementation of +/// Service to sign data string with GNUPG. /// /// /// Instance of public sealed class SigningService( AppSettings appSettings, - ILogger logger) : ISigningService + ILogger logger) { private const string FILENAME = "dataToSign"; private const string FILE_EXTENSION = ".asc"; - /// + /// + /// Initialize service by importing private key from file passed into container. + /// + /// True, if privat key was successfully imported and fingerprint set. + /// If initialization failed. public async Task Initialize() { // Execute command to import private key @@ -40,7 +44,11 @@ public async Task Initialize() } } - /// + /// + /// Sign passed data string and return signature. + /// + /// Data string to create signature from. + /// True and generated signature, if successful. Otherwise false and error. public async Task<(bool Success, string Output)> SignData(string data) { // Write data string to file @@ -100,6 +108,11 @@ public async Task Initialize() return (proc.ExitCode == 0, output); } + /// + /// Get fingerprint. + /// + /// The fingerprint. + /// If fingerprint extraction failed. public async Task GetFingerprint() { var (success, fingerprint) = await ExecuteGnuCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 7213a3b7..93ffeb30 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -5,7 +5,7 @@ using Database.ApiRequests.Dto; using Database.Extensions; using Database.Logging; -using Database.Services.Interfaces; +using Database.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; @@ -14,28 +14,35 @@ namespace Database.Services; /// -/// Implementation of +/// Service to get current user from Metabase /// /// /// /// -/// to store already known users. +/// to store already known users. /// Instance of public sealed class UserService( AppSettings appSettings, - IApiRequestService apiRequestService, + ApiRequestService apiRequestService, IHttpContextAccessor httpContextAccessor, IHttpClientFactory httpClientFactory, - ICacheService cacheService, - ILogger logger) : IUserService + CacheService cacheService, + ILogger logger) { - /// + /// + /// Get application from user calims. + /// + /// ClientId from claims as applicationId. public string? GetApplicationIdFromUser() { return httpContextAccessor.HttpContext?.User.GetClaim(Claims.AuthorizedParty); } - /// + /// + /// Get current user from Metabase. + /// + /// + /// public async Task GetCurrentUser(CancellationToken cancellationToken) { // Extract token diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index c36ae65b..8fa8dc88 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -9,7 +9,6 @@ using Database.Data.Extensions; using Database.Enumerations; using Database.Services; -using Database.Services.Interfaces; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; @@ -62,14 +61,14 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(_appSettings); services.AddSingleton(_environment); // services.AddDatabaseDeveloperPageExceptionFilter(); - services.AddSingleton(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) @@ -129,7 +128,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services private void ConfigureMessageSenderServices(IServiceCollection services) { - services.AddTransient(serviceProvider => + services.AddTransient(serviceProvider => new EmailSender( _appSettings.Email.SmtpHost, _appSettings.Email.SmtpPort, From d8684f3dd1fefa9ceff7d3cd75b08238581917bd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 May 2025 14:36:37 +0200 Subject: [PATCH 352/878] Update GraphQL schema --- frontend/type-defs.graphqls | 262 +++++++++++++++++++++++++++++++++++- 1 file changed, 257 insertions(+), 5 deletions(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 40ddafd1..1548b093 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -5,12 +5,17 @@ schema { interface Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -31,6 +36,24 @@ interface Reference { title: String } +type AddApprovalError { + code: AddApprovalErrorCode! + message: String! + path: [String!]! +} + +type AddApprovalPayload { + dataApproval: DataApproval + errors: [AddApprovalError!] + query: Query! +} + +type AddInstitutionAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [InstitutionAccessRightsError!] + query: Query! +} + type Address { formatted: String! } @@ -43,14 +66,19 @@ type AppliedMethod { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String gValues: [Float!]! id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @cost(weight: "10") @@ -85,6 +113,21 @@ type CielabColor { lStar: Float! } +type ColorReflectance { + cielabColors: [MethodAsServiceCielabColor!]! +} + +type ColorRenderingIndex { + illuminant: String! + observer: String! + ra: Int! +} + +type ColorTransmittance { + cielabColors: [MethodAsServiceCielabColor!]! + colorRenderingIndices: [ColorRenderingIndex!]! +} + type CreateCalorimetricDataError { code: CreateCalorimetricDataErrorCode! message: String! @@ -164,6 +207,16 @@ type CrossDatabaseDataReference { dataTimestamp: DateTime! } +type DataAccessRights { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] + hasRestrictions: Boolean! + isRestrictedByApplication: Boolean! + isRestrictedByInstitution: Boolean! + isRestrictedByUser: Boolean! +} + type DataApproval { approverId: Uuid! keyFingerprint: String! @@ -185,6 +238,12 @@ type DataEdge { node: Data! } +type DataPoint { + emergence: Emergence! + incidence: Incidence! + results: Results! +} + type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -196,6 +255,14 @@ type Database { verificationState: DatabaseVerificationState! } +type Direction { + polar: Int! +} + +type Emergence { + direction: Direction! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -203,13 +270,18 @@ type FileMetaInformation { type GeometricData implements Node & Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @cost(weight: "10") @@ -289,13 +361,18 @@ type GetHttpsResourceTreeRoot { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @cost(weight: "10") @@ -323,13 +400,62 @@ type HygrothermalDataEdge { node: HygrothermalData! } +type Incidence { + direction: Direction! + wavelengths: Wavelengths! +} + +type InstitutionAccessRights { + allowedDatasetsPerTime: NonNegativeInt + allowedUserCount: NonNegativeInt + id: Uuid! + institutionId: Uuid! + period: TimeSpan! + userAlreadyAccessed: [Uuid!]! + version: NonNegativeInt! +} + +type InstitutionAccessRightsError { + code: InstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type KeyValuePairOfGuidAndNullableOfUInt32 { + key: Uuid! + value: NonNegativeInt +} + +type MethodAsServiceCielabColor { + aStar: Float! + bStar: Float! + illuminant: String! + lStar: Int! + observer: String! +} + +type MethodAsServiceError { + code: MethodAsServiceErrorCode! + message: String! + path: [String!]! +} + +type MethodAsServicePayload { + calculationResult: [DataPoint!] + errors: [MethodAsServiceError!] +} + type Mutation { + addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") + addApprovalToData(input: ApprovalInput!): AddApprovalPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") } @@ -356,6 +482,7 @@ type OpenEndedDateTimeRange { type OpticalData implements Node & Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! cielabColors: [CielabColor!]! coatedSide: CoatedSide @@ -363,10 +490,14 @@ type OpticalData implements Node & Data { componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String id: ID! infraredEmittances: [Float!]! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! @@ -414,13 +545,18 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! + approval: ResponseApproval approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! + dataAccessRights: DataAccessRights! databaseId: Uuid! description: String id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @cost(weight: "10") @@ -473,11 +609,14 @@ type Query { calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo @cost(weight: "10") + data(id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + methodAsService(input: MethodAsServiceInput!): MethodAsServicePayload! @cost(weight: "10") + methodAsServiceWithFile(input: MethodAsServiceWithFileInput!): MethodAsServicePayload! @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." @@ -487,6 +626,21 @@ type Query { verificationCode: String! } +type ResponseApproval { + keyFingerprint: String! + query: String! + response: String! + signature: String! + timestamp: DateTime! +} + +type Results { + colorReflectance: ColorReflectance + colorTransmittance: ColorTransmittance + reflectance: Float + transmittance: Float! +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard implements Reference { abstract: String @@ -506,6 +660,24 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UpdateAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [InstitutionAccessRightsError!] + query: Query! +} + +type UpdateDataAccessRightsError { + code: UpdateDataAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataAccessRightsPayload { + dataAccessRights: DataAccessRights + errors: [UpdateDataAccessRightsError!] + query: Query! +} + type UpdateDatabaseError { code: UpdateDatabaseErrorCode! message: String! @@ -537,6 +709,10 @@ type UserInfo { website: String } +type Wavelengths { + wavelength: Int! +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -555,6 +731,12 @@ input AppliedMethodSortInput { methodId: SortEnumType @cost(weight: "10") } +input ApprovalInput { + approval: DataApprovalInput! + creatorId: Uuid! + dataId: Uuid! +} + input BooleanPropositionInput { equalTo: Boolean @cost(weight: "10") notEqualTo: Boolean @cost(weight: "10") @@ -605,7 +787,6 @@ input CielabColorPropositionInput { input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! - approvals: [DataApprovalInput!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -620,7 +801,6 @@ input CreateCalorimetricDataInput { input CreateGeometricDataInput { appliedMethod: AppliedMethodInput! - approvals: [DataApprovalInput!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -645,7 +825,6 @@ input CreateGetHttpsResourceInput { input CreateHygrothermalDataInput { appliedMethod: AppliedMethodInput! - approvals: [DataApprovalInput!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -664,7 +843,6 @@ input CreateNumerationInput { input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! - approvals: [DataApprovalInput!]! cielabColors: [CielabColorInput!]! coatedSide: CoatedSide colorRenderingIndices: [Float!]! @@ -687,7 +865,6 @@ input CreateOpticalDataInput { input CreatePhotovoltaicDataInput { appliedMethod: AppliedMethodInput! - approvals: [DataApprovalInput!]! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! @@ -735,6 +912,13 @@ input CrossDatabaseDataReferencePropositionInput { databaseId: UuidPropositionInput } +input DataAccessRightsInput { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + dataId: Uuid! +} + input DataApprovalInput { approverId: Uuid! keyFingerprint: String! @@ -927,6 +1111,13 @@ input IReferencePropositionInput { section: StringPropositionInput } +input InstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + input Int32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -942,6 +1133,11 @@ input Int32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } +input KeyValuePairOfGuidAndNullableOfUInt32Input { + key: Uuid! + value: NonNegativeInt +} + input ListFilterInputTypeOfCielabColorPropositionInput { all: CielabColorPropositionInput @cost(weight: "10") none: CielabColorPropositionInput @cost(weight: "10") @@ -991,6 +1187,17 @@ input ListStandardizerPropositionInput { any: Boolean @cost(weight: "10") } +input MethodAsServiceInput { + databaseId: Uuid! + dataId: Uuid! + methodId: Uuid! +} + +input MethodAsServiceWithFileInput { + file: Upload! + methodId: Uuid! +} + input NamedMethodArgumentInput { name: String! value: Any! @@ -1267,6 +1474,14 @@ input UuidPropositionInput { notLessThanOrEqualTo: Uuid @cost(weight: "10") } +enum AddApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + SIGNING_FAILED +} + "Defines when a policy shall be executed." enum ApplyPolicy { "Before the resolver was executed." @@ -1287,32 +1502,43 @@ enum CoatedSide { enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED + UNAUTHENTICATED + SIGNING_FAILED } enum CreateGeometricDataErrorCode { UNKNOWN UNAUTHORIZED + UNAUTHENTICATED + SIGNING_FAILED } enum CreateGetHttpsResourceErrorCode { UNKNOWN UNKNOWN_DATA UNAUTHORIZED + UNAUTHENTICATED } enum CreateHygrothermalDataErrorCode { UNKNOWN UNAUTHORIZED + UNAUTHENTICATED + SIGNING_FAILED } enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED + UNAUTHENTICATED + SIGNING_FAILED } enum CreatePhotovoltaicDataErrorCode { UNKNOWN UNAUTHORIZED + UNAUTHENTICATED + SIGNING_FAILED } enum DataKind { @@ -1328,6 +1554,19 @@ enum DatabaseVerificationState { VERIFIED } +enum InstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + ALREADY_EXISTS + UNKNOWN_ACCESS_RIGHTS +} + +enum MethodAsServiceErrorCode { + UNKNOWN_METHOD + UNKNOWN_DATABASE +} + enum OpticalComponentSubtype { MONOLITHIC LAMINATE @@ -1390,6 +1629,13 @@ enum Standardizer { WELL } +enum UpdateDataAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED @@ -1424,6 +1670,12 @@ scalar Locale "The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." scalar NonNegativeInt +"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." +scalar TimeSpan + +"The `Upload` scalar type represents a file upload." +scalar Upload + scalar Url scalar Uuid \ No newline at end of file From b19450e2998b5fa86f0a039a3c438610d9918ec3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 May 2025 17:12:10 +0200 Subject: [PATCH 353/878] Turn reference into union and name data approvals properly --- .../src/Authorization/CommonAuthorization.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 22 ++-- backend/src/Data/DataApproval.cs | 29 +++- backend/src/Data/IReference.cs | 3 - backend/src/Data/Reference.cs | 47 +++++++ .../src/GraphQl/Approvals/AddApprovalError.cs | 13 -- .../GraphQl/Approvals/AddApprovalPayload.cs | 29 ---- .../src/GraphQl/Approvals/ApprovalInput.cs | 10 -- .../GraphQl/Approvals/ApprovalMutations.cs | 118 ----------------- .../GraphQl/Approvals/DataApprovalInput.cs | 16 --- .../src/GraphQl/Approvals/DataApprovalType.cs | 18 --- .../DataApprovals/AddDataApprovalError.cs | 13 ++ .../AddDataApprovalErrorCode.cs} | 8 +- .../DataApprovals/AddDataApprovalPayload.cs | 29 ++++ .../DataApprovals/DataApprovalInput.cs | 17 +++ .../DataApprovals/DataApprovalMutations.cs | 124 ++++++++++++++++++ .../DataApprovalPayload.cs} | 14 +- .../GraphQl/DataApprovals/DataApprovalType.cs | 21 +++ ...ublicationInput.cs => PublicationInput.cs} | 2 +- .../GraphQl/Publications/PublicationType.cs | 14 ++ .../Publications/UpdatePublicationInput.cs | 14 -- .../GraphQl/References/ReferenceFilterType.cs | 17 +++ .../src/GraphQl/References/ReferenceInput.cs | 9 ++ .../GraphQl/References/ReferenceSortType.cs | 17 +++ .../src/GraphQl/References/ReferenceType.cs | 28 +++- ...reateStandardInput.cs => StandardInput.cs} | 2 +- backend/src/GraphQl/Standards/StandardType.cs | 19 +++ .../GraphQl/Standards/UpdateStandardInput.cs | 15 --- frontend/type-defs.graphqls | 105 +++++++-------- 29 files changed, 454 insertions(+), 321 deletions(-) create mode 100644 backend/src/Data/Reference.cs delete mode 100644 backend/src/GraphQl/Approvals/AddApprovalError.cs delete mode 100644 backend/src/GraphQl/Approvals/AddApprovalPayload.cs delete mode 100644 backend/src/GraphQl/Approvals/ApprovalInput.cs delete mode 100644 backend/src/GraphQl/Approvals/ApprovalMutations.cs delete mode 100644 backend/src/GraphQl/Approvals/DataApprovalInput.cs delete mode 100644 backend/src/GraphQl/Approvals/DataApprovalType.cs create mode 100644 backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs rename backend/src/GraphQl/{Approvals/AddApprovalErrorCode.cs => DataApprovals/AddDataApprovalErrorCode.cs} (51%) create mode 100644 backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs create mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalInput.cs create mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs rename backend/src/GraphQl/{Approvals/ApprovalPayload.cs => DataApprovals/DataApprovalPayload.cs} (74%) create mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalType.cs rename backend/src/GraphQl/Publications/{CreatePublicationInput.cs => PublicationInput.cs} (82%) delete mode 100644 backend/src/GraphQl/Publications/UpdatePublicationInput.cs create mode 100644 backend/src/GraphQl/References/ReferenceFilterType.cs create mode 100644 backend/src/GraphQl/References/ReferenceInput.cs create mode 100644 backend/src/GraphQl/References/ReferenceSortType.cs rename backend/src/GraphQl/Standards/{CreateStandardInput.cs => StandardInput.cs} (87%) delete mode 100644 backend/src/GraphQl/Standards/UpdateStandardInput.cs diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 0100c81f..aa11af2c 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -28,7 +28,7 @@ t.Role is InstitutionRepresentativeRole.ASSISTANT ); } - public static bool IsAuthorizedToAddApprovalForInstitution( + public static bool IsAuthorizedToAddDataApprovalForInstitution( CurrentUserDto currentUser, Guid institutionId, CancellationToken cancellationToken diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 8972dec5..39033f73 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -27,7 +27,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; -using Database.GraphQl.Approvals; +using Database.GraphQl.DataApprovals; using Database.GraphQl.AccessRights; using Database.GraphQl.MethodAsService; @@ -167,7 +167,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() - .AddType() + .AddType() .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ @@ -232,14 +232,15 @@ protected override void Configure(IFilterConventionDescriptor descriptor) descriptor.AllowAnd(); descriptor.AllowOr(); // Bind custom types - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); } // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? @@ -410,13 +411,14 @@ protected override void Configure(ISortConventionDescriptor descriptor) { descriptor.AddDefaults(); // Bind custom types - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); } } \ No newline at end of file diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 0e10607f..e090fe6e 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations.Schema; using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -11,17 +10,37 @@ public sealed class DataApproval( string keyFingerprint, string query, string response, - Guid approverId + Guid approverId, + Reference statement ) : IApproval { + // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties + private DataApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + string response, + Guid approverId + ) + : this( + timestamp, + signature, + keyFingerprint, + query, + response, + approverId, + null! // EF Core will set this owned navigation property after construction. + ) + { + } + public Guid ApproverId { get; private set; } = approverId; public DateTime Timestamp { get; private set; } = timestamp; public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; public string Response { get; private set; } = response; - public Publication? Publication { get; set; } - public Standard? Standard { get; set; } - [NotMapped] public IReference? Statement => Standard is not null ? Standard : Publication; + public Reference Statement { get; private set; } = statement; } \ No newline at end of file diff --git a/backend/src/Data/IReference.cs b/backend/src/Data/IReference.cs index e0cb9080..1000f9df 100644 --- a/backend/src/Data/IReference.cs +++ b/backend/src/Data/IReference.cs @@ -2,7 +2,4 @@ namespace Database.Data; public interface IReference { - public string? Title { get; } - public string? Abstract { get; } - public string? Section { get; } } \ No newline at end of file diff --git a/backend/src/Data/Reference.cs b/backend/src/Data/Reference.cs new file mode 100644 index 00000000..02503d73 --- /dev/null +++ b/backend/src/Data/Reference.cs @@ -0,0 +1,47 @@ + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +[Owned] +public sealed class Reference +{ + // Constructor for EF Core because navigation properties cannot be set using a constructor. + private Reference() + { + } + + public Reference(Standard standard) + { + Standard = standard; + } + + public Reference(Publication publication) + { + Publication = publication; + } + + // Standard, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types + public Standard? Standard { get; private set; } + + // Publication, being an owned type, is included by default as told on https://docs.microsoft.com/en-us/ef/core/modeling/owned-entities#querying-owned-types + public Publication? Publication { get; private set; } + + // Note that using `!` is safe here as by construction either `Standard` or `Publication` is non-null and never both. + [NotMapped] public IReference TheReference => Standard is not null ? Standard! : Publication!; + + // To evade the error + // --- + // Entity type `Reference` is an optional dependent using table sharing and + // containing other dependents without any required non shared property to + // identify whether the entity exists. If all nullable properties contain a + // null value in database then an object instance won't be created in the + // query causing nested dependent's values to be lost. Add a required + // property to create instances with null values for other properties or + // mark the incoming navigation as required to always create an instance. + // --- + // I introduce this non-null property. + [Required] public bool Exists { get; private set; } = true; +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalError.cs b/backend/src/GraphQl/Approvals/AddApprovalError.cs deleted file mode 100644 index f702c8eb..00000000 --- a/backend/src/GraphQl/Approvals/AddApprovalError.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Database.GraphQl.GeometricDataX; -using System.Collections.Generic; - -namespace Database.GraphQl.Approvals; - -public sealed class AddApprovalError( - AddApprovalErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs b/backend/src/GraphQl/Approvals/AddApprovalPayload.cs deleted file mode 100644 index a2541b76..00000000 --- a/backend/src/GraphQl/Approvals/AddApprovalPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.Approvals; - -public sealed class AddApprovalPayload - : ApprovalPayload -{ - public AddApprovalPayload( - DataApproval dataApproval - ) - : base(dataApproval) - { - } - - public AddApprovalPayload( - AddApprovalError error - ) - : base(error) - { - } - - public AddApprovalPayload( - DataApproval dataApproval, - AddApprovalError error - ) - : base(dataApproval, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalInput.cs b/backend/src/GraphQl/Approvals/ApprovalInput.cs deleted file mode 100644 index 92f60572..00000000 --- a/backend/src/GraphQl/Approvals/ApprovalInput.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Database.GraphQl.Approvals; - -public sealed record ApprovalInput -( - Guid DataId, - Guid CreatorId, - DataApprovalInput Approval -); \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalMutations.cs b/backend/src/GraphQl/Approvals/ApprovalMutations.cs deleted file mode 100644 index d94f5cce..00000000 --- a/backend/src/GraphQl/Approvals/ApprovalMutations.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Services; -using HotChocolate.Types; - -namespace Database.GraphQl.Approvals; - -[ExtendObjectType(nameof(Mutation))] -public sealed class ApprovalMutations -{ - //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] - public async Task AddApprovalToDataAsync( - ApprovalInput input, - ApplicationDbContext context, - UserService userService, - DataService dataService, - ResponseApprovalService responseApprovalService, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); - if (currentUser is null) - { - return new AddApprovalPayload( - new AddApprovalError( - AddApprovalErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!CommonAuthorization.IsAuthorizedToAddApprovalForInstitution( - currentUser, - input.CreatorId, - cancellationToken - ) - ) - { - return new AddApprovalPayload( - new AddApprovalError( - AddApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to approval for the institution.", - [] - ) - ); - } - - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); - if (data is null) - { - return new AddApprovalPayload( - new AddApprovalError( - AddApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [] - ) - ); - } - - var approval = new DataApproval( - DateTime.Now, - input.Approval.Signature, - input.Approval.KeyFingerprint, - input.Approval.Query, - input.Approval.Response, - currentUser.Id); - if (input.Approval.Publication is not null) - { - approval.Publication = new Publication( - input.Approval.Publication.Title, - input.Approval.Publication.Abstract, - input.Approval.Publication.Section, - input.Approval.Publication.Authors, - input.Approval.Publication.Doi, - input.Approval.Publication.ArXiv, - input.Approval.Publication.Urn, - input.Approval.Publication.WebAddress); - } - if (input.Approval.Standard is not null) - { - approval.Standard = new Standard( - input.Approval.Standard.Title, - input.Approval.Standard.Abstract, - input.Approval.Standard.Section, - input.Approval.Standard.Year, - input.Approval.Standard.Standardizers, - input.Approval.Standard.Locator); - } - data.Approvals.Add(approval); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - context.Remove(data); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - - return new AddApprovalPayload( - approval, - new AddApprovalError( - AddApprovalErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", - [] - ) - ); - } - return new AddApprovalPayload(approval); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/DataApprovalInput.cs b/backend/src/GraphQl/Approvals/DataApprovalInput.cs deleted file mode 100644 index d22e24bf..00000000 --- a/backend/src/GraphQl/Approvals/DataApprovalInput.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using Database.GraphQl.Publications; -using Database.GraphQl.Standards; - -namespace Database.GraphQl.Approvals; - -public sealed record DataApprovalInput( - DateTime Timestamp, - string Signature, - string KeyFingerprint, - string Query, - string Response, - Guid ApproverId, - CreateStandardInput? Standard, - CreatePublicationInput? Publication -); \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/DataApprovalType.cs b/backend/src/GraphQl/Approvals/DataApprovalType.cs deleted file mode 100644 index b719d18d..00000000 --- a/backend/src/GraphQl/Approvals/DataApprovalType.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Database.Data; -using HotChocolate.Types; - -namespace Database.GraphQl.Approvals; - -public sealed class DataApprovalType - : ObjectType -{ - protected override void Configure(IObjectTypeDescriptor descriptor) - { - descriptor - .Field(t => t.Standard) - .Ignore(); - descriptor - .Field(t => t.Publication) - .Ignore(); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs new file mode 100644 index 00000000..79379578 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs @@ -0,0 +1,13 @@ +using Database.GraphQl.GeometricDataX; +using System.Collections.Generic; + +namespace Database.GraphQl.DataApprovals; + +public sealed class AddDataApprovalError( + AddDataApprovalErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs similarity index 51% rename from backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs rename to backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index 7e460b47..5cf7ba81 100644 --- a/backend/src/GraphQl/Approvals/AddApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -1,13 +1,15 @@ using System.Diagnostics.CodeAnalysis; -namespace Database.GraphQl.Approvals; +namespace Database.GraphQl.DataApprovals; [SuppressMessage("Naming", "CA1707")] -public enum AddApprovalErrorCode +public enum AddDataApprovalErrorCode { UNKNOWN, UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, - SIGNING_FAILED + SIGNING_FAILED, + AMBIGUOUS_STATEMENT, + MISSING_STATEMENT } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs new file mode 100644 index 00000000..b59a2ca0 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs @@ -0,0 +1,29 @@ +using Database.Data; + +namespace Database.GraphQl.DataApprovals; + +public sealed class AddDataApprovalPayload + : DataApprovalPayload +{ + public AddDataApprovalPayload( + DataApproval dataApproval + ) + : base(dataApproval) + { + } + + public AddDataApprovalPayload( + AddDataApprovalError error + ) + : base(error) + { + } + + public AddDataApprovalPayload( + DataApproval dataApproval, + AddDataApprovalError error + ) + : base(dataApproval, error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs new file mode 100644 index 00000000..9917241a --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs @@ -0,0 +1,17 @@ +using System; +using Database.GraphQl.References; + +namespace Database.GraphQl.DataApprovals; + +public sealed record DataApprovalInput +( + Guid DataId, + Guid CreatorId, + DateTime Timestamp, + string Signature, + string KeyFingerprint, + string Query, + string Response, + Guid ApproverId, + ReferenceInput Statement +); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs new file mode 100644 index 00000000..1064cb9c --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -0,0 +1,124 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Extensions; +using Database.GraphQl.References; +using Database.Services; +using HotChocolate.Types; + +namespace Database.GraphQl.DataApprovals; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DataApprovalMutations +{ + //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + public async Task AddDataApprovalAsync( + DataApprovalInput input, + ApplicationDbContext context, + UserService userService, + DataService dataService, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + if (currentUser is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + if (!CommonAuthorization.IsAuthorizedToAddDataApprovalForInstitution( + currentUser, + input.CreatorId, + cancellationToken + ) + ) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNAUTHORIZED, + $"The current user is not authorized to approval for the institution.", + [] + ) + ); + } + + if (input.Statement.Standard is null + && input.Statement.Publication is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.MISSING_STATEMENT, + "Both standard and publication are null.", + [nameof(input), nameof(input.Statement).FirstCharToLower()] + ) + ); + } + + if (input.Statement.Standard is not null + && input.Statement.Publication is not null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.AMBIGUOUS_STATEMENT, + "Both standard and publication are non-null.", + [nameof(input), nameof(input.Statement).FirstCharToLower()] + ) + ); + } + + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + if (data is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [] + ) + ); + } + + var approval = new DataApproval( + DateTime.Now, + input.Signature, + input.KeyFingerprint, + input.Query, + input.Response, + currentUser.Id, + ReferenceType.FromInput(input.Statement) + ); + + data.Approvals.Add(approval); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + context.Remove(data); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + return new AddDataApprovalPayload( + approval, + new AddDataApprovalError( + AddDataApprovalErrorCode.SIGNING_FAILED, + $"Signing failed with message: {ex.Message}", + [] + ) + ); + } + return new AddDataApprovalPayload(approval); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Approvals/ApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs similarity index 74% rename from backend/src/GraphQl/Approvals/ApprovalPayload.cs rename to backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs index 0f18fcea..9ae1ee0c 100644 --- a/backend/src/GraphQl/Approvals/ApprovalPayload.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs @@ -1,34 +1,34 @@ using Database.Data; using System.Collections.Generic; -namespace Database.GraphQl.Approvals; +namespace Database.GraphQl.DataApprovals; -public abstract class ApprovalPayload +public abstract class DataApprovalPayload : Payload where TApprovalError : IUserError { - protected ApprovalPayload( + protected DataApprovalPayload( DataApproval dataApproval ) { DataApproval = dataApproval; } - protected ApprovalPayload( + protected DataApprovalPayload( IReadOnlyCollection errors ) { Errors = errors; } - protected ApprovalPayload( + protected DataApprovalPayload( TApprovalError error ) : this([error]) { } - protected ApprovalPayload( + protected DataApprovalPayload( DataApproval dataApproval, IReadOnlyCollection errors ) @@ -37,7 +37,7 @@ IReadOnlyCollection errors Errors = errors; } - protected ApprovalPayload( + protected DataApprovalPayload( DataApproval dataApproval, TApprovalError error ) diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs new file mode 100644 index 00000000..097bed90 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs @@ -0,0 +1,21 @@ +using HotChocolate.Types; +using Database.Data; +using Database.GraphQl.References; + +namespace Database.GraphQl.DataApprovals; + +public sealed class DataApprovalType + : ObjectType +{ + protected override void Configure(IObjectTypeDescriptor descriptor) + { + descriptor + .Field(t => t.Statement) + .Type() + .Resolve(context => context + .Parent() + .Statement + .TheReference + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/CreatePublicationInput.cs b/backend/src/GraphQl/Publications/PublicationInput.cs similarity index 82% rename from backend/src/GraphQl/Publications/CreatePublicationInput.cs rename to backend/src/GraphQl/Publications/PublicationInput.cs index 25d59a13..14f77164 100644 --- a/backend/src/GraphQl/Publications/CreatePublicationInput.cs +++ b/backend/src/GraphQl/Publications/PublicationInput.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl.Publications; -public sealed record CreatePublicationInput( +public sealed record PublicationInput( string? Title, string? Abstract, string? Section, diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs index 0d7dc210..fb9a6044 100644 --- a/backend/src/GraphQl/Publications/PublicationType.cs +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -6,6 +6,20 @@ namespace Database.GraphQl.Publications; public sealed class PublicationType : ObjectType { + internal static Publication FromInput(PublicationInput input) + { + return new Publication( + input.Title, + input.Abstract, + input.Section, + input.Authors, + input.Doi, + input.ArXiv, + input.Urn, + input.WebAddress + ); + } + protected override void Configure( IObjectTypeDescriptor descriptor ) diff --git a/backend/src/GraphQl/Publications/UpdatePublicationInput.cs b/backend/src/GraphQl/Publications/UpdatePublicationInput.cs deleted file mode 100644 index d1bf0c2b..00000000 --- a/backend/src/GraphQl/Publications/UpdatePublicationInput.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Database.GraphQl.Publications; - -public sealed record UpdatePublicationInput( - string? Title, - string? Abstract, - string? Section, - string[]? Authors, - string? Doi, - string? ArXiv, - string? Urn, - Uri? WebAddress -); \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceFilterType.cs b/backend/src/GraphQl/References/ReferenceFilterType.cs new file mode 100644 index 00000000..6512a5f5 --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceFilterType.cs @@ -0,0 +1,17 @@ +using HotChocolate.Data.Filters; +using Database.Data; + +namespace Database.GraphQl.References; + +public sealed class ReferenceFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Standard); + descriptor.Field(x => x.Publication); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceInput.cs b/backend/src/GraphQl/References/ReferenceInput.cs new file mode 100644 index 00000000..0f36684d --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceInput.cs @@ -0,0 +1,9 @@ +using Database.GraphQl.Publications; +using Database.GraphQl.Standards; + +namespace Database.GraphQl.References; + +public sealed record ReferenceInput( + StandardInput? Standard, + PublicationInput? Publication +); \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceSortType.cs b/backend/src/GraphQl/References/ReferenceSortType.cs new file mode 100644 index 00000000..3cce721b --- /dev/null +++ b/backend/src/GraphQl/References/ReferenceSortType.cs @@ -0,0 +1,17 @@ +using HotChocolate.Data.Sorting; +using Database.Data; + +namespace Database.GraphQl.References; + +public sealed class ReferenceSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Standard); + descriptor.Field(x => x.Publication); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs index 3489564e..673230b1 100644 --- a/backend/src/GraphQl/References/ReferenceType.cs +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -1,12 +1,36 @@ +using System; using Database.Data; +using Database.GraphQl.Publications; +using Database.GraphQl.Standards; using HotChocolate.Types; namespace Database.GraphQl.References; public sealed class ReferenceType - : InterfaceType + : UnionType { - protected override void Configure(IInterfaceTypeDescriptor descriptor) + internal static Reference FromInput(ReferenceInput input) + { + if (input?.Standard is null && input?.Publication is null) + { + throw new ArgumentException("Both the reference's standard and publication are null.", nameof(input)); + } + if (input.Standard is not null && input.Publication is not null) + { + throw new ArgumentException("Both the reference's standard and publication are non-null.", nameof(input)); + } + if (input.Standard is not null) + { + return new Reference(StandardType.FromInput(input.Standard)); + } + else if (input.Publication is not null) + { + return new Reference(PublicationType.FromInput(input.Publication)); + } + throw new ArgumentException("Impossible!", nameof(input)); + } + + protected override void Configure(IUnionTypeDescriptor descriptor) { descriptor.Name(nameof(IReference)[1..]); } diff --git a/backend/src/GraphQl/Standards/CreateStandardInput.cs b/backend/src/GraphQl/Standards/StandardInput.cs similarity index 87% rename from backend/src/GraphQl/Standards/CreateStandardInput.cs rename to backend/src/GraphQl/Standards/StandardInput.cs index 7323bb2a..e17e8a59 100644 --- a/backend/src/GraphQl/Standards/CreateStandardInput.cs +++ b/backend/src/GraphQl/Standards/StandardInput.cs @@ -4,7 +4,7 @@ namespace Database.GraphQl.Standards; -public sealed record CreateStandardInput( +public sealed record StandardInput( string? Title, string? Abstract, string? Section, diff --git a/backend/src/GraphQl/Standards/StandardType.cs b/backend/src/GraphQl/Standards/StandardType.cs index 8865bf78..ffd1b013 100644 --- a/backend/src/GraphQl/Standards/StandardType.cs +++ b/backend/src/GraphQl/Standards/StandardType.cs @@ -6,6 +6,25 @@ namespace Database.GraphQl.Standards; public sealed class StandardType : ObjectType { + internal static Standard FromInput(StandardInput input) + { + return new Standard( + input.Title, + input.Abstract, + input.Section, + input.Year, + input.Standardizers, + input.Locator + ) + { + Numeration = new Numeration( + input.Numeration.Prefix, + input.Numeration.MainNumber, + input.Numeration.Suffix + ) + }; + } + protected override void Configure( IObjectTypeDescriptor descriptor ) diff --git a/backend/src/GraphQl/Standards/UpdateStandardInput.cs b/backend/src/GraphQl/Standards/UpdateStandardInput.cs deleted file mode 100644 index 0990987c..00000000 --- a/backend/src/GraphQl/Standards/UpdateStandardInput.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Database.Enumerations; -using Database.GraphQl.Numerations; - -namespace Database.GraphQl.Standards; - -public sealed record UpdateStandardInput( - string? Title, - string? Abstract, - string? Section, - int? Year, - UpdateNumerationInput Numeration, - Standardizer[] Standardizers, - Uri? Locator -); \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 1548b093..78945c85 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -30,21 +30,15 @@ interface Node { id: ID! } -interface Reference { - abstract: String - section: String - title: String -} - -type AddApprovalError { - code: AddApprovalErrorCode! +type AddDataApprovalError { + code: AddDataApprovalErrorCode! message: String! path: [String!]! } -type AddApprovalPayload { +type AddDataApprovalPayload { dataApproval: DataApproval - errors: [AddApprovalError!] + errors: [AddDataApprovalError!] query: Query! } @@ -223,7 +217,7 @@ type DataApproval { query: String! response: String! signature: String! - statement: Reference + statement: Reference @cost(weight: "10") timestamp: DateTime! } @@ -447,7 +441,7 @@ type MethodAsServicePayload { type Mutation { addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - addApprovalToData(input: ApprovalInput!): AddApprovalPayload! @cost(weight: "10") + addDataApproval(input: DataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") @@ -584,7 +578,7 @@ type PhotovoltaicDataEdge { node: PhotovoltaicData! } -type Publication implements Reference { +type Publication { abstract: String "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." arXiv: String @@ -642,7 +636,7 @@ type Results { } "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." -type Standard implements Reference { +type Standard { abstract: String locator: Url numeration: Numeration! @@ -713,6 +707,8 @@ type Wavelengths { wavelength: Int! } +union Reference = Publication | Standard + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -731,12 +727,6 @@ input AppliedMethodSortInput { methodId: SortEnumType @cost(weight: "10") } -input ApprovalInput { - approval: DataApprovalInput! - creatorId: Uuid! - dataId: Uuid! -} - input BooleanPropositionInput { equalTo: Boolean @cost(weight: "10") notEqualTo: Boolean @cost(weight: "10") @@ -875,27 +865,6 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } -input CreatePublicationInput { - abstract: String - arXiv: String - authors: [String!] - doi: String - section: String - title: String - urn: String - webAddress: Url -} - -input CreateStandardInput { - abstract: String - locator: Url - numeration: CreateNumerationInput! - section: String - standardizers: [Standardizer!]! - title: String - year: Int -} - input CrossDatabaseDataReferenceInput { databaseId: Uuid! dataId: Uuid! @@ -921,12 +890,13 @@ input DataAccessRightsInput { input DataApprovalInput { approverId: Uuid! + creatorId: Uuid! + dataId: Uuid! keyFingerprint: String! - publication: CreatePublicationInput query: String! response: String! signature: String! - standard: CreateStandardInput + statement: ReferenceInput! timestamp: DateTime! } @@ -939,9 +909,7 @@ input DataApprovalPropositionInput { keyFingerprint: StringPropositionInput query: StringPropositionInput response: StringPropositionInput - publication: PublicationPropositionInput - standard: StandardPropositionInput - statement: IReferencePropositionInput + statement: ReferencePropositionInput } input DataKindPropositionInput { @@ -1103,14 +1071,6 @@ input IDataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } -input IReferencePropositionInput { - and: [IReferencePropositionInput!] - or: [IReferencePropositionInput!] - title: StringPropositionInput - abstract: StringPropositionInput - section: StringPropositionInput -} - input InstitutionAccessRightsInput { allowedDatasetsPerTimeSpan: NonNegativeInt allowedUserCount: NonNegativeInt @@ -1329,6 +1289,17 @@ input PhotovoltaicDataSortInput { id: SortEnumType @cost(weight: "10") } +input PublicationInput { + abstract: String + arXiv: String + authors: [String!] + doi: String + section: String + title: String + urn: String + webAddress: Url +} + input PublicationPropositionInput { and: [PublicationPropositionInput!] or: [PublicationPropositionInput!] @@ -1347,6 +1318,18 @@ input PublicationPropositionInput { section: StringPropositionInput } +input ReferenceInput { + publication: PublicationInput + standard: StandardInput +} + +input ReferencePropositionInput { + and: [ReferencePropositionInput!] + or: [ReferencePropositionInput!] + standard: StandardPropositionInput + publication: PublicationPropositionInput +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1355,6 +1338,16 @@ input RootGetHttpsResourceInput { hashValue: String! } +input StandardInput { + abstract: String + locator: Url + numeration: CreateNumerationInput! + section: String + standardizers: [Standardizer!]! + title: String + year: Int +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." input StandardPropositionInput { and: [StandardPropositionInput!] @@ -1474,12 +1467,14 @@ input UuidPropositionInput { notLessThanOrEqualTo: Uuid @cost(weight: "10") } -enum AddApprovalErrorCode { +enum AddDataApprovalErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA SIGNING_FAILED + AMBIGUOUS_STATEMENT + MISSING_STATEMENT } "Defines when a policy shall be executed." From d78f5586cae214feaf0a884bef2222eafc1917de Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 May 2025 17:18:21 +0200 Subject: [PATCH 354/878] Migrate database to reflect latest modelling of reference type --- ...0151605_ModelReferenceProperly.Designer.cs | 2544 +++++++++++++++++ .../20250530151605_ModelReferenceProperly.cs | 1103 +++++++ .../ApplicationDbContextModelSnapshot.cs | 732 +++-- 3 files changed, 4068 insertions(+), 311 deletions(-) create mode 100644 backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs create mode 100644 backend/src/Migrations/20250530151605_ModelReferenceProperly.cs diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs new file mode 100644 index 00000000..3ab43180 --- /dev/null +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs @@ -0,0 +1,2544 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250530151605_ModelReferenceProperly")] + partial class ModelReferenceProperly + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs new file mode 100644 index 00000000..f72624bf --- /dev/null +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs @@ -0,0 +1,1103 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class ModelReferenceProperly : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "optical_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "geometric_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "calorimetric_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "geometric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Abstract"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 84869e23..b1b7a8de 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; @@ -802,7 +802,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("CalorimetricDataId"); - b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => { b2.Property("DataApprovalCalorimetricDataId") .HasColumnType("uuid"); @@ -810,29 +810,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("DataApprovalId") .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); - - b2.Property("ArXiv") - .HasColumnType("text"); - - b2.PrimitiveCollection("Authors") - .HasColumnType("text[]"); - - b2.Property("Doi") - .HasColumnType("text"); - - b2.Property("Section") - .HasColumnType("text"); - - b2.Property("Title") - .HasColumnType("text"); - - b2.Property("Urn") - .HasColumnType("text"); - - b2.Property("WebAddress") - .HasColumnType("text"); + b2.Property("Exists") + .HasColumnType("boolean"); b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); @@ -840,75 +819,118 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.WithOwner() .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); - }); - b1.OwnsOne("Database.Data.Standard", "Standard", b2 => - { - b2.Property("DataApprovalCalorimetricDataId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); - b2.Property("DataApprovalId") - .HasColumnType("integer"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Locator") - .HasColumnType("text"); + b3.Property("ArXiv") + .HasColumnType("text"); - b2.Property("Section") - .HasColumnType("text"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b2.PrimitiveCollection("Standardizers") - .IsRequired() - .HasColumnType("database.standardizer[]"); + b3.Property("Doi") + .HasColumnType("text"); - b2.Property("Title") - .HasColumnType("text"); + b3.Property("Section") + .HasColumnType("text"); - b2.Property("Year") - .HasColumnType("integer"); + b3.Property("Title") + .HasColumnType("text"); - b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + b3.Property("Urn") + .HasColumnType("text"); - b2.ToTable("calorimetric_data_Approvals", "database"); + b3.Property("WebAddress") + .HasColumnType("text"); - b2.WithOwner() - .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); - b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => { - b3.Property("StandardDataApprovalCalorimetricDataId") + b3.Property("ReferenceDataApprovalCalorimetricDataId") .HasColumnType("uuid"); - b3.Property("StandardDataApprovalId") + b3.Property("ReferenceDataApprovalId") .HasColumnType("integer"); - b3.Property("MainNumber") - .IsRequired() + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") .HasColumnType("text"); - b3.Property("Prefix") + b3.Property("Section") .HasColumnType("text"); - b3.Property("Suffix") + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") .HasColumnType("text"); - b3.HasKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); b3.ToTable("calorimetric_data_Approvals", "database"); b3.WithOwner() - .HasForeignKey("StandardDataApprovalCalorimetricDataId", "StandardDataApprovalId"); + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); }); - b2.Navigation("Numeration") - .IsRequired(); - }); + b2.Navigation("Publication"); - b1.Navigation("Publication"); + b2.Navigation("Standard"); + }); - b1.Navigation("Standard"); + b1.Navigation("Statement") + .IsRequired(); }); b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => @@ -1119,7 +1141,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("GeometricDataId"); - b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => { b2.Property("DataApprovalGeometricDataId") .HasColumnType("uuid"); @@ -1127,29 +1149,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("DataApprovalId") .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); - - b2.Property("ArXiv") - .HasColumnType("text"); - - b2.PrimitiveCollection("Authors") - .HasColumnType("text[]"); - - b2.Property("Doi") - .HasColumnType("text"); - - b2.Property("Section") - .HasColumnType("text"); - - b2.Property("Title") - .HasColumnType("text"); - - b2.Property("Urn") - .HasColumnType("text"); - - b2.Property("WebAddress") - .HasColumnType("text"); + b2.Property("Exists") + .HasColumnType("boolean"); b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); @@ -1157,75 +1158,118 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.WithOwner() .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); - }); - b1.OwnsOne("Database.Data.Standard", "Standard", b2 => - { - b2.Property("DataApprovalGeometricDataId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); - b2.Property("DataApprovalId") - .HasColumnType("integer"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Locator") - .HasColumnType("text"); + b3.Property("ArXiv") + .HasColumnType("text"); - b2.Property("Section") - .HasColumnType("text"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b2.PrimitiveCollection("Standardizers") - .IsRequired() - .HasColumnType("database.standardizer[]"); + b3.Property("Doi") + .HasColumnType("text"); - b2.Property("Title") - .HasColumnType("text"); + b3.Property("Section") + .HasColumnType("text"); - b2.Property("Year") - .HasColumnType("integer"); + b3.Property("Title") + .HasColumnType("text"); - b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + b3.Property("Urn") + .HasColumnType("text"); - b2.ToTable("geometric_data_Approvals", "database"); + b3.Property("WebAddress") + .HasColumnType("text"); - b2.WithOwner() - .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); - b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => { - b3.Property("StandardDataApprovalGeometricDataId") + b3.Property("ReferenceDataApprovalGeometricDataId") .HasColumnType("uuid"); - b3.Property("StandardDataApprovalId") + b3.Property("ReferenceDataApprovalId") .HasColumnType("integer"); - b3.Property("MainNumber") - .IsRequired() + b3.Property("Abstract") .HasColumnType("text"); - b3.Property("Prefix") + b3.Property("Locator") .HasColumnType("text"); - b3.Property("Suffix") + b3.Property("Section") .HasColumnType("text"); - b3.HasKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); b3.ToTable("geometric_data_Approvals", "database"); b3.WithOwner() - .HasForeignKey("StandardDataApprovalGeometricDataId", "StandardDataApprovalId"); + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); }); - b2.Navigation("Numeration") - .IsRequired(); - }); + b2.Navigation("Publication"); - b1.Navigation("Publication"); + b2.Navigation("Standard"); + }); - b1.Navigation("Standard"); + b1.Navigation("Statement") + .IsRequired(); }); b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => @@ -1554,7 +1598,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("HygrothermalDataId"); - b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => { b2.Property("DataApprovalHygrothermalDataId") .HasColumnType("uuid"); @@ -1562,29 +1606,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("DataApprovalId") .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); - - b2.Property("ArXiv") - .HasColumnType("text"); - - b2.PrimitiveCollection("Authors") - .HasColumnType("text[]"); - - b2.Property("Doi") - .HasColumnType("text"); - - b2.Property("Section") - .HasColumnType("text"); - - b2.Property("Title") - .HasColumnType("text"); - - b2.Property("Urn") - .HasColumnType("text"); - - b2.Property("WebAddress") - .HasColumnType("text"); + b2.Property("Exists") + .HasColumnType("boolean"); b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); @@ -1592,75 +1615,118 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.WithOwner() .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); - }); - b1.OwnsOne("Database.Data.Standard", "Standard", b2 => - { - b2.Property("DataApprovalHygrothermalDataId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); - b2.Property("DataApprovalId") - .HasColumnType("integer"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Locator") - .HasColumnType("text"); + b3.Property("ArXiv") + .HasColumnType("text"); - b2.Property("Section") - .HasColumnType("text"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b2.PrimitiveCollection("Standardizers") - .IsRequired() - .HasColumnType("database.standardizer[]"); + b3.Property("Doi") + .HasColumnType("text"); - b2.Property("Title") - .HasColumnType("text"); + b3.Property("Section") + .HasColumnType("text"); - b2.Property("Year") - .HasColumnType("integer"); + b3.Property("Title") + .HasColumnType("text"); - b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + b3.Property("Urn") + .HasColumnType("text"); - b2.ToTable("hygrothermal_data_Approvals", "database"); + b3.Property("WebAddress") + .HasColumnType("text"); - b2.WithOwner() - .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); - b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => { - b3.Property("StandardDataApprovalHygrothermalDataId") + b3.Property("ReferenceDataApprovalHygrothermalDataId") .HasColumnType("uuid"); - b3.Property("StandardDataApprovalId") + b3.Property("ReferenceDataApprovalId") .HasColumnType("integer"); - b3.Property("MainNumber") - .IsRequired() + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") .HasColumnType("text"); - b3.Property("Prefix") + b3.Property("Section") .HasColumnType("text"); - b3.Property("Suffix") + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") .HasColumnType("text"); - b3.HasKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); b3.ToTable("hygrothermal_data_Approvals", "database"); b3.WithOwner() - .HasForeignKey("StandardDataApprovalHygrothermalDataId", "StandardDataApprovalId"); + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); }); - b2.Navigation("Numeration") - .IsRequired(); - }); + b2.Navigation("Publication"); - b1.Navigation("Publication"); + b2.Navigation("Standard"); + }); - b1.Navigation("Standard"); + b1.Navigation("Statement") + .IsRequired(); }); b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => @@ -1899,7 +1965,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("OpticalDataId"); - b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => { b2.Property("DataApprovalOpticalDataId") .HasColumnType("uuid"); @@ -1907,29 +1973,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("DataApprovalId") .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); - - b2.Property("ArXiv") - .HasColumnType("text"); - - b2.PrimitiveCollection("Authors") - .HasColumnType("text[]"); - - b2.Property("Doi") - .HasColumnType("text"); - - b2.Property("Section") - .HasColumnType("text"); - - b2.Property("Title") - .HasColumnType("text"); - - b2.Property("Urn") - .HasColumnType("text"); - - b2.Property("WebAddress") - .HasColumnType("text"); + b2.Property("Exists") + .HasColumnType("boolean"); b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); @@ -1937,75 +1982,118 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.WithOwner() .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); - }); - b1.OwnsOne("Database.Data.Standard", "Standard", b2 => - { - b2.Property("DataApprovalOpticalDataId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); - b2.Property("DataApprovalId") - .HasColumnType("integer"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Locator") - .HasColumnType("text"); + b3.Property("ArXiv") + .HasColumnType("text"); - b2.Property("Section") - .HasColumnType("text"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b2.PrimitiveCollection("Standardizers") - .IsRequired() - .HasColumnType("database.standardizer[]"); + b3.Property("Doi") + .HasColumnType("text"); - b2.Property("Title") - .HasColumnType("text"); + b3.Property("Section") + .HasColumnType("text"); - b2.Property("Year") - .HasColumnType("integer"); + b3.Property("Title") + .HasColumnType("text"); - b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + b3.Property("Urn") + .HasColumnType("text"); - b2.ToTable("optical_data_Approvals", "database"); + b3.Property("WebAddress") + .HasColumnType("text"); - b2.WithOwner() - .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); - b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => { - b3.Property("StandardDataApprovalOpticalDataId") + b3.Property("ReferenceDataApprovalOpticalDataId") .HasColumnType("uuid"); - b3.Property("StandardDataApprovalId") + b3.Property("ReferenceDataApprovalId") .HasColumnType("integer"); - b3.Property("MainNumber") - .IsRequired() + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") .HasColumnType("text"); - b3.Property("Prefix") + b3.Property("Section") .HasColumnType("text"); - b3.Property("Suffix") + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") .HasColumnType("text"); - b3.HasKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); b3.ToTable("optical_data_Approvals", "database"); b3.WithOwner() - .HasForeignKey("StandardDataApprovalOpticalDataId", "StandardDataApprovalId"); + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); }); - b2.Navigation("Numeration") - .IsRequired(); - }); + b2.Navigation("Publication"); - b1.Navigation("Publication"); + b2.Navigation("Standard"); + }); - b1.Navigation("Standard"); + b1.Navigation("Statement") + .IsRequired(); }); b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => @@ -2218,7 +2306,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.WithOwner() .HasForeignKey("PhotovoltaicDataId"); - b1.OwnsOne("Database.Data.Publication", "Publication", b2 => + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => { b2.Property("DataApprovalPhotovoltaicDataId") .HasColumnType("uuid"); @@ -2226,29 +2314,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.Property("DataApprovalId") .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); - - b2.Property("ArXiv") - .HasColumnType("text"); - - b2.PrimitiveCollection("Authors") - .HasColumnType("text[]"); - - b2.Property("Doi") - .HasColumnType("text"); - - b2.Property("Section") - .HasColumnType("text"); - - b2.Property("Title") - .HasColumnType("text"); - - b2.Property("Urn") - .HasColumnType("text"); - - b2.Property("WebAddress") - .HasColumnType("text"); + b2.Property("Exists") + .HasColumnType("boolean"); b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); @@ -2256,75 +2323,118 @@ protected override void BuildModel(ModelBuilder modelBuilder) b2.WithOwner() .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); - }); - b1.OwnsOne("Database.Data.Standard", "Standard", b2 => - { - b2.Property("DataApprovalPhotovoltaicDataId") - .HasColumnType("uuid"); + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); - b2.Property("DataApprovalId") - .HasColumnType("integer"); + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); - b2.Property("Abstract") - .HasColumnType("text"); + b3.Property("Abstract") + .HasColumnType("text"); - b2.Property("Locator") - .HasColumnType("text"); + b3.Property("ArXiv") + .HasColumnType("text"); - b2.Property("Section") - .HasColumnType("text"); + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); - b2.PrimitiveCollection("Standardizers") - .IsRequired() - .HasColumnType("database.standardizer[]"); + b3.Property("Doi") + .HasColumnType("text"); - b2.Property("Title") - .HasColumnType("text"); + b3.Property("Section") + .HasColumnType("text"); - b2.Property("Year") - .HasColumnType("integer"); + b3.Property("Title") + .HasColumnType("text"); - b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + b3.Property("Urn") + .HasColumnType("text"); - b2.ToTable("photovoltaic_data_Approvals", "database"); + b3.Property("WebAddress") + .HasColumnType("text"); - b2.WithOwner() - .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); - b2.OwnsOne("Database.Data.Numeration", "Numeration", b3 => + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => { - b3.Property("StandardDataApprovalPhotovoltaicDataId") + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") .HasColumnType("uuid"); - b3.Property("StandardDataApprovalId") + b3.Property("ReferenceDataApprovalId") .HasColumnType("integer"); - b3.Property("MainNumber") - .IsRequired() + b3.Property("Abstract") .HasColumnType("text"); - b3.Property("Prefix") + b3.Property("Locator") .HasColumnType("text"); - b3.Property("Suffix") + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") .HasColumnType("text"); - b3.HasKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); b3.ToTable("photovoltaic_data_Approvals", "database"); b3.WithOwner() - .HasForeignKey("StandardDataApprovalPhotovoltaicDataId", "StandardDataApprovalId"); + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); }); - b2.Navigation("Numeration") - .IsRequired(); - }); + b2.Navigation("Publication"); - b1.Navigation("Publication"); + b2.Navigation("Standard"); + }); - b1.Navigation("Standard"); + b1.Navigation("Statement") + .IsRequired(); }); b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => From ca58dd4d12541544dc042c9b205245c6fb4dc64e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 May 2025 17:23:20 +0200 Subject: [PATCH 355/878] Add SQL migration and rollback scripts --- backend/src/Migrations/current | 2 +- ..._20250530151605_ModelReferenceProperly.sql | 228 ++++++++++++++++++ backend/src/Migrations/previous | 2 +- ..._20250530151605_ModelReferenceProperly.sql | 228 ++++++++++++++++++ 4 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql create mode 100644 backend/src/Migrations/rollback_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index bf1ae126..3a1e20b0 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250331141152_AddAccessRights +20250530151605_ModelReferenceProperly diff --git a/backend/src/Migrations/migrate_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql b/backend/src/Migrations/migrate_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql new file mode 100644 index 00000000..6482f7fb --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql @@ -0,0 +1,228 @@ +START TRANSACTION; +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250527144412_AddDataAccessRights', '9.0.5'); + +ALTER TABLE database.access_rights DROP CONSTRAINT "PK_access_rights"; + +ALTER TABLE database.access_rights RENAME TO institution_access_rights; + +ALTER TABLE database.institution_access_rights ADD CONSTRAINT "PK_institution_access_rights" PRIMARY KEY ("Id"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250527150325_MoveInstitutionAccessRights', '9.0.5'); + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."optical_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."geometric_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250530151605_ModelReferenceProperly', '9.0.5'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index a7b7a40d..bf1ae126 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20250218172859_MakeDescriptionOptional +20250331141152_AddAccessRights diff --git a/backend/src/Migrations/rollback_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql b/backend/src/Migrations/rollback_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql new file mode 100644 index 00000000..71b5dfb3 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250331141152_AddAccessRights_to_20250530151605_ModelReferenceProperly.sql @@ -0,0 +1,228 @@ +START TRANSACTION; +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Statement_Exists"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Statement_Exists"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Statement_Exists"; + +ALTER TABLE database."geometric_data_Approvals" DROP COLUMN "Statement_Exists"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Statement_Exists"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Year" TO "Standard_Year"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Title" TO "Standard_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Standardizers" TO "Standard_Standardizers"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Section" TO "Standard_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Suffix" TO "Standard_Numeration_Suffix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Prefix" TO "Standard_Numeration_Prefix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_MainNumber" TO "Standard_Numeration_MainNumber"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Locator" TO "Standard_Locator"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Standard_Abstract" TO "Standard_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_WebAddress" TO "Publication_WebAddress"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Urn" TO "Publication_Urn"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Title" TO "Publication_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Section" TO "Publication_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Doi" TO "Publication_Doi"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Authors" TO "Publication_Authors"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_ArXiv" TO "Publication_ArXiv"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Statement_Publication_Abstract" TO "Publication_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Year" TO "Standard_Year"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Title" TO "Standard_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Standardizers" TO "Standard_Standardizers"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Section" TO "Standard_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Suffix" TO "Standard_Numeration_Suffix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Prefix" TO "Standard_Numeration_Prefix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_MainNumber" TO "Standard_Numeration_MainNumber"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Locator" TO "Standard_Locator"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Standard_Abstract" TO "Standard_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_WebAddress" TO "Publication_WebAddress"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Urn" TO "Publication_Urn"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Title" TO "Publication_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Section" TO "Publication_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Doi" TO "Publication_Doi"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Authors" TO "Publication_Authors"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_ArXiv" TO "Publication_ArXiv"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Statement_Publication_Abstract" TO "Publication_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Year" TO "Standard_Year"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Title" TO "Standard_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Standardizers" TO "Standard_Standardizers"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Section" TO "Standard_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Suffix" TO "Standard_Numeration_Suffix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Prefix" TO "Standard_Numeration_Prefix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_MainNumber" TO "Standard_Numeration_MainNumber"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Locator" TO "Standard_Locator"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Standard_Abstract" TO "Standard_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_WebAddress" TO "Publication_WebAddress"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Urn" TO "Publication_Urn"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Title" TO "Publication_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Section" TO "Publication_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Doi" TO "Publication_Doi"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Authors" TO "Publication_Authors"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_ArXiv" TO "Publication_ArXiv"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Statement_Publication_Abstract" TO "Publication_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Year" TO "Standard_Year"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Title" TO "Standard_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Standardizers" TO "Standard_Standardizers"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Section" TO "Standard_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Suffix" TO "Standard_Numeration_Suffix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Prefix" TO "Standard_Numeration_Prefix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_MainNumber" TO "Standard_Numeration_MainNumber"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Locator" TO "Standard_Locator"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Standard_Abstract" TO "Standard_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_WebAddress" TO "Publication_WebAddress"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Urn" TO "Publication_Urn"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Title" TO "Publication_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Section" TO "Publication_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Doi" TO "Publication_Doi"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Authors" TO "Publication_Authors"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_ArXiv" TO "Publication_ArXiv"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Statement_Publication_Abstract" TO "Publication_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Year" TO "Standard_Year"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Title" TO "Standard_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Standardizers" TO "Standard_Standardizers"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Section" TO "Standard_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Suffix" TO "Standard_Numeration_Suffix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_Prefix" TO "Standard_Numeration_Prefix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Numeration_MainNumber" TO "Standard_Numeration_MainNumber"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Locator" TO "Standard_Locator"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Standard_Abstract" TO "Standard_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_WebAddress" TO "Publication_WebAddress"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Urn" TO "Publication_Urn"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Title" TO "Publication_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Section" TO "Publication_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Doi" TO "Publication_Doi"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Authors" TO "Publication_Authors"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_ArXiv" TO "Publication_ArXiv"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Statement_Publication_Abstract" TO "Publication_Abstract"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250530151605_ModelReferenceProperly'; + +ALTER TABLE database.institution_access_rights DROP CONSTRAINT "PK_institution_access_rights"; + +ALTER TABLE database.institution_access_rights RENAME TO access_rights; + +ALTER TABLE database.access_rights ADD CONSTRAINT "PK_access_rights" PRIMARY KEY ("Id"); + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250527150325_MoveInstitutionAccessRights'; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedApplications"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + +ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedApplications"; + +ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + +ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedApplications"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + +ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + +ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + +ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250527144412_AddDataAccessRights'; + +COMMIT; + From 8c7ac37a80a270b0696d3fcaed54fe9da4d52216 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 May 2025 18:07:22 +0200 Subject: [PATCH 356/878] Add required property `Exists` to `Publication` to identify whether the entity exists --- backend/src/Data/Publication.cs | 13 + ...redPropertyExistsToPublication.Designer.cs | 2559 +++++++++++++++++ ..._AddRequiredPropertyExistsToPublication.cs | 78 + .../ApplicationDbContextModelSnapshot.cs | 15 + backend/src/Migrations/current | 2 +- ...AddRequiredPropertyExistsToPublication.sql | 16 + backend/src/Migrations/previous | 2 +- ...AddRequiredPropertyExistsToPublication.sql | 16 + 8 files changed, 2699 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs create mode 100644 backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs create mode 100644 backend/src/Migrations/migrate_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql create mode 100644 backend/src/Migrations/rollback_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs index 15dd27fe..0cc7f2f9 100644 --- a/backend/src/Data/Publication.cs +++ b/backend/src/Data/Publication.cs @@ -50,4 +50,17 @@ public sealed class Publication( [MinLength(1)] [GraphQLDescription("Referenced section")] public string? Section { get; private set; } = section; + + // To evade the error + // --- + // Entity type `Publication` is an optional dependent using table sharing and + // containing other dependents without any required non shared property to + // identify whether the entity exists. If all nullable properties contain a + // null value in database then an object instance won't be created in the + // query causing nested dependent's values to be lost. Add a required + // property to create instances with null values for other properties or + // mark the incoming navigation as required to always create an instance. + // --- + // I introduce this non-null property. + [Required] public bool Exists { get; private set; } = true; } \ No newline at end of file diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs new file mode 100644 index 00000000..48ed88ae --- /dev/null +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs @@ -0,0 +1,2559 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250530155318_AddRequiredPropertyExistsToPublication")] + partial class AddRequiredPropertyExistsToPublication + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs new file mode 100644 index 00000000..c480fb7c --- /dev/null +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddRequiredPropertyExistsToPublication : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "optical_data_Approvals", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "geometric_data_Approvals", + type: "boolean", + nullable: true); + + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "calorimetric_data_Approvals", + type: "boolean", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "geometric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "calorimetric_data_Approvals"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index b1b7a8de..589e74da 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -840,6 +840,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("Doi") .HasColumnType("text"); + b3.Property("Exists") + .HasColumnType("boolean"); + b3.Property("Section") .HasColumnType("text"); @@ -1179,6 +1182,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("Doi") .HasColumnType("text"); + b3.Property("Exists") + .HasColumnType("boolean"); + b3.Property("Section") .HasColumnType("text"); @@ -1636,6 +1642,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("Doi") .HasColumnType("text"); + b3.Property("Exists") + .HasColumnType("boolean"); + b3.Property("Section") .HasColumnType("text"); @@ -2003,6 +2012,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("Doi") .HasColumnType("text"); + b3.Property("Exists") + .HasColumnType("boolean"); + b3.Property("Section") .HasColumnType("text"); @@ -2344,6 +2356,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("Doi") .HasColumnType("text"); + b3.Property("Exists") + .HasColumnType("boolean"); + b3.Property("Section") .HasColumnType("text"); diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index 3a1e20b0..f4b3371b 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250530151605_ModelReferenceProperly +20250530155318_AddRequiredPropertyExistsToPublication diff --git a/backend/src/Migrations/migrate_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql b/backend/src/Migrations/migrate_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql new file mode 100644 index 00000000..ae056941 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql @@ -0,0 +1,16 @@ +START TRANSACTION; +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."optical_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."geometric_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250530155318_AddRequiredPropertyExistsToPublication', '9.0.5'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index bf1ae126..3a1e20b0 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20250331141152_AddAccessRights +20250530151605_ModelReferenceProperly diff --git a/backend/src/Migrations/rollback_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql b/backend/src/Migrations/rollback_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql new file mode 100644 index 00000000..e03f4608 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250530151605_ModelReferenceProperly_to_20250530155318_AddRequiredPropertyExistsToPublication.sql @@ -0,0 +1,16 @@ +START TRANSACTION; +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Statement_Publication_Exists"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Statement_Publication_Exists"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Statement_Publication_Exists"; + +ALTER TABLE database."geometric_data_Approvals" DROP COLUMN "Statement_Publication_Exists"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Statement_Publication_Exists"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250530155318_AddRequiredPropertyExistsToPublication'; + +COMMIT; + From 53bf9dcc80b407e38e35cfcb9ba95de7bf5ca10b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 May 2025 18:17:03 +0200 Subject: [PATCH 357/878] Mention that logs can also eat up all disk space --- README.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 21abbfa3..96497374 100644 --- a/README.md +++ b/README.md @@ -188,8 +188,10 @@ executing it `make SQL=${SCRIPT_PATH} sql`. When your hard-disk starts to grow full, it may be the case that Docker does not clean-up anonymous volumes properly. You can do so manually by running -`docker system prune` potentially with the argument `--all`. Note that this may -result in loss of data. +`docker system prune` potentially with the arguments `--volumes` and/or +`--all`. Note that this may result in loss of data. It may also be the case +that the log files grew huge. You can delete them by running +`rm ./backend/src/logs/*`. When the `frontend` Docker image does not build in production because of an unused import in an automatically generated file, for example, one in the @@ -205,9 +207,10 @@ typescript: { The same can happen in development when running `make build` (or `yarn run build`) in the shell entered by `make shellf`. In that case, remove the -offending import manually in the file and try again. Do not disable build -errors in development because when you do so, build errors in non-generated -files may leak into the code base. +offending import manually in the file and try again, for example using tail +like so `tail -n +5 ./__generated__/queries/... > x.tmp && mv x.tmp ...` . Do +not disable build errors in development because when you do so, build errors in +non-generated files may leak into the code base. ## Deployment From a21deab110d513113139def3881f21d2b5a49949 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 31 May 2025 13:13:59 +0200 Subject: [PATCH 358/878] Correct description of `down` and clear backend logs --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a5553665..469ba0e8 100644 --- a/Makefile +++ b/Makefile @@ -80,9 +80,10 @@ up : build ## (Re)create, and start containers (after building images if necessa --detach .PHONY : up -down : ## Stop containers and remove containers, networks, volumes, and images created by `up` +down : ## Stop containers and remove containers and networks created by `up` and clear backend logs ${docker_compose} down \ --remove-orphans + rm ./backend/src/logs/* .PHONY : down restart : ## Restart all stopped and running containers From f38e20288d180ecad36ecd5e41f596461228887e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 31 May 2025 13:19:01 +0200 Subject: [PATCH 359/878] Add GNU Make target to `prune` docker system --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 469ba0e8..c3d88547 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,11 @@ restart : ## Restart all stopped and running containers ${docker_compose} restart .PHONY : restart +prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes + docker system prune \ + --volumes +.PHONY : prune + logs : ## Follow logs ${docker_compose} logs \ --since=24h \ From ba7e6a23f60fa1931bd37718bf731907301010a0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 14 Jul 2025 15:49:07 +0200 Subject: [PATCH 360/878] Use `xbase` without extension as default database name --- Makefile | 6 +++--- backend/src/appsettings.development.json | 2 +- docker-compose.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index c3d88547..a7c57420 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ docker_compose = \ --file docker-compose.yml \ --project-name ${NAME} -database_name = xbase_development +database_name = xbase # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -180,7 +180,7 @@ list : ## List all containers with health status .PHONY : list createdb : DBNAME = ${database_name} -createdb : ## Create database with name `${DBNAME}` defaulting to `xbase_development` +createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} exec \ database \ bash -c " \ @@ -189,7 +189,7 @@ createdb : ## Create database with name `${DBNAME}` defaulting to `xbase_develop .PHONY : createdb dropdb : DBNAME = ${database_name} -dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase_development` +dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} exec \ database \ bash -c " \ diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index 6f8636cc..b44c13df 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -8,6 +8,6 @@ } }, "Database": { - "ConnectionString": "Host=database; Port=5432; Database=xbase_development; User Id=postgres; Password=postgres; Maximum Pool Size=90;" + "ConnectionString": "Host=database; Port=5432; Database=xbase; User Id=postgres; Password=postgres; Maximum Pool Size=90;" } } diff --git a/docker-compose.yml b/docker-compose.yml index a8762d98..556a71dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -105,7 +105,7 @@ services: # - 8080:8080 environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables - # - XBASE_DATABASE__CONNECTION_STRING="Host=database;Port=5432;Database=xbase_development;User Id=postgres;Password=postgres;" + # - XBASE_DATABASE__CONNECTION_STRING="Host=database;Port=5432;Database=xbase;User Id=postgres;Password=postgres;" - ASPNETCORE_URLS=http://0.0.0.0:8080 - ASPNETCORE_ENVIRONMENT=development - XBASE_Host=https://${HOST}:${HTTPS_PORT} From 2a58e9118760f1945412bb376158db062a0c22c6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 14 Jul 2025 16:10:25 +0200 Subject: [PATCH 361/878] Ignore when logs cannot be removed (usually becuase the directory does not exist) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index a7c57420..af5026a5 100644 --- a/Makefile +++ b/Makefile @@ -83,7 +83,7 @@ up : build ## (Re)create, and start containers (after building images if necessa down : ## Stop containers and remove containers and networks created by `up` and clear backend logs ${docker_compose} down \ --remove-orphans - rm ./backend/src/logs/* + -rm ./backend/src/logs/* .PHONY : down restart : ## Restart all stopped and running containers @@ -97,7 +97,7 @@ prune : ## Remove all unused containers, unused networks, unused and dangling im logs : ## Follow logs ${docker_compose} logs \ - --since=24h \ + --since=1h \ --follow .PHONY : logs From a8910157207c672586bf9112b398dfaa222c7e3b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 14 Jul 2025 17:20:01 +0200 Subject: [PATCH 362/878] Install GnuPG in production Docker image --- backend/Dockerfile-production | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 9d4e3764..5154715b 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -19,6 +19,10 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + gnupg && \ rm \ --recursive \ --force \ From 7bf075c366c1a6a20d696f876bbfaeca81b46b78 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 23 Jul 2025 09:13:50 +0200 Subject: [PATCH 363/878] Use `tini` instead of `dumb-init` which seems to be Docker's preferred choice according to https://docs.docker.com/reference/cli/docker/container/run/#init --- backend/Dockerfile | 6 +++--- frontend/Dockerfile | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 2af24b78..f91ea6bd 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -53,7 +53,6 @@ RUN \ --assume-yes \ --no-install-recommends \ curl \ - dumb-init \ editorconfig \ gawk \ gnupg \ @@ -61,7 +60,8 @@ RUN \ make \ # npm \ openssl \ - procps && \ + procps \ + tini && \ rm \ --recursive \ --force \ @@ -334,4 +334,4 @@ RUN \ ENV SHELL=/bin/bash -ENTRYPOINT ["/usr/bin/dumb-init", "--"] +ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 4bad00d8..e01dd476 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -28,9 +28,9 @@ RUN \ --assume-yes \ --no-install-recommends \ ca-certificates \ - dumb-init \ make \ openssl \ + tini \ wget && \ rm \ --recursive \ @@ -68,4 +68,4 @@ RUN \ yarn install \ --pure-lockfile -ENTRYPOINT ["/usr/bin/dumb-init", "--"] +ENTRYPOINT ["/usr/bin/tini", "--"] From f0cdf1bdddd427ee49424b275620f367a1d59d12 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 24 Jul 2025 17:26:33 +0200 Subject: [PATCH 364/878] Upgrade C# packages --- backend/src/Database.csproj | 34 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 6 +++--- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 1f4679a7..e1f70f1c 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -25,38 +25,38 @@ - - - - - - + + + + + + - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index d04fe6e2..1dc5ff0c 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,11 +16,11 @@ - + - - + + From 580a6a1b5c74f1643c2a3302619a4958d2f3d3a9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 24 Jul 2025 21:34:37 +0200 Subject: [PATCH 365/878] Use check and bake in build process --- Dockerfile-show-build-context | 5 +++-- Makefile | 21 ++++++++++++--------- backend/Dockerfile | 5 +++-- backend/Dockerfile-bootstrap | 5 +++-- backend/Dockerfile-production | 5 +++-- frontend/Dockerfile | 5 +++-- frontend/Dockerfile-production | 5 +++-- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Dockerfile-show-build-context b/Dockerfile-show-build-context index 234a952b..e4babe29 100644 --- a/Dockerfile-show-build-context +++ b/Dockerfile-show-build-context @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/Makefile b/Makefile index af5026a5..54104739 100644 --- a/Makefile +++ b/Makefile @@ -27,20 +27,23 @@ name : ## Print value of variable `NAME` # ----------------------------- # pull : ## Pull images - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull .PHONY : pull # To debug errors during build add `--progress plain \` to get additional # output. build : pull ## Build images - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --check \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : build backend-build-context : ## Show the build context configured by `./backend/.dockerignore` diff --git a/backend/Dockerfile b/backend/Dockerfile index f91ea6bd..c35f2d80 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true;skip=SecretsUsedInArgOrEnv +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index b57cfebe..d2a30347 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 5154715b..98fb95de 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/frontend/Dockerfile b/frontend/Dockerfile index e01dd476..ffa4f5a1 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 790ceb4e..04b07fc3 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,5 +1,6 @@ -# syntax=docker/dockerfile:1.6 -# The above line fixes the Dockerfile frontend used by BuildKit. For details +# syntax=docker/dockerfile:1.17 +# check=error=true +# The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile From e08dc0afe28354fb81b8ae5d0034cea56456c381 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 17:17:45 +0200 Subject: [PATCH 366/878] Use back in production builds --- Makefile.production | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/Makefile.production b/Makefile.production index 04b3dbc4..4ca43f9a 100644 --- a/Makefile.production +++ b/Makefile.production @@ -34,17 +34,19 @@ name : ## Print value of variable `NAME` .PHONY : name pull : ## Pull images - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull .PHONY : pull build : pull ## Build images - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - --no-cache + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull \ + --no-cache .PHONY : build up : ## (Re)create and start containers @@ -363,13 +365,15 @@ migrate : ## Migrate database (down-ing and up-ing the database service before a # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. deploy-services : ## Deploy services - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull ${docker_compose} up \ --force-recreate \ --renew-anon-volumes \ From 1da7a783fccdbb0df1e512c2001d3dbfbba346eb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 17:28:15 +0200 Subject: [PATCH 367/878] DRY-up image building and separate concerns --- Makefile | 9 ++++++++- Makefile.production | 25 ++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index 54104739..81eb8500 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,14 @@ name : ## Print value of variable `NAME` # Interface with Docker Compose # # ----------------------------- # +check : ## Check build configuration + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --check +.PHONY : check + pull : ## Pull images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ @@ -40,7 +48,6 @@ build : pull ## Build images COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ - --check \ --pull \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) diff --git a/Makefile.production b/Makefile.production index 4ca43f9a..7d1e5d33 100644 --- a/Makefile.production +++ b/Makefile.production @@ -33,6 +33,14 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name +check : ## Check build configuration + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --check +.PHONY : check + pull : ## Pull images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ @@ -40,13 +48,13 @@ pull : ## Pull images ${docker_compose} pull .PHONY : pull -build : pull ## Build images +build : check pull ## Build images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ - --pull \ - --no-cache + --pull + # --no-cache .PHONY : build up : ## (Re)create and start containers @@ -364,16 +372,7 @@ migrate : ## Migrate database (down-ing and up-ing the database service before a # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. -deploy-services : ## Deploy services - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull +deploy-services : build ## Deploy services ${docker_compose} up \ --force-recreate \ --renew-anon-volumes \ From e7c521c1a63a16e1d789eb48d524ad5927d75323 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 17:33:32 +0200 Subject: [PATCH 368/878] Pass build args also when only checking the build configuration --- Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 81eb8500..d60fbba0 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,9 @@ check : ## Check build configuration COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ - --check + --check \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : check pull : ## Pull images From 763da488750c3c8423df5dcbdf57b8dfd93d342f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 17:40:06 +0200 Subject: [PATCH 369/878] Add GNU Make target to print docker-compose equivalent bake file --- Makefile | 11 +++++++++++ Makefile.production | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/Makefile b/Makefile index d60fbba0..29533c0f 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,17 @@ build : pull ## Build images --build-arg USER_ID=$(shell id --user) .PHONY : build +bake : ## Print docker-compose file equivalent bake file + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --print \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) +.PHONY : bake + backend-build-context : ## Show the build context configured by `./backend/.dockerignore` DOCKER_BUILDKIT=1 \ docker build \ diff --git a/Makefile.production b/Makefile.production index 7d1e5d33..ffe428d8 100644 --- a/Makefile.production +++ b/Makefile.production @@ -57,6 +57,16 @@ build : check pull ## Build images # --no-cache .PHONY : build +bake : ## Print docker-compose file equivalent bake file + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --print \ + --pull + # --no-cache +.PHONY : bake + up : ## (Re)create and start containers ${docker_compose} up \ --remove-orphans \ From d9fb8b7d6e79cb8599df9dcc27d69299d6097b5e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 18:06:23 +0200 Subject: [PATCH 370/878] Add GNU Make target to print docker-compose file with everything filled-in --- Makefile | 7 +++++++ Makefile.production | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/Makefile b/Makefile index 29533c0f..51163034 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,13 @@ name : ## Print value of variable `NAME` # Interface with Docker Compose # # ----------------------------- # +config : ## Parse, resolve and render compose file in canonical format + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} config +.PHONY : config + check : ## Check build configuration COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ diff --git a/Makefile.production b/Makefile.production index ffe428d8..f3ee7f2a 100644 --- a/Makefile.production +++ b/Makefile.production @@ -33,6 +33,13 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name +config : ## Parse, resolve and render compose file in canonical format + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} config +.PHONY : config + check : ## Check build configuration COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ From db7a8f46213ac072ab99f1b6fdde987147126b12 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 19:21:17 +0200 Subject: [PATCH 371/878] Upgrade .NET tools --- backend/dotnet-tools.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 4b24c83a..46b8951c 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "9.0.5", + "version": "9.0.7", "commands": [ "dotnet-ef" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.1.2", + "version": "2025.1.4", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "15.1.4", + "version": "15.1.8", "commands": [ "dotnet-graphql" ], From 7e3cdbda0ccaf208eb4ef6fdb88d412d855d057f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 19:23:36 +0200 Subject: [PATCH 372/878] Add migration to upgrade database for OpenIddict 7 --- ...27_UpgradeOpenIddictToVersion7.Designer.cs | 2559 +++++++++++++++++ ...50725172127_UpgradeOpenIddictToVersion7.cs | 42 + .../ApplicationDbContextModelSnapshot.cs | 6 +- 3 files changed, 2604 insertions(+), 3 deletions(-) create mode 100644 backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs create mode 100644 backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs new file mode 100644 index 00000000..36c74d78 --- /dev/null +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs @@ -0,0 +1,2559 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250725172127_UpgradeOpenIddictToVersion7")] + partial class UpgradeOpenIddictToVersion7 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs new file mode 100644 index 00000000..adc42534 --- /dev/null +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs @@ -0,0 +1,42 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class UpgradeOpenIddictToVersion7 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Type", + schema: "database", + table: "OpenIddictTokens", + type: "character varying(150)", + maxLength: 150, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(50)", + oldMaxLength: 50, + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Type", + schema: "database", + table: "OpenIddictTokens", + type: "character varying(50)", + maxLength: 50, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(150)", + oldMaxLength: 150, + oldNullable: true); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 589e74da..cc5c9747 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.5") + .HasAnnotation("ProductVersion", "9.0.7") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); @@ -612,8 +612,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("character varying(400)"); b.Property("Type") - .HasMaxLength(50) - .HasColumnType("character varying(50)"); + .HasMaxLength(150) + .HasColumnType("character varying(150)"); b.HasKey("Id"); From 2b08ee3c4e3f0613c0cf798a1680c45cc8c24370 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 25 Jul 2025 22:00:17 +0200 Subject: [PATCH 373/878] Use NGINX 1.27 --- Makefile | 4 ++-- docker-compose.production.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 51163034..449fd53e 100644 --- a/Makefile +++ b/Makefile @@ -329,7 +329,7 @@ generate-certificate-authority : ## Generate certificate authority ECDSA private --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.25-bookworm \ + nginx:1.27-bookworm \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ @@ -437,7 +437,7 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.25-bookworm \ + nginx:1.27-bookworm \ bash -cx " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c2ae9679..270c4286 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -102,7 +102,7 @@ services: nginx: # https://hub.docker.com/_/nginx restart: always - image: nginx:1.25-bookworm + image: nginx:1.27-bookworm command: [ "nginx", "-g", "daemon off;" From 72b85f6a8d83db6152a54b16c6aa544765d2aeff Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 12:11:01 +0200 Subject: [PATCH 374/878] Add mutations to create and update response approvals (and fix various errors that have been introduced by outside collaborators --- I am agitated!) --- backend/src/ApiRequests/DataApi.cs | 12 +- backend/src/ApiRequests/DatabaseApi.cs | 189 +----------------- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 1 - backend/src/ApiRequests/Dto/DatabaseDto.cs | 25 ++- .../src/ApiRequests/Queries/Database.graphql | 19 ++ .../src/ApiRequests/Queries/Databases.graphql | 18 -- .../Queries/UpdateDatabase.graphql | 5 + .../CalorimetricDataAuthorization.cs | 6 +- .../src/Authorization/CommonAuthorization.cs | 9 +- .../Authorization/FileUploadAuthorization.cs | 5 +- .../GeometricDataAuthorization.cs | 5 +- .../GetHttpsResourceAuthorization.cs | 5 +- .../HygrothermalDataAuthorization.cs | 5 +- .../Authorization/OpticalDataAuthorization.cs | 5 +- .../PhotovoltaicDataAuthorization.cs | 5 +- .../ResponseApprovalAuthorization.cs | 15 ++ .../src/Configuration/GraphQlConfiguration.cs | 10 +- .../src/Controllers/FileUploadController.cs | 7 +- .../AccessRights/DataAccessRightsMutations.cs | 9 +- .../CalorimetricDataMutations.cs | 9 +- .../CreateCalorimetricDataErrorCode.cs | 2 +- .../DataApprovals/AddDataApprovalErrorCode.cs | 2 +- .../DataApprovals/DataApprovalMutations.cs | 41 ++-- backend/src/GraphQl/Databases/Database.cs | 1 + .../GraphQl/Databases/DatabaseMutations.cs | 31 +-- .../src/GraphQl/Databases/DatabaseQueries.cs | 11 +- .../CreateGeometricDataErrorCode.cs | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 9 +- .../GetHttpsResourceMutations.cs | 3 +- .../CreateHygrothermalDataErrorCode.cs | 2 +- .../HygrothermalDataMutations.cs | 9 +- .../MethodAsService/MethodAsServiceQueries.cs | 5 +- .../CreateOpticalDataErrorCode.cs | 2 +- .../OpticalDataX/OpticalDataMutations.cs | 12 +- .../CreatePhotovoltaicDataErrorCode.cs | 2 +- .../PhotovoltaicDataMutations.cs | 9 +- .../Publications/PublicationFilterType.cs | 15 ++ .../Publications/PublicationSortType.cs | 15 ++ .../GraphQl/Publications/PublicationType.cs | 1 + .../CreateResponseApprovalsError.cs | 12 ++ .../CreateResponseApprovalsErrorCode.cs | 13 ++ .../CreateResponseApprovalsPayload.cs | 33 +++ .../ResponseApprovalMutations.cs | 172 ++++++++++++++++ .../UpdateResponseApprovalsError.cs | 12 ++ .../UpdateResponseApprovalsErrorCode.cs | 13 ++ .../UpdateResponseApprovalsPayload.cs | 33 +++ backend/src/Program.cs | 4 +- backend/src/Services/DatabaseService.cs | 27 +++ .../src/Services/ResponseApprovalService.cs | 28 +-- backend/src/Services/SigningService.cs | 10 +- backend/src/Services/UserService.cs | 1 - backend/src/Startup.cs | 1 + backend/src/Utilities/FileHelpers.cs | 8 +- frontend/type-defs.graphqls | 54 ++++- 54 files changed, 595 insertions(+), 364 deletions(-) create mode 100644 backend/src/ApiRequests/Queries/Database.graphql delete mode 100644 backend/src/ApiRequests/Queries/Databases.graphql create mode 100644 backend/src/Authorization/ResponseApprovalAuthorization.cs create mode 100644 backend/src/GraphQl/Publications/PublicationFilterType.cs create mode 100644 backend/src/GraphQl/Publications/PublicationSortType.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs create mode 100644 backend/src/Services/DatabaseService.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 6aafcc38..9ea91a0d 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -117,7 +117,6 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), /// Throws exception, when query could not be constructed or no response. /// /// Query and response for data. - [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Keine reservierten Ausnahmetypen auslösen", Justification = "")] public static async Task> GetDataFromDatabase( Uri databaseUri, Guid dataId, @@ -135,20 +134,13 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), new { uuid = dataId }, "" ); - if (request.Query is null) - { - throw new Exception("Failed to construct GraphQL query."); - } - - var responseObject = await apiRequestService.Database().QueryGraphQlFromUrl( + return await apiRequestService.Database().QueryGraphQlFromUrl( appSettings, databaseUri, request, httpClientFactory, httpContextAccessor, cancellationToken - ).ConfigureAwait(false); - - return responseObject; + ); } } \ No newline at end of file diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index ab6fca09..30a18b5c 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -2,23 +2,20 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests.Dto; using Database.GraphQl.Databases; using Database.Services; using GraphQL; using HotChocolate; -using HotChocolate.Resolvers; using Microsoft.AspNetCore.Http; namespace Database.ApiRequests; -/// -/// Class to request databases from Metabase API. -/// public sealed class DatabaseApi { private static readonly string[] _databaseFileNames = [ - "Databases.graphql" + "Database.graphql" ]; private static readonly string[] _updateDatabaseFileNames = @@ -26,197 +23,33 @@ public sealed class DatabaseApi "UpdateDatabase.graphql" ]; - private sealed record DatabasesData(DatabasesConnectionDto Databases); + private sealed record DatabaseData(DatabaseDto Database); - /// - /// Request database from Metabase. - /// - /// - /// - /// - /// - /// - /// - /// if successful. - /// Throws exception if errors occur. - public static async Task RequestDatabase( - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - IResolverContext resolverContext, - CancellationToken cancellationToken) - { - var response = await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames).ConfigureAwait(false), - new - { - where = new - { - locator = new - { - // TODO This is error-prone. - absoluteUri = new - { - equalTo = new Uri(new Uri(appSettings.Host), "/graphql/") - } - } - } - }, - "Databases" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - - if (response is null) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_RESPONSE") - .SetPath(resolverContext.Path) - .SetMessage("Response is null.") - .Build() - ); - } - - if (response.Data.Databases.Edges is null) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_EDGES") - .SetPath(resolverContext.Path) - .SetMessage("The supposed list of databases is null.") - .Build() - ); - } - - if (response.Data.Databases.Edges.Count == 0) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NO_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases is empty.") - .Build() - ); - } - - if (response.Data.Databases.Edges.Count >= 2) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("AMBIGUOUS_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases has more than one entry.") - .Build() - ); - } - - return response.Data.Databases.Edges[0].Node; - } - - /// - /// Request database from Metabase. - /// - /// Id of database. - /// - /// - /// - /// - /// - /// - /// if successful. - /// Throws exception if errors occur. - public static async Task RequestDatabaseById( + public static async Task RequestDatabase( Guid databaseId, AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, - IResolverContext resolverContext, CancellationToken cancellationToken) { - var response = await apiRequestService.Metabase().QueryGraphQl( + var response = await apiRequestService.Metabase().QueryGraphQl( appSettings, new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames).ConfigureAwait(false), new { - where = new - { - uuid = new - { - equalTo = databaseId.ToString(), - } - } + uuid = databaseId }, - "Databases" + "Database" ), httpClientFactory, httpContextAccessor, cancellationToken ).ConfigureAwait(false); - - if (response is null) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_RESPONSE") - .SetPath(resolverContext.Path) - .SetMessage("Response is null.") - .Build() - ); - } - - if (response.Data.Databases.Edges is null) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NULL_EDGES") - .SetPath(resolverContext.Path) - .SetMessage("The supposed list of databases is null.") - .Build() - ); - } - - if (response.Data.Databases.Edges.Count == 0) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("NO_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases is empty.") - .Build() - ); - } - - if (response.Data.Databases.Edges.Count >= 2) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("AMBIGUOUS_DATABASE") - .SetPath(resolverContext.Path) - .SetMessage("The list of databases has more than one entry.") - .Build() - ); - } - - return response.Data.Databases.Edges[0].Node; + return response.Data?.Database; } - /// - /// Update database in Metabase. - /// - /// with update mutation. - /// - /// - /// - /// - /// - /// Updated - public static async Task UpdateDatabase( + public static async Task UpdateDatabase( UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, ApiRequestService apiRequestService, @@ -225,7 +58,7 @@ public static async Task RequestDatabaseById( CancellationToken cancellationToken ) { - return (await apiRequestService.Metabase().QueryGraphQl( + return (await apiRequestService.Metabase().QueryGraphQl( appSettings, new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery( @@ -233,7 +66,7 @@ await apiRequestService.ConstructGraphQlQuery( ).ConfigureAwait(false), new { - updateDatabaseInput + input = updateDatabaseInput }, "UpdateDatabase" ), diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index 26e568ee..cfb9ffc2 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -18,7 +18,6 @@ public enum DataSigningPermission } public sealed record CurrentUserDto( - Guid Id, Guid Uuid, UserRepresentedInstitutionConnection RepresentedInstitutions ); diff --git a/backend/src/ApiRequests/Dto/DatabaseDto.cs b/backend/src/ApiRequests/Dto/DatabaseDto.cs index 92cf14e2..f6b80993 100644 --- a/backend/src/ApiRequests/Dto/DatabaseDto.cs +++ b/backend/src/ApiRequests/Dto/DatabaseDto.cs @@ -1,7 +1,16 @@ using System; using System.Collections.Generic; +using Database.GraphQl.Databases; -namespace Database.GraphQl.Databases; +namespace Database.ApiRequests.Dto; + +public sealed record Institution( + Guid Uuid +); + +public sealed record DatabaseOperatorEdge( + Institution Node +); public sealed record DatabaseDto( @@ -11,16 +20,20 @@ public sealed record DatabaseDto( Uri Locator, DatabaseVerificationState VerificationState, string VerificationCode, + DatabaseOperatorEdge Operator, // public Institution? Operator { get; set; } bool CanCurrentUserUpdateNode, bool CanCurrentUserVerifyNode ); -public sealed record DatabasesConnectionDto -( - IReadOnlyList? Edges +public sealed record ErrorDto( + string Code, + string Message, + IReadOnlyList Path ); -public sealed record DatabaseEdge( - DatabaseDto Node + +public sealed record DatabasePayloadDto( + DatabaseDto? Database, + IReadOnlyList? Errors ); \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Database.graphql b/backend/src/ApiRequests/Queries/Database.graphql new file mode 100644 index 00000000..68c71c5c --- /dev/null +++ b/backend/src/ApiRequests/Queries/Database.graphql @@ -0,0 +1,19 @@ +query Database( + $uuid: Uuid! +) { + database(uuid: $uuid) { + uuid + name + description + locator + verificationState + verificationCode + operator { + node { + uuid + } + } + canCurrentUserUpdateNode + canCurrentUserVerifyNode + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Databases.graphql b/backend/src/ApiRequests/Queries/Databases.graphql deleted file mode 100644 index bf21e0c0..00000000 --- a/backend/src/ApiRequests/Queries/Databases.graphql +++ /dev/null @@ -1,18 +0,0 @@ -query Databases( - $where: DatabaseFilterInput -) { - databases(where: $where) { - edges { - node { - uuid - name - description - locator - verificationState - verificationCode - canCurrentUserUpdateNode - canCurrentUserVerifyNode - } - } - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql index ba9eadda..7d1d4fd2 100644 --- a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql +++ b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql @@ -9,6 +9,11 @@ mutation UpdateDatabase( locator verificationState verificationCode + operator { + node { + uuid + } + } canCurrentUserUpdateNode canCurrentUserVerifyNode } diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs index 69c9bb66..73bb6070 100644 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ b/backend/src/Authorization/CalorimetricDataAuthorization.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using Database.ApiRequests.Dto; namespace Database.Authorization; @@ -8,10 +7,9 @@ public static class CalorimetricDataAuthorization { public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index aa11af2c..c5db16c8 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Threading; using Database.ApiRequests.Dto; namespace Database.Authorization; @@ -9,8 +8,7 @@ public static class CommonAuthorization { public static bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { return currentUser.RepresentedInstitutions.Edges.Any(t => @@ -30,13 +28,12 @@ t.Role is InstitutionRepresentativeRole.ASSISTANT public static bool IsAuthorizedToAddDataApprovalForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { return currentUser.RepresentedInstitutions.Edges.Any( t => t.Node.Uuid == institutionId - && t.DataSigningPermission == DataSigningPermission.GRANTED + && t.DataSigningPermission is DataSigningPermission.GRANTED ); } } \ No newline at end of file diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs index ae7ebd60..57f750ec 100644 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ b/backend/src/Authorization/FileUploadAuthorization.cs @@ -8,10 +8,9 @@ public static class FileUploadDataAuthorization { public static bool IsAuthorizedToUploadFilesForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs index fc818f04..b5930024 100644 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ b/backend/src/Authorization/GeometricDataAuthorization.cs @@ -8,10 +8,9 @@ public static class GeometricDataAuthorization { public static bool IsAuthorizedToCreateGeometricDataForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs index 540f7682..6f1366a2 100644 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ b/backend/src/Authorization/GetHttpsResourceAuthorization.cs @@ -8,10 +8,9 @@ public static class GetHttpsResourceAuthorization { public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs index a4338751..dc972364 100644 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ b/backend/src/Authorization/HygrothermalDataAuthorization.cs @@ -8,10 +8,9 @@ public static class HygrothermalDataAuthorization { public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs index e5d4b008..e86ed158 100644 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ b/backend/src/Authorization/OpticalDataAuthorization.cs @@ -8,10 +8,9 @@ public static class OpticalDataAuthorization { public static bool IsAuthorizedToCreateOpticalDataForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs index ec8f2a48..c7982897 100644 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs @@ -8,10 +8,9 @@ public static class PhotovoltaicDataAuthorization { public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( CurrentUserDto currentUser, - Guid institutionId, - CancellationToken cancellationToken + Guid institutionId ) { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId, cancellationToken); + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); } } \ No newline at end of file diff --git a/backend/src/Authorization/ResponseApprovalAuthorization.cs b/backend/src/Authorization/ResponseApprovalAuthorization.cs new file mode 100644 index 00000000..43bcda13 --- /dev/null +++ b/backend/src/Authorization/ResponseApprovalAuthorization.cs @@ -0,0 +1,15 @@ +using System; +using Database.ApiRequests.Dto; + +namespace Database.Authorization; + +public static class ResponseApprovalAuthorization +{ + public static bool IsAuthorizedToManageResponseApprovals( + CurrentUserDto currentUser, + Guid institutionId + ) + { + return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); + } +} \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 39033f73..7a7c7380 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -30,6 +30,7 @@ using Database.GraphQl.DataApprovals; using Database.GraphQl.AccessRights; using Database.GraphQl.MethodAsService; +using Database.GraphQl.ResponseApprovals; namespace Database.Configuration; @@ -161,14 +162,15 @@ AppSettings appSettings // Mutation Types .AddMutationType(d => d.Name(nameof(Mutation))) .AddType() + .AddType() + .AddType() .AddType() + .AddType() .AddType() .AddType() .AddType() .AddType() - .AddType() - .AddType() - .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types @@ -240,6 +242,7 @@ protected override void Configure(IFilterConventionDescriptor descriptor) descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); } @@ -419,6 +422,7 @@ protected override void Configure(ISortConventionDescriptor descriptor) descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); + descriptor.BindRuntimeType(); descriptor.BindRuntimeType(); } } \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 8d23cc66..310f2ec1 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -137,9 +137,10 @@ CancellationToken cancellationToken return BadRequest(ModelState); } if (!FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( - currentUser, - getHttpsResource.Data.CreatorId, - cancellationToken)) + currentUser, + getHttpsResource.Data.CreatorId + ) + ) { return Unauthorized(); } diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index ac434eff..6f01f00c 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -47,8 +47,7 @@ CancellationToken cancellationToken if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, - data.CreatorId, - cancellationToken + data.CreatorId ) ) { @@ -91,8 +90,7 @@ CancellationToken cancellationToken if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, - input.InstitutionId, - cancellationToken + input.InstitutionId ) ) { @@ -150,8 +148,7 @@ CancellationToken cancellationToken if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( currentUser, - input.InstitutionId, - cancellationToken + input.InstitutionId ) ) { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 41788177..2acdf569 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -37,8 +37,7 @@ CancellationToken cancellationToken if (!CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { @@ -131,7 +130,7 @@ input.RootResource.AppliedConversionMethod is null calorimetricData.Approval = await responseApprovalService.CreateResponseApproval(calorimetricData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(calorimetricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -139,8 +138,8 @@ input.RootResource.AppliedConversionMethod is null return new CreateCalorimetricDataPayload( calorimetricData, new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + CreateCalorimetricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs index 237cced2..f25cc94f 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs @@ -8,5 +8,5 @@ public enum CreateCalorimetricDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index 5cf7ba81..9af698dd 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -9,7 +9,7 @@ public enum AddDataApprovalErrorCode UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, - SIGNING_FAILED, + CREATING_RESPONSE_APPROVAL_FAILED, AMBIGUOUS_STATEMENT, MISSING_STATEMENT } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 1064cb9c..bd70d349 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -35,18 +35,29 @@ CancellationToken cancellationToken ); } + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + if (data is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [nameof(input), nameof(input.DataId).FirstCharToLower()] + ) + ); + } + if (!CommonAuthorization.IsAuthorizedToAddDataApprovalForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { return new AddDataApprovalPayload( new AddDataApprovalError( AddDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to approval for the institution.", - [] + $"The current user is not authorized to add approvals for the institution.", + [nameof(input), nameof(input.CreatorId).FirstCharToLower()] ) ); } @@ -75,25 +86,13 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); - if (data is null) - { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [] - ) - ); - } - var approval = new DataApproval( - DateTime.Now, + input.Timestamp, input.Signature, input.KeyFingerprint, input.Query, input.Response, - currentUser.Id, + input.ApproverId, ReferenceType.FromInput(input.Statement) ); @@ -105,7 +104,7 @@ CancellationToken cancellationToken data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(data); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -113,8 +112,8 @@ CancellationToken cancellationToken return new AddDataApprovalPayload( approval, new AddDataApprovalError( - AddDataApprovalErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + AddDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs index 6dd938e3..ffe4f20b 100644 --- a/backend/src/GraphQl/Databases/Database.cs +++ b/backend/src/GraphQl/Databases/Database.cs @@ -1,4 +1,5 @@ using System; +using Database.ApiRequests.Dto; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index a72a170d..840add20 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -21,24 +21,29 @@ public async Task UpdateDatabaseAsync( CancellationToken cancellationToken ) { - var database = await DatabaseApi.UpdateDatabase( + var databasePayload = await DatabaseApi.UpdateDatabase( input, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); - return database is not null ? new UpdateDatabasePayload( - Database.FromDto(database), - null) : new UpdateDatabasePayload( - null, - [ - new UpdateDatabaseError( - UpdateDatabaseErrorCode.UNKNOWN, - "Unknown error.", - Array.Empty() - ) - ] - ); + if (databasePayload is null || databasePayload.Database is null) + { + return new UpdateDatabasePayload( + null, + [ + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + [] + ) + ] + ); + } + return new UpdateDatabasePayload( + Database.FromDto(databasePayload.Database), + null + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 976c7050..22d9fd2e 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -28,12 +28,21 @@ CancellationToken cancellationToken try { var database = await DatabaseApi.RequestDatabase( + appSettings.DatabaseId, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, - resolverContext, cancellationToken); + if (database is null) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetPath(resolverContext.Path) + .SetMessage($"Failed to fetch database from the metabase GraphQl endpoint.") + .Build() + ); + } return Database.FromDto(database); } catch (HttpRequestException e) diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs index cb23d15b..8c7558ec 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs @@ -8,5 +8,5 @@ public enum CreateGeometricDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 049c5722..8a91b248 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -37,8 +37,7 @@ CancellationToken cancellationToken if (!GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { @@ -115,7 +114,7 @@ input.RootResource.AppliedConversionMethod is null geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(geometricData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -123,8 +122,8 @@ input.RootResource.AppliedConversionMethod is null return new CreateGeometricDataPayload( geometricData, new CreateGeometricDataError( - CreateGeometricDataErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + CreateGeometricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 856bced6..d9597e39 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -58,8 +58,7 @@ CancellationToken cancellationToken } if (!GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( currentUser, - data.CreatorId, - cancellationToken + data.CreatorId ) ) { diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs index 86de5839..2b98faa3 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs @@ -8,5 +8,5 @@ public enum CreateHygrothermalDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 3b389b4d..51620ef2 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -38,8 +38,7 @@ CancellationToken cancellationToken } if (!HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { @@ -130,7 +129,7 @@ input.RootResource.AppliedConversionMethod is null hygrothermalData.Approval = await responseApprovalService.CreateResponseApproval(hygrothermalData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(hygrothermalData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -138,8 +137,8 @@ input.RootResource.AppliedConversionMethod is null return new CreateHygrothermalDataPayload( hygrothermalData, new CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + CreateHygrothermalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 656edb67..07e2bc43 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -75,16 +75,15 @@ CancellationToken cancellationToken ); } - DatabaseDto database; + DatabaseDto? database; try { - database = await DatabaseApi.RequestDatabaseById( + database = await DatabaseApi.RequestDatabase( input.DatabaseId, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, - resolverContext, cancellationToken); } catch (HttpRequestException e) diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs index 8eb2a18a..54048097 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs @@ -8,5 +8,5 @@ public enum CreateOpticalDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 55d4b698..cdc44716 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -22,8 +22,7 @@ public async Task CreateOpticalDataAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { return new CreateOpticalDataPayload( @@ -36,8 +35,7 @@ CancellationToken cancellationToken } if (!OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { @@ -144,7 +142,7 @@ input.RootResource.AppliedConversionMethod is null opticalData.Approval = await responseApprovalService.CreateResponseApproval(opticalData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(opticalData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -152,8 +150,8 @@ input.RootResource.AppliedConversionMethod is null return new CreateOpticalDataPayload( opticalData, new CreateOpticalDataError( - CreateOpticalDataErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + CreateOpticalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Creating response approval failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs index 88b98d84..4f39811e 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs @@ -8,5 +8,5 @@ public enum CreatePhotovoltaicDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 7007d530..8a5ecb40 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -41,8 +41,7 @@ CancellationToken cancellationToken if (!PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( currentUser, - input.CreatorId, - cancellationToken + input.CreatorId ) ) { @@ -133,7 +132,7 @@ input.RootResource.AppliedConversionMethod is null photovoltaicData.Approval = await responseApprovalService.CreateResponseApproval(photovoltaicData, cancellationToken).ConfigureAwait(false); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + catch (Exception exception) { context.Remove(photovoltaicData); await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); @@ -141,8 +140,8 @@ input.RootResource.AppliedConversionMethod is null return new CreatePhotovoltaicDataPayload( photovoltaicData, new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.SIGNING_FAILED, - $"Signing failed with message: {ex.Message}", + CreatePhotovoltaicDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/GraphQl/Publications/PublicationFilterType.cs b/backend/src/GraphQl/Publications/PublicationFilterType.cs new file mode 100644 index 00000000..e1ac3ce0 --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationFilterType.cs @@ -0,0 +1,15 @@ +using HotChocolate.Data.Filters; +using Database.Data; + +namespace Database.GraphQl.Publications; + +public sealed class PublicationFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.Field(x => x.Exists).Ignore(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationSortType.cs b/backend/src/GraphQl/Publications/PublicationSortType.cs new file mode 100644 index 00000000..dcf57125 --- /dev/null +++ b/backend/src/GraphQl/Publications/PublicationSortType.cs @@ -0,0 +1,15 @@ +using HotChocolate.Data.Sorting; +using Database.Data; + +namespace Database.GraphQl.Publications; + +public sealed class PublicationSortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.Field(x => x.Exists).Ignore(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs index fb9a6044..60e051da 100644 --- a/backend/src/GraphQl/Publications/PublicationType.cs +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -24,5 +24,6 @@ protected override void Configure( IObjectTypeDescriptor descriptor ) { + descriptor.Field(p => p.Exists).Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs new file mode 100644 index 00000000..a2c75f55 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs new file mode 100644 index 00000000..3f4a55d3 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.ResponseApprovals; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateResponseApprovalsErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATABASE, + CREATING_RESPONSE_APPROVAL_FAILED +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs new file mode 100644 index 00000000..d874a7e6 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class CreateResponseApprovalsPayload + : Payload +{ + public IReadOnlyCollection? Data { get; } + public IReadOnlyCollection? Errors { get; } + + public CreateResponseApprovalsPayload( + IReadOnlyCollection data + ) + { + Data = data; + } + + public CreateResponseApprovalsPayload( + CreateResponseApprovalsError error + ) + { + Errors = [error]; + } + + public CreateResponseApprovalsPayload( + IReadOnlyCollection data, + IReadOnlyCollection errors + ) : this(data) + { + Errors = errors; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs new file mode 100644 index 00000000..6dc40b82 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Types; + +namespace Database.GraphQl.ResponseApprovals; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ResponseApprovalMutations +{ + //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + public async Task CreateResponseApprovalsAsync( + ApplicationDbContext context, + UserService userService, + DatabaseService databaseService, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new CreateResponseApprovalsPayload( + new CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + var database = await databaseService.QueryDatabase(cancellationToken); + if (database is null) + { + return new CreateResponseApprovalsPayload( + new CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode.UNKNOWN_DATABASE, + $"The database could not be identified.", + [] + ) + ); + } + if (!ResponseApprovalAuthorization.IsAuthorizedToManageResponseApprovals( + currentUser, + database.Operator.Node.Uuid + ) + ) + { + return new CreateResponseApprovalsPayload( + new CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to create response approvals for the database.", + [] + ) + ); + } + var dataSets = new List(); + var errors = new List(); + await foreach ( + var data in context.CalorimetricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable() + .Union(context.GeometricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) + .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) + .Union(context.OpticalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) + .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) + ) + { + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + errors.Add( + new CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Creating response approval for data {data} failed with message: {exception.Message}", + [] + ) + ); + } + } + await context.SaveChangesAsync(cancellationToken); + if (errors.Count != 0) + { + return new CreateResponseApprovalsPayload(dataSets, errors); + } + return new CreateResponseApprovalsPayload(dataSets); + } + + public async Task UpdateResponseApprovalsAsync( + ApplicationDbContext context, + UserService userService, + DatabaseService databaseService, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new UpdateResponseApprovalsPayload( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + var database = await databaseService.QueryDatabase(cancellationToken); + if (database is null) + { + return new UpdateResponseApprovalsPayload( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UNKNOWN_DATABASE, + $"The database could not be identified.", + [] + ) + ); + } + if (!ResponseApprovalAuthorization.IsAuthorizedToManageResponseApprovals( + currentUser, + database.Operator.Node.Uuid + ) + ) + { + return new UpdateResponseApprovalsPayload( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to create response approvals for the database.", + [] + ) + ); + } + var dataSets = new List(); + var errors = new List(); + await foreach ( + var data in context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() + .Union(context.GeometricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.OpticalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + ) + { + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + errors.Add( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, + $"Updating response approval for data {data} failed with message: {exception.Message}", + [] + ) + ); + } + } + await context.SaveChangesAsync(cancellationToken); + if (errors.Count != 0) + { + return new UpdateResponseApprovalsPayload(dataSets, errors); + } + return new UpdateResponseApprovalsPayload(dataSets); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs new file mode 100644 index 00000000..478ee8f5 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs new file mode 100644 index 00000000..d5d72ac0 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.ResponseApprovals; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateResponseApprovalsErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATABASE, + UPDATING_RESPONSE_APPROVAL_FAILED +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs new file mode 100644 index 00000000..a4f6f113 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class UpdateResponseApprovalsPayload + : Payload +{ + public IReadOnlyCollection? Data { get; } + public IReadOnlyCollection? Errors { get; } + + public UpdateResponseApprovalsPayload( + IReadOnlyCollection data + ) + { + Data = data; + } + + public UpdateResponseApprovalsPayload( + UpdateResponseApprovalsError error + ) + { + Errors = [error]; + } + + public UpdateResponseApprovalsPayload( + IReadOnlyCollection data, + IReadOnlyCollection errors + ) : this(data) + { + Errors = errors; + } +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 76478db9..00bdd598 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -64,9 +64,9 @@ string[] commandLineArguments await application.RunAsync().ConfigureAwait(false); return 0; } - catch (Exception ex) when (ex is not HostAbortedException && ex.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 + catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { - Log.Fatal(ex, "Host terminated unexpectedly"); + Log.Fatal(exception, "Host terminated unexpectedly"); return 1; } finally diff --git a/backend/src/Services/DatabaseService.cs b/backend/src/Services/DatabaseService.cs new file mode 100644 index 00000000..9c58c99d --- /dev/null +++ b/backend/src/Services/DatabaseService.cs @@ -0,0 +1,27 @@ +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.ApiRequests.Dto; +using Microsoft.AspNetCore.Http; + +namespace Database.Services; + +public sealed class DatabaseService( + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpContextAccessor httpContextAccessor, + IHttpClientFactory httpClientFactory +) +{ + internal Task QueryDatabase(CancellationToken cancellationToken) + { + return DatabaseApi.RequestDatabase( + appSettings.DatabaseId, + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index d586a88b..103c0239 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -6,7 +6,6 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; -using Database.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; @@ -62,25 +61,14 @@ public async Task CreateResponseApproval(IData dataObject, Can private async Task<(string Query, string Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) { - switch (dataObject) + return dataObject switch { - case GeometricData data: - return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - case CalorimetricData data: - return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - case HygrothermalData data: - return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - case PhotovoltaicData data: - return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - case OpticalData data: - return await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - default: - throw new NotImplementedException("Unknown IData object."); - } + GeometricData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + CalorimetricData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + HygrothermalData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + PhotovoltaicData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + OpticalData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + _ => throw new NotImplementedException("Unknown IData object."), + }; } } \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index e2105a31..ba536b24 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -134,9 +134,9 @@ private bool WriteDataToFile(string data) return true; } - catch (Exception ex) + catch (Exception exception) { - logger.WriteFileException(ex.ToString()); + logger.WriteFileException(exception.ToString()); return false; } } @@ -150,10 +150,10 @@ private bool WriteDataToFile(string data) return (true, file.ReadToEnd()); } } - catch (IOException ex) + catch (IOException exception) { - logger.ReadFileException(ex.ToString()); - return (false, ex.ToString()); + logger.ReadFileException(exception.ToString()); + return (false, exception.ToString()); } } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 93ffeb30..1e8f8d0a 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -5,7 +5,6 @@ using Database.ApiRequests.Dto; using Database.Extensions; using Database.Logging; -using Database.Services; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8fa8dc88..449ee2a9 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -69,6 +69,7 @@ public void ConfigureServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); } private static void ConfigureRequestResponseServices(IServiceCollection services) diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 0403af11..3fde3527 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -138,11 +138,11 @@ public static async Task ProcessFormFile(IFormFile formFile, return memoryStream.ToArray(); } } - catch (Exception ex) + catch (Exception exception) { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " + - $"Please contact the Help Desk for support. Error: {ex.HResult}"); + $"Please contact the Help Desk for support. Error: {exception.HResult}"); // Log the exception } @@ -182,11 +182,11 @@ public static async Task ProcessStreamedFile( return memoryStream.ToArray(); } } - catch (Exception ex) + catch (Exception exception) { modelState.AddModelError("File", "The upload failed. Please contact the Help Desk " + - $" for support. Error: {ex.HResult}"); + $" for support. Error: {exception.HResult}"); // Log the exception } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 78945c85..26f1448b 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -194,6 +194,18 @@ type CreatePhotovoltaicDataPayload { query: Query! } +type CreateResponseApprovalsError { + code: CreateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type CreateResponseApprovalsPayload { + data: [Data!] + errors: [CreateResponseApprovalsError!] + query: Query! +} + type CrossDatabaseDataReference { databaseId: Uuid! dataId: Uuid! @@ -448,9 +460,11 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") } type NamedMethodArgument { @@ -684,6 +698,18 @@ type UpdateDatabasePayload { query: Query! } +type UpdateResponseApprovalsError { + code: UpdateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type UpdateResponseApprovalsPayload { + data: [Data!] + errors: [UpdateResponseApprovalsError!] + query: Query! +} + type User implements Node { id: ID! name: String! @@ -1472,7 +1498,7 @@ enum AddDataApprovalErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED AMBIGUOUS_STATEMENT MISSING_STATEMENT } @@ -1498,14 +1524,14 @@ enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateGeometricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateGetHttpsResourceErrorCode { @@ -1519,21 +1545,29 @@ enum CreateHygrothermalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum CreatePhotovoltaicDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED - SIGNING_FAILED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + CREATING_RESPONSE_APPROVAL_FAILED } enum DataKind { @@ -1637,6 +1671,14 @@ enum UpdateDatabaseErrorCode { UNKNOWN_DATABASE } +enum UpdateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + UPDATING_RESPONSE_APPROVAL_FAILED +} + enum UriHostNameType { UNKNOWN BASIC From b11022f5e90cada8e52d456971d44bdc4a959baa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 12:20:34 +0200 Subject: [PATCH 375/878] Fix file upload (this can never have worked in the first place; how can a paid software developer change code without ever testing?) --- backend/src/Controllers/FileUploadController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 310f2ec1..8226682f 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -95,7 +95,7 @@ protected override void Dispose(bool disposing) [RequestSizeLimit(10737418240)] // 10 GiB public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, - UserService userService, + [FromServices] UserService userService, CancellationToken cancellationToken ) { From 80c605008c9c497295c10257e54047598d6b47db Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 12:52:51 +0200 Subject: [PATCH 376/878] Authorize user properly before adding data approvals --- .../src/Authorization/CommonAuthorization.cs | 1 - .../DataApprovals/AddDataApprovalErrorCode.cs | 1 + .../DataApprovals/DataApprovalInput.cs | 1 - .../DataApprovals/DataApprovalMutations.cs | 31 +++++++++++++------ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index c5db16c8..9e1b771b 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -33,7 +33,6 @@ Guid institutionId { return currentUser.RepresentedInstitutions.Edges.Any( t => t.Node.Uuid == institutionId - && t.DataSigningPermission is DataSigningPermission.GRANTED ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index 9af698dd..4a5868b5 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -9,6 +9,7 @@ public enum AddDataApprovalErrorCode UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, + UNKNOWN_DATABASE, CREATING_RESPONSE_APPROVAL_FAILED, AMBIGUOUS_STATEMENT, MISSING_STATEMENT diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs index 9917241a..d509af37 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.DataApprovals; public sealed record DataApprovalInput ( Guid DataId, - Guid CreatorId, DateTime Timestamp, string Signature, string KeyFingerprint, diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index bd70d349..3a671d33 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -19,6 +19,7 @@ public async Task AddDataApprovalAsync( ApplicationDbContext context, UserService userService, DataService dataService, + DatabaseService databaseService, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -34,30 +35,40 @@ CancellationToken cancellationToken ) ); } - - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); - if (data is null) + var database = await databaseService.QueryDatabase(cancellationToken); + if (database is null) { return new AddDataApprovalPayload( new AddDataApprovalError( - AddDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [nameof(input), nameof(input.DataId).FirstCharToLower()] + AddDataApprovalErrorCode.UNKNOWN_DATABASE, + $"The database could not be identified.", + [] ) ); } - if (!CommonAuthorization.IsAuthorizedToAddDataApprovalForInstitution( currentUser, - input.CreatorId + database.Operator.Node.Uuid ) ) { return new AddDataApprovalPayload( new AddDataApprovalError( AddDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add approvals for the institution.", - [nameof(input), nameof(input.CreatorId).FirstCharToLower()] + $"The current user is not authorized to add data approvals for the database.", + [nameof(input), nameof(input.ApproverId).FirstCharToLower()] + ) + ); + } + + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + if (data is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [nameof(input), nameof(input.DataId).FirstCharToLower()] ) ); } From 0858cc62847e74b270d0d175358065175724bd01 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 13:12:35 +0200 Subject: [PATCH 377/878] Update GraphQL schema --- frontend/type-defs.graphqls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 26f1448b..d3838b79 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -916,7 +916,6 @@ input DataAccessRightsInput { input DataApprovalInput { approverId: Uuid! - creatorId: Uuid! dataId: Uuid! keyFingerprint: String! query: String! @@ -1498,6 +1497,7 @@ enum AddDataApprovalErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + UNKNOWN_DATABASE CREATING_RESPONSE_APPROVAL_FAILED AMBIGUOUS_STATEMENT MISSING_STATEMENT From 2bb78d752cef741b479c224e2f529d5629303c79 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 13:27:06 +0200 Subject: [PATCH 378/878] Install GnuPG in runtime stage of production Docker image --- backend/Dockerfile-production | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 98fb95de..6d0857c9 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -61,7 +61,8 @@ RUN \ apt-get install \ --assume-yes \ --no-install-recommends \ - curl && \ + curl \ + gnupg && \ rm \ --recursive \ --force \ From c457818468dc84d6fbedf720f4a75cc786d56629 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 14:18:14 +0200 Subject: [PATCH 379/878] Return empty result instead of throwing exceptions when the access rights cannot be applied --- backend/src/Logging/AccessRightServiceLogging.cs | 5 +++++ backend/src/Services/AccessRightsService.cs | 10 +++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs index 8eb4027e..874f448d 100644 --- a/backend/src/Logging/AccessRightServiceLogging.cs +++ b/backend/src/Logging/AccessRightServiceLogging.cs @@ -6,6 +6,11 @@ namespace Database.Logging; public static partial class AccessRightServiceLogging { + [LoggerMessage( + Level = LogLevel.Information, + Message = "Cannot apply access rights because the user or application is unknown. The fetched user ID is {UserId} and the application ID is {ApplicationId}.")] + public static partial void UnknownUserOrApplication(this ILogger logger, Guid? userId, string? applicationId); + [LoggerMessage( Level = LogLevel.Debug, Message = "Restricted Item Id: {Id} Reason: {Reason}")] diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 1cf13c8e..1105dc5a 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -6,7 +6,6 @@ using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; -using Database.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -39,8 +38,13 @@ public async Task> ApplyAccessRightsOnData(IQueryable data, var institutions = new List(); var accessRights = new List(); - var applicationId = userService.GetApplicationIdFromUser() ?? throw new InvalidOperationException("Application identifier could not be acquired."); - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false) ?? throw new InvalidOperationException("Could not get current user!"); + var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + var applicationId = userService.GetApplicationIdFromUser(); + if (currentUser is null || applicationId is null) + { + logger.UnknownUserOrApplication(currentUser?.Uuid, applicationId); + return Enumerable.Empty().AsQueryable(); + } var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); foreach (var institution in currentUser.RepresentedInstitutions.Edges) From c0b2beec447c38ee65de2d68e891ada2dc6be03d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 14:47:31 +0200 Subject: [PATCH 380/878] Remove unused arg --- backend/Dockerfile | 1 - docker-compose.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index c35f2d80..7f4ef804 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -14,7 +14,6 @@ ARG GROUP_ID ARG USER_ID ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME ARG CERTIFICATE_AUTHORITY_PASSWORD -ARG GNUPG_PRIVATEKEY_FILE_NAME # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) RUN \ diff --git a/docker-compose.yml b/docker-compose.yml index 556a71dc..8561f3e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,7 +63,6 @@ services: args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - CERTIFICATE_AUTHORITY_PASSWORD=${CERTIFICATE_AUTHORITY_PASSWORD} - - GNUPG_PRIVATEKEY_FILE_NAME=${GNUPG_PRIVATEKEY_FILE_NAME} # user: 1000:1000 command: [ "dotnet", "watch", From 8f27cc3182d22e123516c5b1cc419b0c77b12734 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 14:48:40 +0200 Subject: [PATCH 381/878] Store GnuPG keys in volume (otherwise the GnuPG keys are not found when needed even though they have been imported) --- docker-compose.production.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 270c4286..ba6b8835 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -76,10 +76,15 @@ services: - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} + # The value `/dev/pts/0` is the output of running `tty` within `shellb` + # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html + - GPG_TTY=/dev/pts/0 volumes: - backend_logs:/app/logs - files:/app/files - ./secrets:/run/secrets:ro + # Create volume for GnuPG + - gnupg:/root/.gnupg depends_on: - database - email @@ -128,4 +133,5 @@ volumes: data: emails: files: + gnupg: backend_logs: From 0607ec6aa49a44c996d7ea7e6cee6fde5acd3925 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 14:56:01 +0200 Subject: [PATCH 382/878] Pass GnuPG passphrase and file name --- docker-compose.production.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index ba6b8835..cf92bf98 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -76,6 +76,8 @@ services: - XBASE_Email__SmtpPort=${SMTP_PORT} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} + - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} + - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - GPG_TTY=/dev/pts/0 From 92ad276d0c7d14ce233633830696b95b5af9e75f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 31 Jul 2025 18:38:50 +0200 Subject: [PATCH 383/878] Fix access rights somewhat (still needs some proper testing) --- backend/src/Data/ApplicationDbContext.cs | 2 + backend/src/Data/DataAccessRights.cs | 6 +- backend/src/Data/InstitutionAccessRights.cs | 56 +- .../OpticalDataX/OpticalDataQueries.cs | 14 +- ...titutionIdOfAccessRightsUnique.Designer.cs | 2562 +++++++++++++++++ ...6_MakeInstitutionIdOfAccessRightsUnique.cs | 30 + .../ApplicationDbContextModelSnapshot.cs | 3 + backend/src/Migrations/current | 2 +- ..._MakeInstitutionIdOfAccessRightsUnique.sql | 13 + backend/src/Migrations/previous | 2 +- ..._MakeInstitutionIdOfAccessRightsUnique.sql | 13 + backend/src/Services/AccessRightsService.cs | 134 +- backend/src/Services/UserService.cs | 2 +- 13 files changed, 2737 insertions(+), 102 deletions(-) create mode 100644 backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs create mode 100644 backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs create mode 100644 backend/src/Migrations/migrate_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql create mode 100644 backend/src/Migrations/rollback_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 1967a142..8c594db3 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -63,6 +63,8 @@ EntityTypeBuilder builder where TEntity : Entity { // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + builder + .HasKey(e => e.Id); builder .Property(e => e.Id) .HasDefaultValueSql("gen_random_uuid()"); diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index 67563c64..e4544c5a 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -45,19 +45,19 @@ public sealed class DataAccessRights /// [NotMapped] [Projectable] - public bool IsRestrictedByUser => AllowedUserAndQuantity != null; + public bool HasRestrictionsByUser => AllowedUserAndQuantity != null; /// /// Is data access restricted by institution. /// [NotMapped] [Projectable] - public bool IsRestrictedByInstitution => AllowedInstitutions != null; + public bool HasRestrictionsByInstitution => AllowedInstitutions != null; /// /// Is data access restricted by application. /// [NotMapped] [Projectable] - public bool IsRestrictedByApplication => AllowedApplications != null; + public bool HasRestrictionsByApplication => AllowedApplications != null; } \ No newline at end of file diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 5115543d..dd77fd22 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,9 +1,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using Database.ApiRequests.Dto; using Database.Services; +using EntityFrameworkCore.Projectables; +using Microsoft.EntityFrameworkCore; namespace Database.Data; +[Index(nameof(InstitutionId), IsUnique = true)] public sealed class InstitutionAccessRights( Guid institutionId, uint? allowedUserCount, @@ -18,25 +23,28 @@ TimeSpan period public TimeSpan Period { get; set; } = period; public List UserAlreadyAccessed { get; private set; } = []; - /// - /// Check if dataset is restricted by access rights for institution. Datasets per time period of - /// max user per institution. - /// - /// - /// Id of current user. - /// - /// Reason, why data is restricted. - /// - internal bool IsDataRestricted(IData dataItem, Guid currentUserId, CacheService cacheService, out string reason) + [NotMapped] + [Projectable] + public bool HasRestrictions => + AllowedDatasetsPerTime != null + || AllowedUserCount != null; + + [NotMapped] + [Projectable] + public bool HasRestrictionsByTime => AllowedDatasetsPerTime != null; + + [NotMapped] + [Projectable] + public bool HasRestrictionsByUser => AllowedUserCount != null; + + internal bool IsDataRestrictedByTime(IData dataItem, CacheService cacheService, out string? reason) { var isRestricted = false; - reason = ""; - + reason = null; // Check restriction for time period if (AllowedDatasetsPerTime is not null) { var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPeriod(InstitutionId); - if (accessesPerPeriod.StartTime.Add(Period) < DateTime.Now) { if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) @@ -44,13 +52,20 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, CacheService isRestricted = true; reason = $"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets for institution {InstitutionId} reached."; } + cacheService.AddAccessCountToPeriod(InstitutionId); } else { cacheService.SetNewTimePeriod(InstitutionId); } } + return isRestricted; + } + internal bool IsDataRestrictedByUser(IData dataItem, Guid currentUserId, out string? reason) + { + var isRestricted = false; + reason = null; // Check restriction for users per institution if (AllowedUserCount is not null) { @@ -59,21 +74,8 @@ internal bool IsDataRestricted(IData dataItem, Guid currentUserId, CacheService isRestricted = true; reason = $"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."; } + UserAlreadyAccessed.Add(currentUserId); } - - // If restricted add user or count - if (!isRestricted) - { - if (AllowedDatasetsPerTime is not null && AllowedDatasetsPerTime > 0) - { - cacheService.AddAccessCountToPeriod(InstitutionId); - } - if (AllowedUserCount is not null && AllowedUserCount > 0) - { - UserAlreadyAccessed.Add(currentUserId); - } - } - return isRestricted; } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 29715376..11686b14 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -22,7 +22,7 @@ public sealed class OpticalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetAllOpticalData( + public Task> GetAllOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -34,15 +34,13 @@ CancellationToken cancellationToken // TODO Use `locale`. sorting.StabilizeOrder(); var filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); - // Check if there is restricted data if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) { - return filteredData; + return Task.FromResult(filteredData); } - // Apply acces rights on data - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + return accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } public async Task GetOpticalDataAsync( @@ -58,12 +56,14 @@ CancellationToken cancellationToken id, cancellationToken ); - if (opticalData is null) + { + return null; + } + if (!opticalData.DataAccessRights.HasRestrictions) { return opticalData; } - return await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs new file mode 100644 index 00000000..0b2457f4 --- /dev/null +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs @@ -0,0 +1,2562 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250731163636_MakeInstitutionIdOfAccessRightsUnique")] + partial class MakeInstitutionIdOfAccessRightsUnique + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .IsRequired() + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs new file mode 100644 index 00000000..f3cfe885 --- /dev/null +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MakeInstitutionIdOfAccessRightsUnique : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_institution_access_rights_InstitutionId", + schema: "database", + table: "institution_access_rights", + column: "InstitutionId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_institution_access_rights_InstitutionId", + schema: "database", + table: "institution_access_rights"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index cc5c9747..9f78f851 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -256,6 +256,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("InstitutionId") + .IsUnique(); + b.ToTable("institution_access_rights", "database"); }); diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index f4b3371b..f0aeaa54 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250530155318_AddRequiredPropertyExistsToPublication +20250731163636_MakeInstitutionIdOfAccessRightsUnique diff --git a/backend/src/Migrations/migrate_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql b/backend/src/Migrations/migrate_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql new file mode 100644 index 00000000..4b8a53b8 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql @@ -0,0 +1,13 @@ +START TRANSACTION; +ALTER TABLE database."OpenIddictTokens" ALTER COLUMN "Type" TYPE character varying(150); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250725172127_UpgradeOpenIddictToVersion7', '9.0.7'); + +CREATE UNIQUE INDEX "IX_institution_access_rights_InstitutionId" ON database.institution_access_rights ("InstitutionId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250731163636_MakeInstitutionIdOfAccessRightsUnique', '9.0.7'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index 3a1e20b0..f4b3371b 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20250530151605_ModelReferenceProperly +20250530155318_AddRequiredPropertyExistsToPublication diff --git a/backend/src/Migrations/rollback_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql b/backend/src/Migrations/rollback_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql new file mode 100644 index 00000000..651b638f --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250530155318_AddRequiredPropertyExistsToPublication_to_20250731163636_MakeInstitutionIdOfAccessRightsUnique.sql @@ -0,0 +1,13 @@ +START TRANSACTION; +DROP INDEX database."IX_institution_access_rights_InstitutionId"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250731163636_MakeInstitutionIdOfAccessRightsUnique'; + +ALTER TABLE database."OpenIddictTokens" ALTER COLUMN "Type" TYPE character varying(50); + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250725172127_UpgradeOpenIddictToVersion7'; + +COMMIT; + diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 1105dc5a..34ed25b6 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -11,112 +11,122 @@ namespace Database.Services; -/// -/// Service to check if requested data can be returned regarding access rights. -/// -/// -/// -/// -/// public sealed class AccessRightsService( ApplicationDbContext context, UserService userService, CacheService cacheService, ILogger logger) { - /// - /// Apply access rights to passed data list. - /// - /// Data to apply acces rights on. - /// - /// List of data that can be returned. - public async Task> ApplyAccessRightsOnData(IQueryable data, CancellationToken cancellationToken) + public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { - var filteredData = new List(); - var restrictions = new List(); - var institutions = new List(); - var accessRights = new List(); + return ( + await ApplyAccessRightsOnData(new List { data }.AsQueryable(), cancellationToken) + ).FirstOrDefault(); + } - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); - var applicationId = userService.GetApplicationIdFromUser(); - if (currentUser is null || applicationId is null) - { - logger.UnknownUserOrApplication(currentUser?.Uuid, applicationId); - return Enumerable.Empty().AsQueryable(); - } + public async Task> ApplyAccessRightsOnData( + IQueryable data, + CancellationToken cancellationToken + ) + where T : IData + { + IReadOnlyList? representedInstitutionIds = null; + IReadOnlyList? institutionAccessRights = null; + uint? alreadyAccessedCount = null; + + var currentUser = await userService.GetCurrentUser(cancellationToken); + var openIdConnectClientId = userService.GetOpenIdConnectClientId(); - var alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); - foreach (var institution in currentUser.RepresentedInstitutions.Edges) + if (currentUser is not null) { - institutions.Add(institution.Node.Uuid); - var accessRightOfInstitution = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == institution.Node.Uuid, cancellationToken).ConfigureAwait(false); - if (accessRightOfInstitution is not null) - { - accessRights.Add(accessRightOfInstitution); - } + representedInstitutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); + institutionAccessRights = (await context.InstitutionAccessRights.AsQueryable() + .Where(x => representedInstitutionIds.Contains(x.InstitutionId)) + .ToListAsync(cancellationToken)) + .AsReadOnly(); + alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); } - var result = ProcessData(data, applicationId, institutions, currentUser, alreadyAccessedCount, cacheService, accessRights); + var result = ProcessData(data, currentUser, openIdConnectClientId, representedInstitutionIds, institutionAccessRights, alreadyAccessedCount, cacheService); // Save InstitutionAccessRight changes await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); return result.AsQueryable(); } - /// - /// Apply access rights on passed data item. - /// - /// Data to apply acces rights on. - /// - /// Data item. - public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) - where T : IData - { - var result = await ApplyAccessRightsOnData(new List { data }.AsQueryable(), cancellationToken).ConfigureAwait(false); - - return result.FirstOrDefault(); - } - private IEnumerable ProcessData( IQueryable data, - string applicationId, - List institutions, - CurrentUserDto currentUser, - uint alreadyAccessedCount, - CacheService cacheService, - List accessRights) + CurrentUserDto? currentUser, + string? openIdConnectClientId, + IReadOnlyList? representedInstitutionIds, + IReadOnlyList? institutionAccessRights, + uint? alreadyAccessedCount, + CacheService cacheService + ) where T : IData { - var reason = ""; foreach (var dataItem in data) { if (dataItem.DataAccessRights.HasRestrictions) { - if (dataItem.IsRestrictedByApplication(applicationId)) + if (dataItem.DataAccessRights.HasRestrictionsByApplication + && ( + openIdConnectClientId is null + || dataItem.IsRestrictedByApplication(openIdConnectClientId) + ) + ) { logger.DataRestriction(dataItem.Id, "Application not allowed."); continue; } - if (dataItem.IsRestrictedByInstitutions(institutions)) + if (dataItem.DataAccessRights.HasRestrictionsByInstitution + && ( + representedInstitutionIds is null + || dataItem.IsRestrictedByInstitutions(representedInstitutionIds) + ) + ) { logger.DataRestriction(dataItem.Id, "Institution not allowed."); continue; } - if (dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedCount)) + if (dataItem.DataAccessRights.HasRestrictionsByUser + && ( + currentUser is null + || alreadyAccessedCount is null + || dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedCount ?? 0) + ) + ) { logger.DataRestriction(dataItem.Id, "Allowed accesses for user reached."); continue; } - if (accessRights.Any(x => x.IsDataRestricted(dataItem, currentUser.Uuid, cacheService, out reason))) + if (currentUser is not null && alreadyAccessedCount is not null) + { + alreadyAccessedCount = alreadyAccessedCount + 1; + cacheService.SetAccessCountForUser(currentUser.Uuid, alreadyAccessedCount ?? 0); + } + var reason = ""; + if (institutionAccessRights is not null + && institutionAccessRights.Any(x => + ( + x.HasRestrictionsByTime + && x.IsDataRestrictedByTime(dataItem, cacheService, out reason) + ) + || ( + x.HasRestrictionsByUser + && ( + currentUser is null + || x.IsDataRestrictedByUser(dataItem, currentUser.Uuid, out reason) + ) + ) + ) + ) { logger.DataRestriction(dataItem.Id, reason); continue; } - - cacheService.SetAccessCountForUser(currentUser.Uuid, ++alreadyAccessedCount); } - yield return dataItem; } } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 1e8f8d0a..085325d2 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -32,7 +32,7 @@ public sealed class UserService( /// Get application from user calims. /// /// ClientId from claims as applicationId. - public string? GetApplicationIdFromUser() + public string? GetOpenIdConnectClientId() { return httpContextAccessor.HttpContext?.User.GetClaim(Claims.AuthorizedParty); } From 57df636e98f1d9b6f656279c1de7fb941b8c5cfb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 12 Aug 2025 19:54:32 +0200 Subject: [PATCH 384/878] Add query variables to approvals --- backend/src/ApiRequests/DataApi.cs | 65 +- backend/src/Data/ApplicationDbContext.cs | 85 +- backend/src/Data/DataApproval.cs | 5 + backend/src/Data/IApproval.cs | 2 + backend/src/Data/NamedMethodArgument.cs | 11 +- backend/src/Data/ResponseApproval.cs | 3 + .../CalorimetricDataMutations.cs | 20 +- .../DataApprovals/DataApprovalInput.cs | 2 + .../DataApprovals/DataApprovalMutations.cs | 1 + .../GeometricDataX/GeometricDataMutations.cs | 4 +- .../GetHttpsResourceMutations.cs | 10 +- .../HygrothermalDataMutations.cs | 20 +- .../MethodAsService/MethodAsServiceQueries.cs | 2 - .../src/GraphQl/NamedMethodArgumentType.cs | 10 - .../OpticalDataX/OpticalDataMutations.cs | 20 +- .../PhotovoltaicDataMutations.cs | 20 +- .../Logging/ResponseApprovalServiceLogging.cs | 10 +- ...0_AddQueryVariablesToApprovals.Designer.cs | 2606 +++++++++++++++++ ...0812171810_AddQueryVariablesToApprovals.cs | 149 + .../ApplicationDbContextModelSnapshot.cs | 68 +- .../src/Services/ResponseApprovalService.cs | 41 +- 21 files changed, 2937 insertions(+), 217 deletions(-) create mode 100644 backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs create mode 100644 backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 9ea91a0d..04b5b260 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -10,7 +10,7 @@ namespace Database.ApiRequests; /// -/// Class to request XData from Database API. +/// Class to request *Data from Database API. /// public sealed class DataApi { @@ -58,49 +58,33 @@ public sealed class DataApi /// /// Create query to request data and get response. /// - /// Id of data to request. - /// File names of query files. - /// - /// - /// - /// - /// - /// - /// Throws exception, when query could not be constructed or no response. + /// + /// Throws exception, when response could not be serialized. /// - /// Query and response for data. - public static async Task<(string Query, string Response)> CreateQueryAndGetResponse( + /// Query, variables, and response for data. + public static async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData( Guid dataId, string[] filenames, AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - - where TGraphQlResponse : class + CancellationToken cancellationToken + ) + where TGraphQlData : class { - var request = new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), - new { uuid = dataId }, - "" - ); - var query = request.Query ?? throw new InvalidOperationException("Failed to construct GraphQL query."); - - var responseObject = await apiRequestService.Database().QueryGraphQl( - appSettings, - request, - httpClientFactory, - httpContextAccessor, - cancellationToken - ).ConfigureAwait(false); - var response = JsonSerializer.Serialize(responseObject); - if (string.IsNullOrEmpty(response)) - { - throw new InvalidOperationException("No response for query."); - } - - return (query, response); + var variables = new { uuid = dataId }; + var query = await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false); + var response = JsonSerializer.Serialize( + await apiRequestService.Database().QueryGraphQl( + appSettings, + new GraphQLRequest(query, variables), + httpClientFactory, + httpContextAccessor, + cancellationToken + ) + ) ?? throw new JsonException("Failed to serialize the response."); + return (query, JsonSerializer.SerializeToElement(variables), response); } /// @@ -117,7 +101,7 @@ await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), /// Throws exception, when query could not be constructed or no response. /// /// Query and response for data. - public static async Task> GetDataFromDatabase( + public static async Task> GetDataFromDatabase( Uri databaseUri, Guid dataId, string[] filenames, @@ -127,14 +111,13 @@ public static async Task> GetDataFromDatabase< IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) - where TGraphQlResponse : class + where TGraphQlData : class { var request = new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false), - new { uuid = dataId }, - "" + new { uuid = dataId } ); - return await apiRequestService.Database().QueryGraphQlFromUrl( + return await apiRequestService.Database().QueryGraphQlFromUrl( appSettings, databaseUri, request, diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 8c594db3..3d2c0014 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -75,6 +75,45 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigureData( + EntityTypeBuilder builder + ) + where TData : class, IData + { + builder + .OwnsOne( + data => data.AppliedMethod, + method => + { + method.OwnsMany(m => m.Arguments); + method.OwnsMany(m => m.Sources); + } + ); + builder + .OwnsOne( + x => x.Approval, + approval => + { + approval + .Property(a => a.Variables) + .HasDefaultValueSql("'{}'"); + } + ); + builder + .OwnsMany( + x => x.Approvals, + approvals => + { + approvals + .Property(a => a.Variables) + .HasDefaultValueSql("'{}'"); + } + ); + return builder; + } + private static void ConfigureIdentityEntities( ModelBuilder builder ) @@ -86,46 +125,32 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.HasDefaultSchema(_schemaName); - modelBuilder.HasPostgresExtension( - "pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + modelBuilder.HasPostgresExtension("pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation CreateEnumerations(modelBuilder, _schemaName); ConfigureIdentityEntities(modelBuilder); ConfigureEntity( modelBuilder.Entity() ) .ToTable("get_https_resource"); - ConfigureEntity( - modelBuilder.Entity() - ) - .OwnsOne( - data => data.AppliedMethod, - method => - { - method.OwnsMany(m => m.Arguments); - method.OwnsMany(m => m.Sources); - } - ) - .ToTable("calorimetric_data"); - ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("hygrothermal_data"); - ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("optical_data"); - ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("photovoltaic_data"); + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("calorimetric_data"); + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("geometric_data"); + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("hygrothermal_data"); + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("optical_data"); + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("photovoltaic_data"); ConfigureEntity( modelBuilder.Entity() ) .ToTable("user"); - ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("geometric_data"); ConfigureEntity( modelBuilder.Entity() ) diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index e090fe6e..1f986d6e 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -9,6 +10,7 @@ public sealed class DataApproval( string signature, string keyFingerprint, string query, + JsonElement variables, string response, Guid approverId, Reference statement @@ -21,6 +23,7 @@ private DataApproval( string signature, string keyFingerprint, string query, + JsonElement variables, string response, Guid approverId ) @@ -29,6 +32,7 @@ Guid approverId signature, keyFingerprint, query, + variables, response, approverId, null! // EF Core will set this owned navigation property after construction. @@ -41,6 +45,7 @@ Guid approverId public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; + public JsonElement Variables { get; private set; } = variables; public string Response { get; private set; } = response; public Reference Statement { get; private set; } = statement; } \ No newline at end of file diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 98911da3..982cf5fc 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using HotChocolate.Types; namespace Database.Data; @@ -10,5 +11,6 @@ public interface IApproval string Signature { get; } string KeyFingerprint { get; } string Query { get; } + JsonElement Variables { get; } string Response { get; } } \ No newline at end of file diff --git a/backend/src/Data/NamedMethodArgument.cs b/backend/src/Data/NamedMethodArgument.cs index e6c05366..f05970ab 100644 --- a/backend/src/Data/NamedMethodArgument.cs +++ b/backend/src/Data/NamedMethodArgument.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; @@ -7,15 +6,9 @@ namespace Database.Data; [Owned] public sealed class NamedMethodArgument( string name, - JsonDocument value + JsonElement value ) - : IDisposable { public string Name { get; private set; } = name; - public JsonDocument Value { get; private set; } = value; - - public void Dispose() - { - Value.Dispose(); - } + public JsonElement Value { get; private set; } = value; } \ No newline at end of file diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index f878a29c..70aa1338 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using Microsoft.EntityFrameworkCore; namespace Database.Data @@ -9,6 +10,7 @@ public sealed class ResponseApproval( string signature, string keyFingerprint, string query, + JsonElement variables, string response ) : IApproval @@ -17,6 +19,7 @@ string response public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; + public JsonElement Variables { get; private set; } = variables; public string Response { get; private set; } = response; } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 2acdf569..44787f08 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -63,15 +63,7 @@ CancellationToken cancellationToken input.AppliedMethod.Arguments .Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value )) .ToList(), input.AppliedMethod.Sources @@ -107,15 +99,7 @@ input.RootResource.AppliedConversionMethod is null input.RootResource.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.RootResource.AppliedConversionMethod.SourceName diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs index d509af37..aca327c4 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs @@ -1,4 +1,5 @@ using System; +using System.Text.Json; using Database.GraphQl.References; namespace Database.GraphQl.DataApprovals; @@ -10,6 +11,7 @@ public sealed record DataApprovalInput string Signature, string KeyFingerprint, string Query, + JsonElement Variables, string Response, Guid ApproverId, ReferenceInput Statement diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 3a671d33..3335c255 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -102,6 +102,7 @@ CancellationToken cancellationToken input.Signature, input.KeyFingerprint, input.Query, + input.Variables, input.Response, input.ApproverId, ReferenceType.FromInput(input.Statement) diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 8a91b248..f2328757 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -63,7 +63,7 @@ CancellationToken cancellationToken input.AppliedMethod.Arguments .Select(a => new NamedMethodArgument( a.Name, - JsonDocument.Parse(@"""TODO""") + a.Value )) .ToList(), input.AppliedMethod.Sources @@ -98,7 +98,7 @@ input.RootResource.AppliedConversionMethod is null input.RootResource.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.RootResource.AppliedConversionMethod.SourceName diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index d9597e39..b0752e2c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -94,15 +94,7 @@ input.AppliedConversionMethod is null input.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `OpticalDataMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.AppliedConversionMethod.SourceName diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 51620ef2..0e6d283c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -64,15 +64,7 @@ CancellationToken cancellationToken input.AppliedMethod.Arguments .Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value )) .ToList(), input.AppliedMethod.Sources @@ -106,15 +98,7 @@ input.RootResource.AppliedConversionMethod is null input.RootResource.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.RootResource.AppliedConversionMethod.SourceName diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 07e2bc43..1f6e4e8c 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Linq; using System.Net.Http; using System.Text.Json; @@ -8,7 +7,6 @@ using Database.ApiRequests; using Database.ApiRequests.Dto; using Database.Data; -using Database.GraphQl.Databases; using Database.Services; using HotChocolate; using HotChocolate.Resolvers; diff --git a/backend/src/GraphQl/NamedMethodArgumentType.cs b/backend/src/GraphQl/NamedMethodArgumentType.cs index bd72b35b..92729ba9 100644 --- a/backend/src/GraphQl/NamedMethodArgumentType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentType.cs @@ -10,15 +10,5 @@ protected override void Configure( IObjectTypeDescriptor descriptor ) { - // Note that in the GraphQL configuration we map the scalar "Any" - // to `JsonType`. - descriptor - .Field(x => x.Value) - .Type>() - .Resolve(context => - context.Parent() - .Value - .RootElement - ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index cdc44716..a72cd504 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -64,15 +64,7 @@ CancellationToken cancellationToken input.AppliedMethod.Arguments .Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value )) .ToList(), input.AppliedMethod.Sources @@ -119,15 +111,7 @@ input.RootResource.AppliedConversionMethod is null input.RootResource.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.RootResource.AppliedConversionMethod.SourceName diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 8a5ecb40..755cb849 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -67,15 +67,7 @@ CancellationToken cancellationToken input.AppliedMethod.Arguments .Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value )) .ToList(), input.AppliedMethod.Sources @@ -109,15 +101,7 @@ input.RootResource.AppliedConversionMethod is null input.RootResource.AppliedConversionMethod.Arguments.Select(a => new NamedMethodArgument( a.Name, - // TODO Turn `a.Value` into `JsonDocument`. It comes - // as nested `IReadOnlyDictionary/-List` as said on - // https://chillicream.com/docs/hotchocolate/v11/defining-a-schema/scalars/#any-type - // Take inspiration from - // https://josef.codes/custom-dictionary-string-object-jsonconverter-for-system-text-json/ - // and - // https://github.com/joseftw/JOS.SystemTextJsonDictionaryStringObjectJsonConverter/blob/develop/src/JOS.SystemTextJsonDictionaryObjectModelBinder/DictionaryStringObjectJsonConverter.cs - // This is also needed in `GetHttpsResourceMutations`. - JsonDocument.Parse(@"""TODO""") + a.Value ) ).ToList(), input.RootResource.AppliedConversionMethod.SourceName diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs index bb19c8c7..c2a0247e 100644 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -1,3 +1,5 @@ +using System; +using System.Text.Json; using Database.Services; using Microsoft.Extensions.Logging; @@ -7,11 +9,11 @@ public static partial class ResponseApprovalServiceLogging { [LoggerMessage( Level = LogLevel.Debug, - Message = "Getting query and response for {DataType}")] - public static partial void GetQueryAndResponse(this ILogger logger, string dataType); + Message = "Querying all meta data for {DataType} with ID {Id}")] + public static partial void QueryAllMetaData(this ILogger logger, string dataType, Guid id); [LoggerMessage( Level = LogLevel.Debug, - Message = "Query and response: {Query} \n {Response}")] - public static partial void QueryAndResponce(this ILogger logger, string query, string response); + Message = "Query, variables, and response: {Query}, {Variables}, and {Response}")] + public static partial void QueryAndVariablesAndResponce(this ILogger logger, string query, JsonElement variables, string response); } \ No newline at end of file diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs new file mode 100644 index 00000000..b4475460 --- /dev/null +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs @@ -0,0 +1,2606 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250812171810_AddQueryVariablesToApprovals")] + partial class AddQueryVariablesToApprovals + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Response") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs new file mode 100644 index 00000000..97eca89c --- /dev/null +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs @@ -0,0 +1,149 @@ +using System.Text.Json; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddQueryVariablesToApprovals : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "geometric_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "calorimetric_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "geometric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "calorimetric_data"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 9f78f851..7d59ee59 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -662,8 +662,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); @@ -757,6 +756,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("CalorimetricDataId"); b1.ToTable("calorimetric_data", "database"); @@ -798,6 +802,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("CalorimetricDataId", "Id"); b1.ToTable("calorimetric_data_Approvals", "database"); @@ -1004,8 +1013,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodGeometricDataId", "Id"); @@ -1099,6 +1107,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("GeometricDataId"); b1.ToTable("geometric_data", "database"); @@ -1140,6 +1153,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("GeometricDataId", "Id"); b1.ToTable("geometric_data_Approvals", "database"); @@ -1400,8 +1418,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); @@ -1464,8 +1481,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); @@ -1559,6 +1575,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("HygrothermalDataId"); b1.ToTable("hygrothermal_data", "database"); @@ -1600,6 +1621,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("HygrothermalDataId", "Id"); b1.ToTable("hygrothermal_data_Approvals", "database"); @@ -1834,8 +1860,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodOpticalDataId", "Id"); @@ -1929,6 +1954,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("OpticalDataId"); b1.ToTable("optical_data", "database"); @@ -1970,6 +2000,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("OpticalDataId", "Id"); b1.ToTable("optical_data_Approvals", "database"); @@ -2178,8 +2213,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b2.Property("Value") - .IsRequired() + b2.Property("Value") .HasColumnType("jsonb"); b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); @@ -2273,6 +2307,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("PhotovoltaicDataId"); b1.ToTable("photovoltaic_data", "database"); @@ -2314,6 +2353,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + b1.HasKey("PhotovoltaicDataId", "Id"); b1.ToTable("photovoltaic_data_Approvals", "database"); diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 103c0239..79c16af6 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,5 +1,6 @@ using System; using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -11,22 +12,14 @@ namespace Database.Services; -/// -/// Service to create response approval -/// -/// -/// -/// -/// -/// -/// public sealed class ResponseApprovalService( AppSettings appSettings, SigningService signingService, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, - ILogger logger) + ILogger logger +) { private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); private sealed record GeometricDataResponse(GeometricDataDto GeometricData); @@ -44,31 +37,27 @@ private sealed record OpticalDataResponse(OpticalDataDto OpticalData); public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset - logger.GetQueryAndResponse(dataObject.GetType().Name); - var queryAdnResponse = await GetQueryAndResponse(dataObject, cancellationToken).ConfigureAwait(false); - - logger.QueryAndResponce(queryAdnResponse.Query, queryAdnResponse.Response); - - var signatureResult = await signingService.SignData(queryAdnResponse.Response).ConfigureAwait(false); - + logger.QueryAllMetaData(dataObject.GetType().Name, dataObject.Id); + var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken).ConfigureAwait(false); + logger.QueryAndVariablesAndResponce(query, variables, response); + var signatureResult = await signingService.SignData(response).ConfigureAwait(false); if (!signatureResult.Success) { throw new InvalidOperationException($"Signing of data failed! {signatureResult.Output}"); } - - return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, await signingService.GetFingerprint(), queryAdnResponse.Query, queryAdnResponse.Response); + return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, await signingService.GetFingerprint(), query, variables, response); } - private async Task<(string Query, string Response)> GetQueryAndResponse(IData dataObject, CancellationToken cancellationToken) + private async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData(IData dataObject, CancellationToken cancellationToken) { return dataObject switch { - GeometricData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - CalorimetricData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - HygrothermalData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - PhotovoltaicData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - OpticalData data => await DataApi.CreateQueryAndGetResponse(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - _ => throw new NotImplementedException("Unknown IData object."), + GeometricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + CalorimetricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + HygrothermalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + PhotovoltaicData data => await DataApi.QueryAllMetaData(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + OpticalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + _ => throw new NotImplementedException($"Unsupported data type {typeof(IData)}"), }; } } \ No newline at end of file From 9d295959657a5704db7c626d7dcd112101ff58c2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 11:13:52 +0200 Subject: [PATCH 385/878] Make signing service thread-safe and improve error handling (in particular, avoid unnecessarily writing and reading to a files with a fixed name) --- backend/src/Logging/SigningServiceLogging.cs | 28 +-- .../src/Services/ResponseApprovalService.cs | 8 +- backend/src/Services/SigningService.cs | 176 +++++------------- 3 files changed, 51 insertions(+), 161 deletions(-) diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs index 50af1124..b29f8e80 100644 --- a/backend/src/Logging/SigningServiceLogging.cs +++ b/backend/src/Logging/SigningServiceLogging.cs @@ -8,22 +8,12 @@ public static partial class SigningServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG Fingerprint: {Fingerprint}")] - public static partial void Fingerprint(this ILogger logger, string fingerprint); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "GNUPG: Failed to get fingerprint.")] - public static partial void FingerprintError(this ILogger logger); + public static partial void ExtractedFingerprint(this ILogger logger, string fingerprint); [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG Signature: {Signature}")] - public static partial void Signature(this ILogger logger, string signature); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "GNUPG: Failed to create signature: {Output}")] - public static partial void SignatureError(this ILogger logger, string output); + public static partial void CreatedSignature(this ILogger logger, string signature); [LoggerMessage( Level = LogLevel.Debug, @@ -33,7 +23,7 @@ public static partial class SigningServiceLogging [LoggerMessage( Level = LogLevel.Error, Message = "GNUPG Execute Command: {Error}")] - public static partial void ExecuteCommandError(this ILogger logger, string error); + public static partial void ExecuteCommandDiagnostics(this ILogger logger, string error); [LoggerMessage( Level = LogLevel.Debug, @@ -43,15 +33,5 @@ public static partial class SigningServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "GNUPG: Execute Command Result: {result}")] - public static partial void ExecuteCommandResult(this ILogger logger, int result); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "GNUPG: Write file exception: {Exception}")] - public static partial void WriteFileException(this ILogger logger, string exception); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "GNUPG: Read file exception: {Exception}")] - public static partial void ReadFileException(this ILogger logger, string exception); + public static partial void ExecuteCommandExitCode(this ILogger logger, int result); } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 79c16af6..06cb5dea 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -40,12 +40,8 @@ public async Task CreateResponseApproval(IData dataObject, Can logger.QueryAllMetaData(dataObject.GetType().Name, dataObject.Id); var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken).ConfigureAwait(false); logger.QueryAndVariablesAndResponce(query, variables, response); - var signatureResult = await signingService.SignData(response).ConfigureAwait(false); - if (!signatureResult.Success) - { - throw new InvalidOperationException($"Signing of data failed! {signatureResult.Output}"); - } - return new ResponseApproval(DateTime.UtcNow, signatureResult.Output, await signingService.GetFingerprint(), query, variables, response); + var signature = await signingService.SignData(response).ConfigureAwait(false); + return new ResponseApproval(DateTime.UtcNow, signature, await signingService.ExtractFingerprint(), query, variables, response); } private async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData(IData dataObject, CancellationToken cancellationToken) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index ba536b24..e0981297 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -1,165 +1,79 @@ using System; using System.Diagnostics; -using System.IO; using System.Threading.Tasks; using Database.Logging; -using Database.Services; using Microsoft.Extensions.Logging; +using Quartz.Util; namespace Database.Services; -/// -/// Service to sign data string with GNUPG. -/// -/// -/// Instance of public sealed class SigningService( AppSettings appSettings, ILogger logger) { - private const string FILENAME = "dataToSign"; - private const string FILE_EXTENSION = ".asc"; - - /// - /// Initialize service by importing private key from file passed into container. - /// - /// True, if privat key was successfully imported and fingerprint set. - /// If initialization failed. public async Task Initialize() { - // Execute command to import private key - var (success, output) = await ExecuteGnuCommand($"gpg --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --import ./gpg-keys/{appSettings.GnupgPrivateKeyFilename}"); - if (success) - { - var fingerprint = await GetFingerprint(); - if (!string.IsNullOrEmpty(fingerprint)) - { - logger.Fingerprint(fingerprint); - } - else - { - logger.FingerprintError(); - throw new InvalidOperationException("Failed to initialize GnuPG signing service."); - } - } + var (success, output, diagnostics) = await ExecuteGnuCommand( + $"gpg --batch --passphrase '{appSettings.GnupgPrivateKeyPassphrase}' --import './gpg-keys/{appSettings.GnupgPrivateKeyFilename}'" + ); } - /// - /// Sign passed data string and return signature. - /// - /// Data string to create signature from. - /// True and generated signature, if successful. Otherwise false and error. - public async Task<(bool Success, string Output)> SignData(string data) + public async Task SignData(string data) { - // Write data string to file - if (!WriteDataToFile(data)) - { - return (false, ""); - } - - // Execute command to generate detached signature file Creates file with .asc ending - var (success, output) = await ExecuteGnuCommand($"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {await GetFingerprint()} {FILENAME}"); - - if (success) - { - // Read signature from file - (success, output) = ReadSignatureFromFile(); - logger.Signature(output); - } - else - { - logger.SignatureError(output); - } - - // Remove created files - RemoveFiles(); - return (success, output); - } - - private async Task<(bool, string)> ExecuteGnuCommand(string command) - { - logger.ExecuteCommand(command); - - var escapedArgs = command.Replace("\"", "\\\""); - ProcessStartInfo startInfo = new() - { - FileName = "bash", - Arguments = $"-c \"{escapedArgs}\"", - CreateNoWindow = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - }; - - var proc = Process.Start(startInfo); - ArgumentNullException.ThrowIfNull(proc); - var output = proc.StandardOutput.ReadToEnd(); - var error = proc.StandardError.ReadToEnd(); - await proc.WaitForExitAsync(); - if (string.IsNullOrEmpty(output)) - { - output = error; - } - else if (!string.IsNullOrEmpty(error)) - { - logger.ExecuteCommandError(error); - } - logger.ExecuteCommandOutput(output); - logger.ExecuteCommandResult(proc.ExitCode); - return (proc.ExitCode == 0, output); - } - - /// - /// Get fingerprint. - /// - /// The fingerprint. - /// If fingerprint extraction failed. - public async Task GetFingerprint() - { - var (success, fingerprint) = await ExecuteGnuCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); + var (success, signature, diagnostics) = await ExecuteGnuCommand( + $"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {await ExtractFingerprint()}", + data + ); if (!success) { - throw new InvalidOperationException("Failed to extract fingerprint."); + throw new InvalidOperationException($"Failed to create signature with standard output {signature} and diagnostic information {diagnostics}"); } - return fingerprint; + logger.CreatedSignature(signature); + return signature; } - private bool WriteDataToFile(string data) + public async Task ExtractFingerprint() { - try - { - using (var outputFile = new StreamWriter(FILENAME)) - { - outputFile.WriteLine(data); - } - - return true; - } - catch (Exception exception) + var (success, fingerprint, diagnostics) = await ExecuteGnuCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); + if (!success || fingerprint.IsNullOrWhiteSpace()) { - logger.WriteFileException(exception.ToString()); - return false; + throw new InvalidOperationException($"Failed to extract fingerprint with standard output {fingerprint} and diagnostic information {diagnostics}"); } + logger.ExtractedFingerprint(fingerprint); + return fingerprint; } - private (bool, string) ReadSignatureFromFile() + private async Task<(bool Success, string Output, string Error)> ExecuteGnuCommand( + string command, + string? input = null + ) { - try - { - using (var file = new StreamReader(FILENAME + FILE_EXTENSION)) + logger.ExecuteCommand(command); + var escapedCommand = command.Replace("\"", "\\\""); + var process = Process.Start( + new ProcessStartInfo() { - return (true, file.ReadToEnd()); + FileName = "bash", + Arguments = $"-c \"{escapedCommand}\"", + ErrorDialog = false, + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, } - } - catch (IOException exception) + ); + ArgumentNullException.ThrowIfNull(process); + if (input is not null) { - logger.ReadFileException(exception.ToString()); - return (false, exception.ToString()); + await process.StandardInput.WriteLineAsync(input); } - } - - private static void RemoveFiles() - { - File.Delete(FILENAME); - File.Delete(FILENAME + FILE_EXTENSION); + process.StandardInput.Close(); + var output = process.StandardOutput.ReadToEnd(); + var diagnostics = process.StandardError.ReadToEnd(); + await process.WaitForExitAsync(); + logger.ExecuteCommandOutput(output); + logger.ExecuteCommandDiagnostics(diagnostics); + logger.ExecuteCommandExitCode(process.ExitCode); + return (process.ExitCode == 0, output, diagnostics); } } \ No newline at end of file From 87f4d36e543c71a442cbfcb3a3bf76ef39350583 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 11:17:37 +0200 Subject: [PATCH 386/878] Create and update response approvals in parallel --- .../ResponseApprovalMutations.cs | 93 ++++++++++--------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs index 6dc40b82..f32269ca 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -58,34 +59,36 @@ CancellationToken cancellationToken ) ); } - var dataSets = new List(); - var errors = new List(); - await foreach ( - var data in context.CalorimetricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable() + var dataSets = new ConcurrentBag(); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + context.CalorimetricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable() .Union(context.GeometricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) .Union(context.OpticalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) - .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) - ) - { - try + .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()), + cancellationToken, + async (data, cancellationToken) => { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - dataSets.Add(data); + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + errors.Add( + new CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Creating response approval for data {data} failed with message: {exception.Message}", + [] + ) + ); + } } - catch (Exception exception) - { - errors.Add( - new CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Creating response approval for data {data} failed with message: {exception.Message}", - [] - ) - ); - } - } + ); await context.SaveChangesAsync(cancellationToken); - if (errors.Count != 0) + if (!errors.IsEmpty) { return new CreateResponseApprovalsPayload(dataSets, errors); } @@ -136,34 +139,36 @@ CancellationToken cancellationToken ) ); } - var dataSets = new List(); - var errors = new List(); - await foreach ( - var data in context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() + var dataSets = new ConcurrentBag(); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() .Union(context.GeometricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) .Union(context.OpticalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - ) - { - try + .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()), + cancellationToken, + async (data, cancellationToken) => { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - dataSets.Add(data); + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + errors.Add( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, + $"Updating response approval for data {data} failed with message: {exception.Message}", + [] + ) + ); + } } - catch (Exception exception) - { - errors.Add( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, - $"Updating response approval for data {data} failed with message: {exception.Message}", - [] - ) - ); - } - } + ); await context.SaveChangesAsync(cancellationToken); - if (errors.Count != 0) + if (!errors.IsEmpty) { return new UpdateResponseApprovalsPayload(dataSets, errors); } From 15b7a3d386cf5bbeb1fff7bf0d8db3583dfedd27 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 11:18:45 +0200 Subject: [PATCH 387/878] Use the name `Diagnostics` for the output of the standard error stream (because that is what it is actually used for) --- backend/src/Services/SigningService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index e0981297..afe93277 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -43,7 +43,7 @@ public async Task ExtractFingerprint() return fingerprint; } - private async Task<(bool Success, string Output, string Error)> ExecuteGnuCommand( + private async Task<(bool Success, string Output, string Diagnostics)> ExecuteGnuCommand( string command, string? input = null ) From de1b2733771ea01c3b56b0dbd52619df226b3f93 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 12:06:48 +0200 Subject: [PATCH 388/878] Remove all `ConfigureAwait(false)` calls as suggested as best practice for appication-level code on https://devblogs.microsoft.com/dotnet/configureawait-faq/#when-should-i-use-configureawait(false) --- the script `sed -i -e ':a;N;$!ba;s/\s*\.ConfigureAwait(false)//g'` did the trick --- backend/src/ApiRequests/DataApi.cs | 4 ++-- backend/src/ApiRequests/DatabaseApi.cs | 8 ++++---- backend/src/ApiRequests/UserApi.cs | 6 +++--- .../Configuration/HttpContextAuthentication.cs | 4 ++-- .../src/Controllers/AntiforgeryController.cs | 2 +- .../Controllers/AuthenticationController.cs | 4 ++-- .../src/Controllers/FileUploadController.cs | 13 ++++++------- .../AccessRights/DataAccessRightsMutations.cs | 18 +++++++++--------- .../CalorimetricDataMutations.cs | 10 +++++----- .../CalorimetricDataQueries.cs | 4 ++-- backend/src/GraphQl/Connection.cs | 2 +- .../DataApprovals/DataApprovalMutations.cs | 12 ++++++------ backend/src/GraphQl/DataX/DataQueries.cs | 4 ++-- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 4 ++-- .../src/GraphQl/Databases/DatabaseMutations.cs | 2 +- backend/src/GraphQl/Edge.cs | 2 +- .../AssociationsByAssociateIdDataLoader.cs | 1 - .../GraphQl/Entities/EntityByIdDataLoader.cs | 3 +-- backend/src/GraphQl/ForkingConnection.cs | 2 +- .../GeometricDataX/GeometricDataMutations.cs | 10 +++++----- .../GeometricDataX/GeometricDataQueries.cs | 6 +++--- .../GetHttpsResourceMutations.cs | 14 +++++++------- .../GetHttpsResourceResolvers.cs | 10 +++++----- .../HygrothermalDataMutations.cs | 10 +++++----- .../HygrothermalDataQueries.cs | 4 ++-- .../MethodAsService/MethodAsServiceQueries.cs | 6 +++--- .../OpticalDataX/OpticalDataMutations.cs | 8 ++++---- .../GraphQl/OpticalDataX/OpticalDataQueries.cs | 2 +- .../PhotovoltaicDataMutations.cs | 10 +++++----- .../PhotovoltaicDataQueries.cs | 4 ++-- backend/src/GraphQl/Users/UserQueries.cs | 2 +- backend/src/Program.cs | 8 ++++---- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/ApiRequestService.cs | 11 +++++------ backend/src/Services/DataService.cs | 10 +++++----- .../src/Services/ResponseApprovalService.cs | 4 ++-- backend/src/Services/UserService.cs | 6 +++--- 37 files changed, 114 insertions(+), 118 deletions(-) diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 04b5b260..af0aa880 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -74,7 +74,7 @@ CancellationToken cancellationToken where TGraphQlData : class { var variables = new { uuid = dataId }; - var query = await apiRequestService.ConstructGraphQlQuery(filenames).ConfigureAwait(false); + var query = await apiRequestService.ConstructGraphQlQuery(filenames); var response = JsonSerializer.Serialize( await apiRequestService.Database().QueryGraphQl( appSettings, @@ -114,7 +114,7 @@ public static async Task> GetDataFromDatabase( diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 30a18b5c..1ec9b0ea 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -35,7 +35,7 @@ private sealed record DatabaseData(DatabaseDto Database); { var response = await apiRequestService.Metabase().QueryGraphQl( appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames).ConfigureAwait(false), + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames), new { uuid = databaseId @@ -45,7 +45,7 @@ private sealed record DatabaseData(DatabaseDto Database); httpClientFactory, httpContextAccessor, cancellationToken - ).ConfigureAwait(false); + ); return response.Data?.Database; } @@ -63,7 +63,7 @@ CancellationToken cancellationToken new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery( _updateDatabaseFileNames - ).ConfigureAwait(false), + ), new { input = updateDatabaseInput @@ -73,6 +73,6 @@ await apiRequestService.ConstructGraphQlQuery( httpClientFactory, httpContextAccessor, cancellationToken - ).ConfigureAwait(false))?.Data; + ))?.Data; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index 542a1dd3..e26d6f91 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -39,14 +39,14 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); { return (await apiRequestService.Metabase().QueryGraphQl( appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_currentUserFileNames).ConfigureAwait(false), + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_currentUserFileNames), new { }, "CurrentUser" ), httpClientFactory, httpContextAccessor, cancellationToken - ).ConfigureAwait(false)) + )) ?.Data ?.CurrentUser ?? null; @@ -75,6 +75,6 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); httpClientFactory, httpContextAccessor, cancellationToken - ).ConfigureAwait(false); + ); } } \ No newline at end of file diff --git a/backend/src/Configuration/HttpContextAuthentication.cs b/backend/src/Configuration/HttpContextAuthentication.cs index b1999886..94f166f8 100644 --- a/backend/src/Configuration/HttpContextAuthentication.cs +++ b/backend/src/Configuration/HttpContextAuthentication.cs @@ -20,7 +20,7 @@ HttpContext httpContext // `OpenIddictBuilder#AddClient` in // `AuthConfiguration#ConfigureOpenIddictServices`. var cookieAuthenticateResult = await httpContext - .AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + .AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) { httpContext.User = cookieAuthenticateResult.Principal; @@ -36,7 +36,7 @@ HttpContext httpContext // `AuthConfiguration#ConfigureOpenIddictServices` by // `OpenIddictBuilder#AddValidation`. var jwtAuthenticateResult = await httpContext - .AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme).ConfigureAwait(false); + .AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) { httpContext.User = jwtAuthenticateResult.Principal; diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index dc58f4ee..113d39ef 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -25,7 +25,7 @@ public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Con [HttpGet("~/antiforgery/token")] public async Task Token() { - await HttpContextAuthentication.Authenticate(HttpContext).ConfigureAwait(false); + await HttpContextAuthentication.Authenticate(HttpContext); var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append( XsrfCookieKey, diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 30a11a06..b6862c67 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -87,7 +87,7 @@ public async Task LogOut(string? returnUrl) } // Remove the local authentication cookie before triggering a redirection to the remote server. - await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme).ConfigureAwait(false); + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. return SignOut( new AuthenticationProperties( @@ -232,7 +232,7 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate user.Update(name); } - await _context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await _context.SaveChangesAsync(cancellationToken); // Ask the cookie authentication handler to return a new cookie and redirect // the user agent to the return URL stored in the authentication properties. return SignIn( diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 8226682f..1cfaf07c 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -100,7 +100,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { ModelState.AddModelError("CurrentUser", @@ -122,8 +122,7 @@ CancellationToken cancellationToken .Include(e => e.OpticalData) .Include(e => e.PhotovoltaicData) .Where(e => e.Id == getHttpsResourceUuid) - .SingleOrDefaultAsync(cancellationToken) - .ConfigureAwait(false); + .SingleOrDefaultAsync(cancellationToken); if (getHttpsResource is null) { ModelState.AddModelError("GetHttpsResourceUuid", @@ -151,7 +150,7 @@ CancellationToken cancellationToken MediaTypeHeaderValue.Parse(Request.ContentType), s_defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); - var section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + var section = await reader.ReadNextSectionAsync(cancellationToken); while (section is not null) { @@ -195,7 +194,7 @@ CancellationToken cancellationToken ModelState, _permittedExtensions, FileSizeLimit - ).ConfigureAwait(false); + ); if (!ModelState.IsValid) { @@ -204,7 +203,7 @@ CancellationToken cancellationToken using var targetStream = System.IO.File.Create( Path.Combine(TargetDirectoryPath, trustedFileNameForFileStorage)); - await targetStream.WriteAsync(streamedFileContent, cancellationToken).ConfigureAwait(false); + await targetStream.WriteAsync(streamedFileContent, cancellationToken); _logger.SavedUploadedFile(trustedFileNameForDisplay, TargetDirectoryPath, trustedFileNameForFileStorage); @@ -212,7 +211,7 @@ CancellationToken cancellationToken // Drain any remaining section body that hasn't been consumed and // read the headers for the next section. - section = await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false); + section = await reader.ReadNextSectionAsync(cancellationToken); } return Created(nameof(FileUploadController), null); diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 6f01f00c..15f30ad0 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -21,7 +21,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new UpdateDataAccessRightsPayload( @@ -33,7 +33,7 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); if (data is null) { return new UpdateDataAccessRightsPayload( @@ -64,7 +64,7 @@ CancellationToken cancellationToken data.DataAccessRights.AllowedApplications = input.AllowedApplications; data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new UpdateDataAccessRightsPayload(data.DataAccessRights); } @@ -76,7 +76,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new AddInstitutionAccessRightsPayload( @@ -103,7 +103,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); if (accessRights is not null) { @@ -122,7 +122,7 @@ CancellationToken cancellationToken input.AllowedDatasetsPerTimeSpan, TimeSpan.FromDays(input.PeriodInDays)); context.InstitutionAccessRights.Add(accessRights); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new AddInstitutionAccessRightsPayload(accessRights); } @@ -134,7 +134,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new UpdateAccessRightsPayload( @@ -161,7 +161,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken).ConfigureAwait(false); + var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); if (accessRights is null) { @@ -177,7 +177,7 @@ CancellationToken cancellationToken accessRights.AllowedUserCount = input.AllowedUserCount; accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new UpdateAccessRightsPayload(accessRights); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 44787f08..5228ca94 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -23,7 +23,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new CreateCalorimetricDataPayload( @@ -107,17 +107,17 @@ input.RootResource.AppliedConversionMethod is null ); calorimetricData.Resources.Add(resource); context.CalorimetricData.Add(calorimetricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - calorimetricData.Approval = await responseApprovalService.CreateResponseApproval(calorimetricData, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + calorimetricData.Approval = await responseApprovalService.CreateResponseApproval(calorimetricData, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(calorimetricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreateCalorimetricDataPayload( calorimetricData, diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 14945177..3ca4c1c1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } public async Task GetCalorimetricDataAsync( @@ -63,6 +63,6 @@ CancellationToken cancellationToken return calorimetricData; } - return await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index f83cb31d..a1f2ade0 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -25,7 +25,7 @@ CancellationToken cancellationToken ) { return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken).ConfigureAwait(false) ?? [] + await dataLoader.LoadAsync(Subject.Id, cancellationToken) ?? [] ) .Select(_createEdge); } diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 3335c255..1fbc3e58 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -24,7 +24,7 @@ public async Task AddDataApprovalAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { return new AddDataApprovalPayload( @@ -61,7 +61,7 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken).ConfigureAwait(false); + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); if (data is null) { return new AddDataApprovalPayload( @@ -109,17 +109,17 @@ CancellationToken cancellationToken ); data.Approvals.Add(approval); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(data); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new AddDataApprovalPayload( approval, diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index cbaa5f46..6b7a3e1e 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -21,13 +21,13 @@ CancellationToken cancellationToken ) { // TODO Use `locale`. - var data = await dataService.GetDataAsync(id, dbContext, cancellationToken).ConfigureAwait(false); + var data = await dataService.GetDataAsync(id, dbContext, cancellationToken); if (data is null) { return data; } - return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index feacccab..7ea8751e 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -17,7 +17,7 @@ CancellationToken cancellationToken ) { return new GetHttpsResourceTreeRoot( - (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false) ?? []).Single() + (await byId.LoadAsync(_data.Id, cancellationToken) ?? []).Single() ); } @@ -26,7 +26,7 @@ public async Task> GetNonRootVe CancellationToken cancellationToken ) { - return (await byId.LoadAsync(_data.Id, cancellationToken).ConfigureAwait(false) ?? []) + return (await byId.LoadAsync(_data.Id, cancellationToken) ?? []) .Select(v => new GetHttpsResourceTreeNonRootVertex(v) ) diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 840add20..7bb25cd0 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -27,7 +27,7 @@ CancellationToken cancellationToken apiRequestService, httpClientFactory, httpContextAccessor, - cancellationToken).ConfigureAwait(false); + cancellationToken); if (databasePayload is null || databasePayload.Database is null) { return new UpdateDatabasePayload( diff --git a/backend/src/GraphQl/Edge.cs b/backend/src/GraphQl/Edge.cs index 0950b83a..88bd4a64 100644 --- a/backend/src/GraphQl/Edge.cs +++ b/backend/src/GraphQl/Edge.cs @@ -17,6 +17,6 @@ public async Task GetNodeAsync( CancellationToken cancellationToken ) { - return (await byId.LoadAsync(_nodeId, cancellationToken).ConfigureAwait(false))!; + return (await byId.LoadAsync(_nodeId, cancellationToken))!; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs index ba7cc9d4..b1ce135a 100644 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs @@ -33,7 +33,6 @@ CancellationToken cancellationToken return ( await _getAssociations(dbContext, keys) .ToListAsync(cancellationToken) - .ConfigureAwait(false) ) .ToLookup(_getAssociateId); } diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs index d706c2ec..a79193fa 100644 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs @@ -34,7 +34,6 @@ CancellationToken cancellationToken entity => entity.Id, entity => (TEntity?)entity, cancellationToken - ) - .ConfigureAwait(false); + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index 90c0d8e8..af67bd98 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -41,7 +41,7 @@ CancellationToken cancellationToken where TDataLoader : IDataLoader { return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken).ConfigureAwait(false) ?? [] + await dataLoader.LoadAsync(Subject.Id, cancellationToken) ?? [] ) .Select(_createEdge); } diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index f2328757..bfeb27c1 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -23,7 +23,7 @@ public async Task CreateGeometricDataAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken).ConfigureAwait(false); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { return new CreateGeometricDataPayload( @@ -107,17 +107,17 @@ input.RootResource.AppliedConversionMethod is null geometricData.Resources.Add(resource); context.GeometricData.Add(geometricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(geometricData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreateGeometricDataPayload( geometricData, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 99c3aea0..91aa18ae 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -40,7 +40,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } public async Task GetGeometricDataAsync( @@ -55,13 +55,13 @@ CancellationToken cancellationToken var geometricData = await byId.LoadAsync( id, cancellationToken - ).ConfigureAwait(false); + ); if (geometricData is null) { return geometricData; } - return await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index b0752e2c..059acfcf 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -26,11 +26,11 @@ CancellationToken cancellationToken { IData? data = input.DataKind switch { - DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), - DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), - DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), - DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), - DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken).ConfigureAwait(false), + DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), _ => throw new ArgumentException($"The data kind {input.DataKind} is not supported.") }; if (data is null) @@ -45,7 +45,7 @@ CancellationToken cancellationToken } var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new CreateGetHttpsResourcePayload( @@ -101,7 +101,7 @@ input.AppliedConversionMethod is null ) ); context.GetHttpsResources.Add(getHttpsResource); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreateGetHttpsResourcePayload(getHttpsResource); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index cd14d57e..a39935bc 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -28,7 +28,7 @@ CancellationToken cancellationToken return await calorimetricDataById.LoadAsync( getHttpsResource.CalorimetricDataId ?? throw new ArgumentException("Impossible!"), cancellationToken - ).ConfigureAwait(false); + ); } if (getHttpsResource.HygrothermalDataId is not null) @@ -36,7 +36,7 @@ CancellationToken cancellationToken return await hygrothermalDataById.LoadAsync( getHttpsResource.HygrothermalDataId ?? throw new ArgumentException("Impossible!"), cancellationToken - ).ConfigureAwait(false); + ); } if (getHttpsResource.OpticalDataId is not null) @@ -44,7 +44,7 @@ CancellationToken cancellationToken return await opticalDataById.LoadAsync( getHttpsResource.OpticalDataId ?? throw new ArgumentException("Impossible!"), cancellationToken - ).ConfigureAwait(false); + ); } if (getHttpsResource.PhotovoltaicDataId is not null) @@ -52,7 +52,7 @@ CancellationToken cancellationToken return await photovoltaicDataById.LoadAsync( getHttpsResource.PhotovoltaicDataId ?? throw new ArgumentException("Impossible!"), cancellationToken - ).ConfigureAwait(false); + ); } if (getHttpsResource.GeometricDataId is not null) @@ -60,7 +60,7 @@ CancellationToken cancellationToken return await geometricDataById.LoadAsync( getHttpsResource.GeometricDataId ?? throw new ArgumentException("Impossible!"), cancellationToken - ).ConfigureAwait(false); + ); } return null; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 0e6d283c..55bb85f7 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -25,7 +25,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new CreateHygrothermalDataPayload( @@ -106,17 +106,17 @@ input.RootResource.AppliedConversionMethod is null ); hygrothermalData.Resources.Add(resource); context.HygrothermalData.Add(hygrothermalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - hygrothermalData.Approval = await responseApprovalService.CreateResponseApproval(hygrothermalData, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + hygrothermalData.Approval = await responseApprovalService.CreateResponseApproval(hygrothermalData, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(hygrothermalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreateHygrothermalDataPayload( hygrothermalData, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 932dd136..fd8aec68 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } public async Task GetHygrothermalDataAsync( @@ -64,6 +64,6 @@ CancellationToken cancellationToken return hygrothermalData; } - return await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 1f6e4e8c..808dcad4 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken stream, JsonSerializerSettings.File, cancellationToken - ).ConfigureAwait(false) ?? throw new JsonException("Failed to deserialize the GraphQL response."); + ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); var result = methodCalculationService.UseMethodToCalculate(input.MethodId, fileInput.Data.DataPoints); @@ -120,11 +120,11 @@ CancellationToken cancellationToken ); } - var data = await DataApi.GetDataFromDatabase(database.Locator, input.DataId, DataApi.DataXFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); + var data = await DataApi.GetDataFromDatabase(database.Locator, input.DataId, DataApi.DataXFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); var locator = data.Data.DataX.ResourceTree.Root.Value.Locator; - var filedata = await apiRequestService.QueryRest(new Uri(locator), httpClientFactory, httpContextAccessor, cancellationToken).ConfigureAwait(false); + var filedata = await apiRequestService.QueryRest(new Uri(locator), httpClientFactory, httpContextAccessor, cancellationToken); var result = methodCalculationService.UseMethodToCalculate(input.MethodId, filedata.Data.DataPoints.ToList()); diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index a72cd504..981de4f9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -119,17 +119,17 @@ input.RootResource.AppliedConversionMethod is null ); opticalData.Resources.Add(resource); context.OpticalData.Add(opticalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - opticalData.Approval = await responseApprovalService.CreateResponseApproval(opticalData, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + opticalData.Approval = await responseApprovalService.CreateResponseApproval(opticalData, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(opticalData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreateOpticalDataPayload( opticalData, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 11686b14..e4104bff 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -64,6 +64,6 @@ CancellationToken cancellationToken { return opticalData; } - return await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 755cb849..eab13001 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -27,7 +27,7 @@ CancellationToken cancellationToken ) { var currentUser = await userService.GetCurrentUser( - cancellationToken).ConfigureAwait(false); + cancellationToken); if (currentUser is null) { return new CreatePhotovoltaicDataPayload( @@ -109,17 +109,17 @@ input.RootResource.AppliedConversionMethod is null ); photovoltaicData.Resources.Add(resource); context.PhotovoltaicData.Add(photovoltaicData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); try { - photovoltaicData.Approval = await responseApprovalService.CreateResponseApproval(photovoltaicData, cancellationToken).ConfigureAwait(false); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + photovoltaicData.Approval = await responseApprovalService.CreateResponseApproval(photovoltaicData, cancellationToken); + await context.SaveChangesAsync(cancellationToken); } catch (Exception exception) { context.Remove(photovoltaicData); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return new CreatePhotovoltaicDataPayload( photovoltaicData, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index d793b149..fd22ba9b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken } // Apply acces rights on data - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } public async Task GetPhotovoltaicDataAsync( @@ -64,6 +64,6 @@ CancellationToken cancellationToken return photovoltaicData; } - return await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken).ConfigureAwait(false); + return await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 01802a9d..b209b1ed 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -36,7 +36,7 @@ await context.Users.AsNoTracking() .SingleOrDefaultAsync( u => claimsPrincipal.GetClaims(ClaimTypes.NameIdentifier).Contains(u.Subject), cancellationToken - ).ConfigureAwait(false); + ); } public async Task GetCurrentUserInfoAsync( diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 00bdd598..6cb6b467 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -57,11 +57,11 @@ string[] commandLineArguments using (var scope = application.Services.CreateScope()) { // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDb(scope.ServiceProvider).ConfigureAwait(false); - await InitializeSigningService(scope.ServiceProvider).ConfigureAwait(false); + await CreateAndSeedDb(scope.ServiceProvider); + await InitializeSigningService(scope.ServiceProvider); } - await application.RunAsync().ConfigureAwait(false); + await application.RunAsync(); return 0; } catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 @@ -118,7 +118,7 @@ IServiceProvider services services.GetRequiredService>() .CreateDbContext(); dbContext.Database.EnsureCreated(); - await DbSeeder.DoAsync(services).ConfigureAwait(false); + await DbSeeder.DoAsync(services); } catch (Exception exception) { diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 34ed25b6..7b8ef746 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -51,7 +51,7 @@ CancellationToken cancellationToken var result = ProcessData(data, currentUser, openIdConnectClientId, representedInstitutionIds, institutionAccessRights, alreadyAccessedCount, cacheService); // Save InstitutionAccessRight changes - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + await context.SaveChangesAsync(cancellationToken); return result.AsQueryable(); } diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index c9764121..ed9926f5 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -67,7 +67,7 @@ await Task.WhenAll( fileNames.Select(fileName => File.ReadAllTextAsync($"./ApiRequests/Queries/{fileName}") ) - ).ConfigureAwait(false) + ) ); } @@ -180,7 +180,7 @@ CancellationToken cancellationToken s_useMetabase ? httpClientFactory.CreateClient(MetabaseHttpClient) : httpClientFactory.CreateClient(DatabaseHttpClient); - var bearerToken = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); + var bearerToken = await httpContextAccessor.ExtractBearerToken(); using var httpRequestMessage = new HttpRequestMessage( httpMethod, uri @@ -191,7 +191,7 @@ CancellationToken cancellationToken httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); } using var httpResponseMessage = - await httpClient.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false); + await httpClient.SendAsync(httpRequestMessage, cancellationToken); if (httpResponseMessage.StatusCode != HttpStatusCode.OK) { throw new HttpRequestException( @@ -203,8 +203,7 @@ CancellationToken cancellationToken // `httpResponseMessage.Content.ReadFromJsonAsync>` which // would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ using var responseStream = await httpResponseMessage.Content - .ReadAsStreamAsync(cancellationToken) - .ConfigureAwait(false); + .ReadAsStreamAsync(cancellationToken); // For debugging, the following lines of code write the response to standard output. // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); @@ -212,7 +211,7 @@ CancellationToken cancellationToken responseStream, serializerOptions, cancellationToken - ).ConfigureAwait(false) ?? throw new JsonException("Failed to deserialize the GraphQL response."); + ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); return deserializedResponse; } diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index 0823e1bf..b2b17b46 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -22,10 +22,10 @@ public sealed class DataService /// Dataset with passed id or null. public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { - return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? - await context.HygrothermalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? - await context.OpticalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? - await context.GeometricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) ?? - await context.PhotovoltaicData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken).ConfigureAwait(false) as IData; + return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.HygrothermalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.OpticalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.GeometricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.PhotovoltaicData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; } } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 06cb5dea..defe5d91 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -38,9 +38,9 @@ public async Task CreateResponseApproval(IData dataObject, Can { // Get dataset logger.QueryAllMetaData(dataObject.GetType().Name, dataObject.Id); - var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken).ConfigureAwait(false); + var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); logger.QueryAndVariablesAndResponce(query, variables, response); - var signature = await signingService.SignData(response).ConfigureAwait(false); + var signature = await signingService.SignData(response); return new ResponseApproval(DateTime.UtcNow, signature, await signingService.ExtractFingerprint(), query, variables, response); } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 085325d2..1ae64336 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -45,19 +45,19 @@ public sealed class UserService( public async Task GetCurrentUser(CancellationToken cancellationToken) { // Extract token - var token = await httpContextAccessor.ExtractBearerToken().ConfigureAwait(false); + var token = await httpContextAccessor.ExtractBearerToken(); logger.ExtractedToken(token); if (token is null) { - return await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); + return await GetCurrentUserFromMetabase(cancellationToken); } // Check if there is already a user for token if (!cacheService.TryGetUser(token, out var cacheUser)) { // Get user from Metabase - cacheUser = await GetCurrentUserFromMetabase(cancellationToken).ConfigureAwait(false); + cacheUser = await GetCurrentUserFromMetabase(cancellationToken); // Store user in cache cacheService.SetUser(token, cacheUser); } From b42d733d681dc4c67bf829e261a2fd94cf3652dc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 12:41:03 +0200 Subject: [PATCH 389/878] Add `has*Data` queries --- .../CalorimetricDataQueries.cs | 30 +++++++++---- .../GeometricDataX/GeometricDataQueries.cs | 33 +++++++++----- .../HygrothermalDataQueries.cs | 31 ++++++++----- .../OpticalDataX/OpticalDataQueries.cs | 29 ++++++++---- .../PhotovoltaicDataQueries.cs | 31 ++++++++----- frontend/type-defs.graphqls | 44 +++++++++++++++++-- 6 files changed, 145 insertions(+), 53 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 3ca4c1c1..aec06949 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -31,20 +31,30 @@ public async Task> GetAllCalorimetricData( CancellationToken cancellationToken ) { - // TODO Use `locale`. sorting.StabilizeOrder(); - var filteredData = context.CalorimetricData.Sort(resolverContext).Filter(resolverContext); - - // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) + var filteredData = context.CalorimetricData + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) { return filteredData; } - - // Apply acces rights on data return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } + [UseFiltering(typeof(CalorimetricData))] + public Task GetHasCalorimetricData( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return context.CalorimetricData + .Filter(resolverContext) + .AnyAsync(cancellationToken); + } + public async Task GetCalorimetricDataAsync( Guid id, [GraphQLType] string? locale, @@ -53,16 +63,18 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - // TODO Use `locale`. var calorimetricData = await byId.LoadAsync( id, cancellationToken ); if (calorimetricData is null) + { + return null; + } + if (!calorimetricData.DataAccessRights.HasRestrictions) { return calorimetricData; } - return await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 91aa18ae..9213879a 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -18,7 +18,7 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataQueries { [UsePaging] - [UseFiltering(typeof(GeometricDataFilterType))] + [UseFiltering] [UseSorting] public async Task> GetAllGeometricData( [GraphQLType] string? locale, @@ -29,20 +29,30 @@ public async Task> GetAllGeometricData( CancellationToken cancellationToken ) { - // TODO Use `locale`. sorting.StabilizeOrder(); - var filteredData = context.GeometricData.Sort(resolverContext).Filter(resolverContext); - - // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) + var filteredData = context.GeometricData + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) { return filteredData; } - - // Apply acces rights on data return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } + [UseFiltering(typeof(GeometricData))] + public Task GetHasGeometricData( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return context.GeometricData + .Filter(resolverContext) + .AnyAsync(cancellationToken); + } + public async Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, @@ -51,17 +61,18 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - // TODO Use `locale`. var geometricData = await byId.LoadAsync( id, cancellationToken ); - if (geometricData is null) + { + return null; + } + if (!geometricData.DataAccessRights.HasRestrictions) { return geometricData; } - return await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index fd8aec68..5e0209d2 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -31,20 +31,30 @@ public async Task> GetAllHygrothermalData( CancellationToken cancellationToken ) { - // TODO Use `locale`. sorting.StabilizeOrder(); - var filteredData = context.HygrothermalData.Sort(resolverContext).Filter(resolverContext); - - // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) + var filteredData = context.HygrothermalData + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) { return filteredData; } - - // Apply acces rights on data return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } + [UseFiltering(typeof(HygrothermalData))] + public Task GetHasHygrothermalData( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return context.HygrothermalData + .Filter(resolverContext) + .AnyAsync(cancellationToken); + } + public async Task GetHygrothermalDataAsync( Guid id, [GraphQLType] string? locale, @@ -53,17 +63,18 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - // TODO Use `locale`. var hygrothermalData = await byId.LoadAsync( id, cancellationToken ); - if (hygrothermalData is null) + { + return null; + } + if (!hygrothermalData.DataAccessRights.HasRestrictions) { return hygrothermalData; } - return await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index e4104bff..760eac20 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -22,7 +22,7 @@ public sealed class OpticalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllOpticalData( + public async Task> GetAllOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -31,16 +31,28 @@ public Task> GetAllOpticalData( CancellationToken cancellationToken ) { - // TODO Use `locale`. sorting.StabilizeOrder(); - var filteredData = context.OpticalData.Sort(resolverContext).Filter(resolverContext); - // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) + var filteredData = context.OpticalData + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) { - return Task.FromResult(filteredData); + return filteredData; } - // Apply acces rights on data - return accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + } + + [UseFiltering(typeof(OpticalData))] + public Task GetHasOpticalData( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return context.OpticalData + .Filter(resolverContext) + .AnyAsync(cancellationToken); } public async Task GetOpticalDataAsync( @@ -51,7 +63,6 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - // TODO Use `locale`. var opticalData = await byId.LoadAsync( id, cancellationToken diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index fd22ba9b..e1b598db 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -31,20 +31,30 @@ public async Task> GetAllPhotovoltaicData( CancellationToken cancellationToken ) { - // TODO Use `locale`. sorting.StabilizeOrder(); - var filteredData = context.PhotovoltaicData.Sort(resolverContext).Filter(resolverContext); - - // Check if there is restricted data - if (!filteredData.Any(x => x.DataAccessRights.HasRestrictions)) + var filteredData = context.PhotovoltaicData + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) { return filteredData; } - - // Apply acces rights on data return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } + [UseFiltering(typeof(PhotovoltaicData))] + public Task GetHasPhotovoltaicData( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return context.PhotovoltaicData + .Filter(resolverContext) + .AnyAsync(cancellationToken); + } + public async Task GetPhotovoltaicDataAsync( Guid id, [GraphQLType] string? locale, @@ -53,17 +63,18 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - // TODO Use `locale`. var photovoltaicData = await byId.LoadAsync( id, cancellationToken ); - if (photovoltaicData is null) + { + return null; + } + if (!photovoltaicData.DataAccessRights.HasRestrictions) { return photovoltaicData; } - return await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken); } } \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index d3838b79..ee6ad97c 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -218,9 +218,9 @@ type DataAccessRights { allowedInstitutions: [Uuid!] allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] hasRestrictions: Boolean! - isRestrictedByApplication: Boolean! - isRestrictedByInstitution: Boolean! - isRestrictedByUser: Boolean! + hasRestrictionsByApplication: Boolean! + hasRestrictionsByInstitution: Boolean! + hasRestrictionsByUser: Boolean! } type DataApproval { @@ -231,6 +231,7 @@ type DataApproval { signature: String! statement: Reference @cost(weight: "10") timestamp: DateTime! + variables: Any! } type DataConnection { @@ -414,6 +415,9 @@ type Incidence { type InstitutionAccessRights { allowedDatasetsPerTime: NonNegativeInt allowedUserCount: NonNegativeInt + hasRestrictions: Boolean! + hasRestrictionsByTime: Boolean! + hasRestrictionsByUser: Boolean! id: Uuid! institutionId: Uuid! period: TimeSpan! @@ -469,7 +473,7 @@ type Mutation { type NamedMethodArgument { name: String! - value: Any! @cost(weight: "10") + value: Any! } type NamedMethodSource { @@ -622,6 +626,11 @@ type Query { geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") methodAsService(input: MethodAsServiceInput!): MethodAsServicePayload! @cost(weight: "10") methodAsServiceWithFile(input: MethodAsServiceWithFileInput!): MethodAsServicePayload! @cost(weight: "10") @@ -640,6 +649,7 @@ type ResponseApproval { response: String! signature: String! timestamp: DateTime! + variables: Any! } type Results { @@ -923,6 +933,7 @@ input DataApprovalInput { signature: String! statement: ReferenceInput! timestamp: DateTime! + variables: Any! } input DataApprovalPropositionInput { @@ -933,6 +944,7 @@ input DataApprovalPropositionInput { signature: StringPropositionInput keyFingerprint: StringPropositionInput query: StringPropositionInput + variables: JsonElementPropositionInput response: StringPropositionInput statement: ReferencePropositionInput } @@ -1118,6 +1130,19 @@ input Int32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } +input JsonElementPropositionInput { + and: [JsonElementPropositionInput!] + or: [JsonElementPropositionInput!] + valueKind: JsonValueKindPropositionInput +} + +input JsonValueKindPropositionInput { + equalTo: JsonValueKind @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") + in: [JsonValueKind!] @cost(weight: "10") + notIn: [JsonValueKind!] @cost(weight: "10") +} + input KeyValuePairOfGuidAndNullableOfUInt32Input { key: Uuid! value: NonNegativeInt @@ -1591,6 +1616,17 @@ enum InstitutionAccessRightsErrorCode { UNKNOWN_ACCESS_RIGHTS } +enum JsonValueKind { + UNDEFINED + OBJECT + ARRAY + STRING + NUMBER + TRUE + FALSE + NULL +} + enum MethodAsServiceErrorCode { UNKNOWN_METHOD UNKNOWN_DATABASE From 4685f1aa1179a24f35bc6ed080a1e867e9d67ec5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 13 Aug 2025 12:58:16 +0200 Subject: [PATCH 390/878] Remove to-do as there is a proper issue for it https://github.com/building-envelope-data/database/issues/18 --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 7a7c7380..fce6ac4d 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -351,7 +351,7 @@ public static IFilterConventionDescriptor AddDefaultOperations( descriptor.Operation(DefaultFilterOperations.Or).Name("or"); descriptor.Operation(DefaultFilterOperations.Data).Name("data"); descriptor.Operation(AdditionalFilterOperations.Not).Name("not"); - // TODO `inClosedInterval` + // `inClosedInterval` return descriptor; } From 7bc0fe8bd20127ab39c4173ce980e3e3526af203 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 15 Aug 2025 16:34:59 +0200 Subject: [PATCH 391/878] Allow mutations exactly to those users who represent the institution that manages this database or a managing institution of that institution --- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 21 +++--- .../ApiRequests/Queries/CurrentUser.graphql | 65 +++++++++++++++++-- backend/src/ApiRequests/UserApi.cs | 16 ++--- .../CalorimetricDataAuthorization.cs | 15 ----- .../src/Authorization/CommonAuthorization.cs | 17 ++--- .../Authorization/FileUploadAuthorization.cs | 16 ----- .../GeometricDataAuthorization.cs | 16 ----- .../GetHttpsResourceAuthorization.cs | 16 ----- .../HygrothermalDataAuthorization.cs | 16 ----- .../Authorization/OpticalDataAuthorization.cs | 16 ----- .../PhotovoltaicDataAuthorization.cs | 16 ----- .../ResponseApprovalAuthorization.cs | 15 ----- .../src/Controllers/FileUploadController.cs | 6 +- .../AccessRights/DataAccessRightsMutations.cs | 37 ++++------- .../CalorimetricDataMutations.cs | 8 +-- .../DataApprovals/AddDataApprovalErrorCode.cs | 1 - .../DataApprovals/DataApprovalMutations.cs | 22 +------ backend/src/GraphQl/DataX/DataQueries.cs | 5 +- .../GeometricDataX/GeometricDataMutations.cs | 8 +-- .../GetHttpsResourceMutations.cs | 8 +-- .../HygrothermalDataMutations.cs | 8 +-- .../OpticalDataX/OpticalDataMutations.cs | 8 +-- .../PhotovoltaicDataMutations.cs | 8 +-- .../ResponseApprovalMutations.cs | 41 +++++++----- backend/src/Services/AccessRightsService.cs | 4 +- backend/src/Services/ApiRequestService.cs | 1 - backend/src/Services/DataService.cs | 10 +-- backend/src/Services/UserService.cs | 6 +- 28 files changed, 146 insertions(+), 280 deletions(-) delete mode 100644 backend/src/Authorization/CalorimetricDataAuthorization.cs delete mode 100644 backend/src/Authorization/FileUploadAuthorization.cs delete mode 100644 backend/src/Authorization/GeometricDataAuthorization.cs delete mode 100644 backend/src/Authorization/GetHttpsResourceAuthorization.cs delete mode 100644 backend/src/Authorization/HygrothermalDataAuthorization.cs delete mode 100644 backend/src/Authorization/OpticalDataAuthorization.cs delete mode 100644 backend/src/Authorization/PhotovoltaicDataAuthorization.cs delete mode 100644 backend/src/Authorization/ResponseApprovalAuthorization.cs diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs index cfb9ffc2..5c2056e5 100644 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ b/backend/src/ApiRequests/Dto/CurrentUserDto.cs @@ -9,27 +9,21 @@ public enum InstitutionRepresentativeRole ASSISTANT } -[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711")] -public enum DataSigningPermission -{ - NEVER, - GRANTED, - REMOVED -} - public sealed record CurrentUserDto( Guid Uuid, - UserRepresentedInstitutionConnection RepresentedInstitutions + string Name, + UserRepresentedInstitutionConnection RepresentedInstitutions, + UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions ); public sealed record UserRepresentedInstitutionConnection( - IReadOnlyList Edges + IReadOnlyList Edges, + uint TotalCount ); public sealed record UserRepresentedInstitutionEdge( UserRepresentedInstitutionNode Node, - InstitutionRepresentativeRole Role, - DataSigningPermission DataSigningPermission + InstitutionRepresentativeRole Role ); public sealed record UserRepresentedInstitutionNode( @@ -39,7 +33,8 @@ InstitutionManagedInstitutionConnection ManagedInstitutions ); public sealed record InstitutionManagedInstitutionConnection( - IReadOnlyList Edges + IReadOnlyList Edges, + uint TotalCount ); public sealed record InstitutionManagedInstitutionEdge( diff --git a/backend/src/ApiRequests/Queries/CurrentUser.graphql b/backend/src/ApiRequests/Queries/CurrentUser.graphql index a84f4f35..630ff761 100644 --- a/backend/src/ApiRequests/Queries/CurrentUser.graphql +++ b/backend/src/ApiRequests/Queries/CurrentUser.graphql @@ -1,6 +1,9 @@ -query CurrentUser { +query CurrentUser( + $databaseId: Uuid! +) { currentUser { uuid + name representedInstitutions { edges { node { @@ -8,16 +11,66 @@ query CurrentUser { name managedInstitutions { edges { - node { - uuid - name - } + node { + uuid + name + } } + totalCount + } + } + role + } + totalCount + } + databaseOperatingRepresentedInstitutions: representedInstitutions( + where: { + or: [ + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + operatedDatabases: { + some: { + uuid: { equalTo: $databaseId } + } + } + } + } + ] } + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + managedInstitutions: { + some: { + operatedDatabases: { + some: { + uuid: { + equalTo: $databaseId + } + } + } + } + } + } + } + ] + } + ] + } + ) { + edges { + node { + uuid + name } role - dataSigningPermission } + totalCount } } } \ No newline at end of file diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index e26d6f91..c44c3bd8 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -14,7 +14,7 @@ namespace Database.ApiRequests; /// public sealed class UserApi { - private static readonly string[] _currentUserFileNames = + private static readonly string[] s_currentUserFileNames = [ "CurrentUser.graphql" ]; @@ -39,8 +39,10 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); { return (await apiRequestService.Metabase().QueryGraphQl( appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_currentUserFileNames), - new { }, + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_currentUserFileNames), + new { + databaseId = appSettings.DatabaseId + }, "CurrentUser" ), httpClientFactory, @@ -61,17 +63,15 @@ private sealed record CurrentUserData(CurrentUserDto CurrentUser); /// /// /// if successful. - public static async Task RequestUserInfo( + public static Task RequestUserInfo( AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - var uri = new Uri(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"); - - return await apiRequestService.Metabase().QueryRest( - uri, + return apiRequestService.Metabase().QueryRest( + new Uri(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"), httpClientFactory, httpContextAccessor, cancellationToken diff --git a/backend/src/Authorization/CalorimetricDataAuthorization.cs b/backend/src/Authorization/CalorimetricDataAuthorization.cs deleted file mode 100644 index 73bb6070..00000000 --- a/backend/src/Authorization/CalorimetricDataAuthorization.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class CalorimetricDataAuthorization -{ - public static bool IsAuthorizedToCreateCalorimetricDataForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 9e1b771b..17f70814 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -6,6 +6,13 @@ namespace Database.Authorization; public static class CommonAuthorization { + public static bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( + CurrentUserDto currentUser + ) + { + return currentUser.DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; + } + public static bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( CurrentUserDto currentUser, Guid institutionId @@ -25,14 +32,4 @@ t.Role is InstitutionRepresentativeRole.ASSISTANT ) ); } - - public static bool IsAuthorizedToAddDataApprovalForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return currentUser.RepresentedInstitutions.Edges.Any( - t => t.Node.Uuid == institutionId - ); - } } \ No newline at end of file diff --git a/backend/src/Authorization/FileUploadAuthorization.cs b/backend/src/Authorization/FileUploadAuthorization.cs deleted file mode 100644 index 57f750ec..00000000 --- a/backend/src/Authorization/FileUploadAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class FileUploadDataAuthorization -{ - public static bool IsAuthorizedToUploadFilesForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/GeometricDataAuthorization.cs b/backend/src/Authorization/GeometricDataAuthorization.cs deleted file mode 100644 index b5930024..00000000 --- a/backend/src/Authorization/GeometricDataAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class GeometricDataAuthorization -{ - public static bool IsAuthorizedToCreateGeometricDataForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/GetHttpsResourceAuthorization.cs b/backend/src/Authorization/GetHttpsResourceAuthorization.cs deleted file mode 100644 index 6f1366a2..00000000 --- a/backend/src/Authorization/GetHttpsResourceAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class GetHttpsResourceAuthorization -{ - public static bool IsAuthorizedToCreateGetHttpsResourceForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/HygrothermalDataAuthorization.cs b/backend/src/Authorization/HygrothermalDataAuthorization.cs deleted file mode 100644 index dc972364..00000000 --- a/backend/src/Authorization/HygrothermalDataAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class HygrothermalDataAuthorization -{ - public static bool IsAuthorizedToCreateHygrothermalDataForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/OpticalDataAuthorization.cs b/backend/src/Authorization/OpticalDataAuthorization.cs deleted file mode 100644 index e86ed158..00000000 --- a/backend/src/Authorization/OpticalDataAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class OpticalDataAuthorization -{ - public static bool IsAuthorizedToCreateOpticalDataForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs b/backend/src/Authorization/PhotovoltaicDataAuthorization.cs deleted file mode 100644 index c7982897..00000000 --- a/backend/src/Authorization/PhotovoltaicDataAuthorization.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Threading; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class PhotovoltaicDataAuthorization -{ - public static bool IsAuthorizedToCreatePhotovoltaicDataForInstitution( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/ResponseApprovalAuthorization.cs b/backend/src/Authorization/ResponseApprovalAuthorization.cs deleted file mode 100644 index 43bcda13..00000000 --- a/backend/src/Authorization/ResponseApprovalAuthorization.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using Database.ApiRequests.Dto; - -namespace Database.Authorization; - -public static class ResponseApprovalAuthorization -{ - public static bool IsAuthorizedToManageResponseApprovals( - CurrentUserDto currentUser, - Guid institutionId - ) - { - return CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution(currentUser, institutionId); - } -} \ No newline at end of file diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 1cfaf07c..7b367f84 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -135,11 +135,7 @@ CancellationToken cancellationToken $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); return BadRequest(ModelState); } - if (!FileUploadDataAuthorization.IsAuthorizedToUploadFilesForInstitution( - currentUser, - getHttpsResource.Data.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return Unauthorized(); } diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 15f30ad0..44681426 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -20,8 +20,7 @@ public async Task UpdateDataAccessRightsAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { return new UpdateDataAccessRightsPayload( @@ -33,28 +32,24 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); - if (data is null) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateDataAccessRightsPayload( new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, - $"Unknown data.", + UpdateDataAccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update access rights for the data.", [] ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - currentUser, - data.CreatorId - ) - ) + var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); + if (data is null) { return new UpdateDataAccessRightsPayload( new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update access rights for the data.", + UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, + $"Unknown data.", [] ) ); @@ -88,11 +83,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - currentUser, - input.InstitutionId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new AddInstitutionAccessRightsPayload( new InstitutionAccessRightsError( @@ -103,7 +94,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); + var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); if (accessRights is not null) { @@ -146,11 +137,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - currentUser, - input.InstitutionId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateAccessRightsPayload( new InstitutionAccessRightsError( @@ -161,7 +148,7 @@ CancellationToken cancellationToken ); } - var accessRights = await context.InstitutionAccessRights.FirstOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); + var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); if (accessRights is null) { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 5228ca94..ff4fe595 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -35,16 +35,12 @@ CancellationToken cancellationToken ); } - if (!CalorimetricDataAuthorization.IsAuthorizedToCreateCalorimetricDataForInstitution( - currentUser, - input.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( CreateCalorimetricDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create calorimetric data for the institution.", + $"The current user is not authorized to create calorimetric data in this database.", [] ) ); diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index 4a5868b5..9af698dd 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -9,7 +9,6 @@ public enum AddDataApprovalErrorCode UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, - UNKNOWN_DATABASE, CREATING_RESPONSE_APPROVAL_FAILED, AMBIGUOUS_STATEMENT, MISSING_STATEMENT diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 1fbc3e58..17e9a649 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -19,7 +19,6 @@ public async Task AddDataApprovalAsync( ApplicationDbContext context, UserService userService, DataService dataService, - DatabaseService databaseService, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -35,28 +34,13 @@ CancellationToken cancellationToken ) ); } - var database = await databaseService.QueryDatabase(cancellationToken); - if (database is null) - { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNKNOWN_DATABASE, - $"The database could not be identified.", - [] - ) - ); - } - if (!CommonAuthorization.IsAuthorizedToAddDataApprovalForInstitution( - currentUser, - database.Operator.Node.Uuid - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new AddDataApprovalPayload( new AddDataApprovalError( AddDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add data approvals for the database.", - [nameof(input), nameof(input.ApproverId).FirstCharToLower()] + $"The current user is not authorized to add data approvals to this database.", + [] ) ); } diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 6b7a3e1e..a7d10c5b 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -20,14 +20,11 @@ public sealed class DataQueries CancellationToken cancellationToken ) { - // TODO Use `locale`. var data = await dataService.GetDataAsync(id, dbContext, cancellationToken); - if (data is null) { - return data; + return null; } - return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index bfeb27c1..caf0939a 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -35,16 +35,12 @@ CancellationToken cancellationToken ); } - if (!GeometricDataAuthorization.IsAuthorizedToCreateGeometricDataForInstitution( - currentUser, - input.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateGeometricDataPayload( new CreateGeometricDataError( CreateGeometricDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create geometric data for the institution.", + $"The current user is not authorized to create geometric data in this database.", [] ) ); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 059acfcf..b4c8fca0 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -56,16 +56,12 @@ CancellationToken cancellationToken ) ); } - if (!GetHttpsResourceAuthorization.IsAuthorizedToCreateGetHttpsResourceForInstitution( - currentUser, - data.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( CreateGetHttpsResourceErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create GET HTTPS resource for the institution.", + $"The current user is not authorized to create GET HTTPS resource in this database.", [] ) ); diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index 55bb85f7..a15d6bd7 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -36,16 +36,12 @@ CancellationToken cancellationToken ) ); } - if (!HygrothermalDataAuthorization.IsAuthorizedToCreateHygrothermalDataForInstitution( - currentUser, - input.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( CreateHygrothermalDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create hygrothermal data for the institution.", + $"The current user is not authorized to create hygrothermal data in this database.", [] ) ); diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 981de4f9..20e380d6 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -33,16 +33,12 @@ CancellationToken cancellationToken ) ); } - if (!OpticalDataAuthorization.IsAuthorizedToCreateOpticalDataForInstitution( - currentUser, - input.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateOpticalDataPayload( new CreateOpticalDataError( CreateOpticalDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create optical data for the institution.", + $"The current user is not authorized to create optical data in this database.", [] ) ); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index eab13001..2adfa310 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -39,16 +39,12 @@ CancellationToken cancellationToken ); } - if (!PhotovoltaicDataAuthorization.IsAuthorizedToCreatePhotovoltaicDataForInstitution( - currentUser, - input.CreatorId - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create photovoltaic data for the institution.", + $"The current user is not authorized to create photovoltaic data in this database.", [] ) ); diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs index f32269ca..4f5cbfe1 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,9 +7,25 @@ using Database.Data; using Database.Services; using HotChocolate.Types; +using Microsoft.Extensions.Logging; namespace Database.GraphQl.ResponseApprovals; +public static partial class ResponseApprovalMutationsLogging +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Creating response approval for data of type {Type} with ID {Id} failed." + )] + public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); + + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Updating response approval for data of type {Type} with ID {Id} failed." + )] + public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); +} + [ExtendObjectType(nameof(Mutation))] public sealed class ResponseApprovalMutations { @@ -20,6 +35,7 @@ public async Task CreateResponseApprovalsAsync( UserService userService, DatabaseService databaseService, ResponseApprovalService responseApprovalService, + ILogger logger, CancellationToken cancellationToken ) { @@ -45,16 +61,12 @@ CancellationToken cancellationToken ) ); } - if (!ResponseApprovalAuthorization.IsAuthorizedToManageResponseApprovals( - currentUser, - database.Operator.Node.Uuid - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateResponseApprovalsPayload( new CreateResponseApprovalsError( CreateResponseApprovalsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create response approvals for the database.", + $"The current user is not authorized to create response approvals in this database.", [] ) ); @@ -77,10 +89,11 @@ await Parallel.ForEachAsync( } catch (Exception exception) { + logger.FailedCreatingResponseApproval(data.GetType(), data.Id, exception); errors.Add( new CreateResponseApprovalsError( CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Creating response approval for data {data} failed with message: {exception.Message}", + $"Creating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", [] ) ); @@ -100,6 +113,7 @@ public async Task UpdateResponseApprovalsAsync( UserService userService, DatabaseService databaseService, ResponseApprovalService responseApprovalService, + ILogger logger, CancellationToken cancellationToken ) { @@ -125,16 +139,12 @@ CancellationToken cancellationToken ) ); } - if (!ResponseApprovalAuthorization.IsAuthorizedToManageResponseApprovals( - currentUser, - database.Operator.Node.Uuid - ) - ) + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateResponseApprovalsPayload( new UpdateResponseApprovalsError( UpdateResponseApprovalsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create response approvals for the database.", + $"The current user is not authorized to create response approvals in this database.", [] ) ); @@ -157,10 +167,11 @@ await Parallel.ForEachAsync( } catch (Exception exception) { + logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, exception); errors.Add( new UpdateResponseApprovalsError( UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, - $"Updating response approval for data {data} failed with message: {exception.Message}", + $"Updating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", [] ) ); diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 7b8ef746..2f73cf30 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -20,9 +20,9 @@ public sealed class AccessRightsService( public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { - return ( + return await ( await ApplyAccessRightsOnData(new List { data }.AsQueryable(), cancellationToken) - ).FirstOrDefault(); + ).SingleOrDefaultAsync(cancellationToken); } public async Task> ApplyAccessRightsOnData( diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index ed9926f5..91c7bf43 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -9,7 +9,6 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Extensions; -using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs index b2b17b46..88775f30 100644 --- a/backend/src/Services/DataService.cs +++ b/backend/src/Services/DataService.cs @@ -22,10 +22,10 @@ public sealed class DataService /// Dataset with passed id or null. public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) { - return await context.CalorimetricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.HygrothermalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.OpticalData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.GeometricData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.PhotovoltaicData.FirstOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; + return await context.CalorimetricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.HygrothermalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.OpticalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.GeometricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await context.PhotovoltaicData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 1ae64336..1c6f9bef 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -47,12 +47,10 @@ public sealed class UserService( // Extract token var token = await httpContextAccessor.ExtractBearerToken(); logger.ExtractedToken(token); - if (token is null) { return await GetCurrentUserFromMetabase(cancellationToken); } - // Check if there is already a user for token if (!cacheService.TryGetUser(token, out var cacheUser)) { @@ -61,7 +59,6 @@ public sealed class UserService( // Store user in cache cacheService.SetUser(token, cacheUser); } - return cacheUser; } @@ -72,6 +69,7 @@ public sealed class UserService( apiRequestService, httpClientFactory, httpContextAccessor, - cancellationToken); + cancellationToken + ); } } \ No newline at end of file From 7da6771455e742cb7cec9e5b2bd85747cba12bff Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 Aug 2025 12:05:44 +0200 Subject: [PATCH 392/878] Do not track entities in queries and do not inject database contexts directly (but only a factory to create them) --- .../CalorimetricDataX/CalorimetricDataQueries.cs | 5 +++-- .../GraphQl/GeometricDataX/GeometricDataQueries.cs | 5 +++-- .../HygrothermalDataX/HygrothermalDataQueries.cs | 5 +++-- .../src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 5 +++-- .../PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 5 +++-- backend/src/Services/AccessRightsService.cs | 11 ++++++++--- backend/src/Services/CacheService.cs | 1 - 7 files changed, 23 insertions(+), 14 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index aec06949..8d181bb3 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -32,7 +32,8 @@ CancellationToken cancellationToken ) { sorting.StabilizeOrder(); - var filteredData = context.CalorimetricData + var filteredData = + context.CalorimetricData.AsNoTracking() .Sort(resolverContext) .Filter(resolverContext); if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) @@ -50,7 +51,7 @@ public Task GetHasCalorimetricData( CancellationToken cancellationToken ) { - return context.CalorimetricData + return context.CalorimetricData.AsNoTracking() .Filter(resolverContext) .AnyAsync(cancellationToken); } diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 9213879a..eb846077 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -30,7 +30,8 @@ CancellationToken cancellationToken ) { sorting.StabilizeOrder(); - var filteredData = context.GeometricData + var filteredData = + context.GeometricData.AsNoTracking() .Sort(resolverContext) .Filter(resolverContext); if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) @@ -48,7 +49,7 @@ public Task GetHasGeometricData( CancellationToken cancellationToken ) { - return context.GeometricData + return context.GeometricData.AsNoTracking() .Filter(resolverContext) .AnyAsync(cancellationToken); } diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 5e0209d2..34c2da7a 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -32,7 +32,8 @@ CancellationToken cancellationToken ) { sorting.StabilizeOrder(); - var filteredData = context.HygrothermalData + var filteredData = + context.HygrothermalData.AsNoTracking() .Sort(resolverContext) .Filter(resolverContext); if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) @@ -50,7 +51,7 @@ public Task GetHasHygrothermalData( CancellationToken cancellationToken ) { - return context.HygrothermalData + return context.HygrothermalData.AsNoTracking() .Filter(resolverContext) .AnyAsync(cancellationToken); } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 760eac20..c5b71ca6 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -32,7 +32,8 @@ CancellationToken cancellationToken ) { sorting.StabilizeOrder(); - var filteredData = context.OpticalData + var filteredData = + context.OpticalData.AsNoTracking() .Sort(resolverContext) .Filter(resolverContext); if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) @@ -50,7 +51,7 @@ public Task GetHasOpticalData( CancellationToken cancellationToken ) { - return context.OpticalData + return context.OpticalData.AsNoTracking() .Filter(resolverContext) .AnyAsync(cancellationToken); } diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index e1b598db..4d72444c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -32,7 +32,8 @@ CancellationToken cancellationToken ) { sorting.StabilizeOrder(); - var filteredData = context.PhotovoltaicData + var filteredData = + context.PhotovoltaicData.AsNoTracking() .Sort(resolverContext) .Filter(resolverContext); if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) @@ -50,7 +51,7 @@ public Task GetHasPhotovoltaicData( CancellationToken cancellationToken ) { - return context.PhotovoltaicData + return context.PhotovoltaicData.AsNoTracking() .Filter(resolverContext) .AnyAsync(cancellationToken); } diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 2f73cf30..6cded6bd 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -12,7 +12,7 @@ namespace Database.Services; public sealed class AccessRightsService( - ApplicationDbContext context, + IDbContextFactory dbContextFactory, UserService userService, CacheService cacheService, ILogger logger) @@ -31,6 +31,7 @@ CancellationToken cancellationToken ) where T : IData { + ApplicationDbContext? context = null; IReadOnlyList? representedInstitutionIds = null; IReadOnlyList? institutionAccessRights = null; uint? alreadyAccessedCount = null; @@ -40,6 +41,7 @@ CancellationToken cancellationToken if (currentUser is not null) { + context = dbContextFactory.CreateDbContext(); representedInstitutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); institutionAccessRights = (await context.InstitutionAccessRights.AsQueryable() .Where(x => representedInstitutionIds.Contains(x.InstitutionId)) @@ -50,8 +52,11 @@ CancellationToken cancellationToken var result = ProcessData(data, currentUser, openIdConnectClientId, representedInstitutionIds, institutionAccessRights, alreadyAccessedCount, cacheService); - // Save InstitutionAccessRight changes - await context.SaveChangesAsync(cancellationToken); + if (context is not null) + { + // Save InstitutionAccessRight changes + await context.SaveChangesAsync(cancellationToken); + } return result.AsQueryable(); } diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 3bcd3805..b350ebab 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,6 +1,5 @@ using System; using Database.ApiRequests.Dto; -using Database.Services; using Microsoft.Extensions.Caching.Memory; namespace Database.Services; From 6e16de3a957b3e7246976567217e8e1cc0eece79 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 Aug 2025 12:06:39 +0200 Subject: [PATCH 393/878] Update GraphQL schema --- frontend/type-defs.graphqls | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index ee6ad97c..32e84f04 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -1522,7 +1522,6 @@ enum AddDataApprovalErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA - UNKNOWN_DATABASE CREATING_RESPONSE_APPROVAL_FAILED AMBIGUOUS_STATEMENT MISSING_STATEMENT From 3658f75ec48a134107370c33fe0c2d40f3945c35 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 Aug 2025 20:04:20 +0200 Subject: [PATCH 394/878] Remove unnecessary setting of GPG_TTY environment variable --- backend/Dockerfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 7f4ef804..8f7da2b6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -146,10 +146,6 @@ COPY \ ./test/Database.Tests.csproj \ ./test/ -# Invoking GPG agent (https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html) -RUN \ - export GPG_TTY=$(tty) - # https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile.alpine-x64 # https://github.com/dotnet/dotnet-docker/blob/master/samples/run-aspnetcore-https-development.md # https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.2&tabs=linux From 3e2b4cf154075dc2f8e63be3cec3e243ef9f79f8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 Aug 2025 20:05:01 +0200 Subject: [PATCH 395/878] Clean-up signing service a bit --- backend/src/Services/SigningService.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index afe93277..9b3d9de9 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -13,14 +13,14 @@ public sealed class SigningService( { public async Task Initialize() { - var (success, output, diagnostics) = await ExecuteGnuCommand( + var (success, output, diagnostics) = await ExecuteCommand( $"gpg --batch --passphrase '{appSettings.GnupgPrivateKeyPassphrase}' --import './gpg-keys/{appSettings.GnupgPrivateKeyFilename}'" ); } public async Task SignData(string data) { - var (success, signature, diagnostics) = await ExecuteGnuCommand( + var (success, signature, diagnostics) = await ExecuteCommand( $"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {await ExtractFingerprint()}", data ); @@ -34,7 +34,7 @@ public async Task SignData(string data) public async Task ExtractFingerprint() { - var (success, fingerprint, diagnostics) = await ExecuteGnuCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); + var (success, fingerprint, diagnostics) = await ExecuteCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); if (!success || fingerprint.IsNullOrWhiteSpace()) { throw new InvalidOperationException($"Failed to extract fingerprint with standard output {fingerprint} and diagnostic information {diagnostics}"); @@ -43,7 +43,7 @@ public async Task ExtractFingerprint() return fingerprint; } - private async Task<(bool Success, string Output, string Diagnostics)> ExecuteGnuCommand( + private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommand( string command, string? input = null ) @@ -56,16 +56,17 @@ public async Task ExtractFingerprint() FileName = "bash", Arguments = $"-c \"{escapedCommand}\"", ErrorDialog = false, + UseShellExecute = false, CreateNoWindow = true, RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, } - ); - ArgumentNullException.ThrowIfNull(process); + ) ?? throw new InvalidOperationException($"The process for command '${command}' with the input '${input}' failed to start."); if (input is not null) { - await process.StandardInput.WriteLineAsync(input); + await process.StandardInput.WriteLineAsync(input); + await process.StandardInput.FlushAsync(); } process.StandardInput.Close(); var output = process.StandardOutput.ReadToEnd(); From 20cfebb6f4bb9b7229cbbb538eb4578d3044acf3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Sep 2025 17:05:48 +0200 Subject: [PATCH 396/878] Update approvals, in particular, rename `response` to `message` --- backend/src/ApiRequests/DataApi.cs | 4 +- backend/src/ApiRequests/DatabaseApi.cs | 2 +- .../Queries/CalorimetricData.graphql | 4 +- .../ApiRequests/Queries/CurrentUser.graphql | 4 +- .../ApiRequests/Queries/DataFields.graphql | 29 +- backend/src/ApiRequests/Queries/DataX.graphql | 4 +- .../src/ApiRequests/Queries/Database.graphql | 4 +- .../ApiRequests/Queries/GeometricData.graphql | 4 +- .../Queries/HygrothermalData.graphql | 4 +- .../ApiRequests/Queries/OpticalData.graphql | 4 +- .../Queries/PhotovoltaicData.graphql | 4 +- .../src/Configuration/GraphQlConfiguration.cs | 2 + backend/src/Data/DataApproval.cs | 8 +- backend/src/Data/IApproval.cs | 2 +- backend/src/Data/IReference.cs | 6 + backend/src/Data/ResponseApproval.cs | 4 +- .../DataApprovals/DataApprovalInput.cs | 2 +- .../DataApprovals/DataApprovalMutations.cs | 2 +- ...sePropertyOfApprovalsToMessage.Designer.cs | 2606 +++++++++++++++++ ...ameResponsePropertyOfApprovalsToMessage.cs | 138 + .../ApplicationDbContextModelSnapshot.cs | 42 +- frontend/type-defs.graphqls | 8 +- 22 files changed, 2833 insertions(+), 54 deletions(-) create mode 100644 backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs create mode 100644 backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index af0aa880..3654135e 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -73,7 +73,7 @@ CancellationToken cancellationToken ) where TGraphQlData : class { - var variables = new { uuid = dataId }; + var variables = new { id = dataId }; var query = await apiRequestService.ConstructGraphQlQuery(filenames); var response = JsonSerializer.Serialize( await apiRequestService.Database().QueryGraphQl( @@ -115,7 +115,7 @@ public static async Task> GetDataFromDatabase( appSettings, diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs index 1ec9b0ea..7ede9f38 100644 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ b/backend/src/ApiRequests/DatabaseApi.cs @@ -38,7 +38,7 @@ private sealed record DatabaseData(DatabaseDto Database); new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames), new { - uuid = databaseId + id = databaseId }, "Database" ), diff --git a/backend/src/ApiRequests/Queries/CalorimetricData.graphql b/backend/src/ApiRequests/Queries/CalorimetricData.graphql index 451caf6e..a709ba75 100644 --- a/backend/src/ApiRequests/Queries/CalorimetricData.graphql +++ b/backend/src/ApiRequests/Queries/CalorimetricData.graphql @@ -1,5 +1,5 @@ -query CalorimetricData($uuid: Uuid!) { - calorimetricData(id: $uuid) { +query CalorimetricData($id: Uuid!) { + calorimetricData(id: $id) { ...CalorimetricDataFields } } diff --git a/backend/src/ApiRequests/Queries/CurrentUser.graphql b/backend/src/ApiRequests/Queries/CurrentUser.graphql index 630ff761..d58ac0b9 100644 --- a/backend/src/ApiRequests/Queries/CurrentUser.graphql +++ b/backend/src/ApiRequests/Queries/CurrentUser.graphql @@ -33,7 +33,7 @@ query CurrentUser( institution: { operatedDatabases: { some: { - uuid: { equalTo: $databaseId } + id: { equalTo: $databaseId } } } } @@ -49,7 +49,7 @@ query CurrentUser( some: { operatedDatabases: { some: { - uuid: { + id: { equalTo: $databaseId } } diff --git a/backend/src/ApiRequests/Queries/DataFields.graphql b/backend/src/ApiRequests/Queries/DataFields.graphql index 77cb14aa..7deefeb4 100644 --- a/backend/src/ApiRequests/Queries/DataFields.graphql +++ b/backend/src/ApiRequests/Queries/DataFields.graphql @@ -28,7 +28,34 @@ fragment DataFields on Data { signature keyFingerprint query - response + variables + message + statement { + __typename + ... on Publication { + abstract + arXiv + authors + doi + section + title + urn + webAddress + } + ... on Standard { + abstract + locator + section + standardizers + title + year + numeration { + mainNumber + prefix + suffix + } + } + } approverId } resources { diff --git a/backend/src/ApiRequests/Queries/DataX.graphql b/backend/src/ApiRequests/Queries/DataX.graphql index 2c0a8271..e59c951e 100644 --- a/backend/src/ApiRequests/Queries/DataX.graphql +++ b/backend/src/ApiRequests/Queries/DataX.graphql @@ -1,5 +1,5 @@ -query Data($uuid: Uuid!) { - data(id: $uuid) { +query Data($id: Uuid!) { + data(id: $id) { ...DataFields } } \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Database.graphql b/backend/src/ApiRequests/Queries/Database.graphql index 68c71c5c..b8b375a8 100644 --- a/backend/src/ApiRequests/Queries/Database.graphql +++ b/backend/src/ApiRequests/Queries/Database.graphql @@ -1,7 +1,7 @@ query Database( - $uuid: Uuid! + $id: Uuid! ) { - database(uuid: $uuid) { + database(id: $id) { uuid name description diff --git a/backend/src/ApiRequests/Queries/GeometricData.graphql b/backend/src/ApiRequests/Queries/GeometricData.graphql index ea303ae9..cbd0aa6c 100644 --- a/backend/src/ApiRequests/Queries/GeometricData.graphql +++ b/backend/src/ApiRequests/Queries/GeometricData.graphql @@ -1,5 +1,5 @@ -query GeometricData($uuid: Uuid!) { - geometricData(id: $uuid) { +query GeometricData($id: Uuid!) { + geometricData(id: $id) { ...GeometricDataFields } } diff --git a/backend/src/ApiRequests/Queries/HygrothermalData.graphql b/backend/src/ApiRequests/Queries/HygrothermalData.graphql index a8da4aa8..b85b2aff 100644 --- a/backend/src/ApiRequests/Queries/HygrothermalData.graphql +++ b/backend/src/ApiRequests/Queries/HygrothermalData.graphql @@ -1,5 +1,5 @@ -query HygrothermalData($uuid: Uuid!) { - hygrothermalData(id: $uuid) { +query HygrothermalData($id: Uuid!) { + hygrothermalData(id: $id) { ...HygrothermalDataFields } } diff --git a/backend/src/ApiRequests/Queries/OpticalData.graphql b/backend/src/ApiRequests/Queries/OpticalData.graphql index 2d3ce8d8..ac50eb55 100644 --- a/backend/src/ApiRequests/Queries/OpticalData.graphql +++ b/backend/src/ApiRequests/Queries/OpticalData.graphql @@ -1,5 +1,5 @@ -query OpticalData($uuid: Uuid!) { - opticalData(id: $uuid) { +query OpticalData($id: Uuid!) { + opticalData(id: $id) { ...OpticalDataFields } } diff --git a/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql b/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql index 164795a5..29ea2bf4 100644 --- a/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql +++ b/backend/src/ApiRequests/Queries/PhotovoltaicData.graphql @@ -1,5 +1,5 @@ -query PhotovoltaicData($uuid: Uuid!) { - photovoltaicData(id: $uuid) { +query PhotovoltaicData($id: Uuid!) { + photovoltaicData(id: $id) { ...PhotovoltaicDataFields } } diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index fce6ac4d..b8faf1b3 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -36,6 +36,8 @@ namespace Database.Configuration; public static class GraphQlConfiguration { + internal const string TypeDiscriminatorPropertyName = "__typename"; + public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment, diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 1f986d6e..127b24a9 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -11,7 +11,7 @@ public sealed class DataApproval( string keyFingerprint, string query, JsonElement variables, - string response, + string message, Guid approverId, Reference statement ) @@ -24,7 +24,7 @@ private DataApproval( string keyFingerprint, string query, JsonElement variables, - string response, + string message, Guid approverId ) : this( @@ -33,7 +33,7 @@ Guid approverId keyFingerprint, query, variables, - response, + message, approverId, null! // EF Core will set this owned navigation property after construction. ) @@ -46,6 +46,6 @@ Guid approverId public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; public JsonElement Variables { get; private set; } = variables; - public string Response { get; private set; } = response; + public string Message { get; private set; } = message; public Reference Statement { get; private set; } = statement; } \ No newline at end of file diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 982cf5fc..375300cd 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -12,5 +12,5 @@ public interface IApproval string KeyFingerprint { get; } string Query { get; } JsonElement Variables { get; } - string Response { get; } + string Message { get; } } \ No newline at end of file diff --git a/backend/src/Data/IReference.cs b/backend/src/Data/IReference.cs index 1000f9df..62fc90c5 100644 --- a/backend/src/Data/IReference.cs +++ b/backend/src/Data/IReference.cs @@ -1,5 +1,11 @@ +using System.Text.Json.Serialization; +using Database.Configuration; + namespace Database.Data; +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonDerivedType(typeof(Standard), typeDiscriminator: nameof(Standard))] +[JsonDerivedType(typeof(Publication), typeDiscriminator: nameof(Publication))] public interface IReference { } \ No newline at end of file diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 70aa1338..5a69b90d 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -11,7 +11,7 @@ public sealed class ResponseApproval( string keyFingerprint, string query, JsonElement variables, - string response + string message ) : IApproval { @@ -20,6 +20,6 @@ string response public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; public JsonElement Variables { get; private set; } = variables; - public string Response { get; private set; } = response; + public string Message { get; private set; } = message; } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs index aca327c4..ad2dff7f 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs @@ -12,7 +12,7 @@ public sealed record DataApprovalInput string KeyFingerprint, string Query, JsonElement Variables, - string Response, + string Message, Guid ApproverId, ReferenceInput Statement ); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 17e9a649..02d2b31f 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -87,7 +87,7 @@ CancellationToken cancellationToken input.KeyFingerprint, input.Query, input.Variables, - input.Response, + input.Message, input.ApproverId, ReferenceType.FromInput(input.Statement) ); diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs new file mode 100644 index 00000000..2a5fa3f7 --- /dev/null +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs @@ -0,0 +1,2606 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250902122837_RenameResponsePropertyOfApprovalsToMessage")] + partial class RenameResponsePropertyOfApprovalsToMessage + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs new file mode 100644 index 00000000..e3852e8e --- /dev/null +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs @@ -0,0 +1,138 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class RenameResponsePropertyOfApprovalsToMessage : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "optical_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "geometric_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data", + newName: "Approval_Message"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "photovoltaic_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "optical_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "optical_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "hygrothermal_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "geometric_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "geometric_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "calorimetric_data", + newName: "Approval_Response"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 7d59ee59..324193b1 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.7") + .HasAnnotation("ProductVersion", "9.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); @@ -741,11 +741,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -787,11 +787,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1092,11 +1092,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1138,11 +1138,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1560,11 +1560,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1606,11 +1606,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1939,11 +1939,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -1985,11 +1985,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -2292,11 +2292,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); @@ -2338,11 +2338,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Query") + b1.Property("Message") .IsRequired() .HasColumnType("text"); - b1.Property("Response") + b1.Property("Query") .IsRequired() .HasColumnType("text"); diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 32e84f04..934771df 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -226,8 +226,8 @@ type DataAccessRights { type DataApproval { approverId: Uuid! keyFingerprint: String! + message: String! query: String! - response: String! signature: String! statement: Reference @cost(weight: "10") timestamp: DateTime! @@ -645,8 +645,8 @@ type Query { type ResponseApproval { keyFingerprint: String! + message: String! query: String! - response: String! signature: String! timestamp: DateTime! variables: Any! @@ -928,8 +928,8 @@ input DataApprovalInput { approverId: Uuid! dataId: Uuid! keyFingerprint: String! + message: String! query: String! - response: String! signature: String! statement: ReferenceInput! timestamp: DateTime! @@ -945,7 +945,7 @@ input DataApprovalPropositionInput { keyFingerprint: StringPropositionInput query: StringPropositionInput variables: JsonElementPropositionInput - response: StringPropositionInput + message: StringPropositionInput statement: ReferencePropositionInput } From 55fae8fec4046ec6defe000aa2ec204d558c9add Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Sep 2025 19:47:02 +0200 Subject: [PATCH 397/878] Serialize and deserialize `Reference` properly and add real query response to response approvals instead of a serialization of the deserilization of the response (these are two different things because the deserialization turns the response into the C# domain model and the domain model is not exactly the same as the one of the GraphQL schema) --- backend/src/ApiRequests/DataApi.cs | 17 +++-- backend/src/Data/Reference.cs | 3 + backend/src/Json/ReferenceJsonConverter.cs | 35 ++++++++++ .../Logging/ResponseApprovalServiceLogging.cs | 2 +- backend/src/Services/ApiRequestService.cs | 69 +++++++++++++++---- .../src/Services/ResponseApprovalService.cs | 2 +- backend/src/Startup.cs | 23 ++++--- frontend/queries/data.graphql | 35 ++++++++-- 8 files changed, 148 insertions(+), 38 deletions(-) create mode 100644 backend/src/Json/ReferenceJsonConverter.cs diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 3654135e..7662d09f 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -75,15 +76,13 @@ CancellationToken cancellationToken { var variables = new { id = dataId }; var query = await apiRequestService.ConstructGraphQlQuery(filenames); - var response = JsonSerializer.Serialize( - await apiRequestService.Database().QueryGraphQl( - appSettings, - new GraphQLRequest(query, variables), - httpClientFactory, - httpContextAccessor, - cancellationToken - ) - ) ?? throw new JsonException("Failed to serialize the response."); + var response = await apiRequestService.Database().QueryGraphQl( + appSettings, + new GraphQLRequest(query, variables), + httpClientFactory, + httpContextAccessor, + cancellationToken + ); return (query, JsonSerializer.SerializeToElement(variables), response); } diff --git a/backend/src/Data/Reference.cs b/backend/src/Data/Reference.cs index 02503d73..88a26483 100644 --- a/backend/src/Data/Reference.cs +++ b/backend/src/Data/Reference.cs @@ -1,11 +1,14 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; +using Database.Json; namespace Database.Data; [Owned] +[JsonConverter(typeof(ReferenceJsonConverter))] public sealed class Reference { // Constructor for EF Core because navigation properties cannot be set using a constructor. diff --git a/backend/src/Json/ReferenceJsonConverter.cs b/backend/src/Json/ReferenceJsonConverter.cs new file mode 100644 index 00000000..e78a59e0 --- /dev/null +++ b/backend/src/Json/ReferenceJsonConverter.cs @@ -0,0 +1,35 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Database.Data; + +namespace Database.Json; + +public sealed class ReferenceJsonConverter + : JsonConverter +{ + public ReferenceJsonConverter() + { + } + + private static JsonConverter GetReferenceConverter(JsonSerializerOptions options) + => (JsonConverter)options.GetConverter(typeof(IReference)); + + public override Reference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var reference = GetReferenceConverter(options) + .Read(ref reader, typeof(IReference), options); + return reference switch + { + Standard standard => new Reference(standard), + Publication publication => new Reference(publication), + _ => throw new JsonException() + }; + } + + public override void Write(Utf8JsonWriter writer, Reference value, JsonSerializerOptions options) + { + GetReferenceConverter(options) + .Write(writer, value.TheReference, options); + } +} \ No newline at end of file diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs index c2a0247e..89b773fe 100644 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ b/backend/src/Logging/ResponseApprovalServiceLogging.cs @@ -10,7 +10,7 @@ public static partial class ResponseApprovalServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "Querying all meta data for {DataType} with ID {Id}")] - public static partial void QueryAllMetaData(this ILogger logger, string dataType, Guid id); + public static partial void QueryAllMetaData(this ILogger logger, Type dataType, Guid id); [LoggerMessage( Level = LogLevel.Debug, diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 91c7bf43..ee25e508 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -70,6 +70,27 @@ await Task.WhenAll( ); } + public Task QueryGraphQl( + AppSettings appSettings, + GraphQLRequest request, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return Query( + HttpMethod.Post, + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of + // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), + httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), + MakeJsonHttpContent(request), + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } + /// /// Send GraphQL request to API and return response. /// @@ -164,16 +185,15 @@ CancellationToken cancellationToken ); } - private static async Task Query( + private static async Task Query( HttpMethod httpMethod, Uri uri, + Func> read, HttpContent? httpContent, - JsonSerializerOptions serializerOptions, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) - where TResponse : class { using var httpClient = s_useMetabase @@ -197,21 +217,42 @@ CancellationToken cancellationToken $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, httpResponseMessage.StatusCode); } + return await read(httpResponseMessage.Content); + } + private static async Task Query( + HttpMethod httpMethod, + Uri uri, + HttpContent? httpRequestContent, + JsonSerializerOptions serializerOptions, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + where TResponse : class + { // We could use - // `httpResponseMessage.Content.ReadFromJsonAsync>` which + // `httpResponseContent.ReadFromJsonAsync>` which // would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ - using var responseStream = await httpResponseMessage.Content - .ReadAsStreamAsync(cancellationToken); - - // For debugging, the following lines of code write the response to standard output. - // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); - var deserializedResponse = await JsonSerializer.DeserializeAsync( - responseStream, - serializerOptions, + return await Query( + httpMethod, + uri, + async httpResponseContent => { + var responseStream = await httpResponseContent.ReadAsStreamAsync(); + // For debugging, the following line of code writes the response to standard output. + // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); + return await JsonSerializer.DeserializeAsync( + responseStream, + serializerOptions, + cancellationToken + ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); + + }, + httpRequestContent, + httpClientFactory, + httpContextAccessor, cancellationToken - ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - return deserializedResponse; + ); } private static ByteArrayContent MakeJsonHttpContent( diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index defe5d91..5620f716 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -37,7 +37,7 @@ private sealed record OpticalDataResponse(OpticalDataDto OpticalData); public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { // Get dataset - logger.QueryAllMetaData(dataObject.GetType().Name, dataObject.Id); + logger.QueryAllMetaData(dataObject.GetType(), dataObject.Id); var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); logger.QueryAndVariablesAndResponce(query, variables, response); var signature = await signingService.SignData(response); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 449ee2a9..65dff411 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -61,15 +61,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(_appSettings); services.AddSingleton(_environment); // services.AddDatabaseDeveloperPageExceptionFilter(); - services.AddSingleton(); - services.AddSingleton(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); + ConfigureCustomServices(services); } private static void ConfigureRequestResponseServices(IServiceCollection services) @@ -241,6 +233,19 @@ private static void ConfigureHttpClientServices(IServiceCollection services, IWe } } + public static void ConfigureCustomServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + public void Configure(WebApplication app) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 91006602..65f0ae09 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -55,12 +55,39 @@ fragment DataPartial on Data { } } approvals { - approverId + timestamp + signature keyFingerprint query - response - signature - timestamp + variables + message + statement { + __typename + ... on Publication { + abstract + arXiv + authors + doi + section + title + urn + webAddress + } + ... on Standard { + abstract + locator + section + standardizers + title + year + numeration { + mainNumber + prefix + suffix + } + } + } + approverId } createdAt creatorId From 3c2a1d73252f6782ed1e679290b1f0181678dbb7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Sep 2025 19:48:08 +0200 Subject: [PATCH 398/878] Add URL to article on custom JSON converters --- backend/src/Json/ReferenceJsonConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Json/ReferenceJsonConverter.cs b/backend/src/Json/ReferenceJsonConverter.cs index e78a59e0..517440d0 100644 --- a/backend/src/Json/ReferenceJsonConverter.cs +++ b/backend/src/Json/ReferenceJsonConverter.cs @@ -5,6 +5,7 @@ namespace Database.Json; +// Inspired by https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to public sealed class ReferenceJsonConverter : JsonConverter { From 57d0ce9eecc6e755ad7a910fe46c138b82f78ce1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Sep 2025 20:44:15 +0200 Subject: [PATCH 399/878] Add GNU Make targets to migrate and restore database --- Makefile | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 449fd53e..eca9dce8 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ pull : ## Pull images # To debug errors during build add `--progress plain \` to get additional # output. -build : pull ## Build images +build : check pull ## Build images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ @@ -68,9 +68,8 @@ bake : ## Print docker-compose file equivalent bake file DOCKER_BUILDKIT=1 \ ${docker_compose} build \ --print \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + --pull + # --no-cache .PHONY : bake backend-build-context : ## Show the build context configured by `./backend/.dockerignore` @@ -239,6 +238,67 @@ sql : ## Run the SQL script in the file `${SQL}` in the running `database` servi --dbname=${database_name} .PHONY : sql +# Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL +# Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html +backup : CONTAINER_NAME = backup_${NAME}_database +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=./backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` + mkdir --parents ${BACKUP_DIRECTORY} + ${docker_compose} down \ + --remove-orphans \ + database + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} run \ + --name ${CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + docker exec \ + ${CONTAINER_NAME} \ + pg_dumpall \ + --clean \ + --username=postgres \ + | gzip \ + > ${BACKUP_DIRECTORY}/${dump_archive_name} + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database +.PHONY : backup + +restore : CONTAINER_NAME = restore_${NAME}_database +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=./backups/2021-04-22_15_43_35/ restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` + ${docker_compose} down \ + --remove-orphans \ + database + docker volume rm \ + ${NAME}_data + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} run \ + --name ${CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ + | docker exec \ + --interactive \ + ${CONTAINER_NAME} \ + psql \ + --echo-all \ + --file=- \ + --username=postgres \ + --dbname=postgres + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database +.PHONY : restore + begin-maintenance : ## Begin maintenance cp \ ./nginx/html/maintenance.off.html \ From 61aa0cb715c40f7a9b03424a72999dca4e350ed6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Sep 2025 20:44:43 +0200 Subject: [PATCH 400/878] Add GNU Make target to execute statements from a SQL script --- Makefile.production | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile.production b/Makefile.production index f3ee7f2a..de2b8ec9 100644 --- a/Makefile.production +++ b/Makefile.production @@ -356,10 +356,8 @@ checkout-target : ## Fetch and checkout `${TARGET}` git checkout --force "${TARGET}" .PHONY : checkout-target -migrate : FROM = $(file < ./backend/src/Migrations/previous) -migrate : TO = $(file < ./backend/src/Migrations/current) -migrate : CONTAINER_NAME = migrate_${NAME}_database -migrate : ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +sql : CONTAINER_NAME = sql_${NAME}_database +sql : ## Run the SQL script in the file `${SQL}` in the database service, for example, `make SQL=./my.sql sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) ${docker_compose} down \ --remove-orphans \ database @@ -370,7 +368,7 @@ migrate : ## Migrate database (down-ing and up-ing the database service before a --detach \ database while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - cat ./backend/src/Migrations/migrate_from_${FROM}_to_${TO}.sql \ + cat ${SQL} \ | docker exec \ --interactive \ ${CONTAINER_NAME} \ @@ -387,6 +385,12 @@ migrate : ## Migrate database (down-ing and up-ing the database service before a database .PHONY : migrate +migrate : FROM = $(file < ./backend/src/Migrations/previous) +migrate : TO = $(file < ./backend/src/Migrations/current) +migrate : SQL = ./backend/src/Migrations/migrate_from_${FROM}_to_${TO}.sql +migrate : sql ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +.PHONY : migrate + # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. deploy-services : build ## Deploy services From 08190d99b18c4ff2cdb6ba51d2bd3e9abf4d6422 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 14:50:36 +0200 Subject: [PATCH 401/878] Update .NET dependencies and tools --- backend/dotnet-tools.json | 8 ++++---- backend/src/Database.csproj | 26 +++++++++++++------------- backend/test/Database.Tests.csproj | 10 +++++----- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 46b8951c..607cf0a6 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "9.0.7", + "version": "9.0.8", "commands": [ "dotnet-ef" ], @@ -59,14 +59,14 @@ "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.10.1", + "version": "0.10.2", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.1.4", + "version": "2025.2.0.1", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "15.1.8", + "version": "15.1.9", "commands": [ "dotnet-graphql" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index e1f70f1c..adde690e 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -25,23 +25,23 @@ - - - - - - + + + + + + - + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -51,7 +51,7 @@ - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 1dc5ff0c..5536bba6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,14 +16,14 @@ - + - + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive From 30909cc3efaefa8dca44e6669124a2216ecbb215 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 14:51:04 +0200 Subject: [PATCH 402/878] Add SQL migration and rollback scripts --- backend/src/Migrations/current | 2 +- ...meResponsePropertyOfApprovalsToMessage.sql | 49 +++++++++++++++++++ backend/src/Migrations/previous | 2 +- ...meResponsePropertyOfApprovalsToMessage.sql | 49 +++++++++++++++++++ 4 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql create mode 100644 backend/src/Migrations/rollback_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index f0aeaa54..d627d4ba 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250731163636_MakeInstitutionIdOfAccessRightsUnique +20250902122837_RenameResponsePropertyOfApprovalsToMessage diff --git a/backend/src/Migrations/migrate_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql b/backend/src/Migrations/migrate_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql new file mode 100644 index 00000000..0ff9f0a6 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql @@ -0,0 +1,49 @@ +START TRANSACTION; +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.photovoltaic_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."optical_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.optical_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.hygrothermal_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."geometric_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.geometric_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.calorimetric_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250812171810_AddQueryVariablesToApprovals', '9.0.8'); + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.photovoltaic_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.optical_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.hygrothermal_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.geometric_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.calorimetric_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250902122837_RenameResponsePropertyOfApprovalsToMessage', '9.0.8'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index f4b3371b..f0aeaa54 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20250530155318_AddRequiredPropertyExistsToPublication +20250731163636_MakeInstitutionIdOfAccessRightsUnique diff --git a/backend/src/Migrations/rollback_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql b/backend/src/Migrations/rollback_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql new file mode 100644 index 00000000..be8d64bd --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250731163636_MakeInstitutionIdOfAccessRightsUnique_to_20250902122837_RenameResponsePropertyOfApprovalsToMessage.sql @@ -0,0 +1,49 @@ +START TRANSACTION; +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Message" TO "Response"; + +ALTER TABLE database.photovoltaic_data RENAME COLUMN "Approval_Message" TO "Approval_Response"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Message" TO "Response"; + +ALTER TABLE database.optical_data RENAME COLUMN "Approval_Message" TO "Approval_Response"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Message" TO "Response"; + +ALTER TABLE database.hygrothermal_data RENAME COLUMN "Approval_Message" TO "Approval_Response"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Message" TO "Response"; + +ALTER TABLE database.geometric_data RENAME COLUMN "Approval_Message" TO "Approval_Response"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Message" TO "Response"; + +ALTER TABLE database.calorimetric_data RENAME COLUMN "Approval_Message" TO "Approval_Response"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250902122837_RenameResponsePropertyOfApprovalsToMessage'; + +ALTER TABLE database."photovoltaic_data_Approvals" DROP COLUMN "Variables"; + +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_Variables"; + +ALTER TABLE database."optical_data_Approvals" DROP COLUMN "Variables"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_Variables"; + +ALTER TABLE database."hygrothermal_data_Approvals" DROP COLUMN "Variables"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_Variables"; + +ALTER TABLE database."geometric_data_Approvals" DROP COLUMN "Variables"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_Variables"; + +ALTER TABLE database."calorimetric_data_Approvals" DROP COLUMN "Variables"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_Variables"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250812171810_AddQueryVariablesToApprovals'; + +COMMIT; + From 721a8239dca791703d17b684e92fc52c3e106cbd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 15:31:13 +0200 Subject: [PATCH 403/878] Fix GNU Make recipe for generating GnuPG keys --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index eca9dce8..850ec7c7 100644 --- a/Makefile +++ b/Makefile @@ -326,8 +326,8 @@ gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` "${NAME} (${COMMENT}) <${EMAIL}>" \ ed25519 \ sign \ - never - fingerprint=gpg \ + never; \ + fingerprint=$$(gpg \ --list-secret-keys \ --with-colons \ --keyid-format=long \ @@ -335,9 +335,10 @@ gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` | grep \ --before=3 \ "${NAME} (${COMMENT}) <${EMAIL}>" \ - | awk -F: '$$1=="fpr" {printf $$10; exit}' + | awk -F: '$$1=="fpr" {printf $$10; exit}'); \ + echo $$fingerprint; \ mkdir --parents \ - ./backend/src/gpg-keys + ./backend/src/gpg-keys; \ gpg \ --armor \ --export-secret-keys $$fingerprint \ From 834be6394682952e736b2e1ef4a185afed1bbda8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 15:37:46 +0200 Subject: [PATCH 404/878] Use file based namespace everywhere --- backend/src/Data/ResponseApproval.cs | 37 +- .../20211019125407_InitialCreate.cs | 1303 +++++----- .../20230126153325_RenameXminToVersion.cs | 449 ++-- .../20230210135413_AddDataProtectionKeys.cs | 53 +- .../Migrations/20230420143838_OpenIddict.cs | 343 ++- .../20230427133946_CreateUserTable.cs | 55 +- .../20240522120225_UpgradeOpenIddict.cs | 123 +- ...240709144935_AddStatementToDataApproval.cs | 1677 +++++++------ ...722133619_MoveEnumTypesToMetabaseSchema.cs | 353 ++- .../20240917073732_AddGeometricData.cs | 369 ++- .../20250129163501_UpgradeToNet9.cs | 99 +- ...55333_AddTypeSubtypeAndCoatedSideToData.cs | 95 +- .../20250218172859_MakeDescriptionOptional.cs | 55 +- .../20250331141152_AddAccessRights.cs | 661 +++-- .../20250527144412_AddDataAccessRights.cs | 377 ++- ...50527150325_MoveInstitutionAccessRights.cs | 85 +- .../20250530151605_ModelReferenceProperly.cs | 2187 ++++++++--------- ..._AddRequiredPropertyExistsToPublication.cs | 119 +- ...50725172127_UpgradeOpenIddictToVersion7.cs | 63 +- ...6_MakeInstitutionIdOfAccessRightsUnique.cs | 39 +- ...0812171810_AddQueryVariablesToApprovals.cs | 277 ++- ...ameResponsePropertyOfApprovalsToMessage.cs | 257 +- 22 files changed, 4527 insertions(+), 4549 deletions(-) diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 5a69b90d..9cfcfc4c 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -2,24 +2,23 @@ using System.Text.Json; using Microsoft.EntityFrameworkCore; -namespace Database.Data +namespace Database.Data; + +[Owned] +public sealed class ResponseApproval( + DateTime timestamp, + string signature, + string keyFingerprint, + string query, + JsonElement variables, + string message + ) + : IApproval { - [Owned] - public sealed class ResponseApproval( - DateTime timestamp, - string signature, - string keyFingerprint, - string query, - JsonElement variables, - string message - ) - : IApproval - { - public DateTime Timestamp { get; private set; } = timestamp; - public string Signature { get; private set; } = signature; - public string KeyFingerprint { get; private set; } = keyFingerprint; - public string Query { get; private set; } = query; - public JsonElement Variables { get; private set; } = variables; - public string Message { get; private set; } = message; - } + public DateTime Timestamp { get; private set; } = timestamp; + public string Signature { get; private set; } = signature; + public string KeyFingerprint { get; private set; } = keyFingerprint; + public string Query { get; private set; } = query; + public JsonElement Variables { get; private set; } = variables; + public string Message { get; private set; } = message; } \ No newline at end of file diff --git a/backend/src/Migrations/20211019125407_InitialCreate.cs b/backend/src/Migrations/20211019125407_InitialCreate.cs index 3da7159e..c253233b 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.cs @@ -4,659 +4,658 @@ using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -namespace Database.Migrations +namespace Database.Migrations; + +public partial class InitialCreate : Migration { - public partial class InitialCreate : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "database"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.CreateTable( + name: "calorimetric_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + GValues = table.Column(type: "double precision[]", nullable: false), + UValues = table.Column(type: "double precision[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_calorimetric_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "hygrothermal_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_hygrothermal_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "optical_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + NearnormalHemisphericalVisibleTransmittances = table.Column(type: "double precision[]", nullable: false), + NearnormalHemisphericalVisibleReflectances = table.Column(type: "double precision[]", nullable: false), + NearnormalHemisphericalSolarTransmittances = table.Column(type: "double precision[]", nullable: false), + NearnormalHemisphericalSolarReflectances = table.Column(type: "double precision[]", nullable: false), + InfraredEmittances = table.Column(type: "double precision[]", nullable: false), + ColorRenderingIndices = table.Column(type: "double precision[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_optical_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "photovoltaic_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_photovoltaic_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "calorimetric_data_Approvals", + schema: "database", + columns: table => new + { + CalorimetricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Timestamp = table.Column(type: "timestamp without time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + ApproverId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_calorimetric_data_Approvals", x => new { x.CalorimetricDataId, x.Id }); + table.ForeignKey( + name: "FK_calorimetric_data_Approvals_calorimetric_data_CalorimetricD~", + column: x => x.CalorimetricDataId, + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "calorimetric_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodCalorimetricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_calorimetric_data_Arguments", x => new { x.AppliedMethodCalorimetricDataId, x.Id }); + table.ForeignKey( + name: "FK_calorimetric_data_Arguments_calorimetric_data_AppliedMethod~", + column: x => x.AppliedMethodCalorimetricDataId, + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "calorimetric_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodCalorimetricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), + Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_calorimetric_data_Sources", x => new { x.AppliedMethodCalorimetricDataId, x.Id }); + table.ForeignKey( + name: "FK_calorimetric_data_Sources_calorimetric_data_AppliedMethodCa~", + column: x => x.AppliedMethodCalorimetricDataId, + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "hygrothermal_data_Approvals", + schema: "database", + columns: table => new + { + HygrothermalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Timestamp = table.Column(type: "timestamp without time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + ApproverId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_hygrothermal_data_Approvals", x => new { x.HygrothermalDataId, x.Id }); + table.ForeignKey( + name: "FK_hygrothermal_data_Approvals_hygrothermal_data_HygrothermalD~", + column: x => x.HygrothermalDataId, + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "hygrothermal_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodHygrothermalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_hygrothermal_data_Arguments", x => new { x.AppliedMethodHygrothermalDataId, x.Id }); + table.ForeignKey( + name: "FK_hygrothermal_data_Arguments_hygrothermal_data_AppliedMethod~", + column: x => x.AppliedMethodHygrothermalDataId, + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "hygrothermal_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodHygrothermalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), + Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_hygrothermal_data_Sources", x => new { x.AppliedMethodHygrothermalDataId, x.Id }); + table.ForeignKey( + name: "FK_hygrothermal_data_Sources_hygrothermal_data_AppliedMethodHy~", + column: x => x.AppliedMethodHygrothermalDataId, + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "CielabColor", + schema: "database", + columns: table => new + { + OpticalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + LStar = table.Column(type: "double precision", nullable: false), + AStar = table.Column(type: "double precision", nullable: false), + BStar = table.Column(type: "double precision", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CielabColor", x => new { x.OpticalDataId, x.Id }); + table.ForeignKey( + name: "FK_CielabColor_optical_data_OpticalDataId", + column: x => x.OpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "optical_data_Approvals", + schema: "database", + columns: table => new + { + OpticalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Timestamp = table.Column(type: "timestamp without time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + ApproverId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_optical_data_Approvals", x => new { x.OpticalDataId, x.Id }); + table.ForeignKey( + name: "FK_optical_data_Approvals_optical_data_OpticalDataId", + column: x => x.OpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "optical_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodOpticalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_optical_data_Arguments", x => new { x.AppliedMethodOpticalDataId, x.Id }); + table.ForeignKey( + name: "FK_optical_data_Arguments_optical_data_AppliedMethodOpticalDat~", + column: x => x.AppliedMethodOpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "optical_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodOpticalDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), + Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_optical_data_Sources", x => new { x.AppliedMethodOpticalDataId, x.Id }); + table.ForeignKey( + name: "FK_optical_data_Sources_optical_data_AppliedMethodOpticalDataId", + column: x => x.AppliedMethodOpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "get_https_resource", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + Description = table.Column(type: "text", nullable: false), + HashValue = table.Column(type: "text", nullable: false), + DataFormatId = table.Column(type: "uuid", nullable: false), + CalorimetricDataId = table.Column(type: "uuid", nullable: true), + HygrothermalDataId = table.Column(type: "uuid", nullable: true), + OpticalDataId = table.Column(type: "uuid", nullable: true), + PhotovoltaicDataId = table.Column(type: "uuid", nullable: true), + ParentId = table.Column(type: "uuid", nullable: true), + AppliedConversionMethod_MethodId = table.Column(type: "uuid", nullable: true), + AppliedConversionMethod_SourceName = table.Column(type: "text", nullable: true), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_get_https_resource", x => x.Id); + table.ForeignKey( + name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", + column: x => x.CalorimetricDataId, + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_get_https_resource_get_https_resource_ParentId", + column: x => x.ParentId, + principalSchema: "database", + principalTable: "get_https_resource", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", + column: x => x.HygrothermalDataId, + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_get_https_resource_optical_data_OpticalDataId", + column: x => x.OpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", + column: x => x.PhotovoltaicDataId, + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "photovoltaic_data_Approvals", + schema: "database", + columns: table => new + { + PhotovoltaicDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Timestamp = table.Column(type: "timestamp without time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + ApproverId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_photovoltaic_data_Approvals", x => new { x.PhotovoltaicDataId, x.Id }); + table.ForeignKey( + name: "FK_photovoltaic_data_Approvals_photovoltaic_data_PhotovoltaicD~", + column: x => x.PhotovoltaicDataId, + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "photovoltaic_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodPhotovoltaicDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_photovoltaic_data_Arguments", x => new { x.AppliedMethodPhotovoltaicDataId, x.Id }); + table.ForeignKey( + name: "FK_photovoltaic_data_Arguments_photovoltaic_data_AppliedMethod~", + column: x => x.AppliedMethodPhotovoltaicDataId, + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "photovoltaic_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodPhotovoltaicDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), + Value_DataKind = table.Column(type: "data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_photovoltaic_data_Sources", x => new { x.AppliedMethodPhotovoltaicDataId, x.Id }); + table.ForeignKey( + name: "FK_photovoltaic_data_Sources_photovoltaic_data_AppliedMethodPh~", + column: x => x.AppliedMethodPhotovoltaicDataId, + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "FileMetaInformation", + schema: "database", + columns: table => new + { + GetHttpsResourceId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Path = table.Column(type: "text[]", nullable: false), + DataFormatId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_FileMetaInformation", x => new { x.GetHttpsResourceId, x.Id }); + table.ForeignKey( + name: "FK_FileMetaInformation_get_https_resource_GetHttpsResourceId", + column: x => x.GetHttpsResourceId, + principalSchema: "database", + principalTable: "get_https_resource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "get_https_resource_Arguments", + schema: "database", + columns: table => new + { + ToTreeVertexAppliedConversionMethodGetHttpsResourceId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_get_https_resource_Arguments", x => new { x.ToTreeVertexAppliedConversionMethodGetHttpsResourceId, x.Id }); + table.ForeignKey( + name: "FK_get_https_resource_Arguments_get_https_resource_ToTreeVerte~", + column: x => x.ToTreeVertexAppliedConversionMethodGetHttpsResourceId, + principalSchema: "database", + principalTable: "get_https_resource", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_CalorimetricDataId", + schema: "database", + table: "get_https_resource", + column: "CalorimetricDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_HygrothermalDataId", + schema: "database", + table: "get_https_resource", + column: "HygrothermalDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_OpticalDataId", + schema: "database", + table: "get_https_resource", + column: "OpticalDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_ParentId", + schema: "database", + table: "get_https_resource", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource", + column: "PhotovoltaicDataId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.EnsureSchema( - name: "database"); - - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - - migrationBuilder.CreateTable( - name: "calorimetric_data", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - GValues = table.Column(type: "double precision[]", nullable: false), - UValues = table.Column(type: "double precision[]", nullable: false), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false), - Locale = table.Column(type: "text", nullable: false), - ComponentId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: true), - Description = table.Column(type: "text", nullable: true), - Warnings = table.Column(type: "text[]", nullable: false), - CreatorId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), - AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_calorimetric_data", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "hygrothermal_data", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false), - Locale = table.Column(type: "text", nullable: false), - ComponentId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: true), - Description = table.Column(type: "text", nullable: true), - Warnings = table.Column(type: "text[]", nullable: false), - CreatorId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), - AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_hygrothermal_data", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "optical_data", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - NearnormalHemisphericalVisibleTransmittances = table.Column(type: "double precision[]", nullable: false), - NearnormalHemisphericalVisibleReflectances = table.Column(type: "double precision[]", nullable: false), - NearnormalHemisphericalSolarTransmittances = table.Column(type: "double precision[]", nullable: false), - NearnormalHemisphericalSolarReflectances = table.Column(type: "double precision[]", nullable: false), - InfraredEmittances = table.Column(type: "double precision[]", nullable: false), - ColorRenderingIndices = table.Column(type: "double precision[]", nullable: false), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false), - Locale = table.Column(type: "text", nullable: false), - ComponentId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: true), - Description = table.Column(type: "text", nullable: true), - Warnings = table.Column(type: "text[]", nullable: false), - CreatorId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), - AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_optical_data", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "photovoltaic_data", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false), - Locale = table.Column(type: "text", nullable: false), - ComponentId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: true), - Description = table.Column(type: "text", nullable: true), - Warnings = table.Column(type: "text[]", nullable: false), - CreatorId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp without time zone", nullable: false), - AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_photovoltaic_data", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "calorimetric_data_Approvals", - schema: "database", - columns: table => new - { - CalorimetricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Timestamp = table.Column(type: "timestamp without time zone", nullable: false), - Signature = table.Column(type: "text", nullable: false), - KeyFingerprint = table.Column(type: "text", nullable: false), - Query = table.Column(type: "text", nullable: false), - Response = table.Column(type: "text", nullable: false), - ApproverId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_calorimetric_data_Approvals", x => new { x.CalorimetricDataId, x.Id }); - table.ForeignKey( - name: "FK_calorimetric_data_Approvals_calorimetric_data_CalorimetricD~", - column: x => x.CalorimetricDataId, - principalSchema: "database", - principalTable: "calorimetric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "calorimetric_data_Arguments", - schema: "database", - columns: table => new - { - AppliedMethodCalorimetricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_calorimetric_data_Arguments", x => new { x.AppliedMethodCalorimetricDataId, x.Id }); - table.ForeignKey( - name: "FK_calorimetric_data_Arguments_calorimetric_data_AppliedMethod~", - column: x => x.AppliedMethodCalorimetricDataId, - principalSchema: "database", - principalTable: "calorimetric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "calorimetric_data_Sources", - schema: "database", - columns: table => new - { - AppliedMethodCalorimetricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value_DataId = table.Column(type: "uuid", nullable: false), - Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), - Value_DataKind = table.Column(type: "data_kind", nullable: false), - Value_DatabaseId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_calorimetric_data_Sources", x => new { x.AppliedMethodCalorimetricDataId, x.Id }); - table.ForeignKey( - name: "FK_calorimetric_data_Sources_calorimetric_data_AppliedMethodCa~", - column: x => x.AppliedMethodCalorimetricDataId, - principalSchema: "database", - principalTable: "calorimetric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "hygrothermal_data_Approvals", - schema: "database", - columns: table => new - { - HygrothermalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Timestamp = table.Column(type: "timestamp without time zone", nullable: false), - Signature = table.Column(type: "text", nullable: false), - KeyFingerprint = table.Column(type: "text", nullable: false), - Query = table.Column(type: "text", nullable: false), - Response = table.Column(type: "text", nullable: false), - ApproverId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_hygrothermal_data_Approvals", x => new { x.HygrothermalDataId, x.Id }); - table.ForeignKey( - name: "FK_hygrothermal_data_Approvals_hygrothermal_data_HygrothermalD~", - column: x => x.HygrothermalDataId, - principalSchema: "database", - principalTable: "hygrothermal_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "hygrothermal_data_Arguments", - schema: "database", - columns: table => new - { - AppliedMethodHygrothermalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_hygrothermal_data_Arguments", x => new { x.AppliedMethodHygrothermalDataId, x.Id }); - table.ForeignKey( - name: "FK_hygrothermal_data_Arguments_hygrothermal_data_AppliedMethod~", - column: x => x.AppliedMethodHygrothermalDataId, - principalSchema: "database", - principalTable: "hygrothermal_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "hygrothermal_data_Sources", - schema: "database", - columns: table => new - { - AppliedMethodHygrothermalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value_DataId = table.Column(type: "uuid", nullable: false), - Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), - Value_DataKind = table.Column(type: "data_kind", nullable: false), - Value_DatabaseId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_hygrothermal_data_Sources", x => new { x.AppliedMethodHygrothermalDataId, x.Id }); - table.ForeignKey( - name: "FK_hygrothermal_data_Sources_hygrothermal_data_AppliedMethodHy~", - column: x => x.AppliedMethodHygrothermalDataId, - principalSchema: "database", - principalTable: "hygrothermal_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "CielabColor", - schema: "database", - columns: table => new - { - OpticalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - LStar = table.Column(type: "double precision", nullable: false), - AStar = table.Column(type: "double precision", nullable: false), - BStar = table.Column(type: "double precision", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CielabColor", x => new { x.OpticalDataId, x.Id }); - table.ForeignKey( - name: "FK_CielabColor_optical_data_OpticalDataId", - column: x => x.OpticalDataId, - principalSchema: "database", - principalTable: "optical_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "optical_data_Approvals", - schema: "database", - columns: table => new - { - OpticalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Timestamp = table.Column(type: "timestamp without time zone", nullable: false), - Signature = table.Column(type: "text", nullable: false), - KeyFingerprint = table.Column(type: "text", nullable: false), - Query = table.Column(type: "text", nullable: false), - Response = table.Column(type: "text", nullable: false), - ApproverId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_optical_data_Approvals", x => new { x.OpticalDataId, x.Id }); - table.ForeignKey( - name: "FK_optical_data_Approvals_optical_data_OpticalDataId", - column: x => x.OpticalDataId, - principalSchema: "database", - principalTable: "optical_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "optical_data_Arguments", - schema: "database", - columns: table => new - { - AppliedMethodOpticalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_optical_data_Arguments", x => new { x.AppliedMethodOpticalDataId, x.Id }); - table.ForeignKey( - name: "FK_optical_data_Arguments_optical_data_AppliedMethodOpticalDat~", - column: x => x.AppliedMethodOpticalDataId, - principalSchema: "database", - principalTable: "optical_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "optical_data_Sources", - schema: "database", - columns: table => new - { - AppliedMethodOpticalDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value_DataId = table.Column(type: "uuid", nullable: false), - Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), - Value_DataKind = table.Column(type: "data_kind", nullable: false), - Value_DatabaseId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_optical_data_Sources", x => new { x.AppliedMethodOpticalDataId, x.Id }); - table.ForeignKey( - name: "FK_optical_data_Sources_optical_data_AppliedMethodOpticalDataId", - column: x => x.AppliedMethodOpticalDataId, - principalSchema: "database", - principalTable: "optical_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "get_https_resource", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - Description = table.Column(type: "text", nullable: false), - HashValue = table.Column(type: "text", nullable: false), - DataFormatId = table.Column(type: "uuid", nullable: false), - CalorimetricDataId = table.Column(type: "uuid", nullable: true), - HygrothermalDataId = table.Column(type: "uuid", nullable: true), - OpticalDataId = table.Column(type: "uuid", nullable: true), - PhotovoltaicDataId = table.Column(type: "uuid", nullable: true), - ParentId = table.Column(type: "uuid", nullable: true), - AppliedConversionMethod_MethodId = table.Column(type: "uuid", nullable: true), - AppliedConversionMethod_SourceName = table.Column(type: "text", nullable: true), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_get_https_resource", x => x.Id); - table.ForeignKey( - name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", - column: x => x.CalorimetricDataId, - principalSchema: "database", - principalTable: "calorimetric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_get_https_resource_get_https_resource_ParentId", - column: x => x.ParentId, - principalSchema: "database", - principalTable: "get_https_resource", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", - column: x => x.HygrothermalDataId, - principalSchema: "database", - principalTable: "hygrothermal_data", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_get_https_resource_optical_data_OpticalDataId", - column: x => x.OpticalDataId, - principalSchema: "database", - principalTable: "optical_data", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", - column: x => x.PhotovoltaicDataId, - principalSchema: "database", - principalTable: "photovoltaic_data", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "photovoltaic_data_Approvals", - schema: "database", - columns: table => new - { - PhotovoltaicDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Timestamp = table.Column(type: "timestamp without time zone", nullable: false), - Signature = table.Column(type: "text", nullable: false), - KeyFingerprint = table.Column(type: "text", nullable: false), - Query = table.Column(type: "text", nullable: false), - Response = table.Column(type: "text", nullable: false), - ApproverId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_photovoltaic_data_Approvals", x => new { x.PhotovoltaicDataId, x.Id }); - table.ForeignKey( - name: "FK_photovoltaic_data_Approvals_photovoltaic_data_PhotovoltaicD~", - column: x => x.PhotovoltaicDataId, - principalSchema: "database", - principalTable: "photovoltaic_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "photovoltaic_data_Arguments", - schema: "database", - columns: table => new - { - AppliedMethodPhotovoltaicDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_photovoltaic_data_Arguments", x => new { x.AppliedMethodPhotovoltaicDataId, x.Id }); - table.ForeignKey( - name: "FK_photovoltaic_data_Arguments_photovoltaic_data_AppliedMethod~", - column: x => x.AppliedMethodPhotovoltaicDataId, - principalSchema: "database", - principalTable: "photovoltaic_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "photovoltaic_data_Sources", - schema: "database", - columns: table => new - { - AppliedMethodPhotovoltaicDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value_DataId = table.Column(type: "uuid", nullable: false), - Value_DataTimestamp = table.Column(type: "timestamp without time zone", nullable: false), - Value_DataKind = table.Column(type: "data_kind", nullable: false), - Value_DatabaseId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_photovoltaic_data_Sources", x => new { x.AppliedMethodPhotovoltaicDataId, x.Id }); - table.ForeignKey( - name: "FK_photovoltaic_data_Sources_photovoltaic_data_AppliedMethodPh~", - column: x => x.AppliedMethodPhotovoltaicDataId, - principalSchema: "database", - principalTable: "photovoltaic_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "FileMetaInformation", - schema: "database", - columns: table => new - { - GetHttpsResourceId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Path = table.Column(type: "text[]", nullable: false), - DataFormatId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_FileMetaInformation", x => new { x.GetHttpsResourceId, x.Id }); - table.ForeignKey( - name: "FK_FileMetaInformation_get_https_resource_GetHttpsResourceId", - column: x => x.GetHttpsResourceId, - principalSchema: "database", - principalTable: "get_https_resource", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "get_https_resource_Arguments", - schema: "database", - columns: table => new - { - ToTreeVertexAppliedConversionMethodGetHttpsResourceId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_get_https_resource_Arguments", x => new { x.ToTreeVertexAppliedConversionMethodGetHttpsResourceId, x.Id }); - table.ForeignKey( - name: "FK_get_https_resource_Arguments_get_https_resource_ToTreeVerte~", - column: x => x.ToTreeVertexAppliedConversionMethodGetHttpsResourceId, - principalSchema: "database", - principalTable: "get_https_resource", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_CalorimetricDataId", - schema: "database", - table: "get_https_resource", - column: "CalorimetricDataId"); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_HygrothermalDataId", - schema: "database", - table: "get_https_resource", - column: "HygrothermalDataId"); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_OpticalDataId", - schema: "database", - table: "get_https_resource", - column: "OpticalDataId"); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_ParentId", - schema: "database", - table: "get_https_resource", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_PhotovoltaicDataId", - schema: "database", - table: "get_https_resource", - column: "PhotovoltaicDataId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "calorimetric_data_Approvals", - schema: "database"); - - migrationBuilder.DropTable( - name: "calorimetric_data_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "calorimetric_data_Sources", - schema: "database"); - - migrationBuilder.DropTable( - name: "CielabColor", - schema: "database"); - - migrationBuilder.DropTable( - name: "FileMetaInformation", - schema: "database"); - - migrationBuilder.DropTable( - name: "get_https_resource_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "hygrothermal_data_Approvals", - schema: "database"); - - migrationBuilder.DropTable( - name: "hygrothermal_data_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "hygrothermal_data_Sources", - schema: "database"); - - migrationBuilder.DropTable( - name: "optical_data_Approvals", - schema: "database"); - - migrationBuilder.DropTable( - name: "optical_data_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "optical_data_Sources", - schema: "database"); - - migrationBuilder.DropTable( - name: "photovoltaic_data_Approvals", - schema: "database"); - - migrationBuilder.DropTable( - name: "photovoltaic_data_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "photovoltaic_data_Sources", - schema: "database"); - - migrationBuilder.DropTable( - name: "get_https_resource", - schema: "database"); - - migrationBuilder.DropTable( - name: "calorimetric_data", - schema: "database"); - - migrationBuilder.DropTable( - name: "hygrothermal_data", - schema: "database"); - - migrationBuilder.DropTable( - name: "optical_data", - schema: "database"); - - migrationBuilder.DropTable( - name: "photovoltaic_data", - schema: "database"); - - migrationBuilder.AlterDatabase() - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - } + migrationBuilder.DropTable( + name: "calorimetric_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "calorimetric_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "calorimetric_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "CielabColor", + schema: "database"); + + migrationBuilder.DropTable( + name: "FileMetaInformation", + schema: "database"); + + migrationBuilder.DropTable( + name: "get_https_resource_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "hygrothermal_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "hygrothermal_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "hygrothermal_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "optical_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "optical_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "optical_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "photovoltaic_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "photovoltaic_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "photovoltaic_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "get_https_resource", + schema: "database"); + + migrationBuilder.DropTable( + name: "calorimetric_data", + schema: "database"); + + migrationBuilder.DropTable( + name: "hygrothermal_data", + schema: "database"); + + migrationBuilder.DropTable( + name: "optical_data", + schema: "database"); + + migrationBuilder.DropTable( + name: "photovoltaic_data", + schema: "database"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); } } diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs index 01365343..0217bd84 100644 --- a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -3,233 +3,232 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class RenameXminToVersion : Migration { /// - public partial class RenameXminToVersion : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "photovoltaic_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "optical_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "optical_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "hygrothermal_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "calorimetric_data_Sources", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "calorimetric_data_Approvals", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp without time zone"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "photovoltaic_data_Sources", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "photovoltaic_data", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "optical_data_Sources", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "optical_data_Approvals", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "optical_data", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "hygrothermal_data_Sources", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "hygrothermal_data", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "calorimetric_data_Sources", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "calorimetric_data_Approvals", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "calorimetric_data", - type: "timestamp with time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp without time zone"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "photovoltaic_data_Sources", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "photovoltaic_data", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "optical_data_Sources", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "optical_data_Approvals", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "optical_data", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "hygrothermal_data_Sources", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "hygrothermal_data", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Value_DataTimestamp", - schema: "database", - table: "calorimetric_data_Sources", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "Timestamp", - schema: "database", - table: "calorimetric_data_Approvals", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - - migrationBuilder.AlterColumn( - name: "CreatedAt", - schema: "database", - table: "calorimetric_data", - type: "timestamp without time zone", - nullable: false, - oldClrType: typeof(DateTime), - oldType: "timestamp with time zone"); - } + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "photovoltaic_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "optical_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "optical_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "hygrothermal_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Value_DataTimestamp", + schema: "database", + table: "calorimetric_data_Sources", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Timestamp", + schema: "database", + table: "calorimetric_data_Approvals", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp without time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); } } diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs index 2ff935b5..9cf47b0d 100644 --- a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs @@ -3,36 +3,35 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddDataProtectionKeys : Migration { /// - public partial class AddDataProtectionKeys : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "DataProtectionKeys", - schema: "database", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); - }); - } + migrationBuilder.CreateTable( + name: "DataProtectionKeys", + schema: "database", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FriendlyName = table.Column(type: "text", nullable: true), + Xml = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_DataProtectionKeys", x => x.Id); + }); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "DataProtectionKeys", - schema: "database"); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "DataProtectionKeys", + schema: "database"); } } diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs index eb81a185..afafaa93 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -3,180 +3,179 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class OpenIddict : Migration { + private static readonly string[] _applicationIdStatusSubjectAndTypeIndexColumns = ["ApplicationId", "Status", "Subject", "Type"]; + /// - public partial class OpenIddict : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - private static readonly string[] _applicationIdStatusSubjectAndTypeIndexColumns = ["ApplicationId", "Status", "Subject", "Type"]; - - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "OpenIddictApplications", - schema: "database", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ClientId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), - ClientSecret = table.Column(type: "text", nullable: true), - ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - ConsentType = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - DisplayName = table.Column(type: "text", nullable: true), - DisplayNames = table.Column(type: "text", nullable: true), - Permissions = table.Column(type: "text", nullable: true), - PostLogoutRedirectUris = table.Column(type: "text", nullable: true), - Properties = table.Column(type: "text", nullable: true), - RedirectUris = table.Column(type: "text", nullable: true), - Requirements = table.Column(type: "text", nullable: true), - Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictScopes", - schema: "database", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - Description = table.Column(type: "text", nullable: true), - Descriptions = table.Column(type: "text", nullable: true), - DisplayName = table.Column(type: "text", nullable: true), - DisplayNames = table.Column(type: "text", nullable: true), - Name = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), - Properties = table.Column(type: "text", nullable: true), - Resources = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictAuthorizations", - schema: "database", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ApplicationId = table.Column(type: "text", nullable: true), - ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - CreationDate = table.Column(type: "timestamp with time zone", nullable: true), - Properties = table.Column(type: "text", nullable: true), - Scopes = table.Column(type: "text", nullable: true), - Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), - Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); - table.ForeignKey( - name: "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~", - column: x => x.ApplicationId, - principalSchema: "database", - principalTable: "OpenIddictApplications", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "OpenIddictTokens", - schema: "database", - columns: table => new - { - Id = table.Column(type: "text", nullable: false), - ApplicationId = table.Column(type: "text", nullable: true), - AuthorizationId = table.Column(type: "text", nullable: true), - ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - CreationDate = table.Column(type: "timestamp with time zone", nullable: true), - ExpirationDate = table.Column(type: "timestamp with time zone", nullable: true), - Payload = table.Column(type: "text", nullable: true), - Properties = table.Column(type: "text", nullable: true), - RedemptionDate = table.Column(type: "timestamp with time zone", nullable: true), - ReferenceId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), - Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), - Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), - Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); - table.ForeignKey( - name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", - column: x => x.ApplicationId, - principalSchema: "database", - principalTable: "OpenIddictApplications", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", - column: x => x.AuthorizationId, - principalSchema: "database", - principalTable: "OpenIddictAuthorizations", - principalColumn: "Id"); - }); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictApplications_ClientId", - schema: "database", - table: "OpenIddictApplications", - column: "ClientId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", - schema: "database", - table: "OpenIddictAuthorizations", - columns: _applicationIdStatusSubjectAndTypeIndexColumns); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictScopes_Name", - schema: "database", - table: "OpenIddictScopes", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", - schema: "database", - table: "OpenIddictTokens", - columns: _applicationIdStatusSubjectAndTypeIndexColumns); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_AuthorizationId", - schema: "database", - table: "OpenIddictTokens", - column: "AuthorizationId"); - - migrationBuilder.CreateIndex( - name: "IX_OpenIddictTokens_ReferenceId", - schema: "database", - table: "OpenIddictTokens", - column: "ReferenceId", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "OpenIddictScopes", - schema: "database"); - - migrationBuilder.DropTable( - name: "OpenIddictTokens", - schema: "database"); - - migrationBuilder.DropTable( - name: "OpenIddictAuthorizations", - schema: "database"); - - migrationBuilder.DropTable( - name: "OpenIddictApplications", - schema: "database"); - } + migrationBuilder.CreateTable( + name: "OpenIddictApplications", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ClientId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + ClientSecret = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + ConsentType = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + DisplayName = table.Column(type: "text", nullable: true), + DisplayNames = table.Column(type: "text", nullable: true), + Permissions = table.Column(type: "text", nullable: true), + PostLogoutRedirectUris = table.Column(type: "text", nullable: true), + Properties = table.Column(type: "text", nullable: true), + RedirectUris = table.Column(type: "text", nullable: true), + Requirements = table.Column(type: "text", nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictApplications", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictScopes", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Description = table.Column(type: "text", nullable: true), + Descriptions = table.Column(type: "text", nullable: true), + DisplayName = table.Column(type: "text", nullable: true), + DisplayNames = table.Column(type: "text", nullable: true), + Name = table.Column(type: "character varying(200)", maxLength: 200, nullable: true), + Properties = table.Column(type: "text", nullable: true), + Resources = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictScopes", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictAuthorizations", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + Properties = table.Column(type: "text", nullable: true), + Scopes = table.Column(type: "text", nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictAuthorizations", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~", + column: x => x.ApplicationId, + principalSchema: "database", + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "OpenIddictTokens", + schema: "database", + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + ApplicationId = table.Column(type: "text", nullable: true), + AuthorizationId = table.Column(type: "text", nullable: true), + ConcurrencyToken = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + CreationDate = table.Column(type: "timestamp with time zone", nullable: true), + ExpirationDate = table.Column(type: "timestamp with time zone", nullable: true), + Payload = table.Column(type: "text", nullable: true), + Properties = table.Column(type: "text", nullable: true), + RedemptionDate = table.Column(type: "timestamp with time zone", nullable: true), + ReferenceId = table.Column(type: "character varying(100)", maxLength: 100, nullable: true), + Status = table.Column(type: "character varying(50)", maxLength: 50, nullable: true), + Subject = table.Column(type: "character varying(400)", maxLength: 400, nullable: true), + Type = table.Column(type: "character varying(50)", maxLength: 50, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_OpenIddictTokens", x => x.Id); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId", + column: x => x.ApplicationId, + principalSchema: "database", + principalTable: "OpenIddictApplications", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId", + column: x => x.AuthorizationId, + principalSchema: "database", + principalTable: "OpenIddictAuthorizations", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictApplications_ClientId", + schema: "database", + table: "OpenIddictApplications", + column: "ClientId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type", + schema: "database", + table: "OpenIddictAuthorizations", + columns: _applicationIdStatusSubjectAndTypeIndexColumns); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictScopes_Name", + schema: "database", + table: "OpenIddictScopes", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type", + schema: "database", + table: "OpenIddictTokens", + columns: _applicationIdStatusSubjectAndTypeIndexColumns); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_AuthorizationId", + schema: "database", + table: "OpenIddictTokens", + column: "AuthorizationId"); + + migrationBuilder.CreateIndex( + name: "IX_OpenIddictTokens_ReferenceId", + schema: "database", + table: "OpenIddictTokens", + column: "ReferenceId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OpenIddictScopes", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictTokens", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictAuthorizations", + schema: "database"); + + migrationBuilder.DropTable( + name: "OpenIddictApplications", + schema: "database"); } } diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.cs b/backend/src/Migrations/20230427133946_CreateUserTable.cs index 362942c7..1b78e1fd 100644 --- a/backend/src/Migrations/20230427133946_CreateUserTable.cs +++ b/backend/src/Migrations/20230427133946_CreateUserTable.cs @@ -3,37 +3,36 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class CreateUserTable : Migration { /// - public partial class CreateUserTable : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "user", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - Subject = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_user", x => x.Id); - }); - } + migrationBuilder.CreateTable( + name: "user", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + Subject = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_user", x => x.Id); + }); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "user", - schema: "database"); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "user", + schema: "database"); } } diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs index 52c65e9a..085d01f8 100644 --- a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs @@ -2,79 +2,78 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class UpgradeOpenIddict : Migration { /// - public partial class UpgradeOpenIddict : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Email", - schema: "database", - table: "user"); + migrationBuilder.DropColumn( + name: "Email", + schema: "database", + table: "user"); - migrationBuilder.RenameColumn( - name: "Type", - schema: "database", - table: "OpenIddictApplications", - newName: "ClientType"); + migrationBuilder.RenameColumn( + name: "Type", + schema: "database", + table: "OpenIddictApplications", + newName: "ClientType"); - migrationBuilder.AddColumn( - name: "ApplicationType", - schema: "database", - table: "OpenIddictApplications", - type: "character varying(50)", - maxLength: 50, - nullable: true); + migrationBuilder.AddColumn( + name: "ApplicationType", + schema: "database", + table: "OpenIddictApplications", + type: "character varying(50)", + maxLength: 50, + nullable: true); - migrationBuilder.AddColumn( - name: "JsonWebKeySet", - schema: "database", - table: "OpenIddictApplications", - type: "text", - nullable: true); + migrationBuilder.AddColumn( + name: "JsonWebKeySet", + schema: "database", + table: "OpenIddictApplications", + type: "text", + nullable: true); - migrationBuilder.AddColumn( - name: "Settings", - schema: "database", - table: "OpenIddictApplications", - type: "text", - nullable: true); - } + migrationBuilder.AddColumn( + name: "Settings", + schema: "database", + table: "OpenIddictApplications", + type: "text", + nullable: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "ApplicationType", - schema: "database", - table: "OpenIddictApplications"); + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ApplicationType", + schema: "database", + table: "OpenIddictApplications"); - migrationBuilder.DropColumn( - name: "JsonWebKeySet", - schema: "database", - table: "OpenIddictApplications"); + migrationBuilder.DropColumn( + name: "JsonWebKeySet", + schema: "database", + table: "OpenIddictApplications"); - migrationBuilder.DropColumn( - name: "Settings", - schema: "database", - table: "OpenIddictApplications"); + migrationBuilder.DropColumn( + name: "Settings", + schema: "database", + table: "OpenIddictApplications"); - migrationBuilder.RenameColumn( - name: "ClientType", - schema: "database", - table: "OpenIddictApplications", - newName: "Type"); + migrationBuilder.RenameColumn( + name: "ClientType", + schema: "database", + table: "OpenIddictApplications", + newName: "Type"); - migrationBuilder.AddColumn( - name: "Email", - schema: "database", - table: "user", - type: "text", - nullable: false, - defaultValue: ""); - } + migrationBuilder.AddColumn( + name: "Email", + schema: "database", + table: "user", + type: "text", + nullable: false, + defaultValue: ""); } } diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs index 4d17959c..1d5c2644 100644 --- a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs @@ -3,847 +3,846 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddStatementToDataApproval : Migration { /// - public partial class AddStatementToDataApproval : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "standardizer[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + type: "integer", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - - migrationBuilder.AddColumn( - name: "Publication_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_ArXiv", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Authors", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Doi", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Urn", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_WebAddress", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Locator", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "standardizer[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Year", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "integer", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Abstract", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_ArXiv", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Authors", - schema: "database", - table: "optical_data_Approvals", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Doi", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Section", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Title", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Urn", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_WebAddress", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Abstract", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Locator", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Section", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals", - type: "standardizer[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Title", - schema: "database", - table: "optical_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Year", - schema: "database", - table: "optical_data_Approvals", - type: "integer", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_ArXiv", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Authors", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Doi", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Urn", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_WebAddress", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Locator", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "standardizer[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Year", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "integer", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_ArXiv", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Authors", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Doi", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Section", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Title", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_Urn", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Publication_WebAddress", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Locator", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Section", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals", - type: "standardizer[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Title", - schema: "database", - table: "calorimetric_data_Approvals", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Standard_Year", - schema: "database", - table: "calorimetric_data_Approvals", - type: "integer", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Publication_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_ArXiv", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Authors", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Doi", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Section", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Title", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Urn", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_WebAddress", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Locator", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Section", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Title", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Year", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Abstract", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_ArXiv", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Authors", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Doi", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Section", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Title", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Urn", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_WebAddress", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Abstract", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Locator", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Section", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Title", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Year", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_ArXiv", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Authors", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Doi", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Section", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Title", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Urn", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_WebAddress", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Locator", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Section", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Title", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Year", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Abstract", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_ArXiv", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Authors", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Doi", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Section", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Title", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_Urn", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Publication_WebAddress", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Abstract", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Locator", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Section", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Title", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Standard_Year", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - } + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); } } diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs index 615fb132..55125a47 100644 --- a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs @@ -3,185 +3,184 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class MoveEnumTypesToMetabaseSchema : Migration { /// - public partial class MoveEnumTypesToMetabaseSchema : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "photovoltaic_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "optical_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "hygrothermal_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "calorimetric_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "photovoltaic_data_Sources", - type: "database.data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "database.standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "optical_data_Sources", - type: "database.data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals", - type: "database.standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "hygrothermal_data_Sources", - type: "database.data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "database.standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "calorimetric_data_Sources", - type: "database.data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals", - type: "database.standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "standardizer[]", - oldNullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") - .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "photovoltaic_data_Sources", - type: "data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "database.data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "database.standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "optical_data_Sources", - type: "data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "database.data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals", - type: "standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "database.standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "hygrothermal_data_Sources", - type: "data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "database.data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "database.standardizer[]", - oldNullable: true); - - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "calorimetric_data_Sources", - type: "data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "database.data_kind"); - - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals", - type: "standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "database.standardizer[]", - oldNullable: true); - } + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .Annotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .Annotation("Npgsql:PostgresExtension:pgcrypto", ",,") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") + .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "photovoltaic_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "optical_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "hygrothermal_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "calorimetric_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); + + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); } } diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index 3157c737..2126f6af 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -6,193 +6,192 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddGeometricData : Migration { /// - public partial class AddGeometricData : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data"); + + migrationBuilder.AddColumn( + name: "GeometricDataId", + schema: "database", + table: "get_https_resource", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "geometric_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + Thicknesses = table.Column(type: "double precision[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Approvals", + schema: "database", + columns: table => new + { + GeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ApproverId = table.Column(type: "uuid", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Response = table.Column(type: "text", nullable: false), + Publication_Authors = table.Column(type: "text[]", nullable: true), + Publication_Doi = table.Column(type: "text", nullable: true), + Publication_ArXiv = table.Column(type: "text", nullable: true), + Publication_Urn = table.Column(type: "text", nullable: true), + Publication_WebAddress = table.Column(type: "text", nullable: true), + Publication_Title = table.Column(type: "text", nullable: true), + Publication_Abstract = table.Column(type: "text", nullable: true), + Publication_Section = table.Column(type: "text", nullable: true), + Standard_Year = table.Column(type: "integer", nullable: true), + Standard_Numeration_Prefix = table.Column(type: "text", nullable: true), + Standard_Numeration_MainNumber = table.Column(type: "text", nullable: true), + Standard_Numeration_Suffix = table.Column(type: "text", nullable: true), + Standard_Standardizers = table.Column(type: "database.standardizer[]", nullable: true), + Standard_Locator = table.Column(type: "text", nullable: true), + Standard_Title = table.Column(type: "text", nullable: true), + Standard_Abstract = table.Column(type: "text", nullable: true), + Standard_Section = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Approvals", x => new { x.GeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Approvals_geometric_data_GeometricDataId", + column: x => x.GeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Arguments", x => new { x.AppliedMethodGeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Arguments_geometric_data_AppliedMethodGeomet~", + column: x => x.AppliedMethodGeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "geometric_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp with time zone", nullable: false), + Value_DataKind = table.Column(type: "database.data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_geometric_data_Sources", x => new { x.AppliedMethodGeometricDataId, x.Id }); + table.ForeignKey( + name: "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~", + column: x => x.AppliedMethodGeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId", + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data"); - - migrationBuilder.AddColumn( - name: "GeometricDataId", - schema: "database", - table: "get_https_resource", - type: "uuid", - nullable: true); - - migrationBuilder.CreateTable( - name: "geometric_data", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - Thicknesses = table.Column(type: "double precision[]", nullable: false), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false), - Locale = table.Column(type: "text", nullable: false), - ComponentId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: true), - Description = table.Column(type: "text", nullable: true), - Warnings = table.Column(type: "text[]", nullable: false), - CreatorId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_geometric_data", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "geometric_data_Approvals", - schema: "database", - columns: table => new - { - GeometricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - ApproverId = table.Column(type: "uuid", nullable: false), - Timestamp = table.Column(type: "timestamp with time zone", nullable: false), - Signature = table.Column(type: "text", nullable: false), - KeyFingerprint = table.Column(type: "text", nullable: false), - Query = table.Column(type: "text", nullable: false), - Response = table.Column(type: "text", nullable: false), - Publication_Authors = table.Column(type: "text[]", nullable: true), - Publication_Doi = table.Column(type: "text", nullable: true), - Publication_ArXiv = table.Column(type: "text", nullable: true), - Publication_Urn = table.Column(type: "text", nullable: true), - Publication_WebAddress = table.Column(type: "text", nullable: true), - Publication_Title = table.Column(type: "text", nullable: true), - Publication_Abstract = table.Column(type: "text", nullable: true), - Publication_Section = table.Column(type: "text", nullable: true), - Standard_Year = table.Column(type: "integer", nullable: true), - Standard_Numeration_Prefix = table.Column(type: "text", nullable: true), - Standard_Numeration_MainNumber = table.Column(type: "text", nullable: true), - Standard_Numeration_Suffix = table.Column(type: "text", nullable: true), - Standard_Standardizers = table.Column(type: "database.standardizer[]", nullable: true), - Standard_Locator = table.Column(type: "text", nullable: true), - Standard_Title = table.Column(type: "text", nullable: true), - Standard_Abstract = table.Column(type: "text", nullable: true), - Standard_Section = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_geometric_data_Approvals", x => new { x.GeometricDataId, x.Id }); - table.ForeignKey( - name: "FK_geometric_data_Approvals_geometric_data_GeometricDataId", - column: x => x.GeometricDataId, - principalSchema: "database", - principalTable: "geometric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "geometric_data_Arguments", - schema: "database", - columns: table => new - { - AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "jsonb", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_geometric_data_Arguments", x => new { x.AppliedMethodGeometricDataId, x.Id }); - table.ForeignKey( - name: "FK_geometric_data_Arguments_geometric_data_AppliedMethodGeomet~", - column: x => x.AppliedMethodGeometricDataId, - principalSchema: "database", - principalTable: "geometric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "geometric_data_Sources", - schema: "database", - columns: table => new - { - AppliedMethodGeometricDataId = table.Column(type: "uuid", nullable: false), - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Value_DataId = table.Column(type: "uuid", nullable: false), - Value_DataTimestamp = table.Column(type: "timestamp with time zone", nullable: false), - Value_DataKind = table.Column(type: "database.data_kind", nullable: false), - Value_DatabaseId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_geometric_data_Sources", x => new { x.AppliedMethodGeometricDataId, x.Id }); - table.ForeignKey( - name: "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~", - column: x => x.AppliedMethodGeometricDataId, - principalSchema: "database", - principalTable: "geometric_data", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_get_https_resource_GeometricDataId", - schema: "database", - table: "get_https_resource", - column: "GeometricDataId"); - - migrationBuilder.AddForeignKey( - name: "FK_get_https_resource_geometric_data_GeometricDataId", - schema: "database", - table: "get_https_resource", - column: "GeometricDataId", - principalSchema: "database", - principalTable: "geometric_data", - principalColumn: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_get_https_resource_geometric_data_GeometricDataId", - schema: "database", - table: "get_https_resource"); - - migrationBuilder.DropTable( - name: "geometric_data_Approvals", - schema: "database"); - - migrationBuilder.DropTable( - name: "geometric_data_Arguments", - schema: "database"); - - migrationBuilder.DropTable( - name: "geometric_data_Sources", - schema: "database"); - - migrationBuilder.DropTable( - name: "geometric_data", - schema: "database"); - - migrationBuilder.DropIndex( - name: "IX_get_https_resource_GeometricDataId", - schema: "database", - table: "get_https_resource"); - - migrationBuilder.DropColumn( - name: "GeometricDataId", - schema: "database", - table: "get_https_resource"); - - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); - } + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropTable( + name: "geometric_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "geometric_data", + schema: "database"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropColumn( + name: "GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); } } diff --git a/backend/src/Migrations/20250129163501_UpgradeToNet9.cs b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs index 84446de7..8d11504d 100644 --- a/backend/src/Migrations/20250129163501_UpgradeToNet9.cs +++ b/backend/src/Migrations/20250129163501_UpgradeToNet9.cs @@ -3,63 +3,62 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class UpgradeToNet9 : Migration { /// - public partial class UpgradeToNet9 : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "geometric_data_Sources", - type: "database.data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "data_kind"); + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "geometric_data_Sources", + type: "database.data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "data_kind"); - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "geometric_data_Approvals", - type: "database.standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "standardizer[]", - oldNullable: true); - } + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + type: "database.standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "standardizer[]", + oldNullable: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") - .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data"); + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data"); - migrationBuilder.AlterColumn( - name: "Value_DataKind", - schema: "database", - table: "geometric_data_Sources", - type: "data_kind", - nullable: false, - oldClrType: typeof(DataKind), - oldType: "database.data_kind"); + migrationBuilder.AlterColumn( + name: "Value_DataKind", + schema: "database", + table: "geometric_data_Sources", + type: "data_kind", + nullable: false, + oldClrType: typeof(DataKind), + oldType: "database.data_kind"); - migrationBuilder.AlterColumn( - name: "Standard_Standardizers", - schema: "database", - table: "geometric_data_Approvals", - type: "standardizer[]", - nullable: true, - oldClrType: typeof(Standardizer[]), - oldType: "database.standardizer[]", - oldNullable: true); - } + migrationBuilder.AlterColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + type: "standardizer[]", + nullable: true, + oldClrType: typeof(Standardizer[]), + oldType: "database.standardizer[]", + oldNullable: true); } } \ No newline at end of file diff --git a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs index f1714e66..c18b2942 100644 --- a/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs +++ b/backend/src/Migrations/20250213155333_AddTypeSubtypeAndCoatedSideToData.cs @@ -3,63 +3,62 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddTypeSubtypeAndCoatedSideToData : Migration { /// - public partial class AddTypeSubtypeAndCoatedSideToData : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") - .Annotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") - .Annotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") + .Annotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .Annotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); - migrationBuilder.AddColumn( - name: "CoatedSide", - schema: "database", - table: "optical_data", - type: "database.coated_side", - nullable: true); + migrationBuilder.AddColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data", + type: "database.coated_side", + nullable: true); - migrationBuilder.AddColumn( - name: "Subtype", - schema: "database", - table: "optical_data", - type: "database.optical_component_subtype", - nullable: true); + migrationBuilder.AddColumn( + name: "Subtype", + schema: "database", + table: "optical_data", + type: "database.optical_component_subtype", + nullable: true); - migrationBuilder.AddColumn( - name: "Type", - schema: "database", - table: "optical_data", - type: "database.optical_component_type", - nullable: true); - } + migrationBuilder.AddColumn( + name: "Type", + schema: "database", + table: "optical_data", + type: "database.optical_component_type", + nullable: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "CoatedSide", - schema: "database", - table: "optical_data"); + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data"); - migrationBuilder.DropColumn( - name: "Subtype", - schema: "database", - table: "optical_data"); + migrationBuilder.DropColumn( + name: "Subtype", + schema: "database", + table: "optical_data"); - migrationBuilder.DropColumn( - name: "Type", - schema: "database", - table: "optical_data"); + migrationBuilder.DropColumn( + name: "Type", + schema: "database", + table: "optical_data"); - migrationBuilder.AlterDatabase() - .OldAnnotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") - .OldAnnotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") - .OldAnnotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); - } + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") + .OldAnnotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .OldAnnotation("Npgsql:Enum:database.optical_component_type", "glazing,shading"); } } \ No newline at end of file diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs index e2df8ce1..9ca698a4 100644 --- a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs @@ -2,37 +2,36 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class MakeDescriptionOptional : Migration { /// - public partial class MakeDescriptionOptional : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Description", - schema: "database", - table: "get_https_resource", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "text"); - } + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Description", - schema: "database", - table: "get_https_resource", - type: "text", - nullable: false, - defaultValue: "", - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Description", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); } } diff --git a/backend/src/Migrations/20250331141152_AddAccessRights.cs b/backend/src/Migrations/20250331141152_AddAccessRights.cs index 1cad1d68..6dc2799b 100644 --- a/backend/src/Migrations/20250331141152_AddAccessRights.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.cs @@ -4,339 +4,338 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddAccessRights : Migration { /// - public partial class AddAccessRights : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "photovoltaic_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "optical_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "hygrothermal_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "geometric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "geometric_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Query", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Signature", + schema: "database", + table: "calorimetric_data", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_Timestamp", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.CreateTable( + name: "access_rights", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + InstitutionId = table.Column(type: "uuid", nullable: false), + AllowedUserCount = table.Column(type: "bigint", nullable: true), + AllowedDatasetsPerTime = table.Column(type: "bigint", nullable: true), + Period = table.Column(type: "interval", nullable: false), + UserAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_access_rights", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "photovoltaic_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Query", - schema: "database", - table: "photovoltaic_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Response", - schema: "database", - table: "photovoltaic_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Signature", - schema: "database", - table: "photovoltaic_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Timestamp", - schema: "database", - table: "photovoltaic_data", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "optical_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Query", - schema: "database", - table: "optical_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Response", - schema: "database", - table: "optical_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Signature", - schema: "database", - table: "optical_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Timestamp", - schema: "database", - table: "optical_data", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "hygrothermal_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Query", - schema: "database", - table: "hygrothermal_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Response", - schema: "database", - table: "hygrothermal_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Signature", - schema: "database", - table: "hygrothermal_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Timestamp", - schema: "database", - table: "hygrothermal_data", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "geometric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Query", - schema: "database", - table: "geometric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Response", - schema: "database", - table: "geometric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Signature", - schema: "database", - table: "geometric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Timestamp", - schema: "database", - table: "geometric_data", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "calorimetric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Query", - schema: "database", - table: "calorimetric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Response", - schema: "database", - table: "calorimetric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Signature", - schema: "database", - table: "calorimetric_data", - type: "text", - nullable: true); - - migrationBuilder.AddColumn( - name: "Approval_Timestamp", - schema: "database", - table: "calorimetric_data", - type: "timestamp with time zone", - nullable: true); - - migrationBuilder.CreateTable( - name: "access_rights", - schema: "database", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), - InstitutionId = table.Column(type: "uuid", nullable: false), - AllowedUserCount = table.Column(type: "bigint", nullable: true), - AllowedDatasetsPerTime = table.Column(type: "bigint", nullable: true), - Period = table.Column(type: "interval", nullable: false), - UserAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), - xmin = table.Column(type: "xid", rowVersion: true, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_access_rights", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "access_rights", - schema: "database"); - - migrationBuilder.DropColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Approval_Query", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Approval_Response", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Approval_Signature", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Approval_Timestamp", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Approval_Query", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Approval_Response", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Approval_Signature", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Approval_Timestamp", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Approval_Query", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Approval_Response", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Approval_Signature", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Approval_Timestamp", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Query", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Response", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Signature", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Timestamp", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Approval_KeyFingerprint", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Query", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Response", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Signature", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "Approval_Timestamp", - schema: "database", - table: "calorimetric_data"); - } + migrationBuilder.DropTable( + name: "access_rights", + schema: "database"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_KeyFingerprint", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Query", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Signature", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "Approval_Timestamp", + schema: "database", + table: "calorimetric_data"); } } diff --git a/backend/src/Migrations/20250527144412_AddDataAccessRights.cs b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs index f978e818..af7403ee 100644 --- a/backend/src/Migrations/20250527144412_AddDataAccessRights.cs +++ b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs @@ -4,197 +4,196 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddDataAccessRights : Migration { /// - public partial class AddDataAccessRights : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "photovoltaic_data", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "photovoltaic_data", - type: "uuid[]", - nullable: true); - - migrationBuilder.AddColumn>( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "photovoltaic_data", - type: "jsonb", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "optical_data", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "optical_data", - type: "uuid[]", - nullable: true); - - migrationBuilder.AddColumn>( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "optical_data", - type: "jsonb", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "hygrothermal_data", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "hygrothermal_data", - type: "uuid[]", - nullable: true); - - migrationBuilder.AddColumn>( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "hygrothermal_data", - type: "jsonb", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "geometric_data", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "geometric_data", - type: "uuid[]", - nullable: true); - - migrationBuilder.AddColumn>( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "geometric_data", - type: "jsonb", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "calorimetric_data", - type: "text[]", - nullable: true); - - migrationBuilder.AddColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "calorimetric_data", - type: "uuid[]", - nullable: true); - - migrationBuilder.AddColumn>( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "calorimetric_data", - type: "jsonb", - nullable: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedApplications", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedInstitutions", - schema: "database", - table: "calorimetric_data"); - - migrationBuilder.DropColumn( - name: "DataAccessRights_AllowedUserAndQuantity", - schema: "database", - table: "calorimetric_data"); - } + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data"); } } diff --git a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs index ad0a81a0..f9cb6e21 100644 --- a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs +++ b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs @@ -2,51 +2,50 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class MoveInstitutionAccessRights : Migration { /// - public partial class MoveInstitutionAccessRights : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropPrimaryKey( - name: "PK_access_rights", - schema: "database", - table: "access_rights"); - - migrationBuilder.RenameTable( - name: "access_rights", - schema: "database", - newName: "institution_access_rights", - newSchema: "database"); - - migrationBuilder.AddPrimaryKey( - name: "PK_institution_access_rights", - schema: "database", - table: "institution_access_rights", - column: "Id"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropPrimaryKey( - name: "PK_institution_access_rights", - schema: "database", - table: "institution_access_rights"); - - migrationBuilder.RenameTable( - name: "institution_access_rights", - schema: "database", - newName: "access_rights", - newSchema: "database"); - - migrationBuilder.AddPrimaryKey( - name: "PK_access_rights", - schema: "database", - table: "access_rights", - column: "Id"); - } + migrationBuilder.DropPrimaryKey( + name: "PK_access_rights", + schema: "database", + table: "access_rights"); + + migrationBuilder.RenameTable( + name: "access_rights", + schema: "database", + newName: "institution_access_rights", + newSchema: "database"); + + migrationBuilder.AddPrimaryKey( + name: "PK_institution_access_rights", + schema: "database", + table: "institution_access_rights", + column: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_institution_access_rights", + schema: "database", + table: "institution_access_rights"); + + migrationBuilder.RenameTable( + name: "institution_access_rights", + schema: "database", + newName: "access_rights", + newSchema: "database"); + + migrationBuilder.AddPrimaryKey( + name: "PK_access_rights", + schema: "database", + table: "access_rights", + column: "Id"); } } diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs index f72624bf..abf707d4 100644 --- a/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs @@ -2,1102 +2,1101 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class ModelReferenceProperly : Migration { /// - public partial class ModelReferenceProperly : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Statement_Publication_Abstract"); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "optical_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "geometric_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + + migrationBuilder.AddColumn( + name: "Statement_Exists", + schema: "database", + table: "calorimetric_data_Approvals", + type: "boolean", + nullable: false, + defaultValue: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Standard_Year", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Standard_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Standard_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Standard_Locator", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Standard_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Publication_WebAddress", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Publication_Urn", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Publication_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Publication_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Publication_Doi", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Publication_Authors", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Publication_ArXiv", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Publication_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Statement_Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Standard_Year", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Standard_Title", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Standard_Section", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Standard_Locator", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Standard_Abstract", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Publication_WebAddress", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Publication_Urn", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Publication_Title", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Publication_Section", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Publication_Doi", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Publication_Authors", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Publication_ArXiv", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Publication_Abstract", - schema: "database", - table: "optical_data_Approvals", - newName: "Statement_Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Standard_Year", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Standard_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Standard_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Standard_Locator", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Standard_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Publication_WebAddress", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Publication_Urn", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Publication_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Publication_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Publication_Doi", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Publication_Authors", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Publication_ArXiv", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Publication_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Statement_Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Standard_Year", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Standard_Title", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Standard_Standardizers", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Standard_Section", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Standard_Locator", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Standard_Abstract", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Publication_WebAddress", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Publication_Urn", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Publication_Title", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Publication_Section", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Publication_Doi", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Publication_Authors", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Publication_ArXiv", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Publication_Abstract", - schema: "database", - table: "geometric_data_Approvals", - newName: "Statement_Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Standard_Year", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Standard_Title", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Standard_Section", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Suffix", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_Prefix", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Standard_Numeration_MainNumber", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Standard_Locator", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Standard_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Publication_WebAddress", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Publication_Urn", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Publication_Title", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Publication_Section", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Publication_Doi", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Publication_Authors", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Publication_ArXiv", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Publication_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Statement_Publication_Abstract"); - - migrationBuilder.AddColumn( - name: "Statement_Exists", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "boolean", - nullable: false, - defaultValue: true); - - migrationBuilder.AddColumn( - name: "Statement_Exists", - schema: "database", - table: "optical_data_Approvals", - type: "boolean", - nullable: false, - defaultValue: true); - - migrationBuilder.AddColumn( - name: "Statement_Exists", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "boolean", - nullable: false, - defaultValue: true); - - migrationBuilder.AddColumn( - name: "Statement_Exists", - schema: "database", - table: "geometric_data_Approvals", - type: "boolean", - nullable: false, - defaultValue: true); - - migrationBuilder.AddColumn( - name: "Statement_Exists", - schema: "database", - table: "calorimetric_data_Approvals", - type: "boolean", - nullable: false, - defaultValue: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Statement_Exists", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Statement_Exists", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Statement_Exists", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Statement_Exists", - schema: "database", - table: "geometric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Statement_Exists", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Year", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Standardizers", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Suffix", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Prefix", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_MainNumber", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Locator", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_WebAddress", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Urn", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Title", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Section", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Doi", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Authors", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_ArXiv", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Abstract", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Year", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Title", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Standardizers", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Section", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Suffix", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Prefix", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_MainNumber", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Locator", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Abstract", - schema: "database", - table: "optical_data_Approvals", - newName: "Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_WebAddress", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Urn", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Title", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Section", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Doi", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Authors", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_ArXiv", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Abstract", - schema: "database", - table: "optical_data_Approvals", - newName: "Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Year", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Standardizers", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Suffix", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Prefix", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_MainNumber", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Locator", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_WebAddress", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Urn", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Title", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Section", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Doi", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Authors", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_ArXiv", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Abstract", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Year", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Title", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Standardizers", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Section", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Suffix", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Prefix", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_MainNumber", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Locator", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Abstract", - schema: "database", - table: "geometric_data_Approvals", - newName: "Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_WebAddress", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Urn", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Title", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Section", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Doi", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Authors", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_ArXiv", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Abstract", - schema: "database", - table: "geometric_data_Approvals", - newName: "Publication_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Year", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Year"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Title", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Standardizers", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Standardizers"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Section", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Suffix", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Numeration_Suffix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_Prefix", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Numeration_Prefix"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Numeration_MainNumber", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Numeration_MainNumber"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Locator", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Locator"); - - migrationBuilder.RenameColumn( - name: "Statement_Standard_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Standard_Abstract"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_WebAddress", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_WebAddress"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Urn", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Urn"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Title", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Title"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Section", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Section"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Doi", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Doi"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Authors", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Authors"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_ArXiv", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_ArXiv"); - - migrationBuilder.RenameColumn( - name: "Statement_Publication_Abstract", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Publication_Abstract"); - } + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "geometric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Statement_Exists", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "optical_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "geometric_data_Approvals", + newName: "Publication_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Year", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Year"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Standardizers", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Standardizers"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Suffix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_Suffix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_Prefix", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_Prefix"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Numeration_MainNumber", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Numeration_MainNumber"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Locator", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Locator"); + + migrationBuilder.RenameColumn( + name: "Statement_Standard_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Standard_Abstract"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_WebAddress", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_WebAddress"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Urn", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Urn"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Title", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Title"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Section", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Section"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Doi", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Doi"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Authors", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Authors"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_ArXiv", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_ArXiv"); + + migrationBuilder.RenameColumn( + name: "Statement_Publication_Abstract", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Publication_Abstract"); } } \ No newline at end of file diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs index c480fb7c..ddd221bb 100644 --- a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs @@ -2,77 +2,76 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddRequiredPropertyExistsToPublication : Migration { /// - public partial class AddRequiredPropertyExistsToPublication : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "boolean", - nullable: true); + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "boolean", + nullable: true); - migrationBuilder.AddColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "optical_data_Approvals", - type: "boolean", - nullable: true); + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "optical_data_Approvals", + type: "boolean", + nullable: true); - migrationBuilder.AddColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "boolean", - nullable: true); + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "boolean", + nullable: true); - migrationBuilder.AddColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "geometric_data_Approvals", - type: "boolean", - nullable: true); + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "geometric_data_Approvals", + type: "boolean", + nullable: true); - migrationBuilder.AddColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "calorimetric_data_Approvals", - type: "boolean", - nullable: true); - } + migrationBuilder.AddColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "calorimetric_data_Approvals", + type: "boolean", + nullable: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "photovoltaic_data_Approvals"); + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "photovoltaic_data_Approvals"); - migrationBuilder.DropColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "optical_data_Approvals"); + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "optical_data_Approvals"); - migrationBuilder.DropColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "hygrothermal_data_Approvals"); + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "hygrothermal_data_Approvals"); - migrationBuilder.DropColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "geometric_data_Approvals"); + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "geometric_data_Approvals"); - migrationBuilder.DropColumn( - name: "Statement_Publication_Exists", - schema: "database", - table: "calorimetric_data_Approvals"); - } + migrationBuilder.DropColumn( + name: "Statement_Publication_Exists", + schema: "database", + table: "calorimetric_data_Approvals"); } } diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs index adc42534..3f7955d8 100644 --- a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs @@ -2,41 +2,40 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class UpgradeOpenIddictToVersion7 : Migration { /// - public partial class UpgradeOpenIddictToVersion7 : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Type", - schema: "database", - table: "OpenIddictTokens", - type: "character varying(150)", - maxLength: 150, - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(50)", - oldMaxLength: 50, - oldNullable: true); - } + migrationBuilder.AlterColumn( + name: "Type", + schema: "database", + table: "OpenIddictTokens", + type: "character varying(150)", + maxLength: 150, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(50)", + oldMaxLength: 50, + oldNullable: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "Type", - schema: "database", - table: "OpenIddictTokens", - type: "character varying(50)", - maxLength: 50, - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(150)", - oldMaxLength: 150, - oldNullable: true); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Type", + schema: "database", + table: "OpenIddictTokens", + type: "character varying(50)", + maxLength: 50, + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(150)", + oldMaxLength: 150, + oldNullable: true); } } diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs index f3cfe885..015656ef 100644 --- a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs @@ -2,29 +2,28 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class MakeInstitutionIdOfAccessRightsUnique : Migration { /// - public partial class MakeInstitutionIdOfAccessRightsUnique : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_institution_access_rights_InstitutionId", - schema: "database", - table: "institution_access_rights", - column: "InstitutionId", - unique: true); - } + migrationBuilder.CreateIndex( + name: "IX_institution_access_rights_InstitutionId", + schema: "database", + table: "institution_access_rights", + column: "InstitutionId", + unique: true); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_institution_access_rights_InstitutionId", - schema: "database", - table: "institution_access_rights"); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_institution_access_rights_InstitutionId", + schema: "database", + table: "institution_access_rights"); } } diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs index 97eca89c..64b87b19 100644 --- a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs @@ -3,147 +3,146 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class AddQueryVariablesToApprovals : Migration { /// - public partial class AddQueryVariablesToApprovals : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "photovoltaic_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "hygrothermal_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "geometric_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Variables", + schema: "database", + table: "calorimetric_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Approval_Variables", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "Variables", - schema: "database", - table: "photovoltaic_data_Approvals", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Approval_Variables", - schema: "database", - table: "photovoltaic_data", - type: "jsonb", - nullable: true, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Variables", - schema: "database", - table: "optical_data_Approvals", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Approval_Variables", - schema: "database", - table: "optical_data", - type: "jsonb", - nullable: true, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Variables", - schema: "database", - table: "hygrothermal_data_Approvals", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Approval_Variables", - schema: "database", - table: "hygrothermal_data", - type: "jsonb", - nullable: true, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Variables", - schema: "database", - table: "geometric_data_Approvals", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Approval_Variables", - schema: "database", - table: "geometric_data", - type: "jsonb", - nullable: true, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Variables", - schema: "database", - table: "calorimetric_data_Approvals", - type: "jsonb", - nullable: false, - defaultValueSql: "'{}'"); - - migrationBuilder.AddColumn( - name: "Approval_Variables", - schema: "database", - table: "calorimetric_data", - type: "jsonb", - nullable: true, - defaultValueSql: "'{}'"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "Variables", - schema: "database", - table: "photovoltaic_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Approval_Variables", - schema: "database", - table: "photovoltaic_data"); - - migrationBuilder.DropColumn( - name: "Variables", - schema: "database", - table: "optical_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Approval_Variables", - schema: "database", - table: "optical_data"); - - migrationBuilder.DropColumn( - name: "Variables", - schema: "database", - table: "hygrothermal_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Approval_Variables", - schema: "database", - table: "hygrothermal_data"); - - migrationBuilder.DropColumn( - name: "Variables", - schema: "database", - table: "geometric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Approval_Variables", - schema: "database", - table: "geometric_data"); - - migrationBuilder.DropColumn( - name: "Variables", - schema: "database", - table: "calorimetric_data_Approvals"); - - migrationBuilder.DropColumn( - name: "Approval_Variables", - schema: "database", - table: "calorimetric_data"); - } + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "photovoltaic_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "hygrothermal_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "geometric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Variables", + schema: "database", + table: "calorimetric_data_Approvals"); + + migrationBuilder.DropColumn( + name: "Approval_Variables", + schema: "database", + table: "calorimetric_data"); } } diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs index e3852e8e..3bb8a4ca 100644 --- a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs @@ -2,137 +2,136 @@ #nullable disable -namespace Database.Migrations +namespace Database.Migrations; + +/// +public partial class RenameResponsePropertyOfApprovalsToMessage : Migration { /// - public partial class RenameResponsePropertyOfApprovalsToMessage : Migration + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "photovoltaic_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "optical_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "optical_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "hygrothermal_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "geometric_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "geometric_data", + newName: "Approval_Message"); + + migrationBuilder.RenameColumn( + name: "Response", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Message"); + + migrationBuilder.RenameColumn( + name: "Approval_Response", + schema: "database", + table: "calorimetric_data", + newName: "Approval_Message"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Response", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Message"); - - migrationBuilder.RenameColumn( - name: "Approval_Response", - schema: "database", - table: "photovoltaic_data", - newName: "Approval_Message"); - - migrationBuilder.RenameColumn( - name: "Response", - schema: "database", - table: "optical_data_Approvals", - newName: "Message"); - - migrationBuilder.RenameColumn( - name: "Approval_Response", - schema: "database", - table: "optical_data", - newName: "Approval_Message"); - - migrationBuilder.RenameColumn( - name: "Response", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Message"); - - migrationBuilder.RenameColumn( - name: "Approval_Response", - schema: "database", - table: "hygrothermal_data", - newName: "Approval_Message"); - - migrationBuilder.RenameColumn( - name: "Response", - schema: "database", - table: "geometric_data_Approvals", - newName: "Message"); - - migrationBuilder.RenameColumn( - name: "Approval_Response", - schema: "database", - table: "geometric_data", - newName: "Approval_Message"); - - migrationBuilder.RenameColumn( - name: "Response", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Message"); - - migrationBuilder.RenameColumn( - name: "Approval_Response", - schema: "database", - table: "calorimetric_data", - newName: "Approval_Message"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.RenameColumn( - name: "Message", - schema: "database", - table: "photovoltaic_data_Approvals", - newName: "Response"); - - migrationBuilder.RenameColumn( - name: "Approval_Message", - schema: "database", - table: "photovoltaic_data", - newName: "Approval_Response"); - - migrationBuilder.RenameColumn( - name: "Message", - schema: "database", - table: "optical_data_Approvals", - newName: "Response"); - - migrationBuilder.RenameColumn( - name: "Approval_Message", - schema: "database", - table: "optical_data", - newName: "Approval_Response"); - - migrationBuilder.RenameColumn( - name: "Message", - schema: "database", - table: "hygrothermal_data_Approvals", - newName: "Response"); - - migrationBuilder.RenameColumn( - name: "Approval_Message", - schema: "database", - table: "hygrothermal_data", - newName: "Approval_Response"); - - migrationBuilder.RenameColumn( - name: "Message", - schema: "database", - table: "geometric_data_Approvals", - newName: "Response"); - - migrationBuilder.RenameColumn( - name: "Approval_Message", - schema: "database", - table: "geometric_data", - newName: "Approval_Response"); - - migrationBuilder.RenameColumn( - name: "Message", - schema: "database", - table: "calorimetric_data_Approvals", - newName: "Response"); - - migrationBuilder.RenameColumn( - name: "Approval_Message", - schema: "database", - table: "calorimetric_data", - newName: "Approval_Response"); - } + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "photovoltaic_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "photovoltaic_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "optical_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "optical_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "hygrothermal_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "hygrothermal_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "geometric_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "geometric_data", + newName: "Approval_Response"); + + migrationBuilder.RenameColumn( + name: "Message", + schema: "database", + table: "calorimetric_data_Approvals", + newName: "Response"); + + migrationBuilder.RenameColumn( + name: "Approval_Message", + schema: "database", + table: "calorimetric_data", + newName: "Approval_Response"); } } From 6943edde1f551108e7c0ab120a2de7e80cf1e3b3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 15:53:24 +0200 Subject: [PATCH 405/878] Remove unnecessary fetching of meta information about the database --- .../ResponseApprovalMutations.cs | 24 ----------------- backend/src/Services/DatabaseService.cs | 27 ------------------- backend/src/Startup.cs | 7 +++-- 3 files changed, 3 insertions(+), 55 deletions(-) delete mode 100644 backend/src/Services/DatabaseService.cs diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs index 4f5cbfe1..e0a23fa1 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs @@ -33,7 +33,6 @@ public sealed class ResponseApprovalMutations public async Task CreateResponseApprovalsAsync( ApplicationDbContext context, UserService userService, - DatabaseService databaseService, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken @@ -50,17 +49,6 @@ CancellationToken cancellationToken ) ); } - var database = await databaseService.QueryDatabase(cancellationToken); - if (database is null) - { - return new CreateResponseApprovalsPayload( - new CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode.UNKNOWN_DATABASE, - $"The database could not be identified.", - [] - ) - ); - } if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateResponseApprovalsPayload( @@ -111,7 +99,6 @@ await Parallel.ForEachAsync( public async Task UpdateResponseApprovalsAsync( ApplicationDbContext context, UserService userService, - DatabaseService databaseService, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken @@ -128,17 +115,6 @@ CancellationToken cancellationToken ) ); } - var database = await databaseService.QueryDatabase(cancellationToken); - if (database is null) - { - return new UpdateResponseApprovalsPayload( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UNKNOWN_DATABASE, - $"The database could not be identified.", - [] - ) - ); - } if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateResponseApprovalsPayload( diff --git a/backend/src/Services/DatabaseService.cs b/backend/src/Services/DatabaseService.cs deleted file mode 100644 index 9c58c99d..00000000 --- a/backend/src/Services/DatabaseService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.ApiRequests.Dto; -using Microsoft.AspNetCore.Http; - -namespace Database.Services; - -public sealed class DatabaseService( - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpContextAccessor httpContextAccessor, - IHttpClientFactory httpClientFactory -) -{ - internal Task QueryDatabase(CancellationToken cancellationToken) - { - return DatabaseApi.RequestDatabase( - appSettings.DatabaseId, - appSettings, - apiRequestService, - httpClientFactory, - httpContextAccessor, - cancellationToken); - } -} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 65dff411..23def29e 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -235,15 +235,14 @@ private static void ConfigureHttpClientServices(IServiceCollection services, IWe public static void ConfigureCustomServices(IServiceCollection services) { - services.AddSingleton(); - services.AddSingleton(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } public void Configure(WebApplication app) From aba43b607d5d26d947899852c5c65b1422edfa3c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 16:05:36 +0200 Subject: [PATCH 406/878] Add `approverId` to response approvals (and add and remove some generics) --- .env.production.sample | 5 ++-- .env.sample | 1 + .env.staging.sample | 10 +++++++ backend/src/ApiRequests/DataApi.cs | 4 +-- backend/src/AppSettings.cs | 1 + backend/src/Data/IApproval.cs | 1 + backend/src/Data/ResponseApproval.cs | 4 ++- .../src/Services/ResponseApprovalService.cs | 28 +++++++++++++------ docker-compose.production.yml | 1 + docker-compose.yml | 1 + 10 files changed, 41 insertions(+), 15 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 7f3e3375..762958d5 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -5,12 +5,13 @@ HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org -DATABASE_ID= +DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 +OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= -GNUPG_PRIVATEKEY_FILE_NAME= +GNUPG_PRIVATEKEY_FILE_NAME=private.gpg GNUPG_PRIVATEKEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index 9d9454d3..3deff956 100644 --- a/.env.sample +++ b/.env.sample @@ -8,6 +8,7 @@ METABASE_HOST=local.buildingenvelopedata.org METABASE_HTTPS_PORT=4041 DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 +OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_SECRET=secret VERIFICATION_CODE=secret diff --git a/.env.staging.sample b/.env.staging.sample index 026f30db..bc1e9169 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -5,7 +5,17 @@ HTTP_PORT=9000 METABASE_HOST=staging.buildingenvelopedata.org +DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 +OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 + OPEN_ID_CONNECT_CLIENT_SECRET= +VERIFICATION_CODE= + +GNUPG_PRIVATEKEY_FILE_NAME=private.gpg +GNUPG_PRIVATEKEY_PASSPHRASE= + +JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= +JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= SMTP_HOST=email SMTP_PORT=25 diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index 7662d09f..dff89c37 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Net.Http; using System.Text.Json; using System.Threading; @@ -63,7 +62,7 @@ public sealed class DataApi /// Throws exception, when response could not be serialized. /// /// Query, variables, and response for data. - public static async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData( + public static async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData( Guid dataId, string[] filenames, AppSettings appSettings, @@ -72,7 +71,6 @@ public sealed class DataApi IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) - where TGraphQlData : class { var variables = new { id = dataId }; var query = await apiRequestService.ConstructGraphQlQuery(filenames); diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 1dc51bc0..ce1907d9 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -13,6 +13,7 @@ public sealed class AppSettings = ""; public Guid DatabaseId { get; set; } + public Guid OperatorId { get; set; } public string VerificationCode { get; set; } = ""; diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 375300cd..53766270 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -13,4 +13,5 @@ public interface IApproval string Query { get; } JsonElement Variables { get; } string Message { get; } + public Guid ApproverId { get; } } \ No newline at end of file diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 9cfcfc4c..a0ae247b 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -11,7 +11,8 @@ public sealed class ResponseApproval( string keyFingerprint, string query, JsonElement variables, - string message + string message, + Guid approverId ) : IApproval { @@ -21,4 +22,5 @@ string message public string Query { get; private set; } = query; public JsonElement Variables { get; private set; } = variables; public string Message { get; private set; } = message; + public Guid ApproverId { get; private set; } = approverId; } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 5620f716..2e67548b 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -34,26 +34,36 @@ private sealed record OpticalDataResponse(OpticalDataDto OpticalData); /// /// Thows exception, when singing of data failed. /// - public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) + public async Task CreateResponseApproval(T dataObject, CancellationToken cancellationToken) + where T : IData { // Get dataset logger.QueryAllMetaData(dataObject.GetType(), dataObject.Id); var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); logger.QueryAndVariablesAndResponce(query, variables, response); var signature = await signingService.SignData(response); - return new ResponseApproval(DateTime.UtcNow, signature, await signingService.ExtractFingerprint(), query, variables, response); + return new ResponseApproval( + DateTime.UtcNow, + signature, + await signingService.ExtractFingerprint(), + query, + variables, + response, + appSettings.OperatorId + ); } - private async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData(IData dataObject, CancellationToken cancellationToken) + private async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData(T dataObject, CancellationToken cancellationToken) + where T : IData { return dataObject switch { - GeometricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - CalorimetricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - HygrothermalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - PhotovoltaicData data => await DataApi.QueryAllMetaData(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - OpticalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - _ => throw new NotImplementedException($"Unsupported data type {typeof(IData)}"), + GeometricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + CalorimetricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + HygrothermalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + PhotovoltaicData data => await DataApi.QueryAllMetaData(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + OpticalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + _ => throw new NotImplementedException($"Unsupported data type {typeof(T)}"), }; } } \ No newline at end of file diff --git a/docker-compose.production.yml b/docker-compose.production.yml index cf92bf98..6558e80d 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -70,6 +70,7 @@ services: - XBASE_Host=https://${HOST} - XBASE_MetabaseHost=https://${METABASE_HOST} - XBASE_DatabaseId=${DATABASE_ID} + - XBASE_OperatorId=${OPERATOR_ID} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} diff --git a/docker-compose.yml b/docker-compose.yml index 8561f3e4..770ae65f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,6 +110,7 @@ services: - XBASE_Host=https://${HOST}:${HTTPS_PORT} - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} - XBASE_DatabaseId=${DATABASE_ID} + - XBASE_OperatorId=${OPERATOR_ID} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} From 8684772800a5a6e90e02935c3c088379763a0369 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 14:32:26 +0200 Subject: [PATCH 407/878] Print dummy line --- backend/src/Methods/TransmittanceSumMethod.cs | 2 ++ backend/test/Play.cs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 8626f8b2..6807924e 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -23,4 +23,6 @@ public List Calculate(IReadOnlyList dataPoints) new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)) ]; } + + public string PrintDummyLine() { Console.WriteLine("Print dummy line."); return "DummyLinePrinted"; } } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index bcbcda76..9bbcf10d 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using NUnit.Framework; +using Database.Methods; namespace Database.Tests; @@ -10,6 +11,10 @@ public sealed class Play public async Task Do() { Console.WriteLine("Do play!"); + + TransmittanceSumMethod myTransmittanceSumInstance = new TransmittanceSumMethod(); + myTransmittanceSumInstance.PrintDummyLine(); + await Task.FromResult(0); } } \ No newline at end of file From 4451bcac437d9444d5f7626df53c5adbc1fe8433 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 15:12:45 +0200 Subject: [PATCH 408/878] Create a `dataPoints` example --- backend/src/Methods/TransmittanceSumMethod.cs | 1 - backend/test/Play.cs | 29 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 6807924e..cd305d20 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,5 +24,4 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public string PrintDummyLine() { Console.WriteLine("Print dummy line."); return "DummyLinePrinted"; } } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 9bbcf10d..d2f87a7c 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using NUnit.Framework; using Database.Methods; +using Database.GraphQl.MethodAsService; +using System.Collections.Generic; namespace Database.Tests; @@ -12,8 +14,31 @@ public async Task Do() { Console.WriteLine("Do play!"); - TransmittanceSumMethod myTransmittanceSumInstance = new TransmittanceSumMethod(); - myTransmittanceSumInstance.PrintDummyLine(); + // Create example dataPoints + var direction = new Direction(8); + + var wavelengths0 = new Wavelengths(450); + var incidence0 = new Incidence(wavelengths0, direction); + var emergence0 = new Emergence(direction); + var results0 = new Results(0.5); + + var wavelengths1 = new Wavelengths(550); + var incidence1 = new Incidence(wavelengths1, direction); + var emergence1 = new Emergence(direction); + var results1 = new Results(0.7); + + var wavelengths2 = new Wavelengths(650); + var incidence2 = new Incidence(wavelengths2, direction); + var emergence2 = new Emergence(direction); + var results2 = new Results(0.8); + + var dataPoint0 = new DataPoint(incidence0, emergence0, results0); + var dataPoint1 = new DataPoint(incidence1, emergence1, results1); + var dataPoint2 = new DataPoint(incidence2, emergence2, results2); + + IReadOnlyList dataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; + + Console.WriteLine(dataPoints); await Task.FromResult(0); } From 313138e90ecc5730c5488d20415e47e169e83916 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 15:36:25 +0200 Subject: [PATCH 409/878] Use the `TransmittanceSumMethod` --- backend/test/Play.cs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/backend/test/Play.cs b/backend/test/Play.cs index d2f87a7c..e6b276e0 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -14,31 +14,36 @@ public async Task Do() { Console.WriteLine("Do play!"); - // Create example dataPoints + // Create example myDataPoints var direction = new Direction(8); - var wavelengths0 = new Wavelengths(450); var incidence0 = new Incidence(wavelengths0, direction); var emergence0 = new Emergence(direction); var results0 = new Results(0.5); - var wavelengths1 = new Wavelengths(550); var incidence1 = new Incidence(wavelengths1, direction); var emergence1 = new Emergence(direction); var results1 = new Results(0.7); - var wavelengths2 = new Wavelengths(650); var incidence2 = new Incidence(wavelengths2, direction); var emergence2 = new Emergence(direction); var results2 = new Results(0.8); - var dataPoint0 = new DataPoint(incidence0, emergence0, results0); var dataPoint1 = new DataPoint(incidence1, emergence1, results1); var dataPoint2 = new DataPoint(incidence2, emergence2, results2); - - IReadOnlyList dataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; - - Console.WriteLine(dataPoints); + IReadOnlyList myDataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; + Console.WriteLine(myDataPoints); + + // Use the TransmittanceSumMethod + TransmittanceSumMethod myTransmittanceSumMethod = new TransmittanceSumMethod(); + List transmittanceSumOutput = myTransmittanceSumMethod.Calculate(myDataPoints); + Console.WriteLine(transmittanceSumOutput); + foreach (var dataPoint in transmittanceSumOutput) + { + Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + } await Task.FromResult(0); } From 994eee1ebbbf4f0cd86cad359e6e471411082a68 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 15:39:12 +0200 Subject: [PATCH 410/878] Add the raw proposal of Johannes Hanek as a separate file --- .../src/Methods/CalculateWeightedIntegralByJoHa.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 backend/src/Methods/CalculateWeightedIntegralByJoHa.cs diff --git a/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs b/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs new file mode 100644 index 00000000..e6df9def --- /dev/null +++ b/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs @@ -0,0 +1,14 @@ +static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) +{ + double integral = 0.0; + + for (int i = 0; i < data.Count - 1; i++) + { + // Trapezregel zur Integralberechnung + double deltaX = data[i + 1].Wavelength - data[i].Wavelength; + double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; + integral += averageY * deltaX * weight; + } + + return integral; +} \ No newline at end of file From 5ceaff1cf79ce877d7b197e3ccf9c8c52c7af44e Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 16:40:54 +0200 Subject: [PATCH 411/878] Extend IMethod. --- .../CalculateWeightedIntegralByJoHa.cs | 28 +++++++++------ backend/src/Methods/IMethod.cs | 1 + .../src/Methods/SpectralToIntegralMethod.cs | 36 +++++++++++++++++++ backend/src/Methods/TransmittanceSumMethod.cs | 10 ++++++ backend/test/Play.cs | 16 ++++----- 5 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 backend/src/Methods/SpectralToIntegralMethod.cs diff --git a/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs b/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs index e6df9def..67e8c0ce 100644 --- a/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs +++ b/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs @@ -1,14 +1,22 @@ -static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) -{ - double integral = 0.0; +using System.Collections.Generic; - for (int i = 0; i < data.Count - 1; i++) +namespace JohannesHanek +{ + public sealed class JohannesHanek() { - // Trapezregel zur Integralberechnung - double deltaX = data[i + 1].Wavelength - data[i].Wavelength; - double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; - integral += averageY * deltaX * weight; - } + static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) + { + double integral = 0.0; - return integral; + for (int i = 0; i < data.Count - 1; i++) + { + // Trapezregel zur Integralberechnung + double deltaX = data[i + 1].Wavelength - data[i].Wavelength; + double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; + integral += averageY * deltaX * weight; + } + + return integral; + } + } } \ No newline at end of file diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 8b6e064e..04fc6c7f 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -10,4 +10,5 @@ public interface IMethod Guid Id { get; } List Calculate(IReadOnlyList dataPoints); + List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs new file mode 100644 index 00000000..004dccb8 --- /dev/null +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Database.GraphQl.MethodAsService; + +namespace Database.Methods; + +public sealed class SpectralToIntegralMethod : IMethod +{ + public string Name => "SpectralToIntegral"; + + public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); + + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) + { + double transmittanceSum = 0; + foreach (var dataPoint in spectralDataPoints) + { + transmittanceSum += dataPoint.Results.Transmittance; + } + + DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)); + + return [integralDataPoint]; + } + + public List Calculate(IReadOnlyList dataPoints) + { + Console.WriteLine("The SpectralToIntegralMethod needs two list of dataPoints as input."); + + return + [ + new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(0)) + ]; + } + +} \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index cd305d20..e1491cae 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,4 +24,14 @@ public List Calculate(IReadOnlyList dataPoints) ]; } + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) + { + Console.WriteLine("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); + + return + [ + new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(0)) + ]; + } + } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index e6b276e0..c5f11c02 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -34,15 +34,15 @@ public async Task Do() IReadOnlyList myDataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; Console.WriteLine(myDataPoints); - // Use the TransmittanceSumMethod - TransmittanceSumMethod myTransmittanceSumMethod = new TransmittanceSumMethod(); - List transmittanceSumOutput = myTransmittanceSumMethod.Calculate(myDataPoints); - Console.WriteLine(transmittanceSumOutput); - foreach (var dataPoint in transmittanceSumOutput) + // Use the SpectralToIntegralMethod + SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(myDataPoints, myDataPoints); + Console.WriteLine(integralDataPoints); + foreach (DataPoint integralDataPoint in integralDataPoints) { - Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + Console.WriteLine($"Incidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}"); } await Task.FromResult(0); From 15917303d6d6ed6cede1cd54ea8919b1cfdc90bb Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 26 Jun 2025 16:55:01 +0200 Subject: [PATCH 412/878] Create example `weightingSpectrum` --- backend/test/Play.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/test/Play.cs b/backend/test/Play.cs index c5f11c02..8d6dd12d 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -14,7 +14,7 @@ public async Task Do() { Console.WriteLine("Do play!"); - // Create example myDataPoints + // Create example spectralDataPoints var direction = new Direction(8); var wavelengths0 = new Wavelengths(450); var incidence0 = new Incidence(wavelengths0, direction); @@ -31,12 +31,16 @@ public async Task Do() var dataPoint0 = new DataPoint(incidence0, emergence0, results0); var dataPoint1 = new DataPoint(incidence1, emergence1, results1); var dataPoint2 = new DataPoint(incidence2, emergence2, results2); - IReadOnlyList myDataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; - Console.WriteLine(myDataPoints); + IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; + + // Create example weightingSpectrum + DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); + DataPoint dataPoint11 = new DataPoint(new Incidence(new Wavelengths(600), direction), new Emergence(direction), new Results(0.5)); + IReadOnlyList weightingSpectrum = new List { dataPoint10, dataPoint11 }; // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(myDataPoints, myDataPoints); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, weightingSpectrum); Console.WriteLine(integralDataPoints); foreach (DataPoint integralDataPoint in integralDataPoints) { From cff7b400079e04dc730ffda95840896c22eca7f3 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Fri, 27 Jun 2025 11:09:32 +0200 Subject: [PATCH 413/878] Throw an exception if the wrong overloaded method us used --- backend/src/Methods/SpectralToIntegralMethod.cs | 3 ++- backend/src/Methods/TransmittanceSumMethod.cs | 3 ++- backend/test/Play.cs | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 004dccb8..b101696b 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -25,7 +25,8 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR public List Calculate(IReadOnlyList dataPoints) { - Console.WriteLine("The SpectralToIntegralMethod needs two list of dataPoints as input."); + + throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); return [ diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index e1491cae..0aa1d931 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -26,7 +26,8 @@ public List Calculate(IReadOnlyList dataPoints) public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) { - Console.WriteLine("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); + + throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); return [ diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 8d6dd12d..aec2f188 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -49,6 +49,8 @@ public async Task Do() Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}"); } + List integralDataPoints2 = mySpectralToIntegralMethod.Calculate(spectralDataPoints); + await Task.FromResult(0); } } \ No newline at end of file From 02b2fcedb6eccdb12f489a2b8ca1ad120fe1f1f0 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Fri, 27 Jun 2025 13:23:03 +0200 Subject: [PATCH 414/878] current status --- .../src/Methods/SpectralToIntegralMethod.cs | 38 +++++++++++++------ backend/src/Methods/TransmittanceSumMethod.cs | 6 --- backend/test/Play.cs | 4 +- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index b101696b..47548ff9 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -12,26 +12,40 @@ public sealed class SpectralToIntegralMethod : IMethod public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) { - double transmittanceSum = 0; - foreach (var dataPoint in spectralDataPoints) - { - transmittanceSum += dataPoint.Results.Transmittance; - } + double integral = 0.0; + double weight = 1; + + // for (int i = 0; i < spectralDataPoints.Count - 1; i++) + // { + // // Trapezoidal rule to calculate an integral + // double deltaX = spectralDataPoints[i + 1].Incidence.Wavelength - spectralDataPoints[i].DataPoint.Incidence.Wavelength; + // double averageY = (spectralDataPoints[i].DataPoint.Results.Transmittance + spectralDataPoints[i + 1].DataPoint.Results.Transmittance) / 2; + // integral += averageY * deltaX * weight; + // } - DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)); + DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(integral)); return [integralDataPoint]; } + /* static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) + { + double integral = 0.0; + + for (int i = 0; i < data.Count - 1; i++) + { + // Trapezregel zur Integralberechnung + double deltaX = data[i + 1].Wavelength - data[i].Wavelength; + double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; + integral += averageY * deltaX * weight; + } + + return integral; + } */ + public List Calculate(IReadOnlyList dataPoints) { - throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); - - return - [ - new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(0)) - ]; } } \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 0aa1d931..92631da7 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -26,13 +26,7 @@ public List Calculate(IReadOnlyList dataPoints) public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) { - throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); - - return - [ - new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(0)) - ]; } } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index aec2f188..5567921e 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -19,7 +19,7 @@ public async Task Do() var wavelengths0 = new Wavelengths(450); var incidence0 = new Incidence(wavelengths0, direction); var emergence0 = new Emergence(direction); - var results0 = new Results(0.5); + var results0 = new Results(0.1); var wavelengths1 = new Wavelengths(550); var incidence1 = new Incidence(wavelengths1, direction); var emergence1 = new Emergence(direction); @@ -49,8 +49,6 @@ public async Task Do() Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}"); } - List integralDataPoints2 = mySpectralToIntegralMethod.Calculate(spectralDataPoints); - await Task.FromResult(0); } } \ No newline at end of file From d3655dd395228c34c57c69f710da973e5f89b17a Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Fri, 27 Jun 2025 15:40:51 +0200 Subject: [PATCH 415/878] Calculate the integral transmittance with a constant weight --- .../src/Methods/SpectralToIntegralMethod.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 47548ff9..a2174289 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -12,18 +12,20 @@ public sealed class SpectralToIntegralMethod : IMethod public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) { - double integral = 0.0; - double weight = 1; - - // for (int i = 0; i < spectralDataPoints.Count - 1; i++) - // { - // // Trapezoidal rule to calculate an integral - // double deltaX = spectralDataPoints[i + 1].Incidence.Wavelength - spectralDataPoints[i].DataPoint.Incidence.Wavelength; - // double averageY = (spectralDataPoints[i].DataPoint.Results.Transmittance + spectralDataPoints[i + 1].DataPoint.Results.Transmittance) / 2; - // integral += averageY * deltaX * weight; - // } - - DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(integral)); + double numerator = 0.0; + double denominator = 0.0; + double weight = 1.0; + + for (int i = 0; i < spectralDataPoints.Count - 1; i++) + { + // Trapezoidal rule to calculate an integral + double deltaX = spectralDataPoints[i + 1].Incidence.Wavelengths.Wavelength - spectralDataPoints[i].Incidence.Wavelengths.Wavelength; + double averageY = (spectralDataPoints[i].Results.Transmittance + spectralDataPoints[i + 1].Results.Transmittance) / 2; + numerator += averageY * deltaX * weight; + denominator += deltaX * weight; + } + + DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); return [integralDataPoint]; } From 780b1fb2476d43e74a0927143bc59136c051744c Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 1 Jul 2025 12:04:06 +0200 Subject: [PATCH 416/878] Calculation is correct, but deltaWavelength must be calculated from the weightingDataPoints --- backend/src/Methods/IMethod.cs | 2 +- .../src/Methods/SpectralToIntegralMethod.cs | 53 ++++++++++--------- backend/src/Methods/TransmittanceSumMethod.cs | 2 +- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 04fc6c7f..ca7a14bb 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -10,5 +10,5 @@ public interface IMethod Guid Id { get; } List Calculate(IReadOnlyList dataPoints); - List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum); + List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index a2174289..5a109596 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -10,41 +10,42 @@ public sealed class SpectralToIntegralMethod : IMethod public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) { - double numerator = 0.0; - double denominator = 0.0; - double weight = 1.0; - - for (int i = 0; i < spectralDataPoints.Count - 1; i++) + // TODO filter for spectral data points only, no integral data points. + double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; + foreach (DataPoint weightingDataPoint in weightingDataPoints) { + wavelengthWeighting = weightingDataPoint.Incidence.Wavelengths.Wavelength; + DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + foreach (DataPoint spectralDataPoint in spectralDataPoints) + { + // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint + if ((spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) + { spectralDataPointWavelengthBelow = spectralDataPoint; } + if ((spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) + { spectralDataPointWavelengthAbove = spectralDataPoint; } + } // Trapezoidal rule to calculate an integral - double deltaX = spectralDataPoints[i + 1].Incidence.Wavelengths.Wavelength - spectralDataPoints[i].Incidence.Wavelengths.Wavelength; - double averageY = (spectralDataPoints[i].Results.Transmittance + spectralDataPoints[i + 1].Results.Transmittance) / 2; - numerator += averageY * deltaX * weight; - denominator += deltaX * weight; + deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; + numerator += averageValue * deltaWavelength * weightingDataPoint.Results.Transmittance; + denominator += deltaWavelength * weightingDataPoint.Results.Transmittance; + // Print debug output + foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPoint, spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + { + Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + } + Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); } - DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); return [integralDataPoint]; } - /* static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) - { - double integral = 0.0; - - for (int i = 0; i < data.Count - 1; i++) - { - // Trapezregel zur Integralberechnung - double deltaX = data[i + 1].Wavelength - data[i].Wavelength; - double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; - integral += averageY * deltaX * weight; - } - - return integral; - } */ - public List Calculate(IReadOnlyList dataPoints) { throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 92631da7..f817cc9e 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,7 +24,7 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingSpectrum) + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) { throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); } From 495753abdd823fa1faa97c42bd3c076329fe4bbc Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 1 Jul 2025 15:25:48 +0200 Subject: [PATCH 417/878] Filter works for the first time --- .../src/Methods/SpectralToIntegralMethod.cs | 99 ++++++++++++++----- backend/test/Play.cs | 8 +- 2 files changed, 76 insertions(+), 31 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 5a109596..76b201b3 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Database.GraphQl.MethodAsService; namespace Database.Methods; @@ -12,40 +13,84 @@ public sealed class SpectralToIntegralMethod : IMethod public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) { - // TODO filter for spectral data points only, no integral data points. - double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; - foreach (DataPoint weightingDataPoint in weightingDataPoints) + // Filter for spectral data points only, no integral data points. + List spectralDataPointsToFilter = new List(spectralDataPoints); + // List weightingDataPointsFiltered = weightingDataPoints.RemoveAll(dataPoint => dataPoint.Incidence.Wavelengths == "integral"); + + // Print debug output + foreach (DataPoint dataPoint in spectralDataPoints) + { + Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + } + + Console.WriteLine($"\nspectralDataPointsToFilter.TrueForAll(WavelengthOutOfBounds) = {spectralDataPointsToFilter.TrueForAll(WavelengthOutOfBounds)}\n"); + Console.WriteLine($"\nspectralDataPointsToFilter.Find(WavelengthOutOfBounds) = {spectralDataPointsToFilter.Find(WavelengthOutOfBounds)}\n"); + + List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); + + // List spectralDataPointsFiltered = spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds); + + // Console.WriteLine($"\nspectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds) = {spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds)}\n"); + Console.WriteLine($"\nspectralDataPointsFiltered.Find(WavelengthOutOfBounds) = {spectralDataPointsFiltered.Find(WavelengthOutOfBounds)}\n"); + + // Print debug output + foreach (DataPoint dataPoint in spectralDataPointsFiltered) { - wavelengthWeighting = weightingDataPoint.Incidence.Wavelengths.Wavelength; - DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - foreach (DataPoint spectralDataPoint in spectralDataPoints) - { - // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - if ((spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) - { spectralDataPointWavelengthBelow = spectralDataPoint; } - if ((spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) - { spectralDataPointWavelengthAbove = spectralDataPoint; } - } - // Trapezoidal rule to calculate an integral - deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; - averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; - numerator += averageValue * deltaWavelength * weightingDataPoint.Results.Transmittance; - denominator += deltaWavelength * weightingDataPoint.Results.Transmittance; - // Print debug output - foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPoint, spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) - { - Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); - } - Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); + Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); } + + // List spectralDataPointsFiltered = spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds); + // // List weightingDataPointsFiltered = weightingDataPoints.RemoveAll(dataPoint => dataPoint.Incidence.Wavelengths == "integral"); + // // Sort the dataPoints according to their wavelength + // List spectralDataPointsSorted = spectralDataPointsFiltered.OrderByDescending(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength); + // List weightingDataPointsSorted = weightingDataPointsFiltered.OrderByDescending(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength); + + + + double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; + // foreach (DataPoint weightingDataPoint in weightingDataPointsSorted) + // { + // wavelengthWeighting = weightingDataPoint.Incidence.Wavelengths.Wavelength; + // DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + // DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + // foreach (DataPoint spectralDataPoint in spectralDataPointsSorted) + // { + // // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint + // if ((spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) + // { spectralDataPointWavelengthBelow = spectralDataPoint; } + // if ((spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) + // { spectralDataPointWavelengthAbove = spectralDataPoint; } + // } + // // Trapezoidal rule to calculate an integral + // deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + // averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; + // numerator += averageValue * deltaWavelength * weightingDataPoint.Results.Transmittance; + // denominator += deltaWavelength * weightingDataPoint.Results.Transmittance; + // // Print debug output + // foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPoint, spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + // { + // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + // } + // Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); + // } DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); return [integralDataPoint]; } + // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 2500 nm. + private static bool WavelengthOutOfBounds(DataPoint dataPoint) + { + return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 2500); + // return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 2500) || (dataPoint.Incidence.Wavelengths.Wavelength != null); + } + public List Calculate(IReadOnlyList dataPoints) { throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 5567921e..af5c981f 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -31,7 +31,8 @@ public async Task Do() var dataPoint0 = new DataPoint(incidence0, emergence0, results0); var dataPoint1 = new DataPoint(incidence1, emergence1, results1); var dataPoint2 = new DataPoint(incidence2, emergence2, results2); - IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint1, dataPoint2 }; + DataPoint dataPoint3 = new DataPoint(new Incidence(new Wavelengths(-100), direction), new Emergence(direction), new Results(99)); + IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint1, dataPoint2, dataPoint3 }; // Create example weightingSpectrum DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); @@ -41,12 +42,11 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, weightingSpectrum); - Console.WriteLine(integralDataPoints); foreach (DataPoint integralDataPoint in integralDataPoints) { - Console.WriteLine($"Incidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}"); + Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } await Task.FromResult(0); From 22b2ffe272b8aa1cf8ecc555f6b61d07eab253d6 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 1 Jul 2025 15:56:33 +0200 Subject: [PATCH 418/878] Sort the dataPoints according to their wavelength --- .../src/Methods/SpectralToIntegralMethod.cs | 30 +++++-------------- backend/test/Play.cs | 2 +- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 76b201b3..afead5c2 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -13,30 +13,16 @@ public sealed class SpectralToIntegralMethod : IMethod public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) { - // Filter for spectral data points only, no integral data points. + // Filter data points which have a wavelength which is out of bounds. List spectralDataPointsToFilter = new List(spectralDataPoints); - // List weightingDataPointsFiltered = weightingDataPoints.RemoveAll(dataPoint => dataPoint.Incidence.Wavelengths == "integral"); - - // Print debug output - foreach (DataPoint dataPoint in spectralDataPoints) - { - Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); - } - - Console.WriteLine($"\nspectralDataPointsToFilter.TrueForAll(WavelengthOutOfBounds) = {spectralDataPointsToFilter.TrueForAll(WavelengthOutOfBounds)}\n"); - Console.WriteLine($"\nspectralDataPointsToFilter.Find(WavelengthOutOfBounds) = {spectralDataPointsToFilter.Find(WavelengthOutOfBounds)}\n"); - + List weightingDataPointsToFilter = new List(weightingDataPoints); List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); - - // List spectralDataPointsFiltered = spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds); - - // Console.WriteLine($"\nspectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds) = {spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds)}\n"); - Console.WriteLine($"\nspectralDataPointsFiltered.Find(WavelengthOutOfBounds) = {spectralDataPointsFiltered.Find(WavelengthOutOfBounds)}\n"); - - // Print debug output - foreach (DataPoint dataPoint in spectralDataPointsFiltered) + List weightingDataPointsFiltered = weightingDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); + // Sort the dataPoints + List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); + List weightingDataPointsSorted = weightingDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); + // Print filtered and sorted spectralDataPoints + foreach (DataPoint dataPoint in spectralDataPointsSorted) { Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); diff --git a/backend/test/Play.cs b/backend/test/Play.cs index af5c981f..78d0eb38 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -32,7 +32,7 @@ public async Task Do() var dataPoint1 = new DataPoint(incidence1, emergence1, results1); var dataPoint2 = new DataPoint(incidence2, emergence2, results2); DataPoint dataPoint3 = new DataPoint(new Incidence(new Wavelengths(-100), direction), new Emergence(direction), new Results(99)); - IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint1, dataPoint2, dataPoint3 }; + IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint2, dataPoint1, dataPoint3 }; // Create example weightingSpectrum DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); From d0ca86254c86b95a9eedaf8eeacaa1a9228477c9 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 1 Jul 2025 16:16:08 +0200 Subject: [PATCH 419/878] Add standard. Exchange `foreach` by `for` --- backend/src/Methods/IMethod.cs | 2 +- .../src/Methods/SpectralToIntegralMethod.cs | 77 +++++++++---------- backend/src/Methods/TransmittanceSumMethod.cs | 4 +- backend/test/Play.cs | 2 +- 4 files changed, 39 insertions(+), 46 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index ca7a14bb..6bae34ea 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -10,5 +10,5 @@ public interface IMethod Guid Id { get; } List Calculate(IReadOnlyList dataPoints); - List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints); + List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index afead5c2..0916e95a 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -11,7 +11,7 @@ public sealed class SpectralToIntegralMethod : IMethod public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard) { // Filter data points which have a wavelength which is out of bounds. List spectralDataPointsToFilter = new List(spectralDataPoints); @@ -21,50 +21,43 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR // Sort the dataPoints List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); List weightingDataPointsSorted = weightingDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); - // Print filtered and sorted spectralDataPoints - foreach (DataPoint dataPoint in spectralDataPointsSorted) + // // Print filtered and sorted spectralDataPoints + // foreach (DataPoint dataPoint in spectralDataPointsSorted) + // { + // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + // } + double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; + + for (int i = 0; i < weightingDataPointsSorted.Count; i++) { - Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + wavelengthWeighting = weightingDataPointsSorted[i].Incidence.Wavelengths.Wavelength; + DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + for (int j = 0; j < spectralDataPointsSorted.Count; j++) + { + // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint + if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) + { spectralDataPointWavelengthBelow = spectralDataPointsSorted[j]; } + if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) + { spectralDataPointWavelengthAbove = spectralDataPointsSorted[j]; } + } + // Trapezoidal rule to calculate an integral + deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; + numerator += averageValue * deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; + denominator += deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; + // Print debug output + foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPointsSorted[i], spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + { + Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + } + Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); } - // List spectralDataPointsFiltered = spectralDataPointsToFilter.RemoveAll(WavelengthOutOfBounds); - // // List weightingDataPointsFiltered = weightingDataPoints.RemoveAll(dataPoint => dataPoint.Incidence.Wavelengths == "integral"); - // // Sort the dataPoints according to their wavelength - // List spectralDataPointsSorted = spectralDataPointsFiltered.OrderByDescending(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength); - // List weightingDataPointsSorted = weightingDataPointsFiltered.OrderByDescending(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength); - - - - double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; - // foreach (DataPoint weightingDataPoint in weightingDataPointsSorted) - // { - // wavelengthWeighting = weightingDataPoint.Incidence.Wavelengths.Wavelength; - // DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - // DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - // foreach (DataPoint spectralDataPoint in spectralDataPointsSorted) - // { - // // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - // if ((spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) - // { spectralDataPointWavelengthBelow = spectralDataPoint; } - // if ((spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) - // { spectralDataPointWavelengthAbove = spectralDataPoint; } - // } - // // Trapezoidal rule to calculate an integral - // deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; - // averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; - // numerator += averageValue * deltaWavelength * weightingDataPoint.Results.Transmittance; - // denominator += deltaWavelength * weightingDataPoint.Results.Transmittance; - // // Print debug output - // foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPoint, spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) - // { - // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); - // } - // Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); - // } DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); return [integralDataPoint]; diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index f817cc9e..79f751ff 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,9 +24,9 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints) + public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard) { - throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints as input."); + throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints and one standard as input."); } } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 78d0eb38..d1b280f7 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -41,7 +41,7 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, weightingSpectrum); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, weightingSpectrum, "EN410"); foreach (DataPoint integralDataPoint in integralDataPoints) { Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); From 7437ae802976e0266058df12a1af62d1f59aaa95 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 1 Jul 2025 16:27:23 +0200 Subject: [PATCH 420/878] Introduce `switch(standard)` --- backend/src/Methods/SpectralToIntegralMethod.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 0916e95a..ae2ef446 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -43,8 +43,14 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) { spectralDataPointWavelengthAbove = spectralDataPointsSorted[j]; } } + // Calculate width of the wavelength interval at weightingDataPointsSorted[i] + switch (standard) + { + case "EN410": + deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + break; + } // Trapezoidal rule to calculate an integral - deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; numerator += averageValue * deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; denominator += deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; From ff56bc4f44c3ec14d9602ae4b1aecf93231dbb77 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 14:19:30 +0200 Subject: [PATCH 421/878] First step to add EN410 weights hard coded --- .../src/Methods/SpectralToIntegralMethod.cs | 53 +++++++++++++++++-- backend/test/Play.cs | 2 + 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index ae2ef446..e53e6ff2 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -47,7 +47,8 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR switch (standard) { case "EN410": - deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + if (wavelengthWeighting == 300) + deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; break; } // Trapezoidal rule to calculate an integral @@ -69,11 +70,55 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR return [integralDataPoint]; } - // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 2500 nm. + List<(int wavelength, double weight, double deltaWavelength)> en410WeightingSpectrum = new List<(int, double, double)> + { + (380, 0, 5), + (390, 0.0005, 10), + (400, 0.003, 10), + (410, 0.0103, 10), + (420, 0.0352, 10), + (430, 0.0948, 10), + (440, 0.2274, 10), + (450, 0.4192, 10), + (460, 0.6663, 10), + (470, 0.985, 10), + (480, 1.5189, 10), + (490, 2.1336, 10), + (500, 3.3491, 10), + (510, 5.1393, 10), + (520, 7.0523, 10), + (530, 8.799, 10), + (540, 9.4427, 10), + (550, 9.8077, 10), + (560, 9.4306, 10), + (570, 8.6891, 10), + (580, 7.8994, 10), + (590, 6.3306, 10), + (600, 5.3542, 10), + (610, 4.2491, 10), + (620, 3.1502, 10), + (630, 2.0812, 10), + (640, 1.381, 10), + (650, 0.807, 10), + (660, 0.4612, 10), + (670, 0.2485, 10), + (680, 0.1255, 10), + (690, 0.0536, 10), + (700, 0.0276, 10), + (710, 0.0146, 10), + (720, 0.0057, 10), + (730, 0.0035, 10), + (740, 0.0021, 10), + (750, 0.0008, 10), + (760, 0.0001, 10), + (770, 0, 10), + (780, 0, 5) + }; + + // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { - return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 2500); - // return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 2500) || (dataPoint.Incidence.Wavelengths.Wavelength != null); + return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 3000); } public List Calculate(IReadOnlyList dataPoints) diff --git a/backend/test/Play.cs b/backend/test/Play.cs index d1b280f7..a67508ba 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -49,6 +49,8 @@ public async Task Do() Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } + Console.WriteLine(mySpectralToIntegralMethod.en410WavelengthsWeights) + await Task.FromResult(0); } } \ No newline at end of file From da4d1d4ec3b43636242d40bd10321bfb4f377d78 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 15:02:47 +0200 Subject: [PATCH 422/878] Define and use the field `en410WavelengthsWeights` --- backend/src/Methods/IMethod.cs | 2 +- .../src/Methods/SpectralToIntegralMethod.cs | 125 ++++++++++-------- backend/src/Methods/TransmittanceSumMethod.cs | 4 +- backend/test/Play.cs | 12 +- 4 files changed, 76 insertions(+), 67 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 6bae34ea..fbbb14db 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -10,5 +10,5 @@ public interface IMethod Guid Id { get; } List Calculate(IReadOnlyList dataPoints); - List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard); + List Calculate(IReadOnlyList spectralDataPoints, string standard); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index e53e6ff2..77f14b12 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Database.GraphQl.MethodAsService; @@ -8,19 +9,73 @@ namespace Database.Methods; public sealed class SpectralToIntegralMethod : IMethod { public string Name => "SpectralToIntegral"; - public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); + private List<(int wavelength, double weight, double deltaWavelength)> en410WavelengthsWeightsList = new List<(int, double, double)> + { + (380, 0, 5), + (390, 0.0005, 10), + (400, 0.003, 10), + (410, 0.0103, 10), + (420, 0.0352, 10), + (430, 0.0948, 10), + (440, 0.2274, 10), + (450, 0.4192, 10), + (460, 0.6663, 10), + (470, 0.985, 10), + (480, 1.5189, 10), + (490, 2.1336, 10), + (500, 3.3491, 10), + (510, 5.1393, 10), + (520, 7.0523, 10), + (530, 8.799, 10), + (540, 9.4427, 10), + (550, 9.8077, 10), + (560, 9.4306, 10), + (570, 8.6891, 10), + (580, 7.8994, 10), + (590, 6.3306, 10), + (600, 5.3542, 10), + (610, 4.2491, 10), + (620, 3.1502, 10), + (630, 2.0812, 10), + (640, 1.381, 10), + (650, 0.807, 10), + (660, 0.4612, 10), + (670, 0.2485, 10), + (680, 0.1255, 10), + (690, 0.0536, 10), + (700, 0.0276, 10), + (710, 0.0146, 10), + (720, 0.0057, 10), + (730, 0.0035, 10), + (740, 0.0021, 10), + (750, 0.0008, 10), + (760, 0.0001, 10), + (770, 0, 10), + (780, 0, 5) + }; + public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410WavelengthsWeights { get; } - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard) + public SpectralToIntegralMethod() { - // Filter data points which have a wavelength which is out of bounds. + en410WavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410WavelengthsWeightsList); + } + + public List Calculate(IReadOnlyList spectralDataPoints, string standard) + { + // Turn IReadOnlyLists into more flexible Lists List spectralDataPointsToFilter = new List(spectralDataPoints); - List weightingDataPointsToFilter = new List(weightingDataPoints); + List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; + switch (standard) + { + case "EN410": + wavelengthsWeights = en410WavelengthsWeights.ToList(); + break; + } + // Filter data points which have a wavelength which is out of bounds. List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); - List weightingDataPointsFiltered = weightingDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); // Sort the dataPoints List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); - List weightingDataPointsSorted = weightingDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); // // Print filtered and sorted spectralDataPoints // foreach (DataPoint dataPoint in spectralDataPointsSorted) // { @@ -30,9 +85,9 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR // } double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; - for (int i = 0; i < weightingDataPointsSorted.Count; i++) + for (int i = 0; i < 2/*wavelengthsWeights.Count*/; i++) { - wavelengthWeighting = weightingDataPointsSorted[i].Incidence.Wavelengths.Wavelength; + wavelengthWeighting = wavelengthsWeights[i].wavelength; DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); for (int j = 0; j < spectralDataPointsSorted.Count; j++) @@ -47,16 +102,15 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR switch (standard) { case "EN410": - if (wavelengthWeighting == 300) - deltaWavelength = spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength; + deltaWavelength = 10; break; } // Trapezoidal rule to calculate an integral averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; - numerator += averageValue * deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; - denominator += deltaWavelength * weightingDataPointsSorted[i].Results.Transmittance; + numerator += averageValue * deltaWavelength * wavelengthsWeights[i].weight; + denominator += deltaWavelength * wavelengthsWeights[i].weight; // Print debug output - foreach (DataPoint dataPoint in new DataPoint[] { weightingDataPointsSorted[i], spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + foreach (DataPoint dataPoint in new DataPoint[] { spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) { Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); @@ -70,51 +124,6 @@ public List Calculate(IReadOnlyList spectralDataPoints, IR return [integralDataPoint]; } - List<(int wavelength, double weight, double deltaWavelength)> en410WeightingSpectrum = new List<(int, double, double)> - { - (380, 0, 5), - (390, 0.0005, 10), - (400, 0.003, 10), - (410, 0.0103, 10), - (420, 0.0352, 10), - (430, 0.0948, 10), - (440, 0.2274, 10), - (450, 0.4192, 10), - (460, 0.6663, 10), - (470, 0.985, 10), - (480, 1.5189, 10), - (490, 2.1336, 10), - (500, 3.3491, 10), - (510, 5.1393, 10), - (520, 7.0523, 10), - (530, 8.799, 10), - (540, 9.4427, 10), - (550, 9.8077, 10), - (560, 9.4306, 10), - (570, 8.6891, 10), - (580, 7.8994, 10), - (590, 6.3306, 10), - (600, 5.3542, 10), - (610, 4.2491, 10), - (620, 3.1502, 10), - (630, 2.0812, 10), - (640, 1.381, 10), - (650, 0.807, 10), - (660, 0.4612, 10), - (670, 0.2485, 10), - (680, 0.1255, 10), - (690, 0.0536, 10), - (700, 0.0276, 10), - (710, 0.0146, 10), - (720, 0.0057, 10), - (730, 0.0035, 10), - (740, 0.0021, 10), - (750, 0.0008, 10), - (760, 0.0001, 10), - (770, 0, 10), - (780, 0, 5) - }; - // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 79f751ff..6bbe45ba 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,9 +24,9 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, IReadOnlyList weightingDataPoints, string standard) + public List Calculate(IReadOnlyList spectralDataPoints, string standard) { - throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with two lists of dataPoints and one standard as input."); + throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with a standard as input."); } } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index a67508ba..a4701196 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -34,14 +34,14 @@ public async Task Do() DataPoint dataPoint3 = new DataPoint(new Incidence(new Wavelengths(-100), direction), new Emergence(direction), new Results(99)); IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint2, dataPoint1, dataPoint3 }; - // Create example weightingSpectrum - DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); - DataPoint dataPoint11 = new DataPoint(new Incidence(new Wavelengths(600), direction), new Emergence(direction), new Results(0.5)); - IReadOnlyList weightingSpectrum = new List { dataPoint10, dataPoint11 }; + // // Create example weightingSpectrum + // DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); + // DataPoint dataPoint11 = new DataPoint(new Incidence(new Wavelengths(600), direction), new Emergence(direction), new Results(0.5)); + // IReadOnlyList weightingSpectrum = new List { dataPoint10, dataPoint11 }; // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, weightingSpectrum, "EN410"); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, "EN410"); foreach (DataPoint integralDataPoint in integralDataPoints) { Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); @@ -49,7 +49,7 @@ public async Task Do() Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } - Console.WriteLine(mySpectralToIntegralMethod.en410WavelengthsWeights) + Console.WriteLine(mySpectralToIntegralMethod.en410WavelengthsWeights); await Task.FromResult(0); } From 8e5866fca3ce1185af6b4edcfc976be0c6da04dc Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 15:36:53 +0200 Subject: [PATCH 423/878] difficulties with enumeration --- backend/src/Methods/IMethod.cs | 3 +- .../src/Methods/SpectralToIntegralMethod.cs | 210 +++++++++++++++--- backend/src/Methods/TransmittanceSumMethod.cs | 2 +- backend/test/Play.cs | 4 +- 4 files changed, 186 insertions(+), 33 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index fbbb14db..55873801 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -8,7 +8,8 @@ public interface IMethod { string Name { get; } Guid Id { get; } + public enum StandardType { en410Visible, en410Solar, iso9050Solar } List Calculate(IReadOnlyList dataPoints); - List Calculate(IReadOnlyList spectralDataPoints, string standard); + List Calculate(IReadOnlyList spectralDataPoints, StandardType standard); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 77f14b12..2735e004 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -3,6 +3,7 @@ using System.Collections.ObjectModel; using System.Linq; using Database.GraphQl.MethodAsService; +using Database.Methods; namespace Database.Methods; @@ -10,7 +11,7 @@ public sealed class SpectralToIntegralMethod : IMethod { public string Name => "SpectralToIntegral"; public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); - private List<(int wavelength, double weight, double deltaWavelength)> en410WavelengthsWeightsList = new List<(int, double, double)> + private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> { (380, 0, 5), (390, 0.0005, 10), @@ -54,35 +55,194 @@ public sealed class SpectralToIntegralMethod : IMethod (770, 0, 10), (780, 0, 5) }; - public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410WavelengthsWeights { get; } + private List<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeightsList = new List<(int, double, double)> + { + (300, 0.0005, 10), + (320, 0.0069, 20), + (340, 0.0122, 20), + (360, 0.0145, 20), + (380, 0.0177, 20), + (400, 0.0235, 20), + (420, 0.0268, 20), + (440, 0.0294, 20), + (460, 0.0343, 20), + (480, 0.0339, 20), + (500, 0.0326, 20), + (520, 0.0318, 20), + (540, 0.0321, 20), + (560, 0.0312, 20), + (580, 0.0294, 20), + (600, 0.0289, 20), + (620, 0.0289, 20), + (640, 0.028, 20), + (660, 0.0273, 20), + (680, 0.0246, 20), + (700, 0.0237, 20), + (720, 0.022, 20), + (740, 0.023, 20), + (760, 0.0199, 20), + (780, 0.0211, 20), + (800, 0.033, 35), + (850, 0.0453, 50), + (900, 0.0381, 50), + (950, 0.022, 50), + (1000, 0.0329, 50), + (1050, 0.0306, 50), + (1100, 0.0185, 50), + (1150, 0.0136, 50), + (1200, 0.021, 50), + (1250, 0.0211, 50), + (1300, 0.0166, 50), + (1350, 0.0042, 50), + (1400, 0.001, 50), + (1450, 0.0044, 50), + (1500, 0.0095, 50), + (1550, 0.0123, 50), + (1600, 0.011, 50), + (1650, 0.0106, 50), + (1700, 0.0093, 50), + (1750, 0.0068, 50), + (1800, 0.0024, 50), + (1850, 0.0005, 50), + (1900, 0.0002, 50), + (1950, 0.0012, 50), + (2000, 0.003, 50), + (2050, 0.0037, 50), + (2100, 0.0057, 75), + (2200, 0.0066, 100), + (2300, 0.006, 100), + (2400, 0.0041, 100), + (2500, 0.0006, 50) + }; + private List<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeightsList = new List<(int, double, double)> + { + (300, 0, 5), + (305, 0.000057, 5), + (310, 0.000236, 5), + (315, 0.000554, 5), + (320, 0.000916, 5), + (325, 0.001309, 5), + (330, 0.001914, 5), + (335, 0.002018, 5), + (340, 0.002189, 5), + (345, 0.00226, 5), + (350, 0.002445, 5), + (355, 0.002555, 5), + (360, 0.002683, 5), + (365, 0.00302, 5), + (370, 0.003359, 5), + (375, 0.003509, 5), + (380, 0.0036, 5), + (385, 0.003529, 5), + (390, 0.003551, 5), + (395, 0.004294, 5), + (400, 0.007812, 10), + (410, 0.011638, 10), + (420, 0.011877, 10), + (430, 0.011347, 10), + (440, 0.013246, 10), + (450, 0.015343, 10), + (460, 0.016166, 10), + (470, 0.016178, 10), + (480, 0.016402, 10), + (490, 0.015794, 10), + (500, 0.015801, 10), + (510, 0.015973, 10), + (520, 0.015357, 10), + (530, 0.015867, 10), + (540, 0.015827, 10), + (550, 0.015844, 10), + (560, 0.01559, 10), + (570, 0.015256, 10), + (580, 0.014745, 10), + (590, 0.01433, 10), + (600, 0.014663, 10), + (610, 0.01503, 10), + (620, 0.014859, 10), + (630, 0.014622, 10), + (640, 0.014526, 10), + (650, 0.014445, 10), + (660, 0.014313, 10), + (670, 0.014023, 10), + (680, 0.012838, 10), + (690, 0.011788, 10), + (700, 0.012453, 10), + (710, 0.012798, 10), + (720, 0.010589, 10), + (730, 0.011233, 10), + (740, 0.012175, 10), + (750, 0.012181, 10), + (760, 0.009515, 10), + (770, 0.010479, 10), + (780, 0.011381, 10), + (790, 0.011262, 10), + (800, 0.028718, 50), + (850, 0.04824, 50), + (900, 0.040297, 50), + (950, 0.021384, 50), + (1000, 0.036097, 50), + (1050, 0.03411, 50), + (1100, 0.018861, 50), + (1150, 0.013228, 50), + (1200, 0.022551, 50), + (1250, 0.023376, 50), + (1300, 0.017756, 50), + (1350, 0.003743, 50), + (1400, 0.000741, 50), + (1450, 0.003792, 50), + (1500, 0.009693, 50), + (1550, 0.013693, 50), + (1600, 0.012203, 50), + (1650, 0.010615, 50), + (1700, 0.007256, 50), + (1750, 0.007183, 50), + (1800, 0.002157, 50), + (1850, 0.000398, 50), + (1900, 0.000082, 50), + (1950, 0.001087, 50), + (2000, 0.003024, 50), + (2050, 0.003988, 50), + (2100, 0.004229, 50), + (2150, 0.004142, 50), + (2200, 0.00369, 50), + (2250, 0.003592, 50), + (2300, 0.003436, 50), + (2350, 0.003163, 50), + (2400, 0.002233, 50), + (2450, 0.001202, 50) + }; + public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeights { get; } + public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeights { get; } + public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeights { get; } public SpectralToIntegralMethod() { - en410WavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410WavelengthsWeightsList); + en410VisibleWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410VisibleWavelengthsWeightsList); + en410SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410SolarWavelengthsWeightsList); + iso9050SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(iso9050SolarWavelengthsWeightsList); } - public List Calculate(IReadOnlyList spectralDataPoints, string standard) + public List Calculate(IReadOnlyList spectralDataPoints, StandardType standard) { // Turn IReadOnlyLists into more flexible Lists List spectralDataPointsToFilter = new List(spectralDataPoints); List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; switch (standard) { - case "EN410": - wavelengthsWeights = en410WavelengthsWeights.ToList(); + case "StandardType.en410Visible": + wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); + break; + case "StandardType.en410Solar": + wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); + break; + case "StandardType.iso9050Solar": + wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; } // Filter data points which have a wavelength which is out of bounds. List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); // Sort the dataPoints List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); - // // Print filtered and sorted spectralDataPoints - // foreach (DataPoint dataPoint in spectralDataPointsSorted) - // { - // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); - // } double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; for (int i = 0; i < 2/*wavelengthsWeights.Count*/; i++) @@ -98,25 +258,19 @@ public List Calculate(IReadOnlyList spectralDataPoints, st if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) { spectralDataPointWavelengthAbove = spectralDataPointsSorted[j]; } } - // Calculate width of the wavelength interval at weightingDataPointsSorted[i] - switch (standard) - { - case "EN410": - deltaWavelength = 10; - break; - } // Trapezoidal rule to calculate an integral + deltaWavelength = wavelengthsWeights[i].deltaWavelength; averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; numerator += averageValue * deltaWavelength * wavelengthsWeights[i].weight; denominator += deltaWavelength * wavelengthsWeights[i].weight; - // Print debug output - foreach (DataPoint dataPoint in new DataPoint[] { spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) - { - Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); - } - Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); + // // Print debug output + // foreach (DataPoint dataPoint in new DataPoint[] { spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + // { + // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); + // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); + // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + // } + // Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); } DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 6bbe45ba..72a807fd 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,7 +24,7 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, string standard) + public List Calculate(IReadOnlyList spectralDataPoints, StandardType standard) { throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with a standard as input."); } diff --git a/backend/test/Play.cs b/backend/test/Play.cs index a4701196..7d68fb87 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -41,7 +41,7 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, "EN410"); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, "en410Solar"); foreach (DataPoint integralDataPoint in integralDataPoints) { Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); @@ -49,8 +49,6 @@ public async Task Do() Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } - Console.WriteLine(mySpectralToIntegralMethod.en410WavelengthsWeights); - await Task.FromResult(0); } } \ No newline at end of file From 855594ab2106a2e4ab12410139437343a5c11e56 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 15:48:13 +0200 Subject: [PATCH 424/878] Use enumeration `StandardType` --- backend/src/Methods/SpectralToIntegralMethod.cs | 8 ++++---- backend/src/Methods/TransmittanceSumMethod.cs | 2 +- backend/test/Play.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 2735e004..ece030b5 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -222,20 +222,20 @@ public SpectralToIntegralMethod() iso9050SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(iso9050SolarWavelengthsWeightsList); } - public List Calculate(IReadOnlyList spectralDataPoints, StandardType standard) + public List Calculate(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) { // Turn IReadOnlyLists into more flexible Lists List spectralDataPointsToFilter = new List(spectralDataPoints); List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; switch (standard) { - case "StandardType.en410Visible": + case IMethod.StandardType.en410Visible: wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; - case "StandardType.en410Solar": + case IMethod.StandardType.en410Solar: wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; - case "StandardType.iso9050Solar": + case IMethod.StandardType.iso9050Solar: wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; } diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 72a807fd..7d00a4f9 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,7 +24,7 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, StandardType standard) + public List Calculate(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) { throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with a standard as input."); } diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 7d68fb87..3d444b4b 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -41,7 +41,7 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, "en410Solar"); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, IMethod.StandardType.en410Solar); foreach (DataPoint integralDataPoint in integralDataPoints) { Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); From 7bd0eee8b866a373c3c460022af6cbc57f14e64c Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 16:21:30 +0200 Subject: [PATCH 425/878] Calculate the integral values for three standards --- .../CalculateWeightedIntegralByJoHa.cs | 22 ------------------- backend/test/Play.cs | 15 ++++++++----- 2 files changed, 10 insertions(+), 27 deletions(-) delete mode 100644 backend/src/Methods/CalculateWeightedIntegralByJoHa.cs diff --git a/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs b/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs deleted file mode 100644 index 67e8c0ce..00000000 --- a/backend/src/Methods/CalculateWeightedIntegralByJoHa.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace JohannesHanek -{ - public sealed class JohannesHanek() - { - static double CalculateWeightedIntegral(List<(double Wavelength, double Intensity)> data, double weight) - { - double integral = 0.0; - - for (int i = 0; i < data.Count - 1; i++) - { - // Trapezregel zur Integralberechnung - double deltaX = data[i + 1].Wavelength - data[i].Wavelength; - double averageY = (data[i].Intensity + data[i + 1].Intensity) / 2; - integral += averageY * deltaX * weight; - } - - return integral; - } - } -} \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 3d444b4b..4cc98a5e 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -41,12 +41,17 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, IMethod.StandardType.en410Solar); - foreach (DataPoint integralDataPoint in integralDataPoints) + + foreach (IMethod.StandardType standard in Enum.GetValues()) { - Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); + List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, standard); + Console.WriteLine($"Standard: {standard}"); + foreach (DataPoint integralDataPoint in integralDataPoints) + { + Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); + } } await Task.FromResult(0); From ef78366730010a0d841b09573989d49bf363295c Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 17:23:32 +0200 Subject: [PATCH 426/878] Bug: step function result of iso9050Solar does not fit to Excel calculation --- .../src/Methods/SpectralToIntegralMethod.cs | 17 ++++--- backend/test/Play.cs | 44 ++++++++----------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index ece030b5..3a9a5cbb 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -233,10 +233,10 @@ public List Calculate(IReadOnlyList spectralDataPoints, IM wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; case IMethod.StandardType.en410Solar: - wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); + wavelengthsWeights = en410SolarWavelengthsWeights.ToList(); break; case IMethod.StandardType.iso9050Solar: - wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); + wavelengthsWeights = iso9050SolarWavelengthsWeights.ToList(); break; } // Filter data points which have a wavelength which is out of bounds. @@ -245,7 +245,8 @@ public List Calculate(IReadOnlyList spectralDataPoints, IM List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; - for (int i = 0; i < 2/*wavelengthsWeights.Count*/; i++) + for (int i = 0; i < wavelengthsWeights.Count; i++) + // for (int i = 0; i < 3; i++) { wavelengthWeighting = wavelengthsWeights[i].wavelength; DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); @@ -263,14 +264,12 @@ public List Calculate(IReadOnlyList spectralDataPoints, IM averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; numerator += averageValue * deltaWavelength * wavelengthsWeights[i].weight; denominator += deltaWavelength * wavelengthsWeights[i].weight; - // // Print debug output - // foreach (DataPoint dataPoint in new DataPoint[] { spectralDataPointWavelengthBelow, spectralDataPointWavelengthAbove }) + // Debug output + // if (i == 2) // { - // Console.WriteLine($"Incidence Wavelength: {dataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {dataPoint.Incidence.Direction.Polar}"); - // Console.WriteLine($"Emergence Direction Polar: {dataPoint.Emergence.Direction.Polar}"); - // Console.WriteLine($"Results Transmittance: {dataPoint.Results.Transmittance}"); + // Console.WriteLine($"\nwavelengthWeighting={wavelengthWeighting}, wavelengthsWeights[i].weight={wavelengthsWeights[i].weight}, deltaWavelength ={deltaWavelength}, averageValue={averageValue}, numerator={numerator}, denominator={denominator}, ratio={numerator / denominator}\n"); // } - // Console.WriteLine($"deltaWavelength = {deltaWavelength}\naverageValue = {averageValue}\nnumerator = {numerator}\ndenominator = {denominator}\n"); + // Console.WriteLine($"ratio={numerator / denominator}"); } DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 4cc98a5e..7e60bdb4 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -14,34 +14,28 @@ public async Task Do() { Console.WriteLine("Do play!"); - // Create example spectralDataPoints - var direction = new Direction(8); - var wavelengths0 = new Wavelengths(450); - var incidence0 = new Incidence(wavelengths0, direction); - var emergence0 = new Emergence(direction); - var results0 = new Results(0.1); - var wavelengths1 = new Wavelengths(550); - var incidence1 = new Incidence(wavelengths1, direction); - var emergence1 = new Emergence(direction); - var results1 = new Results(0.7); - var wavelengths2 = new Wavelengths(650); - var incidence2 = new Incidence(wavelengths2, direction); - var emergence2 = new Emergence(direction); - var results2 = new Results(0.8); - var dataPoint0 = new DataPoint(incidence0, emergence0, results0); - var dataPoint1 = new DataPoint(incidence1, emergence1, results1); - var dataPoint2 = new DataPoint(incidence2, emergence2, results2); - DataPoint dataPoint3 = new DataPoint(new Incidence(new Wavelengths(-100), direction), new Emergence(direction), new Results(99)); - IReadOnlyList spectralDataPoints = new List { dataPoint0, dataPoint2, dataPoint1, dataPoint3 }; - - // // Create example weightingSpectrum - // DataPoint dataPoint10 = new DataPoint(new Incidence(new Wavelengths(500), direction), new Emergence(direction), new Results(2)); - // DataPoint dataPoint11 = new DataPoint(new Incidence(new Wavelengths(600), direction), new Emergence(direction), new Results(0.5)); - // IReadOnlyList weightingSpectrum = new List { dataPoint10, dataPoint11 }; + //// Create example spectralDataPoints + // // Flat spectrum + // IReadOnlyList spectralDataPoints = new List { + // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), + // new DataPoint(new Incidence(new Wavelengths(800), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), + // new DataPoint(new Incidence(new Wavelengths(810), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), + // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)) }; + // only non-visible values + IReadOnlyList spectralDataPoints = new List { + new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(780), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(781), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(1)) }; + // // only visible values + // IReadOnlyList spectralDataPoints = new List { + // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + // new DataPoint(new Incidence(new Wavelengths(780), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + // new DataPoint(new Incidence(new Wavelengths(781), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - foreach (IMethod.StandardType standard in Enum.GetValues()) { List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, standard); From 94254e1746f69886d5887433fad7b35c587fc00b Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 2 Jul 2025 17:50:48 +0200 Subject: [PATCH 427/878] Validation against Excel tool of TLSF. Accurate to 15 digits --- backend/test/Play.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 7e60bdb4..dec9351f 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -21,18 +21,18 @@ public async Task Do() // new DataPoint(new Incidence(new Wavelengths(800), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), // new DataPoint(new Incidence(new Wavelengths(810), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)) }; - // only non-visible values - IReadOnlyList spectralDataPoints = new List { - new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - new DataPoint(new Incidence(new Wavelengths(780), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - new DataPoint(new Incidence(new Wavelengths(781), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(1)) }; - // // only visible values + // // only non-visible values // IReadOnlyList spectralDataPoints = new List { - // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - // new DataPoint(new Incidence(new Wavelengths(780), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - // new DataPoint(new Incidence(new Wavelengths(781), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; + // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + // new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + // new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(1)) }; + // only visible values + IReadOnlyList spectralDataPoints = new List { + new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); From 24a05b8ab987f4671c25935b24637b64f0085b07 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 3 Jul 2025 17:23:54 +0200 Subject: [PATCH 428/878] Return all integral values as a List --- backend/src/Methods/IMethod.cs | 2 - .../src/Methods/SpectralToIntegralMethod.cs | 37 +++++++++++-------- backend/src/Methods/TransmittanceSumMethod.cs | 5 --- backend/test/Play.cs | 18 ++++----- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 55873801..4941c3c6 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -9,7 +9,5 @@ public interface IMethod string Name { get; } Guid Id { get; } public enum StandardType { en410Visible, en410Solar, iso9050Solar } - List Calculate(IReadOnlyList dataPoints); - List Calculate(IReadOnlyList spectralDataPoints, StandardType standard); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 3a9a5cbb..fd4233f6 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -222,7 +222,20 @@ public SpectralToIntegralMethod() iso9050SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(iso9050SolarWavelengthsWeightsList); } - public List Calculate(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) + public List Calculate(IReadOnlyList dataPoints) + { + IMethod.StandardType[] standards = Enum.GetValues(); + List resultsForOneStandard = new List(); + List resultsForAllStandards = new List(); + for (int i = 0; i < standards.Length; i++) + { + resultsForOneStandard = CalculateOneStandard(dataPoints, standards[i]); + resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(i), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); + } + return resultsForAllStandards; + } + + public List CalculateOneStandard(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) { // Turn IReadOnlyLists into more flexible Lists List spectralDataPointsToFilter = new List(spectralDataPoints); @@ -243,7 +256,7 @@ public List Calculate(IReadOnlyList spectralDataPoints, IM List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); // Sort the dataPoints List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); - double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D; + double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D, integralValue = 0.0D; for (int i = 0; i < wavelengthsWeights.Count; i++) // for (int i = 0; i < 3; i++) @@ -264,18 +277,12 @@ public List Calculate(IReadOnlyList spectralDataPoints, IM averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; numerator += averageValue * deltaWavelength * wavelengthsWeights[i].weight; denominator += deltaWavelength * wavelengthsWeights[i].weight; - // Debug output - // if (i == 2) - // { - // Console.WriteLine($"\nwavelengthWeighting={wavelengthWeighting}, wavelengthsWeights[i].weight={wavelengthsWeights[i].weight}, deltaWavelength ={deltaWavelength}, averageValue={averageValue}, numerator={numerator}, denominator={denominator}, ratio={numerator / denominator}\n"); - // } - // Console.WriteLine($"ratio={numerator / denominator}"); } + integralValue = numerator / denominator; + return [new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(integralValue))]; + } - DataPoint integralDataPoint = new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(numerator / denominator)); - return [integralDataPoint]; - } // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) @@ -283,9 +290,9 @@ private static bool WavelengthOutOfBounds(DataPoint dataPoint) return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 3000); } - public List Calculate(IReadOnlyList dataPoints) - { - throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); - } + // public List Calculate(IReadOnlyList dataPoints) + // { + // throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); + // } } \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 7d00a4f9..cd305d20 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -24,9 +24,4 @@ public List Calculate(IReadOnlyList dataPoints) ]; } - public List Calculate(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) - { - throw new InvalidOperationException("The TransmittanceSumMethod cannot be used with a standard as input."); - } - } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index dec9351f..5e819eed 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -36,17 +36,17 @@ public async Task Do() // Use the SpectralToIntegralMethod SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - foreach (IMethod.StandardType standard in Enum.GetValues()) + // foreach (IMethod.StandardType standard in Enum.GetValues()) + // { + List results = mySpectralToIntegralMethod.Calculate(spectralDataPoints); + // Console.WriteLine($"Standard: {standard}"); + foreach (DataPoint integralDataPoint in results) { - List integralDataPoints = mySpectralToIntegralMethod.Calculate(spectralDataPoints, standard); - Console.WriteLine($"Standard: {standard}"); - foreach (DataPoint integralDataPoint in integralDataPoints) - { - Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); - } + Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); + Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } + // } await Task.FromResult(0); } From bc42f1df80ab1d23e0835d7ca5e62a786eeca258 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 3 Jul 2025 17:40:40 +0200 Subject: [PATCH 429/878] Create the methods at buildingenvelopedata.org and update the uuid of the methods --- backend/src/Methods/SpectralToIntegralMethod.cs | 2 +- backend/src/Methods/TransmittanceSumMethod.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index fd4233f6..4dc90df3 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -10,7 +10,7 @@ namespace Database.Methods; public sealed class SpectralToIntegralMethod : IMethod { public string Name => "SpectralToIntegral"; - public Guid Id => Guid.Parse("5abb16b2-7161-470c-9744-85dd14b0e637"); + public Guid Id => Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> { (380, 0, 5), diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index cd305d20..efae0a63 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -8,7 +8,7 @@ public sealed class TransmittanceSumMethod : IMethod { public string Name => "TransmittanceSum"; - public Guid Id => Guid.Parse("dfa3a7b3-a6da-444c-b43f-5ffd021c4a5c"); + public Guid Id => Guid.Parse("8a7684c7-aaca-4057-8a30-1cb951c2c6a0"); public List Calculate(IReadOnlyList dataPoints) { From 2af8a5ec4f1929e71674e5444f54eda64a8d7fae Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 7 Jul 2025 17:48:43 +0200 Subject: [PATCH 430/878] first fluentassertion fails --- .../src/Methods/SpectralToIntegralMethod.cs | 11 ++---- .../Methods/SpectralToIntegralMethodTests.cs | 37 +++++++++++++++++++ backend/test/Play.cs | 34 ----------------- 3 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 backend/test/Methods/SpectralToIntegralMethodTests.cs diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 4dc90df3..37603b8a 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -11,8 +11,7 @@ public sealed class SpectralToIntegralMethod : IMethod { public string Name => "SpectralToIntegral"; public Guid Id => Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); - private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> - { + private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> { (380, 0, 5), (390, 0.0005, 10), (400, 0.003, 10), @@ -55,8 +54,7 @@ public sealed class SpectralToIntegralMethod : IMethod (770, 0, 10), (780, 0, 5) }; - private List<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeightsList = new List<(int, double, double)> - { + private List<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeightsList = new List<(int, double, double)> { (300, 0.0005, 10), (320, 0.0069, 20), (340, 0.0122, 20), @@ -114,9 +112,8 @@ public sealed class SpectralToIntegralMethod : IMethod (2400, 0.0041, 100), (2500, 0.0006, 50) }; - private List<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeightsList = new List<(int, double, double)> - { - (300, 0, 5), + private List<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeightsList = new List<(int, double, double)> { + (300, 0, 5), (305, 0.000057, 5), (310, 0.000236, 5), (315, 0.000554, 5), diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs new file mode 100644 index 00000000..c29b4a6a --- /dev/null +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; +using NUnit.Framework; +using Database.Methods; +using Database.GraphQl.MethodAsService; +using System.Collections.Generic; +using FluentAssertations; + +namespace Database.Tests.Methods; + +public sealed class SpectralToIntegralMethodTests +{ + [Test] + public void CalculateTest() + { + //// Create example spectralDataPoints + // only visible values + IReadOnlyList spectralDataPoints = new List { + new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; + + // Use the SpectralToIntegralMethod + SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); + List results = mySpectralToIntegralMethod.Calculate(spectralDataPoints); + foreach (DataPoint integralDataPoint in results) + { + if (integralDataPoint.Incidence.Wavelengths.Wavelength==0) + {integralDataPoint.Results.Transmittance.Should().BeApproximately(1.0F, 0.0000000000001F);} + } + + // Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + // Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); + // Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); + } +} \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 5e819eed..049b8fbc 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -14,40 +14,6 @@ public async Task Do() { Console.WriteLine("Do play!"); - //// Create example spectralDataPoints - // // Flat spectrum - // IReadOnlyList spectralDataPoints = new List { - // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), - // new DataPoint(new Incidence(new Wavelengths(800), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), - // new DataPoint(new Incidence(new Wavelengths(810), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)), - // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0.5)) }; - // // only non-visible values - // IReadOnlyList spectralDataPoints = new List { - // new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - // new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - // new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - // new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(1)) }; - // only visible values - IReadOnlyList spectralDataPoints = new List { - new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; - - // Use the SpectralToIntegralMethod - SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - // foreach (IMethod.StandardType standard in Enum.GetValues()) - // { - List results = mySpectralToIntegralMethod.Calculate(spectralDataPoints); - // Console.WriteLine($"Standard: {standard}"); - foreach (DataPoint integralDataPoint in results) - { - Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); - Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); - } - // } - await Task.FromResult(0); } } \ No newline at end of file From 007f1f8d91b41bba01d0d8fdce3eb3fb9eeac740 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 9 Jul 2025 17:50:29 +0200 Subject: [PATCH 431/878] Successful `SpectralToIntegralMethodTests.cs` instead of `Play.cs` --- .../test/Methods/SpectralToIntegralMethodTests.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index c29b4a6a..b1e0b2f2 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -4,7 +4,7 @@ using Database.Methods; using Database.GraphQl.MethodAsService; using System.Collections.Generic; -using FluentAssertations; +using FluentAssertions; namespace Database.Tests.Methods; @@ -27,11 +27,14 @@ public void CalculateTest() foreach (DataPoint integralDataPoint in results) { if (integralDataPoint.Incidence.Wavelengths.Wavelength==0) - {integralDataPoint.Results.Transmittance.Should().BeApproximately(1.0F, 0.0000000000001F);} + {integralDataPoint.Results.Transmittance.Should().BeApproximately(1.0F, 0.000000000000001F);} + if (integralDataPoint.Incidence.Wavelengths.Wavelength==1) + {integralDataPoint.Results.Transmittance.Should().BeApproximately(0.3733675409177318F, 0.00000001F);} + if (integralDataPoint.Incidence.Wavelengths.Wavelength==2) + {integralDataPoint.Results.Transmittance.Should().BeApproximately(0.212543149312381F, 0.00000001F);} + // Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); + // Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); + // Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } - - // Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); - // Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - // Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); } } \ No newline at end of file From 126d6302643dbb92c15f7d265782f99178e98597 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 22 Jul 2025 16:52:09 +0200 Subject: [PATCH 432/878] `MethodCalculationService` should be added as singleton --- backend/src/Startup.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 23def29e..275a5252 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -114,7 +114,8 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO I consider the flattened structure a bug. How can we solve this? } ); - services.AddOpenApi("v1", _ => { + services.AddOpenApi("v1", _ => + { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; }); } @@ -198,7 +199,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. services.AddDbContext(options => - {}, + { }, contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton ); From db053276284e81555e03dd80366a8c5367f2cca7 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 22 Jul 2025 16:55:06 +0200 Subject: [PATCH 433/878] Fix the list of methods --- backend/src/Services/MethodCalculationService.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs index f978940f..9eda5f49 100644 --- a/backend/src/Services/MethodCalculationService.cs +++ b/backend/src/Services/MethodCalculationService.cs @@ -9,12 +9,10 @@ namespace Database.Services; public sealed class MethodCalculationService { - private List _methods = []; - - public void AddMethod(IMethod method) - { - _methods.Add(method); - } + private List _methods = [ + new TransmittanceSumMethod(), + new SpectralToIntegralMethod(), + ]; public bool MethodExists(Guid methodId) { From 23e8a0791e68a4c45db24f931a96d44e089460d9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 16:54:45 +0200 Subject: [PATCH 434/878] Undo some unintended changes (line breaks and unnecessary imports and such) --- backend/src/Methods/TransmittanceSumMethod.cs | 1 - backend/test/Play.cs | 4 ---- 2 files changed, 5 deletions(-) diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index efae0a63..4659eeb6 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -23,5 +23,4 @@ public List Calculate(IReadOnlyList dataPoints) new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)) ]; } - } \ No newline at end of file diff --git a/backend/test/Play.cs b/backend/test/Play.cs index 049b8fbc..bcbcda76 100644 --- a/backend/test/Play.cs +++ b/backend/test/Play.cs @@ -1,9 +1,6 @@ using System; using System.Threading.Tasks; using NUnit.Framework; -using Database.Methods; -using Database.GraphQl.MethodAsService; -using System.Collections.Generic; namespace Database.Tests; @@ -13,7 +10,6 @@ public sealed class Play public async Task Do() { Console.WriteLine("Do play!"); - await Task.FromResult(0); } } \ No newline at end of file From 1fc28475b6f150e4589a62022e6c3ecaa94926be Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Sep 2025 18:25:03 +0200 Subject: [PATCH 435/878] Add information for polymorphoic resolution when serializing and deserializing JSON from and into the domain model --- backend/src/Data/IApproval.cs | 5 +++++ backend/src/Data/IData.cs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 53766270..8e457331 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,10 +1,15 @@ using System; using System.Text.Json; +using System.Text.Json.Serialization; +using Database.Configuration; using HotChocolate.Types; namespace Database.Data; [InterfaceType("Approval")] +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonDerivedType(typeof(ResponseApproval), typeDiscriminator: nameof(ResponseApproval))] +[JsonDerivedType(typeof(DataApproval), typeDiscriminator: nameof(DataApproval))] public interface IApproval { DateTime Timestamp { get; } diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 23db9341..cb855f6b 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,8 +1,16 @@ using System; using System.Collections.Generic; +using System.Text.Json.Serialization; +using Database.Configuration; namespace Database.Data; +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonDerivedType(typeof(CalorimetricData), typeDiscriminator: nameof(CalorimetricData))] +[JsonDerivedType(typeof(GeometricData), typeDiscriminator: nameof(GeometricData))] +[JsonDerivedType(typeof(HygrothermalData), typeDiscriminator: nameof(HygrothermalData))] +[JsonDerivedType(typeof(OpticalData), typeDiscriminator: nameof(OpticalData))] +[JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] public interface IData : IEntity { Guid ComponentId { get; } From 938dfbbbf29068ea98fea3982559780da01d56f3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:38:44 +0200 Subject: [PATCH 436/878] Use `var` instead of explicit type wherever possible --- .../src/Methods/SpectralToIntegralMethod.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 37603b8a..fdc8b5f6 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -221,10 +221,10 @@ public SpectralToIntegralMethod() public List Calculate(IReadOnlyList dataPoints) { - IMethod.StandardType[] standards = Enum.GetValues(); - List resultsForOneStandard = new List(); - List resultsForAllStandards = new List(); - for (int i = 0; i < standards.Length; i++) + var standards = Enum.GetValues(); + var resultsForOneStandard = new List(); + var resultsForAllStandards = new List(); + for (var i = 0; i < standards.Length; i++) { resultsForOneStandard = CalculateOneStandard(dataPoints, standards[i]); resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(i), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); @@ -235,7 +235,7 @@ public List Calculate(IReadOnlyList dataPoints) public List CalculateOneStandard(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) { // Turn IReadOnlyLists into more flexible Lists - List spectralDataPointsToFilter = new List(spectralDataPoints); + var spectralDataPointsToFilter = new List(spectralDataPoints); List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; switch (standard) { @@ -250,18 +250,18 @@ public List CalculateOneStandard(IReadOnlyList spectralDat break; } // Filter data points which have a wavelength which is out of bounds. - List spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); + var spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); // Sort the dataPoints - List spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); + var spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D, integralValue = 0.0D; - for (int i = 0; i < wavelengthsWeights.Count; i++) + for (var i = 0; i < wavelengthsWeights.Count; i++) // for (int i = 0; i < 3; i++) { wavelengthWeighting = wavelengthsWeights[i].wavelength; - DataPoint spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - DataPoint spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - for (int j = 0; j < spectralDataPointsSorted.Count; j++) + var spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + var spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); + for (var j = 0; j < spectralDataPointsSorted.Count; j++) { // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) From 2a7aa21797f62b39a9af00fb867c75f5a199ffd5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:39:37 +0200 Subject: [PATCH 437/878] Add explicit visibility modifiers --- backend/src/Methods/IMethod.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 4941c3c6..f93ef59e 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -6,8 +6,8 @@ namespace Database.Methods; public interface IMethod { - string Name { get; } - Guid Id { get; } + public string Name { get; } + public Guid Id { get; } public enum StandardType { en410Visible, en410Solar, iso9050Solar } - List Calculate(IReadOnlyList dataPoints); + public List Calculate(IReadOnlyList dataPoints); } \ No newline at end of file From 7bf9efeac22cd41959e21f55dbb1c1d8ad31c650 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:44:51 +0200 Subject: [PATCH 438/878] Remove `suffix` `Type` from calculation standard enumeration, add the prefix `Calculation`, use upper-case member names for consistency with other enumerations, and move the enumeration into its own file --- backend/src/Methods/CalculationStandard.cs | 11 +++++++++++ backend/src/Methods/IMethod.cs | 1 - backend/src/Methods/SpectralToIntegralMethod.cs | 10 +++++----- 3 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 backend/src/Methods/CalculationStandard.cs diff --git a/backend/src/Methods/CalculationStandard.cs b/backend/src/Methods/CalculationStandard.cs new file mode 100644 index 00000000..167ff7d7 --- /dev/null +++ b/backend/src/Methods/CalculationStandard.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Methods; + +[SuppressMessage("Naming", "CA1707")] +public enum CalculationStandard +{ + EN_410_VISIBLE, + EN_410_SOLAR, + ISO_9050_SOLAR +} \ No newline at end of file diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index f93ef59e..4b59e9a3 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -8,6 +8,5 @@ public interface IMethod { public string Name { get; } public Guid Id { get; } - public enum StandardType { en410Visible, en410Solar, iso9050Solar } public List Calculate(IReadOnlyList dataPoints); } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index fdc8b5f6..7cf4cb53 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -221,7 +221,7 @@ public SpectralToIntegralMethod() public List Calculate(IReadOnlyList dataPoints) { - var standards = Enum.GetValues(); + var standards = Enum.GetValues(); var resultsForOneStandard = new List(); var resultsForAllStandards = new List(); for (var i = 0; i < standards.Length; i++) @@ -232,20 +232,20 @@ public List Calculate(IReadOnlyList dataPoints) return resultsForAllStandards; } - public List CalculateOneStandard(IReadOnlyList spectralDataPoints, IMethod.StandardType standard) + public List CalculateOneStandard(IReadOnlyList spectralDataPoints, CalculationStandard standard) { // Turn IReadOnlyLists into more flexible Lists var spectralDataPointsToFilter = new List(spectralDataPoints); List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; switch (standard) { - case IMethod.StandardType.en410Visible: + case CalculationStandard.EN_410_VISIBLE: wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); break; - case IMethod.StandardType.en410Solar: + case CalculationStandard.EN_410_SOLAR: wavelengthsWeights = en410SolarWavelengthsWeights.ToList(); break; - case IMethod.StandardType.iso9050Solar: + case CalculationStandard.ISO_9050_SOLAR: wavelengthsWeights = iso9050SolarWavelengthsWeights.ToList(); break; } From 157575dab1b095bda9cdaaa6e5800702242319d9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:46:14 +0200 Subject: [PATCH 439/878] Remove unnecessary assignment of variable --- backend/src/Methods/SpectralToIntegralMethod.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 7cf4cb53..3f856d63 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -222,11 +222,10 @@ public SpectralToIntegralMethod() public List Calculate(IReadOnlyList dataPoints) { var standards = Enum.GetValues(); - var resultsForOneStandard = new List(); var resultsForAllStandards = new List(); for (var i = 0; i < standards.Length; i++) { - resultsForOneStandard = CalculateOneStandard(dataPoints, standards[i]); + var resultsForOneStandard = CalculateOneStandard(dataPoints, standards[i]); resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(i), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); } return resultsForAllStandards; From 2adbbd1c5f34f731a4f066e11af52fac41419c2e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:54:23 +0200 Subject: [PATCH 440/878] Use `foreach` instead of `for` wherever possible --- backend/src/Extensions/EnumerableExtensions.cs | 13 +++++++++++++ backend/src/Methods/SpectralToIntegralMethod.cs | 9 ++++----- 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 backend/src/Extensions/EnumerableExtensions.cs diff --git a/backend/src/Extensions/EnumerableExtensions.cs b/backend/src/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..52cd51e3 --- /dev/null +++ b/backend/src/Extensions/EnumerableExtensions.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Database.Extensions; + +public static class EnumerableExtensions +{ + // An alternative would be `Index` from https://github.com/morelinq/MoreLINQ + public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable source) + { + return source.Select((item, index) => (item, index)); + } +} \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 3f856d63..34fa2275 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Database.Extensions; using Database.GraphQl.MethodAsService; -using Database.Methods; namespace Database.Methods; @@ -221,12 +221,11 @@ public SpectralToIntegralMethod() public List Calculate(IReadOnlyList dataPoints) { - var standards = Enum.GetValues(); var resultsForAllStandards = new List(); - for (var i = 0; i < standards.Length; i++) + foreach (var (standard, wavelength) in Enum.GetValues().WithIndex()) { - var resultsForOneStandard = CalculateOneStandard(dataPoints, standards[i]); - resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(i), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); + var resultsForOneStandard = CalculateOneStandard(dataPoints, standard); + resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(wavelength), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); } return resultsForAllStandards; } From 6e7e4391d450aa6d5596c61f54f5a6e439281e6c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 10:55:48 +0200 Subject: [PATCH 441/878] Improve layout --- backend/src/Methods/SpectralToIntegralMethod.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 34fa2275..5edcca6b 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -225,7 +225,16 @@ public List Calculate(IReadOnlyList dataPoints) foreach (var (standard, wavelength) in Enum.GetValues().WithIndex()) { var resultsForOneStandard = CalculateOneStandard(dataPoints, standard); - resultsForAllStandards.AddRange([new DataPoint(new Incidence(new Wavelengths(wavelength), new Direction(0)), new Emergence(new Direction(0)), new Results(resultsForOneStandard[0].Results.Transmittance))]); + resultsForAllStandards.AddRange([ + new DataPoint( + new Incidence( + new Wavelengths(wavelength), + new Direction(0) + ), + new Emergence(new Direction(0) + ), + new Results(resultsForOneStandard[0].Results.Transmittance)) + ]); } return resultsForAllStandards; } From 060834fb33c3e7fd37d686e3c9a1f77875a320b0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 11:18:57 +0200 Subject: [PATCH 442/878] Use a new instance of each method for each calculation to avoid race conditions and side-effects (from multiple simulataneous calculations working on the same instance and affecting each other), simplify and rename the calculation service to avoid unnecessary error sources, improve layout, and add const and readonly modifiers wherever possible (and sensible) --- .../MethodAsService/MethodAsServiceQueries.cs | 22 +++++++-------- .../src/Methods/SpectralToIntegralMethod.cs | 5 ++-- backend/src/Methods/TransmittanceSumMethod.cs | 12 ++++---- .../src/Services/MethodCalculationService.cs | 28 ------------------- backend/src/Services/MethodFactory.cs | 17 +++++++++++ backend/src/Startup.cs | 2 +- 6 files changed, 39 insertions(+), 47 deletions(-) delete mode 100644 backend/src/Services/MethodCalculationService.cs create mode 100644 backend/src/Services/MethodFactory.cs diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 808dcad4..84d01cbd 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -6,8 +6,8 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.ApiRequests.Dto; -using Database.Data; using Database.Services; +using GraphQL.Client.Abstractions.Utilities; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -22,17 +22,18 @@ private sealed record DataXResponse(DataXDto DataX); public async Task MethodAsServiceWithFileAsync( MethodAsServiceWithFileInput input, - MethodCalculationService methodCalculationService, + MethodFactory methodFactory, CancellationToken cancellationToken ) { - if (!methodCalculationService.MethodExists(input.MethodId)) + var method = methodFactory.GetMethod(input.MethodId); + if (method is null) { return new MethodAsServicePayload( new MethodAsServiceError( MethodAsServiceErrorCode.UNKNOWN_METHOD, $"The method is unknown.", - [] + [nameof(input), nameof(input.MethodId).ToLowerFirst()] ) ); } @@ -44,7 +45,7 @@ CancellationToken cancellationToken cancellationToken ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - var result = methodCalculationService.UseMethodToCalculate(input.MethodId, fileInput.Data.DataPoints); + var result = method.Calculate(fileInput.Data.DataPoints); return new MethodAsServicePayload(result); } @@ -52,9 +53,7 @@ CancellationToken cancellationToken public async Task MethodAsServiceAsync( AppSettings appSettings, MethodAsServiceInput input, - ApplicationDbContext context, - UserService userService, - MethodCalculationService methodCalculationService, + MethodFactory methodFactory, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, @@ -62,13 +61,14 @@ public async Task MethodAsServiceAsync( CancellationToken cancellationToken ) { - if (!methodCalculationService.MethodExists(input.MethodId)) + var method = methodFactory.GetMethod(input.MethodId); + if (method is null) { return new MethodAsServicePayload( new MethodAsServiceError( MethodAsServiceErrorCode.UNKNOWN_METHOD, $"The method is unknown.", - [] + [nameof(input), nameof(input.MethodId).ToLowerFirst()] ) ); } @@ -126,7 +126,7 @@ CancellationToken cancellationToken var filedata = await apiRequestService.QueryRest(new Uri(locator), httpClientFactory, httpContextAccessor, cancellationToken); - var result = methodCalculationService.UseMethodToCalculate(input.MethodId, filedata.Data.DataPoints.ToList()); + var result = methodFactory.Calculate(filedata.Data.DataPoints.ToList()); return new MethodAsServicePayload(result); } diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegralMethod.cs index 5edcca6b..7bab928c 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegralMethod.cs @@ -9,8 +9,9 @@ namespace Database.Methods; public sealed class SpectralToIntegralMethod : IMethod { - public string Name => "SpectralToIntegral"; - public Guid Id => Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); + public static readonly Guid Id = Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); + public const string Name = "SpectralToIntegral"; + private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> { (380, 0, 5), (390, 0.0005, 10), diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs index 4659eeb6..bb1c5aa1 100644 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSumMethod.cs @@ -6,9 +6,8 @@ namespace Database.Methods; public sealed class TransmittanceSumMethod : IMethod { - public string Name => "TransmittanceSum"; - - public Guid Id => Guid.Parse("8a7684c7-aaca-4057-8a30-1cb951c2c6a0"); + public static readonly Guid Id = Guid.Parse("8a7684c7-aaca-4057-8a30-1cb951c2c6a0"); + public const string Name = "TransmittanceSum"; public List Calculate(IReadOnlyList dataPoints) { @@ -17,10 +16,13 @@ public List Calculate(IReadOnlyList dataPoints) { transmittanceSum += dataPoint.Results.Transmittance; } - return [ - new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(transmittanceSum)) + new DataPoint( + new Incidence(new Wavelengths(0), new Direction(0)), + new Emergence(new Direction(0)), + new Results(transmittanceSum) + ) ]; } } \ No newline at end of file diff --git a/backend/src/Services/MethodCalculationService.cs b/backend/src/Services/MethodCalculationService.cs deleted file mode 100644 index 9eda5f49..00000000 --- a/backend/src/Services/MethodCalculationService.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Database.GraphQl.MethodAsService; -using Database.Methods; -using Database.Services; - -namespace Database.Services; - -public sealed class MethodCalculationService -{ - private List _methods = [ - new TransmittanceSumMethod(), - new SpectralToIntegralMethod(), - ]; - - public bool MethodExists(Guid methodId) - { - return _methods.Exists(x => x.Id == methodId); - } - - public List UseMethodToCalculate(Guid methodId, IReadOnlyList dataPoints) - { - var method = _methods.First(x => x.Id == methodId); - - return method.Calculate(dataPoints); - } -} \ No newline at end of file diff --git a/backend/src/Services/MethodFactory.cs b/backend/src/Services/MethodFactory.cs new file mode 100644 index 00000000..76d61869 --- /dev/null +++ b/backend/src/Services/MethodFactory.cs @@ -0,0 +1,17 @@ +using System; +using Database.Methods; + +namespace Database.Services; + +public sealed class MethodFactory +{ + public IMethod? GetMethod(Guid methodId) + { + return methodId switch + { + var _ when methodId == TransmittanceSumMethod.Id => new TransmittanceSumMethod(), + var _ when methodId == SpectralToIntegralMethod.Id => new SpectralToIntegralMethod(), + _ => null, + } + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 275a5252..a14a62a5 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -242,7 +242,7 @@ public static void ConfigureCustomServices(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); } From e7afc5ff2c9dd51a51e25cac49813cd5c806edcc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 11:23:49 +0200 Subject: [PATCH 443/878] Use collection expressions wherever possible --- backend/src/GraphQl/Databases/DatabaseQueries.cs | 2 +- .../src/GraphQl/MethodAsService/MethodAsServiceQueries.cs | 6 +----- backend/src/GraphQl/Users/UserQueries.cs | 2 +- backend/src/Utilities/FileHelpers.cs | 8 ++++---- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 22d9fd2e..51f40114 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -61,7 +61,7 @@ CancellationToken cancellationToken throw new GraphQLException( ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) .ToList()) // TODO Splitting the path at '.' is wrong in general. .SetMessage( $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 84d01cbd..9fd50e1d 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -37,16 +37,13 @@ CancellationToken cancellationToken ) ); } - await using var stream = input.File.OpenReadStream(); var fileInput = await JsonSerializer.DeserializeAsync( stream, JsonSerializerSettings.File, cancellationToken ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - var result = method.Calculate(fileInput.Data.DataPoints); - return new MethodAsServicePayload(result); } @@ -72,7 +69,6 @@ CancellationToken cancellationToken ) ); } - DatabaseDto? database; try { @@ -100,7 +96,7 @@ CancellationToken cancellationToken throw new GraphQLException( ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) .ToList()) // TODO Splitting the path at '.' is wrong in general. .SetMessage( $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index b209b1ed..f6d29e76 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -75,7 +75,7 @@ CancellationToken cancellationToken resolverContext.ReportError( ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? Array.Empty()) + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) .ToList()) // TODO Splitting the path at '.' is wrong in general. .SetMessage( $"Failed to deserialize GraphQL response of request to {appSettings.MetabaseHost}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 3fde3527..e0d8b3e1 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -98,7 +98,7 @@ public static async Task ProcessFormFile(IFormFile formFile, modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - return Array.Empty(); + return []; } if (formFile.Length > sizeLimit) @@ -108,7 +108,7 @@ public static async Task ProcessFormFile(IFormFile formFile, $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " + $"{megabyteSizeLimit:N1} MB."); - return Array.Empty(); + return []; } try @@ -146,7 +146,7 @@ public static async Task ProcessFormFile(IFormFile formFile, // Log the exception } - return Array.Empty(); + return []; } public static async Task ProcessStreamedFile( @@ -190,7 +190,7 @@ public static async Task ProcessStreamedFile( // Log the exception } - return Array.Empty(); + return []; } private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) From 7f99500b6d47d655b3787aa3024b8e32f283b46c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 11:31:49 +0200 Subject: [PATCH 444/878] Use `ToLowerFirst` from GraphQL client (instead of custom extension) --- backend/src/Extensions/StringExtensions.cs | 9 --------- .../src/GraphQl/DataApprovals/DataApprovalMutations.cs | 6 +++--- .../GetHttpsResources/GetHttpsResourceMutations.cs | 2 +- .../src/GraphQl/MethodAsService/MethodAsServiceInput.cs | 2 +- .../GraphQl/MethodAsService/MethodAsServiceQueries.cs | 2 +- 5 files changed, 6 insertions(+), 15 deletions(-) diff --git a/backend/src/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 00642a3f..5f5c07c4 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -5,15 +5,6 @@ namespace Database.Extensions; public static class StringExtensions { - public static string FirstCharToLower(this string str) - { - return string.IsNullOrEmpty(str) - || !char.IsLetter(str, 0) - || char.IsLower(str, 0) - ? str - : char.ToLowerInvariant(str[0]) + str[1..]; - } - public static string Base64Encode(this string plainText) { return Convert.ToBase64String( diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 02d2b31f..8716ea65 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -52,7 +52,7 @@ CancellationToken cancellationToken new AddDataApprovalError( AddDataApprovalErrorCode.UNKNOWN_DATA, $"Unknown data.", - [nameof(input), nameof(input.DataId).FirstCharToLower()] + [nameof(input), nameof(input.DataId).ToLowerFirst()] ) ); } @@ -64,7 +64,7 @@ CancellationToken cancellationToken new AddDataApprovalError( AddDataApprovalErrorCode.MISSING_STATEMENT, "Both standard and publication are null.", - [nameof(input), nameof(input.Statement).FirstCharToLower()] + [nameof(input), nameof(input.Statement).ToLowerFirst()] ) ); } @@ -76,7 +76,7 @@ CancellationToken cancellationToken new AddDataApprovalError( AddDataApprovalErrorCode.AMBIGUOUS_STATEMENT, "Both standard and publication are non-null.", - [nameof(input), nameof(input.Statement).FirstCharToLower()] + [nameof(input), nameof(input.Statement).ToLowerFirst()] ) ); } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index b4c8fca0..22f04daa 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -39,7 +39,7 @@ CancellationToken cancellationToken new CreateGetHttpsResourceError( CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, $"There is no data of kind {input.DataKind} with identifier {input.DataId}.", - [nameof(input), nameof(input.DataId).FirstCharToLower()] + [nameof(input), nameof(input.DataId).ToLowerFirst()] ) ); } diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs index a8f001b0..65562663 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs @@ -6,4 +6,4 @@ public sealed record MethodAsServiceInput( Guid DataId, Guid DatabaseId, Guid MethodId - ); \ No newline at end of file +); \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs index 9fd50e1d..c9a7e5ec 100644 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs @@ -111,7 +111,7 @@ CancellationToken cancellationToken new MethodAsServiceError( MethodAsServiceErrorCode.UNKNOWN_DATABASE, $"The database is unknown.", - [] + [nameof(input), nameof(input.DatabaseId).ToLowerFirst()] ) ); } From 58b9ed00574872f4b33c23d22777549c4e43c0a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 14:51:28 +0200 Subject: [PATCH 445/878] Restructure methods as service fully and clean-up code somewhat --- backend/Makefile | 11 +- backend/src/ApiRequests/DataApi.cs | 53 +--- backend/src/ApiRequests/Dto/XDataDto.cs | 16 -- backend/src/ApiRequests/Queries/DataX.graphql | 5 - backend/src/ApiRequests/UserApi.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 4 +- .../DataApprovals/DataApprovalMutations.cs | 1 + .../GetHttpsResourceMutations.cs | 1 + .../GraphQl/MethodAsService/FileInputData.cs | 70 ------ .../MethodAsService/MethodAsServiceError.cs | 13 - .../MethodAsServiceErrorCode.cs | 10 - .../MethodAsService/MethodAsServiceInput.cs | 9 - .../MethodAsService/MethodAsServicePayload.cs | 51 ---- .../MethodAsService/MethodAsServiceQueries.cs | 129 ---------- .../MethodAsServiceWithFileInput.cs | 10 - .../GraphQl/Methods/CalculateMethodError.cs | 12 + .../Methods/CalculateMethodErrorCode.cs | 11 + .../GraphQl/Methods/CalculateMethodPayload.cs | 52 ++++ backend/src/GraphQl/Methods/MethodQueries.cs | 226 ++++++++++++++++++ backend/src/Methods/IMethod.cs | 7 +- backend/src/Methods/MethodBase.cs | 28 +++ .../CalculationStandard.cs | 2 +- .../SpectralToIntegralInput.cs | 28 +++ .../SpectralToIntegralMethod.cs | 148 +++++------- .../SpectralToIntegralOutput.cs | 7 + .../TransmittanceSum/TransmittanceSumInput.cs | 19 ++ .../TransmittanceSumMethod.cs | 19 ++ backend/src/Methods/TransmittanceSumMethod.cs | 28 --- backend/src/Services/ApiRequestService.cs | 111 +++++---- backend/src/Services/MethodFactory.cs | 4 +- .../Methods/SpectralToIntegralMethodTests.cs | 42 ++-- 31 files changed, 574 insertions(+), 555 deletions(-) delete mode 100644 backend/src/ApiRequests/Queries/DataX.graphql delete mode 100644 backend/src/GraphQl/MethodAsService/FileInputData.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs delete mode 100644 backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs create mode 100644 backend/src/GraphQl/Methods/CalculateMethodError.cs create mode 100644 backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs create mode 100644 backend/src/GraphQl/Methods/CalculateMethodPayload.cs create mode 100644 backend/src/GraphQl/Methods/MethodQueries.cs create mode 100644 backend/src/Methods/MethodBase.cs rename backend/src/Methods/{ => SpectralToIntegral}/CalculationStandard.cs (78%) create mode 100644 backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs rename backend/src/Methods/{ => SpectralToIntegral}/SpectralToIntegralMethod.cs (54%) create mode 100644 backend/src/Methods/SpectralToIntegral/SpectralToIntegralOutput.cs create mode 100644 backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs create mode 100644 backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs delete mode 100644 backend/src/Methods/TransmittanceSumMethod.cs diff --git a/backend/Makefile b/backend/Makefile index ae8171e3..91500a8a 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -17,18 +17,23 @@ help : ## Print this help # https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test +# Sadly, `--list-tests` ignores the option `--filter` in the test adapter +# `NUnit`: https://github.com/dotnet/sdk/issues/8643#issuecomment-1497765835 # dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal -test : ## Run tests +test : ARGS = +test : ## Run tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"' test` @echo "--------------------------------------------------" @echo "Complete test list" @echo "--------------------------------------------------" dotnet test \ - --list-tests + --list-tests \ + ${ARGS} @echo "--------------------------------------------------" @echo "All test runs" @echo "--------------------------------------------------" dotnet test \ - --collect:"XPlat Code Coverage" + --collect:"XPlat Code Coverage" \ + ${ARGS} .PHONY : test assets : ## Build assets diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/DataApi.cs index dff89c37..4d2a070b 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/DataApi.cs @@ -9,9 +9,6 @@ namespace Database.ApiRequests; -/// -/// Class to request *Data from Database API. -/// public sealed class DataApi { public static readonly string[] CalorimetricDataFileNames = @@ -49,22 +46,9 @@ public sealed class DataApi "OpticalData.graphql" ]; - public static readonly string[] DataXFileNames = - [ - "DataFields.graphql", - "DataX.graphql" - ]; - - /// - /// Create query to request data and get response. - /// - /// - /// Throws exception, when response could not be serialized. - /// - /// Query, variables, and response for data. public static async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData( Guid dataId, - string[] filenames, + string[] fileNames, AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -73,7 +57,7 @@ CancellationToken cancellationToken ) { var variables = new { id = dataId }; - var query = await apiRequestService.ConstructGraphQlQuery(filenames); + var query = await apiRequestService.ConstructGraphQlQuery(fileNames); var response = await apiRequestService.Database().QueryGraphQl( appSettings, new GraphQLRequest(query, variables), @@ -84,24 +68,10 @@ CancellationToken cancellationToken return (query, JsonSerializer.SerializeToElement(variables), response); } - /// - /// Create query to request data and get response. - /// - /// Id of data to request. - /// File names of query files. - /// - /// - /// - /// - /// - /// - /// Throws exception, when query could not be constructed or no response. - /// - /// Query and response for data. - public static async Task> GetDataFromDatabase( - Uri databaseUri, + public static async Task> QueryDataFromDatabase( + Uri databaseUrl, Guid dataId, - string[] filenames, + string query, AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -110,14 +80,13 @@ public static async Task> GetDataFromDatabase( + return await apiRequestService.Database().QueryGraphQl( appSettings, - databaseUri, - request, + databaseUrl, + new GraphQLRequest( + query, + new { id = dataId } + ), httpClientFactory, httpContextAccessor, cancellationToken diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs index 2aedf057..279dcc10 100644 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ b/backend/src/ApiRequests/Dto/XDataDto.cs @@ -5,22 +5,6 @@ namespace Database.ApiRequests.Dto; -public sealed record DataXDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings -); - public sealed record GeometricDataDto( string Uuid, DateTime Timestamp, diff --git a/backend/src/ApiRequests/Queries/DataX.graphql b/backend/src/ApiRequests/Queries/DataX.graphql deleted file mode 100644 index e59c951e..00000000 --- a/backend/src/ApiRequests/Queries/DataX.graphql +++ /dev/null @@ -1,5 +0,0 @@ -query Data($id: Uuid!) { - data(id: $id) { - ...DataFields - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs index c44c3bd8..1fdbef45 100644 --- a/backend/src/ApiRequests/UserApi.cs +++ b/backend/src/ApiRequests/UserApi.cs @@ -70,7 +70,7 @@ public static Task RequestUserInfo( IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return apiRequestService.Metabase().QueryRest( + return apiRequestService.Metabase().PerformHttpGetRequest( new Uri(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"), httpClientFactory, httpContextAccessor, diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b8faf1b3..9a7c1d66 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -29,7 +29,7 @@ using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; using Database.GraphQl.DataApprovals; using Database.GraphQl.AccessRights; -using Database.GraphQl.MethodAsService; +using Database.GraphQl.Methods; using Database.GraphQl.ResponseApprovals; namespace Database.Configuration; @@ -159,7 +159,7 @@ AppSettings appSettings .AddType() .AddType() .AddType() - .AddType() + .AddType() .AddType() // Mutation Types .AddMutationType(d => d.Name(nameof(Mutation))) diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 8716ea65..59dd3230 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -6,6 +6,7 @@ using Database.Extensions; using Database.GraphQl.References; using Database.Services; +using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; namespace Database.GraphQl.DataApprovals; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 22f04daa..2fe632e2 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -10,6 +10,7 @@ using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using Database.Services; +using GraphQL.Client.Abstractions.Utilities; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/MethodAsService/FileInputData.cs b/backend/src/GraphQl/MethodAsService/FileInputData.cs deleted file mode 100644 index 56f0cc5e..00000000 --- a/backend/src/GraphQl/MethodAsService/FileInputData.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.MethodAsService; - -public sealed record FileInputData( - Data Data -); - -public sealed record Data( - ComponentCharacteristics ComponentCharacteristics, - IReadOnlyList DataPoints -); - -public sealed record ComponentCharacteristics( - string Surface, - string Symmetries -); - -public sealed record DataPoint( - Incidence Incidence, - Emergence Emergence, - Results Results -); - -public sealed record Incidence( - Wavelengths Wavelengths, - Direction Direction -); - -public sealed record Emergence( - Direction Direction -); - -public sealed record Results( - double Transmittance, - double? Reflectance = null, - ColorTransmittance? ColorTransmittance = null, - ColorReflectance? ColorReflectance = null -); - -public sealed record Direction( - int Polar -); - -public sealed record Wavelengths( - int Wavelength -); - -public sealed record ColorTransmittance( - IReadOnlyList CielabColors, - IReadOnlyList ColorRenderingIndices -); - -public sealed record ColorReflectance( - IReadOnlyList CielabColors -); - -public sealed record MethodAsServiceCielabColor( - int LStar, - double AStar, - double BStar, - string Observer, - string Illuminant -); - -public sealed record ColorRenderingIndex( - int Ra, - string Observer, - string Illuminant -); \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs deleted file mode 100644 index 4ab42d43..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceError.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Database.GraphQl.GeometricDataX; -using System.Collections.Generic; - -namespace Database.GraphQl.MethodAsService; - -public sealed class MethodAsServiceError( - MethodAsServiceErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs deleted file mode 100644 index 8509c7e3..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceErrorCode.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.MethodAsService; - -[SuppressMessage("Naming", "CA1707")] -public enum MethodAsServiceErrorCode -{ - UNKNOWN_METHOD, - UNKNOWN_DATABASE -} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs deleted file mode 100644 index 65562663..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceInput.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Database.GraphQl.MethodAsService; - -public sealed record MethodAsServiceInput( - Guid DataId, - Guid DatabaseId, - Guid MethodId -); \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs b/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs deleted file mode 100644 index a19cf2a7..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServicePayload.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.MethodAsService; - -public sealed class MethodAsServicePayload - : Payload -{ - public MethodAsServicePayload( - IReadOnlyList calculationResult - ) - { - CalculationResult = calculationResult; - } - - public MethodAsServicePayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - public MethodAsServicePayload( - MethodAsServiceError error - ) - : this([error]) - { - } - - public MethodAsServicePayload( - IReadOnlyList calculationResult, - IReadOnlyCollection errors - ) - { - CalculationResult = calculationResult; - Errors = errors; - } - - public MethodAsServicePayload( - List calculationResult, - MethodAsServiceError error - ) - : this( - calculationResult, - [error] - ) - { - } - - public IReadOnlyList? CalculationResult { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs deleted file mode 100644 index c9a7e5ec..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceQueries.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.ApiRequests.Dto; -using Database.Services; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate; -using HotChocolate.Resolvers; -using HotChocolate.Types; -using Microsoft.AspNetCore.Http; - -namespace Database.GraphQl.MethodAsService; - -[ExtendObjectType(nameof(Query))] -public sealed class MethodAsServiceQueries -{ - private sealed record DataXResponse(DataXDto DataX); - - public async Task MethodAsServiceWithFileAsync( - MethodAsServiceWithFileInput input, - MethodFactory methodFactory, - CancellationToken cancellationToken - ) - { - var method = methodFactory.GetMethod(input.MethodId); - if (method is null) - { - return new MethodAsServicePayload( - new MethodAsServiceError( - MethodAsServiceErrorCode.UNKNOWN_METHOD, - $"The method is unknown.", - [nameof(input), nameof(input.MethodId).ToLowerFirst()] - ) - ); - } - await using var stream = input.File.OpenReadStream(); - var fileInput = await JsonSerializer.DeserializeAsync( - stream, - JsonSerializerSettings.File, - cancellationToken - ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - var result = method.Calculate(fileInput.Data.DataPoints); - return new MethodAsServicePayload(result); - } - - public async Task MethodAsServiceAsync( - AppSettings appSettings, - MethodAsServiceInput input, - MethodFactory methodFactory, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - IResolverContext resolverContext, - CancellationToken cancellationToken - ) - { - var method = methodFactory.GetMethod(input.MethodId); - if (method is null) - { - return new MethodAsServicePayload( - new MethodAsServiceError( - MethodAsServiceErrorCode.UNKNOWN_METHOD, - $"The method is unknown.", - [nameof(input), nameof(input.MethodId).ToLowerFirst()] - ) - ); - } - DatabaseDto? database; - try - { - database = await DatabaseApi.RequestDatabase( - input.DatabaseId, - appSettings, - apiRequestService, - httpClientFactory, - httpContextAccessor, - cancellationToken); - } - catch (HttpRequestException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("METABASE_REQUEST_FAILED") - .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") - .SetException(e) - .Build() - ); - } - catch (JsonException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") - .SetException(e) - .Build() - ); - } - - if (database is null) - { - return new MethodAsServicePayload( - new MethodAsServiceError( - MethodAsServiceErrorCode.UNKNOWN_DATABASE, - $"The database is unknown.", - [nameof(input), nameof(input.DatabaseId).ToLowerFirst()] - ) - ); - } - - var data = await DataApi.GetDataFromDatabase(database.Locator, input.DataId, DataApi.DataXFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken); - - var locator = data.Data.DataX.ResourceTree.Root.Value.Locator; - - var filedata = await apiRequestService.QueryRest(new Uri(locator), httpClientFactory, httpContextAccessor, cancellationToken); - - var result = methodFactory.Calculate(filedata.Data.DataPoints.ToList()); - - return new MethodAsServicePayload(result); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs b/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs deleted file mode 100644 index 0a636f94..00000000 --- a/backend/src/GraphQl/MethodAsService/MethodAsServiceWithFileInput.cs +++ /dev/null @@ -1,10 +0,0 @@ -using HotChocolate.Types; -using HotChocolate; -using System; - -namespace Database.GraphQl.MethodAsService; - -public sealed record MethodAsServiceWithFileInput( - [GraphQLType(typeof(NonNullType))] IFile File, - Guid MethodId - ); \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodError.cs b/backend/src/GraphQl/Methods/CalculateMethodError.cs new file mode 100644 index 00000000..5d3b286f --- /dev/null +++ b/backend/src/GraphQl/Methods/CalculateMethodError.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.Methods; + +public sealed class CalculateMethodError( + CalculateMethodErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs b/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs new file mode 100644 index 00000000..b1619346 --- /dev/null +++ b/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.Methods; + +[SuppressMessage("Naming", "CA1707")] +public enum CalculateMethodErrorCode +{ + UNKNOWN_METHOD, + UNKNOWN_DATABASE, + DATA_QUERY_FAILED +} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodPayload.cs b/backend/src/GraphQl/Methods/CalculateMethodPayload.cs new file mode 100644 index 00000000..a94c34e5 --- /dev/null +++ b/backend/src/GraphQl/Methods/CalculateMethodPayload.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Text.Json; + +namespace Database.GraphQl.Methods; + +public sealed class CalculateMethodPayload + : Payload +{ + public CalculateMethodPayload( + JsonDocument result + ) + { + Result = result; + } + + public CalculateMethodPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + public CalculateMethodPayload( + CalculateMethodError error + ) + : this([error]) + { + } + + public CalculateMethodPayload( + JsonDocument result, + IReadOnlyCollection errors + ) + { + Result = result; + Errors = errors; + } + + public CalculateMethodPayload( + JsonDocument result, + CalculateMethodError error + ) + : this( + result, + [error] + ) + { + } + + public JsonDocument? Result { get; } + public IReadOnlyCollection? Errors { get; } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs new file mode 100644 index 00000000..7f9dbd61 --- /dev/null +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -0,0 +1,226 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.ApiRequests.Dto; +using Database.Enumerations; +using Database.Services; +using GraphQL; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.AspNetCore.Http; + +namespace Database.GraphQl.Methods; + +[ExtendObjectType(nameof(Query))] +public sealed class MethodQueries +{ + private sealed record DataData( + Data? Data + ); + + private sealed record Data( + ResourceTree ResourceTree + ); + + private sealed record ResourceTree( + Root Root + ); + + private sealed record Root( + Resource Value + ); + + private sealed record Resource( + string HashValue, + Uri Locator, + Guid DataFormatId + ); + + public async Task CalculateMethodWithDataUploadAsync( + Guid methodId, + [GraphQLType(typeof(NonNullType))] IFile data, + MethodFactory methodFactory, + CancellationToken cancellationToken + ) + { + var method = methodFactory.GetMethod(methodId); + if (method is null) + { + return new CalculateMethodPayload( + new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [nameof(methodId)] + ) + ); + } + using var stream = data.OpenReadStream(); + var jsonData = await JsonDocument.ParseAsync( + stream, + ApiRequestService.JsonDocumentOptions, + cancellationToken + ); + var result = method.Calculate(jsonData); + return new CalculateMethodPayload(result); + } + + public async Task CalculateMethodAsync( + Guid methodId, + CrossDatabaseDataReferenceInput dataReference, + AppSettings appSettings, + MethodFactory methodFactory, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var method = methodFactory.GetMethod(methodId); + if (method is null) + { + return new CalculateMethodPayload( + new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_METHOD, + $"The method is unknown.", + [nameof(methodId)] + ) + ); + } + DatabaseDto? database; + try + { + database = await DatabaseApi.RequestDatabase( + dataReference.DatabaseId, + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken); + } + catch (HttpRequestException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("METABASE_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") + .SetException(e) + .Build() + ); + } + catch (JsonException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("JSON_DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetException(e) + .Build() + ); + } + if (database is null) + { + return new CalculateMethodPayload( + new CalculateMethodError( + CalculateMethodErrorCode.UNKNOWN_DATABASE, + $"The database is unknown.", + [nameof(dataReference), nameof(dataReference.DatabaseId).ToLowerFirst()] + ) + ); + } + var query = ConstructQuery(dataReference.DataKind); + GraphQLResponse? response; + try + { + response = await DataApi.QueryDataFromDatabase( + database.Locator, + dataReference.DataId, + query, + appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken + ); + } + catch (HttpRequestException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("DATABASE_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code {e.StatusCode} to request the database GraphQl endpoint {database.Locator}.") + .SetException(e) + .Build() + ); + } + catch (JsonException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("JSON_DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to the database GraphQl endpoint {database.Locator}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") + .SetException(e) + .Build() + ); + } + if (response.Data?.Data is null) + { + if (response.Errors?.Length >= 1) + { + foreach (var error in response.Errors) + { + var errorBuilder = ErrorBuilder.New() + .SetCode("DATABASE_QUERY_ERROR") + // .SetPath(error.Path) // TODO Add the error path. Just using `error.Path` does not work as it contains non-"GraphQlName"s according to HotChocolate sometimes. + .SetMessage($"The GraphQL response received from the database {database.Locator} for the query {query} reported the error {error.Message}."); + resolverContext.ReportError(errorBuilder.Build()); + } + } + return new CalculateMethodPayload( + new CalculateMethodError( + CalculateMethodErrorCode.DATA_QUERY_FAILED, + $"Failed to query database {database.Locator} for the data.", + [nameof(dataReference)] + ) + ); + } + var locator = response.Data.Data.ResourceTree.Root.Value.Locator; + // TODO The locator could also point to a non-JSON resource. Support those also. + var data = await apiRequestService.PerformHttpGetRequest( + locator, httpClientFactory, httpContextAccessor, cancellationToken + ); + var result = method.Calculate(data); + return new CalculateMethodPayload(result); + } + + private static string ConstructQuery(DataKind dataKind) + { + var name = Enum.GetName(dataKind)![..^"_DATA".Length].ToLowerInvariant(); + return + $$""" + query Data($id: Uuid!) { + data: {{name}}Data(id: $id) { + resourceTree { + root { + value { + hashValue + locator + dataFormatId + } + } + } + } + } + """; + } +} \ No newline at end of file diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 4b59e9a3..dd4145da 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; -using Database.GraphQl.MethodAsService; +using System.Text.Json; namespace Database.Methods; public interface IMethod { - public string Name { get; } - public Guid Id { get; } - public List Calculate(IReadOnlyList dataPoints); + public JsonDocument Calculate(JsonDocument input); } \ No newline at end of file diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs new file mode 100644 index 00000000..51db68df --- /dev/null +++ b/backend/src/Methods/MethodBase.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Database.Methods; + +public abstract class MethodBase + : IMethod +{ + private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() + { + AllowTrailingCommas = true, + ReadCommentHandling = JsonCommentHandling.Skip, + UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, + }; + + public JsonDocument Calculate(JsonDocument input) + { + var parsedInput = input.Deserialize(s_jsonSerializerOptions) + ?? throw new InvalidOperationException(); + var output = Calculate(parsedInput); + return JsonDocument.Parse( + JsonSerializer.SerializeToUtf8Bytes(output) + ); + } + + public abstract TOutput Calculate(TInput input); +} \ No newline at end of file diff --git a/backend/src/Methods/CalculationStandard.cs b/backend/src/Methods/SpectralToIntegral/CalculationStandard.cs similarity index 78% rename from backend/src/Methods/CalculationStandard.cs rename to backend/src/Methods/SpectralToIntegral/CalculationStandard.cs index 167ff7d7..a2bf6663 100644 --- a/backend/src/Methods/CalculationStandard.cs +++ b/backend/src/Methods/SpectralToIntegral/CalculationStandard.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; -namespace Database.Methods; +namespace Database.Methods.SpectralToIntegral; [SuppressMessage("Naming", "CA1707")] public enum CalculationStandard diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs new file mode 100644 index 00000000..1be3864b --- /dev/null +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Database.Methods.SpectralToIntegral; + +public sealed record SpectralToIntegralInput( + SpectralToIntegralData Data +); + +public sealed record SpectralToIntegralData( + IReadOnlyList DataPoints +); + +public sealed record DataPoint( + Incidence Incidence, + Results Results +); + +public sealed record Incidence( + Wavelengths Wavelengths +); + +public sealed record Wavelengths( + double Wavelength +); + +public sealed record Results( + double Transmittance +); \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs similarity index 54% rename from backend/src/Methods/SpectralToIntegralMethod.cs rename to backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index 7bab928c..6fc2b9a9 100644 --- a/backend/src/Methods/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -1,18 +1,16 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.Immutable; using System.Linq; -using Database.Extensions; -using Database.GraphQl.MethodAsService; -namespace Database.Methods; +namespace Database.Methods.SpectralToIntegral; -public sealed class SpectralToIntegralMethod : IMethod +public sealed class SpectralToIntegralMethod + : MethodBase { public static readonly Guid Id = Guid.Parse("285d172c-9bcf-4c57-9be0-ee95651ba7db"); - public const string Name = "SpectralToIntegral"; - private List<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeightsList = new List<(int, double, double)> { + public ImmutableArray<(int wavelength, double weight, double deltaWavelength)> En410VisibleWavelengthsWeights => [ (380, 0, 5), (390, 0.0005, 10), (400, 0.003, 10), @@ -54,8 +52,9 @@ public sealed class SpectralToIntegralMethod : IMethod (760, 0.0001, 10), (770, 0, 10), (780, 0, 5) - }; - private List<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeightsList = new List<(int, double, double)> { + ]; + + public ImmutableArray<(int wavelength, double weight, double deltaWavelength)> En410SolarWavelengthsWeights => [ (300, 0.0005, 10), (320, 0.0069, 20), (340, 0.0122, 20), @@ -112,8 +111,9 @@ public sealed class SpectralToIntegralMethod : IMethod (2300, 0.006, 100), (2400, 0.0041, 100), (2500, 0.0006, 50) - }; - private List<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeightsList = new List<(int, double, double)> { + ]; + + public ImmutableArray<(int wavelength, double weight, double deltaWavelength)> Iso9050SolarWavelengthsWeights => [ (300, 0, 5), (305, 0.000057, 5), (310, 0.000236, 5), @@ -208,96 +208,72 @@ public sealed class SpectralToIntegralMethod : IMethod (2350, 0.003163, 50), (2400, 0.002233, 50), (2450, 0.001202, 50) - }; - public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410VisibleWavelengthsWeights { get; } - public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> en410SolarWavelengthsWeights { get; } - public IReadOnlyList<(int wavelength, double weight, double deltaWavelength)> iso9050SolarWavelengthsWeights { get; } + ]; - public SpectralToIntegralMethod() + public override SpectralToIntegralOutput Calculate(SpectralToIntegralInput input) { - en410VisibleWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410VisibleWavelengthsWeightsList); - en410SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(en410SolarWavelengthsWeightsList); - iso9050SolarWavelengthsWeights = new ReadOnlyCollection<(int, double, double)>(iso9050SolarWavelengthsWeightsList); - } - - public List Calculate(IReadOnlyList dataPoints) - { - var resultsForAllStandards = new List(); - foreach (var (standard, wavelength) in Enum.GetValues().WithIndex()) - { - var resultsForOneStandard = CalculateOneStandard(dataPoints, standard); - resultsForAllStandards.AddRange([ - new DataPoint( - new Incidence( - new Wavelengths(wavelength), - new Direction(0) - ), - new Emergence(new Direction(0) - ), - new Results(resultsForOneStandard[0].Results.Transmittance)) - ]); - } - return resultsForAllStandards; + return new SpectralToIntegralOutput( + En410Visible: Calculate(input.Data.DataPoints, En410VisibleWavelengthsWeights), + En410Solar: Calculate(input.Data.DataPoints, En410SolarWavelengthsWeights), + Iso9050Solar: Calculate(input.Data.DataPoints, Iso9050SolarWavelengthsWeights) + ); } - public List CalculateOneStandard(IReadOnlyList spectralDataPoints, CalculationStandard standard) + public double Calculate( + IReadOnlyList spectralDataPoints, + ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights + ) { - // Turn IReadOnlyLists into more flexible Lists - var spectralDataPointsToFilter = new List(spectralDataPoints); - List<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights = new List<(int, double, double)> { (0, 0, 0) }; - switch (standard) + var sortedSpectralDataPoints = + spectralDataPoints + .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) + .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) + .ToList() + .AsReadOnly(); + var numerator = 0.0D; + var denominator = 0.0D; + foreach (var wavelengthsWeight in wavelengthsWeights) { - case CalculationStandard.EN_410_VISIBLE: - wavelengthsWeights = en410VisibleWavelengthsWeights.ToList(); - break; - case CalculationStandard.EN_410_SOLAR: - wavelengthsWeights = en410SolarWavelengthsWeights.ToList(); - break; - case CalculationStandard.ISO_9050_SOLAR: - wavelengthsWeights = iso9050SolarWavelengthsWeights.ToList(); - break; - } - // Filter data points which have a wavelength which is out of bounds. - var spectralDataPointsFiltered = spectralDataPointsToFilter.Where(dataPoint => !WavelengthOutOfBounds(dataPoint)).ToList(); - // Sort the dataPoints - var spectralDataPointsSorted = spectralDataPointsFiltered.OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength).ToList(); - double wavelengthWeighting = 0.0D, deltaWavelength = 0.0D, averageValue = 0.0D, numerator = 0.0D, denominator = 0.0D, integralValue = 0.0D; - - for (var i = 0; i < wavelengthsWeights.Count; i++) - // for (int i = 0; i < 3; i++) - { - wavelengthWeighting = wavelengthsWeights[i].wavelength; - var spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - var spectralDataPointWavelengthAbove = new DataPoint(new Incidence(new Wavelengths(1000000), new Direction(8)), new Emergence(new Direction(8)), new Results(99)); - for (var j = 0; j < spectralDataPointsSorted.Count; j++) + var spectralDataPointWavelengthBelow = new DataPoint( + new Incidence(new Wavelengths(0)), + new Results(99) + ); + var spectralDataPointWavelengthAbove = new DataPoint( + new Incidence(new Wavelengths(1000000)), + new Results(99) + ); + foreach (var spectralDataPoint in sortedSpectralDataPoints) { // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength <= wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength)) - { spectralDataPointWavelengthBelow = spectralDataPointsSorted[j]; } - if ((spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength > wavelengthWeighting) && (spectralDataPointsSorted[j].Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength)) - { spectralDataPointWavelengthAbove = spectralDataPointsSorted[j]; } + if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeight.wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength + ) + { + spectralDataPointWavelengthBelow = spectralDataPoint; + } + if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength + ) + { + spectralDataPointWavelengthAbove = spectralDataPoint; + } } // Trapezoidal rule to calculate an integral - deltaWavelength = wavelengthsWeights[i].deltaWavelength; - averageValue = (spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance) / 2; - numerator += averageValue * deltaWavelength * wavelengthsWeights[i].weight; - denominator += deltaWavelength * wavelengthsWeights[i].weight; + var averageValue = ( + spectralDataPointWavelengthBelow.Results.Transmittance + + spectralDataPointWavelengthAbove.Results.Transmittance + ) / 2; + numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; } - integralValue = numerator / denominator; - return [new DataPoint(new Incidence(new Wavelengths(0), new Direction(0)), new Emergence(new Direction(0)), new Results(integralValue))]; + return numerator / denominator; } - - // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { - return (dataPoint.Incidence.Wavelengths.Wavelength <= 0) || (dataPoint.Incidence.Wavelengths.Wavelength >= 3000); + return + dataPoint.Incidence.Wavelengths.Wavelength <= 0 + || dataPoint.Incidence.Wavelengths.Wavelength >= 3000; } - - // public List Calculate(IReadOnlyList dataPoints) - // { - // throw new InvalidOperationException("The SpectralToIntegralMethod needs two lists of dataPoints as input."); - // } - } \ No newline at end of file diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralOutput.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralOutput.cs new file mode 100644 index 00000000..19d64ce6 --- /dev/null +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralOutput.cs @@ -0,0 +1,7 @@ +namespace Database.Methods.SpectralToIntegral; + +public sealed record SpectralToIntegralOutput( + double En410Visible, + double En410Solar, + double Iso9050Solar +); \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs b/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs new file mode 100644 index 00000000..e9ee7bf5 --- /dev/null +++ b/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Database.Methods.TransmittanceSum; + +public sealed record TransmittanceSumInput( + Data Data +); + +public sealed record Data( + IReadOnlyList DataPoints +); + +public sealed record DataPoint( + Results Results +); + +public sealed record Results( + double Transmittance +); \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs new file mode 100644 index 00000000..d2704764 --- /dev/null +++ b/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs @@ -0,0 +1,19 @@ +using System; + +namespace Database.Methods.TransmittanceSum; + +public sealed class TransmittanceSumMethod + : MethodBase +{ + public static readonly Guid Id = Guid.Parse("8a7684c7-aaca-4057-8a30-1cb951c2c6a0"); + + public override double Calculate(TransmittanceSumInput input) + { + double transmittanceSum = 0; + foreach (var dataPoint in input.Data.DataPoints) + { + transmittanceSum += dataPoint.Results.Transmittance; + } + return transmittanceSum; + } +} \ No newline at end of file diff --git a/backend/src/Methods/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSumMethod.cs deleted file mode 100644 index bb1c5aa1..00000000 --- a/backend/src/Methods/TransmittanceSumMethod.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.MethodAsService; - -namespace Database.Methods; - -public sealed class TransmittanceSumMethod : IMethod -{ - public static readonly Guid Id = Guid.Parse("8a7684c7-aaca-4057-8a30-1cb951c2c6a0"); - public const string Name = "TransmittanceSum"; - - public List Calculate(IReadOnlyList dataPoints) - { - double transmittanceSum = 0; - foreach (var dataPoint in dataPoints) - { - transmittanceSum += dataPoint.Results.Transmittance; - } - return - [ - new DataPoint( - new Incidence(new Wavelengths(0), new Direction(0)), - new Emergence(new Direction(0)), - new Results(transmittanceSum) - ) - ]; - } -} \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index ee25e508..6ac23666 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -4,6 +4,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -29,6 +30,13 @@ public sealed class ApiRequestService /// public const string DatabaseHttpClient = "Database"; + public static JsonDocumentOptions JsonDocumentOptions => new() + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = 0 + }; + private static bool s_useMetabase = true; /// @@ -84,7 +92,7 @@ CancellationToken cancellationToken // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), - MakeJsonHttpContent(request), + MakeGraphQlJsonHttpContent(request), httpClientFactory, httpContextAccessor, cancellationToken @@ -115,7 +123,7 @@ CancellationToken cancellationToken // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), - MakeJsonHttpContent(request), + MakeGraphQlJsonHttpContent(request), JsonSerializerSettings.GraphQL, httpClientFactory, httpContextAccessor, @@ -134,7 +142,7 @@ CancellationToken cancellationToken /// /// /// of expected type. - public Task> QueryGraphQlFromUrl( + public Task> QueryGraphQl( AppSettings appSettings, Uri uri, GraphQLRequest request, @@ -148,8 +156,8 @@ CancellationToken cancellationToken HttpMethod.Post, // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(uri, "/graphql/"), - MakeJsonHttpContent(request), + uri, + MakeGraphQlJsonHttpContent(request), JsonSerializerSettings.GraphQL, httpClientFactory, httpContextAccessor, @@ -157,67 +165,45 @@ CancellationToken cancellationToken ); } - /// - /// Send REST request to API and return response. - /// - /// Expected response type. - /// to send request to. - /// - /// - /// - /// - public Task QueryRest( + public Task PerformHttpGetRequest( Uri uri, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) - where TResponse : class { - return Query( + return Query( HttpMethod.Get, uri, + async httpResponseContent => + await JsonDocument.ParseAsync( + await httpResponseContent.ReadAsStreamAsync(), + JsonDocumentOptions + ), null, - JsonSerializerSettings.Rest, httpClientFactory, httpContextAccessor, cancellationToken ); } - private static async Task Query( - HttpMethod httpMethod, + public Task PerformHttpGetRequest( Uri uri, - Func> read, - HttpContent? httpContent, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) + where TResponse : class { - using var httpClient = - s_useMetabase - ? httpClientFactory.CreateClient(MetabaseHttpClient) - : httpClientFactory.CreateClient(DatabaseHttpClient); - var bearerToken = await httpContextAccessor.ExtractBearerToken(); - using var httpRequestMessage = new HttpRequestMessage( - httpMethod, - uri + return Query( + HttpMethod.Get, + uri, + null, + JsonSerializerSettings.Rest, + httpClientFactory, + httpContextAccessor, + cancellationToken ); - httpRequestMessage.Content = httpContent; - if (bearerToken is not null) - { - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); - } - using var httpResponseMessage = - await httpClient.SendAsync(httpRequestMessage, cancellationToken); - if (httpResponseMessage.StatusCode != HttpStatusCode.OK) - { - throw new HttpRequestException( - $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, - httpResponseMessage.StatusCode); - } - return await read(httpResponseMessage.Content); } private static async Task Query( @@ -255,7 +241,42 @@ CancellationToken cancellationToken ); } - private static ByteArrayContent MakeJsonHttpContent( + private static async Task Query( + HttpMethod httpMethod, + Uri uri, + Func> read, + HttpContent? httpContent, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + using var httpClient = + s_useMetabase + ? httpClientFactory.CreateClient(MetabaseHttpClient) + : httpClientFactory.CreateClient(DatabaseHttpClient); + var bearerToken = await httpContextAccessor.ExtractBearerToken(); + using var httpRequestMessage = new HttpRequestMessage( + httpMethod, + uri + ); + httpRequestMessage.Content = httpContent; + if (bearerToken is not null) + { + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + } + using var httpResponseMessage = + await httpClient.SendAsync(httpRequestMessage, cancellationToken); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { + throw new HttpRequestException( + $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, + httpResponseMessage.StatusCode); + } + return await read(httpResponseMessage.Content); + } + + private static ByteArrayContent MakeGraphQlJsonHttpContent( TContent content ) { diff --git a/backend/src/Services/MethodFactory.cs b/backend/src/Services/MethodFactory.cs index 76d61869..343f42c6 100644 --- a/backend/src/Services/MethodFactory.cs +++ b/backend/src/Services/MethodFactory.cs @@ -1,5 +1,7 @@ using System; using Database.Methods; +using Database.Methods.SpectralToIntegral; +using Database.Methods.TransmittanceSum; namespace Database.Services; @@ -12,6 +14,6 @@ public sealed class MethodFactory var _ when methodId == TransmittanceSumMethod.Id => new TransmittanceSumMethod(), var _ when methodId == SpectralToIntegralMethod.Id => new SpectralToIntegralMethod(), _ => null, - } + }; } } \ No newline at end of file diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index b1e0b2f2..0da2a9ef 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -1,10 +1,6 @@ -using System; -using System.Threading.Tasks; using NUnit.Framework; -using Database.Methods; -using Database.GraphQl.MethodAsService; -using System.Collections.Generic; using FluentAssertions; +using Database.Methods.SpectralToIntegral; namespace Database.Tests.Methods; @@ -13,28 +9,18 @@ public sealed class SpectralToIntegralMethodTests [Test] public void CalculateTest() { - //// Create example spectralDataPoints - // only visible values - IReadOnlyList spectralDataPoints = new List { - new DataPoint(new Incidence(new Wavelengths(200), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(784), new Direction(8)), new Emergence(new Direction(8)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(785), new Direction(8)), new Emergence(new Direction(8)), new Results(0)), - new DataPoint(new Incidence(new Wavelengths(2600), new Direction(8)), new Emergence(new Direction(8)), new Results(0)) }; - - // Use the SpectralToIntegralMethod - SpectralToIntegralMethod mySpectralToIntegralMethod = new SpectralToIntegralMethod(); - List results = mySpectralToIntegralMethod.Calculate(spectralDataPoints); - foreach (DataPoint integralDataPoint in results) - { - if (integralDataPoint.Incidence.Wavelengths.Wavelength==0) - {integralDataPoint.Results.Transmittance.Should().BeApproximately(1.0F, 0.000000000000001F);} - if (integralDataPoint.Incidence.Wavelengths.Wavelength==1) - {integralDataPoint.Results.Transmittance.Should().BeApproximately(0.3733675409177318F, 0.00000001F);} - if (integralDataPoint.Incidence.Wavelengths.Wavelength==2) - {integralDataPoint.Results.Transmittance.Should().BeApproximately(0.212543149312381F, 0.00000001F);} - // Console.WriteLine($"integralDataPoint\nIncidence Wavelength: {integralDataPoint.Incidence.Wavelengths.Wavelength}, Direction Polar: {integralDataPoint.Incidence.Direction.Polar}"); - // Console.WriteLine($"Emergence Direction Polar: {integralDataPoint.Emergence.Direction.Polar}"); - // Console.WriteLine($"Results Transmittance: {integralDataPoint.Results.Transmittance}\n"); - } + var spectralDataPoints = new SpectralToIntegralInput( + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(784)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(785)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(2600)), new Results(0)) + ]) + ); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); } } \ No newline at end of file From 9529ee56684fe66d04d1b6feeaf47e68d3a6e761 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 7 Sep 2025 15:03:13 +0200 Subject: [PATCH 446/878] Add migration that adds approver ID to response approvals --- ...ddApproverIdToResponseApproval.Designer.cs | 2621 +++++++++++++++++ ...7130041_AddApproverIdToResponseApproval.cs | 79 + .../ApplicationDbContextModelSnapshot.cs | 15 + 3 files changed, 2715 insertions(+) create mode 100644 backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs create mode 100644 backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs new file mode 100644 index 00000000..5822cb4d --- /dev/null +++ b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs @@ -0,0 +1,2621 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250907130041_AddApproverIdToResponseApproval")] + partial class AddApproverIdToResponseApproval + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs new file mode 100644 index 00000000..e8736e79 --- /dev/null +++ b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs @@ -0,0 +1,79 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddApproverIdToResponseApproval : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Approval_ApproverId", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_ApproverId", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_ApproverId", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_ApproverId", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "Approval_ApproverId", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Approval_ApproverId", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "Approval_ApproverId", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "Approval_ApproverId", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "Approval_ApproverId", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Approval_ApproverId", + schema: "database", + table: "calorimetric_data"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 324193b1..1d73719d 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -737,6 +737,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("CalorimetricDataId") .HasColumnType("uuid"); + b1.Property("ApproverId") + .HasColumnType("uuid"); + b1.Property("KeyFingerprint") .IsRequired() .HasColumnType("text"); @@ -1088,6 +1091,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("GeometricDataId") .HasColumnType("uuid"); + b1.Property("ApproverId") + .HasColumnType("uuid"); + b1.Property("KeyFingerprint") .IsRequired() .HasColumnType("text"); @@ -1556,6 +1562,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("HygrothermalDataId") .HasColumnType("uuid"); + b1.Property("ApproverId") + .HasColumnType("uuid"); + b1.Property("KeyFingerprint") .IsRequired() .HasColumnType("text"); @@ -1935,6 +1944,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("OpticalDataId") .HasColumnType("uuid"); + b1.Property("ApproverId") + .HasColumnType("uuid"); + b1.Property("KeyFingerprint") .IsRequired() .HasColumnType("text"); @@ -2288,6 +2300,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("PhotovoltaicDataId") .HasColumnType("uuid"); + b1.Property("ApproverId") + .HasColumnType("uuid"); + b1.Property("KeyFingerprint") .IsRequired() .HasColumnType("text"); From 4c393f41a7a59d94e5245d0bcb975b0b6d4eb716 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 8 Sep 2025 15:45:56 +0200 Subject: [PATCH 447/878] Rename `front` to `non_prime` and `back` to `prime` in coated side and add `unknown` and `non_applicable` --- backend/src/Enumerations/CoatedSide.cs | 8 +++++--- frontend/type-defs.graphqls | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/backend/src/Enumerations/CoatedSide.cs b/backend/src/Enumerations/CoatedSide.cs index e787051c..e9ebe5d6 100644 --- a/backend/src/Enumerations/CoatedSide.cs +++ b/backend/src/Enumerations/CoatedSide.cs @@ -5,8 +5,10 @@ namespace Database.Enumerations; [SuppressMessage("Naming", "CA1707")] public enum CoatedSide { - FRONT, - BACK, + PRIME, + NON_PRIME, BOTH, - NEITHER + NEITHER, + UNKNOWN, + NOT_APPLICABLE } \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 934771df..3a149131 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -644,6 +644,7 @@ type Query { } type ResponseApproval { + approverId: Uuid! keyFingerprint: String! message: String! query: String! @@ -1538,10 +1539,12 @@ enum ApplyPolicy { } enum CoatedSide { - FRONT - BACK + PRIME + NON_PRIME BOTH NEITHER + UNKNOWN + NOT_APPLICABLE } enum CreateCalorimetricDataErrorCode { From 1f30f340d921d5e864cf2979ff6948d16e90b8aa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 8 Sep 2025 15:50:16 +0200 Subject: [PATCH 448/878] Add SQL migration and rollback scripts --- backend/src/Migrations/current | 2 +- ...907130041_AddApproverIdToResponseApproval.sql | 16 ++++++++++++++++ backend/src/Migrations/previous | 2 +- ...907130041_AddApproverIdToResponseApproval.sql | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/migrate_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql create mode 100644 backend/src/Migrations/rollback_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current index d627d4ba..de443e87 100644 --- a/backend/src/Migrations/current +++ b/backend/src/Migrations/current @@ -1 +1 @@ -20250902122837_RenameResponsePropertyOfApprovalsToMessage +20250907130041_AddApproverIdToResponseApproval diff --git a/backend/src/Migrations/migrate_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql b/backend/src/Migrations/migrate_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql new file mode 100644 index 00000000..adb299e7 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql @@ -0,0 +1,16 @@ +START TRANSACTION; +ALTER TABLE database.photovoltaic_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.optical_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.hygrothermal_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.geometric_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.calorimetric_data ADD "Approval_ApproverId" uuid; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250907130041_AddApproverIdToResponseApproval', '9.0.8'); + +COMMIT; + diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous index f0aeaa54..d627d4ba 100644 --- a/backend/src/Migrations/previous +++ b/backend/src/Migrations/previous @@ -1 +1 @@ -20250731163636_MakeInstitutionIdOfAccessRightsUnique +20250902122837_RenameResponsePropertyOfApprovalsToMessage diff --git a/backend/src/Migrations/rollback_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql b/backend/src/Migrations/rollback_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql new file mode 100644 index 00000000..a9064b65 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20250902122837_RenameResponsePropertyOfApprovalsToMessage_to_20250907130041_AddApproverIdToResponseApproval.sql @@ -0,0 +1,16 @@ +START TRANSACTION; +ALTER TABLE database.photovoltaic_data DROP COLUMN "Approval_ApproverId"; + +ALTER TABLE database.optical_data DROP COLUMN "Approval_ApproverId"; + +ALTER TABLE database.hygrothermal_data DROP COLUMN "Approval_ApproverId"; + +ALTER TABLE database.geometric_data DROP COLUMN "Approval_ApproverId"; + +ALTER TABLE database.calorimetric_data DROP COLUMN "Approval_ApproverId"; + +DELETE FROM "__EFMigrationsHistory" +WHERE "MigrationId" = '20250907130041_AddApproverIdToResponseApproval'; + +COMMIT; + From ef605194d7cfa295c0ba138a4df9d26e8d64f5ad Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 8 Sep 2025 16:21:47 +0200 Subject: [PATCH 449/878] Dispose of JSON documents properly --- see https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/use-dom#jsondocument-is-idisposable --- backend/src/GraphQl/Methods/CalculateMethodPayload.cs | 8 ++++---- backend/src/GraphQl/Methods/MethodQueries.cs | 4 ++-- backend/src/Methods/IMethod.cs | 2 +- backend/src/Methods/MethodBase.cs | 5 +++-- backend/src/Services/ApiRequestService.cs | 9 ++++++--- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/backend/src/GraphQl/Methods/CalculateMethodPayload.cs b/backend/src/GraphQl/Methods/CalculateMethodPayload.cs index a94c34e5..de61d1fd 100644 --- a/backend/src/GraphQl/Methods/CalculateMethodPayload.cs +++ b/backend/src/GraphQl/Methods/CalculateMethodPayload.cs @@ -7,7 +7,7 @@ public sealed class CalculateMethodPayload : Payload { public CalculateMethodPayload( - JsonDocument result + JsonElement result ) { Result = result; @@ -28,7 +28,7 @@ CalculateMethodError error } public CalculateMethodPayload( - JsonDocument result, + JsonElement result, IReadOnlyCollection errors ) { @@ -37,7 +37,7 @@ IReadOnlyCollection errors } public CalculateMethodPayload( - JsonDocument result, + JsonElement result, CalculateMethodError error ) : this( @@ -47,6 +47,6 @@ CalculateMethodError error { } - public JsonDocument? Result { get; } + public JsonElement? Result { get; } public IReadOnlyCollection? Errors { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 7f9dbd61..1f548aa8 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -61,12 +61,12 @@ CancellationToken cancellationToken ); } using var stream = data.OpenReadStream(); - var jsonData = await JsonDocument.ParseAsync( + using var jsonData = await JsonDocument.ParseAsync( stream, ApiRequestService.JsonDocumentOptions, cancellationToken ); - var result = method.Calculate(jsonData); + var result = method.Calculate(jsonData.RootElement); return new CalculateMethodPayload(result); } diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index dd4145da..6389d6af 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -5,5 +5,5 @@ namespace Database.Methods; public interface IMethod { - public JsonDocument Calculate(JsonDocument input); + public JsonElement Calculate(JsonElement input); } \ No newline at end of file diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index 51db68df..d8f72712 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -14,14 +14,15 @@ public abstract class MethodBase UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; - public JsonDocument Calculate(JsonDocument input) + public JsonElement Calculate(JsonElement input) { var parsedInput = input.Deserialize(s_jsonSerializerOptions) ?? throw new InvalidOperationException(); var output = Calculate(parsedInput); - return JsonDocument.Parse( + using var document = JsonDocument.Parse( JsonSerializer.SerializeToUtf8Bytes(output) ); + return document.RootElement.Clone(); } public abstract TOutput Calculate(TInput input); diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 6ac23666..ae13d47f 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -165,7 +165,7 @@ CancellationToken cancellationToken ); } - public Task PerformHttpGetRequest( + public Task PerformHttpGetRequest( Uri uri, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, @@ -176,10 +176,13 @@ CancellationToken cancellationToken HttpMethod.Get, uri, async httpResponseContent => - await JsonDocument.ParseAsync( + { + using var document = await JsonDocument.ParseAsync( await httpResponseContent.ReadAsStreamAsync(), JsonDocumentOptions - ), + ); + return document.RootElement.Clone(); + }, null, httpClientFactory, httpContextAccessor, From 8aaec0fe718959f658291e809feb88a99c638db0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 8 Sep 2025 17:03:39 +0200 Subject: [PATCH 450/878] Make input model match the JSON data, configure the JSON serializer properties, and update the GraphQL schema --- backend/src/GraphQl/Methods/MethodQueries.cs | 3 +- backend/src/Methods/MethodBase.cs | 9 ++ .../SpectralToIntegralInput.cs | 2 +- .../SpectralToIntegralMethod.cs | 8 +- .../TransmittanceSum/TransmittanceSumInput.cs | 4 +- .../TransmittanceSumMethod.cs | 3 +- .../Methods/SpectralToIntegralMethodTests.cs | 4 +- frontend/type-defs.graphqls | 102 ++++-------------- 8 files changed, 43 insertions(+), 92 deletions(-) diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 1f548aa8..eff3e31f 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -195,7 +195,8 @@ CancellationToken cancellationToken ); } var locator = response.Data.Data.ResourceTree.Root.Value.Locator; - // TODO The locator could also point to a non-JSON resource. Support those also. + // TODO The locator could also point to a non-JSON resource. Support those also: response.Data.Data.ResourceTree.Root.Value.DataFormatId + // TODO Check that the data has the hash value: response.Data.Data.ResourceTree.Root.Value.HashValue; var data = await apiRequestService.PerformHttpGetRequest( locator, httpClientFactory, httpContextAccessor, cancellationToken ); diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index d8f72712..4c4d3d0a 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -10,12 +10,21 @@ public abstract class MethodBase private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() { AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IgnoreReadOnlyFields = true, + IgnoreReadOnlyProperties = false, + IncludeFields = false, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Skip, + RespectNullableAnnotations = true, UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; public JsonElement Calculate(JsonElement input) { + Console.WriteLine(JsonSerializer.Serialize(input)); var parsedInput = input.Deserialize(s_jsonSerializerOptions) ?? throw new InvalidOperationException(); var output = Calculate(parsedInput); diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs index 1be3864b..9642793b 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralInput.cs @@ -3,7 +3,7 @@ namespace Database.Methods.SpectralToIntegral; public sealed record SpectralToIntegralInput( - SpectralToIntegralData Data + IReadOnlyList Data ); public sealed record SpectralToIntegralData( diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index 6fc2b9a9..d655e9d7 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -212,10 +212,12 @@ public sealed class SpectralToIntegralMethod public override SpectralToIntegralOutput Calculate(SpectralToIntegralInput input) { + // TODO What to do when there is more than 1 item? + var firstDataItem = input.Data[0]; return new SpectralToIntegralOutput( - En410Visible: Calculate(input.Data.DataPoints, En410VisibleWavelengthsWeights), - En410Solar: Calculate(input.Data.DataPoints, En410SolarWavelengthsWeights), - Iso9050Solar: Calculate(input.Data.DataPoints, Iso9050SolarWavelengthsWeights) + En410Visible: Calculate(firstDataItem.DataPoints, En410VisibleWavelengthsWeights), + En410Solar: Calculate(firstDataItem.DataPoints, En410SolarWavelengthsWeights), + Iso9050Solar: Calculate(firstDataItem.DataPoints, Iso9050SolarWavelengthsWeights) ); } diff --git a/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs b/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs index e9ee7bf5..7383ba8b 100644 --- a/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs +++ b/backend/src/Methods/TransmittanceSum/TransmittanceSumInput.cs @@ -3,10 +3,10 @@ namespace Database.Methods.TransmittanceSum; public sealed record TransmittanceSumInput( - Data Data + IReadOnlyList Data ); -public sealed record Data( +public sealed record TransmittanceSumData( IReadOnlyList DataPoints ); diff --git a/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs b/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs index d2704764..9974957b 100644 --- a/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs +++ b/backend/src/Methods/TransmittanceSum/TransmittanceSumMethod.cs @@ -10,7 +10,8 @@ public sealed class TransmittanceSumMethod public override double Calculate(TransmittanceSumInput input) { double transmittanceSum = 0; - foreach (var dataPoint in input.Data.DataPoints) + // TODO What to do when there is more than 1 item? + foreach (var dataPoint in input.Data[0].DataPoints) { transmittanceSum += dataPoint.Results.Transmittance; } diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 0da2a9ef..3ba8d733 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -9,14 +9,14 @@ public sealed class SpectralToIntegralMethodTests [Test] public void CalculateTest() { - var spectralDataPoints = new SpectralToIntegralInput( + var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), new DataPoint(new Incidence(new Wavelengths(784)), new Results(1)), new DataPoint(new Incidence(new Wavelengths(785)), new Results(0)), new DataPoint(new Incidence(new Wavelengths(2600)), new Results(0)) ]) - ); + ]); var method = new SpectralToIntegralMethod(); var results = method.Calculate(spectralDataPoints); results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 934771df..cafc5cb2 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -58,6 +58,17 @@ type AppliedMethod { sources: [NamedMethodSource!]! } +type CalculateMethodError { + code: CalculateMethodErrorCode! + message: String! + path: [String!]! +} + +type CalculateMethodPayload { + errors: [CalculateMethodError!] + result: Any +} + type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! approval: ResponseApproval @@ -107,21 +118,6 @@ type CielabColor { lStar: Float! } -type ColorReflectance { - cielabColors: [MethodAsServiceCielabColor!]! -} - -type ColorRenderingIndex { - illuminant: String! - observer: String! - ra: Int! -} - -type ColorTransmittance { - cielabColors: [MethodAsServiceCielabColor!]! - colorRenderingIndices: [ColorRenderingIndex!]! -} - type CreateCalorimetricDataError { code: CreateCalorimetricDataErrorCode! message: String! @@ -245,12 +241,6 @@ type DataEdge { node: Data! } -type DataPoint { - emergence: Emergence! - incidence: Incidence! - results: Results! -} - type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -262,14 +252,6 @@ type Database { verificationState: DatabaseVerificationState! } -type Direction { - polar: Int! -} - -type Emergence { - direction: Direction! -} - type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -407,11 +389,6 @@ type HygrothermalDataEdge { node: HygrothermalData! } -type Incidence { - direction: Direction! - wavelengths: Wavelengths! -} - type InstitutionAccessRights { allowedDatasetsPerTime: NonNegativeInt allowedUserCount: NonNegativeInt @@ -436,25 +413,6 @@ type KeyValuePairOfGuidAndNullableOfUInt32 { value: NonNegativeInt } -type MethodAsServiceCielabColor { - aStar: Float! - bStar: Float! - illuminant: String! - lStar: Int! - observer: String! -} - -type MethodAsServiceError { - code: MethodAsServiceErrorCode! - message: String! - path: [String!]! -} - -type MethodAsServicePayload { - calculationResult: [DataPoint!] - errors: [MethodAsServiceError!] -} - type Mutation { addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") addDataApproval(input: DataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") @@ -618,6 +576,8 @@ type Query { allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo @cost(weight: "10") @@ -632,8 +592,6 @@ type Query { hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") - methodAsService(input: MethodAsServiceInput!): MethodAsServicePayload! @cost(weight: "10") - methodAsServiceWithFile(input: MethodAsServiceWithFileInput!): MethodAsServicePayload! @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." @@ -644,6 +602,7 @@ type Query { } type ResponseApproval { + approverId: Uuid! keyFingerprint: String! message: String! query: String! @@ -652,13 +611,6 @@ type ResponseApproval { variables: Any! } -type Results { - colorReflectance: ColorReflectance - colorTransmittance: ColorTransmittance - reflectance: Float - transmittance: Float! -} - "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -739,10 +691,6 @@ type UserInfo { website: String } -type Wavelengths { - wavelength: Int! -} - union Reference = Publication | Standard input AppliedMethodInput { @@ -1197,17 +1145,6 @@ input ListStandardizerPropositionInput { any: Boolean @cost(weight: "10") } -input MethodAsServiceInput { - databaseId: Uuid! - dataId: Uuid! - methodId: Uuid! -} - -input MethodAsServiceWithFileInput { - file: Upload! - methodId: Uuid! -} - input NamedMethodArgumentInput { name: String! value: Any! @@ -1537,6 +1474,12 @@ enum ApplyPolicy { VALIDATION } +enum CalculateMethodErrorCode { + UNKNOWN_METHOD + UNKNOWN_DATABASE + DATA_QUERY_FAILED +} + enum CoatedSide { FRONT BACK @@ -1626,11 +1569,6 @@ enum JsonValueKind { NULL } -enum MethodAsServiceErrorCode { - UNKNOWN_METHOD - UNKNOWN_DATABASE -} - enum OpticalComponentSubtype { MONOLITHIC LAMINATE From 1bd18d5d8b3a4b4dcee0b74ec896ace3b2457efb Mon Sep 17 00:00:00 2001 From: Christoph Maurer <33456252+christoph-maurer@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:54:48 +0200 Subject: [PATCH 451/878] Add example for `make gpg` to README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 96497374..d7b03f99 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ If you have a question for which you don't find the answer in this repository, p `${GNUPG_PRIVATEKEY_PASSPHRASE}` to the file `./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` by running `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with - your information filled in. + your information filled in. For example: `make NAME=Anna" "Smith + COMMENT=first" "test EMAIL=anna.smith@fraunhofer.de gpg` 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see From a32ebe3a27e2d06127adca4321d4977ff37e07b9 Mon Sep 17 00:00:00 2001 From: Christoph Maurer <33456252+christoph-maurer@users.noreply.github.com> Date: Tue, 9 Sep 2025 15:11:41 +0200 Subject: [PATCH 452/878] Refer to `.env.sample` for `${GNUPG_PRIVATEKEY_PASSPHRASE}` --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d7b03f99..583333fe 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ If you have a question for which you don't find the answer in this repository, p `./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` by running `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information filled in. For example: `make NAME=Anna" "Smith - COMMENT=first" "test EMAIL=anna.smith@fraunhofer.de gpg` + COMMENT=first" "test EMAIL=anna.smith@fraunhofer.de gpg`. The variable + `${GNUPG_PRIVATEKEY_PASSPHRASE}` is set in `.env.sample`. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see From 2197981c4f66984df442a57f750a16f401bb8541 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Oct 2025 16:25:19 +0200 Subject: [PATCH 453/878] Remove unused environment variables of unused `NextAuth.js` library --- frontend/.env.local.sample | 6 ------ frontend/environment.d.ts | 5 ----- 2 files changed, 11 deletions(-) diff --git a/frontend/.env.local.sample b/frontend/.env.local.sample index d4a836e8..21c3ff38 100644 --- a/frontend/.env.local.sample +++ b/frontend/.env.local.sample @@ -1,9 +1,3 @@ NEXT_PUBLIC_DATABASE_URL=https://local.solarbuildingenvelopes.com:5051 NEXT_PUBLIC_METABASE_URL=https://local.buildingenvelopedata.org:4041 -NEXTAUTH_URL=https://local.solarbuildingenvelopes.com:5051 NEXT_WEBPACK_USEPOLLING=false - -AUTH_CLIENT_ID=testlab-solar-facades -AUTH_CLIENT_SECRET=secret -AUTH_SECRET=secret -AUTH_JWT_SECRET=INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnw diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index 8755fd84..8532be0c 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -3,13 +3,8 @@ declare global { interface ProcessEnv { NEXT_PUBLIC_DATABASE_URL: string; NEXT_PUBLIC_METABASE_URL: string; - NEXTAUTH_URL: string; NEXT_WEBPACK_USEPOLLING: string; NODE_ENV: "test" | "development" | "production"; - AUTH_CLIENT_ID: string; - AUTH_CLIENT_SECRET: string; - AUTH_SECRET: string; - AUTH_JWT_SECRET: string; } } } From 587e1c2b32d6854adaf4ef451442713fcb2c6540 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Oct 2025 16:32:44 +0200 Subject: [PATCH 454/878] Set `NODE_ENV` to `production` as anything else is considered an antipattern --- https://nodejs.org/en/learn/getting-started/nodejs-the-difference-between-development-and-production --- docker-compose.yml | 2 +- frontend/environment.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 770ae65f..82ca053a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -164,7 +164,7 @@ services: extra_hosts: - ${HOST}:host-gateway environment: - - NODE_ENV=development + - NODE_ENV=production depends_on: - backend diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index 8532be0c..00801779 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -4,7 +4,7 @@ declare global { NEXT_PUBLIC_DATABASE_URL: string; NEXT_PUBLIC_METABASE_URL: string; NEXT_WEBPACK_USEPOLLING: string; - NODE_ENV: "test" | "development" | "production"; + NODE_ENV: "production"; } } } From 23ee382dc9901add6b42aceff11c846a328aa889 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Oct 2025 18:10:47 +0200 Subject: [PATCH 455/878] Document new `.env` environment variables --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 583333fe..45a4545f 100644 --- a/README.md +++ b/README.md @@ -245,19 +245,26 @@ and the pages following it. of the Docker container names listed by `docker ps --all`; - `HOST` is the domain name with sub-domain of the deployment, in particular, it is used to make resource locators absolute; - - `HTTP_PORT` is the HTTP port on which the reverse proxy NGINX listens - for requests; + - `HTTP_PORT` is the HTTP port to which the reverse proxy NGINX forwards + for HTTPS requests (see `PRODUCTION_HTTP_PORT` and + `STAGING_HTTP_PORT` in the `.env` file of your clone of + [machine](https://github.com/building-envelope-data/machine)); - `METABASE_HOST` is the domain name with sub-domain of the metabase, in particular, to use it as OpenId Connect provider and to ask it for information about logged-in users needed for authorization; - `DATABASE_ID` is the UUID that was assigned to this product-data database upon registering it at the metabase; + - `OPERATOR_ID` is the UUID of the institution that operates this + product-data database; - `VERIFICATION_CODE` is the verification code that was generated for this product-data database upon registering it at the metabase; - `OPEN_ID_CONNECT_CLIENT_SECRET` is the OpenId Connect client secret of this product-data database as a client of the metabase acting as identity provider (the client secret is given when registering an OpenId Connect client at the metabase); + - `GNUPG_PRIVATEKEY_FILE_NAME` and `GNUPG_PRIVATEKEY_PASSPHRASE` are the + file name of an exported GnuPG private key and its passphrase that + lives in the directory `./backend/src/gpg-keys/`; - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to encrypt and sign JSON web tokens (JWT) used by OpenId Connect; From 15ab147d4bef5e36b8997b7eca2d19ced95c034b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 7 Oct 2025 18:29:44 +0200 Subject: [PATCH 456/878] Allow and set `NODE_ENV` to `development` to fix the type error `TypeError: (0 , react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxDEV) is not a function` --- docker-compose.yml | 2 +- frontend/environment.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 82ca053a..770ae65f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -164,7 +164,7 @@ services: extra_hosts: - ${HOST}:host-gateway environment: - - NODE_ENV=production + - NODE_ENV=development depends_on: - backend diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index 00801779..55c71f8b 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -4,7 +4,7 @@ declare global { NEXT_PUBLIC_DATABASE_URL: string; NEXT_PUBLIC_METABASE_URL: string; NEXT_WEBPACK_USEPOLLING: string; - NODE_ENV: "production"; + NODE_ENV: "development" | "production"; } } } From 3a3b2c316c2e6ac174b8626a93f348741c396cdd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 8 Oct 2025 19:05:03 +0200 Subject: [PATCH 457/878] Clean-up external API requests, transform and report errors from them, and prefer records over classes --- .../src/ApiRequests/CheckGnuPgFingerprint.cs | 5 + backend/src/ApiRequests/DatabaseApi.cs | 78 ----------- backend/src/ApiRequests/Dto/CurrentUserDto.cs | 47 ------- backend/src/ApiRequests/Dto/DatabaseDto.cs | 39 ------ backend/src/ApiRequests/Dto/UserInfoDto.cs | 19 --- backend/src/ApiRequests/Dto/XDataDto.cs | 121 ------------------ backend/src/ApiRequests/GetUserInfo.cs | 49 +++++++ .../src/ApiRequests/GraphQlRequestHelper.cs | 48 +++++++ .../Queries/CheckGnuPgFingerprint.graphql | 23 ++++ .../{DataApi.cs => QueryAllMetaData.cs} | 32 +---- backend/src/ApiRequests/QueryCurrentUser.cs | 96 ++++++++++++++ backend/src/ApiRequests/QueryData.cs | 38 ++++++ backend/src/ApiRequests/QueryDatabase.cs | 46 +++++++ backend/src/ApiRequests/UpdateDatabase.cs | 75 +++++++++++ backend/src/ApiRequests/UserApi.cs | 80 ------------ .../src/Authorization/CommonAuthorization.cs | 6 +- backend/src/Data/ApplicationDbContext.cs | 12 ++ backend/src/Data/InstitutionAccessRights.cs | 1 - backend/src/Data/UserInfo.cs | 45 ------- .../AccessRights/DataAccessRightsMutations.cs | 3 +- .../CalorimetricDataMutations.cs | 1 - .../DataApprovals/DataApprovalMutations.cs | 90 ++++++++++++- .../DataApprovals/RemoveDataApprovalError.cs | 13 ++ .../RemoveDataApprovalErrorCode.cs | 14 ++ .../DataApprovals/RemoveDataApprovalInput.cs | 9 ++ .../RemoveDataApprovalPayload.cs | 29 +++++ backend/src/GraphQl/DataX/DataEdge.cs | 10 +- backend/src/GraphQl/DataX/DataEdgeBase.cs | 12 +- backend/src/GraphQl/DataX/DataQueries.cs | 3 +- backend/src/GraphQl/Databases/Database.cs | 42 ------ .../GraphQl/Databases/DatabaseMutations.cs | 50 ++++---- .../src/GraphQl/Databases/DatabasePayload.cs | 14 -- .../src/GraphQl/Databases/DatabaseQueries.cs | 47 ++----- .../GraphQl/Databases/UpdateDatabaseError.cs | 12 -- .../Databases/UpdateDatabaseErrorCode.cs | 11 -- .../GraphQl/Databases/UpdateDatabaseInput.cs | 10 -- .../Databases/UpdateDatabasePayload.cs | 11 -- backend/src/GraphQl/Methods/MethodQueries.cs | 74 ++--------- backend/src/GraphQl/Users/UserQueries.cs | 44 ++----- backend/src/Models/Database.cs | 15 +++ backend/src/Models/DatabaseOperatorEdge.cs | 5 + .../DatabaseVerificationState.cs | 2 +- backend/src/Models/Institution.cs | 7 + backend/src/Services/AccessRightsService.cs | 4 +- backend/src/Services/ApiRequestService.cs | 24 +++- backend/src/Services/CacheService.cs | 6 +- backend/src/Services/DataService.cs | 31 ----- .../src/Services/ResponseApprovalService.cs | 18 +-- backend/src/Services/UserService.cs | 33 ++--- backend/src/Startup.cs | 1 - 50 files changed, 691 insertions(+), 814 deletions(-) create mode 100644 backend/src/ApiRequests/CheckGnuPgFingerprint.cs delete mode 100644 backend/src/ApiRequests/DatabaseApi.cs delete mode 100644 backend/src/ApiRequests/Dto/CurrentUserDto.cs delete mode 100644 backend/src/ApiRequests/Dto/DatabaseDto.cs delete mode 100644 backend/src/ApiRequests/Dto/UserInfoDto.cs delete mode 100644 backend/src/ApiRequests/Dto/XDataDto.cs create mode 100644 backend/src/ApiRequests/GetUserInfo.cs create mode 100644 backend/src/ApiRequests/GraphQlRequestHelper.cs create mode 100644 backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql rename backend/src/ApiRequests/{DataApi.cs => QueryAllMetaData.cs} (69%) create mode 100644 backend/src/ApiRequests/QueryCurrentUser.cs create mode 100644 backend/src/ApiRequests/QueryData.cs create mode 100644 backend/src/ApiRequests/QueryDatabase.cs create mode 100644 backend/src/ApiRequests/UpdateDatabase.cs delete mode 100644 backend/src/ApiRequests/UserApi.cs delete mode 100644 backend/src/Data/UserInfo.cs create mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs create mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs create mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs create mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs delete mode 100644 backend/src/GraphQl/Databases/Database.cs delete mode 100644 backend/src/GraphQl/Databases/DatabasePayload.cs delete mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseError.cs delete mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs delete mode 100644 backend/src/GraphQl/Databases/UpdateDatabaseInput.cs delete mode 100644 backend/src/GraphQl/Databases/UpdateDatabasePayload.cs create mode 100644 backend/src/Models/Database.cs create mode 100644 backend/src/Models/DatabaseOperatorEdge.cs rename backend/src/{GraphQl/Databases => Models}/DatabaseVerificationState.cs (64%) create mode 100644 backend/src/Models/Institution.cs delete mode 100644 backend/src/Services/DataService.cs diff --git a/backend/src/ApiRequests/CheckGnuPgFingerprint.cs b/backend/src/ApiRequests/CheckGnuPgFingerprint.cs new file mode 100644 index 00000000..2c8eb98c --- /dev/null +++ b/backend/src/ApiRequests/CheckGnuPgFingerprint.cs @@ -0,0 +1,5 @@ +namespace Database.ApiRequests; + +public sealed record ValidFingerprints( + uint TotalCount +); \ No newline at end of file diff --git a/backend/src/ApiRequests/DatabaseApi.cs b/backend/src/ApiRequests/DatabaseApi.cs deleted file mode 100644 index 7ede9f38..00000000 --- a/backend/src/ApiRequests/DatabaseApi.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests.Dto; -using Database.GraphQl.Databases; -using Database.Services; -using GraphQL; -using HotChocolate; -using Microsoft.AspNetCore.Http; - -namespace Database.ApiRequests; - -public sealed class DatabaseApi -{ - private static readonly string[] _databaseFileNames = - [ - "Database.graphql" - ]; - - private static readonly string[] _updateDatabaseFileNames = - [ - "UpdateDatabase.graphql" - ]; - - private sealed record DatabaseData(DatabaseDto Database); - - public static async Task RequestDatabase( - Guid databaseId, - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - { - var response = await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(_databaseFileNames), - new - { - id = databaseId - }, - "Database" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - return response.Data?.Database; - } - - public static async Task UpdateDatabase( - UpdateDatabaseInput updateDatabaseInput, - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken - ) - { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery( - _updateDatabaseFileNames - ), - new - { - input = updateDatabaseInput - }, - "UpdateDatabase" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ))?.Data; - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/CurrentUserDto.cs b/backend/src/ApiRequests/Dto/CurrentUserDto.cs deleted file mode 100644 index 5c2056e5..00000000 --- a/backend/src/ApiRequests/Dto/CurrentUserDto.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Database.ApiRequests.Dto; - -public enum InstitutionRepresentativeRole -{ - OWNER, - ASSISTANT -} - -public sealed record CurrentUserDto( - Guid Uuid, - string Name, - UserRepresentedInstitutionConnection RepresentedInstitutions, - UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions -); - -public sealed record UserRepresentedInstitutionConnection( - IReadOnlyList Edges, - uint TotalCount -); - -public sealed record UserRepresentedInstitutionEdge( - UserRepresentedInstitutionNode Node, - InstitutionRepresentativeRole Role -); - -public sealed record UserRepresentedInstitutionNode( - Guid Uuid, - string Name, - InstitutionManagedInstitutionConnection ManagedInstitutions -); - -public sealed record InstitutionManagedInstitutionConnection( - IReadOnlyList Edges, - uint TotalCount -); - -public sealed record InstitutionManagedInstitutionEdge( - InstitutionManagedInstitutionNode Node -); - -public sealed record InstitutionManagedInstitutionNode( - Guid Uuid, - string Name -); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/DatabaseDto.cs b/backend/src/ApiRequests/Dto/DatabaseDto.cs deleted file mode 100644 index f6b80993..00000000 --- a/backend/src/ApiRequests/Dto/DatabaseDto.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.GraphQl.Databases; - -namespace Database.ApiRequests.Dto; - -public sealed record Institution( - Guid Uuid -); - -public sealed record DatabaseOperatorEdge( - Institution Node -); - -public sealed record DatabaseDto( - - Guid Uuid, - string Name, - string Description, - Uri Locator, - DatabaseVerificationState VerificationState, - string VerificationCode, - DatabaseOperatorEdge Operator, - - // public Institution? Operator { get; set; } - bool CanCurrentUserUpdateNode, - bool CanCurrentUserVerifyNode -); - -public sealed record ErrorDto( - string Code, - string Message, - IReadOnlyList Path -); - -public sealed record DatabasePayloadDto( - DatabaseDto? Database, - IReadOnlyList? Errors -); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/UserInfoDto.cs b/backend/src/ApiRequests/Dto/UserInfoDto.cs deleted file mode 100644 index 4b22065d..00000000 --- a/backend/src/ApiRequests/Dto/UserInfoDto.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -namespace Database.ApiRequests.Dto; - -public sealed record UserInfoDto( - AddressDto? Address, - string Email, - bool EmailVerified, - string Name, - string? PhoneNumber, - bool PhoneNumberVerified, - IReadOnlyList? Roles, - string Sub, // Subject - string? Website -); - -public sealed record AddressDto( - string Formatted -); \ No newline at end of file diff --git a/backend/src/ApiRequests/Dto/XDataDto.cs b/backend/src/ApiRequests/Dto/XDataDto.cs deleted file mode 100644 index 279dcc10..00000000 --- a/backend/src/ApiRequests/Dto/XDataDto.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.Data; -using Database.Enumerations; - -namespace Database.ApiRequests.Dto; - -public sealed record GeometricDataDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings, - IReadOnlyList Thicknesses -); - -public sealed record CalorimetricDataDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings, - IReadOnlyList GValues, - IReadOnlyList UValues -); - -public sealed record HygrothermalDataDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings -); - -public sealed record OpticalDataDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings, - OpticalComponentType? Type, - OpticalComponentSubtype? Subtype, - CoatedSide? CoatedSide, - IReadOnlyList NearnormalHemisphericalVisibleTransmittances, - IReadOnlyList NearnormalHemisphericalVisibleReflectances, - IReadOnlyList NearnormalHemisphericalSolarTransmittances, - IReadOnlyList NearnormalHemisphericalSolarReflectances, - IReadOnlyList InfraredEmittances, - IReadOnlyList ColorRenderingIndices, - ICollection CielabColors -); - -public sealed record PhotovoltaicDataDto( - string Uuid, - DateTime Timestamp, - string ComponentId, - string Name, - string Description, - AppliedMethodDto AppliedMethod, - ResourceTreeDto ResourceTree, - IReadOnlyList Approvals, - DateTime CreatedAt, - string CreatorId, - string Locale, - IReadOnlyList Resources, - IReadOnlyList Warnings -); - -public sealed record AppliedMethodDto( - string MethodId, - IReadOnlyList Arguments, - IReadOnlyList Sources -); - -public sealed record ResourceDto( - string Description, - string HashValue, - string Locator, - Guid DataFormatId -); - -public sealed record ResourceTreeDto( - RootDto Root, - IReadOnlyList NonRootVertices -); - -public sealed record RootDto( - ResourceDto Value -); \ No newline at end of file diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs new file mode 100644 index 00000000..e22a0631 --- /dev/null +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequests; + +public sealed class GetUserInfo +{ + public static Uri GetEndpoint(AppSettings appSettings) => + new(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"); + + public sealed record UserInfo( + Address? Address, + string Email, + bool EmailVerified, + string Name, + string? PhoneNumber, + bool PhoneNumberVerified, + IReadOnlyList? Roles, + string Sub, // Subject + string? Website + ); + + public sealed record Address( + string Formatted + ); + + /// + /// Request user info from Metabase. + /// + public static Task Do( + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + return apiRequestService.Metabase().PerformHttpGetRequest( + GetEndpoint(appSettings), + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/GraphQlRequestHelper.cs b/backend/src/ApiRequests/GraphQlRequestHelper.cs new file mode 100644 index 00000000..f8942268 --- /dev/null +++ b/backend/src/ApiRequests/GraphQlRequestHelper.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Resolvers; + +namespace Database.ApiRequests; + +public sealed class GraphQlRequestHelper +{ + public static async Task TransformExceptionsAsync( + Func> action, + IResolverContext resolverContext, + Uri databaseLocator + ) + { + try + { + return await action(); + } + catch (HttpRequestException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("EXTERNAL_GRAPHQL_REQUEST_FAILED") + .SetPath(resolverContext.Path) + .SetMessage($"Failed with status code '{e.StatusCode}' to request the endpoint '{databaseLocator}'.") + .SetException(e) + .Build() + ); + } + catch (JsonException e) + { + throw new GraphQLException( + ErrorBuilder.New() + .SetCode("JSON_DESERIALIZATION_FAILED") + .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) + .ToList()) // TODO Splitting the path at '.' is wrong in general. + .SetMessage( + $"Failed to deserialize GraphQL response of request to the endpoint '{databaseLocator}'. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is '{e.Path}'.") + .SetException(e) + .Build() + ); + } + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql b/backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql new file mode 100644 index 00000000..60a93720 --- /dev/null +++ b/backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql @@ -0,0 +1,23 @@ +query CheckGnuPgFingerprint( + $institutionId: Uuid! + $fingerprint: String! + $createdAt: DateTime! +) { + validFingerprints: institutions( + where: { + id: { equalTo: $institutionId } + gnuPgKeyFingerprints: { + some: { + fingerprint: { equalTo: $fingerprint } + allowedAt: { lessThanOrEqualTo: $createdAt } + or: [ + { forbiddenAt: { equalTo: null } } + { forbiddenAt: { greaterThan: $createdAt } } + ] + } + } + } + ) { + totalCount + } +} diff --git a/backend/src/ApiRequests/DataApi.cs b/backend/src/ApiRequests/QueryAllMetaData.cs similarity index 69% rename from backend/src/ApiRequests/DataApi.cs rename to backend/src/ApiRequests/QueryAllMetaData.cs index 4d2a070b..dc96ece8 100644 --- a/backend/src/ApiRequests/DataApi.cs +++ b/backend/src/ApiRequests/QueryAllMetaData.cs @@ -9,8 +9,11 @@ namespace Database.ApiRequests; -public sealed class DataApi +public sealed class QueryAllMetaData { + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + ApiRequestService.DatabaseGraphQlEndpoint(appSettings); + public static readonly string[] CalorimetricDataFileNames = [ "DataFields.graphql", @@ -46,7 +49,7 @@ public sealed class DataApi "OpticalData.graphql" ]; - public static async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData( + public static async Task<(string Query, JsonElement Variables, string Response)> Do( Guid dataId, string[] fileNames, AppSettings appSettings, @@ -67,29 +70,4 @@ CancellationToken cancellationToken ); return (query, JsonSerializer.SerializeToElement(variables), response); } - - public static async Task> QueryDataFromDatabase( - Uri databaseUrl, - Guid dataId, - string query, - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - - where TGraphQlData : class - { - return await apiRequestService.Database().QueryGraphQl( - appSettings, - databaseUrl, - new GraphQLRequest( - query, - new { id = dataId } - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUser.cs b/backend/src/ApiRequests/QueryCurrentUser.cs new file mode 100644 index 00000000..311bd62f --- /dev/null +++ b/backend/src/ApiRequests/QueryCurrentUser.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequests; + +/// +/// Class to request user info from Metabase API. +/// +public sealed class QueryCurrentUser +{ + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + + public sealed record CurrentUser( + Guid Uuid, + string Name, + UserRepresentedInstitutionConnection RepresentedInstitutions, + UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions + ); + + public sealed record UserRepresentedInstitutionConnection( + IReadOnlyList Edges, + uint TotalCount + ); + + public enum InstitutionRepresentativeRole + { + OWNER, + ASSISTANT + } + + public sealed record UserRepresentedInstitutionEdge( + UserRepresentedInstitutionNode Node, + InstitutionRepresentativeRole Role + ); + + public sealed record UserRepresentedInstitutionNode( + Guid Uuid, + string Name, + InstitutionManagedInstitutionConnection ManagedInstitutions + ); + + public sealed record InstitutionManagedInstitutionConnection( + IReadOnlyList Edges, + uint TotalCount + ); + + public sealed record InstitutionManagedInstitutionEdge( + InstitutionManagedInstitutionNode Node + ); + + public sealed record InstitutionManagedInstitutionNode( + Guid Uuid, + string Name + ); + + private sealed record CurrentUserData(CurrentUser CurrentUser); + + private static readonly string[] s_currentUserFileNames = + [ + "CurrentUser.graphql" + ]; + + /// + /// Request current user from Metabase. + /// + public static async Task Do( + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + return (await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_currentUserFileNames), + new { + databaseId = appSettings.DatabaseId + }, + "CurrentUser" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + )) + ?.Data + ?.CurrentUser + ?? null; + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryData.cs b/backend/src/ApiRequests/QueryData.cs new file mode 100644 index 00000000..937679ff --- /dev/null +++ b/backend/src/ApiRequests/QueryData.cs @@ -0,0 +1,38 @@ +using System; +using System.Net.Http; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequests; + +public sealed class QueryData +{ + public static async Task> Do( + Uri databaseUrl, + Guid dataId, + string query, + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + + where TGraphQlData : class + { + return await apiRequestService.Database().QueryGraphQl( + appSettings, + databaseUrl, + new GraphQLRequest( + query, + new { id = dataId } + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs new file mode 100644 index 00000000..c4d1d75b --- /dev/null +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -0,0 +1,46 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GraphQL; +using Microsoft.AspNetCore.Http; + +namespace Database.ApiRequests; + +public sealed class QueryDatabase +{ + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + + private sealed record DatabaseData(Models.Database Database); + + private static readonly string[] s_queryDatabaseFileNames = + [ + "Database.graphql" + ]; + + public static async Task Do( + Guid databaseId, + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken) + { + var response = await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_queryDatabaseFileNames), + new + { + id = databaseId + }, + "Database" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + return response.Data?.Database; + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs new file mode 100644 index 00000000..03c1075f --- /dev/null +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using GraphQL; +using Database.Services; +using System; + +namespace Database.ApiRequests; + +public sealed class UpdateDatabase +{ + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + + public sealed record UpdateDatabaseInput( + Guid DatabaseId, + string Name, + string Description, + Uri Locator + ); + + public sealed record UpdateDatabasePayload( + Models.Database? Database, + IReadOnlyList? Errors + ); + + [SuppressMessage("Naming", "CA1707")] + public enum UpdateDatabaseErrorCode + { + UNKNOWN, + UNAUTHORIZED, + UNKNOWN_DATABASE + } + + public sealed record UpdateDatabaseError( + UpdateDatabaseErrorCode Code, + string Message, + IReadOnlyList Path + ); + + private static readonly string[] s_updateDatabaseFileNames = + [ + "UpdateDatabase.graphql" + ]; + + public static async Task Do( + UpdateDatabaseInput updateDatabaseInput, + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery( + s_updateDatabaseFileNames + ), + new + { + input = updateDatabaseInput + }, + "UpdateDatabase" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + ))?.Data; + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/UserApi.cs b/backend/src/ApiRequests/UserApi.cs deleted file mode 100644 index 1fdbef45..00000000 --- a/backend/src/ApiRequests/UserApi.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests.Dto; -using Database.Services; -using GraphQL; -using Microsoft.AspNetCore.Http; - -namespace Database.ApiRequests; - -/// -/// Class to request user info from Metabase API. -/// -public sealed class UserApi -{ - private static readonly string[] s_currentUserFileNames = - [ - "CurrentUser.graphql" - ]; - - private sealed record CurrentUserData(CurrentUserDto CurrentUser); - - /// - /// Request current user from Metabase. - /// - /// - /// - /// - /// - /// - /// if successful. - public static async Task RequestCurrentUser( - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_currentUserFileNames), - new { - databaseId = appSettings.DatabaseId - }, - "CurrentUser" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - )) - ?.Data - ?.CurrentUser - ?? null; - } - - /// - /// Request user info from Metabase. - /// - /// - /// - /// - /// - /// - /// if successful. - public static Task RequestUserInfo( - AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) - { - return apiRequestService.Metabase().PerformHttpGetRequest( - new Uri(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"), - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 17f70814..d4f3286a 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,20 +1,20 @@ using System; using System.Linq; -using Database.ApiRequests.Dto; +using static Database.ApiRequests.QueryCurrentUser; namespace Database.Authorization; public static class CommonAuthorization { public static bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( - CurrentUserDto currentUser + CurrentUser currentUser ) { return currentUser.DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; } public static bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - CurrentUserDto currentUser, + CurrentUser currentUser, Guid institutionId ) { diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 3d2c0014..5fa1fc37 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,3 +1,6 @@ +using System; +using System.Threading; +using System.Threading.Tasks; using Database.Enumerations; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; @@ -43,6 +46,15 @@ DbContextOptions options public DbSet GeometricData { get; private set; } = default!; public DbSet InstitutionAccessRights { get; private set; } = default!; + public async Task GetDataAsync(Guid id, CancellationToken cancellationToken) + { + return await CalorimetricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await HygrothermalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await OpticalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await GeometricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? + await PhotovoltaicData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; + } + private static void CreateEnumerations(ModelBuilder builder, string schemaName) { // https://www.npgsql.org/efcore/mapping/enum.html?tabs=with-datasource#mapping-your-enum diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index dd77fd22..0fb532b6 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; -using Database.ApiRequests.Dto; using Database.Services; using EntityFrameworkCore.Projectables; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/Data/UserInfo.cs b/backend/src/Data/UserInfo.cs deleted file mode 100644 index 69e55b0b..00000000 --- a/backend/src/Data/UserInfo.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using Database.ApiRequests.Dto; - -namespace Database.Data; - -public sealed class Address(string formatted) -{ - public string Formatted { get; private set; } = formatted; -} - -public sealed class UserInfo( - Address? address, - string email, - bool emailVerified, - string name, - string? phonenumber, - bool phonenumberVerified, - IReadOnlyList? roles, - string subject, - string? website) -{ - public Address? Address { get; private set; } = address; - public string Email { get; private set; } = email; - public bool EmailVerified { get; private set; } = emailVerified; - public string Name { get; private set; } = name; - public string? PhoneNumber { get; private set; } = phonenumber; - public bool PhoneNumberVerified { get; private set; } = phonenumberVerified; - public IReadOnlyList? Roles { get; private set; } = roles; - public string Sub { get; private set; } = subject; - public string? Website { get; private set; } = website; - - public static UserInfo? FromDto(UserInfoDto? userInfoDto) - { - return userInfoDto is not null ? new UserInfo( - userInfoDto.Address is not null ? new Address(userInfoDto.Address.Formatted) : null, - userInfoDto.Email, - userInfoDto.EmailVerified, - userInfoDto.Name, - userInfoDto.PhoneNumber, - userInfoDto.PhoneNumberVerified, - userInfoDto.Roles, - userInfoDto.Sub, - userInfoDto.Website) : null; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 44681426..abddbd7f 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -16,7 +16,6 @@ public async Task UpdateDataAccessRightsAsync( DataAccessRightsInput input, ApplicationDbContext context, UserService userService, - DataService dataService, CancellationToken cancellationToken ) { @@ -43,7 +42,7 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); + var data = await context.GetDataAsync(input.DataId, cancellationToken); if (data is null) { return new UpdateDataAccessRightsPayload( diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index ff4fe595..1f842563 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Authorization; diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 59dd3230..58e9fa0d 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -1,9 +1,9 @@ using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.Extensions; using Database.GraphQl.References; using Database.Services; using GraphQL.Client.Abstractions.Utilities; @@ -19,7 +19,6 @@ public async Task AddDataApprovalAsync( DataApprovalInput input, ApplicationDbContext context, UserService userService, - DataService dataService, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -46,7 +45,7 @@ CancellationToken cancellationToken ); } - var data = await dataService.GetDataAsync(input.DataId, context, cancellationToken); + var data = await context.GetDataAsync(input.DataId, cancellationToken); if (data is null) { return new AddDataApprovalPayload( @@ -84,9 +83,9 @@ CancellationToken cancellationToken var approval = new DataApproval( input.Timestamp, - input.Signature, + input.Signature.Trim(), input.KeyFingerprint, - input.Query, + input.Query.Trim(), input.Variables, input.Message, input.ApproverId, @@ -103,7 +102,7 @@ CancellationToken cancellationToken } catch (Exception exception) { - context.Remove(data); + data.Approvals.Remove(approval); await context.SaveChangesAsync(cancellationToken); return new AddDataApprovalPayload( @@ -117,4 +116,83 @@ CancellationToken cancellationToken } return new AddDataApprovalPayload(approval); } + + public async Task RemoveDataApprovalAsync( + DataApprovalInput input, + ApplicationDbContext context, + UserService userService, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNAUTHORIZED, + $"The current user is not authorized to add data approvals to this database.", + [] + ) + ); + } + + var data = await context.GetDataAsync(input.DataId, cancellationToken); + if (data is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ); + } + + var approval = data.Approvals.Where(a => a.Signature.Trim() == input.Signature.Trim()).FirstOrDefault(); + if (approval is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNKNOWN_APPROVAL, + $"Unknown data approval.", + [nameof(input), nameof(input.Signature).ToLowerFirst()] + ) + ); + } + + data.Approvals.Remove(approval); + await context.SaveChangesAsync(cancellationToken); + + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + data.Approvals.Add(approval); + await context.SaveChangesAsync(cancellationToken); + + return new RemoveDataApprovalPayload( + approval, + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", + [] + ) + ); + } + return new RemoveDataApprovalPayload(approval); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs new file mode 100644 index 00000000..b80e8d11 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs @@ -0,0 +1,13 @@ +using Database.GraphQl.GeometricDataX; +using System.Collections.Generic; + +namespace Database.GraphQl.DataApprovals; + +public sealed class RemoveDataApprovalError( + RemoveDataApprovalErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs new file mode 100644 index 00000000..3545d052 --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.DataApprovals; + +[SuppressMessage("Naming", "CA1707")] +public enum RemoveDataApprovalErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_APPROVAL, + CREATING_RESPONSE_APPROVAL_FAILED +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs new file mode 100644 index 00000000..2722b57b --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs @@ -0,0 +1,9 @@ +using System; + +namespace Database.GraphQl.DataApprovals; + +public sealed record RemoveDataApprovalInput +( + Guid DataId, + string Signature +); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs new file mode 100644 index 00000000..bc562e1a --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs @@ -0,0 +1,29 @@ +using Database.Data; + +namespace Database.GraphQl.DataApprovals; + +public sealed class RemoveDataApprovalPayload + : DataApprovalPayload +{ + public RemoveDataApprovalPayload( + DataApproval dataApproval + ) + : base(dataApproval) + { + } + + public RemoveDataApprovalPayload( + RemoveDataApprovalError error + ) + : base(error) + { + } + + public RemoveDataApprovalPayload( + DataApproval dataApproval, + RemoveDataApprovalError error + ) + : base(dataApproval, error) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdge.cs b/backend/src/GraphQl/DataX/DataEdge.cs index 0b60ee08..4a34bb9a 100644 --- a/backend/src/GraphQl/DataX/DataEdge.cs +++ b/backend/src/GraphQl/DataX/DataEdge.cs @@ -2,13 +2,13 @@ namespace Database.GraphQl.DataX; -public sealed class DataEdge( - string cursor, - IData node +public sealed record DataEdge( + string Cursor, + IData Node ) : DataEdgeBase( - cursor, - node + Cursor, + Node ) { } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdgeBase.cs b/backend/src/GraphQl/DataX/DataEdgeBase.cs index 90ed6048..eb214b4c 100644 --- a/backend/src/GraphQl/DataX/DataEdgeBase.cs +++ b/backend/src/GraphQl/DataX/DataEdgeBase.cs @@ -1,10 +1,6 @@ namespace Database.GraphQl.DataX; -public abstract class DataEdgeBase( - string cursor, - TData node - ) -{ - public string Cursor { get; } = cursor; - public TData Node { get; } = node; -} \ No newline at end of file +public abstract record DataEdgeBase( + string Cursor, + TData Node +); \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index a7d10c5b..f0dd48c1 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -15,12 +15,11 @@ public sealed class DataQueries Guid id, [GraphQLType] string? locale, ApplicationDbContext dbContext, - DataService dataService, AccessRightsService accessRightsService, CancellationToken cancellationToken ) { - var data = await dataService.GetDataAsync(id, dbContext, cancellationToken); + var data = await dbContext.GetDataAsync(id, cancellationToken); if (data is null) { return null; diff --git a/backend/src/GraphQl/Databases/Database.cs b/backend/src/GraphQl/Databases/Database.cs deleted file mode 100644 index ffe4f20b..00000000 --- a/backend/src/GraphQl/Databases/Database.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using Database.ApiRequests.Dto; - -namespace Database.GraphQl.Databases; - -public sealed class Database( - Guid uuid, - string name, - string description, - Uri locator, - DatabaseVerificationState verificationState, - string verificationCode, - bool canCurrentUserUpdateNode, - bool canCurrentUserVerifyNode - ) -{ - public Guid Uuid { get; } = uuid; - public string Name { get; } = name; - public string Description { get; } = description; - public Uri Locator { get; } = locator; - public DatabaseVerificationState VerificationState { get; } = verificationState; - - public string VerificationCode { get; } = verificationCode; - - // public Institution? Operator { get; set; } - public bool CanCurrentUserUpdateNode { get; } = canCurrentUserUpdateNode; - - public bool CanCurrentUserVerifyNode { get; } = canCurrentUserVerifyNode; - - public static Database FromDto(DatabaseDto databaseDto) - { - return new Database( - databaseDto.Uuid, - databaseDto.Name, - databaseDto.Description, - databaseDto.Locator, - databaseDto.VerificationState, - databaseDto.VerificationCode, - databaseDto.CanCurrentUserUpdateNode, - databaseDto.CanCurrentUserVerifyNode); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 7bb25cd0..84fb805d 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -1,11 +1,12 @@ -using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using Database.Services; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.AspNetCore.Http; +using static Database.ApiRequests.UpdateDatabase; namespace Database.GraphQl.Databases; @@ -18,32 +19,35 @@ public async Task UpdateDatabaseAsync( ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, + IResolverContext resolverContext, CancellationToken cancellationToken ) { - var databasePayload = await DatabaseApi.UpdateDatabase( - input, - appSettings, - apiRequestService, - httpClientFactory, - httpContextAccessor, - cancellationToken); - if (databasePayload is null || databasePayload.Database is null) + var databasePayload = await GraphQlRequestHelper.TransformExceptionsAsync( + () => UpdateDatabase.Do( + input, + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken + ), + resolverContext, + UpdateDatabase.GetGraphQlEndpoint(appSettings) + ); + if (databasePayload is null) { - return new UpdateDatabasePayload( - null, - [ - new UpdateDatabaseError( - UpdateDatabaseErrorCode.UNKNOWN, - "Unknown error.", - [] - ) - ] - ); + return new UpdateDatabasePayload( + null, + [ + new UpdateDatabaseError( + UpdateDatabaseErrorCode.UNKNOWN, + "Unknown error.", + [] + ) + ] + ); } - return new UpdateDatabasePayload( - Database.FromDto(databasePayload.Database), - null - ); + return databasePayload; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabasePayload.cs b/backend/src/GraphQl/Databases/DatabasePayload.cs deleted file mode 100644 index 1511ac07..00000000 --- a/backend/src/GraphQl/Databases/DatabasePayload.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.Databases; - -public abstract class DatabasePayload( - Database? database, - IReadOnlyCollection? errors - ) - : Payload - where TDatabaseError : IUserError -{ - public Database? Database { get; } = database; - public IReadOnlyCollection? Errors { get; } = errors; -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 51f40114..8622a53f 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,7 +1,4 @@ -using System; -using System.Linq; using System.Net.Http; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -16,7 +13,7 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Query))] public sealed class DatabaseQueries { - public async Task GetDatabaseAsync( + public async Task GetDatabaseAsync( AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -25,49 +22,27 @@ public async Task GetDatabaseAsync( CancellationToken cancellationToken ) { - try - { - var database = await DatabaseApi.RequestDatabase( + var database = await GraphQlRequestHelper.TransformExceptionsAsync( + () => QueryDatabase.Do( appSettings.DatabaseId, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, - cancellationToken); - if (database is null) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetPath(resolverContext.Path) - .SetMessage($"Failed to fetch database from the metabase GraphQl endpoint.") - .Build() - ); - } - return Database.FromDto(database); - } - catch (HttpRequestException e) + cancellationToken + ), + resolverContext, + QueryDatabase.GetGraphQlEndpoint(appSettings) + ); + if (database is null) { throw new GraphQLException( ErrorBuilder.New() - .SetCode("METABASE_REQUEST_FAILED") .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") - .SetException(e) - .Build() - ); - } - catch (JsonException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") - .SetException(e) + .SetMessage($"Failed to fetch database from the metabase GraphQl endpoint.") .Build() ); } + return database; } } \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs b/backend/src/GraphQl/Databases/UpdateDatabaseError.cs deleted file mode 100644 index 5e82f40f..00000000 --- a/backend/src/GraphQl/Databases/UpdateDatabaseError.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.Databases; - -public sealed class UpdateDatabaseError( - UpdateDatabaseErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs b/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs deleted file mode 100644 index 84756185..00000000 --- a/backend/src/GraphQl/Databases/UpdateDatabaseErrorCode.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.Databases; - -[SuppressMessage("Naming", "CA1707")] -public enum UpdateDatabaseErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNKNOWN_DATABASE -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs b/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs deleted file mode 100644 index 00310ce7..00000000 --- a/backend/src/GraphQl/Databases/UpdateDatabaseInput.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Database.GraphQl.Databases; - -public sealed record UpdateDatabaseInput( - Guid DatabaseId, - string Name, - string Description, - Uri Locator -); \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs b/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs deleted file mode 100644 index 047741ca..00000000 --- a/backend/src/GraphQl/Databases/UpdateDatabasePayload.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.Databases; - -public sealed class UpdateDatabasePayload( - Database? database, - IReadOnlyCollection? errors - ) - : DatabasePayload(database, errors) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index eff3e31f..1d986e02 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,11 +1,9 @@ using System; -using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.ApiRequests.Dto; using Database.Enumerations; using Database.Services; using GraphQL; @@ -93,41 +91,18 @@ CancellationToken cancellationToken ) ); } - DatabaseDto? database; - try - { - database = await DatabaseApi.RequestDatabase( + var database = await GraphQlRequestHelper.TransformExceptionsAsync( + () => QueryDatabase.Do( dataReference.DatabaseId, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, - cancellationToken); - } - catch (HttpRequestException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("METABASE_REQUEST_FAILED") - .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request the metabase GraphQl endpoint.") - .SetException(e) - .Build() - ); - } - catch (JsonException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to the metabase GraphQl endpoint. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") - .SetException(e) - .Build() - ); - } + cancellationToken + ), + resolverContext, + QueryDatabase.GetGraphQlEndpoint(appSettings) + ); if (database is null) { return new CalculateMethodPayload( @@ -140,39 +115,16 @@ CancellationToken cancellationToken } var query = ConstructQuery(dataReference.DataKind); GraphQLResponse? response; - try - { - response = await DataApi.QueryDataFromDatabase( + response = await GraphQlRequestHelper.TransformExceptionsAsync( + () => QueryData.Do( database.Locator, dataReference.DataId, query, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken - ); - } - catch (HttpRequestException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("DATABASE_REQUEST_FAILED") - .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request the database GraphQl endpoint {database.Locator}.") - .SetException(e) - .Build() - ); - } - catch (JsonException e) - { - throw new GraphQLException( - ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to the database GraphQl endpoint {database.Locator}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") - .SetException(e) - .Build() - ); - } + ), + resolverContext, + database.Locator + ); if (response.Data?.Data is null) { if (response.Errors?.Length >= 1) diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index f6d29e76..2c0b5a71 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,19 +1,17 @@ -using System; using System.Linq; using System.Net.Http; using System.Security.Claims; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using Database.Data; using Database.Services; -using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; +using static Database.ApiRequests.GetUserInfo; namespace Database.GraphQl.Users; @@ -30,7 +28,6 @@ CancellationToken cancellationToken { return null; } - return await context.Users.AsNoTracking() .SingleOrDefaultAsync( @@ -39,7 +36,7 @@ await context.Users.AsNoTracking() ); } - public async Task GetCurrentUserInfoAsync( + public Task GetCurrentUserInfoAsync( AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, @@ -48,41 +45,16 @@ await context.Users.AsNoTracking() CancellationToken cancellationToken ) { - try - { - return UserInfo.FromDto(await UserApi.RequestUserInfo( + return GraphQlRequestHelper.TransformExceptionsAsync( + () => GetUserInfo.Do( appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken - )); - } - catch (HttpRequestException e) - { - resolverContext.ReportError( - ErrorBuilder.New() - .SetCode("METABASE_REQUEST_FAILED") - .SetPath(resolverContext.Path) - .SetMessage($"Failed with status code {e.StatusCode} to request {appSettings.MetabaseHost}.") - .SetException(e) - .Build() - ); - return null; - } - catch (JsonException e) - { - resolverContext.ReportError( - ErrorBuilder.New() - .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to {appSettings.MetabaseHost}. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is {e.Path}.") - .SetException(e) - .Build() - ); - return null; - } + ), + resolverContext, + GetUserInfo.GetEndpoint(appSettings) + ); } } \ No newline at end of file diff --git a/backend/src/Models/Database.cs b/backend/src/Models/Database.cs new file mode 100644 index 00000000..451fe61b --- /dev/null +++ b/backend/src/Models/Database.cs @@ -0,0 +1,15 @@ +using System; + +namespace Database.Models; + +public sealed record Database( + Guid Uuid, + string Name, + string Description, + Uri Locator, + DatabaseVerificationState VerificationState, + string VerificationCode, + DatabaseOperatorEdge Operator, + bool CanCurrentUserUpdateNode, + bool CanCurrentUserVerifyNode +); \ No newline at end of file diff --git a/backend/src/Models/DatabaseOperatorEdge.cs b/backend/src/Models/DatabaseOperatorEdge.cs new file mode 100644 index 00000000..377a3d43 --- /dev/null +++ b/backend/src/Models/DatabaseOperatorEdge.cs @@ -0,0 +1,5 @@ +namespace Database.Models; + +public sealed record DatabaseOperatorEdge( + Institution Node +); \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseVerificationState.cs b/backend/src/Models/DatabaseVerificationState.cs similarity index 64% rename from backend/src/GraphQl/Databases/DatabaseVerificationState.cs rename to backend/src/Models/DatabaseVerificationState.cs index 7e621902..ec151f92 100644 --- a/backend/src/GraphQl/Databases/DatabaseVerificationState.cs +++ b/backend/src/Models/DatabaseVerificationState.cs @@ -1,4 +1,4 @@ -namespace Database.GraphQl.Databases; +namespace Database.Models; public enum DatabaseVerificationState { diff --git a/backend/src/Models/Institution.cs b/backend/src/Models/Institution.cs new file mode 100644 index 00000000..6906dd24 --- /dev/null +++ b/backend/src/Models/Institution.cs @@ -0,0 +1,7 @@ +using System; + +namespace Database.Models; + +public sealed record Institution( + Guid Uuid +); \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 6cded6bd..f3b09a76 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -3,11 +3,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using static Database.ApiRequests.QueryCurrentUser; namespace Database.Services; @@ -62,7 +62,7 @@ CancellationToken cancellationToken private IEnumerable ProcessData( IQueryable data, - CurrentUserDto? currentUser, + CurrentUser? currentUser, string? openIdConnectClientId, IReadOnlyList? representedInstitutionIds, IReadOnlyList? institutionAccessRights, diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index ae13d47f..12786a5b 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Net.Http.Json; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -30,6 +29,21 @@ public sealed class ApiRequestService /// public const string DatabaseHttpClient = "Database"; + private const string GraphQlPathSegment = "/graphql/"; + + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of + // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + public static Uri MetabaseGraphQlEndpoint(AppSettings appSettings) => + new(new Uri(appSettings.MetabaseHost, UriKind.Absolute), GraphQlPathSegment); + + public static Uri DatabaseGraphQlEndpoint(AppSettings appSettings) => + new(new Uri(appSettings.Host, UriKind.Absolute), GraphQlPathSegment); + + private static Uri GetGraphQlEndpoint(AppSettings appSettings) + { + return s_useMetabase ? MetabaseGraphQlEndpoint(appSettings) : DatabaseGraphQlEndpoint(appSettings); + } + public static JsonDocumentOptions JsonDocumentOptions => new() { AllowTrailingCommas = true, @@ -88,9 +102,7 @@ CancellationToken cancellationToken { return Query( HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of - // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), + GetGraphQlEndpoint(appSettings), httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), MakeGraphQlJsonHttpContent(request), httpClientFactory, @@ -120,9 +132,7 @@ CancellationToken cancellationToken { return Query>( HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of - // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - new Uri(new Uri(s_useMetabase ? appSettings.MetabaseHost : appSettings.Host, UriKind.Absolute), "/graphql/"), + GetGraphQlEndpoint(appSettings), MakeGraphQlJsonHttpContent(request), JsonSerializerSettings.GraphQL, httpClientFactory, diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index b350ebab..3be29760 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,6 +1,6 @@ using System; -using Database.ApiRequests.Dto; using Microsoft.Extensions.Caching.Memory; +using static Database.ApiRequests.QueryCurrentUser; namespace Database.Services; @@ -9,14 +9,14 @@ public sealed class CacheService( IMemoryCache accessCountCache, IMemoryCache timePeriodCountCache) { - public CurrentUserDto? SetUser(string token, CurrentUserDto? cacheUser) + public CurrentUser? SetUser(string token, CurrentUser? cacheUser) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); return currentUserCache.Set(token, cacheUser, cacheEntryOptions); } - public bool TryGetUser(string token, out CurrentUserDto? cacheUser) + public bool TryGetUser(string token, out CurrentUser? cacheUser) { return currentUserCache.TryGetValue(token, out cacheUser); } diff --git a/backend/src/Services/DataService.cs b/backend/src/Services/DataService.cs deleted file mode 100644 index 88775f30..00000000 --- a/backend/src/Services/DataService.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.Services; -using Microsoft.EntityFrameworkCore; - -namespace Database.Services; - -/// -/// Service to get data from -/// -public sealed class DataService -{ - /// - /// Get dataset with passed id from all datasets - /// - /// Id of dataset to get. - /// - /// - /// Dataset with passed id or null. - public async Task GetDataAsync(Guid id, ApplicationDbContext context, CancellationToken cancellationToken) - { - return await context.CalorimetricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.HygrothermalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.OpticalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.GeometricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await context.PhotovoltaicData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; - } -} \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 2e67548b..bb6d6261 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -3,8 +3,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; -using Database.ApiRequests.Dto; using Database.Data; using Database.Logging; using Microsoft.AspNetCore.Http; @@ -21,12 +19,6 @@ public sealed class ResponseApprovalService( ILogger logger ) { - private sealed record CalorimetricDataResponse(CalorimetricDataDto CalorimetricData); - private sealed record GeometricDataResponse(GeometricDataDto GeometricData); - private sealed record HygrothermalDataResponse(HygrothermalDataDto HygrothermalData); - private sealed record PhotovoltaicDataResponse(PhotovoltaicDataDto PhotovoltaicData); - private sealed record OpticalDataResponse(OpticalDataDto OpticalData); - /// /// Create response approval by calling graphql Api and signing responce. /// @@ -58,11 +50,11 @@ await signingService.ExtractFingerprint(), { return dataObject switch { - GeometricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - CalorimetricData data => await DataApi.QueryAllMetaData(data.Id, DataApi.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - HygrothermalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - PhotovoltaicData data => await DataApi.QueryAllMetaData(data.Id, DataApi.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - OpticalData data => await DataApi.QueryAllMetaData(data.Id, DataApi.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + CalorimetricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), _ => throw new NotImplementedException($"Unsupported data type {typeof(T)}"), }; } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 1c6f9bef..5a936e20 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -2,12 +2,12 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.ApiRequests.Dto; using Database.Extensions; using Database.Logging; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; +using static Database.ApiRequests.QueryCurrentUser; using static OpenIddict.Abstractions.OpenIddictConstants; namespace Database.Services; @@ -38,38 +38,39 @@ public sealed class UserService( } /// - /// Get current user from Metabase. + /// Get current user from Metabase (or the local cache). /// /// /// - public async Task GetCurrentUser(CancellationToken cancellationToken) + public async Task GetCurrentUser(CancellationToken cancellationToken) { // Extract token var token = await httpContextAccessor.ExtractBearerToken(); logger.ExtractedToken(token); if (token is null) { - return await GetCurrentUserFromMetabase(cancellationToken); + return await QueryCurrentUser.Do( + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); } // Check if there is already a user for token if (!cacheService.TryGetUser(token, out var cacheUser)) { // Get user from Metabase - cacheUser = await GetCurrentUserFromMetabase(cancellationToken); + cacheUser = await QueryCurrentUser.Do( + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); // Store user in cache cacheService.SetUser(token, cacheUser); } return cacheUser; } - - private Task GetCurrentUserFromMetabase(CancellationToken cancellationToken) - { - return UserApi.RequestCurrentUser( - appSettings, - apiRequestService, - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index a14a62a5..7a271863 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -238,7 +238,6 @@ public static void ConfigureCustomServices(IServiceCollection services) { services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); From 3942006b9dbcfec663470229c476f800b067f180 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 9 Oct 2025 15:09:28 +0200 Subject: [PATCH 458/878] Specify fingerprint of GnuPG secret key for signing response approvals explicitly in `.env` file, give the correponding variables more meaningful names, make app settings readonly, and improve corresponding readme --- .env.production.sample | 5 ++- .env.sample | 5 ++- .env.staging.sample | 5 ++- Makefile | 24 +++++----- README.md | 26 ++++++----- backend/src/AppSettings.cs | 45 ++++++++++--------- .../src/Services/ResponseApprovalService.cs | 4 +- backend/src/Services/SigningService.cs | 28 +++++------- backend/src/Startup.cs | 10 +++-- docker-compose.production.yml | 9 ++-- docker-compose.yml | 7 +-- 11 files changed, 90 insertions(+), 78 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 762958d5..69e65193 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -11,8 +11,9 @@ OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= -GNUPG_PRIVATEKEY_FILE_NAME=private.gpg -GNUPG_PRIVATEKEY_PASSPHRASE= +GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index 3deff956..eb4d39e7 100644 --- a/.env.sample +++ b/.env.sample @@ -18,8 +18,9 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_PRIVATEKEY_FILE_NAME=private.gpg -GNUPG_PRIVATEKEY_PASSPHRASE=passphrase +GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password diff --git a/.env.staging.sample b/.env.staging.sample index bc1e9169..cedf6e0b 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -11,8 +11,9 @@ OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= -GNUPG_PRIVATEKEY_FILE_NAME=private.gpg -GNUPG_PRIVATEKEY_PASSPHRASE= +GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/Makefile b/Makefile index 850ec7c7..c41e314c 100644 --- a/Makefile +++ b/Makefile @@ -317,16 +317,16 @@ prepare-release : ## Prepare release .PHONY : prepare-release gpg : COMMENT = -gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}`, for example, `make NAME="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` - gpg \ - --quick-generate-key \ - --pinentry-mode loopback \ - --batch \ - --passphrase ${GNUPG_PRIVATEKEY_PASSPHRASE} \ - "${NAME} (${COMMENT}) <${EMAIL}>" \ - ed25519 \ - sign \ - never; \ +gpg : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make NAME="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` + gpg \ + --quick-generate-key \ + --pinentry-mode loopback \ + --batch \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + "${NAME} (${COMMENT}) <${EMAIL}>" \ + ed25519 \ + sign \ + never; \ fingerprint=$$(gpg \ --list-secret-keys \ --with-colons \ @@ -336,13 +336,13 @@ gpg : ## Generate GnuPG key with the passphrase `${GNUPG_PRIVATEKEY_PASSPHRASE}` --before=3 \ "${NAME} (${COMMENT}) <${EMAIL}>" \ | awk -F: '$$1=="fpr" {printf $$10; exit}'); \ - echo $$fingerprint; \ + echo Fingerprint: $$fingerprint; \ mkdir --parents \ ./backend/src/gpg-keys; \ gpg \ --armor \ --export-secret-keys $$fingerprint \ - > ./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME} + > ./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} # --------------------- # # Generate Certificates # diff --git a/README.md b/README.md index 45a4545f..ab611b6e 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,17 @@ If you have a question for which you don't find the answer in this repository, p generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `make jwt-certificates`. -1. Generate and export a GnuPG key with the passphrase - `${GNUPG_PRIVATEKEY_PASSPHRASE}` to the file - `./backend/src/gpg-keys/${GNUPG_PRIVATEKEY_FILE_NAME}` - by running `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with - your information filled in. For example: `make NAME=Anna" "Smith - COMMENT=first" "test EMAIL=anna.smith@fraunhofer.de gpg`. The variable - `${GNUPG_PRIVATEKEY_PASSPHRASE}` is set in `.env.sample`. +1. Set the values of the variables `GNUPG_SECRET_SIGNING_KEY_FILE_NAME` and + `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` in the `./.env` file. Generate and + export a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` to the file + `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME}` by running + `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with your + information filled in. For example + `make NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de gpg`. + Copy the key's fingerprint which is output by the command and set it as the + value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` + file. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see @@ -262,9 +266,11 @@ and the pages following it. this product-data database as a client of the metabase acting as identity provider (the client secret is given when registering an OpenId Connect client at the metabase); - - `GNUPG_PRIVATEKEY_FILE_NAME` and `GNUPG_PRIVATEKEY_PASSPHRASE` are the - file name of an exported GnuPG private key and its passphrase that - lives in the directory `./backend/src/gpg-keys/`; + - `GNUPG_SECRET_SIGNING_KEY_FILE_NAME`, + `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE`, and + `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` are the file name of an + exported GnuPG secret key for signing, its passphrase, and its + fingerprint that lives in the directory `./backend/src/gpg-keys/`; - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to encrypt and sign JSON web tokens (JWT) used by OpenId Connect; diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index ce1907d9..813aa957 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -6,60 +6,65 @@ namespace Database; public sealed class AppSettings { - public string Host { get; set; } + public string Host { get; private set; } = ""; - public string MetabaseHost { get; set; } + public string MetabaseHost { get; private set; } = ""; - public Guid DatabaseId { get; set; } - public Guid OperatorId { get; set; } + public Guid DatabaseId { get; private set; } + public Guid OperatorId { get; private set; } - public string VerificationCode { get; set; } + public string OpenIdConnectClientSecret { get; private set; } = ""; - public string GnupgPrivateKeyFilename { get; set; } = ""; - public string GnupgPrivateKeyPassphrase { get; set; } = ""; + public string VerificationCode { get; private set; } + = ""; - public LoggingSettings Logging { get; set; } = new(); + public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; private set; } = new(); + public LoggingSettings Logging { get; private set; } = new(); - public JsonWebTokenSettings JsonWebToken { get; set; } = new(); + public JsonWebTokenSettings JsonWebToken { get; private set; } = new(); - public EmailSettings Email { get; set; } = new(); + public EmailSettings Email { get; private set; } = new(); - public string OpenIdConnectClientSecret { get; set; } - = ""; + public DatabaseSettings Database { get; private set; } = new(); - public DatabaseSettings Database { get; set; } = new(); + public sealed class GnupgSecretSigningKeySettings + { + public string FileName { get; private set; } = ""; + public string Passphrase { get; private set; } = ""; + public string Fingerprint { get; private set; } = ""; + } public sealed class LoggingSettings { - public bool EnableSensitiveDataLogging { get; set; } + public bool EnableSensitiveDataLogging { get; private set; } } public sealed class JsonWebTokenSettings { - public string EncryptionCertificatePassword { get; set; } + public string EncryptionCertificatePassword { get; private set; } = ""; - public string SigningCertificatePassword { get; set; } + public string SigningCertificatePassword { get; private set; } = ""; } public sealed class EmailSettings { - public string SmtpHost { get; set; } + public string SmtpHost { get; private set; } = ""; - public int SmtpPort { get; set; } + public int SmtpPort { get; private set; } } public sealed class DatabaseSettings { - public string ConnectionString { get; set; } + public string ConnectionString { get; private set; } = ""; - public string SchemaName { get; set; } + public string SchemaName { get; private set; } = ""; } } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index bb6d6261..5db586f2 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -33,11 +33,11 @@ public async Task CreateResponseApproval(T dataObject, Canc logger.QueryAllMetaData(dataObject.GetType(), dataObject.Id); var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); logger.QueryAndVariablesAndResponce(query, variables, response); - var signature = await signingService.SignData(response); + var (signature, fingerprint) = await signingService.SignData(response); return new ResponseApproval( DateTime.UtcNow, signature, - await signingService.ExtractFingerprint(), + fingerprint, query, variables, response, diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 9b3d9de9..b828ac2d 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Database.Logging; using Microsoft.Extensions.Logging; -using Quartz.Util; namespace Database.Services; @@ -14,33 +13,26 @@ public sealed class SigningService( public async Task Initialize() { var (success, output, diagnostics) = await ExecuteCommand( - $"gpg --batch --passphrase '{appSettings.GnupgPrivateKeyPassphrase}' --import './gpg-keys/{appSettings.GnupgPrivateKeyFilename}'" + $"gpg --batch --passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' --import './gpg-keys/{appSettings.GnupgSecretSigningKey.FileName}'" ); + if (!success) + { + throw new InvalidOperationException($"Failed to import GnuPG secret key for signing with passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' and file name '{appSettings.GnupgSecretSigningKey.FileName}'. The command gave the standard output '{output}' and the diagnostic information '{diagnostics}'."); + } } - public async Task SignData(string data) + public async Task<(string Signature, string Fingerprint)> SignData(string data) { var (success, signature, diagnostics) = await ExecuteCommand( - $"gpg --pinentry-mode loopback --batch --passphrase {appSettings.GnupgPrivateKeyPassphrase} --detach-sig --armor --local-user {await ExtractFingerprint()}", + $"gpg --pinentry-mode loopback --batch --passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' --detach-sig --armor --local-user '{appSettings.GnupgSecretSigningKey.Fingerprint}'", data ); if (!success) { - throw new InvalidOperationException($"Failed to create signature with standard output {signature} and diagnostic information {diagnostics}"); + throw new InvalidOperationException($"Failed to create signature with standard output '{signature}' and diagnostic information '{diagnostics}'"); } logger.CreatedSignature(signature); - return signature; - } - - public async Task ExtractFingerprint() - { - var (success, fingerprint, diagnostics) = await ExecuteCommand("gpg --list-secret-keys --with-colons --keyid-format=long | awk -F: '$1==\"fpr\" {printf $10; exit}'"); - if (!success || fingerprint.IsNullOrWhiteSpace()) - { - throw new InvalidOperationException($"Failed to extract fingerprint with standard output {fingerprint} and diagnostic information {diagnostics}"); - } - logger.ExtractedFingerprint(fingerprint); - return fingerprint; + return (signature, appSettings.GnupgSecretSigningKey.Fingerprint); } private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommand( @@ -62,7 +54,7 @@ public async Task ExtractFingerprint() RedirectStandardOutput = true, RedirectStandardError = true, } - ) ?? throw new InvalidOperationException($"The process for command '${command}' with the input '${input}' failed to start."); + ) ?? throw new InvalidOperationException($"The process for command '{command}' with the input '{input}' failed to start."); if (input is not null) { await process.StandardInput.WriteLineAsync(input); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7a271863..d2d6f4c9 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -35,9 +35,13 @@ IConfiguration configuration { private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; private const string AntiforgeryHeaderName = "X-XSRF-TOKEN"; - private readonly AppSettings _appSettings = configuration.Get() ?? - throw new InvalidOperationException( - "Failed to get application settings from configuration."); + private readonly AppSettings _appSettings = + configuration.Get(_ => + { + _.BindNonPublicProperties = true; + _.ErrorOnUnknownConfiguration = false; + }) + ?? throw new InvalidOperationException("Failed to get application settings from configuration."); private readonly IWebHostEnvironment _environment = environment; diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 6558e80d..ff56a0d0 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -71,14 +71,15 @@ services: - XBASE_MetabaseHost=https://${METABASE_HOST} - XBASE_DatabaseId=${DATABASE_ID} - XBASE_OperatorId=${OPERATOR_ID} + - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_VerificationCode=${VERIFICATION_CODE} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} - - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} - - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} + - XBASE_GnupgSecretSigningKey__Filename=${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} + - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} + - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - GPG_TTY=/dev/pts/0 diff --git a/docker-compose.yml b/docker-compose.yml index 770ae65f..241b60ca 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -112,13 +112,14 @@ services: - XBASE_DatabaseId=${DATABASE_ID} - XBASE_OperatorId=${OPERATOR_ID} - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_VerificationCode=${VERIFICATION_CODE} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_GnupgPrivateKeyFilename=${GNUPG_PRIVATEKEY_FILE_NAME} - - XBASE_GnupgPrivateKeyPassphrase=${GNUPG_PRIVATEKEY_PASSPHRASE} + - XBASE_GnupgSecretSigningKey__Filename=${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} + - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} + - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose From 08a8fbf16a6ea2216c51a687e227f273d37c2a17 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 9 Oct 2025 15:18:36 +0200 Subject: [PATCH 459/878] Put data-transfer objects (or query-response models) alongside the queries themselves --- backend/src/ApiRequests/QueryDatabase.cs | 30 +++++++++++++++++-- backend/src/ApiRequests/UpdateDatabase.cs | 2 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 2 +- backend/src/Models/Database.cs | 15 ---------- backend/src/Models/DatabaseOperatorEdge.cs | 5 ---- .../src/Models/DatabaseVerificationState.cs | 7 ----- backend/src/Models/Institution.cs | 7 ----- 7 files changed, 30 insertions(+), 38 deletions(-) delete mode 100644 backend/src/Models/Database.cs delete mode 100644 backend/src/Models/DatabaseOperatorEdge.cs delete mode 100644 backend/src/Models/DatabaseVerificationState.cs delete mode 100644 backend/src/Models/Institution.cs diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index c4d1d75b..b94a8fc1 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -13,14 +13,40 @@ public sealed class QueryDatabase public static Uri GetGraphQlEndpoint(AppSettings appSettings) => ApiRequestService.MetabaseGraphQlEndpoint(appSettings); - private sealed record DatabaseData(Models.Database Database); + public sealed record Database( + Guid Uuid, + string Name, + string Description, + Uri Locator, + DatabaseVerificationState VerificationState, + string VerificationCode, + DatabaseOperatorEdge Operator, + bool CanCurrentUserUpdateNode, + bool CanCurrentUserVerifyNode + ); + + public enum DatabaseVerificationState + { + PENDING, + VERIFIED + } + + public sealed record DatabaseOperatorEdge( + Institution Node + ); + + public sealed record Institution( + Guid Uuid + ); + + private sealed record DatabaseData(Database Database); private static readonly string[] s_queryDatabaseFileNames = [ "Database.graphql" ]; - public static async Task Do( + public static async Task Do( Guid databaseId, AppSettings appSettings, ApiRequestService apiRequestService, diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 03c1075f..12b5ec71 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -23,7 +23,7 @@ Uri Locator ); public sealed record UpdateDatabasePayload( - Models.Database? Database, + QueryDatabase.Database? Database, IReadOnlyList? Errors ); diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 8622a53f..7e3d6447 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -13,7 +13,7 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Query))] public sealed class DatabaseQueries { - public async Task GetDatabaseAsync( + public async Task GetDatabaseAsync( AppSettings appSettings, ApiRequestService apiRequestService, IHttpClientFactory httpClientFactory, diff --git a/backend/src/Models/Database.cs b/backend/src/Models/Database.cs deleted file mode 100644 index 451fe61b..00000000 --- a/backend/src/Models/Database.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Database.Models; - -public sealed record Database( - Guid Uuid, - string Name, - string Description, - Uri Locator, - DatabaseVerificationState VerificationState, - string VerificationCode, - DatabaseOperatorEdge Operator, - bool CanCurrentUserUpdateNode, - bool CanCurrentUserVerifyNode -); \ No newline at end of file diff --git a/backend/src/Models/DatabaseOperatorEdge.cs b/backend/src/Models/DatabaseOperatorEdge.cs deleted file mode 100644 index 377a3d43..00000000 --- a/backend/src/Models/DatabaseOperatorEdge.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Database.Models; - -public sealed record DatabaseOperatorEdge( - Institution Node -); \ No newline at end of file diff --git a/backend/src/Models/DatabaseVerificationState.cs b/backend/src/Models/DatabaseVerificationState.cs deleted file mode 100644 index ec151f92..00000000 --- a/backend/src/Models/DatabaseVerificationState.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Database.Models; - -public enum DatabaseVerificationState -{ - PENDING, - VERIFIED -} \ No newline at end of file diff --git a/backend/src/Models/Institution.cs b/backend/src/Models/Institution.cs deleted file mode 100644 index 6906dd24..00000000 --- a/backend/src/Models/Institution.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System; - -namespace Database.Models; - -public sealed record Institution( - Guid Uuid -); \ No newline at end of file From a0ce43d5d0437b0f4df94dae27eff353fa9f6b53 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 9 Oct 2025 16:07:07 +0200 Subject: [PATCH 460/878] Configure the default formatter for C# files properly --- backend/.vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index b3a1df7c..a858e309 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -1,4 +1,6 @@ { "editor.formatOnSave": true, - "editor.defaultFormatter": "ms-dotnettools.csdevkit" + "[csharp]": { + "editor.defaultFormatter": "ms-dotnettools.csharp" + } } \ No newline at end of file From 8a6c28a9c454ebd65a379d60277f16030bcb7124 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 9 Oct 2025 16:09:26 +0200 Subject: [PATCH 461/878] Add GraphQL query to check whether a GnuPG fingerprint was valid for a given institution at a given time --- .../src/ApiRequests/CheckGnuPgFingerprint.cs | 5 -- .../ApiRequests/IsGnuPgFingerprintValid.cs | 52 +++++++++++++++++++ ...raphql => IsGnuPgFingerprintValid.graphql} | 4 +- backend/src/ApiRequests/QueryCurrentUser.cs | 52 +++++++++---------- backend/src/ApiRequests/QueryDatabase.cs | 36 ++++++------- backend/src/ApiRequests/UpdateDatabase.cs | 11 ++-- backend/src/Services/ApiRequestService.cs | 7 +++ 7 files changed, 106 insertions(+), 61 deletions(-) delete mode 100644 backend/src/ApiRequests/CheckGnuPgFingerprint.cs create mode 100644 backend/src/ApiRequests/IsGnuPgFingerprintValid.cs rename backend/src/ApiRequests/Queries/{CheckGnuPgFingerprint.graphql => IsGnuPgFingerprintValid.graphql} (94%) diff --git a/backend/src/ApiRequests/CheckGnuPgFingerprint.cs b/backend/src/ApiRequests/CheckGnuPgFingerprint.cs deleted file mode 100644 index 2c8eb98c..00000000 --- a/backend/src/ApiRequests/CheckGnuPgFingerprint.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Database.ApiRequests; - -public sealed record ValidFingerprints( - uint TotalCount -); \ No newline at end of file diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs new file mode 100644 index 00000000..2bf84e2d --- /dev/null +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -0,0 +1,52 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using GraphQL; +using Microsoft.AspNetCore.Http; +using Database.Services; + +namespace Database.ApiRequests; + +public sealed class IsGnuPgFingerprintValid +{ + private const string QueryFileName = "IsGnuPgFingerprintValid.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + + public sealed record ValidFingerprints( + uint TotalCount + ); + + private sealed record Data(ValidFingerprints? ValidFingerprints); + + public static async Task Do( + string fingerprint, + Guid institutionId, + DateTime createdAt, + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor, + CancellationToken cancellationToken + ) + { + return (await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(QueryFileName), + new + { + fingerprint, + institutionId, + createdAt + }, + "IsGnuPgFingerprintValid" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + )).Data.ValidFingerprints?.TotalCount == 1; + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql similarity index 94% rename from backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql rename to backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql index 60a93720..51edad8c 100644 --- a/backend/src/ApiRequests/Queries/CheckGnuPgFingerprint.graphql +++ b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql @@ -1,6 +1,6 @@ -query CheckGnuPgFingerprint( - $institutionId: Uuid! +query IsGnuPgFingerprintValid( $fingerprint: String! + $institutionId: Uuid! $createdAt: DateTime! ) { validFingerprints: institutions( diff --git a/backend/src/ApiRequests/QueryCurrentUser.cs b/backend/src/ApiRequests/QueryCurrentUser.cs index 311bd62f..77b4bed6 100644 --- a/backend/src/ApiRequests/QueryCurrentUser.cs +++ b/backend/src/ApiRequests/QueryCurrentUser.cs @@ -14,6 +14,8 @@ namespace Database.ApiRequests; /// public sealed class QueryCurrentUser { + private const string CurrentUserFileName = "CurrentUser.graphql"; + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => ApiRequestService.MetabaseGraphQlEndpoint(appSettings); @@ -23,49 +25,44 @@ public sealed record CurrentUser( UserRepresentedInstitutionConnection RepresentedInstitutions, UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions ); - + public sealed record UserRepresentedInstitutionConnection( IReadOnlyList Edges, uint TotalCount ); - + public enum InstitutionRepresentativeRole { OWNER, ASSISTANT } - + public sealed record UserRepresentedInstitutionEdge( UserRepresentedInstitutionNode Node, InstitutionRepresentativeRole Role ); - + public sealed record UserRepresentedInstitutionNode( Guid Uuid, string Name, InstitutionManagedInstitutionConnection ManagedInstitutions ); - + public sealed record InstitutionManagedInstitutionConnection( IReadOnlyList Edges, uint TotalCount ); - + public sealed record InstitutionManagedInstitutionEdge( InstitutionManagedInstitutionNode Node ); - + public sealed record InstitutionManagedInstitutionNode( Guid Uuid, string Name ); - private sealed record CurrentUserData(CurrentUser CurrentUser); - - private static readonly string[] s_currentUserFileNames = - [ - "CurrentUser.graphql" - ]; + private sealed record CurrentUserData(CurrentUser? CurrentUser); /// /// Request current user from Metabase. @@ -78,19 +75,20 @@ private sealed record CurrentUserData(CurrentUser CurrentUser); CancellationToken cancellationToken) { return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_currentUserFileNames), - new { - databaseId = appSettings.DatabaseId - }, - "CurrentUser" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - )) - ?.Data - ?.CurrentUser - ?? null; + appSettings, + new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(CurrentUserFileName), + new + { + databaseId = appSettings.DatabaseId + }, + "CurrentUser" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + )) + .Data + .CurrentUser; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index b94a8fc1..b33873ea 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -10,6 +10,8 @@ namespace Database.ApiRequests; public sealed class QueryDatabase { + private const string QueryFileName = "Database.graphql"; + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => ApiRequestService.MetabaseGraphQlEndpoint(appSettings); @@ -39,12 +41,7 @@ public sealed record Institution( Guid Uuid ); - private sealed record DatabaseData(Database Database); - - private static readonly string[] s_queryDatabaseFileNames = - [ - "Database.graphql" - ]; + private sealed record DatabaseData(Database? Database); public static async Task Do( Guid databaseId, @@ -54,19 +51,18 @@ private sealed record DatabaseData(Database Database); IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - var response = await apiRequestService.Metabase().QueryGraphQl( - appSettings, - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(s_queryDatabaseFileNames), - new - { - id = databaseId - }, - "Database" - ), - httpClientFactory, - httpContextAccessor, - cancellationToken - ); - return response.Data?.Database; + return (await apiRequestService.Metabase().QueryGraphQl( + appSettings, + new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(QueryFileName), + new + { + id = databaseId + }, + "Database" + ), + httpClientFactory, + httpContextAccessor, + cancellationToken + )).Data.Database; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 12b5ec71..f854b02b 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -12,6 +12,8 @@ namespace Database.ApiRequests; public sealed class UpdateDatabase { + private const string UpdateDatabaseFileName = "UpdateDatabase.graphql"; + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => ApiRequestService.MetabaseGraphQlEndpoint(appSettings); @@ -41,11 +43,6 @@ public sealed record UpdateDatabaseError( IReadOnlyList Path ); - private static readonly string[] s_updateDatabaseFileNames = - [ - "UpdateDatabase.graphql" - ]; - public static async Task Do( UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, @@ -59,7 +56,7 @@ CancellationToken cancellationToken appSettings, new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery( - s_updateDatabaseFileNames + UpdateDatabaseFileName ), new { @@ -70,6 +67,6 @@ await apiRequestService.ConstructGraphQlQuery( httpClientFactory, httpContextAccessor, cancellationToken - ))?.Data; + )).Data; } } \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 12786a5b..d80197ee 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -73,6 +73,13 @@ public ApiRequestService Database() return this; } + public Task ConstructGraphQlQuery( + string fileName + ) + { + return ConstructGraphQlQuery([fileName]); + } + /// /// Construct query from passed files. /// From 778e9b363c72f7e05cf675975063e1f890050ccd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 9 Oct 2025 16:09:51 +0200 Subject: [PATCH 462/878] Check whether a GnuPG fingerprint was valid for a given institution at a given time when adding a data approval --- .../DataApprovals/AddDataApprovalErrorCode.cs | 3 +- .../DataApprovals/DataApprovalMutations.cs | 16 +++++++++ backend/src/Services/DataApprovalService.cs | 35 +++++++++++++++++++ backend/src/Startup.cs | 1 + 4 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 backend/src/Services/DataApprovalService.cs diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index 9af698dd..c7cf17ac 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -11,5 +11,6 @@ public enum AddDataApprovalErrorCode UNKNOWN_DATA, CREATING_RESPONSE_APPROVAL_FAILED, AMBIGUOUS_STATEMENT, - MISSING_STATEMENT + MISSING_STATEMENT, + INVALID_KEY_FINGERPRINT } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 58e9fa0d..dfc65392 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -19,6 +19,7 @@ public async Task AddDataApprovalAsync( DataApprovalInput input, ApplicationDbContext context, UserService userService, + DataApprovalService dataApprovalService, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -44,6 +45,21 @@ CancellationToken cancellationToken ) ); } + if (!await dataApprovalService.IsGnuPgFingerprintValid( + input.KeyFingerprint, + input.ApproverId, + input.Timestamp, + cancellationToken + )) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.INVALID_KEY_FINGERPRINT, + $"The key fingerprint does not exist for the institution '{input.ApproverId}' or was not allowed before or was forbidden before the timestamp {input.Timestamp}.", + [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] + ) + ); + } var data = await context.GetDataAsync(input.DataId, cancellationToken); if (data is null) diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs new file mode 100644 index 00000000..9fb5f20d --- /dev/null +++ b/backend/src/Services/DataApprovalService.cs @@ -0,0 +1,35 @@ +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Database.Data; + +namespace Database.Services; + +public sealed class DataApprovalService( + AppSettings appSettings, + ApiRequestService apiRequestService, + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor +) +{ + public Task IsGnuPgFingerprintValid( + string fingerprint, + Guid institutionId, + DateTime createdAt, + CancellationToken cancellationToken + ) + { + return ApiRequests.IsGnuPgFingerprintValid.Do( + fingerprint, + institutionId, + createdAt, + appSettings, + apiRequestService, + httpClientFactory, + httpContextAccessor, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index d2d6f4c9..b9e0aa02 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -242,6 +242,7 @@ public static void ConfigureCustomServices(IServiceCollection services) { services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); From e837ce677a41076bede99bc1ebc8d7bbe32173fd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 11:19:21 +0200 Subject: [PATCH 463/878] Use correct separator: tab (not spaces) --- Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index c41e314c..14ed389c 100644 --- a/Makefile +++ b/Makefile @@ -318,15 +318,15 @@ prepare-release : ## Prepare release gpg : COMMENT = gpg : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make NAME="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` - gpg \ - --quick-generate-key \ - --pinentry-mode loopback \ - --batch \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - "${NAME} (${COMMENT}) <${EMAIL}>" \ - ed25519 \ - sign \ - never; \ + gpg \ + --quick-generate-key \ + --pinentry-mode loopback \ + --batch \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + "${NAME} (${COMMENT}) <${EMAIL}>" \ + ed25519 \ + sign \ + never; \ fingerprint=$$(gpg \ --list-secret-keys \ --with-colons \ From 5cce6722643cdeb9d178d3efb231e378e6cea698 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 15:38:54 +0200 Subject: [PATCH 464/878] Bind Next.JS cache to an anonymous volume --- docker-compose.yml | 1 + frontend/Dockerfile | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 241b60ca..db78eb88 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -161,6 +161,7 @@ services: # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. + - /app/.next - /app/node_modules extra_hosts: - ${HOST}:host-gateway diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ffa4f5a1..0c9d3e24 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -69,4 +69,11 @@ RUN \ yarn install \ --pure-lockfile +# Create empty directories for user `me` and group `us` to make the respective +# mounted volumes be owned by the user `me` and the group `us`. +RUN \ + mkdir --parents \ + ./.next \ + ./node_modules + ENTRYPOINT ["/usr/bin/tini", "--"] From 86ef981bef905c1b6ec1a97cb42c857f7a714f30 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 16:39:44 +0200 Subject: [PATCH 465/878] Install the latest GnuPG from the official GnuPG repositories: https://www.gnupg.org/blog/20250827-new-repository.html --- backend/Dockerfile | 15 ++++++++++++ backend/Dockerfile-production | 43 +++++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 8f7da2b6..70a8c094 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -91,6 +91,21 @@ COPY \ RUN \ update-ca-certificates +# Install the latest GnuPG from the official GnuPG repository +RUN set -eux && \ + curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2 && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + ENV HOME=/home/me RUN \ mkdir --parents ${HOME}/app && \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 6d0857c9..be0ae1b8 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -17,17 +17,17 @@ # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build -RUN \ - apt-get update && \ - apt-get upgrade --assume-yes && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - gnupg && \ - rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* +# RUN \ +# apt-get update && \ +# apt-get upgrade --assume-yes && \ +# apt-get install \ +# --assume-yes \ +# --no-install-recommends \ +# gnupg && \ +# rm \ +# --recursive \ +# --force \ +# /var/lib/apt/lists/* COPY ./ /app @@ -61,8 +61,27 @@ RUN \ apt-get install \ --assume-yes \ --no-install-recommends \ - curl \ + curl && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + +# Install the latest GnuPG from the official GnuPG repository +RUN set -eux && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ gnupg && \ + curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + gnupg2 && \ rm \ --recursive \ --force \ From 04add723e5a97e89afbf3e845de3f8070a43d606 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 16:40:19 +0200 Subject: [PATCH 466/878] Rename file to match class name --- .../{JsonSerializerOptions.cs => JsonSerializerSettings.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename backend/src/ApiRequests/{JsonSerializerOptions.cs => JsonSerializerSettings.cs} (97%) diff --git a/backend/src/ApiRequests/JsonSerializerOptions.cs b/backend/src/ApiRequests/JsonSerializerSettings.cs similarity index 97% rename from backend/src/ApiRequests/JsonSerializerOptions.cs rename to backend/src/ApiRequests/JsonSerializerSettings.cs index 5592d8d9..54d96a60 100644 --- a/backend/src/ApiRequests/JsonSerializerOptions.cs +++ b/backend/src/ApiRequests/JsonSerializerSettings.cs @@ -12,7 +12,7 @@ public static class JsonSerializerSettings /// /// Settings for GraphQL Json requests. /// - public static readonly JsonSerializerOptions GraphQL = + public static readonly JsonSerializerOptions GraphQl = new() { Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, From f9330a4f2ab73be1de97a569bb5f46ce8ae6aaaa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 18:16:28 +0200 Subject: [PATCH 467/878] Rigorously verify data approvals before adding them (and put logging alongside the files in which it is used) --- backend/src/ApiRequests/GetUserInfo.cs | 6 +- .../ApiRequests/IsGnuPgFingerprintValid.cs | 18 +- .../Queries/IsGnuPgFingerprintValid.graphql | 29 ++- backend/src/ApiRequests/QueryAllMetaData.cs | 17 +- backend/src/ApiRequests/QueryCurrentUser.cs | 15 +- backend/src/ApiRequests/QueryData.cs | 11 +- backend/src/ApiRequests/QueryDatabase.cs | 13 +- backend/src/ApiRequests/UpdateDatabase.cs | 10 +- backend/src/AppSettings.cs | 8 + .../DataApprovals/AddDataApprovalErrorCode.cs | 9 +- .../DataApprovals/AddDataApprovalPayload.cs | 8 + .../DataApprovals/DataApprovalMutations.cs | 195 ++++++++++++++++-- .../GraphQl/Databases/DatabaseMutations.cs | 10 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 10 +- backend/src/GraphQl/Methods/MethodQueries.cs | 13 +- backend/src/GraphQl/Users/UserQueries.cs | 6 - .../src/Logging/AccessRightServiceLogging.cs | 18 -- .../Logging/ResponseApprovalServiceLogging.cs | 19 -- backend/src/Logging/SigningServiceLogging.cs | 37 ---- backend/src/Logging/UserServiceLogging.cs | 12 -- backend/src/Services/AccessRightsService.cs | 16 +- backend/src/Services/ApiRequestService.cs | 157 ++++---------- backend/src/Services/DataApprovalService.cs | 9 +- .../src/Services/ResponseApprovalService.cs | 30 ++- backend/src/Services/SigningService.cs | 101 ++++++++- backend/src/Services/UserService.cs | 22 +- backend/src/Startup.cs | 16 +- 27 files changed, 446 insertions(+), 369 deletions(-) delete mode 100644 backend/src/Logging/AccessRightServiceLogging.cs delete mode 100644 backend/src/Logging/ResponseApprovalServiceLogging.cs delete mode 100644 backend/src/Logging/SigningServiceLogging.cs delete mode 100644 backend/src/Logging/UserServiceLogging.cs diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index e22a0631..cc619113 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -35,14 +35,10 @@ string Formatted public static Task Do( AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) { - return apiRequestService.Metabase().PerformHttpGetRequest( + return apiRequestService.PerformHttpGetRequest( GetEndpoint(appSettings), - httpClientFactory, - httpContextAccessor, cancellationToken ); } diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index 2bf84e2d..e3011be9 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -13,13 +13,13 @@ public sealed class IsGnuPgFingerprintValid private const string QueryFileName = "IsGnuPgFingerprintValid.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + appSettings.MetabaseGraphQlEndpoint; - public sealed record ValidFingerprints( - uint TotalCount + public sealed record Institution( + bool HasGnuPgKeyFingerprint ); - private sealed record Data(ValidFingerprints? ValidFingerprints); + private sealed record Data(Institution? Institution); public static async Task Do( string fingerprint, @@ -27,13 +27,11 @@ public static async Task Do( DateTime createdAt, AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(QueryFileName), new @@ -44,9 +42,7 @@ await apiRequestService.ConstructGraphQlQuery(QueryFileName), }, "IsGnuPgFingerprintValid" ), - httpClientFactory, - httpContextAccessor, cancellationToken - )).Data.ValidFingerprints?.TotalCount == 1; + )).Data.Institution?.HasGnuPgKeyFingerprint ?? false; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql index 51edad8c..ac5cff9a 100644 --- a/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql +++ b/backend/src/ApiRequests/Queries/IsGnuPgFingerprintValid.graphql @@ -3,21 +3,20 @@ query IsGnuPgFingerprintValid( $institutionId: Uuid! $createdAt: DateTime! ) { - validFingerprints: institutions( - where: { - id: { equalTo: $institutionId } - gnuPgKeyFingerprints: { - some: { - fingerprint: { equalTo: $fingerprint } - allowedAt: { lessThanOrEqualTo: $createdAt } - or: [ - { forbiddenAt: { equalTo: null } } - { forbiddenAt: { greaterThan: $createdAt } } - ] - } + institution(id: $institutionId) { + hasGnuPgKeyFingerprint( + where: { + and: [ + { fingerprint: { equalTo: $fingerprint } } + { allowedAt: { lessThanOrEqualTo: $createdAt } } + { + or: [ + { forbiddenAt: { equalTo: null } } + { forbiddenAt: { greaterThan: $createdAt } } + ] + } + ] } - } - ) { - totalCount + ) } } diff --git a/backend/src/ApiRequests/QueryAllMetaData.cs b/backend/src/ApiRequests/QueryAllMetaData.cs index dc96ece8..bb697b22 100644 --- a/backend/src/ApiRequests/QueryAllMetaData.cs +++ b/backend/src/ApiRequests/QueryAllMetaData.cs @@ -1,18 +1,16 @@ using System; -using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Services; using GraphQL; -using Microsoft.AspNetCore.Http; namespace Database.ApiRequests; public sealed class QueryAllMetaData { public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - ApiRequestService.DatabaseGraphQlEndpoint(appSettings); + appSettings.DatabaseGraphQlEndpoint; public static readonly string[] CalorimetricDataFileNames = [ @@ -54,18 +52,17 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => string[] fileNames, AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { var variables = new { id = dataId }; var query = await apiRequestService.ConstructGraphQlQuery(fileNames); - var response = await apiRequestService.Database().QueryGraphQl( - appSettings, - new GraphQLRequest(query, variables), - httpClientFactory, - httpContextAccessor, + var response = await apiRequestService.QueryGraphQlAsString( + GetGraphQlEndpoint(appSettings), + new GraphQLRequest( + query, + variables + ), cancellationToken ); return (query, JsonSerializer.SerializeToElement(variables), response); diff --git a/backend/src/ApiRequests/QueryCurrentUser.cs b/backend/src/ApiRequests/QueryCurrentUser.cs index 77b4bed6..48d2e4f1 100644 --- a/backend/src/ApiRequests/QueryCurrentUser.cs +++ b/backend/src/ApiRequests/QueryCurrentUser.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.Services; using GraphQL; -using Microsoft.AspNetCore.Http; namespace Database.ApiRequests; @@ -17,7 +15,7 @@ public sealed class QueryCurrentUser private const string CurrentUserFileName = "CurrentUser.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + appSettings.MetabaseGraphQlEndpoint; public sealed record CurrentUser( Guid Uuid, @@ -70,12 +68,11 @@ private sealed record CurrentUserData(CurrentUser? CurrentUser); public static async Task Do( AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) + CancellationToken cancellationToken + ) { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(CurrentUserFileName), new @@ -84,8 +81,6 @@ await apiRequestService.ConstructGraphQlQuery(CurrentUserFileName), }, "CurrentUser" ), - httpClientFactory, - httpContextAccessor, cancellationToken )) .Data diff --git a/backend/src/ApiRequests/QueryData.cs b/backend/src/ApiRequests/QueryData.cs index 937679ff..7f3032c2 100644 --- a/backend/src/ApiRequests/QueryData.cs +++ b/backend/src/ApiRequests/QueryData.cs @@ -1,11 +1,8 @@ using System; -using System.Net.Http; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Services; using GraphQL; -using Microsoft.AspNetCore.Http; namespace Database.ApiRequests; @@ -15,23 +12,17 @@ public static async Task> Do( Uri databaseUrl, Guid dataId, string query, - AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken) where TGraphQlData : class { - return await apiRequestService.Database().QueryGraphQl( - appSettings, + return await apiRequestService.QueryGraphQl( databaseUrl, new GraphQLRequest( query, new { id = dataId } ), - httpClientFactory, - httpContextAccessor, cancellationToken ); } diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index b33873ea..c2a29d74 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -13,7 +13,7 @@ public sealed class QueryDatabase private const string QueryFileName = "Database.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + appSettings.MetabaseGraphQlEndpoint; public sealed record Database( Guid Uuid, @@ -47,12 +47,11 @@ private sealed record DatabaseData(Database? Database); Guid databaseId, AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, - CancellationToken cancellationToken) + CancellationToken cancellationToken + ) { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(QueryFileName), new { @@ -60,8 +59,6 @@ private sealed record DatabaseData(Database? Database); }, "Database" ), - httpClientFactory, - httpContextAccessor, cancellationToken )).Data.Database; } diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index f854b02b..6d048140 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -15,7 +15,7 @@ public sealed class UpdateDatabase private const string UpdateDatabaseFileName = "UpdateDatabase.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - ApiRequestService.MetabaseGraphQlEndpoint(appSettings); + appSettings.MetabaseGraphQlEndpoint; public sealed record UpdateDatabaseInput( Guid DatabaseId, @@ -47,13 +47,11 @@ IReadOnlyList Path UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - return (await apiRequestService.Metabase().QueryGraphQl( - appSettings, + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery( UpdateDatabaseFileName @@ -64,8 +62,6 @@ await apiRequestService.ConstructGraphQlQuery( }, "UpdateDatabase" ), - httpClientFactory, - httpContextAccessor, cancellationToken )).Data; } diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 813aa957..22db991e 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -6,6 +6,14 @@ namespace Database; public sealed class AppSettings { + private const string GraphQlPathSegment = "/graphql/"; + + // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of + // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 + public Uri DatabaseGraphQlEndpoint { get => new(new Uri(Host, UriKind.Absolute), GraphQlPathSegment); } + + public Uri MetabaseGraphQlEndpoint { get => new(new Uri(MetabaseHost, UriKind.Absolute), GraphQlPathSegment); } + public string Host { get; private set; } = ""; diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs index c7cf17ac..7dd6c382 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs @@ -12,5 +12,12 @@ public enum AddDataApprovalErrorCode CREATING_RESPONSE_APPROVAL_FAILED, AMBIGUOUS_STATEMENT, MISSING_STATEMENT, - INVALID_KEY_FINGERPRINT + INVALID_KEY_FINGERPRINT, + FAILED_RECEIVING_KEY, + BAD_SIGNATURE, + FAILED_VERIFYING_SIGNATURE, + FAILED_VERIFYING_RESPONSE, + ILLEGAL_MESSAGE, + WRONG_RESPONSE, + WRONG_STATEMENT } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs index b59a2ca0..4b23e83d 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Database.Data; namespace Database.GraphQl.DataApprovals; @@ -19,6 +20,13 @@ AddDataApprovalError error { } + public AddDataApprovalPayload( + IReadOnlyCollection errors + ) + : base(errors) + { + } + public AddDataApprovalPayload( DataApproval dataApproval, AddDataApprovalError error diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index dfc65392..836212d8 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -1,19 +1,42 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.GraphQl.Publications; using Database.GraphQl.References; +using Database.GraphQl.Standards; using Database.Services; +using GraphQL; using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.Extensions.Logging; namespace Database.GraphQl.DataApprovals; +public static partial class DataApprovalMutationsLogging +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Unknown signature verification result: {SigantureVerificationResult}")] + public static partial void UnknownSignatureVerificationResult(this ILogger logger, SigantureVerificationResult sigantureVerificationResult); +} + [ExtendObjectType(nameof(Mutation))] public sealed class DataApprovalMutations { + private sealed record Message( + JsonElement Statement, + JsonElement Response + ); + //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task AddDataApprovalAsync( DataApprovalInput input, @@ -21,6 +44,10 @@ public async Task AddDataApprovalAsync( UserService userService, DataApprovalService dataApprovalService, ResponseApprovalService responseApprovalService, + SigningService signingService, + ApiRequestService apiRequestService, + ILogger logger, + AppSettings appSettings, CancellationToken cancellationToken ) { @@ -45,6 +72,7 @@ CancellationToken cancellationToken ) ); } + var errors = new List(); if (!await dataApprovalService.IsGnuPgFingerprintValid( input.KeyFingerprint, input.ApproverId, @@ -52,7 +80,7 @@ CancellationToken cancellationToken cancellationToken )) { - return new AddDataApprovalPayload( + errors.Add( new AddDataApprovalError( AddDataApprovalErrorCode.INVALID_KEY_FINGERPRINT, $"The key fingerprint does not exist for the institution '{input.ApproverId}' or was not allowed before or was forbidden before the timestamp {input.Timestamp}.", @@ -60,23 +88,45 @@ CancellationToken cancellationToken ) ); } - - var data = await context.GetDataAsync(input.DataId, cancellationToken); - if (data is null) + var sigantureVerificationResult = await signingService.VerifySignature(input.Message, input.Signature, input.KeyFingerprint); + switch (sigantureVerificationResult) { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); + case SigantureVerificationResult.FAILED_RECEIVING_KEY: + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.FAILED_RECEIVING_KEY, + $"Failed to receive the key '{input.KeyFingerprint}' from the keyserver '{SigningService.KeyServerUrl.AbsoluteUri}'.", + [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] + ) + ); + break; + case SigantureVerificationResult.BAD_SIGNATURE: + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.BAD_SIGNATURE, + $"The signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' is bad.", + [nameof(input), nameof(input.Signature).ToLowerFirst()] + ) + ); + break; + case SigantureVerificationResult.GOOD_SIGNATURE: + break; + default: + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.FAILED_VERIFYING_SIGNATURE, + $"Failed to verify the signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' for the reason '{sigantureVerificationResult}.", + [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] + ) + ); + logger.UnknownSignatureVerificationResult(sigantureVerificationResult); + break; } - + ; if (input.Statement.Standard is null && input.Statement.Publication is null) { - return new AddDataApprovalPayload( + errors.Add( new AddDataApprovalError( AddDataApprovalErrorCode.MISSING_STATEMENT, "Both standard and publication are null.", @@ -84,11 +134,10 @@ CancellationToken cancellationToken ) ); } - if (input.Statement.Standard is not null && input.Statement.Publication is not null) { - return new AddDataApprovalPayload( + errors.Add( new AddDataApprovalError( AddDataApprovalErrorCode.AMBIGUOUS_STATEMENT, "Both standard and publication are non-null.", @@ -96,6 +145,118 @@ CancellationToken cancellationToken ) ); } + Message? message = null; + try + { + using var document = JsonDocument.Parse(input.Message); + var root = document.RootElement; + message = new Message( + root.GetProperty(nameof(message.Statement).ToLowerFirst()).Clone(), + root.GetProperty(nameof(message.Response).ToLowerFirst()).Clone() + ); + } + catch (Exception exception) when (exception is JsonException || exception is KeyNotFoundException) + { + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.ILLEGAL_MESSAGE, + $"The message is not valid JSON or does not have the required structure '{{\"statement\": ..., \"response\": ...}}: {exception}", + [nameof(input), nameof(input.Message).ToLowerFirst()] + ) + ); + } + if (message is not null + && input.Statement.Standard is null + && input.Statement.Publication is not null + ) + { + var messagePublication = message.Statement.Deserialize(JsonSerializerSettings.GraphQl); + if (!JsonElement.DeepEquals( + JsonSerializer.SerializeToElement(input.Statement.Publication), + JsonSerializer.SerializeToElement(messagePublication) + )) + { + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.WRONG_STATEMENT, + $"The message statement '{JsonSerializer.Serialize(messagePublication)}' is not equal to the input publication statement '{JsonSerializer.Serialize(input.Statement.Publication)}'.", + [nameof(input), nameof(input.Statement).ToLowerFirst(), nameof(input.Statement.Publication).ToLowerFirst()] + ) + ); + } + } + if (message is not null + && input.Statement.Standard is not null + && input.Statement.Publication is null + ) + { + var messageStandard = message.Statement.Deserialize(JsonSerializerSettings.GraphQl); + if (!JsonElement.DeepEquals( + JsonSerializer.SerializeToElement(input.Statement.Standard), + JsonSerializer.SerializeToElement(messageStandard) + )) + { + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.WRONG_STATEMENT, + $"The message statement '{JsonSerializer.Serialize(messageStandard)}' is not equal to the input standard statement '{JsonSerializer.Serialize(input.Statement.Standard)}'.", + [nameof(input), nameof(input.Statement).ToLowerFirst(), nameof(input.Statement.Standard).ToLowerFirst()] + ) + ); + } + } + JsonElement? response = null; + try + { + // TODO Query the database GraphQL endpoint directly in C# instead of over HTTP. + response = await apiRequestService.QueryGraphQlAsJson( + appSettings.DatabaseGraphQlEndpoint, + new GraphQLRequest( + input.Query, + input.Variables + ), + cancellationToken + ); + } + catch (Exception exception) when (exception is HttpRequestException || exception is JsonException) + { + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.FAILED_VERIFYING_RESPONSE, + $"Failed verifying the response due to the exception: {exception}", + [nameof(input), nameof(input.Query).ToLowerFirst()] + ) + ); + } + if (message is not null + && response is not null + && !JsonElement.DeepEquals((JsonElement)response, message.Response) + ) + { + errors.Add( + new AddDataApprovalError( + AddDataApprovalErrorCode.WRONG_RESPONSE, + $"The message response '{JsonSerializer.Serialize(message.Response)}' is not equal to the response '{JsonSerializer.Serialize(response)}' of the query '{input.Query}' against the GraphQL endpoint '{appSettings.DatabaseGraphQlEndpoint}'.", + [nameof(input), nameof(input.Message).ToLowerFirst()] + ) + ); + } + if (errors.Count >= 1) + { + return new AddDataApprovalPayload(errors); + } + + var data = await context.GetDataAsync(input.DataId, cancellationToken); + if (data is null) + { + return new AddDataApprovalPayload( + new AddDataApprovalError( + AddDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ); + } var approval = new DataApproval( input.Timestamp, @@ -175,7 +336,9 @@ CancellationToken cancellationToken ); } - var approval = data.Approvals.Where(a => a.Signature.Trim() == input.Signature.Trim()).FirstOrDefault(); + var approval = data.Approvals + .Where(a => a.Signature.Trim() == input.Signature.Trim()) + .SingleOrDefault(); if (approval is null) { return new RemoveDataApprovalPayload( diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/DatabaseMutations.cs index 84fb805d..e85a32e5 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/DatabaseMutations.cs @@ -1,11 +1,9 @@ -using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Services; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; +using Database.ApiRequests; +using Database.Services; using static Database.ApiRequests.UpdateDatabase; namespace Database.GraphQl.Databases; @@ -17,8 +15,6 @@ public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -28,8 +24,6 @@ CancellationToken cancellationToken input, appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ), resolverContext, diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 7e3d6447..40962cd8 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,12 +1,10 @@ -using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; +using Database.ApiRequests; +using Database.Services; namespace Database.GraphQl.Databases; @@ -16,8 +14,6 @@ public sealed class DatabaseQueries public async Task GetDatabaseAsync( AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -27,8 +23,6 @@ CancellationToken cancellationToken appSettings.DatabaseId, appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ), resolverContext, diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 1d986e02..aa365aec 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,5 +1,4 @@ using System; -using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -11,7 +10,6 @@ using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; namespace Database.GraphQl.Methods; @@ -61,7 +59,7 @@ CancellationToken cancellationToken using var stream = data.OpenReadStream(); using var jsonData = await JsonDocument.ParseAsync( stream, - ApiRequestService.JsonDocumentOptions, + ApiRequestService.LaxJsonDocumentOptions, cancellationToken ); var result = method.Calculate(jsonData.RootElement); @@ -74,8 +72,6 @@ public async Task CalculateMethodAsync( AppSettings appSettings, MethodFactory methodFactory, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -96,8 +92,6 @@ CancellationToken cancellationToken dataReference.DatabaseId, appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ), resolverContext, @@ -120,7 +114,8 @@ CancellationToken cancellationToken database.Locator, dataReference.DataId, query, - appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken + apiRequestService, + cancellationToken ), resolverContext, database.Locator @@ -150,7 +145,7 @@ CancellationToken cancellationToken // TODO The locator could also point to a non-JSON resource. Support those also: response.Data.Data.ResourceTree.Root.Value.DataFormatId // TODO Check that the data has the hash value: response.Data.Data.ResourceTree.Root.Value.HashValue; var data = await apiRequestService.PerformHttpGetRequest( - locator, httpClientFactory, httpContextAccessor, cancellationToken + locator, cancellationToken ); var result = method.Calculate(data); return new CalculateMethodPayload(result); diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 2c0b5a71..0a4f772d 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Net.Http; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -8,7 +7,6 @@ using Database.Services; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; using static Database.ApiRequests.GetUserInfo; @@ -39,8 +37,6 @@ await context.Users.AsNoTracking() public Task GetCurrentUserInfoAsync( AppSettings appSettings, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -49,8 +45,6 @@ CancellationToken cancellationToken () => GetUserInfo.Do( appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ), resolverContext, diff --git a/backend/src/Logging/AccessRightServiceLogging.cs b/backend/src/Logging/AccessRightServiceLogging.cs deleted file mode 100644 index 874f448d..00000000 --- a/backend/src/Logging/AccessRightServiceLogging.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Database.Services; -using Microsoft.Extensions.Logging; - -namespace Database.Logging; - -public static partial class AccessRightServiceLogging -{ - [LoggerMessage( - Level = LogLevel.Information, - Message = "Cannot apply access rights because the user or application is unknown. The fetched user ID is {UserId} and the application ID is {ApplicationId}.")] - public static partial void UnknownUserOrApplication(this ILogger logger, Guid? userId, string? applicationId); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Restricted Item Id: {Id} Reason: {Reason}")] - public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); -} \ No newline at end of file diff --git a/backend/src/Logging/ResponseApprovalServiceLogging.cs b/backend/src/Logging/ResponseApprovalServiceLogging.cs deleted file mode 100644 index 89b773fe..00000000 --- a/backend/src/Logging/ResponseApprovalServiceLogging.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Text.Json; -using Database.Services; -using Microsoft.Extensions.Logging; - -namespace Database.Logging; - -public static partial class ResponseApprovalServiceLogging -{ - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Querying all meta data for {DataType} with ID {Id}")] - public static partial void QueryAllMetaData(this ILogger logger, Type dataType, Guid id); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Query, variables, and response: {Query}, {Variables}, and {Response}")] - public static partial void QueryAndVariablesAndResponce(this ILogger logger, string query, JsonElement variables, string response); -} \ No newline at end of file diff --git a/backend/src/Logging/SigningServiceLogging.cs b/backend/src/Logging/SigningServiceLogging.cs deleted file mode 100644 index b29f8e80..00000000 --- a/backend/src/Logging/SigningServiceLogging.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Database.Services; -using Microsoft.Extensions.Logging; - -namespace Database.Logging; - -public static partial class SigningServiceLogging -{ - [LoggerMessage( - Level = LogLevel.Debug, - Message = "GNUPG Fingerprint: {Fingerprint}")] - public static partial void ExtractedFingerprint(this ILogger logger, string fingerprint); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "GNUPG Signature: {Signature}")] - public static partial void CreatedSignature(this ILogger logger, string signature); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "GNUPG: Execute Command: {Command}")] - public static partial void ExecuteCommand(this ILogger logger, string command); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "GNUPG Execute Command: {Error}")] - public static partial void ExecuteCommandDiagnostics(this ILogger logger, string error); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "GNUPG: Execute Command Output: {Output}")] - public static partial void ExecuteCommandOutput(this ILogger logger, string output); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "GNUPG: Execute Command Result: {result}")] - public static partial void ExecuteCommandExitCode(this ILogger logger, int result); -} \ No newline at end of file diff --git a/backend/src/Logging/UserServiceLogging.cs b/backend/src/Logging/UserServiceLogging.cs deleted file mode 100644 index 95e8aa8a..00000000 --- a/backend/src/Logging/UserServiceLogging.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Database.Services; -using Microsoft.Extensions.Logging; - -namespace Database.Logging; - -public static partial class UserServiceLogging -{ - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Extracted Bearer Token: {Token}")] - public static partial void ExtractedToken(this ILogger logger, string? token); -} \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index f3b09a76..4237ab91 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -3,14 +3,26 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; -using Database.Logging; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Database.Data; using static Database.ApiRequests.QueryCurrentUser; namespace Database.Services; +public static partial class AccessRightServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Information, + Message = "Cannot apply access rights because the user or application is unknown. The fetched user ID is {UserId} and the application ID is {ApplicationId}.")] + public static partial void UnknownUserOrApplication(this ILogger logger, Guid? userId, string? applicationId); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Restricted Item Id: {Id} Reason: {Reason}")] + public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); +} + public sealed class AccessRightsService( IDbContextFactory dbContextFactory, UserService userService, diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index d80197ee..8f20866f 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -15,64 +15,29 @@ namespace Database.Services; /// -/// Service to request Metabase or Database Api. GraphQL and REST. +/// Service to request GraphQL and REST APIs. /// -public sealed class ApiRequestService +public sealed class ApiRequestService( + IHttpClientFactory httpClientFactory, + IHttpContextAccessor httpContextAccessor +) { - /// - /// Name of Metabase http client. - /// - public const string MetabaseHttpClient = "Metabase"; - - /// - /// Name of Database http client. - /// - public const string DatabaseHttpClient = "Database"; - - private const string GraphQlPathSegment = "/graphql/"; - - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of - // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - public static Uri MetabaseGraphQlEndpoint(AppSettings appSettings) => - new(new Uri(appSettings.MetabaseHost, UriKind.Absolute), GraphQlPathSegment); + public const string CustomHttpClient = "Custom"; - public static Uri DatabaseGraphQlEndpoint(AppSettings appSettings) => - new(new Uri(appSettings.Host, UriKind.Absolute), GraphQlPathSegment); - - private static Uri GetGraphQlEndpoint(AppSettings appSettings) + public static JsonDocumentOptions StrictJsonDocumentOptions => new() { - return s_useMetabase ? MetabaseGraphQlEndpoint(appSettings) : DatabaseGraphQlEndpoint(appSettings); - } + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Disallow, + MaxDepth = 0 + }; - public static JsonDocumentOptions JsonDocumentOptions => new() + public static JsonDocumentOptions LaxJsonDocumentOptions => new() { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip, MaxDepth = 0 }; - private static bool s_useMetabase = true; - - /// - /// Use Metabase as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection - /// - /// - public ApiRequestService Metabase() - { - s_useMetabase = true; - return this; - } - - /// - /// Use Database as target for following requests. See https://chillicream.com/docs/hotchocolate/v14/server/dependency-injection - /// - /// - public ApiRequestService Database() - { - s_useMetabase = false; - return this; - } - public Task ConstructGraphQlQuery( string fileName ) @@ -99,51 +64,39 @@ await Task.WhenAll( ); } - public Task QueryGraphQl( - AppSettings appSettings, + public Task QueryGraphQlAsJson( + Uri url, GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { return Query( HttpMethod.Post, - GetGraphQlEndpoint(appSettings), - httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), + url, + async httpResponseContent => + { + using var document = await JsonDocument.ParseAsync( + await httpResponseContent.ReadAsStreamAsync(), + StrictJsonDocumentOptions + ); + return document.RootElement.Clone(); + }, MakeGraphQlJsonHttpContent(request), - httpClientFactory, - httpContextAccessor, cancellationToken ); } - /// - /// Send GraphQL request to API and return response. - /// - /// Expected response type. - /// - /// - /// - /// - /// - /// of expected type. - public Task> QueryGraphQl( - AppSettings appSettings, + public Task QueryGraphQlAsString( + Uri url, GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) - where TGraphQlResponse : class { - return Query>( + return Query( HttpMethod.Post, - GetGraphQlEndpoint(appSettings), + url, + httpResponseContent => httpResponseContent.ReadAsStringAsync(cancellationToken), MakeGraphQlJsonHttpContent(request), - JsonSerializerSettings.GraphQL, - httpClientFactory, - httpContextAccessor, cancellationToken ); } @@ -153,64 +106,50 @@ CancellationToken cancellationToken /// /// Expected response type. /// - /// Uri of GraphQL Api. /// /// /// /// /// of expected type. public Task> QueryGraphQl( - AppSettings appSettings, - Uri uri, + Uri url, GraphQLRequest request, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) where TGraphQlResponse : class { return Query>( HttpMethod.Post, - // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of - // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - uri, + url, MakeGraphQlJsonHttpContent(request), - JsonSerializerSettings.GraphQL, - httpClientFactory, - httpContextAccessor, + JsonSerializerSettings.GraphQl, cancellationToken ); } public Task PerformHttpGetRequest( - Uri uri, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, + Uri url, CancellationToken cancellationToken ) { return Query( HttpMethod.Get, - uri, + url, async httpResponseContent => { using var document = await JsonDocument.ParseAsync( await httpResponseContent.ReadAsStreamAsync(), - JsonDocumentOptions + LaxJsonDocumentOptions ); return document.RootElement.Clone(); }, null, - httpClientFactory, - httpContextAccessor, cancellationToken ); } public Task PerformHttpGetRequest( Uri uri, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) where TResponse : class @@ -220,19 +159,15 @@ CancellationToken cancellationToken uri, null, JsonSerializerSettings.Rest, - httpClientFactory, - httpContextAccessor, cancellationToken ); } - private static async Task Query( + private async Task Query( HttpMethod httpMethod, - Uri uri, + Uri url, HttpContent? httpRequestContent, JsonSerializerOptions serializerOptions, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) where TResponse : class @@ -242,8 +177,9 @@ CancellationToken cancellationToken // would make debugging more difficult though, https://docs.microsoft.com/en-us/dotnet/api/system.net.http.json.httpcontentjsonextensions.readfromjsonasync?view=net-5.0#System_Net_Http_Json_HttpContentJsonExtensions_ReadFromJsonAsync__1_System_Net_Http_HttpContent_System_Text_Json_JsonSerializerOptions_System_Threading_CancellationToken_ return await Query( httpMethod, - uri, - async httpResponseContent => { + url, + async httpResponseContent => + { var responseStream = await httpResponseContent.ReadAsStreamAsync(); // For debugging, the following line of code writes the response to standard output. // Console.WriteLine(new StreamReader(responseStream).ReadToEnd()); @@ -252,29 +188,22 @@ CancellationToken cancellationToken serializerOptions, cancellationToken ) ?? throw new JsonException("Failed to deserialize the GraphQL response."); - + }, httpRequestContent, - httpClientFactory, - httpContextAccessor, cancellationToken ); } - private static async Task Query( + private async Task Query( HttpMethod httpMethod, Uri uri, Func> read, HttpContent? httpContent, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, CancellationToken cancellationToken ) { - using var httpClient = - s_useMetabase - ? httpClientFactory.CreateClient(MetabaseHttpClient) - : httpClientFactory.CreateClient(DatabaseHttpClient); + using var httpClient = httpClientFactory.CreateClient(CustomHttpClient); var bearerToken = await httpContextAccessor.ExtractBearerToken(); using var httpRequestMessage = new HttpRequestMessage( httpMethod, @@ -306,7 +235,7 @@ TContent content new ByteArrayContent( JsonSerializer.SerializeToUtf8Bytes( content, - JsonSerializerSettings.GraphQL + JsonSerializerSettings.GraphQl ) ); result.Headers.ContentType = diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs index 9fb5f20d..8837ccb1 100644 --- a/backend/src/Services/DataApprovalService.cs +++ b/backend/src/Services/DataApprovalService.cs @@ -1,17 +1,12 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Database.Data; namespace Database.Services; public sealed class DataApprovalService( AppSettings appSettings, - ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor + ApiRequestService apiRequestService ) { public Task IsGnuPgFingerprintValid( @@ -27,8 +22,6 @@ CancellationToken cancellationToken createdAt, appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ); } diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 5db586f2..5cd79c23 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,21 +1,29 @@ using System; -using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.Data; -using Database.Logging; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using Database.Data; namespace Database.Services; +public static partial class ResponseApprovalServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Querying all meta data for {DataType} with ID {Id}")] + public static partial void QueryAllMetaData(this ILogger logger, Type dataType, Guid id); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Query, variables, and response: {Query}, {Variables}, and {Response}")] + public static partial void QueryAndVariablesAndResponce(this ILogger logger, string query, JsonElement variables, string response); +} + public sealed class ResponseApprovalService( AppSettings appSettings, SigningService signingService, ApiRequestService apiRequestService, - IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor, ILogger logger ) { @@ -50,11 +58,11 @@ public async Task CreateResponseApproval(T dataObject, Canc { return dataObject switch { - GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - CalorimetricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.CalorimetricDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), - OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, httpClientFactory, httpContextAccessor, cancellationToken), + GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, cancellationToken), + CalorimetricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.CalorimetricDataFileNames, appSettings, apiRequestService, cancellationToken), + HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, cancellationToken), + PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, cancellationToken), + OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, cancellationToken), _ => throw new NotImplementedException($"Unsupported data type {typeof(T)}"), }; } diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index b828ac2d..0c81f0bf 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -1,15 +1,69 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Threading.Tasks; -using Database.Logging; using Microsoft.Extensions.Logging; namespace Database.Services; +[SuppressMessage("Naming", "CA1707")] +public enum SigantureVerificationResult +{ + FAILED_RECEIVING_KEY, + GOOD_SIGNATURE, + BAD_SIGNATURE +} + +public static partial class SigningServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Fingerprint: {Fingerprint}")] + public static partial void ExtractedFingerprint(this ILogger logger, string fingerprint); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG Signature: {Signature}")] + public static partial void CreatedSignature(this ILogger logger, string signature); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to receive the key '{Fingerprint}' from the keyserver '{KeyServerUrl}' with standard output '{Output}' and diagnostic information '{Diagnostics}'")] + public static partial void FailedToReceiveKey(this ILogger logger, string fingerprint, Uri keyServerUrl, string output, string diagnostics); + + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Failed to verify the signature '{Signature}' for the message '{Message}' and the fingerprint '{Fingerprint}' with standard output '{Output}' and diagnostic information '{Diagnostics}'")] + public static partial void FailedToVerifySignature(this ILogger logger, string fingerprint, string signature, string message, string output, string diagnostics); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command: {Command}")] + public static partial void ExecuteCommand(this ILogger logger, string command); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "GNUPG Execute Command: {Error}")] + public static partial void ExecuteCommandDiagnostics(this ILogger logger, string error); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Output: {Output}")] + public static partial void ExecuteCommandOutput(this ILogger logger, string output); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "GNUPG: Execute Command Result: {result}")] + public static partial void ExecuteCommandExitCode(this ILogger logger, int result); +} + public sealed class SigningService( AppSettings appSettings, ILogger logger) { + public static readonly Uri KeyServerUrl = new("hkps://keys.openpgp.org/", UriKind.Absolute); + public async Task Initialize() { var (success, output, diagnostics) = await ExecuteCommand( @@ -35,6 +89,51 @@ public async Task Initialize() return (signature, appSettings.GnupgSecretSigningKey.Fingerprint); } + public async Task VerifySignature( + string message, string signature, string fingerprint + ) + { + if (!await ReceiveKey(fingerprint)) + { + return SigantureVerificationResult.FAILED_RECEIVING_KEY; + } + var temporaryMessageFilePath = Path.GetTempFileName(); + try + { + File.WriteAllText(temporaryMessageFilePath, message); + var (success, output, diagnostics) = await ExecuteCommand( + $"gpg --pinentry-mode loopback --batch --always-trust --assert-signer '{fingerprint}' --verify - '{temporaryMessageFilePath}'", + signature + ); + if (!success) + { + logger.FailedToVerifySignature(fingerprint, signature, message, output, diagnostics); + } + return success + ? SigantureVerificationResult.GOOD_SIGNATURE + : SigantureVerificationResult.BAD_SIGNATURE; + } + finally + { + if (File.Exists(temporaryMessageFilePath)) + { + File.Delete(temporaryMessageFilePath); + } + } + } + + private async Task ReceiveKey(string fingerprint) + { + var (success, output, diagnostics) = await ExecuteCommand( + $"gpg --pinentry-mode loopback --batch --keyserver {KeyServerUrl.AbsoluteUri} --receive-keys {fingerprint}" + ); + if (!success) + { + logger.FailedToReceiveKey(fingerprint, KeyServerUrl, output, diagnostics); + } + return success; + } + private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommand( string command, string? input = null diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 5a936e20..c7e2eaf2 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,17 +1,24 @@ using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Extensions; -using Database.Logging; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; -using static Database.ApiRequests.QueryCurrentUser; using static OpenIddict.Abstractions.OpenIddictConstants; +using Database.ApiRequests; +using Database.Extensions; +using static Database.ApiRequests.QueryCurrentUser; namespace Database.Services; +public static partial class UserServiceLogging +{ + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Extracted Bearer Token: {Token}")] + public static partial void ExtractedToken(this ILogger logger, string? token); +} + /// /// Service to get current user from Metabase /// @@ -24,7 +31,6 @@ public sealed class UserService( AppSettings appSettings, ApiRequestService apiRequestService, IHttpContextAccessor httpContextAccessor, - IHttpClientFactory httpClientFactory, CacheService cacheService, ILogger logger) { @@ -52,8 +58,6 @@ public sealed class UserService( return await QueryCurrentUser.Do( appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ); } @@ -61,11 +65,9 @@ public sealed class UserService( if (!cacheService.TryGetUser(token, out var cacheUser)) { // Get user from Metabase - cacheUser = await QueryCurrentUser.Do( + cacheUser = await QueryCurrentUser.Do( appSettings, apiRequestService, - httpClientFactory, - httpContextAccessor, cancellationToken ); // Store user in cache diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index b9e0aa02..1fddd179 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -41,7 +41,7 @@ IConfiguration configuration _.BindNonPublicProperties = true; _.ErrorOnUnknownConfiguration = false; }) - ?? throw new InvalidOperationException("Failed to get application settings from configuration."); + ?? throw new InvalidOperationException("Failed to get application settings from configuration."); private readonly IWebHostEnvironment _environment = environment; @@ -216,20 +216,10 @@ private void ConfigureDatabaseServices(IServiceCollection services) private static void ConfigureHttpClientServices(IServiceCollection services, IWebHostEnvironment environment) { services.AddHttpClient(); - var metabaseHttpClientBuilder = services.AddHttpClient(ApiRequestService.MetabaseHttpClient); + var httpClientBuilder = services.AddHttpClient(ApiRequestService.CustomHttpClient); if (environment.IsDevelopment()) { - metabaseHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => - new HttpClientHandler - { - ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator - } - ); - } - var databaseHttpClientBuilder = services.AddHttpClient(ApiRequestService.DatabaseHttpClient); - if (environment.IsDevelopment()) - { - databaseHttpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => + httpClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => new HttpClientHandler { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator From 281492098e52651f7a9a30cdfe69b1fcd1fbc4ee Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 18:35:31 +0200 Subject: [PATCH 468/878] Make OpenId Connect Client ID configurable --- .env.production.sample | 2 ++ .env.sample | 2 ++ .env.staging.sample | 2 ++ backend/src/AppSettings.cs | 12 +++++++++--- backend/src/Configuration/AuthConfiguration.cs | 11 +++++------ docker-compose.production.yml | 3 ++- docker-compose.yml | 3 ++- 7 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 69e65193..e0b77f8f 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -8,7 +8,9 @@ METABASE_HOST=www.buildingenvelopedata.org DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 +OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET= + VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg diff --git a/.env.sample b/.env.sample index eb4d39e7..4a69fd70 100644 --- a/.env.sample +++ b/.env.sample @@ -10,7 +10,9 @@ METABASE_HTTPS_PORT=4041 DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 +OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET=secret + VERIFICATION_CODE=secret CERTIFICATE_AUTHORITY_HOST=ca.org diff --git a/.env.staging.sample b/.env.staging.sample index cedf6e0b..f332fdf3 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -8,7 +8,9 @@ METABASE_HOST=staging.buildingenvelopedata.org DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 +OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET= + VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 22db991e..da26d08b 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -23,13 +23,13 @@ public sealed class AppSettings public Guid DatabaseId { get; private set; } public Guid OperatorId { get; private set; } - public string OpenIdConnectClientSecret { get; private set; } - = ""; - public string VerificationCode { get; private set; } = ""; + public OpenIdConnectClientSettings OpenIdConnectClient { get; private set; } = new(); + public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; private set; } = new(); + public LoggingSettings Logging { get; private set; } = new(); public JsonWebTokenSettings JsonWebToken { get; private set; } = new(); @@ -38,6 +38,12 @@ public sealed class AppSettings public DatabaseSettings Database { get; private set; } = new(); + public sealed class OpenIdConnectClientSettings + { + public string Id { get; private set; } = ""; + public string Secret { get; private set; } = ""; + } + public sealed class GnupgSecretSigningKeySettings { public string FileName { get; private set; } = ""; diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index b55e6312..dacb081f 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -17,7 +17,6 @@ namespace Database.Configuration; // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs public abstract class AuthConfiguration { - public const string ClientId = "testlab-solar-facades"; public const string ReadApiScope = "api:read"; public const string WriteApiScope = "api:write"; @@ -145,14 +144,14 @@ X509Certificate2 signingCertificate // retrieve the issuer signing keys used to validate tokens. _.SetIssuer(new Uri(appSettings.MetabaseHost, UriKind.Absolute)); // Configure the audience accepted by this resource server. - _.AddAudiences(ClientId); + _.AddAudiences(appSettings.OpenIdConnectClient.Id); // Configure the validation handler to use introspection and // register the client credentials used when communicating with // the remote introspection endpoint. // https://www.oauth.com/oauth2-servers/token-introspection-endpoint/ _.UseIntrospection() - .SetClientId(ClientId) - .SetClientSecret(appSettings.OpenIdConnectClientSecret); + .SetClientId(appSettings.OpenIdConnectClient.Id) + .SetClientSecret(appSettings.OpenIdConnectClient.Secret); // Register the ASP.NET Core host. _.UseAspNetCore(); // Register the System.Net.Http integration. @@ -200,8 +199,8 @@ X509Certificate2 signingCertificate // Note: these settings must match the application details // inserted in the database at the server level. - ClientId = ClientId, - ClientSecret = appSettings.OpenIdConnectClientSecret, + ClientId = appSettings.OpenIdConnectClient.Id, + ClientSecret = appSettings.OpenIdConnectClient.Secret, // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims Scopes = diff --git a/docker-compose.production.yml b/docker-compose.production.yml index ff56a0d0..fee99a50 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -71,7 +71,8 @@ services: - XBASE_MetabaseHost=https://${METABASE_HOST} - XBASE_DatabaseId=${DATABASE_ID} - XBASE_OperatorId=${OPERATOR_ID} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_OpenIdConnectClient__Id=${OPEN_ID_CONNECT_CLIENT_ID} + - XBASE_OpenIdConnectClient__Secret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} diff --git a/docker-compose.yml b/docker-compose.yml index db78eb88..25cb9bcf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -111,7 +111,8 @@ services: - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} - XBASE_DatabaseId=${DATABASE_ID} - XBASE_OperatorId=${OPERATOR_ID} - - XBASE_OpenIdConnectClientSecret=${OPEN_ID_CONNECT_CLIENT_SECRET} + - XBASE_OpenIdConnectClient__Id=${OPEN_ID_CONNECT_CLIENT_ID} + - XBASE_OpenIdConnectClient__Secret=${OPEN_ID_CONNECT_CLIENT_SECRET} - XBASE_VerificationCode=${VERIFICATION_CODE} - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} From cf2bf251ab14320303fc81e114565172ad9a639e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 18:36:52 +0200 Subject: [PATCH 469/878] Document client ID environment variable --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ab611b6e..c77b073f 100644 --- a/README.md +++ b/README.md @@ -262,10 +262,11 @@ and the pages following it. product-data database; - `VERIFICATION_CODE` is the verification code that was generated for this product-data database upon registering it at the metabase; - - `OPEN_ID_CONNECT_CLIENT_SECRET` is the OpenId Connect client secret of - this product-data database as a client of the metabase acting as - identity provider (the client secret is given when registering an - OpenId Connect client at the metabase); + - `OPEN_ID_CONNECT_CLIENT_ID` and `OPEN_ID_CONNECT_CLIENT_SECRET` are the + OpenId Connect client identifier and secret of this product-data + database as a client of the metabase acting as identity provider (the + client secret is given when registering an OpenId Connect client at + the metabase); - `GNUPG_SECRET_SIGNING_KEY_FILE_NAME`, `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE`, and `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` are the file name of an From ac5cddc7ee5038af22db7b0e906d5e9801b275db Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 10 Oct 2025 18:43:08 +0200 Subject: [PATCH 470/878] Add migration to update the coated side enumeration --- ...10_UpdateCoatedSideEnumeration.Designer.cs | 2621 +++++++++++++++++ ...51010164110_UpdateCoatedSideEnumeration.cs | 26 + .../ApplicationDbContextModelSnapshot.cs | 2 +- 3 files changed, 2648 insertions(+), 1 deletion(-) create mode 100644 backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs create mode 100644 backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs diff --git a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs new file mode 100644 index 00000000..0d52f368 --- /dev/null +++ b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs @@ -0,0 +1,2621 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251010164110_UpdateCoatedSideEnumeration")] + partial class UpdateCoatedSideEnumeration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs new file mode 100644 index 00000000..313f4c49 --- /dev/null +++ b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class UpdateCoatedSideEnumeration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.coated_side", "both,neither,non_prime,not_applicable,prime,unknown") + .OldAnnotation("Npgsql:Enum:database.coated_side", "back,both,front,neither"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.coated_side", "back,both,front,neither") + .OldAnnotation("Npgsql:Enum:database.coated_side", "both,neither,non_prime,not_applicable,prime,unknown"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 1d73719d..35d78e86 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -24,7 +24,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "9.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "back", "both", "front", "neither" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); From 80ab0b5e217557f3bb2ae0fbdca4b52e154dc849 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Oct 2025 13:11:37 +0200 Subject: [PATCH 471/878] Make database migrations idempotent such that they can be applied when the current state is unknown (which is needed because the database project is deployed independently on various servers) --- add a GNU Make target `migrate` for development environments, simplify adding a new migration by creating the new csharp files, SQL scripts, and updating the `migrate.sql` script in one go (previously `make NAME=... add-migration` and now with `make NAME=... migration`) --- Makefile | 4 + Makefile.production | 16 +- backend/Makefile | 86 +- backend/src/Migrations/current | 1 - backend/src/Migrations/migrate.sql | 1337 ++++++++++++++++++++++++++++ backend/src/Migrations/previous | 1 - 6 files changed, 1393 insertions(+), 52 deletions(-) delete mode 100644 backend/src/Migrations/current create mode 100644 backend/src/Migrations/migrate.sql delete mode 100644 backend/src/Migrations/previous diff --git a/Makefile b/Makefile index 14ed389c..229dfc1f 100644 --- a/Makefile +++ b/Makefile @@ -238,6 +238,10 @@ sql : ## Run the SQL script in the file `${SQL}` in the running `database` servi --dbname=${database_name} .PHONY : sql +migrate : SQL = ./backend/src/Migrations/migrate.sql +migrate : sql ## Migrate the database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql +.PHONY : migrate + # Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL # Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html backup : CONTAINER_NAME = backup_${NAME}_database diff --git a/Makefile.production b/Makefile.production index de2b8ec9..ce8c47ce 100644 --- a/Makefile.production +++ b/Makefile.production @@ -331,6 +331,18 @@ end-maintenance : ## End maintenance rm ./nginx/html/maintenance.html .PHONY : end-maintenance +# To deploy `develop`, I usually just take all docker services down by running +# `cd /app/machine; make down; cd /app/production; make --file=Makefile.production down; cd /app/staging; make --file=Makefile.production down;` +# pull the latest code by running +# `cd /app/machine; git pull -p; cd /app/production; git pull -p; cd /app/staging; git pull -p;` +# redeploy the reverse proxy by running +# `cd /app/machine; make deploy;` +# backup, migrate, and deploy all services by running +# `make --file=Makefile.production BACKUP_DIRECTORY=$(pwd)/backup down backup migrate deploy-services` +# first in `cd /app/staging` and checking that everything works as expected, +# and finally in `cd /app/production` and checking that everything works as +# expected. Before trying it on staging, I usually play the database from +# production into staging. deploy : BACKUP_DIRECTORY = $(shell pwd)/backup deploy : begin-maintenance store-commit backup fetch-all checkout-target migrate deploy-services run-tests end-maintenance ## Deploy tag, branch, or commit `${TARGET}`, for example, `make --file=Makefile.production TARGET=v1.0.0 deploy` .PHONY : deploy @@ -385,9 +397,7 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex database .PHONY : migrate -migrate : FROM = $(file < ./backend/src/Migrations/previous) -migrate : TO = $(file < ./backend/src/Migrations/current) -migrate : SQL = ./backend/src/Migrations/migrate_from_${FROM}_to_${TO}.sql +migrate : SQL = ./backend/src/Migrations/migrate.sql migrate : sql ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) .PHONY : migrate diff --git a/backend/Makefile b/backend/Makefile index 91500a8a..d64cda80 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -119,7 +119,7 @@ format : ## Format code .PHONY : format update-packages : ## Update packages - for subproject in src test ; do ( \ + for subproject in ./src ./test ; do ( \ cd ./$${subproject} && \ dotnet list package | \ awk '/>/ {print $$2}' | \ @@ -168,62 +168,54 @@ schema : ## Fetch GraphQL schema using `hasura/graphqurl` gq http://backend:8080/graphql/ --introspect > ./schema.graphql .PHONY : schema -add-migration : ## Add migration with name `${NAME}`, for example, `make NAME=InitialCreate add-migration` +has-pending-model-changes : ## Check whether there have been any model changes made since the last migration dotnet ef \ - --project src/Database.csproj \ + --project ./src/Database.csproj \ migrations \ - add \ - ${NAME} -.PHONY : add-migration - -migrate : ## Migrate database - dotnet ef \ - --project src/Database.csproj \ - database \ - update -.PHONY : migrate - -prepare-release : find-latest-migration generate-migration-script switch-migrations ## Prepare release -.PHONY : prepare-release - -find-latest-migration : ## Store name of latest migration in `./src/Migrations/new` - echo \ - $(notdir \ - $(basename \ - $(lastword \ - $(sort \ - $(shell find ./src/Migrations/ -regex ".*/[^/.]*_[^/.]*\.cs") \ - ) \ - ) \ - ) \ - ) \ - > ./src/Migrations/new -.PHONY : find-latest-migration + has-pending-model-changes +.PHONY : has-pending-model-changes # https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#sql-scripts -generate-migration-script : FROM = $(file < ./src/Migrations/current) -generate-migration-script : TO = $(file < ./src/Migrations/new) -generate-migration-script : ## Generate migration and rollback scripts for upcoming release from the migration named by `./src/Migrations/current` to the one named by `./src/Migrations/new` +migration : ## Add migration with name `${NAME}`, generate corresponding idempotent migration SQL script, and append it to ./src/Migrations/migrate.sql, for example, `make NAME=AddStatementToDataApproval migration` + old=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ + echo old=$$old && \ dotnet ef \ - --project ./src/Database.csproj \ + --project ./src/Metabase.csproj \ + migrations \ + add \ + ${NAME} && \ + new=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ + echo new=$$new && \ + migrate_path=./src/Migrations/migrate_from_$${old}_to_$${new}.sql && \ + rollback_path=./src/Migrations/rollback_from_$${new}_to_$${old}.sql && \ + dotnet ef \ + --project ./src/Metabase.csproj \ migrations \ script \ - ${FROM} \ - ${TO} \ - --output ./src/Migrations/migrate_from_${FROM}_to_${TO}.sql + --idempotent \ + $${old} \ + $${new} \ + --output $${migrate_path} && \ dotnet ef \ - --project ./src/Database.csproj \ + --project ./src/Metabase.csproj \ migrations \ script \ - ${TO} \ - ${FROM} \ - --output ./src/Migrations/rollback_from_${FROM}_to_${TO}.sql -.PHONY : generate-migration-script - -switch-migrations : ## Turn current into previous and new into current - mv ./src/Migrations/current ./src/Migrations/previous - mv ./src/Migrations/new ./src/Migrations/current -.PHONY : switch-migrations + --idempotent \ + $${new} \ + $${old} \ + --output $${rollback_path} && \ + sed -i -e "$(shell printf '1s/^\357\273\277//')" -e "s/\r//" $${migrate_path} $${rollback_path} && \ + cat $${migrate_path} >> ./src/Migrations/migrate.sql && \ + echo "Review the generated C# and SQL files and make necessary adjusments consistenty across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." +.PHONY : migration + +# https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#command-line-tools +# update-database : ## Update the database to the latest database schema +# dotnet ef \ +# --project ./src/Metabase.csproj \ +# database \ +# update +# .PHONY : update-database list-keys : ## List GnuPG keys gpg --list-keys diff --git a/backend/src/Migrations/current b/backend/src/Migrations/current deleted file mode 100644 index de443e87..00000000 --- a/backend/src/Migrations/current +++ /dev/null @@ -1 +0,0 @@ -20250907130041_AddApproverIdToResponseApproval diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql new file mode 100644 index 00000000..99796642 --- /dev/null +++ b/backend/src/Migrations/migrate.sql @@ -0,0 +1,1337 @@ +CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( + "MigrationId" character varying(150) NOT NULL, + "ProductVersion" character varying(32) NOT NULL, + CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY ("MigrationId") +); + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20211019125407_InitialCreate') THEN + +CREATE SCHEMA IF NOT EXISTS database; + +CREATE TYPE public.data_kind AS ENUM ('calorimetric_data', 'hygrothermal_data', 'optical_data', 'photovoltaic_data'); +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +CREATE TABLE database.calorimetric_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "GValues" double precision[] NOT NULL, + "UValues" double precision[] NOT NULL, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text NULL, + "Description" text NULL, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp without time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_calorimetric_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database.hygrothermal_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text NULL, + "Description" text NULL, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp without time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_hygrothermal_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database.optical_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "NearnormalHemisphericalVisibleTransmittances" double precision[] NOT NULL, + "NearnormalHemisphericalVisibleReflectances" double precision[] NOT NULL, + "NearnormalHemisphericalSolarTransmittances" double precision[] NOT NULL, + "NearnormalHemisphericalSolarReflectances" double precision[] NOT NULL, + "InfraredEmittances" double precision[] NOT NULL, + "ColorRenderingIndices" double precision[] NOT NULL, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text NULL, + "Description" text NULL, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp without time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_optical_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database.photovoltaic_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text NULL, + "Description" text NULL, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp without time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_photovoltaic_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database."calorimetric_data_Approvals" ( + "CalorimetricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Timestamp" timestamp without time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "ApproverId" uuid NOT NULL, + CONSTRAINT "PK_calorimetric_data_Approvals" PRIMARY KEY ("CalorimetricDataId", "Id"), + CONSTRAINT "FK_calorimetric_data_Approvals_calorimetric_data_CalorimetricD~" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."calorimetric_data_Arguments" ( + "AppliedMethodCalorimetricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_calorimetric_data_Arguments" PRIMARY KEY ("AppliedMethodCalorimetricDataId", "Id"), + CONSTRAINT "FK_calorimetric_data_Arguments_calorimetric_data_AppliedMethod~" FOREIGN KEY ("AppliedMethodCalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."calorimetric_data_Sources" ( + "AppliedMethodCalorimetricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp without time zone NOT NULL, + "Value_DataKind" data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_calorimetric_data_Sources" PRIMARY KEY ("AppliedMethodCalorimetricDataId", "Id"), + CONSTRAINT "FK_calorimetric_data_Sources_calorimetric_data_AppliedMethodCa~" FOREIGN KEY ("AppliedMethodCalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."hygrothermal_data_Approvals" ( + "HygrothermalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Timestamp" timestamp without time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "ApproverId" uuid NOT NULL, + CONSTRAINT "PK_hygrothermal_data_Approvals" PRIMARY KEY ("HygrothermalDataId", "Id"), + CONSTRAINT "FK_hygrothermal_data_Approvals_hygrothermal_data_HygrothermalD~" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."hygrothermal_data_Arguments" ( + "AppliedMethodHygrothermalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_hygrothermal_data_Arguments" PRIMARY KEY ("AppliedMethodHygrothermalDataId", "Id"), + CONSTRAINT "FK_hygrothermal_data_Arguments_hygrothermal_data_AppliedMethod~" FOREIGN KEY ("AppliedMethodHygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."hygrothermal_data_Sources" ( + "AppliedMethodHygrothermalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp without time zone NOT NULL, + "Value_DataKind" data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_hygrothermal_data_Sources" PRIMARY KEY ("AppliedMethodHygrothermalDataId", "Id"), + CONSTRAINT "FK_hygrothermal_data_Sources_hygrothermal_data_AppliedMethodHy~" FOREIGN KEY ("AppliedMethodHygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."CielabColor" ( + "OpticalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "LStar" double precision NOT NULL, + "AStar" double precision NOT NULL, + "BStar" double precision NOT NULL, + CONSTRAINT "PK_CielabColor" PRIMARY KEY ("OpticalDataId", "Id"), + CONSTRAINT "FK_CielabColor_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."optical_data_Approvals" ( + "OpticalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Timestamp" timestamp without time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "ApproverId" uuid NOT NULL, + CONSTRAINT "PK_optical_data_Approvals" PRIMARY KEY ("OpticalDataId", "Id"), + CONSTRAINT "FK_optical_data_Approvals_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."optical_data_Arguments" ( + "AppliedMethodOpticalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_optical_data_Arguments" PRIMARY KEY ("AppliedMethodOpticalDataId", "Id"), + CONSTRAINT "FK_optical_data_Arguments_optical_data_AppliedMethodOpticalDat~" FOREIGN KEY ("AppliedMethodOpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."optical_data_Sources" ( + "AppliedMethodOpticalDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp without time zone NOT NULL, + "Value_DataKind" data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_optical_data_Sources" PRIMARY KEY ("AppliedMethodOpticalDataId", "Id"), + CONSTRAINT "FK_optical_data_Sources_optical_data_AppliedMethodOpticalDataId" FOREIGN KEY ("AppliedMethodOpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database.get_https_resource ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Description" text NOT NULL, + "HashValue" text NOT NULL, + "DataFormatId" uuid NOT NULL, + "CalorimetricDataId" uuid NULL, + "HygrothermalDataId" uuid NULL, + "OpticalDataId" uuid NULL, + "PhotovoltaicDataId" uuid NULL, + "ParentId" uuid NULL, + "AppliedConversionMethod_MethodId" uuid NULL, + "AppliedConversionMethod_SourceName" text NULL, + CONSTRAINT "PK_get_https_resource" PRIMARY KEY ("Id"), + CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE RESTRICT, + CONSTRAINT "FK_get_https_resource_get_https_resource_ParentId" FOREIGN KEY ("ParentId") REFERENCES database.get_https_resource ("Id") ON DELETE RESTRICT, + CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE RESTRICT, + CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE RESTRICT, + CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE RESTRICT +); + +CREATE TABLE database."photovoltaic_data_Approvals" ( + "PhotovoltaicDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Timestamp" timestamp without time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "ApproverId" uuid NOT NULL, + CONSTRAINT "PK_photovoltaic_data_Approvals" PRIMARY KEY ("PhotovoltaicDataId", "Id"), + CONSTRAINT "FK_photovoltaic_data_Approvals_photovoltaic_data_PhotovoltaicD~" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."photovoltaic_data_Arguments" ( + "AppliedMethodPhotovoltaicDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_photovoltaic_data_Arguments" PRIMARY KEY ("AppliedMethodPhotovoltaicDataId", "Id"), + CONSTRAINT "FK_photovoltaic_data_Arguments_photovoltaic_data_AppliedMethod~" FOREIGN KEY ("AppliedMethodPhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."photovoltaic_data_Sources" ( + "AppliedMethodPhotovoltaicDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp without time zone NOT NULL, + "Value_DataKind" data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_photovoltaic_data_Sources" PRIMARY KEY ("AppliedMethodPhotovoltaicDataId", "Id"), + CONSTRAINT "FK_photovoltaic_data_Sources_photovoltaic_data_AppliedMethodPh~" FOREIGN KEY ("AppliedMethodPhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."FileMetaInformation" ( + "GetHttpsResourceId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Path" text[] NOT NULL, + "DataFormatId" uuid NOT NULL, + CONSTRAINT "PK_FileMetaInformation" PRIMARY KEY ("GetHttpsResourceId", "Id"), + CONSTRAINT "FK_FileMetaInformation_get_https_resource_GetHttpsResourceId" FOREIGN KEY ("GetHttpsResourceId") REFERENCES database.get_https_resource ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."get_https_resource_Arguments" ( + "ToTreeVertexAppliedConversionMethodGetHttpsResourceId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_get_https_resource_Arguments" PRIMARY KEY ("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"), + CONSTRAINT "FK_get_https_resource_Arguments_get_https_resource_ToTreeVerte~" FOREIGN KEY ("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") REFERENCES database.get_https_resource ("Id") ON DELETE CASCADE +); + +CREATE INDEX "IX_get_https_resource_CalorimetricDataId" ON database.get_https_resource ("CalorimetricDataId"); + +CREATE INDEX "IX_get_https_resource_HygrothermalDataId" ON database.get_https_resource ("HygrothermalDataId"); + +CREATE INDEX "IX_get_https_resource_OpticalDataId" ON database.get_https_resource ("OpticalDataId"); + +CREATE INDEX "IX_get_https_resource_ParentId" ON database.get_https_resource ("ParentId"); + +CREATE INDEX "IX_get_https_resource_PhotovoltaicDataId" ON database.get_https_resource ("PhotovoltaicDataId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20211019125407_InitialCreate', '5.0.10'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20230126153325_RenameXminToVersion') THEN + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataTimestamp" TYPE timestamp with time zone; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Timestamp" TYPE timestamp with time zone; + +ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" TYPE timestamp with time zone; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230126153325_RenameXminToVersion', '7.0.2'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20230210135413_AddDataProtectionKeys') THEN + +CREATE TABLE database."DataProtectionKeys" ( + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "FriendlyName" text NULL, + "Xml" text NULL, + CONSTRAINT "PK_DataProtectionKeys" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230210135413_AddDataProtectionKeys', '7.0.2'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20230420143838_OpenIddict') THEN + +CREATE TABLE database."OpenIddictApplications" ( + "Id" text NOT NULL, + "ClientId" character varying(100), + "ClientSecret" text, + "ConcurrencyToken" character varying(50), + "ConsentType" character varying(50), + "DisplayName" text, + "DisplayNames" text, + "Permissions" text, + "PostLogoutRedirectUris" text, + "Properties" text, + "RedirectUris" text, + "Requirements" text, + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictApplications" PRIMARY KEY ("Id") +); + +CREATE TABLE database."OpenIddictScopes" ( + "Id" text NOT NULL, + "ConcurrencyToken" character varying(50), + "Description" text, + "Descriptions" text, + "DisplayName" text, + "DisplayNames" text, + "Name" character varying(200), + "Properties" text, + "Resources" text, + CONSTRAINT "PK_OpenIddictScopes" PRIMARY KEY ("Id") +); + +CREATE TABLE database."OpenIddictAuthorizations" ( + "Id" text NOT NULL, + "ApplicationId" text, + "ConcurrencyToken" character varying(50), + "CreationDate" timestamp with time zone, + "Properties" text, + "Scopes" text, + "Status" character varying(50), + "Subject" character varying(400), + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictAuthorizations" PRIMARY KEY ("Id"), + CONSTRAINT "FK_OpenIddictAuthorizations_OpenIddictApplications_Application~" FOREIGN KEY ("ApplicationId") REFERENCES database."OpenIddictApplications" ("Id") +); + +CREATE TABLE database."OpenIddictTokens" ( + "Id" text NOT NULL, + "ApplicationId" text, + "AuthorizationId" text, + "ConcurrencyToken" character varying(50), + "CreationDate" timestamp with time zone, + "ExpirationDate" timestamp with time zone, + "Payload" text, + "Properties" text, + "RedemptionDate" timestamp with time zone, + "ReferenceId" character varying(100), + "Status" character varying(50), + "Subject" character varying(400), + "Type" character varying(50), + CONSTRAINT "PK_OpenIddictTokens" PRIMARY KEY ("Id"), + CONSTRAINT "FK_OpenIddictTokens_OpenIddictApplications_ApplicationId" FOREIGN KEY ("ApplicationId") REFERENCES database."OpenIddictApplications" ("Id"), + CONSTRAINT "FK_OpenIddictTokens_OpenIddictAuthorizations_AuthorizationId" FOREIGN KEY ("AuthorizationId") REFERENCES database."OpenIddictAuthorizations" ("Id") +); + +CREATE UNIQUE INDEX "IX_OpenIddictApplications_ClientId" ON database."OpenIddictApplications" ("ClientId"); + +CREATE INDEX "IX_OpenIddictAuthorizations_ApplicationId_Status_Subject_Type" ON database."OpenIddictAuthorizations" ("ApplicationId", "Status", "Subject", "Type"); + +CREATE UNIQUE INDEX "IX_OpenIddictScopes_Name" ON database."OpenIddictScopes" ("Name"); + +CREATE INDEX "IX_OpenIddictTokens_ApplicationId_Status_Subject_Type" ON database."OpenIddictTokens" ("ApplicationId", "Status", "Subject", "Type"); + +CREATE INDEX "IX_OpenIddictTokens_AuthorizationId" ON database."OpenIddictTokens" ("AuthorizationId"); + +CREATE UNIQUE INDEX "IX_OpenIddictTokens_ReferenceId" ON database."OpenIddictTokens" ("ReferenceId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230420143838_OpenIddict', '8.0.6'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20230427133946_CreateUserTable') THEN + +CREATE TABLE database."user" ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Subject" text NOT NULL, + "Email" text NOT NULL, + "Name" text NOT NULL, + CONSTRAINT "PK_user" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20230427133946_CreateUserTable', '8.0.6'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240522120225_UpgradeOpenIddict') THEN + +ALTER TABLE database."user" DROP COLUMN "Email"; + +ALTER TABLE database."OpenIddictApplications" RENAME COLUMN "Type" TO "ClientType"; + +ALTER TABLE database."OpenIddictApplications" ADD "ApplicationType" character varying(50); + +ALTER TABLE database."OpenIddictApplications" ADD "JsonWebKeySet" text; + +ALTER TABLE database."OpenIddictApplications" ADD "Settings" text; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240522120225_UpgradeOpenIddict', '8.0.6'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240709144935_AddStatementToDataApproval') THEN + +CREATE TYPE public.standardizer AS ENUM ('aerc', 'agi', 'ashrae', 'breeam', 'bs', 'bsi', 'cen', 'cie', 'dgnb', 'din', 'dvwg', 'iec', 'ies', 'ift', 'iso', 'jis', 'leed', 'nfrc', 'riba', 'ul', 'unece', 'vdi', 'vff', 'well'); +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."optical_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Standard_Year" integer; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Abstract" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_ArXiv" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Authors" text[]; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Doi" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Section" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Title" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_Urn" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Publication_WebAddress" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Abstract" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Locator" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_MainNumber" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_Prefix" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Numeration_Suffix" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Section" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Standardizers" standardizer[]; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Title" text; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Standard_Year" integer; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240709144935_AddStatementToDataApproval', '8.0.6'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240722133619_MoveEnumTypesToMetabaseSchema') THEN + +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE TYPE database.data_kind AS ENUM ('calorimetric_data', 'hygrothermal_data', 'optical_data', 'photovoltaic_data'); +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE TYPE database.standardizer AS ENUM ('aerc', 'agi', 'ashrae', 'breeam', 'bs', 'bsi', 'cen', 'cie', 'dgnb', 'din', 'dvwg', 'iec', 'ies', 'ift', 'iso', 'jis', 'leed', 'nfrc', 'riba', 'ul', 'unece', 'vdi', 'vff', 'well'); + +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE database."photovoltaic_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."photovoltaic_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."optical_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."hygrothermal_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."hygrothermal_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +ALTER TABLE database."calorimetric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind USING "Value_DataKind"::text::database.data_kind; + +ALTER TABLE database."calorimetric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[] USING "Standard_Standardizers"::text::database.standardizer[]; + +DROP TYPE public.data_kind; +DROP TYPE public.standardizer; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240722133619_MoveEnumTypesToMetabaseSchema', '8.0.6'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20240917073732_AddGeometricData') THEN + +ALTER TYPE database.data_kind ADD VALUE 'geometric_data'; + +ALTER TABLE database.get_https_resource ADD "GeometricDataId" uuid; + +CREATE TABLE database.geometric_data ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "Thicknesses" double precision[] NOT NULL, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text, + "Description" text, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + CONSTRAINT "PK_geometric_data" PRIMARY KEY ("Id") +); + +CREATE TABLE database."geometric_data_Approvals" ( + "GeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "ApproverId" uuid NOT NULL, + "Timestamp" timestamp with time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Response" text NOT NULL, + "Publication_Authors" text[], + "Publication_Doi" text, + "Publication_ArXiv" text, + "Publication_Urn" text, + "Publication_WebAddress" text, + "Publication_Title" text, + "Publication_Abstract" text, + "Publication_Section" text, + "Standard_Year" integer, + "Standard_Numeration_Prefix" text, + "Standard_Numeration_MainNumber" text, + "Standard_Numeration_Suffix" text, + "Standard_Standardizers" database.standardizer[], + "Standard_Locator" text, + "Standard_Title" text, + "Standard_Abstract" text, + "Standard_Section" text, + CONSTRAINT "PK_geometric_data_Approvals" PRIMARY KEY ("GeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Approvals_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."geometric_data_Arguments" ( + "AppliedMethodGeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_geometric_data_Arguments" PRIMARY KEY ("AppliedMethodGeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Arguments_geometric_data_AppliedMethodGeomet~" FOREIGN KEY ("AppliedMethodGeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE TABLE database."geometric_data_Sources" ( + "AppliedMethodGeometricDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp with time zone NOT NULL, + "Value_DataKind" database.data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_geometric_data_Sources" PRIMARY KEY ("AppliedMethodGeometricDataId", "Id"), + CONSTRAINT "FK_geometric_data_Sources_geometric_data_AppliedMethodGeometri~" FOREIGN KEY ("AppliedMethodGeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE +); + +CREATE INDEX "IX_get_https_resource_GeometricDataId" ON database.get_https_resource ("GeometricDataId"); + +ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20240917073732_AddGeometricData', '8.0.8'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250129163501_UpgradeToNet9') THEN + +ALTER TABLE database."geometric_data_Sources" ALTER COLUMN "Value_DataKind" TYPE database.data_kind; + +ALTER TABLE database."geometric_data_Approvals" ALTER COLUMN "Standard_Standardizers" TYPE database.standardizer[]; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250129163501_UpgradeToNet9', '9.0.1'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250213155333_AddTypeSubtypeAndCoatedSideToData') THEN + +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE TYPE database.optical_component_subtype AS ENUM ('acid_etched_glass', 'applied_film', 'cellular_shade', 'chromogenic', 'coated', 'coating', 'diffusing_shade', 'embedded_coating', 'film', 'fritted_glass', 'interlayer', 'laminate', 'monolithic', 'perforated_screen', 'pleated_shade', 'roller_shade', 'roman_shade', 'sandblasted_glass', 'shade_material', 'venetian_blind', 'vertical_louver', 'woven_shade'); +DO $X$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; +END $X$; + +CREATE TYPE database.optical_component_type AS ENUM ('glazing', 'shading'); + +ALTER TABLE database.optical_data ADD "CoatedSide" database.coated_side; + +ALTER TABLE database.optical_data ADD "Subtype" database.optical_component_subtype; + +ALTER TABLE database.optical_data ADD "Type" database.optical_component_type; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250213155333_AddTypeSubtypeAndCoatedSideToData', '9.0.1'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250218172859_MakeDescriptionOptional') THEN + +ALTER TABLE database.get_https_resource ALTER COLUMN "Description" DROP NOT NULL; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250218172859_MakeDescriptionOptional', '9.0.1'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250331141152_AddAccessRights') THEN + +ALTER TABLE database.photovoltaic_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Query" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Response" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Signature" text; + +ALTER TABLE database.photovoltaic_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.optical_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.optical_data ADD "Approval_Query" text; + +ALTER TABLE database.optical_data ADD "Approval_Response" text; + +ALTER TABLE database.optical_data ADD "Approval_Signature" text; + +ALTER TABLE database.optical_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.hygrothermal_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Query" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Response" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Signature" text; + +ALTER TABLE database.hygrothermal_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.geometric_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.geometric_data ADD "Approval_Query" text; + +ALTER TABLE database.geometric_data ADD "Approval_Response" text; + +ALTER TABLE database.geometric_data ADD "Approval_Signature" text; + +ALTER TABLE database.geometric_data ADD "Approval_Timestamp" timestamp with time zone; + +ALTER TABLE database.calorimetric_data ADD "Approval_KeyFingerprint" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Query" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Response" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Signature" text; + +ALTER TABLE database.calorimetric_data ADD "Approval_Timestamp" timestamp with time zone; + +CREATE TABLE database.access_rights ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "InstitutionId" uuid NOT NULL, + "AllowedUserCount" bigint, + "AllowedDatasetsPerTime" bigint, + "Period" interval NOT NULL, + "UserAlreadyAccessed" uuid[] NOT NULL, + CONSTRAINT "PK_access_rights" PRIMARY KEY ("Id") +); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250331141152_AddAccessRights', '9.0.5'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250527144412_AddDataAccessRights') THEN + +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedApplications" text[]; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + +ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedUserAndQuantity" jsonb; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250527144412_AddDataAccessRights', '9.0.5'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250527150325_MoveInstitutionAccessRights') THEN + +ALTER TABLE database.access_rights DROP CONSTRAINT "PK_access_rights"; + +ALTER TABLE database.access_rights RENAME TO institution_access_rights; + +ALTER TABLE database.institution_access_rights ADD CONSTRAINT "PK_institution_access_rights" PRIMARY KEY ("Id"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250527150325_MoveInstitutionAccessRights', '9.0.5'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250530151605_ModelReferenceProperly') THEN + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Year" TO "Statement_Standard_Year"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Title" TO "Statement_Standard_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Standardizers" TO "Statement_Standard_Standardizers"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Section" TO "Statement_Standard_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_Suffix" TO "Statement_Standard_Numeration_Suffix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_Prefix" TO "Statement_Standard_Numeration_Prefix"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Numeration_MainNumber" TO "Statement_Standard_Numeration_MainNumber"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Locator" TO "Statement_Standard_Locator"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Standard_Abstract" TO "Statement_Standard_Abstract"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_WebAddress" TO "Statement_Publication_WebAddress"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Urn" TO "Statement_Publication_Urn"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Title" TO "Statement_Publication_Title"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Section" TO "Statement_Publication_Section"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Doi" TO "Statement_Publication_Doi"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Authors" TO "Statement_Publication_Authors"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_ArXiv" TO "Statement_Publication_ArXiv"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Publication_Abstract" TO "Statement_Publication_Abstract"; + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."optical_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."geometric_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Statement_Exists" boolean NOT NULL DEFAULT TRUE; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250530151605_ModelReferenceProperly', '9.0.5'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250530155318_AddRequiredPropertyExistsToPublication') THEN + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."optical_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."geometric_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Statement_Publication_Exists" boolean; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250530155318_AddRequiredPropertyExistsToPublication', '9.0.5'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250725172127_UpgradeOpenIddictToVersion7') THEN + +ALTER TABLE database."OpenIddictTokens" ALTER COLUMN "Type" TYPE character varying(150); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250725172127_UpgradeOpenIddictToVersion7', '9.0.7'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250731163636_MakeInstitutionIdOfAccessRightsUnique') THEN + +CREATE UNIQUE INDEX "IX_institution_access_rights_InstitutionId" ON database.institution_access_rights ("InstitutionId"); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250731163636_MakeInstitutionIdOfAccessRightsUnique', '9.0.7'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250812171810_AddQueryVariablesToApprovals') THEN + +ALTER TABLE database."photovoltaic_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.photovoltaic_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."optical_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.optical_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."hygrothermal_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.hygrothermal_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."geometric_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.geometric_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +ALTER TABLE database."calorimetric_data_Approvals" ADD "Variables" jsonb NOT NULL DEFAULT ('{}'); + +ALTER TABLE database.calorimetric_data ADD "Approval_Variables" jsonb DEFAULT ('{}'); + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250812171810_AddQueryVariablesToApprovals', '9.0.8'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250902122837_RenameResponsePropertyOfApprovalsToMessage') THEN + +ALTER TABLE database."photovoltaic_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.photovoltaic_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."optical_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.optical_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."hygrothermal_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.hygrothermal_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."geometric_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.geometric_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +ALTER TABLE database."calorimetric_data_Approvals" RENAME COLUMN "Response" TO "Message"; + +ALTER TABLE database.calorimetric_data RENAME COLUMN "Approval_Response" TO "Approval_Message"; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250902122837_RenameResponsePropertyOfApprovalsToMessage', '9.0.8'); + + END IF; +END $EF$; + +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20250907130041_AddApproverIdToResponseApproval') THEN + +ALTER TABLE database.photovoltaic_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.optical_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.hygrothermal_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.geometric_data ADD "Approval_ApproverId" uuid; + +ALTER TABLE database.calorimetric_data ADD "Approval_ApproverId" uuid; + +INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") +VALUES ('20250907130041_AddApproverIdToResponseApproval', '9.0.8'); + + END IF; +END $EF$; + +COMMIT; diff --git a/backend/src/Migrations/previous b/backend/src/Migrations/previous deleted file mode 100644 index d627d4ba..00000000 --- a/backend/src/Migrations/previous +++ /dev/null @@ -1 +0,0 @@ -20250902122837_RenameResponsePropertyOfApprovalsToMessage From e074020712b3b40859d1b89f88f046e429b82498 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Oct 2025 13:52:05 +0200 Subject: [PATCH 472/878] Execute GnuPG within a Docker image to control the version (in older versions for example some elliptic curves may not be available) --- Makefile | 91 ++++++++++++++++++++---------------- backend/Dockerfile-bootstrap | 21 +++++++++ 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index 229dfc1f..2704b777 100644 --- a/Makefile +++ b/Makefile @@ -62,6 +62,17 @@ build : check pull ## Build images --build-arg USER_ID=$(shell id --user) .PHONY : build +build-bootstrap : ## Build the bootstrap image + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + --tag ${NAME}_bootstrap \ + --file ./backend/Dockerfile-bootstrap \ + ./backend +.PHONY : build-bootstrap + bake : ## Print docker-compose file equivalent bake file COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ @@ -321,55 +332,57 @@ prepare-release : ## Prepare release .PHONY : prepare-release gpg : COMMENT = -gpg : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make NAME="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` - gpg \ - --quick-generate-key \ - --pinentry-mode loopback \ - --batch \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - "${NAME} (${COMMENT}) <${EMAIL}>" \ - ed25519 \ - sign \ - never; \ - fingerprint=$$(gpg \ - --list-secret-keys \ - --with-colons \ - --keyid-format=long \ - ${EMAIL} \ - | grep \ - --before=3 \ - "${NAME} (${COMMENT}) <${EMAIL}>" \ - | awk -F: '$$1=="fpr" {printf $$10; exit}'); \ - echo Fingerprint: $$fingerprint; \ - mkdir --parents \ - ./backend/src/gpg-keys; \ - gpg \ - --armor \ - --export-secret-keys $$fingerprint \ - > ./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} +gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -ceux " \ + gpg \ + --quick-generate-key \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + ed25519 \ + sign \ + never; \ + fingerprint=$$(gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + | awk -F: '$$1=="fpr" {printf $$10; exit}'); \ + echo Fingerprint: $$fingerprint; \ + mkdir --parents \ + ./backend/src/gpg-keys; \ + gpg \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + --armor \ + --export-secret-keys $$fingerprint \ + > /app/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} \ + " # --------------------- # # Generate Certificates # # --------------------- # # TODO Pass passwords in a more secure way! -jwt-certificates : ## Create JWT encryption and signing certificates if necessary - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - --tag ${NAME}_bootstrap \ - --file ./backend/Dockerfile-bootstrap \ - ./backend +jwt-certificates : build-bootstrap ## Create JWT encryption and signing certificates if necessary docker run \ --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/backend",target=/app \ ${NAME}_bootstrap \ - bash -cx " \ + bash -ceux " \ dotnet-script \ - ./create-certificates.csx \ + /app/create-certificates.csx \ -- \ ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ @@ -395,7 +408,7 @@ generate-certificate-authority : ## Generate certificate authority ECDSA private --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ nginx:1.27-bookworm \ - bash -cx " \ + bash -ceux " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ -genkey \ @@ -503,7 +516,7 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ nginx:1.27-bookworm \ - bash -cx " \ + bash -ceux " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ -genkey \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index d2a30347..ddc019d2 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -19,6 +19,27 @@ RUN \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me +# Install the latest GnuPG from the official GnuPG repository +RUN set -eux && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + curl \ + gnupg2 && \ + curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2 && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + ENV HOME=/home/me RUN \ mkdir --parents ${HOME} && \ From 595d1c56d5060418aa7397fc6c14f71718ab24bc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Oct 2025 16:28:50 +0200 Subject: [PATCH 473/878] Fix GnuPG version for reproducibility (and to make version changes make Docker images rebuild everywhere) --- backend/Dockerfile | 23 +++++++++++++++++++---- backend/Dockerfile-bootstrap | 17 +++++++++++++---- backend/Dockerfile-production | 22 ++++++++++++++++------ 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 70a8c094..c96e9579 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -91,16 +91,31 @@ COPY \ RUN \ update-ca-certificates -# Install the latest GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository +# For available versions see https://repos.gnupg.org/deb/gnupg/ +ENV GNUPG_VERSION=2.4.8-2 RUN set -eux && \ - curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ - dirmngr \ + curl \ gnupg2 && \ + curl \ + --silent \ + --show-error \ + https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ + > /etc/apt/sources.list.d/gnupg.list && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2=${GNUPG_VERSION} && \ rm \ --recursive \ --force \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index ddc019d2..f598cc57 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -19,22 +19,31 @@ RUN \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me -# Install the latest GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository +# For available versions see https://repos.gnupg.org/deb/gnupg/ +ENV GNUPG_VERSION=2.4.8-2 RUN set -eux && \ + version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ curl \ gnupg2 && \ - curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + curl \ + --silent \ + --show-error \ + https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ + > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2 && \ + gnupg2=${GNUPG_VERSION} && \ rm \ --recursive \ --force \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index be0ae1b8..509a59ce 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -67,21 +67,31 @@ RUN \ --force \ /var/lib/apt/lists/* -# Install the latest GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository +# For available versions see https://repos.gnupg.org/deb/gnupg/ +ENV GNUPG_VERSION=2.4.8-2 RUN set -eux && \ + version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ - dirmngr \ - gnupg && \ - curl --silent --show-error https://repos.gnupg.org/deb/gnupg/bookworm/gnupg-signing-key.gpg | gpg --dearmor > /usr/share/keyrings/gnupg-keyring.gpg && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/bookworm/ bookworm main" > /etc/apt/sources.list.d/gnupg.list && \ + curl \ + gnupg2 && \ + curl \ + --silent \ + --show-error \ + https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ + > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ - gnupg2 && \ + dirmngr \ + gnupg2=${GNUPG_VERSION} && \ rm \ --recursive \ --force \ From 19ff3537483369a5fe680f1577d26ae0f77be522 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Oct 2025 18:56:39 +0200 Subject: [PATCH 474/878] Export GnuPG key to a file with the name .gpg instead of a custom name --- .env.production.sample | 3 +-- .env.sample | 3 +-- .env.staging.sample | 3 +-- Makefile | 30 +++++++++++++++--------------- README.md | 28 ++++++++++++---------------- backend/src/AppSettings.cs | 1 - docker-compose.production.yml | 1 - docker-compose.yml | 1 - 8 files changed, 30 insertions(+), 40 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index e0b77f8f..36c281e4 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -13,9 +13,8 @@ OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg -GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.sample b/.env.sample index 4a69fd70..4e93730a 100644 --- a/.env.sample +++ b/.env.sample @@ -20,9 +20,8 @@ CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy -GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg -GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password diff --git a/.env.staging.sample b/.env.staging.sample index f332fdf3..2a138f14 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -13,9 +13,8 @@ OPEN_ID_CONNECT_CLIENT_SECRET= VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FILE_NAME=private.gpg -GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/Makefile b/Makefile index 2704b777..5595877b 100644 --- a/Makefile +++ b/Makefile @@ -338,7 +338,7 @@ gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/backend",target=/app \ ${NAME}_bootstrap \ - bash -ceux " \ + bash -ceuxo pipefail " \ gpg \ --quick-generate-key \ --batch \ @@ -347,26 +347,26 @@ gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_ '${PERSON} (${COMMENT}) <${EMAIL}>' \ ed25519 \ sign \ - never; \ - fingerprint=$$(gpg \ - --list-secret-keys \ - --with-colons \ - --keyid-format=long \ - ${EMAIL} \ - | grep \ - --before=3 \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - | awk -F: '$$1=="fpr" {printf $$10; exit}'); \ - echo Fingerprint: $$fingerprint; \ + never && \ + fingerprint=\$$(gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ + echo fingerprint=\$${fingerprint} && \ mkdir --parents \ - ./backend/src/gpg-keys; \ + ./backend/src/gpg-keys && \ gpg \ --batch \ --pinentry-mode loopback \ --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ --armor \ - --export-secret-keys $$fingerprint \ - > /app/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} \ + --export-secret-keys \$${fingerprint} \ + > /app/src/gpg-keys/\$${fingerprint}.gpg \ " # --------------------- # diff --git a/README.md b/README.md index c77b073f..297ebf7b 100644 --- a/README.md +++ b/README.md @@ -52,17 +52,14 @@ If you have a question for which you don't find the answer in this repository, p generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `make jwt-certificates`. -1. Set the values of the variables `GNUPG_SECRET_SIGNING_KEY_FILE_NAME` and - `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` in the `./.env` file. Generate and - export a GnuPG key with the passphrase - `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` to the file - `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FILE_NAME}` by running - `make NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with your - information filled in. For example - `make NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de gpg`. - Copy the key's fingerprint which is output by the command and set it as the - value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` - file. +1. Generate and export a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the + file `./backend/src/gpg-keys/.gpg` by running `make + NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information + filled in. For example `make NAME="Anna Smith" COMMENT=first + EMAIL=anna.smith@fraunhofer.de gpg`. Copy the key's fingerprint which is + output by the command and set it as the value of the + `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see @@ -267,11 +264,10 @@ and the pages following it. database as a client of the metabase acting as identity provider (the client secret is given when registering an OpenId Connect client at the metabase); - - `GNUPG_SECRET_SIGNING_KEY_FILE_NAME`, - `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE`, and - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` are the file name of an - exported GnuPG secret key for signing, its passphrase, and its - fingerprint that lives in the directory `./backend/src/gpg-keys/`; + - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` and + `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase + of the exported GnuPG secret key for signing in the file + `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`; - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to encrypt and sign JSON web tokens (JWT) used by OpenId Connect; diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index da26d08b..92d53a34 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -46,7 +46,6 @@ public sealed class OpenIdConnectClientSettings public sealed class GnupgSecretSigningKeySettings { - public string FileName { get; private set; } = ""; public string Passphrase { get; private set; } = ""; public string Fingerprint { get; private set; } = ""; } diff --git a/docker-compose.production.yml b/docker-compose.production.yml index fee99a50..c79c0f00 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -78,7 +78,6 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Filename=${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} # The value `/dev/pts/0` is the output of running `tty` within `shellb` diff --git a/docker-compose.yml b/docker-compose.yml index 25cb9bcf..25333c0b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,7 +118,6 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Filename=${GNUPG_SECRET_SIGNING_KEY_FILE_NAME} - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables From 9b47637ac55d052f77c4c98044aef0eb234bf3dc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 14 Oct 2025 21:49:36 +0200 Subject: [PATCH 475/878] Make `FileName` of GnuPG key available in app settings as a constructed value from fingerprint (and do minor improvements) --- Makefile | 1 + backend/src/AppSettings.cs | 3 +++ docker-compose.production.yml | 2 +- docker-compose.yml | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5595877b..38e9fc01 100644 --- a/Makefile +++ b/Makefile @@ -331,6 +331,7 @@ prepare-release : ## Prepare release make prepare-release .PHONY : prepare-release +# Keep file name .gpg in sync with the one in `AppSettings.cs` gpg : COMMENT = gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` docker run \ diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 92d53a34..4369ce81 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -46,6 +46,9 @@ public sealed class OpenIdConnectClientSettings public sealed class GnupgSecretSigningKeySettings { + // Keep file name {FINGERPRINT}.gpg in sync with the one in the GNU + // Make target `gpg` in the `Makefile` + public string FileName { get => $"{Fingerprint}.gpg"; } public string Passphrase { get; private set; } = ""; public string Fingerprint { get; private set; } = ""; } diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c79c0f00..7df75373 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -78,8 +78,8 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} + - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - GPG_TTY=/dev/pts/0 diff --git a/docker-compose.yml b/docker-compose.yml index 25333c0b..1718928a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,8 +118,8 @@ services: - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - XBASE_Email__SmtpHost=${SMTP_HOST} - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} + - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose From 084de3dce4c087d9b0819af1791bc72871f2a603 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 11:08:28 +0200 Subject: [PATCH 476/878] Remove legacy target to prepare releases (this is not necessary anymore) --- Makefile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Makefile b/Makefile index 38e9fc01..7eb66aba 100644 --- a/Makefile +++ b/Makefile @@ -324,13 +324,6 @@ end-maintenance : ## End maintenance rm ./nginx/html/maintenance.html .PHONY : begin-maintenance -prepare-release : ## Prepare release - ${docker_compose} run \ - --user $(shell id --user):$(shell id --group) \ - backend \ - make prepare-release -.PHONY : prepare-release - # Keep file name .gpg in sync with the one in `AppSettings.cs` gpg : COMMENT = gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` From 9aab35f3a081386f1ab25239850eea69ffaf5f1c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 11:09:32 +0200 Subject: [PATCH 477/878] Add GNU Make target `gpg` to generate GPG keys on production machines --- Makefile.production | 47 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Makefile.production b/Makefile.production index ce8c47ce..320fe547 100644 --- a/Makefile.production +++ b/Makefile.production @@ -415,8 +415,7 @@ run-tests : COMMAND = true run-tests : execb ## Run tests .PHONY : run-tests -# TODO Pass passwords in a more secure way! -jwt-certificates : ## Create JWT encryption and signing certificates if necessary +build-bootstrap : ## Build the bootstrap image DOCKER_BUILDKIT=1 \ docker build \ --pull \ @@ -425,12 +424,16 @@ jwt-certificates : ## Create JWT encryption and signing certificates if necessar --tag ${NAME}_bootstrap \ --file ./backend/Dockerfile-bootstrap \ ./backend +.PHONY : build-bootstrap + +# TODO Pass passwords in a more secure way! +jwt-certificates : build-bootstrap ## Create JWT encryption and signing certificates if necessary docker run \ --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/backend",target=/app \ ${NAME}_bootstrap \ - bash -cx " \ + bash -ceux " \ dotnet-script \ ./create-certificates.csx \ -- \ @@ -438,3 +441,41 @@ jwt-certificates : ## Create JWT encryption and signing certificates if necessar ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ " .PHONY : jwt-certificates + +gpg : COMMENT = +gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -ceuxo pipefail " \ + gpg \ + --quick-generate-key \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + ed25519 \ + sign \ + never && \ + fingerprint=\$$(gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ + echo fingerprint=\$${fingerprint} && \ + mkdir --parents \ + ./backend/src/gpg-keys && \ + gpg \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + --armor \ + --export-secret-keys \$${fingerprint} \ + > /app/src/gpg-keys/\$${fingerprint}.gpg \ + " From 0e7f0695ce8a165d6e2acd6ab557007e638b6333 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 11:34:00 +0200 Subject: [PATCH 478/878] Elaborate on how to create GnuPG keys manually --- README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 297ebf7b..fe6792c5 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,33 @@ If you have a question for which you don't find the answer in this repository, p `make jwt-certificates`. 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `make - NAME=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information - filled in. For example `make NAME="Anna Smith" COMMENT=first - EMAIL=anna.smith@fraunhofer.de gpg`. Copy the key's fingerprint which is - output by the command and set it as the value of the + file `./backend/src/gpg-keys/.gpg` by running `make + PERSON=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information + filled in, for example, `make NAME="Anna Smith" COMMENT=first + EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which + is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. + + Instead of using the GNU Make target `gpg`, you may + 1. install [GnuPG](https://gnupg.org) as described on + [Download GnuPG](https://gnupg.org/download/index.html) or + [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), + 1. generate a GnuPG key by running `gpg --full-generate-key` and answering + the prompts, + 1. list the keys for which you have both a public and secret key by running + `gpg --list-secret-keys --keyid-format=long`, + 1. identify and copy the long form of the fingerprint of the key you have + just created, + 1. remember the key in the variable `fingerprint` by running + `fingerprint=` with `` replaced by the + copied fingerprint, + 1. create the directory to save the secret key to if it does not exist yet + by running `mkdir --parents ./backend/src/gpg-keys`, + 1. export the armored secret key to the file + `./backend/src/gpg-keys/${fingerprint}.gpg` by running + `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, + 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in + the `./.env` file to the remembered fingerprint in your favorite editor. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see From 10f28fa41407563052b3a522bf19cbfc35e15820 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 12:13:53 +0200 Subject: [PATCH 479/878] Improve GnuPG installation, in particular, check the key fingerprint to fend off man-in-the-middle attacks --- backend/Dockerfile | 17 ++++++++++++++++- backend/Dockerfile-bootstrap | 19 +++++++++++++++++-- backend/Dockerfile-production | 18 ++++++++++++++++-- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index c96e9579..c36922da 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -91,9 +91,14 @@ COPY \ RUN \ update-ca-certificates -# Install up-to-date GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository. # For available versions see https://repos.gnupg.org/deb/gnupg/ +# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# Keep the version in sync with the one in the other Dockerfiles of this +# repository and with the one of the `machine` repository specified in the +# `local.yml` Ansible script. ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ @@ -108,6 +113,16 @@ RUN set -eux && \ https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ + fingerprint=$(gpg \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ + if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ + fi && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index f598cc57..0e33afce 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -19,9 +19,14 @@ RUN \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me -# Install up-to-date GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository. # For available versions see https://repos.gnupg.org/deb/gnupg/ +# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# Keep the version in sync with the one in the other Dockerfiles of this +# repository and with the one of the `machine` repository specified in the +# `local.yml` Ansible script. ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ @@ -36,6 +41,16 @@ RUN set -eux && \ https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ + fingerprint=$(gpg \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ + if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ + fi && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ @@ -48,7 +63,7 @@ RUN set -eux && \ --recursive \ --force \ /var/lib/apt/lists/* - +WORKDIR /app ENV HOME=/home/me RUN \ mkdir --parents ${HOME} && \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 509a59ce..928fba43 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -67,9 +67,14 @@ RUN \ --force \ /var/lib/apt/lists/* -# Install up-to-date GnuPG from the official GnuPG repository +# Install up-to-date GnuPG from the official GnuPG repository. # For available versions see https://repos.gnupg.org/deb/gnupg/ +# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# Keep the version in sync with the one in the other Dockerfiles of this +# repository and with the one of the `machine` repository specified in the +# `local.yml` Ansible script. ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ apt-get update && \ @@ -84,6 +89,16 @@ RUN set -eux && \ https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ + fingerprint=$(gpg \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ + if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ + fi && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ @@ -96,7 +111,6 @@ RUN set -eux && \ --recursive \ --force \ /var/lib/apt/lists/* - WORKDIR /app COPY \ From d818f3fc752dedd7fbc9317bef13e58dd74e052c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 12:20:59 +0200 Subject: [PATCH 480/878] Suffix the GnuPG package with `2` for consistency --- backend/Dockerfile | 2 +- backend/Dockerfile-production | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index c36922da..ac7fa180 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -55,7 +55,7 @@ RUN \ curl \ editorconfig \ gawk \ - gnupg \ + gnupg2 \ lldb \ make \ # npm \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 928fba43..0e6d7f70 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -23,7 +23,7 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build # apt-get install \ # --assume-yes \ # --no-install-recommends \ -# gnupg && \ +# gnupg2 && \ # rm \ # --recursive \ # --force \ From e904eb4e410b48bb00e1fcb074ae920b105d0a0e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 14:19:31 +0200 Subject: [PATCH 481/878] Correct project path --- backend/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index d64cda80..33c7dd63 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -180,7 +180,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot old=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ echo old=$$old && \ dotnet ef \ - --project ./src/Metabase.csproj \ + --project ./src/Database.csproj \ migrations \ add \ ${NAME} && \ @@ -189,7 +189,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot migrate_path=./src/Migrations/migrate_from_$${old}_to_$${new}.sql && \ rollback_path=./src/Migrations/rollback_from_$${new}_to_$${old}.sql && \ dotnet ef \ - --project ./src/Metabase.csproj \ + --project ./src/Database.csproj \ migrations \ script \ --idempotent \ @@ -197,7 +197,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot $${new} \ --output $${migrate_path} && \ dotnet ef \ - --project ./src/Metabase.csproj \ + --project ./src/Database.csproj \ migrations \ script \ --idempotent \ @@ -212,7 +212,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot # https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#command-line-tools # update-database : ## Update the database to the latest database schema # dotnet ef \ -# --project ./src/Metabase.csproj \ +# --project ./src/Database.csproj \ # database \ # update # .PHONY : update-database From b37536b9ca9036b30cf1f785478b5889de4b0d7b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 14:20:48 +0200 Subject: [PATCH 482/878] Ensure that the database is up-to-date in the sense that all existing migrations have been applied --- backend/src/Program.cs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 6cb6b467..0d9f2b2f 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -1,6 +1,7 @@ using System; using System.Globalization; using System.IO; +using System.Linq; using System.Threading.Tasks; using Database.Data; using Database.Services; @@ -23,8 +24,8 @@ public static partial class LoggerExtensions [LoggerMessage( EventId = 0, Level = LogLevel.Error, - Message = "An error occurred creating and seeding the database.")] - public static partial void FailedToCreateAndSeedDatabase( + Message = "An error occurred creating or seeding the database.")] + public static partial void FailedToCreateOrSeedDatabase( this ILogger logger, // The first exception is implicitly taken care of as detailed in // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy @@ -56,8 +57,9 @@ string[] commandLineArguments startup.Configure(application); using (var scope = application.Services.CreateScope()) { + EnsureDatabaseIsUpToDate(scope.ServiceProvider); // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDb(scope.ServiceProvider); + await CreateAndSeedDatabase(scope.ServiceProvider); await InitializeSigningService(scope.ServiceProvider); } @@ -108,7 +110,21 @@ string environment } } - private static async Task CreateAndSeedDb( + private static void EnsureDatabaseIsUpToDate( + IServiceProvider services + ) + { + using var dbContext = + services.GetRequiredService>() + .CreateDbContext(); + var pendingMigrations = dbContext.Database.GetPendingMigrations(); + if (pendingMigrations.Any()) + { + throw new InvalidOperationException($"The database is not up to date. The pending migrations are: {string.Join(", ", pendingMigrations)}"); + } + } + + private static async Task CreateAndSeedDatabase( IServiceProvider services ) { @@ -123,7 +139,7 @@ IServiceProvider services catch (Exception exception) { var logger = services.GetRequiredService>(); - logger.FailedToCreateAndSeedDatabase(exception); + logger.FailedToCreateOrSeedDatabase(exception); } } From 83e3266a7fd03ca97bd55e1901bcda80e361b24b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 15 Oct 2025 14:40:58 +0200 Subject: [PATCH 483/878] Add missing SQL script for the migration 20251010164110_UpdateCoatedSideEnumeration --- backend/src/Migrations/migrate.sql | 19 +++++++++++++++++++ ...1010164110_UpdateCoatedSideEnumeration.sql | 18 ++++++++++++++++++ ...130041_AddApproverIdToResponseApproval.sql | 18 ++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql create mode 100644 backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 99796642..315ccfa9 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1335,3 +1335,22 @@ VALUES ('20250907130041_AddApproverIdToResponseApproval', '9.0.8'); END $EF$; COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN + + ALTER TYPE database.coated_side RENAME TO coated_side_old; + CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; + DROP TYPE database.coated_side_old; + + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251010164110_UpdateCoatedSideEnumeration', '9.0.8'); + + END IF; +END $EF$; + +COMMIT; diff --git a/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql b/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql new file mode 100644 index 00000000..8fdfe408 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql @@ -0,0 +1,18 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN + + ALTER TYPE database.coated_side RENAME TO coated_side_old; + CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; + DROP TYPE database.coated_side_old; + + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251010164110_UpdateCoatedSideEnumeration', '9.0.8'); + + END IF; +END $EF$; + +COMMIT; diff --git a/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql b/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql new file mode 100644 index 00000000..41c8fdce --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql @@ -0,0 +1,18 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN + + ALTER TYPE database.coated_side RENAME TO coated_side_old; + CREATE TYPE database.coated_side AS ENUM ('both', 'neither', 'non_prime', 'not_applicable', 'prime', 'unknown'); + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; + DROP TYPE database.coated_side_old; + + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration'; + + END IF; +END $EF$; + +COMMIT; From cebe180738f314d4c580724bf1aa8e54944fa2cb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Oct 2025 11:47:47 +0200 Subject: [PATCH 484/878] Set safe initial permissions for GnuPG directory --- backend/Dockerfile-production | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 0e6d7f70..8e4aca0d 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -111,6 +111,7 @@ RUN set -eux && \ --recursive \ --force \ /var/lib/apt/lists/* + WORKDIR /app COPY \ @@ -118,6 +119,14 @@ COPY \ /app/src/bin/Release/net9.0/publish \ ./ +RUN \ + mkdir --parents \ + /root/.gnupg && \ + chmod u=rwx,g=,o= \ + /root/.gnupg && \ + echo "allow-loopback-pinentry" \ + > /root/.gnupg/gpg-agent.conf + RUN \ ln --symbolic ./Database.dll ./App.dll From f5dc4a979d9e505be3d7dde3fa8cfedbfbbbc7cf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Oct 2025 15:47:22 +0200 Subject: [PATCH 485/878] Add some helpful information on whitespace and JSON escaping --- backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 836212d8..2e8fe764 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -104,7 +104,7 @@ CancellationToken cancellationToken errors.Add( new AddDataApprovalError( AddDataApprovalErrorCode.BAD_SIGNATURE, - $"The signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' is bad.", + $"The signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' is bad. Maybe the whitespace in the message differs from that in the original message file. For example, is there a newline at the end of the message and/or are the control characters for newlines identical? Note that in the WYSIWYG GraphQL interface, using triple double quotes for multiline strings changes the newline character at the very end of the string, which may not be how it was originally. Instead of using a multiline string, you may use a oneline string with escaped characters. To turn your message into such a JSON-escaped string, you may use the command-line tool `jq` as follows: `jq --raw-input --slurp '.' < ./message.json`", [nameof(input), nameof(input.Signature).ToLowerFirst()] ) ); From 78e111cf0b0c4352c9d4432c168ec61a2dc811bc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Oct 2025 17:23:39 +0200 Subject: [PATCH 486/878] Improve the OpenAPI setup --- backend/src/Database.csproj | 4 +++- backend/src/Startup.cs | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index adde690e..94f85c9e 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -36,7 +36,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -52,6 +52,8 @@ + + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 1fddd179..46c49d03 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -24,6 +24,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; +using Scalar.AspNetCore; using Serilog; namespace Database; @@ -35,6 +36,11 @@ IConfiguration configuration { private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; private const string AntiforgeryHeaderName = "X-XSRF-TOKEN"; + + private const string OpenApiDocumentName = "v1"; + private const string OpenApiRoutePattern = "/openapi/{documentName}.json"; + private const string OpenApiDocsRoute = "/openapi/docs"; + private readonly AppSettings _appSettings = configuration.Get(_ => { @@ -118,9 +124,10 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO I consider the flattened structure a bug. How can we solve this? } ); - services.AddOpenApi("v1", _ => + services.AddOpenApi(OpenApiDocumentName, _ => { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; + _.AddScalarTransformers(); }); } @@ -284,7 +291,13 @@ public void Configure(WebApplication app) // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx // app.UseWebSockets(); - app.MapOpenApi("/openapi/{documentName}.json"); + app.MapOpenApi(OpenApiRoutePattern); + app.MapScalarApiReference(OpenApiDocsRoute, _ => + { + _.Servers = []; // https://github.com/dotnet/aspnetcore/issues/57332#issuecomment-2480939916 + _.AddDocument(OpenApiDocumentName); // For multiple documents see https://guides.scalar.com/scalar/scalar-api-references/integrations/net-aspnet-core/integration#configuration-options__multiple-openapi-documents + _.WithOpenApiRoutePattern(OpenApiRoutePattern); + }); app.MapGraphQL() .WithOptions( // https://chillicream.com/docs/hotchocolate/server/middleware @@ -312,7 +325,11 @@ public void Configure(WebApplication app) { ResponseWriter = WriteJsonResponse } - ).DisableHttpMetrics(); + ) + .WithName("Health") + .WithDescription("Check the webserver health.") + .WithTags("Health") + .DisableHttpMetrics(); } // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output From 08ce04d2334104dcda9d470d7e5ecaa42b792760 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 16 Oct 2025 17:24:07 +0200 Subject: [PATCH 487/878] Make the log output more readable --- backend/src/Services/SigningService.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 0c81f0bf..d97ac08c 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -19,12 +19,12 @@ public static partial class SigningServiceLogging { [LoggerMessage( Level = LogLevel.Debug, - Message = "GNUPG Fingerprint: {Fingerprint}")] + Message = "GnuPG Fingerprint: {Fingerprint}")] public static partial void ExtractedFingerprint(this ILogger logger, string fingerprint); [LoggerMessage( Level = LogLevel.Debug, - Message = "GNUPG Signature: {Signature}")] + Message = "GnuPG Signature: {Signature}")] public static partial void CreatedSignature(this ILogger logger, string signature); [LoggerMessage( @@ -39,22 +39,22 @@ public static partial class SigningServiceLogging [LoggerMessage( Level = LogLevel.Debug, - Message = "GNUPG: Execute Command: {Command}")] + Message = "GnuPG: Execute Command: {Command}")] public static partial void ExecuteCommand(this ILogger logger, string command); [LoggerMessage( Level = LogLevel.Error, - Message = "GNUPG Execute Command: {Error}")] + Message = "GnuPG Execute Command: {Error}")] public static partial void ExecuteCommandDiagnostics(this ILogger logger, string error); [LoggerMessage( Level = LogLevel.Debug, - Message = "GNUPG: Execute Command Output: {Output}")] + Message = "GnuPG: Execute Command Output: {Output}")] public static partial void ExecuteCommandOutput(this ILogger logger, string output); [LoggerMessage( Level = LogLevel.Debug, - Message = "GNUPG: Execute Command Result: {result}")] + Message = "GnuPG: Execute Command Result: {result}")] public static partial void ExecuteCommandExitCode(this ILogger logger, int result); } From 6c310e9ff98876541ad9492214d80ba2e49fffb7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 16:34:57 +0200 Subject: [PATCH 488/878] Do not use `EnsureCreated` as it creates the database and its schema without applying migrations (rather use `make createdb` and `make migrate`) --- backend/src/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 0d9f2b2f..09dd80f3 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -133,7 +133,6 @@ IServiceProvider services using var dbContext = services.GetRequiredService>() .CreateDbContext(); - dbContext.Database.EnsureCreated(); await DbSeeder.DoAsync(services); } catch (Exception exception) From dfb382304c25e95df4129bdb789006f382a146fe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 16:35:52 +0200 Subject: [PATCH 489/878] Only start the necessary containers for the command in question --- Makefile | 50 +++++++++++++++++++++++++++++++++++---------- Makefile.production | 20 ++++++++++++------ 2 files changed, 53 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 7eb66aba..75e0d234 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,11 @@ logs : ## Follow logs --follow .PHONY : logs -exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CONTAINER}` container (after starting all containers if necessary) +exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container + ${docker_compose} up \ + --remove-orphans \ + --detach \ + ${CONTAINER} ${docker_compose} exec \ --user $(shell id --user):$(shell id --group) \ ${CONTAINER} \ @@ -149,14 +153,14 @@ exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CO .PHONY : exec execf : CONTAINER = frontend -execf : exec ## Execute the one-time command `${COMMAND}` against an existing `frontend` container (after starting all containers if necessary) +execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container .PHONY : execf execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against an existing `backend` container (after starting all containers if necessary) +execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container .PHONY : execb -run : up ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container (after starting all containers if necessary) +run : ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container ${docker_compose} run \ --rm \ --user $(shell id --user):$(shell id --group) \ @@ -165,27 +169,31 @@ run : up ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` .PHONY : run runf : CONTAINER = frontend -runf : run ## Run the one-time command `${COMMAND}` against a fresh `frontend` container (after starting all containers if necessary) +runf : run ## Run the one-time command `${COMMAND}` against a fresh `frontend` container .PHONY : runf runb : CONTAINER = backend -runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container (after starting all containers if necessary) +runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container .PHONY : runb shellf : COMMAND = bash -shellf : execf ## Enter shell in an existing `frontend` container (after starting all containers if necessary) +shellf : execf ## Enter shell in the `frontend` container .PHONY : shellf shellb : COMMAND = bash -shellb : runb ## Enter shell in a fresh `backend` container (after starting all containers if necessary) +shellb : runb ## Enter shell in a fresh `backend` container .PHONY : shellb shellb-examples : COMMAND = bash -c "cd ./examples && bash" -shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in an existing `backend` container (after starting all containers if necessary) +shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in the `backend` container .PHONY : shellb-examples # Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` + ${docker_compose} up \ + --remove-orphans \ + --detach \ + backend ${docker_compose} exec \ --privileged \ backend \ @@ -194,13 +202,21 @@ traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for examp " .PHONY : traceb -shelln : up ## Enter shell in an existing `nginx` container (after starting all containers if necessary) +shelln : ## Enter shell in the `nginx` container + ${docker_compose} up \ + --remove-orphans \ + --detach \ + nginx ${docker_compose} exec \ nginx \ bash .PHONY : shelln psql : ## Enter PostgreSQL interactive terminal in the running `database` container + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database ${docker_compose} exec \ database \ psql \ @@ -210,7 +226,7 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` contai shelld : CONTAINER = database shelld : COMMAND = bash -shelld : exec ## Enter shell in an existing `database` container (after starting all containers if necessary) +shelld : exec ## Enter shell in the `database` container .PHONY : shelld list : ## List all containers with health status @@ -221,6 +237,10 @@ list : ## List all containers with health status createdb : DBNAME = ${database_name} createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database ${docker_compose} exec \ database \ bash -c " \ @@ -230,6 +250,10 @@ createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` dropdb : DBNAME = ${database_name} dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database ${docker_compose} exec \ database \ bash -c " \ @@ -238,6 +262,10 @@ dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` .PHONY : dropdb sql : ## Run the SQL script in the file `${SQL}` in the running `database` service, for example, `make SQL=./my.sql sql` + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database cat ${SQL} \ | ${docker_compose} exec \ --no-TTY \ diff --git a/Makefile.production b/Makefile.production index 320fe547..ba18eb0e 100644 --- a/Makefile.production +++ b/Makefile.production @@ -95,18 +95,22 @@ logs : ## Follow logs --follow .PHONY : logs -exec : up ## Execute the one-time command `${COMMAND}` against an existing `${CONTAINER}` container (after starting all containers if necessary) +exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container + ${docker_compose} up \ + --remove-orphans \ + --detach \ + ${CONTAINER} ${docker_compose} exec \ ${CONTAINER} \ ${COMMAND} .PHONY : exec execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against an existing `backend` container (after starting all containers if necessary) +execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container .PHONY : execb execf : CONTAINER = frontend -execf : exec ## Execute the one-time command `${COMMAND}` against an existing `frontend` container (after starting all containers if necessary) +execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container .PHONY : execf shellb : COMMAND = bash @@ -114,17 +118,17 @@ shellb : execb ## Enter shell in a running `backend` container (to test database .PHONY : shellb shellf : COMMAND = bash -shellf : execf ## Enter shell in a running `frontend` container (after starting all containers if necessary) +shellf : execf ## Enter shell in a running `frontend` container .PHONY : shellf shelln : CONTAINER = nginx shelln : COMMAND = bash -shelln : exec ## Enter shell in a running `nginx` container (after starting all containers if necessary) +shelln : exec ## Enter shell in a running `nginx` container .PHONY : shelln shelld : CONTAINER = database shelld : COMMAND = bash -shelld : exec ## Enter shell in a running `database` container (after starting all containers if necessary) +shelld : exec ## Enter shell in a running `database` container .PHONY : shelld list : ## List all containers with health status @@ -157,6 +161,10 @@ createdb : ## Create database .PHONY : createdb psql : ## Enter PostgreSQL interactive terminal in the running `database` service + ${docker_compose} up \ + --remove-orphans \ + --detach \ + database ${docker_compose} exec \ database \ psql \ From ccdb9327750f1bed4e0a450076817ff86248fe68 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 16:36:24 +0200 Subject: [PATCH 490/878] Correct the readme regarding deployment and add entry to create and migrate the database --- README.md | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index fe6792c5..54d46085 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ If you have a question for which you don't find the answer in this repository, p `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. +1. Create the PostgreSQL database and schema by running + `make createdb migrate`. 1. Start all services and follow their logs by running `make up logs`. 1. To see the web frontend navigate to `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see @@ -116,6 +118,16 @@ In another shell The same works for frontend containers by running `make shellf`. +### Migrating the Database + +After changing the domain model in `./backend/src/data`, you need to migrate +the database by dropping into `make shellb`, adding a migration with `make +NAME=${MIGRATION_NAME} migration`, verifying and if necessary adapting the new +migration C# code and SQL scripts, exiting the container with `exit`, and +applying the new migration to the PostgreSQL database with `make migrate`. See +[Migrations Overview](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) +and the following pages for details. + ### Developing with Visual Studio Code On the very first usage: @@ -204,12 +216,6 @@ by running `make down remove-data up`. Note that the latter will remove all data from PostgreSQL, recreate the database and its schema, and seed it freshly. -After changing the domain model in `./backend/src/data`, you probably need to -migrate the database by dropping into `make shellb`, adding a migration `make -NAME=${MIGRATION_NAME} add-migration`, generating a migration script `make -FROM=${PREVIOUS_MIGRATION} TO=${NEW_MIGRATION} generate-migration-script`, and -executing it `make SQL=${SCRIPT_PATH} sql`. - When your hard-disk starts to grow full, it may be the case that Docker does not clean-up anonymous volumes properly. You can do so manually by running `docker system prune` potentially with the arguments `--volumes` and/or @@ -320,12 +326,8 @@ and the pages following it. [Releases](https://github.com/building-envelope-data/database/releases). 1. Fetch the release branch by running `git fetch` and check it out by running `git checkout release/v*.*.*`, where `*.*.*` is the version. -1. Prepare the release by running `make prepare-release` in your shell, review, - add, commit, and push the changes. In particular, migration and rollback SQL - files are created in `./backend/src/Migrations/` which need to be reviewed - --- see - [Migrations Overview](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/?tabs=dotnet-core-cli) - and following pages for details. +1. Apply pending migrations with `make migrate`. +1. Make sure that all tests succeed and try out any new features manually. 1. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yml) by merging the release branch into `main` whereby a new pull request from `main` into `develop` is created that you need to merge to finish off. From 619bb49695f1d2eac622ea851a318802f89f0f2d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 20:38:00 +0200 Subject: [PATCH 491/878] Correct name of method (it does not create the database anymore) --- backend/src/Program.cs | 19 ++++++++++--------- backend/src/Startup.cs | 10 +++++----- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 09dd80f3..fe775270 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,8 +3,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using Database.Data; -using Database.Services; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -16,6 +14,8 @@ using Serilog.Formatting.Compact; using ILogger = Microsoft.Extensions.Logging.ILogger; using Log = Serilog.Log; +using Database.Data; +using Database.Services; namespace Database; @@ -24,8 +24,8 @@ public static partial class LoggerExtensions [LoggerMessage( EventId = 0, Level = LogLevel.Error, - Message = "An error occurred creating or seeding the database.")] - public static partial void FailedToCreateOrSeedDatabase( + Message = "An error occurred seeding the database.")] + public static partial void FailedToSeedDatabase( this ILogger logger, // The first exception is implicitly taken care of as detailed in // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy @@ -59,7 +59,7 @@ string[] commandLineArguments { EnsureDatabaseIsUpToDate(scope.ServiceProvider); // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data - await CreateAndSeedDatabase(scope.ServiceProvider); + await SeedDatabase(scope.ServiceProvider); await InitializeSigningService(scope.ServiceProvider); } @@ -124,7 +124,7 @@ IServiceProvider services } } - private static async Task CreateAndSeedDatabase( + private static async Task SeedDatabase( IServiceProvider services ) { @@ -138,7 +138,7 @@ IServiceProvider services catch (Exception exception) { var logger = services.GetRequiredService>(); - logger.FailedToCreateOrSeedDatabase(exception); + logger.FailedToSeedDatabase(exception); } } @@ -146,8 +146,9 @@ private static async Task InitializeSigningService( IServiceProvider services ) { - var signing = services.GetRequiredService(); - await signing.Initialize(); + await services + .GetRequiredService() + .Initialize(); } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 46c49d03..3ddc3e78 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -4,11 +4,6 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; -using Database.Configuration; -using Database.Data; -using Database.Data.Extensions; -using Database.Enumerations; -using Database.Services; using HotChocolate.AspNetCore; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; @@ -26,6 +21,11 @@ using Microsoft.OpenApi; using Scalar.AspNetCore; using Serilog; +using Database.Configuration; +using Database.Data; +using Database.Data.Extensions; +using Database.Enumerations; +using Database.Services; namespace Database; From d62c04ef4e969c6e17c3d1111e5457584cb1dc2a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 20:38:29 +0200 Subject: [PATCH 492/878] Add missing variable declaration and definition --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 75e0d234..6ba2d02e 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ docker_compose = \ database_name = xbase +dump_archive_name = postgresql_dumpall.gz + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ From 29fff594f5749a331b20356460e821f2dec35131 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 20:38:48 +0200 Subject: [PATCH 493/878] Improve docu and layout --- backend/Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index 33c7dd63..21170c8a 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -165,10 +165,13 @@ diagrams : ## Draw images from textual UML diagrams # See https://github.com/hasura/graphqurl#export-schema schema : ## Fetch GraphQL schema using `hasura/graphqurl` - gq http://backend:8080/graphql/ --introspect > ./schema.graphql + gq \ + http://backend:8080/graphql/ \ + --introspect \ + > ./schema.graphql .PHONY : schema -has-pending-model-changes : ## Check whether there have been any model changes made since the last migration +has-pending-model-changes : ## Check whether there have been any model changes made since the last migration was added dotnet ef \ --project ./src/Database.csproj \ migrations \ From e7c6466dc6e287d29adecb5748484721b361174f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 20:40:28 +0200 Subject: [PATCH 494/878] Add linebreaks, table of contents, fix some commands, improve order, and remove chapter that belong to metabase and not database --- README.md | 132 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 54d46085..cc6e00ad 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,44 @@ # Database -This repository presents the reference implementation of a product data server as part of the product data network [buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before deploying this repository, [machine](https://github.com/building-envelope-data/machine) can be used to set up the machine. - -The [API specification of the product data servers](https://github.com/building-envelope-data/api/blob/develop/apis/database.graphql) is available in the repository [api](https://github.com/building-envelope-data/api). There is also a [visualization of the API of a product data server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). - -This repository is deployed as the [product data server of TestLab Solar Facades of Fraunhofer ISE](https://www.solarbuildingenvelopes.com). - -If you have a question for which you don't find the answer in this repository, please raise a [new issue](https://github.com/building-envelope-data/database/issues/new) and add the tag `question`! All ways to contribute are presented by [CONTRIBUTING.md](https://github.com/building-envelope-data/database/blob/develop/CONTRIBUTING.md). The basis for our collaboration is decribed by our [Code of Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). +This repository presents the reference implementation of a product data server +as part of the product data network +[buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before +deploying this repository, +[machine](https://github.com/building-envelope-data/machine) can be used to set +up the machine. + +The [API specification of the product data +servers](https://github.com/building-envelope-data/api/blob/develop/apis/database.graphql) +is available in the repository +[api](https://github.com/building-envelope-data/api). There is also +a [visualization of the API of a product data +server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). + +This repository is deployed as the [product data server of TestLab Solar +Facades of Fraunhofer ISE](https://www.solarbuildingenvelopes.com). + +If you have a question for which you don't find the answer in this repository, +please raise a [new +issue](https://github.com/building-envelope-data/database/issues/new) and add +the tag `question`! All ways to contribute are presented by +[CONTRIBUTING.md](https://github.com/building-envelope-data/database/blob/develop/CONTRIBUTING.md). +The basis for our collaboration is decribed by our [Code of +Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). +## Contents + +[Getting started](#getting-started) + +- [On your Linux machine](#on-your-linux-machine) +- [Migrating the Database](#migrating-the-database) +- [Developing with Visual Studio Code](#developing-with-visual-studio-code) +- [Troubleshooting](#troubleshooting) + +[Deployment](#deployment) + +- [Setting up a Debian production machine](#setting-up-a-debian-production-machine) +- [Creating a release](#creating-a-release) +- [Deploying a release](#deploying-a-release) +- [Troubleshooting](#troubleshooting-1) ## Getting started @@ -296,20 +328,49 @@ and the pages following it. of the exported GnuPG secret key for signing in the file `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`; - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and - `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to - encrypt and sign JSON web tokens (JWT) used by OpenId Connect; + `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to + encrypt and sign JSON web tokens (JWT) used by OpenId Connect; - `SMTP_HOST` and `SMTP_PORT` are host and port of the message transfer - agent to be used to send emails through the Simple Mail Transfer - Protocol (SMTP); + agent to be used to send emails through the Simple Mail Transfer + Protocol (SMTP); - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are - host and port of the message transfer agent and a list of allowed - email addresses to send emails to even in the staging environment. + host and port of the message transfer agent and a list of allowed + email addresses to send emails to even in the staging environment. + 1. Generate JSON Web Token (JWT) encryption and signing certificates by running + `make --file=Makefile.production jwt-certificates`. + 1. Generate and export a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the + file `./backend/src/gpg-keys/.gpg` by running `make + PERSON=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information + filled in, for example, `make NAME="Anna Smith" COMMENT=first + EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which + is output by the command and set it as the value of the + `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. + + Instead of using the GNU Make target `gpg`, you may + 1. install [GnuPG](https://gnupg.org) as described on + [Download GnuPG](https://gnupg.org/download/index.html) or + [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), + 1. generate a GnuPG key by running `gpg --full-generate-key` and answering + the prompts, + 1. list the keys for which you have both a public and secret key by running + `gpg --list-secret-keys --keyid-format=long`, + 1. identify and copy the long form of the fingerprint of the key you have + just created, + 1. remember the key in the variable `fingerprint` by running + `fingerprint=` with `` replaced by the + copied fingerprint, + 1. create the directory to save the secret key to if it does not exist yet + by running `mkdir --parents ./backend/src/gpg-keys`, + 1. export the armored secret key to the file + `./backend/src/gpg-keys/${fingerprint}.gpg` by running + `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, + 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in + the `./.env` file to the remembered fingerprint in your favorite editor. 1. Prepare PostgreSQL by generating new password files by running `make --file=Makefile.production postgres_passwords` and creating the database by running `make --file=Makefile.production createdb`. - 1. Generate JSON Web Token (JWT) encryption and signing certificates by running - `make --file=Makefile.production jwt-certificates`. ### Creating a release @@ -326,11 +387,11 @@ and the pages following it. [Releases](https://github.com/building-envelope-data/database/releases). 1. Fetch the release branch by running `git fetch` and check it out by running `git checkout release/v*.*.*`, where `*.*.*` is the version. -1. Apply pending migrations with `make migrate`. +1. Apply pending migrations with `make --file=Makefile.production migrate`. 1. Make sure that all tests succeed and try out any new features manually. 1. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yml) by merging the release branch into `main` whereby a new pull request from - `main` into `develop` is created that you need to merge to finish off. + `main` into `develop` is created that you need to merge to finish of. ### Deploying a release @@ -360,7 +421,7 @@ and the pages following it. addresses). 1. Change to the production environment by running `cd /app/production`. 1. Adapt the environment file `./.env` if necessary by comparing it with the - `./.env.staging.sample` file of the release to be deployed. + `./.env.production.sample` file of the release to be deployed. 1. Deploy the new release in the production environment by running `make --file=Makefile.production TARGET=${TAG} deploy`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. @@ -456,38 +517,3 @@ under /app/staging before doing it in `production` under /app/production. that you want to update. 1. Update a single field by running `update database.optical_data set "Description" = '...' where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. 1. Delete a faulty record by running `delete from database.optical_data where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. - -## Original Idea - -The product identifier service should provide the following endpoints: - -- Obtain a new product identifier possibly associating internal meta information with it, like a custom string or a JSON blob -- Update the meta information of one of your product identifiers -- Get meta information of one of your product identifiers -- Get the owner of a product identifier (needed, for example, by the IGSDB to check that the user adding product data with a product identifier owns the identifier) -- List all your product identifiers -- Request the transferal of the ownership of one or all of your product identifiers to another (once the receiving user agrees, the transferal is made) -- Respond to a transferal request - -How to obtain a unique product identifier and add product data to some database: - -1. Create an account at a central authentication service, that is, a domain specific and lightweight service like [Auth0](https://auth0.com) managed by us (the details of how users prove to be a certain manufacturer are still open) -2. Authenticate yourself at the authentication service receiving a [JSON web token](https://jwt.io) (this could be a simple username/password authentication scheme) -3. Obtain a new product identifier from the respective service passing your JSON web token as means of authentication -4. Add product data to some database like IGSDB passing the product identifier and your JSON web token - -JSON web tokens are used for authentication across different requests, services, and domains. - -Product identifiers are randomly chosen and verified to be unique 32, 48, or 64 bit numbers, which can be communicated for easy human use as [proquints](https://arxiv.org/html/0901.4016) [there are implementations in various languages](https://github.com/dsw/proquint). We could alternatively use [version 4 universally-unique identifiers](https://tools.ietf.org/html/rfc4122); I consider this to be overkill as it comes with a performance penalty and our identifiers do not need to be universally unique. Either way, [those identifiers do _not_ replace primary keys](https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439). - -Randomness of identifiers ensures that - -- the product identifier does not encode any information regarding the product, like its manufacturer, which would, for example, be problematic when one manufacturer is bought by another -- a user cannot run out of product identifiers, because there is no fixed range of possible identifiers per user -- it's unlikely that flipping one bit or replacing one letter in the proquint representation by another results in a valid identifier owned by the same user - -We may add some error detection and correction capabilities by, for example, generating all but the last 4 bits randomly and using the last 4 bits as [some sort of checksum](https://en.wikipedia.org/wiki/Checksum). - -## Useful Resources - -- [Set up a GraphQL client with Apollo](https://hasura.io/learn/graphql/typescript-react-apollo/apollo-client/) From c48b917c5d42bc7a84fc7c1caf8dec19305a7096 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 17 Oct 2025 21:08:25 +0200 Subject: [PATCH 495/878] Update the GraphQL schema dump used by the frontend --- frontend/type-defs.graphqls | 41 ++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index ae1b28e9..09d21ab0 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -247,11 +247,16 @@ type Database { description: String! locator: Url! name: String! + operator: DatabaseOperatorEdge! uuid: Uuid! verificationCode: String! verificationState: DatabaseVerificationState! } +type DatabaseOperatorEdge { + node: Institution! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -389,6 +394,10 @@ type HygrothermalDataEdge { node: HygrothermalData! } +type Institution { + uuid: Uuid! +} + type InstitutionAccessRights { allowedDatasetsPerTime: NonNegativeInt allowedUserCount: NonNegativeInt @@ -423,6 +432,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") @@ -580,7 +590,7 @@ type Query { calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") - currentUserInfo: UserInfo @cost(weight: "10") + currentUserInfo: UserInfo! @cost(weight: "10") data(id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") @@ -601,6 +611,18 @@ type Query { verificationCode: String! } +type RemoveDataApprovalError { + code: RemoveDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type RemoveDataApprovalPayload { + dataApproval: DataApproval + errors: [RemoveDataApprovalError!] + query: Query! +} + type ResponseApproval { approverId: Uuid! keyFingerprint: String! @@ -1462,6 +1484,14 @@ enum AddDataApprovalErrorCode { CREATING_RESPONSE_APPROVAL_FAILED AMBIGUOUS_STATEMENT MISSING_STATEMENT + INVALID_KEY_FINGERPRINT + FAILED_RECEIVING_KEY + BAD_SIGNATURE + FAILED_VERIFYING_SIGNATURE + FAILED_VERIFYING_RESPONSE + ILLEGAL_MESSAGE + WRONG_RESPONSE + WRONG_STATEMENT } "Defines when a policy shall be executed." @@ -1601,6 +1631,15 @@ enum OpticalComponentType { SHADING } +enum RemoveDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPROVAL + CREATING_RESPONSE_APPROVAL_FAILED +} + enum SortEnumType { ASC DESC From 8075cead21abdb50d115bc1114b306b0fec10840 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Oct 2025 12:26:35 +0200 Subject: [PATCH 496/878] Remove unused schema stitching code and comments --- .../src/Configuration/GraphQlConfiguration.cs | 116 ++++++++---------- backend/src/Startup.cs | 4 +- 2 files changed, 50 insertions(+), 70 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 9a7c1d66..e16016b4 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -31,52 +31,25 @@ using Database.GraphQl.AccessRights; using Database.GraphQl.Methods; using Database.GraphQl.ResponseApprovals; +using HotChocolate.Configuration; +using HotChocolate.Execution; namespace Database.Configuration; public static class GraphQlConfiguration { internal const string TypeDiscriminatorPropertyName = "__typename"; + public const string FilterInputSuffix = "FilterInput"; public static void ConfigureServices( IServiceCollection services, - IWebHostEnvironment environment, - AppSettings appSettings + IWebHostEnvironment environment ) { - // Stitching Services https://chillicream.com/docs/hotchocolate/v13/distributed-schema/schema-stitching - var httpMetabaseClientBuilder = services.AddHttpClient( - WellKnownSchemaNames.Metabase, - _ => _.BaseAddress = new Uri($"{appSettings.MetabaseHost}/graphql") - ); - var httpDatabaseClientBuilder = services.AddHttpClient( - WellKnownSchemaNames.Database, - _ => _.BaseAddress = new Uri($"{appSettings.Host}/graphql") - ); - if (!environment.IsProduction()) - { - httpMetabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => - new HttpClientHandler - { - // ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = - HttpClientHandler.DangerousAcceptAnyServerCertificateValidator - } - ); - } - - httpDatabaseClientBuilder.ConfigurePrimaryHttpMessageHandler(_ => - new HttpClientHandler - { - // ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = - HttpClientHandler.DangerousAcceptAnyServerCertificateValidator - } - ); // Automatic-Persisted-Queries Services services .AddMemoryCache() - .AddSha256DocumentHashProvider(HashFormat.Hex); + .AddSha256DocumentHashProvider(HashFormat.Hex); // https://chillicream.com/docs/hotchocolate/v15/security/#fips-compliance // GraphQL Server services .AddGraphQLServer() @@ -88,6 +61,7 @@ AppSettings appSettings .AddProjections() .AddFiltering() .AddSorting() + .AddQueryContext() .AddAuthorization() .AddGlobalObjectIdentification() .AddQueryFieldToMutationPayloads() @@ -98,21 +72,37 @@ AppSettings appSettings options.UseXmlDocumentation = false; options.SortFieldsByName = true; options.RemoveUnreachableTypes = false; + options.RemoveUnusedTypeSystemDirectives = true; options.DefaultBindingBehavior = BindingBehavior.Implicit; - /* options.FieldMiddleware = ... */ + // options.DefaultFieldBindingFlags = FieldBindingFlags.InstanceAndStatic; + options.EnableDirectiveIntrospection = true; + options.DefaultDirectiveVisibility = DirectiveVisibility.Public; + options.DefaultResolverStrategy = ExecutionStrategy.Parallel; + options.ValidatePipelineOrder = true; + options.StrictRuntimeTypeValidation = true; + options.EnableOneOf = true; + options.EnsureAllNodesCanBeResolved = true; + options.EnableFlagEnums = false; + options.EnableDefer = false; + options.EnableStream = false; + options.EnableSemanticNonNull = false; + options.StripLeadingIFromInterface = false; + options.EnableTag = true; + options.PublishRootFieldPagesToPromiseCache = true; } ) .ModifyRequestOptions(options => { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs - /* options.ExecutionTimeout = ...; */ + options.ExecutionTimeout = TimeSpan.FromSeconds(120); options.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. /* options.QueryCacheSize = ...; */ /* options.UseComplexityMultipliers = ...; */ } ) - // Configure `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. But how? - // Subscriptions + // Configure + // `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. + // But how? Subscriptions /* .AddInMemorySubscriptions() */ // Persisted queries /* .AddFileSystemOperationDocumentStorage("./persisted_operations") */ @@ -140,12 +130,9 @@ AppSettings appSettings ) ) // Scalar Types - .AddType(new UuidType("Uuid", - defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type + .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type .AddType(new UrlType("Url")) - .AddType(new JsonType("Any", - BindingBehavior - .Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar + .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar .AddType(new LocaleType()) // Query Types .AddQueryType(d => d.Name(nameof(Query))) @@ -205,23 +192,10 @@ AppSettings appSettings _.InferConnectionNameFromField = false; } ) - // Stitching - // .AddRemoteSchema(WellKnownSchemaNames.Metabase, ignoreRootTypes: true) - // .AddTypeExtensionsFromString(@" - // extend type OpticalData { - // component: Component @delegate(schema: 'Metabase', path: 'component(uuid: $fields:ComponentId)') - // } - // ") // Automatic Peristed Queries .UseAutomaticPersistedOperationPipeline() .AddInMemoryOperationDocumentStorage(); // Needed by the automatic persisted operation pipeline } - - public static class WellKnownSchemaNames - { - public const string Metabase = "metabase"; - public const string Database = "database"; - } } // See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions @@ -319,7 +293,10 @@ public static class FilterConventionDescriptorExtensions public static IFilterConventionDescriptor AddDefaults( this IFilterConventionDescriptor descriptor) { - return descriptor.AddDefaultOperations().BindDefaultTypes().UseQueryableProvider(); + return descriptor + .AddDefaultOperations() + .BindDefaultTypes() + .UseQueryableProvider(); } // Inspired by FilterConventionDescriptorExtensions#AddDefaultOperations @@ -329,14 +306,6 @@ public static IFilterConventionDescriptor AddDefaultOperations( { descriptor.Operation(DefaultFilterOperations.Equals).Name("equalTo"); descriptor.Operation(DefaultFilterOperations.NotEquals).Name("notEqualTo"); - descriptor.Operation(DefaultFilterOperations.GreaterThan).Name("greaterThan"); - descriptor.Operation(DefaultFilterOperations.NotGreaterThan).Name("notGreaterThan"); - descriptor.Operation(DefaultFilterOperations.GreaterThanOrEquals).Name("greaterThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.NotGreaterThanOrEquals).Name("notGreaterThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.LowerThan).Name("lessThan"); - descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("notLessThan"); - descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("lessThanOrEqualTo"); - descriptor.Operation(DefaultFilterOperations.NotLowerThanOrEquals).Name("notLessThanOrEqualTo"); descriptor.Operation(DefaultFilterOperations.Contains).Name("contains"); descriptor.Operation(DefaultFilterOperations.NotContains).Name("doesNotContain"); descriptor.Operation(DefaultFilterOperations.In).Name("in"); @@ -345,14 +314,23 @@ public static IFilterConventionDescriptor AddDefaultOperations( descriptor.Operation(DefaultFilterOperations.NotStartsWith).Name("doesNotStartWith"); descriptor.Operation(DefaultFilterOperations.EndsWith).Name("endsWith"); descriptor.Operation(DefaultFilterOperations.NotEndsWith).Name("doesNotEndWith"); + descriptor.Operation(DefaultFilterOperations.And).Name("and"); + descriptor.Operation(DefaultFilterOperations.Or).Name("or"); + descriptor.Operation(DefaultFilterOperations.GreaterThan).Name("greaterThan"); + descriptor.Operation(DefaultFilterOperations.NotGreaterThan).Name("notGreaterThan"); + descriptor.Operation(DefaultFilterOperations.GreaterThanOrEquals).Name("greaterThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.NotGreaterThanOrEquals).Name("notGreaterThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.LowerThan).Name("lessThan"); + descriptor.Operation(DefaultFilterOperations.NotLowerThan).Name("notLessThan"); + descriptor.Operation(DefaultFilterOperations.LowerThanOrEquals).Name("lessThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.NotLowerThanOrEquals).Name("notLessThanOrEqualTo"); + descriptor.Operation(DefaultFilterOperations.Some).Name("some"); descriptor.Operation(DefaultFilterOperations.All).Name("all"); descriptor.Operation(DefaultFilterOperations.None).Name("none"); - descriptor.Operation(DefaultFilterOperations.Some).Name("some"); descriptor.Operation(DefaultFilterOperations.Any).Name("any"); - descriptor.Operation(DefaultFilterOperations.And).Name("and"); - descriptor.Operation(DefaultFilterOperations.Or).Name("or"); + descriptor.Operation(DefaultFilterOperations.Like).Name("like"); descriptor.Operation(DefaultFilterOperations.Data).Name("data"); - descriptor.Operation(AdditionalFilterOperations.Not).Name("not"); + // TODO `descriptor.Operation(AdditionalFilterOperations.Not).Name("not");` as in the project `database` // `inClosedInterval` return descriptor; } @@ -398,6 +376,8 @@ private static IFilterConventionDescriptor BindComparableType( descriptor .BindRuntimeType>() .BindRuntimeType>(); + // .BindRuntimeType>() + // .BindRuntimeType>(); // TODO Why does this not work? // if (name is not null) // { @@ -409,7 +389,7 @@ private static IFilterConventionDescriptor BindComparableType( } } -// See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sort-conventions +// See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sorting-conventions public partial class CustomSortConvention : SortConvention { protected override void Configure(ISortConventionDescriptor descriptor) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 3ddc3e78..8ccb0ab5 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -54,7 +54,7 @@ IConfiguration configuration public void ConfigureServices(IServiceCollection services) { AuthConfiguration.ConfigureServices(services, _environment, _appSettings); - GraphQlConfiguration.ConfigureServices(services, _environment, _appSettings); + GraphQlConfiguration.ConfigureServices(services, _environment); ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); @@ -133,7 +133,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services private void ConfigureMessageSenderServices(IServiceCollection services) { - services.AddTransient(serviceProvider => + services.AddTransient(serviceProvider => new EmailSender( _appSettings.Email.SmtpHost, _appSettings.Email.SmtpPort, From 21d24ad9c6525faddf90c791ecae20eb8f753ffc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Oct 2025 12:41:03 +0200 Subject: [PATCH 497/878] Upgrade C# packages and dotnet tools --- backend/dotnet-tools.json | 15 ++++++++++---- backend/src/Database.csproj | 32 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 8 ++++---- 3 files changed, 31 insertions(+), 24 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 607cf0a6..01ba6f35 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "9.0.8", + "version": "9.0.10", "commands": [ "dotnet-ef" ], @@ -59,14 +59,14 @@ "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.10.2", + "version": "0.11.0", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.2.0.1", + "version": "2025.2.3", "commands": [ "jb" ], @@ -80,7 +80,7 @@ "rollForward": false }, "strawberryshake.tools": { - "version": "15.1.9", + "version": "15.1.11", "commands": [ "dotnet-graphql" ], @@ -92,6 +92,13 @@ "certificate-tool" ], "rollForward": false + }, + "dotnet-retest": { + "version": "1.0.0", + "commands": [ + "dotnet-retest" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 94f85c9e..c2a5387d 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -25,32 +25,32 @@ - - - - - - - - - - - + + + + + + + + + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - - - + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 5536bba6..b02c66bc 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,13 +16,13 @@ - + - - + + - + From 3b6350aa86b71778f3f31126a84d86bb32353a9c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 20 Oct 2025 12:45:14 +0200 Subject: [PATCH 498/878] Add integration tests setup and add two integration tests: one that makes sure that the GraphQL schema is up to date and another that makes sure that there are no domain model changes for which there are no migrations --- backend/Makefile | 7 +- backend/src/AppSettings.cs | 2 +- backend/src/Program.cs | 39 +- backend/src/Services/EmailSender.cs | 1 + backend/src/Services/IEmailSender.cs | 12 + .../test/Integration/CollectingEmailSender.cs | 46 + .../CustomWebApplicationFactory.cs | 156 ++ .../Integration/GraphQl/GraphQlSchemaTests.cs | 25 + .../GraphQlSchemaTests.IsUnchanged.snap | 1732 +++++++++++++++++ backend/test/Integration/IntegrationTests.cs | 458 +++++ backend/test/Integration/MigrationTests.cs | 50 + 11 files changed, 2522 insertions(+), 6 deletions(-) create mode 100644 backend/src/Services/IEmailSender.cs create mode 100644 backend/test/Integration/CollectingEmailSender.cs create mode 100644 backend/test/Integration/CustomWebApplicationFactory.cs create mode 100644 backend/test/Integration/GraphQl/GraphQlSchemaTests.cs create mode 100644 backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap create mode 100644 backend/test/Integration/IntegrationTests.cs create mode 100644 backend/test/Integration/MigrationTests.cs diff --git a/backend/Makefile b/backend/Makefile index 21170c8a..c058f049 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -20,8 +20,9 @@ help : ## Print this help # Sadly, `--list-tests` ignores the option `--filter` in the test adapter # `NUnit`: https://github.com/dotnet/sdk/issues/8643#issuecomment-1497765835 # dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal +# dotnet retest -- --filter "FullyQualifiedName~${x}" --verbosity minimal test : ARGS = -test : ## Run tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"' test` +test : ## Run (previously failed) tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"' test` @echo "--------------------------------------------------" @echo "Complete test list" @echo "--------------------------------------------------" @@ -31,7 +32,9 @@ test : ## Run tests, possibly with custom arguments `${ARGS}` from `dotnet test @echo "--------------------------------------------------" @echo "All test runs" @echo "--------------------------------------------------" - dotnet test \ + dotnet retest \ + --retries 0 \ + -- \ --collect:"XPlat Code Coverage" \ ${ARGS} .PHONY : test diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 4369ce81..0e71a68e 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -77,7 +77,7 @@ public sealed class EmailSettings public sealed class DatabaseSettings { - public string ConnectionString { get; private set; } + public string ConnectionString { get; set; } = ""; public string SchemaName { get; private set; } diff --git a/backend/src/Program.cs b/backend/src/Program.cs index fe775270..c8fb9a93 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -24,6 +24,17 @@ public static partial class LoggerExtensions [LoggerMessage( EventId = 0, Level = LogLevel.Error, + Message = "An error occurred creating the database.")] + public static partial void FailedToCreateDatabase( + this ILogger logger, + // The first exception is implicitly taken care of as detailed in + // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy + Exception exception + ); + + [LoggerMessage( + EventId = 1, + Level = LogLevel.Error, Message = "An error occurred seeding the database.")] public static partial void FailedToSeedDatabase( this ILogger logger, @@ -57,8 +68,15 @@ string[] commandLineArguments startup.Configure(application); using (var scope = application.Services.CreateScope()) { - EnsureDatabaseIsUpToDate(scope.ServiceProvider); - // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data + if (!builder.Environment.IsEnvironment(TestEnvironment)) + { + EnsureDatabaseIsUpToDate(scope.ServiceProvider); + // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data + } + if (builder.Environment.IsEnvironment(TestEnvironment)) + { + await CreateDatabase(scope.ServiceProvider); + } await SeedDatabase(scope.ServiceProvider); await InitializeSigningService(scope.ServiceProvider); } @@ -124,7 +142,7 @@ IServiceProvider services } } - private static async Task SeedDatabase( + private static async Task CreateDatabase( IServiceProvider services ) { @@ -133,6 +151,21 @@ IServiceProvider services using var dbContext = services.GetRequiredService>() .CreateDbContext(); + await dbContext.Database.EnsureCreatedAsync(); + } + catch (Exception exception) + { + var logger = services.GetRequiredService>(); + logger.FailedToCreateDatabase(exception); + } + } + + private static async Task SeedDatabase( + IServiceProvider services + ) + { + try + { await DbSeeder.DoAsync(services); } catch (Exception exception) diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index d5b8cb1b..42eb7291 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -25,6 +25,7 @@ public sealed class EmailSender( int smtpPort, ILogger logger ) +: IEmailSender { private readonly ILogger _logger = logger; private readonly string _smtpHost = smtpHost; diff --git a/backend/src/Services/IEmailSender.cs b/backend/src/Services/IEmailSender.cs new file mode 100644 index 00000000..1c7c7130 --- /dev/null +++ b/backend/src/Services/IEmailSender.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace Database.Services; + +public interface IEmailSender +{ + public Task SendAsync( + (string name, string address) recipient, + string subject, + string body + ); +} \ No newline at end of file diff --git a/backend/test/Integration/CollectingEmailSender.cs b/backend/test/Integration/CollectingEmailSender.cs new file mode 100644 index 00000000..b9cd5ee6 --- /dev/null +++ b/backend/test/Integration/CollectingEmailSender.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Database.Services; + +namespace Database.Tests.Integration; + +public sealed class CollectingEmailSender + : IEmailSender +{ + private readonly List _emails; + + public CollectingEmailSender() + { + _emails = []; + } + + public IReadOnlyCollection Emails => _emails.AsReadOnly(); + + public Task SendAsync( + (string name, string address) recipient, + string subject, + string body + ) + { + _emails.Add( + new Email + ( + recipient, + subject, + body + ) + ); + return Task.FromResult(0); + } + + public void Clear() + { + _emails.Clear(); + } + + public sealed record Email( + (string name, string address) Recipient, + string Subject, + string Body + ); +} \ No newline at end of file diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs new file mode 100644 index 00000000..6326dade --- /dev/null +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -0,0 +1,156 @@ +// Inspired by +// https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?customize-webapplicationfactory +// https://www.thinktecture.com/en/entity-framework-core/isolation-of-integration-tests-in-2-1/ + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Database.Data; +using Database.Services; + +namespace Database.Tests.Integration; + +public sealed class CustomWebApplicationFactory + : WebApplicationFactory +{ + private bool _disposed; + + public CustomWebApplicationFactory() + { + EmailSender = new CollectingEmailSender(); + } + + public CollectingEmailSender EmailSender { get; } + + private T Get(Func what) + { + using var scope = Services.CreateScope(); + return what(scope.ServiceProvider); + } + + private void Do(Action what) + { + using var scope = Services.CreateScope(); + what(scope.ServiceProvider); + } + + private Task DoAsync(Func what) + { + using var scope = Services.CreateScope(); + return what(scope.ServiceProvider); + } + + public Task DoAsync(Func what) + { + return DoAsync(services => + what(services.GetRequiredService()) + ); + } + + // private TResult Get(Func what) + // { + // using var scope = Services.CreateScope(); + // return what(scope.ServiceProvider); + // } + + public AppSettings AppSettings + { + get + { + return Get( + services => + services.GetRequiredService() + ); + } + } + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + var connectionString = $"Host=database; Port=5432; Database=xbase_test_{Guid.NewGuid().ToString().Replace("-", "")}; User Id=postgres; Password=postgres; Maximum Pool Size=90;"; + // var schemaName = $"database_{Guid.NewGuid().ToString().Replace("-", "")}"; + // builder.ConfigureAppConfiguration(_ => _.AddInMemoryCollection([new KeyValuePair("Database__ConnectionString", connectionString)])); // "Database__SchemaName" + builder.UseEnvironment(Database.Program.TestEnvironment); + builder.ConfigureAppConfiguration((webHostBuilderContext, configurationBuilder) => + { + configurationBuilder.Sources.Clear(); + Database.Program.ConfigureAppConfiguration( + configurationBuilder, + webHostBuilderContext.HostingEnvironment, + [] + ); + } + ); + builder.ConfigureServices(serviceCollection => + { + using var scope = serviceCollection.BuildServiceProvider().CreateScope(); + // Configure `AppSettings` + var appSettings = scope.ServiceProvider.GetRequiredService(); + appSettings.Database.ConnectionString = connectionString; + // `appSettings.Database.ConnectionString` should readonly. + // However, the commented code below together with + // `AddInMemoryCollection` above, does not configure the + // `ConnectionString` in such a way, that Npgsql knows about it. + // It uses the empty connection string from the original app + // settings in `Startup.cs`. Why? + // var oldAppSettings = scope.ServiceProvider.GetRequiredService(); + // var newAppSettings = oldAppSettings with + // { + // Database = oldAppSettings.Database with + // { + // ConnectionString = connectionString + // // SchemaName = schemaName + // } + // } + // ; + // var appSettingsServiceDescriptor = + // serviceCollection.SingleOrDefault(d => + // d.ServiceType == typeof(AppSettings) + // ); + // if (appSettingsServiceDescriptor is not null) + // { + // serviceCollection.Remove(appSettingsServiceDescriptor); + // } + // serviceCollection.AddSingleton(newAppSettings); + // Configure `IEmailSender` + var emailSenderServiceDescriptor = + serviceCollection.SingleOrDefault(d => + d.ServiceType == typeof(IEmailSender) + ); + if (emailSenderServiceDescriptor is not null) + { + serviceCollection.Remove(emailSenderServiceDescriptor); + } + serviceCollection.AddTransient(_ => EmailSender); + } + ); + } + + // https://docs.microsoft.com/en-us/dotnet/standard/managed-code + // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose + ~CustomWebApplicationFactory() + { + Dispose(false); + } + + protected override void Dispose(bool disposing) + { + if (!_disposed) + { + Do( + services => + { + services + .GetRequiredService() + .Database + .EnsureDeleted(); + } + ); + _disposed = true; + } + + base.Dispose(disposing); + } +} \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs new file mode 100644 index 00000000..e2198aaf --- /dev/null +++ b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs @@ -0,0 +1,25 @@ +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Threading.Tasks; +using FluentAssertions; +using NUnit.Framework; +using Snapshooter.NUnit; + +namespace Database.Tests.Integration.GraphQl; + +[TestFixture] +public sealed class GraphQlSchemaTests + : IntegrationTests +{ + [Test] + [SuppressMessage("Naming", "CA1707")] + public async Task IsUnchanged() + { + // Act + var response = await HttpClient.GetAsync("/graphql?sdl"); + // Assert + response.StatusCode.Should().Be(HttpStatusCode.OK); + var schema = await response.Content.ReadAsStringAsync(); + Snapshot.Match(schema); + } +} \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap new file mode 100644 index 00000000..cab1debe --- /dev/null +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -0,0 +1,1732 @@ +schema { + query: Query + mutation: Mutation +} + +interface Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + uuid: Uuid! + warnings: [String!]! +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +type AddDataApprovalError { + code: AddDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type AddDataApprovalPayload { + dataApproval: DataApproval + errors: [AddDataApprovalError!] + query: Query! +} + +type AddInstitutionAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [InstitutionAccessRightsError!] + query: Query! +} + +type Address { + formatted: String! +} + +type AppliedMethod { + arguments: [NamedMethodArgument!]! + methodId: Uuid! + sources: [NamedMethodSource!]! +} + +type CalculateMethodError { + code: CalculateMethodErrorCode! + message: String! + path: [String!]! +} + +type CalculateMethodPayload { + errors: [CalculateMethodError!] + result: Any +} + +type CalorimetricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + gValues: [Float!]! + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + uuid: Uuid! @cost(weight: "10") + uValues: [Float!]! + warnings: [String!]! +} + +"A connection to a list of items." +type CalorimetricDataConnection { + "A list of edges." + edges: [CalorimetricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type CalorimetricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: CalorimetricData! +} + +type CielabColor { + aStar: Float! + bStar: Float! + lStar: Float! +} + +type CreateCalorimetricDataError { + code: CreateCalorimetricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateCalorimetricDataPayload { + calorimetricData: CalorimetricData + errors: [CreateCalorimetricDataError!] + query: Query! +} + +type CreateGeometricDataError { + code: CreateGeometricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateGeometricDataPayload { + errors: [CreateGeometricDataError!] + geometricData: GeometricData + query: Query! +} + +type CreateGetHttpsResourceError { + code: CreateGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type CreateGetHttpsResourcePayload { + errors: [CreateGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type CreateHygrothermalDataError { + code: CreateHygrothermalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateHygrothermalDataPayload { + errors: [CreateHygrothermalDataError!] + hygrothermalData: HygrothermalData + query: Query! +} + +type CreateOpticalDataError { + code: CreateOpticalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateOpticalDataPayload { + errors: [CreateOpticalDataError!] + opticalData: OpticalData + query: Query! +} + +type CreatePhotovoltaicDataError { + code: CreatePhotovoltaicDataErrorCode! + message: String! + path: [String!]! +} + +type CreatePhotovoltaicDataPayload { + errors: [CreatePhotovoltaicDataError!] + photovoltaicData: PhotovoltaicData + query: Query! +} + +type CreateResponseApprovalsError { + code: CreateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type CreateResponseApprovalsPayload { + data: [Data!] + errors: [CreateResponseApprovalsError!] + query: Query! +} + +type CrossDatabaseDataReference { + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +type DataAccessRights { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] + hasRestrictions: Boolean! + hasRestrictionsByApplication: Boolean! + hasRestrictionsByInstitution: Boolean! + hasRestrictionsByUser: Boolean! +} + +type DataApproval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: Reference @cost(weight: "10") + timestamp: DateTime! + variables: Any! +} + +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + +type Database { + canCurrentUserUpdateNode: Boolean! + canCurrentUserVerifyNode: Boolean! + description: String! + locator: Url! + name: String! + operator: DatabaseOperatorEdge! + uuid: Uuid! + verificationCode: String! + verificationState: DatabaseVerificationState! +} + +type DatabaseOperatorEdge { + node: Institution! +} + +type FileMetaInformation { + dataFormatId: Uuid! + path: [String!]! +} + +type GeometricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + thicknesses: [Float!]! + timestamp: DateTime! + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type GeometricDataConnection { + "A list of edges." + edges: [GeometricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GeometricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GeometricData! +} + +type GetHttpsResource implements Node { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod + archivedFilesMetaInformation: [FileMetaInformation!]! + children: [GetHttpsResource!]! @cost(weight: "10") + data: Data @cost(weight: "10") + dataFormatId: Uuid! + description: String + hashValue: String! + id: ID! + locator: Url! + parent: GetHttpsResource @cost(weight: "10") + uuid: Uuid! @cost(weight: "10") +} + +"A connection to a list of items." +type GetHttpsResourceConnection { + "A list of edges." + edges: [GetHttpsResourceEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GetHttpsResourceEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GetHttpsResource! +} + +type GetHttpsResourceTree { + nonRootVertices: [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + root: GetHttpsResourceTreeRoot! @cost(weight: "10") +} + +type GetHttpsResourceTreeNonRootVertex { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod! + parentId: ID! + value: GetHttpsResource! + vertexId: ID! +} + +type GetHttpsResourceTreeRoot { + value: GetHttpsResource! + vertexId: ID! +} + +type HygrothermalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type HygrothermalDataConnection { + "A list of edges." + edges: [HygrothermalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type HygrothermalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: HygrothermalData! +} + +type Institution { + uuid: Uuid! +} + +type InstitutionAccessRights { + allowedDatasetsPerTime: NonNegativeInt + allowedUserCount: NonNegativeInt + hasRestrictions: Boolean! + hasRestrictionsByTime: Boolean! + hasRestrictionsByUser: Boolean! + id: Uuid! + institutionId: Uuid! + period: TimeSpan! + userAlreadyAccessed: [Uuid!]! + version: NonNegativeInt! +} + +type InstitutionAccessRightsError { + code: InstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type KeyValuePairOfGuidAndNullableOfUInt32 { + key: Uuid! + value: NonNegativeInt +} + +type Mutation { + addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") + addDataApproval(input: DataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") +} + +type NamedMethodArgument { + name: String! + value: Any! +} + +type NamedMethodSource { + name: String! + value: CrossDatabaseDataReference! +} + +type Numeration { + mainNumber: String! + prefix: String + suffix: String +} + +type OpenEndedDateTimeRange { + from: DateTime @cost(weight: "10") + until: DateTime @cost(weight: "10") +} + +type OpticalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + cielabColors: [CielabColor!]! + coatedSide: CoatedSide + colorRenderingIndices: [Float!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + infraredEmittances: [Float!]! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + resources: [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + subtype: OpticalComponentSubtype + timestamp: DateTime! + type: OpticalComponentType + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type OpticalDataConnection { + "A list of edges." + edges: [OpticalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type OpticalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: OpticalData! +} + +"Information about pagination in a connection." +type PageInfo { + "When paginating forwards, the cursor to continue." + endCursor: String + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String +} + +type PhotovoltaicData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type PhotovoltaicDataConnection { + "A list of edges." + edges: [PhotovoltaicDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type PhotovoltaicDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: PhotovoltaicData! +} + +type Publication { + abstract: String + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: String + authors: [String!] + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: String + "Referenced section" + section: String + title: String + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: String + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: Url +} + +type Query { + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") + currentUser: User @cost(weight: "10") + currentUserInfo: UserInfo! @cost(weight: "10") + data(id: Uuid! locale: Locale): Data @cost(weight: "10") + database: Database! @cost(weight: "10") + geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") + getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + "Fetches an object given its ID." + node("ID of the object." id: ID!): Node @cost(weight: "10") + "Lookup nodes by a list of IDs." + nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") + verificationCode: String! +} + +type RemoveDataApprovalError { + code: RemoveDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type RemoveDataApprovalPayload { + dataApproval: DataApproval + errors: [RemoveDataApprovalError!] + query: Query! +} + +type ResponseApproval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +type Standard { + abstract: String + locator: Url + numeration: Numeration! + "The section of the standard to which the reference refers to." + section: String + standardizers: [Standardizer!]! + title: String + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: Int +} + +type ToTreeVertexAppliedConversionMethod { + arguments: [NamedMethodArgument!]! + methodId: Uuid! + sourceName: String! +} + +type UpdateAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [InstitutionAccessRightsError!] + query: Query! +} + +type UpdateDataAccessRightsError { + code: UpdateDataAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataAccessRightsPayload { + dataAccessRights: DataAccessRights + errors: [UpdateDataAccessRightsError!] + query: Query! +} + +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! + message: String! + path: [String!]! +} + +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] + query: Query! +} + +type UpdateResponseApprovalsError { + code: UpdateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type UpdateResponseApprovalsPayload { + data: [Data!] + errors: [UpdateResponseApprovalsError!] + query: Query! +} + +type User implements Node { + id: ID! + name: String! + subject: String! + uuid: Uuid! @cost(weight: "10") +} + +type UserInfo { + address: Address + email: String! + emailVerified: Boolean! + name: String! + phoneNumber: String + phoneNumberVerified: Boolean! + roles: [String!] + sub: String! + website: String +} + +union Reference = Publication | Standard + +input AppliedMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sources: [NamedMethodSourceInput!]! +} + +input AppliedMethodPropositionInput { + and: [AppliedMethodPropositionInput!] + or: [AppliedMethodPropositionInput!] + methodId: UuidPropositionInput + arguments: ListNamedMethodArgumentFilterTypePropositionInput + sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput +} + +input AppliedMethodSortInput { + methodId: SortEnumType @cost(weight: "10") +} + +input BooleanPropositionInput { + equalTo: Boolean @cost(weight: "10") + notEqualTo: Boolean @cost(weight: "10") +} + +input CalorimetricDataPropositionInput { + and: [CalorimetricDataPropositionInput!] + or: [CalorimetricDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput + gValues: FloatsPropositionInput + uValues: FloatsPropositionInput +} + +input CalorimetricDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") +} + +input CielabColorInput { + aStar: Float! + bStar: Float! + lStar: Float! +} + +input CielabColorPropositionInput { + and: [CielabColorPropositionInput!] + or: [CielabColorPropositionInput!] + lStar: FloatPropositionInput + aStar: FloatPropositionInput + bStar: FloatPropositionInput +} + +input CreateCalorimetricDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + gValues: [Float!]! + locale: String! + name: String + rootResource: RootGetHttpsResourceInput! + uValues: [Float!]! + warnings: [String!]! +} + +input CreateGeometricDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: String! + name: String + rootResource: RootGetHttpsResourceInput! + thicknesses: [Float!]! + warnings: [String!]! +} + +input CreateGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String! + hashValue: String! + parentId: Uuid +} + +input CreateHygrothermalDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: String! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + +input CreateNumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + +input CreateOpticalDataInput { + appliedMethod: AppliedMethodInput! + cielabColors: [CielabColorInput!]! + coatedSide: CoatedSide + colorRenderingIndices: [Float!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + infraredEmittances: [Float!]! + locale: String! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + rootResource: RootGetHttpsResourceInput! + subtype: OpticalComponentSubtype + type: OpticalComponentType + warnings: [String!]! +} + +input CreatePhotovoltaicDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: String! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + +input CrossDatabaseDataReferenceInput { + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +input CrossDatabaseDataReferencePropositionInput { + and: [CrossDatabaseDataReferencePropositionInput!] + or: [CrossDatabaseDataReferencePropositionInput!] + dataId: UuidPropositionInput + dataTimestamp: DateTimePropositionInput + dataKind: DataKindPropositionInput + databaseId: UuidPropositionInput +} + +input DataAccessRightsInput { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + dataId: Uuid! +} + +input DataApprovalInput { + approverId: Uuid! + dataId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: ReferenceInput! + timestamp: DateTime! + variables: Any! +} + +input DataApprovalPropositionInput { + and: [DataApprovalPropositionInput!] + or: [DataApprovalPropositionInput!] + approverId: UuidPropositionInput + timestamp: DateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + statement: ReferencePropositionInput +} + +input DataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + in: [DataKind!] @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") +} + +input DataPropositionInput { + and: [DataPropositionInput!] + or: [DataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput +} + +input DateTimePropositionInput { + equalTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + in: [DateTime!] @cost(weight: "10") + notIn: [DateTime!] @cost(weight: "10") + greaterThan: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + greaterThanOrEqualTo: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + lessThan: DateTime @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") + lessThanOrEqualTo: DateTime @cost(weight: "10") + notLessThanOrEqualTo: DateTime @cost(weight: "10") +} + +input FileMetaInformationInput { + dataFormatId: Uuid! + path: [String!]! +} + +input FileMetaInformationPropositionInput { + and: [FileMetaInformationPropositionInput!] + or: [FileMetaInformationPropositionInput!] + path: StringsPropositionInput + dataFormatId: UuidPropositionInput +} + +input FloatPropositionInput { + equalTo: Float @cost(weight: "10") + notEqualTo: Float @cost(weight: "10") + in: [Float!] @cost(weight: "10") + notIn: [Float!] @cost(weight: "10") + greaterThan: Float @cost(weight: "10") + notGreaterThan: Float @cost(weight: "10") + greaterThanOrEqualTo: Float @cost(weight: "10") + notGreaterThanOrEqualTo: Float @cost(weight: "10") + lessThan: Float @cost(weight: "10") + notLessThan: Float @cost(weight: "10") + lessThanOrEqualTo: Float @cost(weight: "10") + notLessThanOrEqualTo: Float @cost(weight: "10") +} + +input FloatsPropositionInput { + all: FloatPropositionInput @cost(weight: "10") + none: FloatPropositionInput @cost(weight: "10") + some: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input GeometricDataPropositionInput { + and: [GeometricDataPropositionInput!] + or: [GeometricDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput + thicknesses: FloatsPropositionInput +} + +input GeometricDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") +} + +input GetHttpsResourcePropositionInput { + and: [GetHttpsResourcePropositionInput!] + or: [GetHttpsResourcePropositionInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + data: DataPropositionInput + parent: GetHttpsResourcePropositionInput +} + +input GetHttpsResourceSortInput { + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + data: IDataSortInput @cost(weight: "10") + parent: GetHttpsResourceSortInput @cost(weight: "10") +} + +input HygrothermalDataPropositionInput { + and: [HygrothermalDataPropositionInput!] + or: [HygrothermalDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput +} + +input HygrothermalDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") +} + +input IDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input InstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + +input Int32PropositionInput { + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int!] @cost(weight: "10") + notIn: [Int!] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") +} + +input JsonElementPropositionInput { + and: [JsonElementPropositionInput!] + or: [JsonElementPropositionInput!] + valueKind: JsonValueKindPropositionInput +} + +input JsonValueKindPropositionInput { + equalTo: JsonValueKind @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") + in: [JsonValueKind!] @cost(weight: "10") + notIn: [JsonValueKind!] @cost(weight: "10") +} + +input KeyValuePairOfGuidAndNullableOfUInt32Input { + key: Uuid! + value: NonNegativeInt +} + +input ListFilterInputTypeOfCielabColorPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfDataApprovalPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfFileMetaInformationPropositionInput { + all: FileMetaInformationPropositionInput @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfNamedMethodSourcePropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListGetHttpsResourceFilterTypePropositionInput { + all: GetHttpsResourcePropositionInput @cost(weight: "10") + none: GetHttpsResourcePropositionInput @cost(weight: "10") + some: GetHttpsResourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListNamedMethodArgumentFilterTypePropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListStandardizerPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input NamedMethodArgumentInput { + name: String! + value: Any! +} + +input NamedMethodArgumentPropositionInput { + and: [NamedMethodArgumentPropositionInput!] + or: [NamedMethodArgumentPropositionInput!] + name: StringPropositionInput +} + +input NamedMethodSourceInput { + name: String! + value: CrossDatabaseDataReferenceInput! +} + +input NamedMethodSourcePropositionInput { + and: [NamedMethodSourcePropositionInput!] + or: [NamedMethodSourcePropositionInput!] + name: StringPropositionInput + value: CrossDatabaseDataReferencePropositionInput +} + +input NullableOfCoatedSidePropositionInput { + equalTo: CoatedSide @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") + in: [CoatedSide] @cost(weight: "10") + notIn: [CoatedSide] @cost(weight: "10") +} + +input NullableOfInt32PropositionInput { + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + notIn: [Int] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") +} + +input NullableOfOpticalComponentSubtypePropositionInput { + equalTo: OpticalComponentSubtype @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") + in: [OpticalComponentSubtype] @cost(weight: "10") + notIn: [OpticalComponentSubtype] @cost(weight: "10") +} + +input NullableOfOpticalComponentTypePropositionInput { + equalTo: OpticalComponentType @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") + in: [OpticalComponentType] @cost(weight: "10") + notIn: [OpticalComponentType] @cost(weight: "10") +} + +input NumerationPropositionInput { + and: [NumerationPropositionInput!] + or: [NumerationPropositionInput!] + prefix: StringPropositionInput + mainNumber: StringPropositionInput + suffix: StringPropositionInput +} + +input OpticalDataPropositionInput { + and: [OpticalDataPropositionInput!] + or: [OpticalDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput + type: NullableOfOpticalComponentTypePropositionInput + subtype: NullableOfOpticalComponentSubtypePropositionInput + coatedSide: NullableOfCoatedSidePropositionInput + nearnormalHemisphericalSolarReflectances: FloatsPropositionInput + nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput + nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput + nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput + infraredEmittances: FloatsPropositionInput + colorRenderingIndices: FloatsPropositionInput + cielabColors: ListFilterInputTypeOfCielabColorPropositionInput +} + +input OpticalDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") +} + +input PhotovoltaicDataPropositionInput { + and: [PhotovoltaicDataPropositionInput!] + or: [PhotovoltaicDataPropositionInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput + id: UuidPropositionInput +} + +input PhotovoltaicDataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") +} + +input PublicationInput { + abstract: String + arXiv: String + authors: [String!] + doi: String + section: String + title: String + urn: String + webAddress: Url +} + +input PublicationPropositionInput { + and: [PublicationPropositionInput!] + or: [PublicationPropositionInput!] + authors: StringsPropositionInput + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: StringPropositionInput + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: StringPropositionInput + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: StringPropositionInput + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "Referenced section" + section: StringPropositionInput +} + +input ReferenceInput { + publication: PublicationInput + standard: StandardInput +} + +input ReferencePropositionInput { + and: [ReferencePropositionInput!] + or: [ReferencePropositionInput!] + standard: StandardPropositionInput + publication: PublicationPropositionInput +} + +input RootGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + hashValue: String! +} + +input StandardInput { + abstract: String + locator: Url + numeration: CreateNumerationInput! + section: String + standardizers: [Standardizer!]! + title: String + year: Int +} + +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardPropositionInput { + and: [StandardPropositionInput!] + or: [StandardPropositionInput!] + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: NullableOfInt32PropositionInput + numeration: NumerationPropositionInput + standardizers: ListStandardizerPropositionInput + locator: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "The section of the standard to which the reference refers to." + section: StringPropositionInput +} + +input StandardizerPropositionInput { + equalTo: Standardizer @cost(weight: "10") + notEqualTo: Standardizer @cost(weight: "10") + in: [Standardizer!] @cost(weight: "10") + notIn: [Standardizer!] @cost(weight: "10") +} + +input StringPropositionInput { + and: [StringPropositionInput!] + or: [StringPropositionInput!] + equalTo: String @cost(weight: "10") + notEqualTo: String @cost(weight: "10") + contains: String @cost(weight: "20") + doesNotContain: String @cost(weight: "20") + in: [String] @cost(weight: "10") + notIn: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + doesNotStartWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + doesNotEndWith: String @cost(weight: "20") +} + +input StringsPropositionInput { + all: StringPropositionInput @cost(weight: "10") + none: StringPropositionInput @cost(weight: "10") + some: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ToTreeVertexAppliedConversionMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sourceName: String! +} + +input ToTreeVertexAppliedConversionMethodPropositionInput { + and: [ToTreeVertexAppliedConversionMethodPropositionInput!] + or: [ToTreeVertexAppliedConversionMethodPropositionInput!] + methodId: UuidPropositionInput + arguments: ListNamedMethodArgumentFilterTypePropositionInput + sourceName: StringPropositionInput +} + +input ToTreeVertexAppliedConversionMethodSortInput { + methodId: SortEnumType @cost(weight: "10") + sourceName: SortEnumType @cost(weight: "10") +} + +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + +input UriHostNameTypePropositionInput { + equalTo: UriHostNameType @cost(weight: "10") + notEqualTo: UriHostNameType @cost(weight: "10") + in: [UriHostNameType!] @cost(weight: "10") + notIn: [UriHostNameType!] @cost(weight: "10") +} + +input UriPropositionInput { + and: [UriPropositionInput!] + or: [UriPropositionInput!] + absolutePath: StringPropositionInput + absoluteUri: StringPropositionInput + localPath: StringPropositionInput + authority: StringPropositionInput + hostNameType: UriHostNameTypePropositionInput + isDefaultPort: BooleanPropositionInput + isFile: BooleanPropositionInput + isLoopback: BooleanPropositionInput + pathAndQuery: StringPropositionInput + segments: StringsPropositionInput + isUnc: BooleanPropositionInput + host: StringPropositionInput + port: Int32PropositionInput + query: StringPropositionInput + fragment: StringPropositionInput + scheme: StringPropositionInput + originalString: StringPropositionInput + dnsSafeHost: StringPropositionInput + idnHost: StringPropositionInput + isAbsoluteUri: BooleanPropositionInput + userEscaped: BooleanPropositionInput + userInfo: StringPropositionInput +} + +input UuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid!] @cost(weight: "10") + notIn: [Uuid!] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + +enum AddDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED + AMBIGUOUS_STATEMENT + MISSING_STATEMENT + INVALID_KEY_FINGERPRINT + FAILED_RECEIVING_KEY + BAD_SIGNATURE + FAILED_VERIFYING_SIGNATURE + FAILED_VERIFYING_RESPONSE + ILLEGAL_MESSAGE + WRONG_RESPONSE + WRONG_STATEMENT +} + +"Defines when a policy shall be executed." +enum ApplyPolicy { + "Before the resolver was executed." + BEFORE_RESOLVER + "After the resolver was executed." + AFTER_RESOLVER + "The policy is applied in the validation step before the execution." + VALIDATION +} + +enum CalculateMethodErrorCode { + UNKNOWN_METHOD + UNKNOWN_DATABASE + DATA_QUERY_FAILED +} + +enum CoatedSide { + PRIME + NON_PRIME + BOTH + NEITHER + UNKNOWN + NOT_APPLICABLE +} + +enum CreateCalorimetricDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateGeometricDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateGetHttpsResourceErrorCode { + UNKNOWN + UNKNOWN_DATA + UNAUTHORIZED + UNAUTHENTICATED +} + +enum CreateHygrothermalDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateOpticalDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreatePhotovoltaicDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum DataKind { + CALORIMETRIC_DATA + GEOMETRIC_DATA + HYGROTHERMAL_DATA + OPTICAL_DATA + PHOTOVOLTAIC_DATA +} + +enum DatabaseVerificationState { + PENDING + VERIFIED +} + +enum InstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + ALREADY_EXISTS + UNKNOWN_ACCESS_RIGHTS +} + +enum JsonValueKind { + UNDEFINED + OBJECT + ARRAY + STRING + NUMBER + TRUE + FALSE + NULL +} + +enum OpticalComponentSubtype { + MONOLITHIC + LAMINATE + INTERLAYER + EMBEDDED_COATING + COATED + COATING + APPLIED_FILM + FILM + VENETIAN_BLIND + DIFFUSING_SHADE + ROLLER_SHADE + WOVEN_SHADE + VERTICAL_LOUVER + PERFORATED_SCREEN + CELLULAR_SHADE + PLEATED_SHADE + ROMAN_SHADE + SHADE_MATERIAL + FRITTED_GLASS + ACID_ETCHED_GLASS + SANDBLASTED_GLASS + CHROMOGENIC +} + +enum OpticalComponentType { + GLAZING + SHADING +} + +enum RemoveDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPROVAL + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum SortEnumType { + ASC + DESC +} + +enum Standardizer { + AERC + AGI + ASHRAE + BREEAM + BS + BSI + CEN + CIE + DGNB + DIN + DVWG + IEC + IES + IFT + ISO + JIS + LEED + NFRC + RIBA + UL + UNECE + VDI + VFF + WELL +} + +enum UpdateDataAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE +} + +enum UpdateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + UPDATING_RESPONSE_APPROVAL_FAILED +} + +enum UriHostNameType { + UNKNOWN + BASIC + DNS + I_PV4 + I_PV6 +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION + +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + +scalar Any + +"The `DateTime` scalar represents an ISO-8601 compliant date time type." +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") + +"BCP 47 compliant Language Tag string" +scalar Locale + +"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." +scalar NonNegativeInt + +"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." +scalar TimeSpan + +"The `Upload` scalar type represents a file upload." +scalar Upload + +scalar Url + +scalar Uuid diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs new file mode 100644 index 00000000..d18af115 --- /dev/null +++ b/backend/test/Integration/IntegrationTests.cs @@ -0,0 +1,458 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using FluentAssertions; +using IdentityModel.Client; +using Json.Path; +using NUnit.Framework; +using Snapshooter; +using TokenResponse = IdentityModel.Client.TokenResponse; +using WebApplicationFactoryClientOptions = Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions; +using Database.Data; + +namespace Database.Tests.Integration; + +[FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +public abstract partial class IntegrationTests + : IDisposable +{ + private static readonly JsonSerializerOptions s_customJsonSerializerOptions = + new() + { + Converters = { new JsonStringEnumConverter() }, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + private bool _disposed; + + protected IntegrationTests() + { + Factory = new CustomWebApplicationFactory(); + HttpClient = CreateHttpClient(); + } + + private CustomWebApplicationFactory Factory { get; } + + protected CollectingEmailSender EmailSender => Factory.EmailSender; + + protected AppSettings AppSettings => Factory.AppSettings; + + protected HttpClient HttpClient { get; } + + public Task DoAsync(Func what) + { + return Factory.DoAsync(what); + } + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + // Suppress finalization. + GC.SuppressFinalize(this); + } + + // https://docs.microsoft.com/en-us/dotnet/standard/managed-code + // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose + ~IntegrationTests() + { + Dispose(false); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + Factory.Dispose(); + HttpClient.Dispose(); + } + + _disposed = true; + } + } + + private static IEnumerable ExtractCookies(HttpResponseMessage response) + { + if (!response.Headers.TryGetValues("Set-Cookie", out var cookieEntries)) + { + return []; + } + var uri = response.RequestMessage?.RequestUri ?? throw new ArgumentException($"The request URI cannot be extracted from the given response {response}."); + var cookieContainer = new CookieContainer(); + foreach (var cookieEntry in cookieEntries) + { + cookieContainer.SetCookies(uri, cookieEntry); + } + return cookieContainer.GetCookies(uri).Cast(); + } + + private static async Task UpdateAntiforgeryCookieAndToken( + HttpClient httpClient + ) + { + // Get the antiforgery token in the cookie "XSRF-TOKEN" and set it + // permanently on the HTTP client by requesting /antiforgery/token + // synchronously. + var response = await httpClient.GetAsync("/antiforgery/token"); + // Add the antiforgery token as the default request header "X-XSRF-TOKEN". + var xsrfToken = + ExtractCookies(response) + .SingleOrDefault(cookie => cookie.Name == "XSRF-TOKEN") + ?.Value + ?? throw new ArgumentException("The `XSRF-TOKEN` cookie is missing in the response of a request to /antiforgery/token"); + httpClient.DefaultRequestHeaders.Remove("X-XSRF-TOKEN"); + httpClient.DefaultRequestHeaders.Add("X-XSRF-TOKEN", xsrfToken); + } + + protected static HttpClient CreateHttpClient( + CustomWebApplicationFactory factory, + bool allowAutoRedirect = true + ) + { + var httpClient = factory.CreateClient( + new WebApplicationFactoryClientOptions + { + AllowAutoRedirect = allowAutoRedirect, + BaseAddress = new Uri("http://localhost", UriKind.Absolute), + HandleCookies = true, + MaxAutomaticRedirections = 3 + } + ); + // TODO Why does running `Task.Run(() => UpdateAntiforgeryCookieAndToken(httpClient)).Wait();` here fails with "cookie is missing" here? It works in the metabase. + return httpClient; + } + + protected HttpClient CreateHttpClient( + bool allowAutoRedirect = true + ) + { + return CreateHttpClient( + Factory, + allowAutoRedirect + ); + } + + protected Task QueryGraphQl( + string query, + string? operationName = null, + object? variables = null + ) + { + return QueryGraphQl( + HttpClient, + query, + operationName, + variables + ); + } + + protected static Task QueryGraphQl( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + return httpClient.PostAsync( + "/graphql", + MakeJsonHttpContent( + new GraphQlRequest( + query, + operationName, + variables + ) + ) + ); + } + + protected Task SuccessfullyQueryGraphQlContent( + string query, + string? operationName = null, + object? variables = null + ) + { + return SuccessfullyQueryGraphQlContent( + HttpClient, + query, + operationName, + variables + ); + } + + protected static async Task SuccessfullyQueryGraphQlContent( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + var httpResponseMessage = await QueryGraphQl( + httpClient, + query, + operationName, + variables + ); + if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + { + // We wrap this check in an if-condition such that the message + // content is only read when the status code is not 200. + httpResponseMessage.StatusCode.Should().Be( + HttpStatusCode.OK, + await httpResponseMessage.Content.ReadAsStringAsync() + ); + } + + return httpResponseMessage.Content; + } + + protected Task UnsuccessfullyQueryGraphQlContent( + string query, + string? operationName = null, + object? variables = null + ) + { + return UnsuccessfullyQueryGraphQlContent( + HttpClient, + query, + operationName, + variables + ); + } + + protected static async Task UnsuccessfullyQueryGraphQlContent( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + var httpResponseMessage = await QueryGraphQl( + httpClient, + query, + operationName, + variables + ); + if (httpResponseMessage.StatusCode == HttpStatusCode.OK) + { + // We wrap this check in an if-condition such that the message + // content is only read when the status code is not 200. + httpResponseMessage.StatusCode.Should().NotBe( + HttpStatusCode.OK, + await httpResponseMessage.Content.ReadAsStringAsync() + ); + } + + return httpResponseMessage.Content; + } + + protected Task SuccessfullyQueryGraphQlContentAsString( + string query, + string? operationName = null, + object? variables = null + ) + { + return SuccessfullyQueryGraphQlContentAsString( + HttpClient, + query, + operationName, + variables + ); + } + + protected static async Task SuccessfullyQueryGraphQlContentAsString( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + return await ( + await SuccessfullyQueryGraphQlContent( + httpClient, + query, + operationName, + variables + ) + ) + .ReadAsStringAsync(); + } + + protected Task SuccessfullyQueryGraphQlContentAsJson( + string query, + string? operationName = null, + object? variables = null + ) + { + return SuccessfullyQueryGraphQlContentAsJson( + HttpClient, + query, + operationName, + variables + ); + } + + protected static async Task SuccessfullyQueryGraphQlContentAsJson( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + using var document = await JsonDocument.ParseAsync( + await ( + await SuccessfullyQueryGraphQlContent( + httpClient, + query, + operationName, + variables + ) + ) + .ReadAsStreamAsync() + ); + return document.RootElement.Clone(); + } + + protected Task UnsuccessfullyQueryGraphQlContentAsString( + string query, + string? operationName = null, + object? variables = null + ) + { + return UnsuccessfullyQueryGraphQlContentAsString( + HttpClient, + query, + operationName, + variables + ); + } + + protected static async Task UnsuccessfullyQueryGraphQlContentAsString( + HttpClient httpClient, + string query, + string? operationName = null, + object? variables = null + ) + { + return await ( + await UnsuccessfullyQueryGraphQlContent( + httpClient, + query, + operationName, + variables + ) + ) + .ReadAsStringAsync(); + } + + protected static string ExtractString( + string jsonPath, + JsonElement jsonElement + ) + { + var pathResult = + JsonPath.Parse(jsonPath).Evaluate( + JsonObject.Create(jsonElement) + ); + + return pathResult.Matches?.Single()?.Value?.GetValue() + ?? throw new ArgumentException("String is null"); + } + + protected static Guid ExtractUuid( + string jsonPath, + JsonElement jsonElement + ) + { + return new Guid( + ExtractString( + jsonPath, + jsonElement + ) + ); + } + + protected static string Base64Encode(string text) + { + return Convert.ToBase64String( + Encoding.UTF8.GetBytes(text) + ); + } + + protected static string Base64Decode(string text) + { + return Encoding.UTF8.GetString( + Convert.FromBase64String(text) + ); + } + + protected void EmailsShouldContainSingle( + (string name, string address) recipient, + string subject, + string bodyRegEx + ) + { + EmailSender.Emails.Should().ContainSingle(); + var email = EmailSender.Emails.First(); + email.Recipient.Should().Be(recipient); + email.Subject.Should().Be(subject); + email.Body.Should().MatchRegex(bodyRegEx); + } + + private static ByteArrayContent MakeJsonHttpContent( + TContent content + ) + { + var result = + new ByteArrayContent( + JsonSerializer.SerializeToUtf8Bytes( + content, + s_customJsonSerializerOptions + ) + ); + result.Headers.ContentType = + new MediaTypeHeaderValue("application/json"); + return result; + } + + // With NUnit using async Snapshooter is not able to calculate + // the necessary Fullname, due to reasons mentioned in + // https://stackoverflow.com/questions/22598323/movenext-instead-of-actual-method-task-name + // The workaround with optional parameters is inspired by the same source. + protected static SnapshotFullName SnapshotFullNameHelper( + Type testType, + string keyName, + [CallerMemberName] string testMethod = "", + [CallerFilePath] string testFilePath = "" + ) + { + var testName = $"{testType.Name}.{testMethod}_{keyName}.snap"; + var testDirectory = + Path.GetDirectoryName(testFilePath) + ?? throw new ArgumentException($"The path '{testFilePath}' denotes a root directory or is `null`."); + return new SnapshotFullName(testName, testDirectory); + } + + private sealed class GraphQlRequest( + string query, + string? operationName, + object? variables + ) + { + public string Query { get; } = query; + public string? OperationName { get; } = operationName; + public object? Variables { get; } = variables; + } +} \ No newline at end of file diff --git a/backend/test/Integration/MigrationTests.cs b/backend/test/Integration/MigrationTests.cs new file mode 100644 index 00000000..841858d8 --- /dev/null +++ b/backend/test/Integration/MigrationTests.cs @@ -0,0 +1,50 @@ +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations.Operations; +using NUnit.Framework; + +namespace Database.Tests.Integration; + +[TestFixture] +public sealed class MigrationTests + : IntegrationTests +{ + // Inspired by https://www.meziantou.net/detect-missing-migrations-in-entity-framework-core.htm + [Test] + public Task EnsureMigrationsAreUpToDate() + { + return DoAsync(async context => + { + // Get required services from the dbcontext + var migrationModelDiffer = context.GetService(); + var migrationsAssembly = context.GetService(); + var modelRuntimeInitializer = context.GetService(); + var designTimeModel = context.GetService(); + // Get current model + var model = designTimeModel.Model; + // Get the snapshot model and finalize it + var snapshotModel = migrationsAssembly.ModelSnapshot?.Model; + if (snapshotModel is IMutableModel mutableModel) + { + // Forces post-processing on the model such that it is ready for use by the runtime + snapshotModel = mutableModel.FinalizeModel(); + } + if (snapshotModel is not null) + { + // Validates and initializes the given model with runtime dependencies + snapshotModel = modelRuntimeInitializer.Initialize(snapshotModel); + } + // Compute differences + var modelDifferences = migrationModelDiffer.GetDifferences( + source: snapshotModel?.GetRelationalModel(), + target: model.GetRelationalModel()); + // The differences should be empty if the migrations are up-to-date + modelDifferences.Should().BeEquivalentTo(Enumerable.Empty()); // .BeEmpty(); + }); + } +} \ No newline at end of file From e8728f02ff31e9f2d4f4e543c93e850636ec5c66 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Oct 2025 13:56:35 +0200 Subject: [PATCH 499/878] Compute the hash value of a GET HTTPS resource after the file has been uploaded and add a mutation to recompute all hash values --- .../src/Controllers/FileUploadController.cs | 108 +++++++--------- backend/src/Data/GetHttpsResource.cs | 13 ++ .../GetHttpsResourceMutations.cs | 117 ++++++++++++++---- ...ecomputeGetHttpsResourceHashValuesError.cs | 12 ++ ...puteGetHttpsResourceHashValuesErrorCode.cs | 12 ++ ...omputeGetHttpsResourceHashValuesPayload.cs | 34 +++++ backend/src/Utilities/Sha256FileHasher.cs | 23 ++++ 7 files changed, 229 insertions(+), 90 deletions(-) create mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs create mode 100644 backend/src/Utilities/Sha256FileHasher.cs diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7b367f84..83273183 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -24,36 +24,28 @@ public static partial class Log [LoggerMessage( EventId = 0, Level = LogLevel.Information, - Message = - "Uploaded file '{TrustedFileNameForDisplay}' saved to '{TargetDirectoryPath}' as '{TrustedFileNameForFileStorage}'")] + Message = "Uploaded file '{FileNameForDisplay}' to '{FilePath}'.")] public static partial void SavedUploadedFile( this ILogger logger, - string? trustedFileNameForDisplay, string targetDirectoryPath, - string trustedFileNameForFileStorage + string? fileNameForDisplay, + string filePath ); } // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ public sealed class FileUploadController( - ILogger logger, - ApplicationDbContext context - ) : Controller + ILogger logger +) : Controller { - private const long - FileSizeLimit = - 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte - - private const string TargetDirectoryPath = "./files/"; + private const long FileSizeLimit = 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions s_defaultFormOptions = new(); private readonly ILogger _logger = logger; - private readonly ApplicationDbContext _context = context; - private readonly string[] _permittedExtensions = - [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; + private readonly string[] _permittedExtensions = [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; private bool _disposed; @@ -63,7 +55,6 @@ protected override void Dispose(bool disposing) if (!_disposed) { // Dispose of resources held by this instance. - _context.Dispose(); _disposed = true; } } @@ -95,27 +86,31 @@ protected override void Dispose(bool disposing) [RequestSizeLimit(10737418240)] // 10 GiB public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, + [FromServices] ApplicationDbContext context, [FromServices] UserService userService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { ModelState.AddModelError("CurrentUser", $"User is not authenticated."); return BadRequest(ModelState); } - + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return Unauthorized(); + } if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { - ModelState.AddModelError("File", - "The request content is not multipart."); + ModelState.AddModelError( + "File", + "The request content is not multipart." + ); return BadRequest(ModelState); } - - var getHttpsResource = await _context.GetHttpsResources.AsNoTracking() + var getHttpsResource = await context.GetHttpsResources.AsQueryable() .Include(e => e.CalorimetricData) .Include(e => e.GeometricData) .Include(e => e.HygrothermalData) @@ -125,65 +120,45 @@ CancellationToken cancellationToken .SingleOrDefaultAsync(cancellationToken); if (getHttpsResource is null) { - ModelState.AddModelError("GetHttpsResourceUuid", - $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); + ModelState.AddModelError( + "GetHttpsResourceUuid", + $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}." + ); return BadRequest(ModelState); } if (getHttpsResource.Data is null) { - ModelState.AddModelError("GetHttpsResourceUuid", - $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}."); + ModelState.AddModelError( + "GetHttpsResourceUuid", + $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}." + ); return BadRequest(ModelState); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return Unauthorized(); - } - - Directory.CreateDirectory(TargetDirectoryPath); - + Directory.CreateDirectory(GetHttpsResource.FilesDirectoryPath); var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), s_defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(cancellationToken); - while (section is not null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse( section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) { // This check assumes that there's a file // present without form data. If form data // is present, this method immediately fails // and returns the model error. - if (!MultipartRequestHelper - .HasFileContentDisposition(contentDisposition ?? - throw new ArgumentException( - "Impossible (because `hasContentDispositionHeader` is `true`)"))) + if (!MultipartRequestHelper.HasFileContentDisposition( + contentDisposition ?? throw new ArgumentException("Impossible (because `hasContentDispositionHeader` is `true`)") + ) + ) { - ModelState.AddModelError("File", - "There is no content-disposition header."); + ModelState.AddModelError("File", "There is no content-disposition header."); return BadRequest(ModelState); } - - // Don't trust the file name sent by the client. To display - // the file name, HTML-encode the value. - var trustedFileNameForDisplay = WebUtility.HtmlEncode( - contentDisposition.FileName.Value); - var trustedFileNameForFileStorage = getHttpsResourceUuid.ToString("D"); - - // **WARNING!** - // In the following example, the file is saved without - // scanning the file's contents. In most production - // scenarios, an anti-virus/anti-malware scanner API - // is used on the file before making the file available - // for download or for use by other systems. For more - // information, see the topic that accompanies this sample. - var streamedFileContent = await FileHelpers.ProcessStreamedFile( section, contentDisposition, @@ -191,25 +166,26 @@ CancellationToken cancellationToken _permittedExtensions, FileSizeLimit ); - if (!ModelState.IsValid) { return BadRequest(ModelState); } - - using var targetStream = System.IO.File.Create( - Path.Combine(TargetDirectoryPath, trustedFileNameForFileStorage)); + using var targetStream = System.IO.File.Create(getHttpsResource.FilePath); await targetStream.WriteAsync(streamedFileContent, cancellationToken); - - _logger.SavedUploadedFile(trustedFileNameForDisplay, TargetDirectoryPath, - trustedFileNameForFileStorage); + // Don't trust the file name sent by the client. To display the + // file name, HTML-encode the value. + _logger.SavedUploadedFile( + WebUtility.HtmlEncode(contentDisposition.FileName.Value), + getHttpsResource.FilePath + ); + break; } - // Drain any remaining section body that hasn't been consumed and // read the headers for the next section. section = await reader.ReadNextSectionAsync(cancellationToken); } - + await getHttpsResource.RecomputeHashValue(cancellationToken); + await context.SaveChangesAsync(cancellationToken); return Created(nameof(FileUploadController), null); } diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 6674ec4e..d49c30f1 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -1,7 +1,12 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.IO; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; using Database.Extensions; +using Database.Utilities; namespace Database.Data; @@ -13,6 +18,8 @@ public sealed class GetHttpsResource( ) : Entity { + public const string FilesDirectoryPath = "./files/"; + public GetHttpsResource( string? description, string hashValue, @@ -87,6 +94,7 @@ public GetHttpsResource( GeometricDataId = geometricDataId; } + public string FilePath { get => Path.Combine(FilesDirectoryPath, Id.ToString("D")); } public string? Description { get; private set; } = description; public string HashValue { get; private set; } = hashValue; public Guid DataFormatId { get; private set; } = dataFormatId; @@ -140,4 +148,9 @@ public static string ConstructVertexId(Guid id) { return id.ToString("D").Base64Encode(); } + + public async Task RecomputeHashValue(CancellationToken cancellationToken) + { + HashValue = await Sha256FileHasher.Compute(FilePath, cancellationToken); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 2fe632e2..0e3aee42 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -1,19 +1,28 @@ using System; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.Data; using Database.Authorization; using Database.Enumerations; -using Database.Extensions; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using Database.Services; using GraphQL.Client.Abstractions.Utilities; +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; namespace Database.GraphQl.GetHttpsResources; +public static partial class GetHttpsResourceMutationsLogging +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Recomputing the hash value of the GET HTTPS resource with ID {Id} failed." + )] + public static partial void FailedRecomputingHashValue(this ILogger logger, Guid id, Exception exception); +} + [ExtendObjectType(nameof(Mutation))] public sealed class GetHttpsResourceMutations { @@ -25,28 +34,7 @@ public async Task CreateGetHttpsResourceAsync( CancellationToken cancellationToken ) { - IData? data = input.DataKind switch - { - DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - _ => throw new ArgumentException($"The data kind {input.DataKind} is not supported.") - }; - if (data is null) - { - return new CreateGetHttpsResourcePayload( - new CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, - $"There is no data of kind {input.DataKind} with identifier {input.DataId}.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); - } - - var currentUser = await userService.GetCurrentUser( - cancellationToken); + var currentUser = await userService.GetCurrentUser(cancellationToken); if (currentUser is null) { return new CreateGetHttpsResourcePayload( @@ -68,6 +56,26 @@ CancellationToken cancellationToken ); } + IData? data = input.DataKind switch + { + DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), + _ => throw new ArgumentException($"The data kind {input.DataKind} is not supported.") + }; + if (data is null) + { + return new CreateGetHttpsResourcePayload( + new CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, + $"There is no data of kind {input.DataKind} with identifier {input.DataId}.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ); + } + var getHttpsResource = new GetHttpsResource( input.Description, input.HashValue, @@ -101,4 +109,65 @@ input.AppliedConversionMethod is null await context.SaveChangesAsync(cancellationToken); return new CreateGetHttpsResourcePayload(getHttpsResource); } + + public async Task RecomputeGetHttpsResourceHashValuesAsync( + ApplicationDbContext context, + UserService userService, + ILogger logger, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new RecomputeGetHttpsResourceHashValuesPayload( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new RecomputeGetHttpsResourceHashValuesPayload( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, + $"The current user is not authorized to recompute GET HTTPS resource hash values in this database.", + [] + ) + ); + } + var resources = await context.GetHttpsResources.ToListAsync(cancellationToken); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + resources, + cancellationToken, + async (resource, cancellationToken) => + { + try + { + await resource.RecomputeHashValue(cancellationToken); + } + catch (Exception exception) + { + logger.FailedRecomputingHashValue(resource.Id, exception); + errors.Add( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.FAILED, + $"Recomputing the hash value for the GET HTTPS resource with ID {resource.Id} failed with message: {exception.Message}", + [] + ) + ); + } + + } + ); + await context.SaveChangesAsync(cancellationToken); + if (!errors.IsEmpty) + { + return new RecomputeGetHttpsResourceHashValuesPayload(resources, errors); + } + return new RecomputeGetHttpsResourceHashValuesPayload(resources); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs new file mode 100644 index 00000000..39d72b34 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode code, + string message, + IReadOnlyList path + ) + : UserErrorBase(code, message, path) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs new file mode 100644 index 00000000..3d64876d --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl.GetHttpsResources; + +[SuppressMessage("Naming", "CA1707")] +public enum RecomputeGetHttpsResourceHashValuesErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + FAILED +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs new file mode 100644 index 00000000..a6a53505 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Database.Data; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class RecomputeGetHttpsResourceHashValuesPayload + : Payload +{ + public IReadOnlyCollection? GetHttpsResources { get; } + public IReadOnlyCollection? Errors { get; } + + public RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection? getHttpsResources + ) + { + GetHttpsResources = getHttpsResources; + } + + public RecomputeGetHttpsResourceHashValuesPayload( + RecomputeGetHttpsResourceHashValuesError error + ) + { + Errors = [error]; + } + + public RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection getHttpsResources, + IReadOnlyCollection errors + ) + : this(getHttpsResources) + { + Errors = errors; + } +} \ No newline at end of file diff --git a/backend/src/Utilities/Sha256FileHasher.cs b/backend/src/Utilities/Sha256FileHasher.cs new file mode 100644 index 00000000..e1f663ce --- /dev/null +++ b/backend/src/Utilities/Sha256FileHasher.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; + +namespace Database.Utilities; + +public static class Sha256FileHasher +{ + public static async Task Compute(string filePath, CancellationToken cancellationToken) + { + if (!File.Exists(filePath)) + { + throw new FileNotFoundException("File not found.", filePath); + } + using var sha256 = SHA256.Create(); + using var fileStream = File.OpenRead(filePath); + return Convert.ToHexString( + await sha256.ComputeHashAsync(fileStream, cancellationToken) + ); + } +} \ No newline at end of file From 414966fac3b41504872c33cae5e570e76d5043e5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Oct 2025 14:29:15 +0200 Subject: [PATCH 500/878] Describe how to create an OpenId Connect Application in the metabase (it is needed to make the single-sign on solution work) --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index cc6e00ad..04a9abdf 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,25 @@ and the pages following it. 1. For each of the two environments staging and production referred to by `${environment}` below: 1. Change into the clone `${environment}` by running `cd /app/${environment}`. + 1. Open `https://www.buildingenvelopedata.org` in your favorite web browser, + log into your account, navigate to the institution operating this + database (which you should be a representative of), add an OpenId Connect + Application with + - client ID and display name of your choice; + - consent type: explicit; + - endpoints: authorization, pushed authorization, introspection, + end session, revocation, token; + - grant types: authorization code and refresh token; + - response types: code; + - scopes: address, email, phone, profile, roles, read:api, write:api; + - requirements: proof key for code exchange and pushed authorization + requests; + - post logout redirect URI: `https://${HOST}/connect/callback/logout/metabase` + - redirect URI: `https://${HOST}/connect/callback/login/metabase` + where `${HOST}` is the domain name with sub-domain of the deployment, + for example, `staging.solarbuildingenvelopes.com` or + `www.solarbuildingenvelopes.com` for the product-data database of the + TestLab Solar Facades. 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env`, `cp ./frontend/.env.local.sample ./frontend/.env.local`, and by adjusting variable values in the copies to your needs, in particular, by setting From 4b2a51c06c28da71fc368c24f620617b9134f3b7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Oct 2025 19:37:46 +0200 Subject: [PATCH 501/878] Remove debug output --- backend/src/Methods/MethodBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index 4c4d3d0a..1cb57197 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -24,7 +24,6 @@ public abstract class MethodBase public JsonElement Calculate(JsonElement input) { - Console.WriteLine(JsonSerializer.Serialize(input)); var parsedInput = input.Deserialize(s_jsonSerializerOptions) ?? throw new InvalidOperationException(); var output = Calculate(parsedInput); From cf8cfbd859ca4318bfd251f831cc1be9823f9cfe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Oct 2025 19:38:46 +0200 Subject: [PATCH 502/878] Use hash value placeholder until the file has been uploaded where the correct hash value is being computed --- backend/src/Data/GetHttpsResource.cs | 2 +- .../CalorimetricDataX/CalorimetricDataMutations.cs | 5 +++-- .../GraphQl/GeometricDataX/GeometricDataMutations.cs | 6 +++--- .../GetHttpsResources/CreateGetHttpsResourceInput.cs | 1 - .../GetHttpsResources/GetHttpsResourceMutations.cs | 3 ++- .../HygrothermalDataX/HygrothermalDataMutations.cs | 8 +++----- .../src/GraphQl/OpticalDataX/OpticalDataMutations.cs | 6 +++--- .../PhotovoltaicDataX/PhotovoltaicDataMutations.cs | 10 +++------- backend/src/GraphQl/RootGetHttpsResourceInput.cs | 1 - backend/src/Utilities/Sha256FileHasher.cs | 12 +++++++++++- 10 files changed, 29 insertions(+), 25 deletions(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index d49c30f1..b666f17a 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -151,6 +151,6 @@ public static string ConstructVertexId(Guid id) public async Task RecomputeHashValue(CancellationToken cancellationToken) { - HashValue = await Sha256FileHasher.Compute(FilePath, cancellationToken); + HashValue = await Sha256FileHasher.ComputeForFile(FilePath, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 1f842563..cf5466d9 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -2,10 +2,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate.Types; +using Database.Utilities; namespace Database.GraphQl.CalorimetricDataX; @@ -78,7 +79,7 @@ CancellationToken cancellationToken ); var resource = new GetHttpsResource( input.RootResource.Description, - input.RootResource.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.RootResource.DataFormatId, null, input.RootResource.ArchivedFilesMetaInformation.Select(i => diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index caf0939a..6a589cb2 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -1,12 +1,12 @@ using System; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate.Types; +using Database.Utilities; namespace Database.GraphQl.GeometricDataX; @@ -78,7 +78,7 @@ CancellationToken cancellationToken ); var resource = new GetHttpsResource( input.RootResource.Description, - input.RootResource.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.RootResource.DataFormatId, null, input.RootResource.ArchivedFilesMetaInformation.Select(i => diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs index 7cbde64d..e5a8e5ca 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs @@ -6,7 +6,6 @@ namespace Database.GraphQl.GetHttpsResources; public sealed record CreateGetHttpsResourceInput( string Description, - string HashValue, Guid DataFormatId, Guid DataId, DataKind DataKind, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 0e3aee42..db5a9c96 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -11,6 +11,7 @@ using GraphQL.Client.Abstractions.Utilities; using System.Collections.Concurrent; using Microsoft.Extensions.Logging; +using Database.Utilities; namespace Database.GraphQl.GetHttpsResources; @@ -78,7 +79,7 @@ CancellationToken cancellationToken var getHttpsResource = new GetHttpsResource( input.Description, - input.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.DataFormatId, input.DataKind == DataKind.CALORIMETRIC_DATA ? input.DataId : null, input.DataKind == DataKind.GEOMETRIC_DATA ? input.DataId : null, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index a15d6bd7..e8fe8f1e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -1,14 +1,12 @@ using System; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using HotChocolate.Types; using Database.Authorization; using Database.Data; -using Database.GraphQl.GeometricDataX; using Database.Services; -using HotChocolate.Types; -using Org.BouncyCastle.Asn1.X509.Qualified; +using Database.Utilities; namespace Database.GraphQl.HygrothermalDataX; @@ -78,7 +76,7 @@ CancellationToken cancellationToken ); var resource = new GetHttpsResource( input.RootResource.Description, - input.RootResource.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.RootResource.DataFormatId, null, input.RootResource.ArchivedFilesMetaInformation.Select(i => diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 20e380d6..5a482c6e 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,12 +1,12 @@ using System; using System.Linq; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate.Types; +using Database.Utilities; namespace Database.GraphQl.OpticalDataX; @@ -91,7 +91,7 @@ CancellationToken cancellationToken ); var resource = new GetHttpsResource( input.RootResource.Description, - input.RootResource.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.RootResource.DataFormatId, null, input.RootResource.ArchivedFilesMetaInformation.Select(i => diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 2adfa310..08bb54d0 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -1,16 +1,12 @@ using System; using System.Linq; -using System.Net.Http; -using System.Security.Claims; -using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using HotChocolate.Types; using Database.Authorization; using Database.Data; -using Database.GraphQl.GeometricDataX; using Database.Services; -using HotChocolate.Types; -using Org.BouncyCastle.Asn1.X509.Qualified; +using Database.Utilities; namespace Database.GraphQl.PhotovoltaicDataX; @@ -81,7 +77,7 @@ CancellationToken cancellationToken ); var resource = new GetHttpsResource( input.RootResource.Description, - input.RootResource.HashValue, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. input.RootResource.DataFormatId, null, input.RootResource.ArchivedFilesMetaInformation.Select(i => diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 6bd69b0b..350aeb96 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -5,7 +5,6 @@ namespace Database.GraphQl; public sealed record RootGetHttpsResourceInput( string Description, - string HashValue, Guid DataFormatId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod diff --git a/backend/src/Utilities/Sha256FileHasher.cs b/backend/src/Utilities/Sha256FileHasher.cs index e1f663ce..4401e838 100644 --- a/backend/src/Utilities/Sha256FileHasher.cs +++ b/backend/src/Utilities/Sha256FileHasher.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Security.Cryptography; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -8,7 +9,7 @@ namespace Database.Utilities; public static class Sha256FileHasher { - public static async Task Compute(string filePath, CancellationToken cancellationToken) + public static async Task ComputeForFile(string filePath, CancellationToken cancellationToken) { if (!File.Exists(filePath)) { @@ -20,4 +21,13 @@ public static async Task Compute(string filePath, CancellationToken canc await sha256.ComputeHashAsync(fileStream, cancellationToken) ); } + + public static string ComputeForString(string content) + { + return Convert.ToHexString( + SHA256.HashData( + Encoding.UTF8.GetBytes(content) + ) + ); + } } \ No newline at end of file From caf799a573b0c450a6ddcd12fab24cada8def13a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 21 Oct 2025 20:46:38 +0200 Subject: [PATCH 503/878] Use `UriBuilder` to build URIs (this avoids for examples problems with slashes) --- backend/src/ApiRequests/GetUserInfo.cs | 2 +- backend/src/AppSettings.cs | 18 ++++++++++++------ backend/src/Configuration/AuthConfiguration.cs | 16 ++++++++++------ .../Controllers/AuthenticationController.cs | 6 +++--- .../GetHttpsResourceResolvers.cs | 5 ++++- 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index cc619113..217bd5fc 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -11,7 +11,7 @@ namespace Database.ApiRequests; public sealed class GetUserInfo { public static Uri GetEndpoint(AppSettings appSettings) => - new(new Uri(appSettings.MetabaseHost, UriKind.Absolute), "/connect/userinfo"); + new UriBuilder(appSettings.MetabaseHostUri) { Path = "/connect/userinfo" }.Uri; public sealed record UserInfo( Address? Address, diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 0e71a68e..6ec4a089 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -10,15 +10,21 @@ public sealed class AppSettings // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - public Uri DatabaseGraphQlEndpoint { get => new(new Uri(Host, UriKind.Absolute), GraphQlPathSegment); } + public Uri DatabaseGraphQlEndpoint + { + get => new UriBuilder(new Uri(Host, UriKind.Absolute)) { Path = GraphQlPathSegment }.Uri; + } - public Uri MetabaseGraphQlEndpoint { get => new(new Uri(MetabaseHost, UriKind.Absolute), GraphQlPathSegment); } + public Uri MetabaseGraphQlEndpoint + { + get => new UriBuilder(new Uri(MetabaseHost, UriKind.Absolute)) { Path = GraphQlPathSegment }.Uri; + } - public string Host { get; private set; } - = ""; + public string Host { get; private set; } = ""; + public Uri HostUri { get => new(Host, UriKind.Absolute); } - public string MetabaseHost { get; private set; } - = ""; + public string MetabaseHost { get; private set; } = ""; + public Uri MetabaseHostUri { get => new(MetabaseHost, UriKind.Absolute); } public Guid DatabaseId { get; private set; } public Guid OperatorId { get; private set; } diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index dacb081f..2e123dc6 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -142,7 +142,7 @@ X509Certificate2 signingCertificate { // The validation handler uses OpenID Connect discovery to // retrieve the issuer signing keys used to validate tokens. - _.SetIssuer(new Uri(appSettings.MetabaseHost, UriKind.Absolute)); + _.SetIssuer(appSettings.MetabaseHostUri); // Configure the audience accepted by this resource server. _.AddAudiences(appSettings.OpenIdConnectClient.Id); // Configure the validation handler to use introspection and @@ -156,8 +156,10 @@ X509Certificate2 signingCertificate _.UseAspNetCore(); // Register the System.Net.Http integration. _.UseSystemNetHttp() - .ConfigureHttpClientHandler(handler => { - if (environment.IsDevelopment()) { + .ConfigureHttpClientHandler(handler => + { + if (environment.IsDevelopment()) + { // https://documentation.openiddict.com/integrations/system-net-http#register-a-custom-httpclienthandler-configuration-delegate handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } @@ -184,8 +186,10 @@ X509Certificate2 signingCertificate // providers that use the user agent as a way to throttle requests (e.g Reddit). _.UseSystemNetHttp() .SetProductInformation(typeof(Startup).Assembly) - .ConfigureHttpClientHandler(handler => { - if (environment.IsDevelopment()) { + .ConfigureHttpClientHandler(handler => + { + if (environment.IsDevelopment()) + { // https://documentation.openiddict.com/integrations/system-net-http#register-a-custom-httpclienthandler-configuration-delegate handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } @@ -195,7 +199,7 @@ X509Certificate2 signingCertificate _.AddRegistration( new OpenIddictClientRegistration { - Issuer = new Uri(appSettings.MetabaseHost, UriKind.Absolute), + Issuer = appSettings.MetabaseHostUri, // Note: these settings must match the application details // inserted in the database at the server level. diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index b6862c67..8264c21e 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -30,7 +30,7 @@ ApplicationDbContext context private readonly IdentityOptions _identityOptions = identityOptions.Value ?? throw new InvalidOperationException("There are no identity options."); private readonly ApplicationDbContext _context = context; - private readonly string _issuer = appSettings.MetabaseHost; + private readonly Uri _issuer = appSettings.MetabaseHostUri; private bool _disposed; protected override void Dispose(bool disposing) @@ -60,7 +60,7 @@ public ActionResult LogIn(string? returnUrl) { // Note: when only one client is registered in the client options, // setting the issuer property is not required and can be omitted. - [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer.AbsoluteUri } ) { @@ -95,7 +95,7 @@ public async Task LogOut(string? returnUrl) { // Note: when only one client is registered in the client options, // setting the issuer property is not required and can be omitted. - [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer, + [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer.AbsoluteUri, // While not required, the specification encourages sending an id_token_hint // parameter containing an identity token returned by the server for this user. [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index a39935bc..b3679e7c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -71,7 +71,10 @@ public Uri GetLocator( AppSettings appSettings ) { - return new Uri($"{appSettings.Host}/api/resources/{getHttpsResource.Id}"); + return new UriBuilder(appSettings.HostUri) + { + Path = "/api/resources/{getHttpsResource.Id" + }.Uri; } public async Task GetParent( From f3d0adf5bee21c35793153b683f80d685872fe5a Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Tue, 21 Oct 2025 17:56:24 +0200 Subject: [PATCH 504/878] Add large-file storage. Start with ComplexTest --- .gitattributes | 1 + .../2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 3 +++ .../Methods/SpectralToIntegralMethodTests.cs | 26 ++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..93fce6ba --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json filter=lfs diff=lfs merge=lfs -text diff --git a/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json new file mode 100644 index 00000000..e3937263 --- /dev/null +++ b/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3de8b46624db0f442d6a70f299148352f754196bf9fafcb71282463d73472187 +size 579012 diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 3ba8d733..45e0cdbe 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -7,7 +7,7 @@ namespace Database.Tests.Methods; public sealed class SpectralToIntegralMethodTests { [Test] - public void CalculateTest() + public void CalculateSimpleTest() { var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ @@ -23,4 +23,28 @@ public void CalculateTest() results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); } + + [Test] + public void CalculateComplexTest() + { + using var document = await JsonDocument.ParseAsync( + await httpResponseContent.ReadAsStreamAsync(), + StrictJsonDocumentOptions +); + var parsedInput = document.RootElement.Deserialize(s_jsonSerializerOptions) + ?? throw new InvalidOperationException(); + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(784)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(785)), new Results(0)), + new DataPoint(new Incidence(new Wavelengths(2600)), new Results(0)) + ]) + ]); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); + } } \ No newline at end of file From f35cb30624ed9b2cbd6e0850457d6bdfbb8e2d11 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 Oct 2025 12:44:58 +0200 Subject: [PATCH 505/878] Add tests for spectral-to-integral calculations that capture the status quo --- .../src/ApiRequests/JsonDocumentSettings.cs | 20 ++++ .../src/ApiRequests/JsonSerializerSettings.cs | 58 +++--------- backend/src/GraphQl/Methods/MethodQueries.cs | 2 +- backend/src/Methods/MethodBase.cs | 4 +- backend/src/Services/ApiRequestService.cs | 25 +---- .../Methods/SpectralToIntegralMethodTests.cs | 92 +++++++++++++++---- 6 files changed, 115 insertions(+), 86 deletions(-) create mode 100644 backend/src/ApiRequests/JsonDocumentSettings.cs diff --git a/backend/src/ApiRequests/JsonDocumentSettings.cs b/backend/src/ApiRequests/JsonDocumentSettings.cs new file mode 100644 index 00000000..8e33935b --- /dev/null +++ b/backend/src/ApiRequests/JsonDocumentSettings.cs @@ -0,0 +1,20 @@ +using System.Text.Json; + +namespace Database.ApiRequests; + +public static class JsonDocumentSettings +{ + public static JsonDocumentOptions Strict => new() + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Disallow, + MaxDepth = 0 + }; + + public static JsonDocumentOptions Lax => new() + { + AllowTrailingCommas = true, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = 0 + }; +} \ No newline at end of file diff --git a/backend/src/ApiRequests/JsonSerializerSettings.cs b/backend/src/ApiRequests/JsonSerializerSettings.cs index 54d96a60..e7312f61 100644 --- a/backend/src/ApiRequests/JsonSerializerSettings.cs +++ b/backend/src/ApiRequests/JsonSerializerSettings.cs @@ -5,61 +5,33 @@ namespace Database.ApiRequests; /// -/// Settings for Json serialization. +/// Options for JSON serialization. /// public static class JsonSerializerSettings { - /// - /// Settings for GraphQL Json requests. - /// - public static readonly JsonSerializerOptions GraphQl = + private static readonly JsonSerializerOptions s_common = new() { + AllowOutOfOrderMetadataProperties = true, Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IgnoreReadOnlyFields = true, + IgnoreReadOnlyProperties = false, + IncludeFields = false, NumberHandling = JsonNumberHandling.Strict, - AllowOutOfOrderMetadataProperties = true, PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + }; - /// - /// Settings for REST Json requests. - /// - public static readonly JsonSerializerOptions Rest = - new() + public static readonly JsonSerializerOptions GraphQl = + new(s_common) { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, - NumberHandling = JsonNumberHandling.Strict, - AllowOutOfOrderMetadataProperties = true, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; - /// - /// Settings for file Json requests. - /// - public static readonly JsonSerializerOptions File = - new() + public static readonly JsonSerializerOptions Rest = + new(s_common) { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, - NumberHandling = JsonNumberHandling.Strict, - AllowOutOfOrderMetadataProperties = true, - PropertyNameCaseInsensitive = false, PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, - ReadCommentHandling = JsonCommentHandling.Disallow, - IncludeFields = false, - IgnoreReadOnlyProperties = false, - IgnoreReadOnlyFields = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; //.SetupImmutableConverter(); + }; } \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index aa365aec..96a97b6b 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -59,7 +59,7 @@ CancellationToken cancellationToken using var stream = data.OpenReadStream(); using var jsonData = await JsonDocument.ParseAsync( stream, - ApiRequestService.LaxJsonDocumentOptions, + JsonDocumentSettings.Lax, cancellationToken ); var result = method.Calculate(jsonData.RootElement); diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index 1cb57197..faf5b038 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -7,7 +7,7 @@ namespace Database.Methods; public abstract class MethodBase : IMethod { - private static readonly JsonSerializerOptions s_jsonSerializerOptions = new() + public static readonly JsonSerializerOptions JsonSerializerOptions = new() { AllowTrailingCommas = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, @@ -24,7 +24,7 @@ public abstract class MethodBase public JsonElement Calculate(JsonElement input) { - var parsedInput = input.Deserialize(s_jsonSerializerOptions) + var parsedInput = input.Deserialize(JsonSerializerOptions) ?? throw new InvalidOperationException(); var output = Calculate(parsedInput); using var document = JsonDocument.Parse( diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 8f20866f..d1840b27 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -24,20 +24,6 @@ IHttpContextAccessor httpContextAccessor { public const string CustomHttpClient = "Custom"; - public static JsonDocumentOptions StrictJsonDocumentOptions => new() - { - AllowTrailingCommas = false, - CommentHandling = JsonCommentHandling.Disallow, - MaxDepth = 0 - }; - - public static JsonDocumentOptions LaxJsonDocumentOptions => new() - { - AllowTrailingCommas = true, - CommentHandling = JsonCommentHandling.Skip, - MaxDepth = 0 - }; - public Task ConstructGraphQlQuery( string fileName ) @@ -77,7 +63,7 @@ CancellationToken cancellationToken { using var document = await JsonDocument.ParseAsync( await httpResponseContent.ReadAsStreamAsync(), - StrictJsonDocumentOptions + JsonDocumentSettings.Strict ); return document.RootElement.Clone(); }, @@ -104,13 +90,6 @@ CancellationToken cancellationToken /// /// Send GraphQL request to API and return response. /// - /// Expected response type. - /// - /// - /// - /// - /// - /// of expected type. public Task> QueryGraphQl( Uri url, GraphQLRequest request, @@ -139,7 +118,7 @@ CancellationToken cancellationToken { using var document = await JsonDocument.ParseAsync( await httpResponseContent.ReadAsStreamAsync(), - LaxJsonDocumentOptions + JsonDocumentSettings.Lax ); return document.RootElement.Clone(); }, diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 45e0cdbe..59e6a6b4 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -1,38 +1,69 @@ using NUnit.Framework; using FluentAssertions; using Database.Methods.SpectralToIntegral; +using System.Threading.Tasks; +using System.Text.Json; +using Database.Methods; +using System.IO; +using System; +using Database.ApiRequests; +using System.Reflection; +using FluentAssertions.Execution; +using System.Diagnostics.CodeAnalysis; namespace Database.Tests.Methods; +// [Follow test naming standards](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-best-practices#follow-test-naming-standards) public sealed class SpectralToIntegralMethodTests { + private static string ConstructFilePath(string relativePath) + { + var testAssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + ?? throw new InvalidOperationException(); ; + var testProjectRoot = Path.GetFullPath(Path.Combine(testAssemblyPath, @"../../../")); + return Path.Combine(testProjectRoot, relativePath); + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_None() + { + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([]) + ]); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(99.0, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(98.99999999999999, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(98.99999999999999, 0.00000001F); + } + } + [Test] - public void CalculateSimpleTest() + [SuppressMessage("Naming", "CA1707")] + public void Calculate_One() { var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(784)), new Results(1)), - new DataPoint(new Incidence(new Wavelengths(785)), new Results(0)), - new DataPoint(new Incidence(new Wavelengths(2600)), new Results(0)) ]) ]); var method = new SpectralToIntegralMethod(); var results = method.Calculate(spectralDataPoints); - results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); + } } [Test] - public void CalculateComplexTest() + [SuppressMessage("Naming", "CA1707")] + public void Calculate_Few() { - using var document = await JsonDocument.ParseAsync( - await httpResponseContent.ReadAsStreamAsync(), - StrictJsonDocumentOptions -); - var parsedInput = document.RootElement.Deserialize(s_jsonSerializerOptions) - ?? throw new InvalidOperationException(); var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), @@ -43,8 +74,35 @@ await httpResponseContent.ReadAsStreamAsync(), ]); var method = new SpectralToIntegralMethod(); var results = method.Calculate(spectralDataPoints); - results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(1.0F, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.3733675409177318F, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.212543149312381F, 0.00000001F); + } + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public async Task Calculate_Many() + { + using var fileStream = File.OpenRead( + ConstructFilePath("./Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") + ); + using var jsonDocument = await JsonDocument.ParseAsync( + fileStream, + JsonDocumentSettings.Lax + ); + var spectralDataPoints = jsonDocument.RootElement.Deserialize( + MethodBase.JsonSerializerOptions + ) ?? throw new InvalidOperationException(); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); + } } } \ No newline at end of file From e59218cbea2e102350af77bc325602d0f40c1ffc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 18:33:51 +0100 Subject: [PATCH 506/878] Configure OpenAPI document --- backend/src/Controllers/AntiforgeryController.cs | 2 ++ backend/src/Controllers/AuthenticationController.cs | 2 ++ backend/src/Controllers/FileUploadController.cs | 3 +++ 3 files changed, 7 insertions(+) diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 113d39ef..af152923 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -4,11 +4,13 @@ using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; namespace Database.Controllers; // For gotchas regarding antiforgery tokens read // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) +[EndpointGroupName("Antiforgery")] public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Controller { private const string XsrfCookieKey = "XSRF-TOKEN"; diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 8264c21e..f0558ad7 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -16,11 +16,13 @@ using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; +using Microsoft.AspNetCore.Routing; namespace Database.Controllers; // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs +[EndpointGroupName("Authentication")] public sealed class AuthenticationController( AppSettings appSettings, IOptions identityOptions, diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 83273183..15db1e57 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -16,6 +16,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; namespace Database.Controllers; @@ -34,6 +36,7 @@ string filePath // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ +[EndpointGroupName("FileUpload")] public sealed class FileUploadController( ILogger logger ) : Controller From e904de8f996462da4ee56c0c2ab4c99915ed7097 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Jan 2025 18:51:24 +0100 Subject: [PATCH 507/878] Improve OpenAPI document for health endpoint --- backend/src/Controllers/AntiforgeryController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index af152923..794ee333 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -10,7 +10,6 @@ namespace Database.Controllers; // For gotchas regarding antiforgery tokens read // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) -[EndpointGroupName("Antiforgery")] public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Controller { private const string XsrfCookieKey = "XSRF-TOKEN"; @@ -24,6 +23,8 @@ public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Con SameSite = SameSiteMode.Strict }; + [EndpointName("AntiforgeryToken")] + [EndpointDescription("Get an antiforgery token.")] [HttpGet("~/antiforgery/token")] public async Task Token() { From 49e1afba79ac21fda3f45e77aec2ca61ba9fcef8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 Oct 2025 15:13:02 +0200 Subject: [PATCH 508/878] Fix resource locator --- .../GetHttpsResources/GetHttpsResourceResolvers.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index b3679e7c..161b237d 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -66,17 +66,6 @@ CancellationToken cancellationToken return null; } - public Uri GetLocator( - [Parent] GetHttpsResource getHttpsResource, - AppSettings appSettings - ) - { - return new UriBuilder(appSettings.HostUri) - { - Path = "/api/resources/{getHttpsResource.Id" - }.Uri; - } - public async Task GetParent( [Parent] GetHttpsResource getHttpsResource, GetHttpsResourceByIdDataLoader byId, From 2367f263373ce15bea5313d5c74c57c7b6f542e7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 22 Oct 2025 15:28:16 +0200 Subject: [PATCH 509/878] Put `GetLocator` back into resolver --- backend/src/ApiRequests/QueryDatabase.cs | 7 +++---- backend/src/Data/GetHttpsResource.cs | 4 +--- .../GetHttpsResources/GetHttpsResourceResolvers.cs | 11 +++++++++++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index c2a29d74..e0d0aad0 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -1,10 +1,8 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Database.Services; using GraphQL; -using Microsoft.AspNetCore.Http; +using Database.Services; namespace Database.ApiRequests; @@ -52,7 +50,8 @@ CancellationToken cancellationToken { return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), - new GraphQLRequest(await apiRequestService.ConstructGraphQlQuery(QueryFileName), + new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(QueryFileName), new { id = databaseId diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index b666f17a..7cf3fb8e 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.IO; -using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Database.Extensions; @@ -99,8 +98,7 @@ public GetHttpsResource( public string HashValue { get; private set; } = hashValue; public Guid DataFormatId { get; private set; } = dataFormatId; - public ICollection ArchivedFilesMetaInformation { get; private set; } = - []; + public ICollection ArchivedFilesMetaInformation { get; private set; } = []; // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! [NotMapped] diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 161b237d..801f8a1c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -66,6 +66,17 @@ CancellationToken cancellationToken return null; } + public Uri GetLocator( + [Parent] GetHttpsResource getHttpsResource, + AppSettings appSettings + ) + { + return new UriBuilder(appSettings.HostUri) + { + Path = $"/api/resources/{getHttpsResource.Id}" + }.Uri; + } + public async Task GetParent( [Parent] GetHttpsResource getHttpsResource, GetHttpsResourceByIdDataLoader byId, From 2ad27ac651e2030cbccef764b6ec3a0e0bceb34c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 23 Oct 2025 17:22:12 +0200 Subject: [PATCH 510/878] Construct paths safely with `new URL` --- frontend/paths.ts | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/frontend/paths.ts b/frontend/paths.ts index a1c918a8..e77c7bce 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -31,27 +31,22 @@ export default { logout: "/connect/logout", metabase: { component(uuid: string) { - return `${ - process.env.NEXT_PUBLIC_METABASE_URL - }/components/${encodeURIComponent(uuid)}`; + return new URL(`/components/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, database(uuid: string) { - return `/databases/${encodeURIComponent(uuid)}`; + return new URL(`/databases/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, dataFormat(uuid: string) { - return `${ - process.env.NEXT_PUBLIC_METABASE_URL - }/data-formats/${encodeURIComponent(uuid)}`; + return new URL(`/data-formats/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, institution(uuid: string) { - return `${ - process.env.NEXT_PUBLIC_METABASE_URL - }/institutions/${encodeURIComponent(uuid)}`; + return new URL(`/institutions/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, method(uuid: string) { - return `${ - process.env.NEXT_PUBLIC_METABASE_URL - }/methods/${encodeURIComponent(uuid)}`; + return new URL(`/methods/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + }, + user(uuid: string) { + return new URL(`/users/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, }, }; From 1364d86c064d528a3745cf3ee2c38c9d48d6e0b8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 23 Oct 2025 17:29:04 +0200 Subject: [PATCH 511/878] Remember user ID of user who created a data set --- backend/src/Data/CalorimetricData.cs | 4 + backend/src/Data/DataX.cs | 7 +- backend/src/Data/GeometricData.cs | 4 + backend/src/Data/HygrothermalData.cs | 4 + backend/src/Data/IData.cs | 1 + backend/src/Data/OpticalData.cs | 4 + backend/src/Data/PhotovoltaicData.cs | 4 + .../CalorimetricDataMutations.cs | 1 + .../GeometricDataX/GeometricDataMutations.cs | 1 + .../HygrothermalDataMutations.cs | 1 + .../OpticalDataX/OpticalDataMutations.cs | 1 + .../PhotovoltaicDataMutations.cs | 1 + ..._RememberUserWhoCreatedDataSet.Designer.cs | 2636 +++++++++++++++++ ...023152609_RememberUserWhoCreatedDataSet.cs | 84 + .../ApplicationDbContextModelSnapshot.cs | 17 +- backend/src/Migrations/migrate.sql | 46 + ...23152609_RememberUserWhoCreatedDataSet.sql | 46 + ...1010164110_UpdateCoatedSideEnumeration.sql | 40 + .../GraphQlSchemaTests.IsUnchanged.snap | 33 +- frontend/components/data/DataPageHeader.tsx | 65 + .../data/calorimetric/CalorimetricData.tsx | 64 +- .../data/geometric/GeometricData.tsx | 53 +- .../data/hygrothermal/HygrothermalData.tsx | 45 +- .../components/data/optical/OpticalData.tsx | 86 +- .../data/photovoltaic/PhotovoltaicData.tsx | 45 +- frontend/queries/data.graphql | 2 + frontend/type-defs.graphqls | 29 +- 27 files changed, 3104 insertions(+), 220 deletions(-) create mode 100644 backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs create mode 100644 backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs create mode 100644 backend/src/Migrations/migrate_from_20251010164110_UpdateCoatedSideEnumeration_to_20251023152609_RememberUserWhoCreatedDataSet.sql create mode 100644 backend/src/Migrations/rollback_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251010164110_UpdateCoatedSideEnumeration.sql create mode 100644 frontend/components/data/DataPageHeader.tsx diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index e16f6453..266aee39 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -8,6 +8,7 @@ public sealed class CalorimetricData : DataX { public CalorimetricData( + Guid userId, string locale, Guid componentId, string? name, @@ -19,6 +20,7 @@ public CalorimetricData( double[] gValues, double[] uValues ) : base( + userId, locale, componentId, name, @@ -35,6 +37,7 @@ double[] uValues // `DbContext` needs this constructor without owned entities. public CalorimetricData( + Guid userId, string locale, Guid componentId, string? name, @@ -45,6 +48,7 @@ public CalorimetricData( double[] gValues, double[] uValues ) : base( + userId, locale, componentId, name, diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index a9501c76..3be12986 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -5,6 +5,7 @@ namespace Database.Data; public abstract class DataX( + Guid userId, string locale, Guid componentId, string? name, @@ -12,10 +13,10 @@ public abstract class DataX( string[] warnings, Guid creatorId, DateTime createdAt - ) - : Entity, IData +) : Entity, IData { protected DataX( + Guid userId, string locale, Guid componentId, string? name, @@ -26,6 +27,7 @@ protected DataX( AppliedMethod appliedMethod ) : this( + userId, locale, componentId, name, @@ -38,6 +40,7 @@ AppliedMethod appliedMethod AppliedMethod = appliedMethod; } + public Guid UserId { get; private set; } = userId; public string Locale { get; private set; } = locale; public Guid ComponentId { get; private set; } = componentId; public string? Name { get; private set; } = name; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index ff11bb7a..bce9b60e 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -8,6 +8,7 @@ public sealed class GeometricData : DataX { public GeometricData( + Guid userId, string locale, Guid componentId, string? name, @@ -18,6 +19,7 @@ public GeometricData( AppliedMethod appliedMethod, double[] thicknesses ) : base( + userId, locale, componentId, name, @@ -32,6 +34,7 @@ double[] thicknesses } public GeometricData( + Guid userId, string locale, Guid componentId, string? name, @@ -41,6 +44,7 @@ public GeometricData( DateTime createdAt, double[] thicknesses ) : base( + userId, locale, componentId, name, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index aef4c23b..64ee49a7 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -8,6 +8,7 @@ public sealed class HygrothermalData : DataX { public HygrothermalData( + Guid userId, string locale, Guid componentId, string? name, @@ -17,6 +18,7 @@ public HygrothermalData( DateTime createdAt, AppliedMethod appliedMethod ) : base( + userId, locale, componentId, name, @@ -31,6 +33,7 @@ AppliedMethod appliedMethod // `DbContext` needs this constructor without owned entities. public HygrothermalData( + Guid userId, string locale, Guid componentId, string? name, @@ -39,6 +42,7 @@ public HygrothermalData( Guid creatorId, DateTime createdAt ) : base( + userId, locale, componentId, name, diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index cb855f6b..df40e39e 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -13,6 +13,7 @@ namespace Database.Data; [JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] public interface IData : IEntity { + Guid UserId { get; } Guid ComponentId { get; } string? Name { get; } string? Description { get; } diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 650fe81d..3fdcf76b 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -9,6 +9,7 @@ public sealed class OpticalData : DataX { public OpticalData( + Guid userId, string locale, Guid componentId, string? name, @@ -28,6 +29,7 @@ public OpticalData( double[] colorRenderingIndices, ICollection cielabColors ) : base( + userId, locale, componentId, name, @@ -52,6 +54,7 @@ ICollection cielabColors // `DbContext` needs this constructor without owned entities. public OpticalData( + Guid userId, string locale, Guid componentId, string? name, @@ -69,6 +72,7 @@ public OpticalData( double[] infraredEmittances, double[] colorRenderingIndices ) : base( + userId, locale, componentId, name, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 019a8ab6..07cc5561 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -8,6 +8,7 @@ public sealed class PhotovoltaicData : DataX { public PhotovoltaicData( + Guid userId, string locale, Guid componentId, string? name, @@ -17,6 +18,7 @@ public PhotovoltaicData( DateTime createdAt, AppliedMethod appliedMethod ) : base( + userId, locale, componentId, name, @@ -31,6 +33,7 @@ AppliedMethod appliedMethod // `DbContext` needs this constructor without owned entities. public PhotovoltaicData( + Guid userId, string locale, Guid componentId, string? name, @@ -39,6 +42,7 @@ public PhotovoltaicData( Guid creatorId, DateTime createdAt ) : base( + userId, locale, componentId, name, diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index cf5466d9..288f9a64 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -47,6 +47,7 @@ CancellationToken cancellationToken } var calorimetricData = new CalorimetricData( + currentUser.Uuid, input.Locale, input.ComponentId, input.Name, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 6a589cb2..6aeade51 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -47,6 +47,7 @@ CancellationToken cancellationToken } var geometricData = new GeometricData( + currentUser.Uuid, input.Locale, input.ComponentId, input.Name, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index e8fe8f1e..aad9b631 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -46,6 +46,7 @@ CancellationToken cancellationToken } var hygrothermalData = new HygrothermalData( + currentUser.Uuid, input.Locale, input.ComponentId, input.Name, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 5a482c6e..0a0971b6 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -45,6 +45,7 @@ CancellationToken cancellationToken } var opticalData = new OpticalData( + currentUser.Uuid, input.Locale, input.ComponentId, input.Name, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 08bb54d0..3300956d 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -47,6 +47,7 @@ CancellationToken cancellationToken } var photovoltaicData = new PhotovoltaicData( + currentUser.Uuid, input.Locale, input.ComponentId, input.Name, diff --git a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs new file mode 100644 index 00000000..c649a375 --- /dev/null +++ b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs @@ -0,0 +1,2636 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251023152609_RememberUserWhoCreatedDataSet")] + partial class RememberUserWhoCreatedDataSet + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs new file mode 100644 index 00000000..af86e744 --- /dev/null +++ b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class RememberUserWhoCreatedDataSet : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "UserId", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UserId", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UserId", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "UserId", + schema: "database", + table: "calorimetric_data"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 35d78e86..120ca924 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("ProductVersion", "9.0.10") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); @@ -66,6 +66,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -111,6 +114,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -210,6 +216,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -321,6 +330,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Type") .HasColumnType("database.optical_component_type"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -362,6 +374,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("UserId") + .HasColumnType("uuid"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 315ccfa9..6608557e 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1354,3 +1354,49 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.photovoltaic_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.optical_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.hygrothermal_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.geometric_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.calorimetric_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251023152609_RememberUserWhoCreatedDataSet', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251010164110_UpdateCoatedSideEnumeration_to_20251023152609_RememberUserWhoCreatedDataSet.sql b/backend/src/Migrations/migrate_from_20251010164110_UpdateCoatedSideEnumeration_to_20251023152609_RememberUserWhoCreatedDataSet.sql new file mode 100644 index 00000000..9f9d08b5 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251010164110_UpdateCoatedSideEnumeration_to_20251023152609_RememberUserWhoCreatedDataSet.sql @@ -0,0 +1,46 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.photovoltaic_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.optical_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.hygrothermal_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.geometric_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.calorimetric_data ADD "UserId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251023152609_RememberUserWhoCreatedDataSet', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251010164110_UpdateCoatedSideEnumeration.sql b/backend/src/Migrations/rollback_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251010164110_UpdateCoatedSideEnumeration.sql new file mode 100644 index 00000000..ab42fa16 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251010164110_UpdateCoatedSideEnumeration.sql @@ -0,0 +1,40 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.optical_data DROP COLUMN "UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.geometric_data DROP COLUMN "UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251023152609_RememberUserWhoCreatedDataSet'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index cab1debe..d7666465 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -1,4 +1,4 @@ -schema { +schema { query: Query mutation: Mutation } @@ -21,6 +21,7 @@ interface Data { resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! warnings: [String!]! } @@ -89,6 +90,7 @@ type CalorimetricData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -282,6 +284,7 @@ type GeometricData implements Node & Data { resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -311,6 +314,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "10") dataFormatId: Uuid! description: String + filePath: String! hashValue: String! id: ID! locator: Url! @@ -372,6 +376,7 @@ type HygrothermalData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -432,6 +437,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues: RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") @@ -489,6 +495,7 @@ type OpticalData implements Node & Data { subtype: OpticalComponentSubtype timestamp: DateTime! type: OpticalComponentType + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -542,6 +549,7 @@ type PhotovoltaicData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -611,6 +619,18 @@ type Query { verificationCode: String! } +type RecomputeGetHttpsResourceHashValuesError { + code: RecomputeGetHttpsResourceHashValuesErrorCode! + message: String! + path: [String!]! +} + +type RecomputeGetHttpsResourceHashValuesPayload { + errors: [RecomputeGetHttpsResourceHashValuesError!] + getHttpsResources: [GetHttpsResource!] + query: Query! +} + type RemoveDataApprovalError { code: RemoveDataApprovalErrorCode! message: String! @@ -815,7 +835,6 @@ input CreateGetHttpsResourceInput { dataId: Uuid! dataKind: DataKind! description: String! - hashValue: String! parentId: Uuid } @@ -1344,7 +1363,6 @@ input RootGetHttpsResourceInput { archivedFilesMetaInformation: [FileMetaInformationInput!]! dataFormatId: Uuid! description: String! - hashValue: String! } input StandardInput { @@ -1631,6 +1649,13 @@ enum OpticalComponentType { SHADING } +enum RecomputeGetHttpsResourceHashValuesErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + FAILED +} + enum RemoveDataApprovalErrorCode { UNKNOWN UNAUTHENTICATED @@ -1729,4 +1754,4 @@ scalar Upload scalar Url -scalar Uuid +scalar Uuid \ No newline at end of file diff --git a/frontend/components/data/DataPageHeader.tsx b/frontend/components/data/DataPageHeader.tsx new file mode 100644 index 00000000..6a05ebee --- /dev/null +++ b/frontend/components/data/DataPageHeader.tsx @@ -0,0 +1,65 @@ +import { Descriptions, Typography } from "antd"; +import { PageHeader } from "@ant-design/pro-layout"; +import Link from "next/link"; +import paths from "../../paths"; +import { DataPartialFragment } from "../../queries/data.graphql"; +import { ReactNode } from "react"; + +export type DataPageHeaderProps = { + data: DataPartialFragment, + extra?: ReactNode + children?: ReactNode +}; + +export default function DataPageHeader({ data, extra, children }: DataPageHeaderProps) { + return ( + + + {data.uuid} + {data.timestamp} + {data.locale} + + + {data.componentId} + + + {data.createdAt} + + + {data.creatorId} + + + {data.warnings} + + + {data.appliedMethod.methodId} + + + + + {data.resourceTree.root.value.uuid} + + + + + {data.userId} + + + {children} + + + ) +}; \ No newline at end of file diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 4bbc0159..e126e8af 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,11 +1,9 @@ import { Scalars } from "../../../__generated__/__types__"; import { useCalorimetricDataQuery } from "../../../queries/data.graphql"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; +import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; -import Link from "next/link"; -import paths from "../../../paths"; import { messageApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; export type CalorimetricDataProps = { calorimetricDataId: Scalars["Uuid"]; @@ -40,49 +38,21 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat } return ( - , - // ]} - backIcon={false} + , + // ]} > - - {calorimetricData.uuid} - - - {calorimetricData.componentId} - - - - - {calorimetricData.creatorId} - - - {calorimetricData.description} - {calorimetricData.createdAt} - - - {calorimetricData.appliedMethod.methodId} - - - - {calorimetricData.gValues.map((x) => x.toLocaleString("en")).join(", ")} - - - {calorimetricData.uValues.map((x) => x.toLocaleString("en")).join(", ")} - - - + + {calorimetricData.gValues.map((x) => x.toLocaleString("en")).join(", ")} + + + {calorimetricData.uValues.map((x) => x.toLocaleString("en")).join(", ")} + + ); } diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 5d9fc930..72d9ca86 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -1,11 +1,9 @@ import { Scalars } from "../../../__generated__/__types__"; import { useGeometricDataQuery } from "../../../queries/data.graphql"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; +import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; -import Link from "next/link"; -import paths from "../../../paths"; import { messageApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; export type GeometricDataProps = { geometricDataId: Scalars["Uuid"]; @@ -42,41 +40,18 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { } return ( - , + // ]} > - - {geometricData.uuid} - - - {geometricData.componentId} - - - - - {geometricData.creatorId} - - - {geometricData.description} - {geometricData.createdAt} - - - {geometricData.appliedMethod.methodId} - - - - {geometricData.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} - - - + + {geometricData.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} + + ); } \ No newline at end of file diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index 1621307f..71b1269f 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -1,11 +1,9 @@ import { Scalars } from "../../../__generated__/__types__"; import { useHygrothermalDataQuery } from "../../../queries/data.graphql"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; +import { Skeleton, Result } from "antd"; import { useEffect } from "react"; -import Link from "next/link"; -import paths from "../../../paths"; import { messageApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; export type HygrothermalDataProps = { hygrothermalDataId: Scalars["Uuid"]; @@ -40,36 +38,15 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat } return ( - , - // ]} - backIcon={false} + , + // ]} > - - {hygrothermalData.uuid} - - - {hygrothermalData.componentId} - - - - - {hygrothermalData.creatorId} - - - {hygrothermalData.description} - {hygrothermalData.createdAt} - - + ); } diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 874f4b6b..6a0e6f31 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,11 +1,9 @@ import { Scalars } from "../../../__generated__/__types__"; import { useOpticalDataQuery } from "../../../queries/data.graphql"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; +import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; -import Link from "next/link"; -import paths from "../../../paths"; import { messageApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; export type OpticalDataProps = { opticalDataId: Scalars["Uuid"]; @@ -40,59 +38,33 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { } return ( - , - // ]} - backIcon={false} + , + // ]} > - - {opticalData.uuid} - - - {opticalData.componentId} - - - - - {opticalData.creatorId} - - - {opticalData.description} - {opticalData.createdAt} - - - {opticalData.appliedMethod.methodId} - - - - {opticalData.nearnormalHemisphericalVisibleTransmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalVisibleReflectances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalSolarTransmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalSolarReflectances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.infraredEmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.colorRenderingIndices.map((x) => x.toLocaleString("en")).join(", ")} - - - + + {opticalData.nearnormalHemisphericalVisibleTransmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalVisibleReflectances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalSolarTransmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.nearnormalHemisphericalSolarReflectances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.infraredEmittances.map((x) => x.toLocaleString("en")).join(", ")} + + + {opticalData.colorRenderingIndices.map((x) => x.toLocaleString("en")).join(", ")} + + ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 35a2e7a9..488dc2f5 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -1,11 +1,9 @@ import { Scalars } from "../../../__generated__/__types__"; import { usePhotovoltaicDataQuery } from "../../../queries/data.graphql"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; +import { Skeleton, Result } from "antd"; import { useEffect } from "react"; -import Link from "next/link"; -import paths from "../../../paths"; import { messageApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; export type PhotovoltaicDataProps = { photovoltaicDataId: Scalars["Uuid"]; @@ -40,36 +38,15 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat } return ( - , - // ]} - backIcon={false} + , + // ]} > - - {photovoltaicData.uuid} - - - {photovoltaicData.componentId} - - - - - {photovoltaicData.creatorId} - - - {photovoltaicData.description} - {photovoltaicData.createdAt} - - + ); } diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 65f0ae09..5d228e4e 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -23,6 +23,7 @@ fragment DataPartial on Data { resourceTree { root { value { + uuid description hashValue locator @@ -99,6 +100,7 @@ fragment DataPartial on Data { dataFormatId } warnings + userId } fragment CalorimetricDataPartial on CalorimetricData { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 09d21ab0..d7666465 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -21,6 +21,7 @@ interface Data { resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! warnings: [String!]! } @@ -89,6 +90,7 @@ type CalorimetricData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -282,6 +284,7 @@ type GeometricData implements Node & Data { resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -311,6 +314,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "10") dataFormatId: Uuid! description: String + filePath: String! hashValue: String! id: ID! locator: Url! @@ -372,6 +376,7 @@ type HygrothermalData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -432,6 +437,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues: RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") @@ -489,6 +495,7 @@ type OpticalData implements Node & Data { subtype: OpticalComponentSubtype timestamp: DateTime! type: OpticalComponentType + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -542,6 +549,7 @@ type PhotovoltaicData implements Node & Data { resources: [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! + userId: Uuid! uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -611,6 +619,18 @@ type Query { verificationCode: String! } +type RecomputeGetHttpsResourceHashValuesError { + code: RecomputeGetHttpsResourceHashValuesErrorCode! + message: String! + path: [String!]! +} + +type RecomputeGetHttpsResourceHashValuesPayload { + errors: [RecomputeGetHttpsResourceHashValuesError!] + getHttpsResources: [GetHttpsResource!] + query: Query! +} + type RemoveDataApprovalError { code: RemoveDataApprovalErrorCode! message: String! @@ -815,7 +835,6 @@ input CreateGetHttpsResourceInput { dataId: Uuid! dataKind: DataKind! description: String! - hashValue: String! parentId: Uuid } @@ -1344,7 +1363,6 @@ input RootGetHttpsResourceInput { archivedFilesMetaInformation: [FileMetaInformationInput!]! dataFormatId: Uuid! description: String! - hashValue: String! } input StandardInput { @@ -1631,6 +1649,13 @@ enum OpticalComponentType { SHADING } +enum RecomputeGetHttpsResourceHashValuesErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + FAILED +} + enum RemoveDataApprovalErrorCode { UNKNOWN UNAUTHENTICATED From 7db415eb38e18c71b967287939ba0bfa5e546758 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 23 Oct 2025 18:02:18 +0200 Subject: [PATCH 512/878] Manage all paths in the `paths.ts` file --- frontend/lib/apollo.ts | 4 ++-- frontend/pages/data/create.tsx | 3 ++- frontend/pages/index.tsx | 30 +++++++++++++++--------------- frontend/paths.ts | 14 ++++++++++++++ 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index e687c1ed..8d9f0619 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -64,8 +64,8 @@ function createIsomorphLink(context: ResolverContext = {}) { // `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` as on the client // side. typeof window === "undefined" - ? `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` - : `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/`, + ? new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href + : new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href, useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", headers: headers, diff --git a/frontend/pages/data/create.tsx b/frontend/pages/data/create.tsx index b8b520f9..dd7d16d4 100644 --- a/frontend/pages/data/create.tsx +++ b/frontend/pages/data/create.tsx @@ -1,5 +1,6 @@ import { Row, Col, Card, Typography } from "antd"; import Layout from "../../components/Layout"; +import paths from "../../paths"; function Page() { return ( @@ -10,7 +11,7 @@ function Page() { Create data by sending GraphQL mutations to the{" "} GraphQL endpoint diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index d706c5be..4f3d741c 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -20,7 +20,7 @@ function Page() { Fraunhofer Institute for Solar Energy Systems ISE . The product data server is part of the Product Data Network{" "} - + buildingenvelopedata.org . The product data server is an instance of the{" "} @@ -34,7 +34,7 @@ function Page() { is open-source with a permissive license, so that everyone can easily create their own product data server as part of the Product Data Network{" "} - + buildingenvelopedata.org . @@ -50,48 +50,48 @@ function Page() { calorimetric data and{" "} photovoltaic data. If you would like to search the entire Product Data Network{" "} - + buildingenvelopedata.org , you can search there for example for{" "} optical data{" "} . You will find there also an overview about all building envelope{" "} {" "} components ,{" "} institutions ,{" "} data formats ,{" "} methods {" "} and{" "} databases {" "} of the Product Data Network{" "} - + buildingenvelopedata.org . @@ -99,14 +99,14 @@ function Page() { This website is completely based on the {" "} GraphQL endpoint . The{" "} GraphQL endpoint {" "} @@ -120,7 +120,7 @@ function Page() { which is especially helpful if you develop software to plan buildings. The{" "} GraphQL endpoint of the metabase {" "} @@ -130,7 +130,7 @@ function Page() { The data format{" "} BED-JSON {" "} @@ -141,7 +141,7 @@ function Page() { . Other{" "} data formats {" "} diff --git a/frontend/paths.ts b/frontend/paths.ts index e77c7bce..24f8617e 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -29,24 +29,38 @@ export default { uploadFile: "/upload-file", login: "/connect/login", logout: "/connect/logout", + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href, metabase: { + home: process.env.NEXT_PUBLIC_METABASE_URL, + components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL).href, component(uuid: string) { return new URL(`/components/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL).href, database(uuid: string) { return new URL(`/databases/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL).href, dataFormat(uuid: string) { return new URL(`/data-formats/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL).href, institution(uuid: string) { return new URL(`/institutions/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL).href, method(uuid: string) { return new URL(`/methods/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL).href, user(uuid: string) { return new URL(`/users/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href }, + allCalorimetricData: new URL(`/data/calorimetric`, process.env.NEXT_PUBLIC_METABASE_URL).href, + allGeometricData: new URL(`/data/geometric`, process.env.NEXT_PUBLIC_METABASE_URL).href, + allHygrothermalData: new URL(`/data/hygrothermal`, process.env.NEXT_PUBLIC_METABASE_URL).href, + allOpticalData: new URL(`/data/optical`, process.env.NEXT_PUBLIC_METABASE_URL).href, + allPhotovoltaicData: new URL(`/data/photovoltaic`, process.env.NEXT_PUBLIC_METABASE_URL).href, + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL).href, }, }; From 3a02d80add00c0b5153c7ef3da6eb2edef3d3830 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 23 Oct 2025 16:07:59 +0200 Subject: [PATCH 513/878] Extract integral values of optical data sets from BED-JSON files using JSONPaths when they have been uploaded --- .../src/ApiRequests/JsonSerializerSettings.cs | 10 +++- .../src/Configuration/GraphQlConfiguration.cs | 2 +- .../src/Controllers/FileUploadController.cs | 7 +++ backend/src/Data/OpticalData.cs | 56 +++++++++++++++++++ backend/src/Database.csproj | 1 + .../DataPoints/DataPointResult.cs | 11 ++++ .../DataPointsEnumerationsExtensions.cs | 50 +++++++++++++++++ .../DataPoints/EmergenceDirection.cs | 7 +++ .../DataPoints/IncidenceDirection.cs | 7 +++ .../DataPoints/WavelengthsIntegral.cs | 10 ++++ .../src/Extensions/EnumerableExtensions.cs | 18 +++++- .../CielabColorResultsJsonExtractor.cs | 19 +++++++ .../src/Extractors/ClassJsonPathExtractor.cs | 35 ++++++++++++ ...ColorRenderingIndexResultsJsonExtractor.cs | 23 ++++++++ .../Extractors/DoubleResultsJsonExtractor.cs | 29 ++++++++++ backend/src/Extractors/JsonExtractorBase.cs | 17 ++++++ .../src/Extractors/StructJsonPathExtractor.cs | 34 +++++++++++ backend/src/GraphQl/DataX/DataMutations.cs | 38 +++++++++++++ 18 files changed, 369 insertions(+), 5 deletions(-) create mode 100644 backend/src/Enumerations/DataPoints/DataPointResult.cs create mode 100644 backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs create mode 100644 backend/src/Enumerations/DataPoints/EmergenceDirection.cs create mode 100644 backend/src/Enumerations/DataPoints/IncidenceDirection.cs create mode 100644 backend/src/Enumerations/DataPoints/WavelengthsIntegral.cs create mode 100644 backend/src/Extractors/CielabColorResultsJsonExtractor.cs create mode 100644 backend/src/Extractors/ClassJsonPathExtractor.cs create mode 100644 backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs create mode 100644 backend/src/Extractors/DoubleResultsJsonExtractor.cs create mode 100644 backend/src/Extractors/JsonExtractorBase.cs create mode 100644 backend/src/Extractors/StructJsonPathExtractor.cs create mode 100644 backend/src/GraphQl/DataX/DataMutations.cs diff --git a/backend/src/ApiRequests/JsonSerializerSettings.cs b/backend/src/ApiRequests/JsonSerializerSettings.cs index e7312f61..7d748cbc 100644 --- a/backend/src/ApiRequests/JsonSerializerSettings.cs +++ b/backend/src/ApiRequests/JsonSerializerSettings.cs @@ -12,8 +12,7 @@ public static class JsonSerializerSettings private static readonly JsonSerializerOptions s_common = new() { - AllowOutOfOrderMetadataProperties = true, - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + AllowOutOfOrderMetadataProperties = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = false, @@ -26,6 +25,7 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions GraphQl = new(s_common) { + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; @@ -34,4 +34,10 @@ public static class JsonSerializerSettings { PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, }; + + public static readonly JsonSerializerOptions BedJson = + new(s_common) + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; } \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index e16016b4..a00b7b41 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -137,7 +137,6 @@ IWebHostEnvironment environment // Query Types .AddQueryType(d => d.Name(nameof(Query))) .AddType() - .AddType() .AddType() .AddType() .AddType() @@ -160,6 +159,7 @@ IWebHostEnvironment environment .AddType() .AddType() .AddType() + .AddType() /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 15db1e57..c8131ce6 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -187,6 +187,13 @@ CancellationToken cancellationToken // read the headers for the next section. section = await reader.ReadNextSectionAsync(cancellationToken); } + if (getHttpsResource.OpticalData is not null) + { + await getHttpsResource.OpticalData.ExtractAndSetMirroredValuesFromFile( + getHttpsResource.FilePath, + getHttpsResource.DataFormatId + ); + } await getHttpsResource.RecomputeHashValue(cancellationToken); await context.SaveChangesAsync(cancellationToken); return Created(nameof(FileUploadController), null); diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 650fe81d..dca8d770 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,13 +1,18 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; using Database.Enumerations; +using Database.Enumerations.DataPoints; +using Database.Extractors; namespace Database.Data; public sealed class OpticalData : DataX { + public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); + public OpticalData( string locale, Guid componentId, @@ -102,4 +107,55 @@ double[] colorRenderingIndices public double[] InfraredEmittances { get; private set; } public double[] ColorRenderingIndices { get; private set; } public ICollection CielabColors { get; private set; } = []; + + public async Task ExtractAndSetMirroredValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + if (dataFormatId == BedJsonDataFormatId) + { + NearnormalHemisphericalVisibleTransmittances = + await new DoubleResultsJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.VISIBLE, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.TRANSMITTANCE + ).Extract(filePath); + NearnormalHemisphericalVisibleReflectances = + await new DoubleResultsJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.VISIBLE, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.REFLECTANCE + ).Extract(filePath); + NearnormalHemisphericalSolarTransmittances = + await new DoubleResultsJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.SOLAR, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.TRANSMITTANCE + ).Extract(filePath); + NearnormalHemisphericalSolarReflectances = + await new DoubleResultsJsonExtractor( + IncidenceDirection.NEARNORMAL, + WavelengthsIntegral.SOLAR, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.REFLECTANCE + ).Extract(filePath); + InfraredEmittances = + await new DoubleResultsJsonExtractor( + IncidenceDirection.HEMISPHERICAL, + WavelengthsIntegral.INFRARED, + EmergenceDirection.HEMISPHERICAL, + DataPointResult.ABSORPTANCE_EMITTANCE + ).Extract(filePath); + ColorRenderingIndices = + await new ColorRenderingIndexResultsJsonExtractor() + .Extract(filePath); + CielabColors = + await new CielabColorResultsJsonExtractor() + .Extract(filePath); + } + } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index c2a5387d..61df65b1 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -33,6 +33,7 @@ + diff --git a/backend/src/Enumerations/DataPoints/DataPointResult.cs b/backend/src/Enumerations/DataPoints/DataPointResult.cs new file mode 100644 index 00000000..4c1dafb0 --- /dev/null +++ b/backend/src/Enumerations/DataPoints/DataPointResult.cs @@ -0,0 +1,11 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations.DataPoints; + +[SuppressMessage("Naming", "CA1707")] +public enum DataPointResult +{ + TRANSMITTANCE, + REFLECTANCE, + ABSORPTANCE_EMITTANCE +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs new file mode 100644 index 00000000..8114d85b --- /dev/null +++ b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs @@ -0,0 +1,50 @@ +using System; + +namespace Database.Enumerations.DataPoints; + +public static class DataPointsEnumerationsExtensions +{ + public static string ToJsonPathQuery(this IncidenceDirection incidenceDirection) + { + return incidenceDirection switch + { + IncidenceDirection.NEARNORMAL => ".polar <= 10", + IncidenceDirection.HEMISPHERICAL => "=='hemispherical'", + _ => throw new ArgumentOutOfRangeException(nameof(incidenceDirection), $"Unsupported incidence direction {incidenceDirection}."), + }; + } + + public static string ToJsonEnum(this WavelengthsIntegral wavelengthsIntegral) + { + return wavelengthsIntegral switch + { + WavelengthsIntegral.INFRARED => "infrared", + WavelengthsIntegral.SOLAR => "solar", + WavelengthsIntegral.ULTRAVIOLET => "ultraviolet", + WavelengthsIntegral.VISIBLE => "visible", + WavelengthsIntegral.OTHER => "other", + _ => throw new ArgumentOutOfRangeException(nameof(wavelengthsIntegral), $"Unsupported wavelengths integral {wavelengthsIntegral}."), + }; + } + + public static string ToJsonEnum(this EmergenceDirection emergenceDirection) + { + return emergenceDirection switch + { + EmergenceDirection.DIFFUSE => "diffuse", + EmergenceDirection.HEMISPHERICAL => "hemispherical", + _ => throw new ArgumentOutOfRangeException(nameof(emergenceDirection), $"Unsupported emergence direction {emergenceDirection}."), + }; + } + + public static string ToJsonEnum(this DataPointResult result) + { + return result switch + { + DataPointResult.TRANSMITTANCE => "transmittance", + DataPointResult.REFLECTANCE => "reflectance", + DataPointResult.ABSORPTANCE_EMITTANCE => "absorptanceEmittance", + _ => throw new ArgumentOutOfRangeException(nameof(result), $"Unsupported result {result}."), + }; + } +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataPoints/EmergenceDirection.cs b/backend/src/Enumerations/DataPoints/EmergenceDirection.cs new file mode 100644 index 00000000..284e62bd --- /dev/null +++ b/backend/src/Enumerations/DataPoints/EmergenceDirection.cs @@ -0,0 +1,7 @@ +namespace Database.Enumerations.DataPoints; + +public enum EmergenceDirection +{ + DIFFUSE, + HEMISPHERICAL +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataPoints/IncidenceDirection.cs b/backend/src/Enumerations/DataPoints/IncidenceDirection.cs new file mode 100644 index 00000000..510cd19e --- /dev/null +++ b/backend/src/Enumerations/DataPoints/IncidenceDirection.cs @@ -0,0 +1,7 @@ +namespace Database.Enumerations.DataPoints; + +public enum IncidenceDirection +{ + NEARNORMAL, + HEMISPHERICAL +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataPoints/WavelengthsIntegral.cs b/backend/src/Enumerations/DataPoints/WavelengthsIntegral.cs new file mode 100644 index 00000000..7ca9dc5e --- /dev/null +++ b/backend/src/Enumerations/DataPoints/WavelengthsIntegral.cs @@ -0,0 +1,10 @@ +namespace Database.Enumerations.DataPoints; + +public enum WavelengthsIntegral +{ + INFRARED, + SOLAR, + ULTRAVIOLET, + VISIBLE, + OTHER +} \ No newline at end of file diff --git a/backend/src/Extensions/EnumerableExtensions.cs b/backend/src/Extensions/EnumerableExtensions.cs index 52cd51e3..423b17df 100644 --- a/backend/src/Extensions/EnumerableExtensions.cs +++ b/backend/src/Extensions/EnumerableExtensions.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; namespace Database.Extensions; @@ -6,8 +7,21 @@ namespace Database.Extensions; public static class EnumerableExtensions { // An alternative would be `Index` from https://github.com/morelinq/MoreLINQ - public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable source) + [Pure] + public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable enumerable) { - return source.Select((item, index) => (item, index)); + return enumerable.Select((item, index) => (item, index)); + } + + [Pure] + public static IEnumerable NotNull(this IEnumerable enumerable) where T : class + { + return enumerable.Where(item => item is not null).Select(item => item!); + } + + [Pure] + public static IEnumerable NotNull(this IEnumerable enumerable) where T : struct + { + return enumerable.Where(item => item.HasValue).Select(item => item!.Value); } } \ No newline at end of file diff --git a/backend/src/Extractors/CielabColorResultsJsonExtractor.cs b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs new file mode 100644 index 00000000..43647a23 --- /dev/null +++ b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs @@ -0,0 +1,19 @@ +using Json.Path; +using Database.Data; +using Database.Enumerations.DataPoints; +using System.Text.Json; +using Database.ApiRequests; + +namespace Database.Extractors; + +public sealed class CielabColorResultsJsonExtractor( +) : ClassJsonPathExtractor( + JsonPath.Parse( + $"$.data[*].dataPoints[?(" + + $"@.incidence.wavelengths.integral=='{WavelengthsIntegral.VISIBLE.ToJsonEnum()}'" + + ")].results['colorTransmittance','colorReflectance'].cielabColors[*]" + ), + node => node.Deserialize(JsonSerializerSettings.BedJson) +) +{ +} \ No newline at end of file diff --git a/backend/src/Extractors/ClassJsonPathExtractor.cs b/backend/src/Extractors/ClassJsonPathExtractor.cs new file mode 100644 index 00000000..39eae141 --- /dev/null +++ b/backend/src/Extractors/ClassJsonPathExtractor.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using Json.More; +using Json.Path; +using Database.Extensions; +using System.Collections.Generic; + +namespace Database.Extractors; + +public class ClassJsonPathExtractor( + JsonPath jsonPath, + Func parseNode +) + : JsonExtractorBase> + where TOutput : class +{ + public override ICollection Extract(JsonDocument jsonDocument) + { + var pathResult = jsonPath.Evaluate(jsonDocument.RootElement.AsNode()); + // `JsonPath` defers evaluation according to + // https://docs.json-everything.net/path/basics/#deferred-execution + // Because the `JsonDocument` will have been disposed of more or less + // after this method returns, we need to evaluate the enumerable + // `pathResult.Matches` directly by calling `ToArray` instead of + // deferring it until later. + return pathResult.Matches + .Where(node => node.Value is not null) + .Select(node => parseNode(node.Value!)) // using `!` is safe here due to the `is not null` filter + .NotNull() + .ToList(); + } + +} \ No newline at end of file diff --git a/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs new file mode 100644 index 00000000..a03b1505 --- /dev/null +++ b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs @@ -0,0 +1,23 @@ +using Json.Path; +using Database.Enumerations.DataPoints; + +namespace Database.Extractors; + +public sealed class ColorRenderingIndexResultsJsonExtractor( +) : StructJsonPathExtractor( + JsonPath.Parse( + $"$.data[*].dataPoints[?(" + + $"@.incidence.wavelengths.integral=='{WavelengthsIntegral.VISIBLE.ToJsonEnum()}'" + + $")].results.colorTransmittance.colorRenderingIndices[*]['ra','raOutIn']" + ), + node => + { + if (node.TryGetValue(out var value)) + { + return value; + } + return null; + } +) +{ +} \ No newline at end of file diff --git a/backend/src/Extractors/DoubleResultsJsonExtractor.cs b/backend/src/Extractors/DoubleResultsJsonExtractor.cs new file mode 100644 index 00000000..7709be4e --- /dev/null +++ b/backend/src/Extractors/DoubleResultsJsonExtractor.cs @@ -0,0 +1,29 @@ +using Json.Path; +using Database.Enumerations.DataPoints; + +namespace Database.Extractors; + +public sealed class DoubleResultsJsonExtractor( + IncidenceDirection incidenceDirection, + WavelengthsIntegral incidenceWavelengthsIntegral, + EmergenceDirection emergenceDirection, + DataPointResult result +) : StructJsonPathExtractor( + JsonPath.Parse( + $"$.data[*].dataPoints[?(" + + $"@.incidence.direction{incidenceDirection.ToJsonPathQuery()} && " + + $"@.incidence.wavelengths.integral=='{incidenceWavelengthsIntegral.ToJsonEnum()}' && " + + $"@.emergence.direction=='{emergenceDirection.ToJsonEnum()}'" + + $")].results.{result.ToJsonEnum()}" + ), + node => + { + if (node.TryGetValue(out var value)) + { + return value; + } + return null; + } +) +{ +} \ No newline at end of file diff --git a/backend/src/Extractors/JsonExtractorBase.cs b/backend/src/Extractors/JsonExtractorBase.cs new file mode 100644 index 00000000..7212ec5a --- /dev/null +++ b/backend/src/Extractors/JsonExtractorBase.cs @@ -0,0 +1,17 @@ +using System.IO; +using System.Text.Json; +using System.Threading.Tasks; + +namespace Database.Extractors; + +public abstract class JsonExtractorBase +{ + public async Task Extract(string jsonFilePath) + { + using var fileStream = File.OpenRead(jsonFilePath); + using var jsonDocument = await JsonDocument.ParseAsync(fileStream); + return Extract(jsonDocument); + } + + public abstract TOutput Extract(JsonDocument jsonDocument); +} \ No newline at end of file diff --git a/backend/src/Extractors/StructJsonPathExtractor.cs b/backend/src/Extractors/StructJsonPathExtractor.cs new file mode 100644 index 00000000..f21b16a8 --- /dev/null +++ b/backend/src/Extractors/StructJsonPathExtractor.cs @@ -0,0 +1,34 @@ +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using Json.More; +using Json.Path; +using Database.Extensions; + +namespace Database.Extractors; + +public class StructJsonPathExtractor( + JsonPath jsonPath, + Func parseNode +) + : JsonExtractorBase + where TOutput : struct +{ + public override TOutput[] Extract(JsonDocument jsonDocument) + { + var pathResult = jsonPath.Evaluate(jsonDocument.RootElement.AsNode()); + // `JsonPath` defers evaluation according to + // https://docs.json-everything.net/path/basics/#deferred-execution + // Because the `JsonDocument` will have been disposed of more or less + // after this method returns, we need to evaluate the enumerable + // `pathResult.Matches` directly by calling `ToArray` instead of + // deferring it until later. + return pathResult.Matches + .Where(node => node.Value is not null) + .Select(node => parseNode(node.Value!)) // using `!` is safe here due to the `is not null` filter + .NotNull() + .ToArray(); + } + +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataMutations.cs b/backend/src/GraphQl/DataX/DataMutations.cs new file mode 100644 index 00000000..1d66bb62 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataMutations.cs @@ -0,0 +1,38 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.DataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DataMutations +{ + // public async Task ExtractMirroredValuesFromFilesAsync( + // ExtractIntegralValuesFromFilesInput input, + // ApplicationDbContext context, + // UserService userService, + // ResponseApprovalService responseApprovalService, + // CancellationToken cancellationToken + // ) + // { + // var currentUser = await userService.GetCurrentUser(cancellationToken); + // if (currentUser is null) + // { + // return new ExtractIntegralValuesFromFilesPayload( + // new ExtractIntegralValuesFromFilesError( + // ExtractIntegralValuesFromFilesErrorCode.UNAUTHENTICATED, + // $"The user is not authenticated.", + // [] + // ) + // ); + // } + // if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + // { + // return new ExtractIntegralValuesFromFilesPayload( + // new ExtractIntegralValuesFromFilesError( + // ExtractIntegralValuesFromFilesErrorCode.UNAUTHORIZED, + // $"The current user is not authorized to set mirrored values from files in this database.", + // [] + // ) + // ); + // } + // } +} \ No newline at end of file From a706a1566713b4de3a6efdde4a88c6187215ac40 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 24 Oct 2025 16:30:57 +0200 Subject: [PATCH 514/878] Add observer and illuminant to CIE lab colors and color rendering indices, make them convert properly to and from JSON, and support numbers with uncertainty when extracting numbers from JSON files --- .../src/ApiRequests/JsonSerializerSettings.cs | 6 +- backend/src/Data/ApplicationDbContext.cs | 25 +- backend/src/Data/CielabColor.cs | 17 +- backend/src/Data/ColorRenderingIndex.cs | 22 + .../src/Enumerations/CalorimetricObserver.cs | 10 + backend/src/Enumerations/Illuminant.cs | 10 + ...ColorRenderingIndexResultsJsonExtractor.cs | 9 +- .../Extractors/DoubleResultsJsonExtractor.cs | 10 +- .../src/Extractors/StructJsonPathExtractor.cs | 13 + backend/src/GraphQl/CielabColorInput.cs | 6 +- .../OpticalDataX/OpticalDataMutations.cs | 4 +- .../ConstantToCamelCaseJsonNamingPolicy.cs | 10 + .../Json/NumberWithUncertaintyConverter.cs | 45 + ...rverAndIlluminantToCielabColor.Designer.cs | 2643 +++++++++++++++++ ...4_AddObserverAndIlluminantToCielabColor.cs | 105 + .../ApplicationDbContextModelSnapshot.cs | 25 +- backend/src/Migrations/migrate.sql | 69 + ..._AddObserverAndIlluminantToCielabColor.sql | 83 + ...23152609_RememberUserWhoCreatedDataSet.sql | 50 + backend/src/Startup.cs | 2 + 20 files changed, 3136 insertions(+), 28 deletions(-) create mode 100644 backend/src/Data/ColorRenderingIndex.cs create mode 100644 backend/src/Enumerations/CalorimetricObserver.cs create mode 100644 backend/src/Enumerations/Illuminant.cs create mode 100644 backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs create mode 100644 backend/src/Json/NumberWithUncertaintyConverter.cs create mode 100644 backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs create mode 100644 backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs create mode 100644 backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql create mode 100644 backend/src/Migrations/rollback_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251023152609_RememberUserWhoCreatedDataSet.sql diff --git a/backend/src/ApiRequests/JsonSerializerSettings.cs b/backend/src/ApiRequests/JsonSerializerSettings.cs index 7d748cbc..ce0a0cc0 100644 --- a/backend/src/ApiRequests/JsonSerializerSettings.cs +++ b/backend/src/ApiRequests/JsonSerializerSettings.cs @@ -1,5 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Database.Json; using GraphQL.Client.Serializer.SystemTextJson; namespace Database.ApiRequests; @@ -20,12 +21,14 @@ public static class JsonSerializerSettings NumberHandling = JsonNumberHandling.Strict, PropertyNameCaseInsensitive = false, ReadCommentHandling = JsonCommentHandling.Disallow, + RespectNullableAnnotations = true, + UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, }; public static readonly JsonSerializerOptions GraphQl = new(s_common) { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), false) }, + Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), allowIntegerValues: false) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; @@ -38,6 +41,7 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions BedJson = new(s_common) { + Converters = { new JsonStringEnumConverter(new ConstantToCamelCaseJsonNamingPolicy(), allowIntegerValues: false) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 5fa1fc37..9b16218c 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Org.BouncyCastle.Math.EC.Rfc7748; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; namespace Database.Data; @@ -18,8 +19,10 @@ public sealed class ApplicationDbContext private const string DefaultSchemaName = "database"; private readonly string _schemaName; + internal const string CalorimetricObserverTypeName = "calorimetric_observer"; internal const string CoatedSideTypeName = "coated_side"; internal const string DataKindTypeName = "data_kind"; + internal const string IlluminantTypeName = "illuminant"; internal const string OpticalComponentSubtypeTypeName = "optical_component_subtype"; internal const string OpticalComponentTypeTypeName = "optical_component_type"; internal const string StandardizerTypeName = "standardizer"; @@ -126,6 +129,26 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigureOpticalData( + EntityTypeBuilder builder + ) + { + builder.OwnsMany( + data => data.CielabColors + ).ToTable( + color => + { + color.HasCheckConstraint( + $"CK_OpticalData_CielabColors_{nameof(CielabColor.LStar)}", + $"\"{nameof(CielabColor.LStar)}\" >= 0.0 AND \"{nameof(CielabColor.LStar)}\" <= 100.0" + ); + } + ); + return builder; + } + private static void ConfigureIdentityEntities( ModelBuilder builder ) @@ -153,7 +176,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureData( ConfigureEntity(modelBuilder.Entity()) ).ToTable("hygrothermal_data"); - ConfigureData( + ConfigureOpticalData( ConfigureEntity(modelBuilder.Entity()) ).ToTable("optical_data"); ConfigureData( diff --git a/backend/src/Data/CielabColor.cs b/backend/src/Data/CielabColor.cs index 8b234437..aab0ae35 100644 --- a/backend/src/Data/CielabColor.cs +++ b/backend/src/Data/CielabColor.cs @@ -1,4 +1,8 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; using Microsoft.EntityFrameworkCore; +using Database.Enumerations; +using Database.Json; namespace Database.Data; @@ -6,10 +10,21 @@ namespace Database.Data; public sealed class CielabColor( double lStar, double aStar, - double bStar + double bStar, + CalorimetricObserver? observer = null, + Illuminant? illuminant = null ) { + [Range(0.0, 100.0)] + [JsonConverter(typeof(NumberWithUncertaintyConverter))] public double LStar { get; private set; } = lStar; + + [JsonConverter(typeof(NumberWithUncertaintyConverter))] public double AStar { get; private set; } = aStar; + + [JsonConverter(typeof(NumberWithUncertaintyConverter))] public double BStar { get; private set; } = bStar; + + public CalorimetricObserver? Observer { get; private set; } = observer; + public Illuminant? Illuminant { get; private set; } = illuminant; } \ No newline at end of file diff --git a/backend/src/Data/ColorRenderingIndex.cs b/backend/src/Data/ColorRenderingIndex.cs new file mode 100644 index 00000000..066f095e --- /dev/null +++ b/backend/src/Data/ColorRenderingIndex.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; +using Database.Enumerations; +using Database.Json; + +namespace Database.Data; + +[Owned] +public sealed class ColorRenderingIndex( + double ra, + // double raOutIn, + CalorimetricObserver? observer, + Illuminant? illuminant + ) +{ + [Range(0.0, 100.0)] + [JsonConverter(typeof(NumberWithUncertaintyConverter))] + public double Ra { get; private set; } = ra; + public CalorimetricObserver? Observer { get; private set; } = observer; + public Illuminant? Illuminant { get; private set; } = illuminant; +} \ No newline at end of file diff --git a/backend/src/Enumerations/CalorimetricObserver.cs b/backend/src/Enumerations/CalorimetricObserver.cs new file mode 100644 index 00000000..0a86e176 --- /dev/null +++ b/backend/src/Enumerations/CalorimetricObserver.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum CalorimetricObserver +{ + TWO_DEGREES, + TEN_DEGREES +} \ No newline at end of file diff --git a/backend/src/Enumerations/Illuminant.cs b/backend/src/Enumerations/Illuminant.cs new file mode 100644 index 00000000..3dd63504 --- /dev/null +++ b/backend/src/Enumerations/Illuminant.cs @@ -0,0 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +[SuppressMessage("Naming", "CA1707")] +public enum Illuminant +{ + A, + D65 +} \ No newline at end of file diff --git a/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs index a03b1505..adf983f7 100644 --- a/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs +++ b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs @@ -10,14 +10,7 @@ public sealed class ColorRenderingIndexResultsJsonExtractor( $"@.incidence.wavelengths.integral=='{WavelengthsIntegral.VISIBLE.ToJsonEnum()}'" + $")].results.colorTransmittance.colorRenderingIndices[*]['ra','raOutIn']" ), - node => - { - if (node.TryGetValue(out var value)) - { - return value; - } - return null; - } + ExtractNumberWithUncertainty ) { } \ No newline at end of file diff --git a/backend/src/Extractors/DoubleResultsJsonExtractor.cs b/backend/src/Extractors/DoubleResultsJsonExtractor.cs index 7709be4e..8ab1a2f7 100644 --- a/backend/src/Extractors/DoubleResultsJsonExtractor.cs +++ b/backend/src/Extractors/DoubleResultsJsonExtractor.cs @@ -1,5 +1,6 @@ using Json.Path; using Database.Enumerations.DataPoints; +using System.Text.Json; namespace Database.Extractors; @@ -16,14 +17,7 @@ DataPointResult result $"@.emergence.direction=='{emergenceDirection.ToJsonEnum()}'" + $")].results.{result.ToJsonEnum()}" ), - node => - { - if (node.TryGetValue(out var value)) - { - return value; - } - return null; - } + ExtractNumberWithUncertainty ) { } \ No newline at end of file diff --git a/backend/src/Extractors/StructJsonPathExtractor.cs b/backend/src/Extractors/StructJsonPathExtractor.cs index f21b16a8..d1d0ba23 100644 --- a/backend/src/Extractors/StructJsonPathExtractor.cs +++ b/backend/src/Extractors/StructJsonPathExtractor.cs @@ -31,4 +31,17 @@ public override TOutput[] Extract(JsonDocument jsonDocument) .ToArray(); } + protected static double? ExtractNumberWithUncertainty(JsonNode node) + { + return node.GetValueKind() switch + { + JsonValueKind.Object => + node.AsObject() + .TryGetPropertyValue("uncertainValue", out var uncertainValueNode) + ? (uncertainValueNode.TryGetValue(out var uncertainValue) ? uncertainValue : null) + : null, + JsonValueKind.Number => node.TryGetValue(out var value) ? value : null, + _ => null, + }; + } } \ No newline at end of file diff --git a/backend/src/GraphQl/CielabColorInput.cs b/backend/src/GraphQl/CielabColorInput.cs index 3bca12da..84da3718 100644 --- a/backend/src/GraphQl/CielabColorInput.cs +++ b/backend/src/GraphQl/CielabColorInput.cs @@ -1,7 +1,11 @@ +using Database.Enumerations; + namespace Database.GraphQl; public sealed record CielabColorInput( double LStar, double AStar, - double BStar + double BStar, + CalorimetricObserver? Observer, + Illuminant? Illuminant ); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 0a0971b6..fe3ed798 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -86,7 +86,9 @@ CancellationToken cancellationToken new CielabColor( c.LStar, c.AStar, - c.BStar + c.BStar, + c.Observer, + c.Illuminant ) ).ToList() ); diff --git a/backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs b/backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs new file mode 100644 index 00000000..e78aece1 --- /dev/null +++ b/backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs @@ -0,0 +1,10 @@ +using System.Text.Json; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.Json; + +public sealed class ConstantToCamelCaseJsonNamingPolicy + : JsonNamingPolicy +{ + public override string ConvertName(string name) => name.ToCamelCase(); +} \ No newline at end of file diff --git a/backend/src/Json/NumberWithUncertaintyConverter.cs b/backend/src/Json/NumberWithUncertaintyConverter.cs new file mode 100644 index 00000000..694e752a --- /dev/null +++ b/backend/src/Json/NumberWithUncertaintyConverter.cs @@ -0,0 +1,45 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Database.Json; + +// Inspired by https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to +public sealed class NumberWithUncertaintyConverter + : JsonConverter +{ + private sealed record NumberWithUncertainty( + double UncertainValue, + [Range(0, double.MaxValue)] + double Uncertainty, + [AllowedValues(68.3, 95.4, 99.7, 100)] + double ConfidenceInterval + ); + + public NumberWithUncertaintyConverter() + { + } + + private static JsonConverter GetNumberWithUncertaintyConverter(JsonSerializerOptions options) + => (JsonConverter)options.GetConverter(typeof(NumberWithUncertainty)); + + public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.Number => reader.GetDouble(), + JsonTokenType.StartObject => + GetNumberWithUncertaintyConverter(options) + .Read(ref reader, typeof(NumberWithUncertainty), options) + ?.UncertainValue + ?? throw new JsonException(), + _ => throw new JsonException() + }; + } + + public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs new file mode 100644 index 00000000..24e8dacd --- /dev/null +++ b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs @@ -0,0 +1,2643 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251024124734_AddObserverAndIlluminantToCielabColor")] + partial class AddObserverAndIlluminantToCielabColor + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs new file mode 100644 index 00000000..49590553 --- /dev/null +++ b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs @@ -0,0 +1,105 @@ +using System.Text.Json; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddObserverAndIlluminantToCielabColor : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.calorimetric_observer", "ten_degrees,two_degrees") + .Annotation("Npgsql:Enum:database.illuminant", "a,d65"); + + migrationBuilder.AlterColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldDefaultValueSql: "'{}'"); + + migrationBuilder.AlterColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldNullable: true, + oldDefaultValueSql: "'{}'"); + + migrationBuilder.AddColumn( + name: "Illuminant", + schema: "database", + table: "CielabColor", + type: "database.illuminant", + nullable: true); + + migrationBuilder.AddColumn( + name: "Observer", + schema: "database", + table: "CielabColor", + type: "database.calorimetric_observer", + nullable: true); + + migrationBuilder.AddCheckConstraint( + name: "CK_OpticalData_CielabColors_LStar", + schema: "database", + table: "CielabColor", + sql: "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropCheckConstraint( + name: "CK_OpticalData_CielabColors_LStar", + schema: "database", + table: "CielabColor"); + + migrationBuilder.DropColumn( + name: "Illuminant", + schema: "database", + table: "CielabColor"); + + migrationBuilder.DropColumn( + name: "Observer", + schema: "database", + table: "CielabColor"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:database.calorimetric_observer", "ten_degrees,two_degrees") + .OldAnnotation("Npgsql:Enum:database.illuminant", "a,d65"); + + migrationBuilder.AlterColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'", + oldClrType: typeof(JsonElement), + oldType: "jsonb"); + + migrationBuilder.AlterColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'", + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldNullable: true); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 120ca924..8437722d 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -24,8 +24,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasAnnotation("ProductVersion", "9.0.10") .HasAnnotation("Relational:MaxIdentifierLength", 63); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); @@ -1843,12 +1845,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.Property("BStar") .HasColumnType("double precision"); + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + b1.Property("LStar") .HasColumnType("double precision"); + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + b1.HasKey("OpticalDataId", "Id"); - b1.ToTable("CielabColor", "database"); + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_AStar", "\"AStar\" >= 0.0 AND \"AStar\" <= 100.0"); + + t.HasCheckConstraint("CK_OpticalData_CielabColors_BStar", "\"BStar\" >= 0.0 AND \"BStar\" <= 100.0"); + + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); b1.WithOwner() .HasForeignKey("OpticalDataId"); @@ -1982,9 +1997,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone"); b1.Property("Variables") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasDefaultValueSql("'{}'"); + .HasColumnType("jsonb"); b1.HasKey("OpticalDataId"); @@ -2028,9 +2041,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone"); b1.Property("Variables") - .ValueGeneratedOnAdd() - .HasColumnType("jsonb") - .HasDefaultValueSql("'{}'"); + .HasColumnType("jsonb"); b1.HasKey("OpticalDataId", "Id"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 6608557e..700385e9 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1400,3 +1400,72 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + CREATE TYPE database.calorimetric_observer AS ENUM ('ten_degrees', 'two_degrees'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + CREATE TYPE database.illuminant AS ENUM ('a', 'd65'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" DROP DEFAULT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" DROP DEFAULT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD "Illuminant" database.illuminant; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD "Observer" database.calorimetric_observer; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_LStar" CHECK ("LStar" >= 0.0 AND "LStar" <= 100.0); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251024124734_AddObserverAndIlluminantToCielabColor', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql b/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql new file mode 100644 index 00000000..32290ef5 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql @@ -0,0 +1,83 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + CREATE TYPE database.calorimetric_observer AS ENUM ('ten_degrees', 'two_degrees'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + CREATE TYPE database.illuminant AS ENUM ('a', 'd65'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" DROP DEFAULT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" DROP DEFAULT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD "Illuminant" database.illuminant; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD "Observer" database.calorimetric_observer; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_AStar" CHECK ("AStar" >= 0.0 AND "AStar" <= 100.0); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_BStar" CHECK ("BStar" >= 0.0 AND "BStar" <= 100.0); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_LStar" CHECK ("LStar" >= 0.0 AND "LStar" <= 100.0); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251024124734_AddObserverAndIlluminantToCielabColor', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251023152609_RememberUserWhoCreatedDataSet.sql b/backend/src/Migrations/rollback_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251023152609_RememberUserWhoCreatedDataSet.sql new file mode 100644 index 00000000..db4bfaaa --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251023152609_RememberUserWhoCreatedDataSet.sql @@ -0,0 +1,50 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" DROP CONSTRAINT "CK_OpticalData_CielabColors_LStar"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" DROP COLUMN "Illuminant"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."CielabColor" DROP COLUMN "Observer"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + DROP TYPE database.calorimetric_observer; + DROP TYPE database.illuminant; + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8ccb0ab5..96ca0b9f 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -175,8 +175,10 @@ AppSettings appSettings appSettings.Database.ConnectionString, _ => _ .SetPostgresVersion(13, 13) + .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.IlluminantTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) From bffbdb0f2776c0663ff863c25c7071acd640220a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 10:15:48 +0200 Subject: [PATCH 515/878] Move JSON settings into JSON namespace --- .../Extractors/CielabColorResultsJsonExtractor.cs | 2 +- .../GraphQl/DataApprovals/DataApprovalMutations.cs | 2 +- backend/src/GraphQl/Methods/MethodQueries.cs | 1 + .../{ApiRequests => Json}/JsonDocumentSettings.cs | 2 +- .../{ApiRequests => Json}/JsonSerializerSettings.cs | 7 +++---- ...mingPolicy.cs => ToCamelCaseJsonNamingPolicy.cs} | 6 +++++- backend/src/Json/ToConstantCaseJsonNamingPolicy.cs | 13 +++++++++++++ backend/src/Services/ApiRequestService.cs | 2 +- .../test/Methods/SpectralToIntegralMethodTests.cs | 2 +- 9 files changed, 27 insertions(+), 10 deletions(-) rename backend/src/{ApiRequests => Json}/JsonDocumentSettings.cs (92%) rename backend/src/{ApiRequests => Json}/JsonSerializerSettings.cs (81%) rename backend/src/Json/{ConstantToCamelCaseJsonNamingPolicy.cs => ToCamelCaseJsonNamingPolicy.cs} (52%) create mode 100644 backend/src/Json/ToConstantCaseJsonNamingPolicy.cs diff --git a/backend/src/Extractors/CielabColorResultsJsonExtractor.cs b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs index 43647a23..83409e43 100644 --- a/backend/src/Extractors/CielabColorResultsJsonExtractor.cs +++ b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs @@ -2,7 +2,7 @@ using Database.Data; using Database.Enumerations.DataPoints; using System.Text.Json; -using Database.ApiRequests; +using Database.Json; namespace Database.Extractors; diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 2e8fe764..35568cd6 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -5,12 +5,12 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; using Database.Authorization; using Database.Data; using Database.GraphQl.Publications; using Database.GraphQl.References; using Database.GraphQl.Standards; +using Database.Json; using Database.Services; using GraphQL; using GraphQL.Client.Abstractions.Utilities; diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 96a97b6b..796feb26 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.ApiRequests; using Database.Enumerations; +using Database.Json; using Database.Services; using GraphQL; using GraphQL.Client.Abstractions.Utilities; diff --git a/backend/src/ApiRequests/JsonDocumentSettings.cs b/backend/src/Json/JsonDocumentSettings.cs similarity index 92% rename from backend/src/ApiRequests/JsonDocumentSettings.cs rename to backend/src/Json/JsonDocumentSettings.cs index 8e33935b..7f297ebb 100644 --- a/backend/src/ApiRequests/JsonDocumentSettings.cs +++ b/backend/src/Json/JsonDocumentSettings.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Database.ApiRequests; +namespace Database.Json; public static class JsonDocumentSettings { diff --git a/backend/src/ApiRequests/JsonSerializerSettings.cs b/backend/src/Json/JsonSerializerSettings.cs similarity index 81% rename from backend/src/ApiRequests/JsonSerializerSettings.cs rename to backend/src/Json/JsonSerializerSettings.cs index ce0a0cc0..3d86a7a4 100644 --- a/backend/src/ApiRequests/JsonSerializerSettings.cs +++ b/backend/src/Json/JsonSerializerSettings.cs @@ -1,9 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Database.Json; using GraphQL.Client.Serializer.SystemTextJson; -namespace Database.ApiRequests; +namespace Database.Json; /// /// Options for JSON serialization. @@ -28,7 +27,7 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions GraphQl = new(s_common) { - Converters = { new JsonStringEnumConverter(new ConstantCaseJsonNamingPolicy(), allowIntegerValues: false) }, + Converters = { new JsonStringEnumConverter(new ToConstantCaseJsonNamingPolicy(), allowIntegerValues: false) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; @@ -41,7 +40,7 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions BedJson = new(s_common) { - Converters = { new JsonStringEnumConverter(new ConstantToCamelCaseJsonNamingPolicy(), allowIntegerValues: false) }, + Converters = { new JsonStringEnumConverter(new ToCamelCaseJsonNamingPolicy(), allowIntegerValues: false) }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, }; } \ No newline at end of file diff --git a/backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs b/backend/src/Json/ToCamelCaseJsonNamingPolicy.cs similarity index 52% rename from backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs rename to backend/src/Json/ToCamelCaseJsonNamingPolicy.cs index e78aece1..088d8457 100644 --- a/backend/src/Json/ConstantToCamelCaseJsonNamingPolicy.cs +++ b/backend/src/Json/ToCamelCaseJsonNamingPolicy.cs @@ -3,7 +3,11 @@ namespace Database.Json; -public sealed class ConstantToCamelCaseJsonNamingPolicy +/// +/// Robust conversion to camel case that in particular converts constant case to +/// camel case. +/// +public sealed class ToCamelCaseJsonNamingPolicy : JsonNamingPolicy { public override string ConvertName(string name) => name.ToCamelCase(); diff --git a/backend/src/Json/ToConstantCaseJsonNamingPolicy.cs b/backend/src/Json/ToConstantCaseJsonNamingPolicy.cs new file mode 100644 index 00000000..a2c06274 --- /dev/null +++ b/backend/src/Json/ToConstantCaseJsonNamingPolicy.cs @@ -0,0 +1,13 @@ +using System.Text.Json; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.Json; + +/// +/// Robust conversion to constant case. +/// +public sealed class ToConstantCaseJsonNamingPolicy + : JsonNamingPolicy +{ + public override string ConvertName(string name) => name.ToConstantCase(); +} \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index d1840b27..c8e54e03 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -7,8 +7,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Database.ApiRequests; using Database.Extensions; +using Database.Json; using GraphQL; using Microsoft.AspNetCore.Http; diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 59e6a6b4..4953fbe9 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -6,10 +6,10 @@ using Database.Methods; using System.IO; using System; -using Database.ApiRequests; using System.Reflection; using FluentAssertions.Execution; using System.Diagnostics.CodeAnalysis; +using Database.Json; namespace Database.Tests.Methods; From b81ebcbb2d40e3009f08357f43c002da110f9271 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 10:48:05 +0200 Subject: [PATCH 516/878] Update GraphQL schema to contain illuminant and calorimetric observer --- .../GraphQlSchemaTests.IsUnchanged.snap | 41 ++++++++++++++----- frontend/type-defs.graphqls | 41 ++++++++++++++----- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index d7666465..16a09a34 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -117,7 +117,9 @@ type CalorimetricDataEdge { type CielabColor { aStar: Float! bStar: Float! + illuminant: Illuminant lStar: Float! + observer: CalorimetricObserver } type CreateCalorimetricDataError { @@ -232,17 +234,6 @@ type DataApproval { variables: Any! } -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -790,7 +781,9 @@ input CalorimetricDataSortInput { input CielabColorInput { aStar: Float! bStar: Float! + illuminant: Illuminant lStar: Float! + observer: CalorimetricObserver } input CielabColorPropositionInput { @@ -799,6 +792,8 @@ input CielabColorPropositionInput { lStar: FloatPropositionInput aStar: FloatPropositionInput bStar: FloatPropositionInput + observer: NullableOfCalorimetricObserverPropositionInput + illuminant: NullableOfIlluminantPropositionInput } input CreateCalorimetricDataInput { @@ -1209,6 +1204,13 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NullableOfCalorimetricObserverPropositionInput { + equalTo: CalorimetricObserver @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") + in: [CalorimetricObserver] @cost(weight: "10") + notIn: [CalorimetricObserver] @cost(weight: "10") +} + input NullableOfCoatedSidePropositionInput { equalTo: CoatedSide @cost(weight: "10") notEqualTo: CoatedSide @cost(weight: "10") @@ -1216,6 +1218,13 @@ input NullableOfCoatedSidePropositionInput { notIn: [CoatedSide] @cost(weight: "10") } +input NullableOfIlluminantPropositionInput { + equalTo: Illuminant @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") + in: [Illuminant] @cost(weight: "10") + notIn: [Illuminant] @cost(weight: "10") +} + input NullableOfInt32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -1528,6 +1537,11 @@ enum CalculateMethodErrorCode { DATA_QUERY_FAILED } +enum CalorimetricObserver { + TWO_DEGREES + TEN_DEGREES +} + enum CoatedSide { PRIME NON_PRIME @@ -1600,6 +1614,11 @@ enum DatabaseVerificationState { VERIFIED } +enum Illuminant { + A + D65 +} + enum InstitutionAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index d7666465..16a09a34 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -117,7 +117,9 @@ type CalorimetricDataEdge { type CielabColor { aStar: Float! bStar: Float! + illuminant: Illuminant lStar: Float! + observer: CalorimetricObserver } type CreateCalorimetricDataError { @@ -232,17 +234,6 @@ type DataApproval { variables: Any! } -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -790,7 +781,9 @@ input CalorimetricDataSortInput { input CielabColorInput { aStar: Float! bStar: Float! + illuminant: Illuminant lStar: Float! + observer: CalorimetricObserver } input CielabColorPropositionInput { @@ -799,6 +792,8 @@ input CielabColorPropositionInput { lStar: FloatPropositionInput aStar: FloatPropositionInput bStar: FloatPropositionInput + observer: NullableOfCalorimetricObserverPropositionInput + illuminant: NullableOfIlluminantPropositionInput } input CreateCalorimetricDataInput { @@ -1209,6 +1204,13 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NullableOfCalorimetricObserverPropositionInput { + equalTo: CalorimetricObserver @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") + in: [CalorimetricObserver] @cost(weight: "10") + notIn: [CalorimetricObserver] @cost(weight: "10") +} + input NullableOfCoatedSidePropositionInput { equalTo: CoatedSide @cost(weight: "10") notEqualTo: CoatedSide @cost(weight: "10") @@ -1216,6 +1218,13 @@ input NullableOfCoatedSidePropositionInput { notIn: [CoatedSide] @cost(weight: "10") } +input NullableOfIlluminantPropositionInput { + equalTo: Illuminant @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") + in: [Illuminant] @cost(weight: "10") + notIn: [Illuminant] @cost(weight: "10") +} + input NullableOfInt32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -1528,6 +1537,11 @@ enum CalculateMethodErrorCode { DATA_QUERY_FAILED } +enum CalorimetricObserver { + TWO_DEGREES + TEN_DEGREES +} + enum CoatedSide { PRIME NON_PRIME @@ -1600,6 +1614,11 @@ enum DatabaseVerificationState { VERIFIED } +enum Illuminant { + A + D65 +} + enum InstitutionAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED From 6779f16ee1efde83051858441fb3ea7981a78273 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 10:51:35 +0200 Subject: [PATCH 517/878] DRY-up GraphQL optical, geometric, ... data type definitions --- .../src/Configuration/GraphQlConfiguration.cs | 3 -- backend/src/Data/IApproval.cs | 4 +-- backend/src/Data/IData.cs | 4 +-- backend/src/Data/IReference.cs | 4 +-- .../CalorimetricDataResolvers.cs | 32 ------------------- .../CalorimetricDataX/CalorimetricDataType.cs | 16 ++-------- .../DataResolvers.cs} | 15 ++++----- backend/src/GraphQl/DataX/DataType.cs | 16 ++++++---- backend/src/GraphQl/DataX/DataTypeBase.cs | 32 +++++++++++++++++++ .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 10 +++--- backend/src/GraphQl/Entities/EntityType.cs | 3 +- .../GeometricDataX/GeometricDataResolvers.cs | 32 ------------------- .../GeometricDataX/GeometricDataType.cs | 16 ++-------- backend/src/GraphQl/GraphQlConstants.cs | 8 +++++ .../HygrothermalDataResolvers.cs | 32 ------------------- .../HygrothermalDataX/HygrothermalDataType.cs | 16 ++-------- .../GraphQl/OpticalDataX/OpticalDataType.cs | 18 ++--------- .../PhotovoltaicDataResolvers.cs | 32 ------------------- .../PhotovoltaicDataX/PhotovoltaicDataType.cs | 16 ++-------- 19 files changed, 78 insertions(+), 231 deletions(-) delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs rename backend/src/GraphQl/{OpticalDataX/OpticalDataResolvers.cs => DataX/DataResolvers.cs} (57%) create mode 100644 backend/src/GraphQl/DataX/DataTypeBase.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs create mode 100644 backend/src/GraphQl/GraphQlConstants.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index a00b7b41..9abb7921 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -38,9 +38,6 @@ namespace Database.Configuration; public static class GraphQlConfiguration { - internal const string TypeDiscriminatorPropertyName = "__typename"; - public const string FilterInputSuffix = "FilterInput"; - public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 8e457331..788f1c2d 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,13 +1,13 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; -using Database.Configuration; using HotChocolate.Types; +using Database.GraphQl; namespace Database.Data; [InterfaceType("Approval")] -[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.TypeDiscriminatorPropertyName)] [JsonDerivedType(typeof(ResponseApproval), typeDiscriminator: nameof(ResponseApproval))] [JsonDerivedType(typeof(DataApproval), typeDiscriminator: nameof(DataApproval))] public interface IApproval diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index df40e39e..6367de3c 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using Database.Configuration; +using Database.GraphQl; namespace Database.Data; -[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.TypeDiscriminatorPropertyName)] [JsonDerivedType(typeof(CalorimetricData), typeDiscriminator: nameof(CalorimetricData))] [JsonDerivedType(typeof(GeometricData), typeDiscriminator: nameof(GeometricData))] [JsonDerivedType(typeof(HygrothermalData), typeDiscriminator: nameof(HygrothermalData))] diff --git a/backend/src/Data/IReference.cs b/backend/src/Data/IReference.cs index 62fc90c5..c8e797f0 100644 --- a/backend/src/Data/IReference.cs +++ b/backend/src/Data/IReference.cs @@ -1,9 +1,9 @@ using System.Text.Json.Serialization; -using Database.Configuration; +using Database.GraphQl; namespace Database.Data; -[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConfiguration.TypeDiscriminatorPropertyName)] +[JsonPolymorphic(TypeDiscriminatorPropertyName = GraphQlConstants.TypeDiscriminatorPropertyName)] [JsonDerivedType(typeof(Standard), typeDiscriminator: nameof(Standard))] [JsonDerivedType(typeof(Publication), typeDiscriminator: nameof(Publication))] public interface IReference diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs deleted file mode 100644 index 8e8f803e..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataResolvers.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.CalorimetricDataX; - -public sealed class CalorimetricDataResolvers -{ - public async Task GetGetHttpsResources( - [Parent] CalorimetricData calorimetricData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return await byId.LoadAsync(calorimetricData.Id, cancellationToken) ?? []; - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] CalorimetricData calorimetricData - ) - { - return new GetHttpsResourceTree(calorimetricData); - } - - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs index 24824537..edd6754a 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs @@ -1,28 +1,16 @@ using Database.Data; using HotChocolate.Types; +using Database.GraphQl.DataX; namespace Database.GraphQl.CalorimetricDataX; public sealed class CalorimetricDataType - : EntityType + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs similarity index 57% rename from backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs rename to backend/src/GraphQl/DataX/DataResolvers.cs index d26f938b..cd987bda 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,28 +1,27 @@ using System; using System.Threading; using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.DataX; using HotChocolate; +using Database.Data; -namespace Database.GraphQl.OpticalDataX; +namespace Database.GraphQl.DataX; -public sealed class OpticalDataResolvers +public sealed class DataResolvers { public async Task GetGetHttpsResources( - [Parent] OpticalData opticalData, + [Parent] IData data, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return await byId.LoadAsync(opticalData.Id, cancellationToken) ?? []; + return await byId.LoadAsync(data.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] OpticalData opticalData + [Parent] IData data ) { - return new GetHttpsResourceTree(opticalData); + return new GetHttpsResourceTree(data); } public DateTime GetTimestamp() diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 83cb2fc0..05975da6 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -4,9 +4,11 @@ namespace Database.GraphQl.DataX; public sealed class DataType(AppSettings appSettings) - : InterfaceType + : InterfaceType { - private readonly AppSettings _appSettings = appSettings; + public const string DatabaseIdFieldName = "databaseId"; + public const string TimestampFieldName = "timestamp"; + public const string ResourceTreeFieldName = "resourceTree"; protected override void Configure( IInterfaceTypeDescriptor descriptor @@ -15,17 +17,17 @@ IInterfaceTypeDescriptor descriptor // `1..` is a range as introduced in https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges descriptor.Name(nameof(IData)[1..]); descriptor - .Field("uuid") + .Field(GraphQlConstants.UuidFieldName) .Type>(); descriptor - .Field("databaseId") + .Field(DatabaseIdFieldName) .Type>() - .Resolve(_ => _appSettings.DatabaseId); + .Resolve(_ => appSettings.DatabaseId); descriptor - .Field("timestamp") + .Field(TimestampFieldName) .Type>(); descriptor - .Field("resourceTree") + .Field(ResourceTreeFieldName) .Type>>(); descriptor .Field(x => x.Locale) diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs new file mode 100644 index 00000000..86008403 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -0,0 +1,32 @@ +using HotChocolate.Types; +using GreenDonut; +using System; +using Database.Data; + +namespace Database.GraphQl.DataX; + +public abstract class DataTypeBase + : EntityType + where TData : IData + where TDataByIdDataLoader : IDataLoader +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(x => x.Locale) + .Type>(); + descriptor + .Field(x => x.Resources) + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + descriptor + .Field(DataType.ResourceTreeFieldName) + .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); + descriptor + .Field(DataType.TimestampFieldName) + .Type>() + .ResolveWith(t => t.GetTimestamp()); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 7ea8751e..13b1e6fd 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -6,18 +6,16 @@ namespace Database.GraphQl.DataX; public sealed class GetHttpsResourceTree( - Data.DataX data - ) + Data.IData data +) { - private readonly Data.DataX _data = data; - public async Task GetRoot( GetHttpsResourceTreeRootByDataIdDataLoader byId, CancellationToken cancellationToken ) { return new GetHttpsResourceTreeRoot( - (await byId.LoadAsync(_data.Id, cancellationToken) ?? []).Single() + (await byId.LoadAsync(data.Id, cancellationToken) ?? []).Single() ); } @@ -26,7 +24,7 @@ public async Task> GetNonRootVe CancellationToken cancellationToken ) { - return (await byId.LoadAsync(_data.Id, cancellationToken) ?? []) + return (await byId.LoadAsync(data.Id, cancellationToken) ?? []) .Select(v => new GetHttpsResourceTreeNonRootVertex(v) ) diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index 13a5eb89..dceacdae 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -24,7 +24,7 @@ IObjectTypeDescriptor descriptor ! // Notice the null-forgiving operator `!`. It's bad that we need to use it here. ); descriptor - .Field("uuid") + .Field(GraphQlConstants.UuidFieldName) .Type>() .Resolve(context => context.Parent().Id @@ -33,7 +33,6 @@ IObjectTypeDescriptor descriptor descriptor .Field(t => t.Version) .Type>() - .Name("version") .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs deleted file mode 100644 index fab9b797..00000000 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataResolvers.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.GeometricDataX; - -public sealed class GeometricDataResolvers -{ - public async Task GetGetHttpsResources( - [Parent] GeometricData geometricData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return await byId.LoadAsync(geometricData.Id, cancellationToken) ?? []; - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] GeometricData geometricData - ) - { - return new GetHttpsResourceTree(geometricData); - } - - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs index 22ade938..3329969e 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs @@ -1,28 +1,16 @@ using Database.Data; using HotChocolate.Types; +using Database.GraphQl.DataX; namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataType - : EntityType + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs new file mode 100644 index 00000000..9ea0cd34 --- /dev/null +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl; + +public static class GraphQlConstants +{ + public const string TypeDiscriminatorPropertyName = "__typename"; + public const string UuidFieldName = "uuid"; + public const string FilterInputSuffix = "FilterInput"; +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs deleted file mode 100644 index 36c7bdbb..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataResolvers.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.HygrothermalDataX; - -public sealed class HygrothermalDataResolvers -{ - public async Task GetGetHttpsResources( - [Parent] HygrothermalData hygrothermalData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return await byId.LoadAsync(hygrothermalData.Id, cancellationToken) ?? []; - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] HygrothermalData hygrothermalData - ) - { - return new GetHttpsResourceTree(hygrothermalData); - } - - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs index 0fb0a10c..b6b3375a 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs @@ -1,28 +1,16 @@ using Database.Data; using HotChocolate.Types; +using Database.GraphQl.DataX; namespace Database.GraphQl.HygrothermalDataX; public sealed class HygrothermalDataType - : EntityType + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs index 3fff01b5..c2f6d962 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs @@ -1,28 +1,16 @@ -using Database.Data; using HotChocolate.Types; +using Database.Data; +using Database.GraphQl.DataX; namespace Database.GraphQl.OpticalDataX; public sealed class OpticalDataType - : EntityType + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs deleted file mode 100644 index 07b5a4ce..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataResolvers.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.DataX; -using HotChocolate; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public sealed class PhotovoltaicDataResolvers -{ - public async Task GetGetHttpsResources( - [Parent] PhotovoltaicData photovoltaicData, - GetHttpsResourcesByDataIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return await byId.LoadAsync(photovoltaicData.Id, cancellationToken) ?? []; - } - - public GetHttpsResourceTree GetGetHttpsResourceTree( - [Parent] PhotovoltaicData photovoltaicData - ) - { - return new GetHttpsResourceTree(photovoltaicData); - } - - public DateTime GetTimestamp() - { - return DateTime.UtcNow; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs index f29a8177..40676b56 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs @@ -1,28 +1,16 @@ using Database.Data; using HotChocolate.Types; +using Database.GraphQl.DataX; namespace Database.GraphQl.PhotovoltaicDataX; public sealed class PhotovoltaicDataType - : EntityType + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(x => x.Locale) - .Type>(); - descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); - descriptor - .Field("resourceTree") - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field("timestamp") - .Type>() - .ResolveWith(t => t.GetTimestamp()); } } \ No newline at end of file From f507b67b0eda27de637499b6d976118ec9ac07ef Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 11:47:02 +0200 Subject: [PATCH 518/878] Make GET HTTPS resources filter and sortable --- .../src/Configuration/GraphQlConfiguration.cs | 1 + backend/src/GraphQl/Connection.cs | 26 +++++-- backend/src/GraphQl/DataX/DataResolvers.cs | 12 +++- backend/src/GraphQl/DataX/DataTypeBase.cs | 2 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 16 ++++- ...ttpsResourceTreeNonRootVertexFilterType.cs | 17 +++++ ...tHttpsResourceTreeNonRootVertexSortType.cs | 17 +++++ .../Extensions/ResolverContextExtensions.cs | 23 ++++++ backend/src/GraphQl/ForkingConnection.cs | 41 ++++++++--- .../GetHttpsResourceFilterType.cs | 5 +- .../GetHttpsResourceSortType.cs | 3 +- backend/src/GraphQl/GraphQlConstants.cs | 3 +- .../GraphQlSchemaTests.IsUnchanged.snap | 71 ++++++++++--------- frontend/type-defs.graphqls | 71 ++++++++++--------- 14 files changed, 216 insertions(+), 92 deletions(-) create mode 100644 backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs create mode 100644 backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs create mode 100644 backend/src/GraphQl/Extensions/ResolverContextExtensions.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 9abb7921..6cf2fdf1 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -160,6 +160,7 @@ IWebHostEnvironment environment /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ /* .AddType() */ // Object Types + .AddType() .AddType() .AddType() .AddType() diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index a1f2ade0..f48817de 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -1,32 +1,44 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Database.Data; using GreenDonut; +using GreenDonut.Data; +using Database.Data; namespace Database.GraphQl; public abstract class Connection( TSubject subject, - Func createEdge + Func createEdge, + QueryContext queryContext ) where TSubject : IEntity where TAssociationsByAssociateIdDataLoader : IDataLoader { private readonly Func _createEdge = createEdge; + private readonly QueryContext _queryContext = queryContext; protected TSubject Subject { get; } = subject; - public async Task> GetEdgesAsync( + public async Task GetTotalCountAsync( TAssociationsByAssociateIdDataLoader dataLoader, CancellationToken cancellationToken ) { - return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) ?? [] - ) - .Select(_createEdge); + return (uint)(await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)).Length; + } + + public async IAsyncEnumerable GetEdgesAsync( + TAssociationsByAssociateIdDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + foreach (var association in await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)) + { + yield return _createEdge(association); + } } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index cd987bda..e89fe29e 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -3,18 +3,28 @@ using System.Threading.Tasks; using HotChocolate; using Database.Data; +using GreenDonut.Data; +using HotChocolate.Data; +using GreenDonut; +using Database.GraphQl.GetHttpsResources; namespace Database.GraphQl.DataX; public sealed class DataResolvers { + [UseFiltering] + [UseSorting] public async Task GetGetHttpsResources( [Parent] IData data, + QueryContext queryContext, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return await byId.LoadAsync(data.Id, cancellationToken) ?? []; + return await + byId + .With(queryContext) + .LoadRequiredAsync(data.Id, cancellationToken) ?? []; } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index 86008403..dff31d87 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -20,7 +20,7 @@ IObjectTypeDescriptor descriptor .Type>(); descriptor .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!)); + .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!, default!)); descriptor .Field(DataType.ResourceTreeFieldName) .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 13b1e6fd..ef9aca1d 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -2,6 +2,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using HotChocolate.Data; namespace Database.GraphQl.DataX; @@ -15,16 +19,24 @@ CancellationToken cancellationToken ) { return new GetHttpsResourceTreeRoot( - (await byId.LoadAsync(data.Id, cancellationToken) ?? []).Single() + (await byId.LoadRequiredAsync(data.Id, cancellationToken)) + .Single() ); } + [UseFiltering] + [UseSorting] public async Task> GetNonRootVertices( + QueryContext queryContext, GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - return (await byId.LoadAsync(data.Id, cancellationToken) ?? []) + return ( + await byId + .With(queryContext) + .LoadRequiredAsync(data.Id, cancellationToken) + ) .Select(v => new GetHttpsResourceTreeNonRootVertex(v) ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs new file mode 100644 index 00000000..12d268c3 --- /dev/null +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -0,0 +1,17 @@ +using HotChocolate.Data.Filters; +using Database.Data; +using Database.GraphQl.GetHttpsResources; + +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeNonRootVertexFilterType + : GetHttpsResourceFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs new file mode 100644 index 00000000..1b775d90 --- /dev/null +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs @@ -0,0 +1,17 @@ +using HotChocolate.Data.Sorting; +using Database.Data; +using Database.GraphQl.GetHttpsResources; + +namespace Database.GraphQl.DataX; + +public sealed class GetHttpsResourceTreeNonRootVertexSortType + : GetHttpsResourceSortType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexSortType)[..^10] + GraphQlConstants.SortInputSuffix); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs new file mode 100644 index 00000000..aecc462e --- /dev/null +++ b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs @@ -0,0 +1,23 @@ +using GreenDonut.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; + +namespace Database.GraphQl.Extensions; + +public static class ResolverContextExtensions +{ + // Inspired by https://github.com/ChilliCream/graphql-platform/blob/9ae7220205412203d0a941a6b0cc779e70b02b09/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs#L76-L86 + public static QueryContext GetQueryContext(this IResolverContext context) + { + var selection = context.Selection; + var filterContext = context.GetFilterContext(); + var sortContext = context.GetSortingContext(); + + // TODO Make selection work + return new QueryContext( + null, // selection.AsSelector(), + filterContext?.AsPredicate(), + sortContext?.AsSortDefinition()); + } +} diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index af67bd98..ec8bd7cf 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Database.Data; using GreenDonut; +using GreenDonut.Data; +using Database.Data; namespace Database.GraphQl; @@ -12,18 +13,31 @@ public abstract class ForkingConnection( TSubject subject, bool useFirstDataLoader, - Func createEdge + Func createEdge, + QueryContext? queryContext ) where TSubject : IEntity where TSomeAssociationsByAssociateIdDataLoader : IDataLoader where TOtherAssociationsByAssociateIdDataLoader : IDataLoader { private readonly Func _createEdge = createEdge; + private readonly QueryContext? _queryContext = queryContext; private readonly bool _useFirstDataLoader = useFirstDataLoader; protected TSubject Subject { get; } = subject; - public Task> GetEdgesAsync( + public Task GetTotalCountAsync( + TSomeAssociationsByAssociateIdDataLoader someDataLoader, + TOtherAssociationsByAssociateIdDataLoader otherDataLoader, + CancellationToken cancellationToken + ) + { + return _useFirstDataLoader + ? GetTotalCountAsync(someDataLoader, cancellationToken) + : GetTotalCountAsync(otherDataLoader, cancellationToken); + } + + public IAsyncEnumerable GetEdgesAsync( TSomeAssociationsByAssociateIdDataLoader someDataLoader, TOtherAssociationsByAssociateIdDataLoader otherDataLoader, CancellationToken cancellationToken @@ -34,15 +48,24 @@ CancellationToken cancellationToken : GetEdgesAsync(otherDataLoader, cancellationToken); } - private async Task> GetEdgesAsync( + private async Task GetTotalCountAsync( TDataLoader dataLoader, CancellationToken cancellationToken ) where TDataLoader : IDataLoader { - return ( - await dataLoader.LoadAsync(Subject.Id, cancellationToken) ?? [] - ) - .Select(_createEdge); + return (uint)(await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)).Length; + } + + private async IAsyncEnumerable GetEdgesAsync( + TDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + where TDataLoader : IDataLoader + { + foreach (var association in await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)) + { + yield return _createEdge(association); + } } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index e5885df4..830cac46 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,9 +1,9 @@ -using Database.Data; using HotChocolate.Data.Filters; +using Database.Data; namespace Database.GraphQl.GetHttpsResources; -public sealed class GetHttpsResourceFilterType +public class GetHttpsResourceFilterType : FilterInputType { protected override void Configure( @@ -17,7 +17,6 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.DataFormatId); descriptor.Field(x => x.AppliedConversionMethod); descriptor.Field(x => x.ArchivedFilesMetaInformation); - descriptor.Field(x => x.Data); descriptor.Field(x => x.Parent); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs index b5b1ccb4..5141ab43 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -3,7 +3,7 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class GetHttpsResourceSortType +public class GetHttpsResourceSortType : SortInputType { protected override void Configure( @@ -16,7 +16,6 @@ ISortInputTypeDescriptor descriptor descriptor.Field(x => x.HashValue); descriptor.Field(x => x.DataFormatId); descriptor.Field(x => x.AppliedConversionMethod); - descriptor.Field(x => x.Data); descriptor.Field(x => x.Parent); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs index 9ea0cd34..c60b3ebd 100644 --- a/backend/src/GraphQl/GraphQlConstants.cs +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -3,6 +3,7 @@ namespace Database.GraphQl; public static class GraphQlConstants { public const string TypeDiscriminatorPropertyName = "__typename"; - public const string UuidFieldName = "uuid"; public const string FilterInputSuffix = "FilterInput"; + public const string SortInputSuffix = "SortInput"; + public const string UuidFieldName = "uuid"; } \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 16a09a34..51645dcc 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -87,7 +87,7 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -234,6 +234,17 @@ type DataApproval { variables: Any! } +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -271,7 +282,7 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! @@ -332,7 +343,7 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices: [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -364,7 +375,7 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -481,7 +492,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! @@ -537,7 +548,7 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -940,21 +951,6 @@ input DataKindPropositionInput { notIn: [DataKind!] @cost(weight: "10") } -input DataPropositionInput { - and: [DataPropositionInput!] - or: [DataPropositionInput!] - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput - warnings: StringsPropositionInput -} - input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") notEqualTo: DateTime @cost(weight: "10") @@ -1041,7 +1037,6 @@ input GetHttpsResourcePropositionInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - data: DataPropositionInput parent: GetHttpsResourcePropositionInput } @@ -1051,10 +1046,30 @@ input GetHttpsResourceSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - data: IDataSortInput @cost(weight: "10") parent: GetHttpsResourceSortInput @cost(weight: "10") } +input GetHttpsResourceTreeNonRootVertSortInput { + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + parent: GetHttpsResourceSortInput @cost(weight: "10") +} + +input GetHttpsResourceTreeNonRootVertexFilterInput { + and: [GetHttpsResourceTreeNonRootVertexFilterInput!] + or: [GetHttpsResourceTreeNonRootVertexFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourcePropositionInput +} + input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] @@ -1082,16 +1097,6 @@ input HygrothermalDataSortInput { id: SortEnumType @cost(weight: "10") } -input IDataSortInput { - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - input InstitutionAccessRightsInput { allowedDatasetsPerTimeSpan: NonNegativeInt allowedUserCount: NonNegativeInt diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 16a09a34..51645dcc 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -87,7 +87,7 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -234,6 +234,17 @@ type DataApproval { variables: Any! } +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + type Database { canCurrentUserUpdateNode: Boolean! canCurrentUserVerifyNode: Boolean! @@ -271,7 +282,7 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! @@ -332,7 +343,7 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices: [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -364,7 +375,7 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -481,7 +492,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! @@ -537,7 +548,7 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources: [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid! @@ -940,21 +951,6 @@ input DataKindPropositionInput { notIn: [DataKind!] @cost(weight: "10") } -input DataPropositionInput { - and: [DataPropositionInput!] - or: [DataPropositionInput!] - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput - warnings: StringsPropositionInput -} - input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") notEqualTo: DateTime @cost(weight: "10") @@ -1041,7 +1037,6 @@ input GetHttpsResourcePropositionInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - data: DataPropositionInput parent: GetHttpsResourcePropositionInput } @@ -1051,10 +1046,30 @@ input GetHttpsResourceSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - data: IDataSortInput @cost(weight: "10") parent: GetHttpsResourceSortInput @cost(weight: "10") } +input GetHttpsResourceTreeNonRootVertSortInput { + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + parent: GetHttpsResourceSortInput @cost(weight: "10") +} + +input GetHttpsResourceTreeNonRootVertexFilterInput { + and: [GetHttpsResourceTreeNonRootVertexFilterInput!] + or: [GetHttpsResourceTreeNonRootVertexFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourcePropositionInput +} + input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] @@ -1082,16 +1097,6 @@ input HygrothermalDataSortInput { id: SortEnumType @cost(weight: "10") } -input IDataSortInput { - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - input InstitutionAccessRightsInput { allowedDatasetsPerTimeSpan: NonNegativeInt allowedUserCount: NonNegativeInt From 35d52f30dde9e34fba17b1ccaa13327e6ab82a16 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 12:18:14 +0200 Subject: [PATCH 519/878] Add filter to hash-value recomputation mutation --- backend/src/GraphQl/DataX/DataFilterType.cs | 1 + backend/src/GraphQl/DataX/DataResolvers.cs | 2 +- .../Extensions/SortingContextExtensions.cs | 6 ++++ .../GetHttpsResourceMutations.cs | 11 ++++++- .../GetHttpsResourceResolvers.cs | 17 ++++------- ...uteGetHttpsResourceHashValuesFilterType.cs | 17 +++++++++++ .../OpticalDataX/OpticalDataQueries.cs | 8 ++--- .../GraphQlSchemaTests.IsUnchanged.snap | 30 ++++++++++++++++++- frontend/type-defs.graphqls | 30 ++++++++++++++++++- 9 files changed, 103 insertions(+), 19 deletions(-) create mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index 4705f142..741845e6 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -11,5 +11,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(IData)[1..] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index e89fe29e..77fc9132 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -24,7 +24,7 @@ CancellationToken cancellationToken return await byId .With(queryContext) - .LoadRequiredAsync(data.Id, cancellationToken) ?? []; + .LoadRequiredAsync(data.Id, cancellationToken); } public GetHttpsResourceTree GetGetHttpsResourceTree( diff --git a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs index faf3c950..2c12031c 100644 --- a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs +++ b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs @@ -1,6 +1,7 @@ using System.Linq; using HotChocolate.Data.Sorting; using Database.Data; +using GreenDonut.Data; namespace Database.GraphQl.Extensions; @@ -21,4 +22,9 @@ public static void StabilizeOrder(this ISortingContext sorting) where T : IEn return query.OrderBy(_ => _.Id); }); } + + public static SortDefinition StabilizeOrder(this SortDefinition sort) where T : IEntity + { + return sort.AddAscending(_ => _.Id); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index db5a9c96..9a869a7f 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -12,6 +12,10 @@ using System.Collections.Concurrent; using Microsoft.Extensions.Logging; using Database.Utilities; +using HotChocolate.Data; +using GreenDonut.Data; +using Microsoft.EntityFrameworkCore.Query; +using Database.GraphQl.Extensions; namespace Database.GraphQl.GetHttpsResources; @@ -111,8 +115,10 @@ input.AppliedConversionMethod is null return new CreateGetHttpsResourcePayload(getHttpsResource); } + [UseFiltering] public async Task RecomputeGetHttpsResourceHashValuesAsync( ApplicationDbContext context, + QueryContext queryContext, UserService userService, ILogger logger, CancellationToken cancellationToken @@ -139,7 +145,10 @@ CancellationToken cancellationToken ) ); } - var resources = await context.GetHttpsResources.ToListAsync(cancellationToken); + var resources = + await context.GetHttpsResources + .With(queryContext, sort => sort.StabilizeOrder()) + .ToListAsync(cancellationToken); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( resources, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 801f8a1c..bea56652 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,13 +1,13 @@ using System; using System.Threading; using System.Threading.Tasks; +using HotChocolate; using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.GeometricDataX; using Database.GraphQl.PhotovoltaicDataX; -using HotChocolate; namespace Database.GraphQl.GetHttpsResources; @@ -26,43 +26,38 @@ CancellationToken cancellationToken if (getHttpsResource.CalorimetricDataId is not null) { return await calorimetricDataById.LoadAsync( - getHttpsResource.CalorimetricDataId ?? throw new ArgumentException("Impossible!"), + getHttpsResource.CalorimetricDataId ?? Guid.Empty, cancellationToken ); } - if (getHttpsResource.HygrothermalDataId is not null) { return await hygrothermalDataById.LoadAsync( - getHttpsResource.HygrothermalDataId ?? throw new ArgumentException("Impossible!"), + getHttpsResource.HygrothermalDataId ?? Guid.Empty, cancellationToken ); } - if (getHttpsResource.OpticalDataId is not null) { return await opticalDataById.LoadAsync( - getHttpsResource.OpticalDataId ?? throw new ArgumentException("Impossible!"), + getHttpsResource.OpticalDataId ?? Guid.Empty, cancellationToken ); } - if (getHttpsResource.PhotovoltaicDataId is not null) { return await photovoltaicDataById.LoadAsync( - getHttpsResource.PhotovoltaicDataId ?? throw new ArgumentException("Impossible!"), + getHttpsResource.PhotovoltaicDataId ?? Guid.Empty, cancellationToken ); } - if (getHttpsResource.GeometricDataId is not null) { return await geometricDataById.LoadAsync( - getHttpsResource.GeometricDataId ?? throw new ArgumentException("Impossible!"), + getHttpsResource.GeometricDataId ?? Guid.Empty, cancellationToken ); } - return null; } diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs new file mode 100644 index 00000000..b871aa08 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs @@ -0,0 +1,17 @@ +using HotChocolate.Data.Filters; +using Database.Data; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class RecomputeGetHttpsResourceHashValuesFilterType + : GetHttpsResourceFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(RecomputeGetHttpsResourceHashValuesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(x => x.Data); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index c5b71ca6..85c6105c 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -2,15 +2,15 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; -using Database.GraphQl.Extensions; -using Database.Services; +using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 51645dcc..ddcd10cf 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -439,7 +439,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues: RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") @@ -944,6 +944,21 @@ input DataApprovalPropositionInput { statement: ReferencePropositionInput } +input DataFilterInput { + and: [DataFilterInput!] + or: [DataFilterInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput +} + input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") notEqualTo: DataKind @cost(weight: "10") @@ -1360,6 +1375,19 @@ input PublicationPropositionInput { section: StringPropositionInput } +input RecomputeGetHttpsResourceHashValuesFilterInput { + and: [RecomputeGetHttpsResourceHashValuesFilterInput!] + or: [RecomputeGetHttpsResourceHashValuesFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourcePropositionInput + data: DataFilterInput +} + input ReferenceInput { publication: PublicationInput standard: StandardInput diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 51645dcc..ddcd10cf 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -439,7 +439,7 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues: RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") @@ -944,6 +944,21 @@ input DataApprovalPropositionInput { statement: ReferencePropositionInput } +input DataFilterInput { + and: [DataFilterInput!] + or: [DataFilterInput!] + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListGetHttpsResourceFilterTypePropositionInput + warnings: StringsPropositionInput +} + input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") notEqualTo: DataKind @cost(weight: "10") @@ -1360,6 +1375,19 @@ input PublicationPropositionInput { section: StringPropositionInput } +input RecomputeGetHttpsResourceHashValuesFilterInput { + and: [RecomputeGetHttpsResourceHashValuesFilterInput!] + or: [RecomputeGetHttpsResourceHashValuesFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourcePropositionInput + data: DataFilterInput +} + input ReferenceInput { publication: PublicationInput standard: StandardInput From e4cb7da15d73d231dd5497d02eeaca4637aed9d9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 13:03:54 +0200 Subject: [PATCH 520/878] Ask for data kind alongside the data ID if it is not known from the context --- backend/src/Data/ApplicationDbContext.cs | 34 +++++++++++++------ .../AccessRights/DataAccessRightsInput.cs | 2 ++ .../AccessRights/DataAccessRightsMutations.cs | 2 +- .../DataApprovals/DataApprovalInput.cs | 2 ++ .../DataApprovals/DataApprovalMutations.cs | 5 ++- backend/src/GraphQl/DataX/DataQueries.cs | 4 ++- .../src/Services/ResponseApprovalService.cs | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 4 ++- frontend/type-defs.graphqls | 4 ++- 9 files changed, 40 insertions(+), 19 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 9b16218c..311b8545 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Enumerations; @@ -40,22 +41,33 @@ DbContextOptions options // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset public DbSet GetHttpsResources { get; private set; } = default!; - public DbSet CalorimetricData { get; private set; } = default!; - public DbSet HygrothermalData { get; private set; } = default!; - public DbSet OpticalData { get; private set; } = default!; - public DbSet PhotovoltaicData { get; private set; } = default!; public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; - public DbSet GeometricData { get; private set; } = default!; public DbSet InstitutionAccessRights { get; private set; } = default!; - public async Task GetDataAsync(Guid id, CancellationToken cancellationToken) + + public DbSet CalorimetricData { get; private set; } = default!; + public DbSet GeometricData { get; private set; } = default!; + public DbSet HygrothermalData { get; private set; } = default!; + public DbSet PhotovoltaicData { get; private set; } = default!; + public DbSet OpticalData { get; private set; } = default!; + + public IQueryable Data(DataKind dataKind) + { + return dataKind switch + { + DataKind.CALORIMETRIC_DATA => CalorimetricData, + DataKind.GEOMETRIC_DATA => GeometricData, + DataKind.HYGROTHERMAL_DATA => HygrothermalData, + DataKind.OPTICAL_DATA => OpticalData, + DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + _ => throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}."), + }; + } + + public Task GetDataAsync(Guid id, DataKind dataKind, CancellationToken cancellationToken) { - return await CalorimetricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await HygrothermalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await OpticalData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await GeometricData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) ?? - await PhotovoltaicData.SingleOrDefaultAsync(x => x.Id == id, cancellationToken) as IData; + return Data(dataKind).SingleOrDefaultAsync(x => x.Id == id, cancellationToken); } private static void CreateEnumerations(ModelBuilder builder, string schemaName) diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs index 6d49cb74..ae3ec80c 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using Database.Enumerations; namespace Database.GraphQl.AccessRights; public sealed record DataAccessRightsInput ( Guid DataId, + DataKind DataKind, IReadOnlyDictionary? AllowedUserAndQuantity, IReadOnlyList? AllowedInstitutions, IReadOnlyList? AllowedApplications diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index abddbd7f..94b6454e 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken ); } - var data = await context.GetDataAsync(input.DataId, cancellationToken); + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); if (data is null) { return new UpdateDataAccessRightsPayload( diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs index ad2dff7f..a1cb47f5 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs @@ -1,5 +1,6 @@ using System; using System.Text.Json; +using Database.Enumerations; using Database.GraphQl.References; namespace Database.GraphQl.DataApprovals; @@ -7,6 +8,7 @@ namespace Database.GraphQl.DataApprovals; public sealed record DataApprovalInput ( Guid DataId, + DataKind DataKind, DateTime Timestamp, string Signature, string KeyFingerprint, diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 35568cd6..6f3ac656 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -15,7 +15,6 @@ using GraphQL; using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; -using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.Extensions.Logging; @@ -246,7 +245,7 @@ CancellationToken cancellationToken return new AddDataApprovalPayload(errors); } - var data = await context.GetDataAsync(input.DataId, cancellationToken); + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); if (data is null) { return new AddDataApprovalPayload( @@ -324,7 +323,7 @@ CancellationToken cancellationToken ); } - var data = await context.GetDataAsync(input.DataId, cancellationToken); + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); if (data is null) { return new RemoveDataApprovalPayload( diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index f0dd48c1..6885c5fe 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; +using Database.Enumerations; using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -13,13 +14,14 @@ public sealed class DataQueries { public async Task GetDataAsync( Guid id, + DataKind dataKind, [GraphQLType] string? locale, ApplicationDbContext dbContext, AccessRightsService accessRightsService, CancellationToken cancellationToken ) { - var data = await dbContext.GetDataAsync(id, cancellationToken); + var data = await dbContext.GetDataAsync(id, dataKind, cancellationToken); if (data is null) { return null; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 5cd79c23..8cd08eed 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -63,7 +63,7 @@ public async Task CreateResponseApproval(T dataObject, Canc HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, cancellationToken), PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, cancellationToken), OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, cancellationToken), - _ => throw new NotImplementedException($"Unsupported data type {typeof(T)}"), + _ => throw new ArgumentOutOfRangeException($"Unsupported data type {typeof(T)}"), }; } } \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index ddcd10cf..5ea9a921 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -601,7 +601,7 @@ type Query { calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") - data(id: Uuid! locale: Locale): Data @cost(weight: "10") + data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -917,11 +917,13 @@ input DataAccessRightsInput { allowedInstitutions: [Uuid!] allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] dataId: Uuid! + dataKind: DataKind! } input DataApprovalInput { approverId: Uuid! dataId: Uuid! + dataKind: DataKind! keyFingerprint: String! message: String! query: String! diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index ddcd10cf..5ea9a921 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -601,7 +601,7 @@ type Query { calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") - data(id: Uuid! locale: Locale): Data @cost(weight: "10") + data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -917,11 +917,13 @@ input DataAccessRightsInput { allowedInstitutions: [Uuid!] allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] dataId: Uuid! + dataKind: DataKind! } input DataApprovalInput { approverId: Uuid! dataId: Uuid! + dataKind: DataKind! keyFingerprint: String! message: String! query: String! From cf908bfe03aa39f01822b5685b935180d67b6123 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 25 Oct 2025 17:11:32 +0200 Subject: [PATCH 521/878] DRY-up optical data creation --- backend/src/Data/CalorimetricData.cs | 29 +- backend/src/Data/DataFactory.cs | 107 +++++ backend/src/Data/DataX.cs | 19 + backend/src/Data/GeometricData.cs | 28 +- backend/src/Data/HygrothermalData.cs | 27 +- backend/src/Data/ICreateData.cs | 18 + backend/src/Data/IData.cs | 10 + backend/src/Data/OpticalData.cs | 37 +- backend/src/Data/PhotovoltaicData.cs | 27 +- .../InstitutionAccessRightsError.cs | 14 +- .../UpdateDataAccessRightsError.cs | 14 +- backend/src/GraphQl/AppliedMethodInput.cs | 18 +- .../CreateCalorimetricDataError.cs | 14 +- backend/src/GraphQl/CielabColorInput.cs | 15 +- .../CrossDatabaseDataReferenceInput.cs | 14 +- .../DataApprovals/AddDataApprovalError.cs | 14 +- .../DataApprovals/DataApprovalMutations.cs | 2 +- .../DataApprovals/RemoveDataApprovalError.cs | 14 +- backend/src/GraphQl/DataX/DataMutations.cs | 372 ++++++++++++++++++ .../src/GraphQl/DataX/DataMutationsBase.cs | 76 ++++ .../src/GraphQl/FileMetaInformationInput.cs | 12 +- .../CreateGeometricDataError.cs | 14 +- .../CreateGetHttpsResourceError.cs | 14 +- ...ecomputeGetHttpsResourceHashValuesError.cs | 14 +- .../CreateHygrothermalDataError.cs | 14 +- .../GraphQl/Methods/CalculateMethodError.cs | 14 +- backend/src/GraphQl/MutationsBase.cs | 46 +++ .../src/GraphQl/NamedMethodArgumentInput.cs | 12 +- backend/src/GraphQl/NamedMethodSourceInput.cs | 13 +- .../OpticalDataX/CreateOpticalDataError.cs | 14 +- .../OpticalDataX/CreateOpticalDataPayload.cs | 31 +- .../OpticalDataX/OpticalDataMutations.cs | 180 +++------ .../OpticalDataX/OpticalDataPayload.cs | 53 +-- .../CreatePhotovoltaicDataError.cs | 14 +- .../GraphQl/Publications/PublicationInput.cs | 18 +- .../GraphQl/Publications/PublicationType.cs | 14 - .../src/GraphQl/References/ReferenceInput.cs | 26 +- .../src/GraphQl/References/ReferenceType.cs | 21 - .../CreateResponseApprovalsError.cs | 14 +- .../UpdateResponseApprovalsError.cs | 14 +- .../src/GraphQl/RootGetHttpsResourceInput.cs | 18 +- .../src/GraphQl/Standards/StandardInput.cs | 23 +- backend/src/GraphQl/Standards/StandardType.cs | 19 - ...oTreeVertexAppliedConversionMethodInput.cs | 14 +- backend/src/GraphQl/UserErrorBase.cs | 21 +- .../GraphQlSchemaTests.IsUnchanged.snap | 54 +++ frontend/type-defs.graphqls | 54 +++ 47 files changed, 1235 insertions(+), 389 deletions(-) create mode 100644 backend/src/Data/DataFactory.cs create mode 100644 backend/src/Data/ICreateData.cs create mode 100644 backend/src/GraphQl/DataX/DataMutationsBase.cs create mode 100644 backend/src/GraphQl/MutationsBase.cs diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 266aee39..9b4b9163 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -5,8 +5,35 @@ namespace Database.Data; public sealed class CalorimetricData - : DataX + : DataX, ICreateData { + public static CalorimetricData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return new CalorimetricData( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + [], + [] + ); + } + public CalorimetricData( Guid userId, string locale, diff --git a/backend/src/Data/DataFactory.cs b/backend/src/Data/DataFactory.cs new file mode 100644 index 00000000..38dc640f --- /dev/null +++ b/backend/src/Data/DataFactory.cs @@ -0,0 +1,107 @@ +using System; +using Database.Enumerations; + +namespace Database.Data; + +public sealed class DataFactory +{ + public static T Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + where T : IData, ICreateData + { + return T.Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ); + } + + public static IData Create( + DataKind dataKind, + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return dataKind switch + { + DataKind.CALORIMETRIC_DATA => Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ), + DataKind.GEOMETRIC_DATA => Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ), + DataKind.HYGROTHERMAL_DATA => Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ), + DataKind.OPTICAL_DATA => Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ), + DataKind.PHOTOVOLTAIC_DATA => Create( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ), + _ => throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}.") + }; + } +} \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 3be12986..da240627 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -40,6 +40,25 @@ AppliedMethod appliedMethod AppliedMethod = appliedMethod; } + public void Update( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + DateTime createdAt, + Guid creatorId + ) + { + Locale = locale; + ComponentId = componentId; + Name = name; + Description = description; + Warnings = warnings; + CreatedAt = createdAt; + CreatorId = creatorId; + } + public Guid UserId { get; private set; } = userId; public string Locale { get; private set; } = locale; public Guid ComponentId { get; private set; } = componentId; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index bce9b60e..018ba591 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -5,8 +5,34 @@ namespace Database.Data; public sealed class GeometricData - : DataX + : DataX, ICreateData { + public static GeometricData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return new GeometricData( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod, + [] + ); + } + public GeometricData( Guid userId, string locale, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 64ee49a7..3da19fbb 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -5,8 +5,33 @@ namespace Database.Data; public sealed class HygrothermalData - : DataX + : DataX, ICreateData { + public static HygrothermalData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return new HygrothermalData( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ); + } + public HygrothermalData( Guid userId, string locale, diff --git a/backend/src/Data/ICreateData.cs b/backend/src/Data/ICreateData.cs new file mode 100644 index 00000000..4e36ab33 --- /dev/null +++ b/backend/src/Data/ICreateData.cs @@ -0,0 +1,18 @@ +using System; + +namespace Database.Data; + +public interface ICreateData where TData : IData +{ + static abstract TData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ); +} \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 6367de3c..ff091dd6 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -48,4 +48,14 @@ public interface IData : IEntity /// Count of already accessed datasets by user. /// True, if dataset is rescricted. Otherwise false. bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount); + + void Update( + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + DateTime createdAt, + Guid creatorId + ); } \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index cba18f28..bffed788 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -9,10 +9,45 @@ namespace Database.Data; public sealed class OpticalData - : DataX + : DataX, ICreateData { public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); + public static OpticalData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return new OpticalData( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + null, + null, + null, + appliedMethod, + [], + [], + [], + [], + [], + [], + [] + ); + } + public OpticalData( Guid userId, string locale, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 07cc5561..01642594 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -5,8 +5,33 @@ namespace Database.Data; public sealed class PhotovoltaicData - : DataX + : DataX, ICreateData { + public static PhotovoltaicData Create( + Guid userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTime createdAt, + AppliedMethod appliedMethod + ) + { + return new PhotovoltaicData( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ); + } + public PhotovoltaicData( Guid userId, string locale, diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs index e84eb035..70496d01 100644 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.AccessRights; -public sealed class InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record InstitutionAccessRightsError( + InstitutionAccessRightsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs index 9821ca3a..68dfbc77 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.AccessRights; -public sealed class UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/AppliedMethodInput.cs b/backend/src/GraphQl/AppliedMethodInput.cs index 26a6d294..54a548fe 100644 --- a/backend/src/GraphQl/AppliedMethodInput.cs +++ b/backend/src/GraphQl/AppliedMethodInput.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; namespace Database.GraphQl; @@ -7,4 +9,18 @@ public sealed record AppliedMethodInput( Guid MethodId, IReadOnlyList Arguments, IReadOnlyList Sources -); \ No newline at end of file +) +{ + public AppliedMethod ToDomainModel() + { + return new AppliedMethod( + MethodId, + Arguments + .Select(a => a.ToDomainModel()) + .ToList(), + Sources + .Select(s => s.ToDomainModel()) + .ToList() + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs index 2c6cc11f..a371a6c4 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.CalorimetricDataX; -public sealed class CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/CielabColorInput.cs b/backend/src/GraphQl/CielabColorInput.cs index 84da3718..2425cc83 100644 --- a/backend/src/GraphQl/CielabColorInput.cs +++ b/backend/src/GraphQl/CielabColorInput.cs @@ -1,3 +1,4 @@ +using Database.Data; using Database.Enumerations; namespace Database.GraphQl; @@ -8,4 +9,16 @@ public sealed record CielabColorInput( double BStar, CalorimetricObserver? Observer, Illuminant? Illuminant -); \ No newline at end of file +) +{ + public CielabColor ToDomainModel() + { + return new CielabColor( + LStar, + AStar, + BStar, + Observer, + Illuminant + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 1fb8451b..c02c45e7 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -1,4 +1,5 @@ using System; +using Database.Data; using Database.Enumerations; namespace Database.GraphQl; @@ -8,4 +9,15 @@ public sealed record CrossDatabaseDataReferenceInput( DateTime DataTimestamp, DataKind DataKind, Guid DatabaseId -); \ No newline at end of file +) +{ + public CrossDatabaseDataReference ToDomainModel() + { + return new CrossDatabaseDataReference( + DataId, + DataTimestamp, + DataKind, + DatabaseId + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs index 79379578..7ec893ba 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs @@ -3,11 +3,9 @@ namespace Database.GraphQl.DataApprovals; -public sealed class AddDataApprovalError( - AddDataApprovalErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record AddDataApprovalError( + AddDataApprovalErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 6f3ac656..7fb1a0c1 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -265,7 +265,7 @@ CancellationToken cancellationToken input.Variables, input.Message, input.ApproverId, - ReferenceType.FromInput(input.Statement) + input.Statement.ToDomainModel() ); data.Approvals.Add(approval); diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs index b80e8d11..802b026c 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs @@ -3,11 +3,9 @@ namespace Database.GraphQl.DataApprovals; -public sealed class RemoveDataApprovalError( - RemoveDataApprovalErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record RemoveDataApprovalError( + RemoveDataApprovalErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataMutations.cs b/backend/src/GraphQl/DataX/DataMutations.cs index 1d66bb62..39bd5b7a 100644 --- a/backend/src/GraphQl/DataX/DataMutations.cs +++ b/backend/src/GraphQl/DataX/DataMutations.cs @@ -1,10 +1,382 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Services; +using Database.Utilities; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate; using HotChocolate.Types; +using static Database.ApiRequests.QueryCurrentUser; namespace Database.GraphQl.DataX; [ExtendObjectType(nameof(Mutation))] public sealed class DataMutations +: DataMutationsBase { + // public sealed record CreateDataInput( + // [GraphQLType>] string Locale, + // DataKind DataKind, + // Guid ComponentId, + // string? Name, + // string? Description, + // string[] Warnings, + // DateTime CreatedAt, + // Guid CreatorId, + // AppliedMethodInput AppliedMethod, + // RootGetHttpsResourceInput RootResource + // ); + + // [SuppressMessage("Naming", "CA1707")] + // public enum CreateDataErrorCode + // { + // UNAUTHENTICATED, + // UNAUTHORIZED, + // UNKNOWN_DATA, + // CREATING_RESPONSE_APPROVAL_FAILED + // } + + // public sealed record CreateDataError( + // CreateDataErrorCode Code, + // string Message, + // IReadOnlyList Path + // ) : UserErrorBase(Code, Message, Path); + + // public sealed record CreateDataPayload( + // IData? Data, + // IReadOnlyCollection? Errors + // ); + + // public Task CreateDataAsync( + // CreateDataInput input, + // ApplicationDbContext context, + // UserService userService, + // ResponseApprovalService responseApprovalService, + // CancellationToken cancellationToken + // ) + // { + // static CreateDataPayload NewPayload( + // IData? data, + // IReadOnlyCollection? errors + // ) => new(data, errors); + // static CreateDataError NewError( + // CreateDataErrorCode code, + // string message, + // IReadOnlyList path + // ) => new(code, message, path); + // return Authorize( + // unauthenticatedErrorCode: CreateDataErrorCode.UNAUTHENTICATED, + // unauthorizedErrorCode: CreateDataErrorCode.UNAUTHORIZED, + // NewPayload, + // NewError, + // userService, + // then: async currentUser => + // { + // var data = DataFactory.Create( + // input.DataKind, + // currentUser.Uuid, + // input.Locale, + // input.ComponentId, + // input.Name, + // input.Description, + // input.Warnings, + // input.CreatorId, + // input.CreatedAt, + // new AppliedMethod( + // input.AppliedMethod.MethodId, + // input.AppliedMethod.Arguments + // .Select(a => new NamedMethodArgument( + // a.Name, + // a.Value + // )) + // .ToList(), + // input.AppliedMethod.Sources + // .Select(s => new NamedMethodSource( + // s.Name, + // new CrossDatabaseDataReference( + // s.Value.DataId, + // s.Value.DataTimestamp, + // s.Value.DataKind, + // s.Value.DatabaseId + // ) + // )) + // .ToList() + // ) + // ); + // var resource = new GetHttpsResource( + // input.RootResource.Description, + // Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + // input.RootResource.DataFormatId, + // null, + // input.RootResource.ArchivedFilesMetaInformation.Select(i => + // new FileMetaInformation( + // i.Path, + // i.DataFormatId + // ) + // ).ToList(), + // input.RootResource.AppliedConversionMethod is null + // ? null + // : new ToTreeVertexAppliedConversionMethod( + // input.RootResource.AppliedConversionMethod.MethodId, + // input.RootResource.AppliedConversionMethod.Arguments.Select(a => + // new NamedMethodArgument( + // a.Name, + // a.Value + // ) + // ).ToList(), + // input.RootResource.AppliedConversionMethod.SourceName + // ) + // ); + // data.Resources.Add(resource); + // switch (input.DataKind) + // { + // case DataKind.CALORIMETRIC_DATA: + // context.CalorimetricData.Add(data); + // break; + // case DataKind.GEOMETRIC_DATA: + // context.GeometricData.Add(data); + // break; + // case DataKind.HYGROTHERMAL_DATA: + // context.HygrothermalData.Add(data); + // break; + // case DataKind.OPTICAL_DATA: + // context.OpticalData.Add(data); + // break; + // case DataKind.PHOTOVOLTAIC_DATA: + // context.PhotovoltaicData.Add(data); + // break; + // default: + // throw new NotSupportedException($"Unsupported data kind {input.DataKind}."); + // } + // await context.SaveChangesAsync(cancellationToken); + // try + // { + // data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + // await context.SaveChangesAsync(cancellationToken); + // } + // catch (Exception exception) + // { + // context.Remove(data); + // await context.SaveChangesAsync(cancellationToken); + // return NewPayload( + // data, + // [NewError( + // CreateDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + // $"Signing failed with message: {exception.Message}", + // [] + // )] + // ); + // } + // return NewPayload(data, null); + // }, + // cancellationToken + // ); + // } + + public sealed record UpdateDataInput( + [GraphQLType>] string Locale, + Guid DataId, + DataKind DataKind, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId + ) : IChangeDataInput; + + [SuppressMessage("Naming", "CA1707")] + public enum UpdateDataErrorCode + { + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED + } + + public sealed record UpdateDataError( + UpdateDataErrorCode Code, + string Message, + IReadOnlyList Path + ) : UserErrorBase(Code, Message, Path); + + public sealed record UpdateDataPayload( + IData? data, + IReadOnlyCollection? Errors + ); + + public Task UpdateDataAsync( + UpdateDataInput input, + ApplicationDbContext context, + UserService userService, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + static UpdateDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + static UpdateDataError NewError( + UpdateDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + return AuthorizeAsync( + unauthenticatedErrorCode: UpdateDataErrorCode.UNAUTHENTICATED, + unauthorizedErrorCode: UpdateDataErrorCode.UNAUTHORIZED, + errors => NewPayload(null, errors), + NewError, + userService, + then: currentUser => FetchDataAsync( + input, + unknownDataErrorCode: UpdateDataErrorCode.UNKNOWN_DATA, + NewPayload, + NewError, + context, + then: async data => + { + var rememberedValues = new UpdateDataInput( + data.Locale, + input.DataId, + input.DataKind, + data.ComponentId, + data.Name, + data.Description, + data.Warnings, + data.CreatedAt, + data.CreatorId + ); + return await ActAndThenCreateResponseApprovalAsync( + UpdateDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + NewPayload, + NewError, + context, + responseApprovalService, + act: async () => + { + data.Update( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatedAt, + input.CreatorId + ); + await context.SaveChangesAsync(cancellationToken); + return data; + }, + undo: async data => + { + data.Update( + rememberedValues.Locale, + rememberedValues.ComponentId, + rememberedValues.Name, + rememberedValues.Description, + rememberedValues.Warnings, + rememberedValues.CreatedAt, + rememberedValues.CreatorId + ); + await context.SaveChangesAsync(cancellationToken); + }, + cancellationToken + ); + }, + cancellationToken + ), + cancellationToken + ); + } + + public sealed record DeleteDataInput( + Guid DataId, + DataKind DataKind + ) : IChangeDataInput; + + [SuppressMessage("Naming", "CA1707")] + public enum DeleteDataErrorCode + { + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA + } + + public sealed record DeleteDataError( + DeleteDataErrorCode Code, + string Message, + IReadOnlyList Path + ) : UserErrorBase(Code, Message, Path); + + public sealed record DeleteDataPayload( + IReadOnlyCollection? Errors + ); + + public Task DeleteDataAsync( + DeleteDataInput input, + ApplicationDbContext context, + UserService userService, + CancellationToken cancellationToken + ) + { + static DeleteDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(errors); + static DeleteDataError NewError( + DeleteDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + return AuthorizeAsync( + unauthenticatedErrorCode: DeleteDataErrorCode.UNAUTHENTICATED, + unauthorizedErrorCode: DeleteDataErrorCode.UNAUTHORIZED, + errors => NewPayload(null, errors), + NewError, + userService, + then: currentUser => FetchDataAsync( + input, + unknownDataErrorCode: DeleteDataErrorCode.UNKNOWN_DATA, + NewPayload, + NewError, + context, + then: async data => + { + switch (data) + { + case CalorimetricData calorimetricData: + context.CalorimetricData.Remove(calorimetricData); + break; + case GeometricData geometricData: + context.GeometricData.Remove(geometricData); + break; + case HygrothermalData hygrothermalData: + context.HygrothermalData.Remove(hygrothermalData); + break; + case PhotovoltaicData photovoltaicData: + context.PhotovoltaicData.Remove(photovoltaicData); + break; + case OpticalData opticalData: + context.OpticalData.Remove(opticalData); + break; + default: + throw new NotSupportedException($"Data kind '{input.DataKind}' is not supported for deletion."); + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data, null); + }, + cancellationToken + ), + cancellationToken + ); + } + // public async Task ExtractMirroredValuesFromFilesAsync( // ExtractIntegralValuesFromFilesInput input, // ApplicationDbContext context, diff --git a/backend/src/GraphQl/DataX/DataMutationsBase.cs b/backend/src/GraphQl/DataX/DataMutationsBase.cs new file mode 100644 index 00000000..a53b0776 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataMutationsBase.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Enumerations; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.GraphQl.DataX; + +public abstract class DataMutationsBase +: MutationsBase +{ + public async Task ActAndThenCreateResponseApprovalAsync( + TErrorCode creatingResponseApprovalFailedErrorCode, + Func?, TPayload> newPayload, + Func, TError> newError, + ApplicationDbContext context, + ResponseApprovalService responseApprovalService, + Func> act, + Func undo, + CancellationToken cancellationToken + ) where TData : IData + { + var data = await act(); + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + await undo(data); + return newPayload( + data, + [newError( + creatingResponseApprovalFailedErrorCode, + $"Signing failed with message: {exception.Message}", + [] + )] + ); + } + return newPayload(data, null); + } + + protected interface IChangeDataInput + { + public Guid DataId { get; } + public DataKind DataKind { get; } + } + + protected static async Task FetchDataAsync( + IChangeDataInput input, + TErrorCode unknownDataErrorCode, + Func?, TPayload> newPayload, + Func, TError> newError, + ApplicationDbContext context, + Func> then, + CancellationToken cancellationToken + ) + { + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); + if (data is null) + { + return newPayload(null, [ + newError( + unknownDataErrorCode, + $"Data with ID '{input.DataId}' and kind '{input.DataKind}' was not found.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ]); + } + return await then(data); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/FileMetaInformationInput.cs b/backend/src/GraphQl/FileMetaInformationInput.cs index 0479cf76..816cbfcf 100644 --- a/backend/src/GraphQl/FileMetaInformationInput.cs +++ b/backend/src/GraphQl/FileMetaInformationInput.cs @@ -1,8 +1,18 @@ using System; +using Database.Data; namespace Database.GraphQl; public sealed record FileMetaInformationInput( string[] Path, Guid DataFormatId -); \ No newline at end of file +) +{ + public FileMetaInformation ToDomainModel() + { + return new FileMetaInformation( + Path, + DataFormatId + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs index 0fd09e51..8abc5acf 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.GeometricDataX; -public sealed class CreateGeometricDataError( - CreateGeometricDataErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateGeometricDataError( + CreateGeometricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs index b729e582..242e3736 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs index 39d72b34..ec572cc0 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.GetHttpsResources; -public sealed class RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs index d55f0970..72446cf3 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.HygrothermalDataX; -public sealed class CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodError.cs b/backend/src/GraphQl/Methods/CalculateMethodError.cs index 5d3b286f..f2fa4a1a 100644 --- a/backend/src/GraphQl/Methods/CalculateMethodError.cs +++ b/backend/src/GraphQl/Methods/CalculateMethodError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.Methods; -public sealed class CalculateMethodError( - CalculateMethodErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CalculateMethodError( + CalculateMethodErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/MutationsBase.cs b/backend/src/GraphQl/MutationsBase.cs new file mode 100644 index 00000000..20a17ace --- /dev/null +++ b/backend/src/GraphQl/MutationsBase.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Services; +using static Database.ApiRequests.QueryCurrentUser; + +namespace Database.GraphQl; + +public abstract class MutationsBase +{ + protected static async Task AuthorizeAsync( + TErrorCode unauthenticatedErrorCode, + TErrorCode unauthorizedErrorCode, + Func?, TPayload> newPayload, + Func, TError> newError, + UserService userService, + Func> then, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return newPayload([ + newError( + unauthenticatedErrorCode, + "The user is not authenticated.", + [] + ) + ]); + } + if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return newPayload([ + newError( + unauthorizedErrorCode, + "The current user is not authorized to perform this mutation in this database.", + [] + ) + ]); + } + return await then(currentUser); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentInput.cs b/backend/src/GraphQl/NamedMethodArgumentInput.cs index 08a3030d..b22556db 100644 --- a/backend/src/GraphQl/NamedMethodArgumentInput.cs +++ b/backend/src/GraphQl/NamedMethodArgumentInput.cs @@ -1,8 +1,18 @@ using System.Text.Json; +using Database.Data; namespace Database.GraphQl; public sealed record NamedMethodArgumentInput( string Name, JsonElement Value -); \ No newline at end of file +) +{ + public NamedMethodArgument ToDomainModel() + { + return new NamedMethodArgument( + Name, + Value + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodSourceInput.cs b/backend/src/GraphQl/NamedMethodSourceInput.cs index 5016a2e2..8df5bc72 100644 --- a/backend/src/GraphQl/NamedMethodSourceInput.cs +++ b/backend/src/GraphQl/NamedMethodSourceInput.cs @@ -1,6 +1,17 @@ +using Database.Data; + namespace Database.GraphQl; public sealed record NamedMethodSourceInput( string Name, CrossDatabaseDataReferenceInput Value -); \ No newline at end of file +) +{ + public NamedMethodSource ToDomainModel() + { + return new NamedMethodSource( + Name, + Value.ToDomainModel() + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs index a2c3736c..0a8b0235 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.OpticalDataX; -public sealed class CreateOpticalDataError( - CreateOpticalDataErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateOpticalDataError( + CreateOpticalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs index e173c816..4098309b 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs @@ -1,29 +1,10 @@ +using System.Collections.Generic; using Database.Data; namespace Database.GraphQl.OpticalDataX; -public sealed class CreateOpticalDataPayload - : OpticalDataPayload -{ - public CreateOpticalDataPayload( - OpticalData opticalData - ) - : base(opticalData) - { - } - - public CreateOpticalDataPayload( - CreateOpticalDataError error - ) - : base(error) - { - } - - public CreateOpticalDataPayload( - OpticalData opticalData, - CreateOpticalDataError error - ) - : base(opticalData, error) - { - } -} \ No newline at end of file +public sealed record CreateOpticalDataPayload( + OpticalData? OpticalData, + IReadOnlyCollection? Errors +) +: OpticalDataPayload(OpticalData, Errors); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index fe3ed798..6585d96d 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -1,20 +1,20 @@ -using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using HotChocolate.Types; -using Database.Authorization; using Database.Data; using Database.Services; -using Database.Utilities; +using Database.GraphQl.DataX; +using System.Collections.Generic; namespace Database.GraphQl.OpticalDataX; [ExtendObjectType(nameof(Mutation))] public sealed class OpticalDataMutations +: DataMutationsBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public async Task CreateOpticalDataAsync( + public Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, UserService userService, @@ -22,123 +22,63 @@ public async Task CreateOpticalDataAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new CreateOpticalDataPayload( - new CreateOpticalDataError( - CreateOpticalDataErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new CreateOpticalDataPayload( - new CreateOpticalDataError( - CreateOpticalDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create optical data in this database.", - [] - ) - ); - } - - var opticalData = new OpticalData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, - new AppliedMethod( - input.AppliedMethod.MethodId, - input.AppliedMethod.Arguments - .Select(a => new NamedMethodArgument( - a.Name, - a.Value - )) - .ToList(), - input.AppliedMethod.Sources - .Select(s => new NamedMethodSource( - s.Name, - new CrossDatabaseDataReference( - s.Value.DataId, - s.Value.DataTimestamp, - s.Value.DataKind, - s.Value.DatabaseId - ) - )) - .ToList() + static CreateOpticalDataPayload NewPayload( + OpticalData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + static CreateOpticalDataError NewError( + CreateOpticalDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + return AuthorizeAsync( + unauthenticatedErrorCode: CreateOpticalDataErrorCode.UNAUTHENTICATED, + unauthorizedErrorCode: CreateOpticalDataErrorCode.UNAUTHORIZED, + errors => NewPayload(null, errors), + NewError, + userService, + then: currentUser => ActAndThenCreateResponseApprovalAsync( + creatingResponseApprovalFailedErrorCode: CreateOpticalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + NewPayload, + NewError, + context, + responseApprovalService, + act: async () => + { + var opticalData = new OpticalData( + currentUser.Uuid, + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatorId, + input.CreatedAt, + input.Type, + input.Subtype, + input.CoatedSide, + input.AppliedMethod.ToDomainModel(), + input.NearnormalHemisphericalVisibleTransmittances, + input.NearnormalHemisphericalVisibleReflectances, + input.NearnormalHemisphericalSolarTransmittances, + input.NearnormalHemisphericalSolarReflectances, + input.InfraredEmittances, + input.ColorRenderingIndices, + input.CielabColors.Select(c => c.ToDomainModel()).ToList() + ); + opticalData.Resources.Add(input.RootResource.ToDomainModel()); + context.OpticalData.Add(opticalData); + await context.SaveChangesAsync(cancellationToken); + return opticalData; + }, + undo: async opticalData => + { + context.Remove(opticalData); + await context.SaveChangesAsync(cancellationToken); + }, + cancellationToken ), - input.NearnormalHemisphericalVisibleTransmittances, - input.NearnormalHemisphericalVisibleReflectances, - input.NearnormalHemisphericalSolarTransmittances, - input.NearnormalHemisphericalSolarReflectances, - input.InfraredEmittances, - input.ColorRenderingIndices, - input.CielabColors.Select(c => - new CielabColor( - c.LStar, - c.AStar, - c.BStar, - c.Observer, - c.Illuminant - ) - ).ToList() - ); - var resource = new GetHttpsResource( - input.RootResource.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.RootResource.DataFormatId, - null, - input.RootResource.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.RootResource.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.RootResource.AppliedConversionMethod.MethodId, - input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.RootResource.AppliedConversionMethod.SourceName - ) + cancellationToken ); - opticalData.Resources.Add(resource); - context.OpticalData.Add(opticalData); - await context.SaveChangesAsync(cancellationToken); - - try - { - opticalData.Approval = await responseApprovalService.CreateResponseApproval(opticalData, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) - { - context.Remove(opticalData); - await context.SaveChangesAsync(cancellationToken); - - return new CreateOpticalDataPayload( - opticalData, - new CreateOpticalDataError( - CreateOpticalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Creating response approval failed with message: {exception.Message}", - [] - ) - ); - } - return new CreateOpticalDataPayload(opticalData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs index d9730a53..d0c552f9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs @@ -3,51 +3,8 @@ namespace Database.GraphQl.OpticalDataX; -public abstract class OpticalDataPayload - : Payload - where TOpticalDataError : IUserError -{ - protected OpticalDataPayload( - OpticalData opticalData - ) - { - OpticalData = opticalData; - } - - protected OpticalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected OpticalDataPayload( - TOpticalDataError error - ) - : this([error]) - { - } - - protected OpticalDataPayload( - OpticalData opticalData, - IReadOnlyCollection errors - ) - { - OpticalData = opticalData; - Errors = errors; - } - - protected OpticalDataPayload( - OpticalData opticalData, - TOpticalDataError error - ) - : this( - opticalData, - [error] - ) - { - } - - public OpticalData? OpticalData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file +public abstract record OpticalDataPayload( + OpticalData? OpticalData, + IReadOnlyCollection? Errors +) +where TOpticalDataError : IUserError; \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs index 1e375136..889d1cd8 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.PhotovoltaicDataX; -public sealed class CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationInput.cs b/backend/src/GraphQl/Publications/PublicationInput.cs index 14f77164..5303b1bc 100644 --- a/backend/src/GraphQl/Publications/PublicationInput.cs +++ b/backend/src/GraphQl/Publications/PublicationInput.cs @@ -1,4 +1,5 @@ using System; +using Database.Data; namespace Database.GraphQl.Publications; @@ -11,4 +12,19 @@ public sealed record PublicationInput( string? ArXiv, string? Urn, Uri? WebAddress -); \ No newline at end of file +) +{ + public Publication ToDomainModel() + { + return new Publication( + Title, + Abstract, + Section, + Authors, + Doi, + ArXiv, + Urn, + WebAddress + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationType.cs b/backend/src/GraphQl/Publications/PublicationType.cs index 60e051da..c103a128 100644 --- a/backend/src/GraphQl/Publications/PublicationType.cs +++ b/backend/src/GraphQl/Publications/PublicationType.cs @@ -6,20 +6,6 @@ namespace Database.GraphQl.Publications; public sealed class PublicationType : ObjectType { - internal static Publication FromInput(PublicationInput input) - { - return new Publication( - input.Title, - input.Abstract, - input.Section, - input.Authors, - input.Doi, - input.ArXiv, - input.Urn, - input.WebAddress - ); - } - protected override void Configure( IObjectTypeDescriptor descriptor ) diff --git a/backend/src/GraphQl/References/ReferenceInput.cs b/backend/src/GraphQl/References/ReferenceInput.cs index 0f36684d..40583fea 100644 --- a/backend/src/GraphQl/References/ReferenceInput.cs +++ b/backend/src/GraphQl/References/ReferenceInput.cs @@ -1,3 +1,5 @@ +using System; +using Database.Data; using Database.GraphQl.Publications; using Database.GraphQl.Standards; @@ -6,4 +8,26 @@ namespace Database.GraphQl.References; public sealed record ReferenceInput( StandardInput? Standard, PublicationInput? Publication -); \ No newline at end of file +) +{ + public Reference ToDomainModel() + { + if (Standard is null && Publication is null) + { + throw new InvalidOperationException("Both the reference's standard and publication are null."); + } + if (Standard is not null && Publication is not null) + { + throw new InvalidOperationException("Both the reference's standard and publication are non-null."); + } + if (Standard is not null) + { + return new Reference(Standard.ToDomainModel()); + } + else if (Publication is not null) + { + return new Reference(Publication.ToDomainModel()); + } + throw new InvalidOperationException("Impossible!"); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs index 673230b1..abad0cdf 100644 --- a/backend/src/GraphQl/References/ReferenceType.cs +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -9,27 +9,6 @@ namespace Database.GraphQl.References; public sealed class ReferenceType : UnionType { - internal static Reference FromInput(ReferenceInput input) - { - if (input?.Standard is null && input?.Publication is null) - { - throw new ArgumentException("Both the reference's standard and publication are null.", nameof(input)); - } - if (input.Standard is not null && input.Publication is not null) - { - throw new ArgumentException("Both the reference's standard and publication are non-null.", nameof(input)); - } - if (input.Standard is not null) - { - return new Reference(StandardType.FromInput(input.Standard)); - } - else if (input.Publication is not null) - { - return new Reference(PublicationType.FromInput(input.Publication)); - } - throw new ArgumentException("Impossible!", nameof(input)); - } - protected override void Configure(IUnionTypeDescriptor descriptor) { descriptor.Name(nameof(IReference)[1..]); diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs index a2c75f55..d9eeed54 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.ResponseApprovals; -public sealed class CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs index 478ee8f5..91fbcc3f 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs @@ -2,11 +2,9 @@ namespace Database.GraphQl.ResponseApprovals; -public sealed class UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode code, - string message, - IReadOnlyList path - ) - : UserErrorBase(code, message, path) -{ -} \ No newline at end of file +public sealed record UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 350aeb96..a04fb72d 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; +using Database.Utilities; namespace Database.GraphQl; @@ -8,4 +11,17 @@ public sealed record RootGetHttpsResourceInput( Guid DataFormatId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod -); \ No newline at end of file +) +{ + public GetHttpsResource ToDomainModel() + { + return new GetHttpsResource( + Description, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + DataFormatId, + null, + ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), + AppliedConversionMethod?.ToDomainModel() + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/StandardInput.cs b/backend/src/GraphQl/Standards/StandardInput.cs index e17e8a59..86bffe52 100644 --- a/backend/src/GraphQl/Standards/StandardInput.cs +++ b/backend/src/GraphQl/Standards/StandardInput.cs @@ -1,4 +1,5 @@ using System; +using Database.Data; using Database.Enumerations; using Database.GraphQl.Numerations; @@ -12,4 +13,24 @@ public sealed record StandardInput( CreateNumerationInput Numeration, Standardizer[] Standardizers, Uri? Locator -); \ No newline at end of file +) +{ + public Standard ToDomainModel() + { + return new Standard( + Title, + Abstract, + Section, + Year, + Standardizers, + Locator + ) + { + Numeration = new Numeration( + Numeration.Prefix, + Numeration.MainNumber, + Numeration.Suffix + ) + }; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/StandardType.cs b/backend/src/GraphQl/Standards/StandardType.cs index ffd1b013..8865bf78 100644 --- a/backend/src/GraphQl/Standards/StandardType.cs +++ b/backend/src/GraphQl/Standards/StandardType.cs @@ -6,25 +6,6 @@ namespace Database.GraphQl.Standards; public sealed class StandardType : ObjectType { - internal static Standard FromInput(StandardInput input) - { - return new Standard( - input.Title, - input.Abstract, - input.Section, - input.Year, - input.Standardizers, - input.Locator - ) - { - Numeration = new Numeration( - input.Numeration.Prefix, - input.Numeration.MainNumber, - input.Numeration.Suffix - ) - }; - } - protected override void Configure( IObjectTypeDescriptor descriptor ) diff --git a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs index 1ee7d1a3..fa131d04 100644 --- a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs +++ b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodInput.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using Database.Data; namespace Database.GraphQl; @@ -7,4 +9,14 @@ public sealed record ToTreeVertexAppliedConversionMethodInput( Guid MethodId, IReadOnlyList Arguments, string SourceName -); \ No newline at end of file +) +{ + public ToTreeVertexAppliedConversionMethod ToDomainModel() + { + return new ToTreeVertexAppliedConversionMethod( + MethodId, + Arguments.Select(a => a.ToDomainModel()).ToList(), + SourceName + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/UserErrorBase.cs b/backend/src/GraphQl/UserErrorBase.cs index 253a2b53..49fc6eba 100644 --- a/backend/src/GraphQl/UserErrorBase.cs +++ b/backend/src/GraphQl/UserErrorBase.cs @@ -1,17 +1,12 @@ +using System; using System.Collections.Generic; -using Enum = System.Enum; namespace Database.GraphQl; -public abstract class UserErrorBase( - TUserErrorCode code, - string message, - IReadOnlyList path - ) - : IUserError - where TUserErrorCode : struct, Enum -{ - public TUserErrorCode Code { get; } = code; - public string Message { get; } = message; - public IReadOnlyList Path { get; } = path; -} \ No newline at end of file +public abstract record UserErrorBase( + TUserErrorCode Code, + string Message, + IReadOnlyList Path +) +: IUserError +where TUserErrorCode : struct, Enum; \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 5ea9a921..5ae49d79 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -261,6 +261,17 @@ type DatabaseOperatorEdge { node: Institution! } +type DeleteDataError { + code: DeleteDataErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataPayload { + errors: [DeleteDataError!] + query: Query! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -439,9 +450,11 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") @@ -692,6 +705,18 @@ type UpdateDataAccessRightsPayload { query: Query! } +type UpdateDataError { + code: UpdateDataErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] + query: Query! +} + type UpdateDatabaseError { code: UpdateDatabaseErrorCode! message: String! @@ -983,6 +1008,11 @@ input DateTimePropositionInput { notLessThanOrEqualTo: DateTime @cost(weight: "10") } +input DeleteDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input FileMetaInformationInput { dataFormatId: Uuid! path: [String!]! @@ -1482,6 +1512,18 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String + name: String + warnings: [String!]! +} + input UpdateDatabaseInput { databaseId: Uuid! description: String! @@ -1649,6 +1691,12 @@ enum DatabaseVerificationState { VERIFIED } +enum DeleteDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum Illuminant { A D65 @@ -1758,6 +1806,12 @@ enum UpdateDataAccessRightsErrorCode { UNKNOWN_DATA } +enum UpdateDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 5ea9a921..5ae49d79 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -261,6 +261,17 @@ type DatabaseOperatorEdge { node: Institution! } +type DeleteDataError { + code: DeleteDataErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataPayload { + errors: [DeleteDataError!] + query: Query! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -439,9 +450,11 @@ type Mutation { createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") @@ -692,6 +705,18 @@ type UpdateDataAccessRightsPayload { query: Query! } +type UpdateDataError { + code: UpdateDataErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] + query: Query! +} + type UpdateDatabaseError { code: UpdateDatabaseErrorCode! message: String! @@ -983,6 +1008,11 @@ input DateTimePropositionInput { notLessThanOrEqualTo: DateTime @cost(weight: "10") } +input DeleteDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input FileMetaInformationInput { dataFormatId: Uuid! path: [String!]! @@ -1482,6 +1512,18 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String + name: String + warnings: [String!]! +} + input UpdateDatabaseInput { databaseId: Uuid! description: String! @@ -1649,6 +1691,12 @@ enum DatabaseVerificationState { VERIFIED } +enum DeleteDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum Illuminant { A D65 @@ -1758,6 +1806,12 @@ enum UpdateDataAccessRightsErrorCode { UNKNOWN_DATA } +enum UpdateDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED From d8f4320d53329591557c87932da2bc6e3a09fec0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Oct 2025 12:32:42 +0100 Subject: [PATCH 522/878] Remove data factory (it results for example in optical data being only partially initialized with default values for type, subtype, and coated side) --- backend/src/Data/CalorimetricData.cs | 29 +------- backend/src/Data/DataFactory.cs | 107 --------------------------- backend/src/Data/GeometricData.cs | 28 +------ backend/src/Data/HygrothermalData.cs | 27 +------ backend/src/Data/ICreateData.cs | 18 ----- backend/src/Data/OpticalData.cs | 37 +-------- backend/src/Data/PhotovoltaicData.cs | 27 +------ 7 files changed, 5 insertions(+), 268 deletions(-) delete mode 100644 backend/src/Data/DataFactory.cs delete mode 100644 backend/src/Data/ICreateData.cs diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 9b4b9163..266aee39 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -5,35 +5,8 @@ namespace Database.Data; public sealed class CalorimetricData - : DataX, ICreateData + : DataX { - public static CalorimetricData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return new CalorimetricData( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod, - [], - [] - ); - } - public CalorimetricData( Guid userId, string locale, diff --git a/backend/src/Data/DataFactory.cs b/backend/src/Data/DataFactory.cs deleted file mode 100644 index 38dc640f..00000000 --- a/backend/src/Data/DataFactory.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using Database.Enumerations; - -namespace Database.Data; - -public sealed class DataFactory -{ - public static T Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - where T : IData, ICreateData - { - return T.Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ); - } - - public static IData Create( - DataKind dataKind, - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return dataKind switch - { - DataKind.CALORIMETRIC_DATA => Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ), - DataKind.GEOMETRIC_DATA => Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ), - DataKind.HYGROTHERMAL_DATA => Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ), - DataKind.OPTICAL_DATA => Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ), - DataKind.PHOTOVOLTAIC_DATA => Create( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ), - _ => throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}.") - }; - } -} \ No newline at end of file diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 018ba591..bce9b60e 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -5,34 +5,8 @@ namespace Database.Data; public sealed class GeometricData - : DataX, ICreateData + : DataX { - public static GeometricData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return new GeometricData( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod, - [] - ); - } - public GeometricData( Guid userId, string locale, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 3da19fbb..64ee49a7 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -5,33 +5,8 @@ namespace Database.Data; public sealed class HygrothermalData - : DataX, ICreateData + : DataX { - public static HygrothermalData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return new HygrothermalData( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ); - } - public HygrothermalData( Guid userId, string locale, diff --git a/backend/src/Data/ICreateData.cs b/backend/src/Data/ICreateData.cs deleted file mode 100644 index 4e36ab33..00000000 --- a/backend/src/Data/ICreateData.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace Database.Data; - -public interface ICreateData where TData : IData -{ - static abstract TData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ); -} \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index bffed788..cba18f28 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -9,45 +9,10 @@ namespace Database.Data; public sealed class OpticalData - : DataX, ICreateData + : DataX { public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); - public static OpticalData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return new OpticalData( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - null, - null, - null, - appliedMethod, - [], - [], - [], - [], - [], - [], - [] - ); - } - public OpticalData( Guid userId, string locale, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 01642594..07cc5561 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -5,33 +5,8 @@ namespace Database.Data; public sealed class PhotovoltaicData - : DataX, ICreateData + : DataX { - public static PhotovoltaicData Create( - Guid userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - DateTime createdAt, - AppliedMethod appliedMethod - ) - { - return new PhotovoltaicData( - userId, - locale, - componentId, - name, - description, - warnings, - creatorId, - createdAt, - appliedMethod - ); - } - public PhotovoltaicData( Guid userId, string locale, From fb386b347adfbd1106b8b509099ddad3829734f2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Oct 2025 12:46:09 +0100 Subject: [PATCH 523/878] Extract creation of domain model from input into the input record --- .../CalorimetricDataMutations.cs | 59 +------------------ .../CreateCalorimetricDataInput.cs | 25 +++++++- .../DataApprovals/DataApprovalMutations.cs | 1 - .../CreateGeometricDataInput.cs | 24 +++++++- .../GeometricDataX/GeometricDataMutations.cs | 58 +----------------- .../CreateHygrothermalDataInput.cs | 23 +++++++- .../HygrothermalDataMutations.cs | 57 +----------------- .../OpticalDataX/CreateOpticalDataInput.cs | 32 +++++++++- .../OpticalDataX/OpticalDataMutations.cs | 23 +------- .../CreatePhotovoltaicDataInput.cs | 23 +++++++- .../PhotovoltaicDataMutations.cs | 57 +----------------- 11 files changed, 127 insertions(+), 255 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index 288f9a64..ffde1e24 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -46,64 +46,7 @@ CancellationToken cancellationToken ); } - var calorimetricData = new CalorimetricData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - new AppliedMethod( - input.AppliedMethod.MethodId, - input.AppliedMethod.Arguments - .Select(a => new NamedMethodArgument( - a.Name, - a.Value - )) - .ToList(), - input.AppliedMethod.Sources - .Select(s => new NamedMethodSource( - s.Name, - new CrossDatabaseDataReference( - s.Value.DataId, - s.Value.DataTimestamp, - s.Value.DataKind, - s.Value.DatabaseId - ) - )) - .ToList() - ), - input.GValues, - input.UValues - ); - var resource = new GetHttpsResource( - input.RootResource.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.RootResource.DataFormatId, - null, - input.RootResource.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.RootResource.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.RootResource.AppliedConversionMethod.MethodId, - input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.RootResource.AppliedConversionMethod.SourceName - ) - ); - calorimetricData.Resources.Add(resource); - context.CalorimetricData.Add(calorimetricData); + var calorimetricData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); try diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs index fb84ef1c..fdf4235c 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; +using Database.Data; namespace Database.GraphQl.CalorimetricDataX; @@ -18,4 +21,24 @@ public sealed record CreateCalorimetricDataInput( RootGetHttpsResourceInput RootResource, double[] GValues, double[] UValues -); \ No newline at end of file +) +{ + public CalorimetricData ToDomainModel(Guid userId) + { + var calorimetricData = new CalorimetricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + GValues, + UValues + ); + calorimetricData.Resources.Add(RootResource.ToDomainModel()); + return calorimetricData; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 7fb1a0c1..364b5af8 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -8,7 +8,6 @@ using Database.Authorization; using Database.Data; using Database.GraphQl.Publications; -using Database.GraphQl.References; using Database.GraphQl.Standards; using Database.Json; using Database.Services; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs index 46a3663e..52f5c015 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; +using Database.Data; namespace Database.GraphQl.GeometricDataX; @@ -16,4 +19,23 @@ public sealed record CreateGeometricDataInput( AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, double[] Thicknesses -); \ No newline at end of file +) +{ + public GeometricData ToDomainModel(Guid userId) + { + var geometricData = new GeometricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + Thicknesses + ); + geometricData.Resources.Add(RootResource.ToDomainModel()); + return geometricData; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 6aeade51..45e1efa0 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -46,63 +46,7 @@ CancellationToken cancellationToken ); } - var geometricData = new GeometricData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - new AppliedMethod( - input.AppliedMethod.MethodId, - input.AppliedMethod.Arguments - .Select(a => new NamedMethodArgument( - a.Name, - a.Value - )) - .ToList(), - input.AppliedMethod.Sources - .Select(s => new NamedMethodSource( - s.Name, - new CrossDatabaseDataReference( - s.Value.DataId, - s.Value.DataTimestamp, - s.Value.DataKind, - s.Value.DatabaseId - ) - )) - .ToList() - ), - input.Thicknesses - ); - var resource = new GetHttpsResource( - input.RootResource.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.RootResource.DataFormatId, - null, - input.RootResource.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.RootResource.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.RootResource.AppliedConversionMethod.MethodId, - input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.RootResource.AppliedConversionMethod.SourceName - ) - ); - geometricData.Resources.Add(resource); - + var geometricData = input.ToDomainModel(currentUser.Uuid); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs index b6320a99..297869f7 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; +using Database.Data; namespace Database.GraphQl.HygrothermalDataX; @@ -16,4 +19,22 @@ public sealed record CreateHygrothermalDataInput( Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource -); \ No newline at end of file +) +{ + public HygrothermalData ToDomainModel(Guid userId) + { + var hygrothermalData = new HygrothermalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + hygrothermalData.Resources.Add(RootResource.ToDomainModel()); + return hygrothermalData; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index aad9b631..f32982c6 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -45,62 +45,7 @@ CancellationToken cancellationToken ); } - var hygrothermalData = new HygrothermalData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - new AppliedMethod( - input.AppliedMethod.MethodId, - input.AppliedMethod.Arguments - .Select(a => new NamedMethodArgument( - a.Name, - a.Value - )) - .ToList(), - input.AppliedMethod.Sources - .Select(s => new NamedMethodSource( - s.Name, - new CrossDatabaseDataReference( - s.Value.DataId, - s.Value.DataTimestamp, - s.Value.DataKind, - s.Value.DatabaseId - ) - )) - .ToList() - ) - ); - var resource = new GetHttpsResource( - input.RootResource.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.RootResource.DataFormatId, - null, - input.RootResource.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.RootResource.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.RootResource.AppliedConversionMethod.MethodId, - input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.RootResource.AppliedConversionMethod.SourceName - ) - ); - hygrothermalData.Resources.Add(resource); - context.HygrothermalData.Add(hygrothermalData); + var hygrothermalData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); try diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs index b3e13c8d..dad6eb04 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; +using Database.Data; namespace Database.GraphQl.OpticalDataX; @@ -27,4 +29,32 @@ public sealed record CreateOpticalDataInput( double[] InfraredEmittances, double[] ColorRenderingIndices, IReadOnlyList CielabColors -); \ No newline at end of file +) +{ + public OpticalData ToDomainModel(Guid userId) + { + var opticalData = new OpticalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + Type, + Subtype, + CoatedSide, + AppliedMethod.ToDomainModel(), + NearnormalHemisphericalVisibleTransmittances, + NearnormalHemisphericalVisibleReflectances, + NearnormalHemisphericalSolarTransmittances, + NearnormalHemisphericalSolarReflectances, + InfraredEmittances, + ColorRenderingIndices, + CielabColors.Select(c => c.ToDomainModel()).ToList() + ); + opticalData.Resources.Add(RootResource.ToDomainModel()); + return opticalData; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs index 6585d96d..d3f29ed7 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs @@ -45,28 +45,7 @@ IReadOnlyList path responseApprovalService, act: async () => { - var opticalData = new OpticalData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - input.Type, - input.Subtype, - input.CoatedSide, - input.AppliedMethod.ToDomainModel(), - input.NearnormalHemisphericalVisibleTransmittances, - input.NearnormalHemisphericalVisibleReflectances, - input.NearnormalHemisphericalSolarTransmittances, - input.NearnormalHemisphericalSolarReflectances, - input.InfraredEmittances, - input.ColorRenderingIndices, - input.CielabColors.Select(c => c.ToDomainModel()).ToList() - ); - opticalData.Resources.Add(input.RootResource.ToDomainModel()); + var opticalData = input.ToDomainModel(currentUser.Uuid); context.OpticalData.Add(opticalData); await context.SaveChangesAsync(cancellationToken); return opticalData; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs index a3db233c..a02c87f6 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Generic; +using System.Linq; using HotChocolate; using HotChocolate.Types; using Database.Enumerations; +using Database.Data; namespace Database.GraphQl.PhotovoltaicDataX; @@ -16,4 +19,22 @@ public sealed record CreatePhotovoltaicDataInput( Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource -); \ No newline at end of file +) +{ + public PhotovoltaicData ToDomainModel(Guid userId) + { + var photovoltaicData = new PhotovoltaicData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + photovoltaicData.Resources.Add(RootResource.ToDomainModel()); + return photovoltaicData; + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 3300956d..3eaa979c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -46,62 +46,7 @@ CancellationToken cancellationToken ); } - var photovoltaicData = new PhotovoltaicData( - currentUser.Uuid, - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatorId, - input.CreatedAt, - new AppliedMethod( - input.AppliedMethod.MethodId, - input.AppliedMethod.Arguments - .Select(a => new NamedMethodArgument( - a.Name, - a.Value - )) - .ToList(), - input.AppliedMethod.Sources - .Select(s => new NamedMethodSource( - s.Name, - new CrossDatabaseDataReference( - s.Value.DataId, - s.Value.DataTimestamp, - s.Value.DataKind, - s.Value.DatabaseId - ) - )) - .ToList() - ) - ); - var resource = new GetHttpsResource( - input.RootResource.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.RootResource.DataFormatId, - null, - input.RootResource.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.RootResource.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.RootResource.AppliedConversionMethod.MethodId, - input.RootResource.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.RootResource.AppliedConversionMethod.SourceName - ) - ); - photovoltaicData.Resources.Add(resource); - context.PhotovoltaicData.Add(photovoltaicData); + var photovoltaicData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); try From 0d66d2864bbcaa7b32af53abc0d37a178d8e466f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Oct 2025 13:35:03 +0100 Subject: [PATCH 524/878] Discover types automatically with hot chocolate's type analyzer --- .../src/Configuration/GraphQlConfiguration.cs | 108 ++-------- backend/src/Database.csproj | 1 + backend/src/GraphQl/DataX/DataSortType.cs | 1 + backend/src/Properties/ModuleInfo.cs | 1 + .../GraphQlSchemaTests.IsUnchanged.snap | 191 +++++++++++++----- frontend/type-defs.graphqls | 139 +++++++++++-- 6 files changed, 280 insertions(+), 161 deletions(-) create mode 100644 backend/src/Properties/ModuleInfo.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 6cf2fdf1..95b5f487 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,38 +1,19 @@ using System; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using HotChocolate.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; using HotChocolate.Language; using HotChocolate.Types; -using System.Net.Http; -using System.Text.RegularExpressions; +using HotChocolate.Configuration; +using HotChocolate.Execution; using Database.Data; using Database.GraphQl; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.Common; -using Database.GraphQl.Databases; using Database.GraphQl.DataX; -using Database.GraphQl.Filters; -using Database.GraphQl.GetHttpsResources; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; -using Database.GraphQl.Publications; -using Database.GraphQl.References; -using Database.GraphQl.Standards; -using Database.GraphQl.GeometricDataX; -using Database.GraphQl.Users; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using IServiceCollection = Microsoft.Extensions.DependencyInjection.IServiceCollection; -using Database.GraphQl.DataApprovals; -using Database.GraphQl.AccessRights; -using Database.GraphQl.Methods; -using Database.GraphQl.ResponseApprovals; -using HotChocolate.Configuration; -using HotChocolate.Execution; namespace Database.Configuration; @@ -50,6 +31,7 @@ IWebHostEnvironment environment // GraphQL Server services .AddGraphQLServer() + .DisableIntrospection(false) // if the introspection result becomes too big we need to disable it in production .BindRuntimeType() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContextFactory() @@ -95,6 +77,7 @@ IWebHostEnvironment environment options.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. /* options.QueryCacheSize = ...; */ /* options.UseComplexityMultipliers = ...; */ + options.EnableSchemaFileSupport = true; } ) // Configure @@ -131,53 +114,14 @@ IWebHostEnvironment environment .AddType(new UrlType("Url")) .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar .AddType(new LocaleType()) - // Query Types - .AddQueryType(d => d.Name(nameof(Query))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - // Mutation Types - .AddMutationType(d => d.Name(nameof(Mutation))) - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - /* .AddSubscriptionType(d => d.Name(nameof(GraphQl.Subscription))) */ - /* .AddType() */ // Object Types .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - .AddType() - // Data Loaders - /* .AddDataLoader() */ + // Query, Mutation and Subscription Types + .AddQueryType(d => d.Name(nameof(Query))) + .AddMutationType(d => d.Name(nameof(Mutation))) + // .AddSubscriptionType(d => d.Name(nameof(Subscription))) + // auto-discover types using `HotChocolate.Types.Analyzers` + .AddTypes() // Paging .AddDbContextCursorPagingProvider() .ModifyPagingOptions(_ => @@ -207,17 +151,6 @@ protected override void Configure(IFilterConventionDescriptor descriptor) // Allow conjunction and disjunction descriptor.AllowAnd(); descriptor.AllowOr(); - // Bind custom types - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); } // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? @@ -393,16 +326,5 @@ public partial class CustomSortConvention : SortConvention protected override void Configure(ISortConventionDescriptor descriptor) { descriptor.AddDefaults(); - // Bind custom types - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); - descriptor.BindRuntimeType(); } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 61df65b1..4c9d55a5 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -32,6 +32,7 @@ + diff --git a/backend/src/GraphQl/DataX/DataSortType.cs b/backend/src/GraphQl/DataX/DataSortType.cs index 85b601bb..42143914 100644 --- a/backend/src/GraphQl/DataX/DataSortType.cs +++ b/backend/src/GraphQl/DataX/DataSortType.cs @@ -10,6 +10,7 @@ protected override void Configure( ISortInputTypeDescriptor descriptor ) { + descriptor.Name(nameof(IData)[1..] + GraphQlConstants.SortInputSuffix); base.Configure(descriptor); } } \ No newline at end of file diff --git a/backend/src/Properties/ModuleInfo.cs b/backend/src/Properties/ModuleInfo.cs new file mode 100644 index 00000000..33703332 --- /dev/null +++ b/backend/src/Properties/ModuleInfo.cs @@ -0,0 +1 @@ +[assembly: HotChocolate.Module("Types")] \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 5ae49d79..ff0264a9 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -3,6 +3,16 @@ schema { mutation: Mutation } +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + interface Data { appliedMethod: AppliedMethod! approval: ResponseApproval @@ -26,12 +36,22 @@ interface Data { warnings: [String!]! } +interface GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID +} + "The node interface is implemented by entities that have a global unique identifier." interface Node { id: ID! } -type AddDataApprovalError { +interface UserError { + message: String! + path: [String!]! +} + +type AddDataApprovalError implements UserError { code: AddDataApprovalErrorCode! message: String! path: [String!]! @@ -59,7 +79,7 @@ type AppliedMethod { sources: [NamedMethodSource!]! } -type CalculateMethodError { +type CalculateMethodError implements UserError { code: CalculateMethodErrorCode! message: String! path: [String!]! @@ -122,7 +142,7 @@ type CielabColor { observer: CalorimetricObserver } -type CreateCalorimetricDataError { +type CreateCalorimetricDataError implements UserError { code: CreateCalorimetricDataErrorCode! message: String! path: [String!]! @@ -134,7 +154,7 @@ type CreateCalorimetricDataPayload { query: Query! } -type CreateGeometricDataError { +type CreateGeometricDataError implements UserError { code: CreateGeometricDataErrorCode! message: String! path: [String!]! @@ -146,7 +166,7 @@ type CreateGeometricDataPayload { query: Query! } -type CreateGetHttpsResourceError { +type CreateGetHttpsResourceError implements UserError { code: CreateGetHttpsResourceErrorCode! message: String! path: [String!]! @@ -158,7 +178,7 @@ type CreateGetHttpsResourcePayload { query: Query! } -type CreateHygrothermalDataError { +type CreateHygrothermalDataError implements UserError { code: CreateHygrothermalDataErrorCode! message: String! path: [String!]! @@ -170,7 +190,7 @@ type CreateHygrothermalDataPayload { query: Query! } -type CreateOpticalDataError { +type CreateOpticalDataError implements UserError { code: CreateOpticalDataErrorCode! message: String! path: [String!]! @@ -182,7 +202,7 @@ type CreateOpticalDataPayload { query: Query! } -type CreatePhotovoltaicDataError { +type CreatePhotovoltaicDataError implements UserError { code: CreatePhotovoltaicDataErrorCode! message: String! path: [String!]! @@ -194,7 +214,7 @@ type CreatePhotovoltaicDataPayload { query: Query! } -type CreateResponseApprovalsError { +type CreateResponseApprovalsError implements UserError { code: CreateResponseApprovalsErrorCode! message: String! path: [String!]! @@ -223,7 +243,7 @@ type DataAccessRights { hasRestrictionsByUser: Boolean! } -type DataApproval { +type DataApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! @@ -261,7 +281,7 @@ type DatabaseOperatorEdge { node: Institution! } -type DeleteDataError { +type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! path: [String!]! @@ -358,14 +378,14 @@ type GetHttpsResourceTree { root: GetHttpsResourceTreeRoot! @cost(weight: "10") } -type GetHttpsResourceTreeNonRootVertex { +type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { appliedConversionMethod: ToTreeVertexAppliedConversionMethod! parentId: ID! value: GetHttpsResource! vertexId: ID! } -type GetHttpsResourceTreeRoot { +type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { value: GetHttpsResource! vertexId: ID! } @@ -429,7 +449,7 @@ type InstitutionAccessRights { version: NonNegativeInt! } -type InstitutionAccessRightsError { +type InstitutionAccessRightsError implements UserError { code: InstitutionAccessRightsErrorCode! message: String! path: [String!]! @@ -618,7 +638,7 @@ type Query { database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") @@ -634,7 +654,7 @@ type Query { verificationCode: String! } -type RecomputeGetHttpsResourceHashValuesError { +type RecomputeGetHttpsResourceHashValuesError implements UserError { code: RecomputeGetHttpsResourceHashValuesErrorCode! message: String! path: [String!]! @@ -646,7 +666,7 @@ type RecomputeGetHttpsResourceHashValuesPayload { query: Query! } -type RemoveDataApprovalError { +type RemoveDataApprovalError implements UserError { code: RemoveDataApprovalErrorCode! message: String! path: [String!]! @@ -658,7 +678,7 @@ type RemoveDataApprovalPayload { query: Query! } -type ResponseApproval { +type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! @@ -693,7 +713,7 @@ type UpdateAccessRightsPayload { query: Query! } -type UpdateDataAccessRightsError { +type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! path: [String!]! @@ -705,7 +725,7 @@ type UpdateDataAccessRightsPayload { query: Query! } -type UpdateDataError { +type UpdateDataError implements UserError { code: UpdateDataErrorCode! message: String! path: [String!]! @@ -729,7 +749,7 @@ type UpdateDatabasePayload { query: Query! } -type UpdateResponseApprovalsError { +type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! path: [String!]! @@ -772,7 +792,7 @@ input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] or: [AppliedMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListNamedMethodArgumentFilterTypePropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput } @@ -796,7 +816,7 @@ input CalorimetricDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput gValues: FloatsPropositionInput @@ -982,7 +1002,7 @@ input DataFilterInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput } @@ -993,6 +1013,16 @@ input DataKindPropositionInput { notIn: [DataKind!] @cost(weight: "10") } +input DataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") notEqualTo: DateTime @cost(weight: "10") @@ -1058,7 +1088,7 @@ input GeometricDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput thicknesses: FloatsPropositionInput @@ -1084,7 +1114,7 @@ input GetHttpsResourcePropositionInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput } input GetHttpsResourceSortInput { @@ -1093,7 +1123,7 @@ input GetHttpsResourceSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertSortInput { @@ -1102,7 +1132,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertexFilterInput { @@ -1114,7 +1144,7 @@ input GetHttpsResourceTreeNonRootVertexFilterInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput } input HygrothermalDataPropositionInput { @@ -1128,7 +1158,7 @@ input HygrothermalDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput } @@ -1205,27 +1235,27 @@ input ListFilterInputTypeOfFileMetaInformationPropositionInput { any: Boolean @cost(weight: "10") } -input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListGetHttpsResourceFilterTypePropositionInput { - all: GetHttpsResourcePropositionInput @cost(weight: "10") - none: GetHttpsResourcePropositionInput @cost(weight: "10") - some: GetHttpsResourcePropositionInput @cost(weight: "10") +input ListFilterInputTypeOfGetHttpsResourcePropositionInput { + all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") any: Boolean @cost(weight: "10") } -input ListNamedMethodArgumentFilterTypePropositionInput { +input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") none: NamedMethodArgumentPropositionInput @cost(weight: "10") some: NamedMethodArgumentPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") } +input ListFilterInputTypeOfNamedMethodSourcePropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input ListStandardizerPropositionInput { all: StandardizerPropositionInput @cost(weight: "10") none: StandardizerPropositionInput @cost(weight: "10") @@ -1244,6 +1274,10 @@ input NamedMethodArgumentPropositionInput { name: StringPropositionInput } +input NamedMethodArgumentSortInput { + name: SortEnumType @cost(weight: "10") +} + input NamedMethodSourceInput { name: String! value: CrossDatabaseDataReferenceInput! @@ -1314,6 +1348,12 @@ input NumerationPropositionInput { suffix: StringPropositionInput } +input NumerationSortInput { + prefix: SortEnumType @cost(weight: "10") + mainNumber: SortEnumType @cost(weight: "10") + suffix: SortEnumType @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] @@ -1325,7 +1365,7 @@ input OpticalDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput type: NullableOfOpticalComponentTypePropositionInput @@ -1362,7 +1402,7 @@ input PhotovoltaicDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput } @@ -1407,6 +1447,21 @@ input PublicationPropositionInput { section: StringPropositionInput } +input PublicationSortInput { + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: SortEnumType @cost(weight: "10") + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: SortEnumType @cost(weight: "10") + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: SortEnumType @cost(weight: "10") + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "Referenced section" + section: SortEnumType @cost(weight: "10") +} + input RecomputeGetHttpsResourceHashValuesFilterInput { and: [RecomputeGetHttpsResourceHashValuesFilterInput!] or: [RecomputeGetHttpsResourceHashValuesFilterInput!] @@ -1416,7 +1471,7 @@ input RecomputeGetHttpsResourceHashValuesFilterInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput data: DataFilterInput } @@ -1432,6 +1487,11 @@ input ReferencePropositionInput { publication: PublicationPropositionInput } +input ReferenceSortInput { + standard: StandardSortInput @cost(weight: "10") + publication: PublicationSortInput @cost(weight: "10") +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1464,6 +1524,18 @@ input StandardPropositionInput { section: StringPropositionInput } +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardSortInput { + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: SortEnumType @cost(weight: "10") + numeration: NumerationSortInput @cost(weight: "10") + locator: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "The section of the standard to which the reference refers to." + section: SortEnumType @cost(weight: "10") +} + input StandardizerPropositionInput { equalTo: Standardizer @cost(weight: "10") notEqualTo: Standardizer @cost(weight: "10") @@ -1503,7 +1575,7 @@ input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] or: [ToTreeVertexAppliedConversionMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListNamedMethodArgumentFilterTypePropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput sourceName: StringPropositionInput } @@ -1513,13 +1585,13 @@ input ToTreeVertexAppliedConversionMethodSortInput { } input UpdateDataInput { - appliedMethod: AppliedMethodInput! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! dataId: Uuid! dataKind: DataKind! description: String + locale: String! name: String warnings: [String!]! } @@ -1565,6 +1637,30 @@ input UriPropositionInput { userInfo: StringPropositionInput } +input UriSortInput { + absolutePath: SortEnumType @cost(weight: "10") + absoluteUri: SortEnumType @cost(weight: "10") + localPath: SortEnumType @cost(weight: "10") + authority: SortEnumType @cost(weight: "10") + hostNameType: SortEnumType @cost(weight: "10") + isDefaultPort: SortEnumType @cost(weight: "10") + isFile: SortEnumType @cost(weight: "10") + isLoopback: SortEnumType @cost(weight: "10") + pathAndQuery: SortEnumType @cost(weight: "10") + isUnc: SortEnumType @cost(weight: "10") + host: SortEnumType @cost(weight: "10") + port: SortEnumType @cost(weight: "10") + query: SortEnumType @cost(weight: "10") + fragment: SortEnumType @cost(weight: "10") + scheme: SortEnumType @cost(weight: "10") + originalString: SortEnumType @cost(weight: "10") + dnsSafeHost: SortEnumType @cost(weight: "10") + idnHost: SortEnumType @cost(weight: "10") + isAbsoluteUri: SortEnumType @cost(weight: "10") + userEscaped: SortEnumType @cost(weight: "10") + userInfo: SortEnumType @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") notEqualTo: Uuid @cost(weight: "10") @@ -1810,6 +1906,7 @@ enum UpdateDataErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED } enum UpdateDatabaseErrorCode { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 5ae49d79..110173ec 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -3,6 +3,16 @@ schema { mutation: Mutation } +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + interface Data { appliedMethod: AppliedMethod! approval: ResponseApproval @@ -26,12 +36,22 @@ interface Data { warnings: [String!]! } +interface GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID +} + "The node interface is implemented by entities that have a global unique identifier." interface Node { id: ID! } -type AddDataApprovalError { +interface UserError { + message: String! + path: [String!]! +} + +type AddDataApprovalError implements UserError { code: AddDataApprovalErrorCode! message: String! path: [String!]! @@ -59,7 +79,7 @@ type AppliedMethod { sources: [NamedMethodSource!]! } -type CalculateMethodError { +type CalculateMethodError implements UserError { code: CalculateMethodErrorCode! message: String! path: [String!]! @@ -122,7 +142,7 @@ type CielabColor { observer: CalorimetricObserver } -type CreateCalorimetricDataError { +type CreateCalorimetricDataError implements UserError { code: CreateCalorimetricDataErrorCode! message: String! path: [String!]! @@ -134,7 +154,7 @@ type CreateCalorimetricDataPayload { query: Query! } -type CreateGeometricDataError { +type CreateGeometricDataError implements UserError { code: CreateGeometricDataErrorCode! message: String! path: [String!]! @@ -146,7 +166,7 @@ type CreateGeometricDataPayload { query: Query! } -type CreateGetHttpsResourceError { +type CreateGetHttpsResourceError implements UserError { code: CreateGetHttpsResourceErrorCode! message: String! path: [String!]! @@ -158,7 +178,7 @@ type CreateGetHttpsResourcePayload { query: Query! } -type CreateHygrothermalDataError { +type CreateHygrothermalDataError implements UserError { code: CreateHygrothermalDataErrorCode! message: String! path: [String!]! @@ -170,7 +190,7 @@ type CreateHygrothermalDataPayload { query: Query! } -type CreateOpticalDataError { +type CreateOpticalDataError implements UserError { code: CreateOpticalDataErrorCode! message: String! path: [String!]! @@ -182,7 +202,7 @@ type CreateOpticalDataPayload { query: Query! } -type CreatePhotovoltaicDataError { +type CreatePhotovoltaicDataError implements UserError { code: CreatePhotovoltaicDataErrorCode! message: String! path: [String!]! @@ -194,7 +214,7 @@ type CreatePhotovoltaicDataPayload { query: Query! } -type CreateResponseApprovalsError { +type CreateResponseApprovalsError implements UserError { code: CreateResponseApprovalsErrorCode! message: String! path: [String!]! @@ -223,7 +243,7 @@ type DataAccessRights { hasRestrictionsByUser: Boolean! } -type DataApproval { +type DataApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! @@ -261,7 +281,7 @@ type DatabaseOperatorEdge { node: Institution! } -type DeleteDataError { +type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! path: [String!]! @@ -358,14 +378,14 @@ type GetHttpsResourceTree { root: GetHttpsResourceTreeRoot! @cost(weight: "10") } -type GetHttpsResourceTreeNonRootVertex { +type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { appliedConversionMethod: ToTreeVertexAppliedConversionMethod! parentId: ID! value: GetHttpsResource! vertexId: ID! } -type GetHttpsResourceTreeRoot { +type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { value: GetHttpsResource! vertexId: ID! } @@ -429,7 +449,7 @@ type InstitutionAccessRights { version: NonNegativeInt! } -type InstitutionAccessRightsError { +type InstitutionAccessRightsError implements UserError { code: InstitutionAccessRightsErrorCode! message: String! path: [String!]! @@ -634,7 +654,7 @@ type Query { verificationCode: String! } -type RecomputeGetHttpsResourceHashValuesError { +type RecomputeGetHttpsResourceHashValuesError implements UserError { code: RecomputeGetHttpsResourceHashValuesErrorCode! message: String! path: [String!]! @@ -646,7 +666,7 @@ type RecomputeGetHttpsResourceHashValuesPayload { query: Query! } -type RemoveDataApprovalError { +type RemoveDataApprovalError implements UserError { code: RemoveDataApprovalErrorCode! message: String! path: [String!]! @@ -658,7 +678,7 @@ type RemoveDataApprovalPayload { query: Query! } -type ResponseApproval { +type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! @@ -693,7 +713,7 @@ type UpdateAccessRightsPayload { query: Query! } -type UpdateDataAccessRightsError { +type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! path: [String!]! @@ -705,7 +725,7 @@ type UpdateDataAccessRightsPayload { query: Query! } -type UpdateDataError { +type UpdateDataError implements UserError { code: UpdateDataErrorCode! message: String! path: [String!]! @@ -729,7 +749,7 @@ type UpdateDatabasePayload { query: Query! } -type UpdateResponseApprovalsError { +type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! path: [String!]! @@ -993,6 +1013,16 @@ input DataKindPropositionInput { notIn: [DataKind!] @cost(weight: "10") } +input DataSortInput { + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") notEqualTo: DateTime @cost(weight: "10") @@ -1244,6 +1274,10 @@ input NamedMethodArgumentPropositionInput { name: StringPropositionInput } +input NamedMethodArgumentSortInput { + name: SortEnumType @cost(weight: "10") +} + input NamedMethodSourceInput { name: String! value: CrossDatabaseDataReferenceInput! @@ -1314,6 +1348,12 @@ input NumerationPropositionInput { suffix: StringPropositionInput } +input NumerationSortInput { + prefix: SortEnumType @cost(weight: "10") + mainNumber: SortEnumType @cost(weight: "10") + suffix: SortEnumType @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] @@ -1407,6 +1447,21 @@ input PublicationPropositionInput { section: StringPropositionInput } +input PublicationSortInput { + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: SortEnumType @cost(weight: "10") + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: SortEnumType @cost(weight: "10") + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: SortEnumType @cost(weight: "10") + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "Referenced section" + section: SortEnumType @cost(weight: "10") +} + input RecomputeGetHttpsResourceHashValuesFilterInput { and: [RecomputeGetHttpsResourceHashValuesFilterInput!] or: [RecomputeGetHttpsResourceHashValuesFilterInput!] @@ -1432,6 +1487,11 @@ input ReferencePropositionInput { publication: PublicationPropositionInput } +input ReferenceSortInput { + standard: StandardSortInput @cost(weight: "10") + publication: PublicationSortInput @cost(weight: "10") +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1464,6 +1524,18 @@ input StandardPropositionInput { section: StringPropositionInput } +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardSortInput { + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: SortEnumType @cost(weight: "10") + numeration: NumerationSortInput @cost(weight: "10") + locator: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "The section of the standard to which the reference refers to." + section: SortEnumType @cost(weight: "10") +} + input StandardizerPropositionInput { equalTo: Standardizer @cost(weight: "10") notEqualTo: Standardizer @cost(weight: "10") @@ -1513,13 +1585,13 @@ input ToTreeVertexAppliedConversionMethodSortInput { } input UpdateDataInput { - appliedMethod: AppliedMethodInput! componentId: Uuid! createdAt: DateTime! creatorId: Uuid! dataId: Uuid! dataKind: DataKind! description: String + locale: String! name: String warnings: [String!]! } @@ -1565,6 +1637,30 @@ input UriPropositionInput { userInfo: StringPropositionInput } +input UriSortInput { + absolutePath: SortEnumType @cost(weight: "10") + absoluteUri: SortEnumType @cost(weight: "10") + localPath: SortEnumType @cost(weight: "10") + authority: SortEnumType @cost(weight: "10") + hostNameType: SortEnumType @cost(weight: "10") + isDefaultPort: SortEnumType @cost(weight: "10") + isFile: SortEnumType @cost(weight: "10") + isLoopback: SortEnumType @cost(weight: "10") + pathAndQuery: SortEnumType @cost(weight: "10") + isUnc: SortEnumType @cost(weight: "10") + host: SortEnumType @cost(weight: "10") + port: SortEnumType @cost(weight: "10") + query: SortEnumType @cost(weight: "10") + fragment: SortEnumType @cost(weight: "10") + scheme: SortEnumType @cost(weight: "10") + originalString: SortEnumType @cost(weight: "10") + dnsSafeHost: SortEnumType @cost(weight: "10") + idnHost: SortEnumType @cost(weight: "10") + isAbsoluteUri: SortEnumType @cost(weight: "10") + userEscaped: SortEnumType @cost(weight: "10") + userInfo: SortEnumType @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") notEqualTo: Uuid @cost(weight: "10") @@ -1810,6 +1906,7 @@ enum UpdateDataErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED } enum UpdateDatabaseErrorCode { From 3dd8954067330f2d5c590ccd559b4ec5c15760f7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 27 Oct 2025 17:39:54 +0100 Subject: [PATCH 525/878] Refactor data mutations to use one class per mutation with common code for authorization and response approval creation --- .../src/Authorization/CommonAuthorization.cs | 18 +- .../src/Configuration/AuthConfiguration.cs | 9 + .../src/Controllers/FileUploadController.cs | 3 +- backend/src/Data/GetHttpsResource.cs | 26 +- backend/src/Extensions/ResultExtensions.cs | 91 ++++ .../AccessRights/DataAccessRightsMutations.cs | 9 +- .../CalorimetricDataMutations.cs | 3 +- .../DataApprovals/DataApprovalMutations.cs | 6 +- backend/src/GraphQl/DataX/DataMutationBase.cs | 73 ++++ backend/src/GraphQl/DataX/DataMutations.cs | 410 ------------------ .../src/GraphQl/DataX/DataMutationsBase.cs | 76 ---- .../src/GraphQl/DataX/DeleteDataMutation.cs | 106 +++++ .../DataX/ExtractIntegralValuesFromFile.cs | 32 ++ ...ttpsResourceTreeNonRootVertexFilterType.cs | 5 + .../src/GraphQl/DataX/UpdateDataMutation.cs | 142 ++++++ .../GeometricDataX/GeometricDataMutations.cs | 3 +- .../GetHttpsResourceFilterType.cs | 6 + .../GetHttpsResourceMutations.cs | 6 +- ...uteGetHttpsResourceHashValuesFilterType.cs | 1 - .../HygrothermalDataMutations.cs | 3 +- backend/src/GraphQl/MutationBase.cs | 50 +++ backend/src/GraphQl/MutationsBase.cs | 46 -- .../OpticalDataX/CreateOpticalDataMutation.cs | 70 +++ .../OpticalDataX/OpticalDataMutations.cs | 63 --- .../PhotovoltaicDataMutations.cs | 3 +- .../ResponseApprovalMutations.cs | 6 +- backend/src/Result.cs | 13 + .../GraphQlSchemaTests.IsUnchanged.snap | 11 +- frontend/type-defs.graphqls | 63 +-- 29 files changed, 710 insertions(+), 643 deletions(-) create mode 100644 backend/src/Extensions/ResultExtensions.cs create mode 100644 backend/src/GraphQl/DataX/DataMutationBase.cs delete mode 100644 backend/src/GraphQl/DataX/DataMutations.cs delete mode 100644 backend/src/GraphQl/DataX/DataMutationsBase.cs create mode 100644 backend/src/GraphQl/DataX/DeleteDataMutation.cs create mode 100644 backend/src/GraphQl/DataX/ExtractIntegralValuesFromFile.cs create mode 100644 backend/src/GraphQl/DataX/UpdateDataMutation.cs create mode 100644 backend/src/GraphQl/MutationBase.cs delete mode 100644 backend/src/GraphQl/MutationsBase.cs create mode 100644 backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs create mode 100644 backend/src/Result.cs diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index d4f3286a..21990615 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,19 +1,29 @@ using System; using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; using static Database.ApiRequests.QueryCurrentUser; namespace Database.Authorization; -public static class CommonAuthorization +public sealed class CommonAuthorization( + UserService userService +) { - public static bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( + public Task GetCurrentUserAsync(CancellationToken cancellationToken) + { + return userService.GetCurrentUser(cancellationToken); + } + + public bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( CurrentUser currentUser - ) + ) { return currentUser.DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; } - public static bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( + public bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( CurrentUser currentUser, Guid institutionId ) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 2e123dc6..f2d6ed6e 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -10,6 +10,7 @@ using OpenIddict.Abstractions; using OpenIddict.Client; using Quartz; +using Database.Authorization; namespace Database.Configuration; @@ -33,6 +34,14 @@ AppSettings appSettings ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); ConfigureOpenIddictServices(services, environment, appSettings, encryptionCertificate, signingCertificate); + AddAuthorizationServices(services); + } + + private static void AddAuthorizationServices( + IServiceCollection services + ) + { + services.AddScoped(); } private static X509Certificate2 LoadCertificate( diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index c8131ce6..2b863c6f 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -90,6 +90,7 @@ protected override void Dispose(bool disposing) public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, [FromServices] ApplicationDbContext context, + [FromServices] CommonAuthorization authorization, [FromServices] UserService userService, CancellationToken cancellationToken ) @@ -101,7 +102,7 @@ CancellationToken cancellationToken $"User is not authenticated."); return BadRequest(ModelState); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return Unauthorized(); } diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 7cf3fb8e..c3bd4fd2 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Extensions; @@ -46,6 +47,7 @@ public GetHttpsResource( HygrothermalDataId = hygrothermalDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; + AssertThatExactlyOneDataIdIsNonNull(); } public GetHttpsResource( @@ -91,6 +93,28 @@ public GetHttpsResource( OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; GeometricDataId = geometricDataId; + AssertThatExactlyOneDataIdIsNonNull(); + } + + private void AssertThatExactlyOneDataIdIsNonNull() + { + var nonNullDataIdCount = new Guid?[] { + CalorimetricDataId, + HygrothermalDataId, + OpticalDataId, + PhotovoltaicDataId, + GeometricDataId + } + .NotNull() + .Count(); + if (nonNullDataIdCount is 0) + { + throw new InvalidOperationException("All data IDs are null."); + } + if (nonNullDataIdCount >= 2) + { + throw new InvalidOperationException("There is more than 1 non-null data ID."); + } } public string FilePath { get => Path.Combine(FilesDirectoryPath, Id.ToString("D")); } @@ -100,7 +124,7 @@ public GetHttpsResource( public ICollection ArchivedFilesMetaInformation { get; private set; } = []; - // TODO Make sure that at least one ID is always present. In that case `Guid.Empty` should never be used! + // Note that at least one data ID is always present. So `Guid.Empty` will never be used. [NotMapped] public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; diff --git a/backend/src/Extensions/ResultExtensions.cs b/backend/src/Extensions/ResultExtensions.cs new file mode 100644 index 00000000..bbe641fd --- /dev/null +++ b/backend/src/Extensions/ResultExtensions.cs @@ -0,0 +1,91 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Database.Extensions; + +public static class ResultExtensions +{ + public static bool Failed( + this Result result, + [NotNullWhen(false)] out TData? data, + [NotNullWhen(true)] out TError? error + ) + where TData : class + where TError : class + { + switch (result) + { + case Result.Data dataResult: + data = dataResult.Value; + error = null; + return false; + case Result.Error errorResult: + data = null; + error = errorResult.Value; + return true; + default: + throw new InvalidOperationException("Impossible!"); + } + } + + public static bool Failed( + this Result result, + [NotNullWhen(true)] out TError? error + ) + where TError : class + { + switch (result) + { + case Result.Data: + error = null; + return false; + case Result.Error errorResult: + error = errorResult.Value; + return true; + default: + throw new InvalidOperationException("Impossible!"); + } + } + + public static bool Succeeded( + this Result result, + [NotNullWhen(true)] out TData? data, + [NotNullWhen(false)] out TError? error + ) + where TData : class + where TError : class + { + switch (result) + { + case Result.Data dataResult: + data = dataResult.Value; + error = null; + return true; + case Result.Error errorResult: + data = null; + error = errorResult.Value; + return false; + default: + throw new InvalidOperationException("Impossible!"); + } + } + + public static bool Succeeded( + this Result result, + [NotNullWhen(true)] out TData? data + ) + where TData : class + { + switch (result) + { + case Result.Data dataResult: + data = dataResult.Value; + return true; + case Result.Error: + data = null; + return false; + default: + throw new InvalidOperationException("Impossible!"); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs index 94b6454e..6c240a47 100644 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs @@ -16,6 +16,7 @@ public async Task UpdateDataAccessRightsAsync( DataAccessRightsInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -31,7 +32,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateDataAccessRightsPayload( new UpdateDataAccessRightsError( @@ -66,6 +67,7 @@ public async Task AddAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -82,7 +84,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new AddInstitutionAccessRightsPayload( new InstitutionAccessRightsError( @@ -120,6 +122,7 @@ public async Task UpdateAccessRightsAsync( InstitutionAccessRightsInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -136,7 +139,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateAccessRightsPayload( new InstitutionAccessRightsError( diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs index ffde1e24..921cfbfd 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs @@ -18,6 +18,7 @@ public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -35,7 +36,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateCalorimetricDataPayload( new CreateCalorimetricDataError( diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs index 364b5af8..ac296260 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs @@ -40,6 +40,7 @@ public async Task AddDataApprovalAsync( DataApprovalInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, DataApprovalService dataApprovalService, ResponseApprovalService responseApprovalService, SigningService signingService, @@ -60,7 +61,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new AddDataApprovalPayload( new AddDataApprovalError( @@ -296,6 +297,7 @@ public async Task RemoveDataApprovalAsync( DataApprovalInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -311,7 +313,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new RemoveDataApprovalPayload( new RemoveDataApprovalError( diff --git a/backend/src/GraphQl/DataX/DataMutationBase.cs b/backend/src/GraphQl/DataX/DataMutationBase.cs new file mode 100644 index 00000000..69432353 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataMutationBase.cs @@ -0,0 +1,73 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GraphQL.Client.Abstractions.Utilities; +using Database.Data; +using Database.Enumerations; +using Database.Services; + +namespace Database.GraphQl.DataX; + +public interface IIdentifyDataInput +{ + public Guid DataId { get; } + public DataKind DataKind { get; } +} + +public abstract class DataMutationBase +: MutationBase +where TData : class, IData +where TPayload : class +{ + protected async Task> FetchDataAsync( + IIdentifyDataInput input, + TErrorCode unknownDataErrorCode, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); + if (data is null) + { + return new Result.Error( + NewPayload(null, [ + NewError( + unknownDataErrorCode, + $"Data with ID '{input.DataId}' and kind '{input.DataKind}' was not found.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ]) + ); + } + return new Result.Data(data); + } + + protected async Task> CreateResponseApprovalAsync( + TData data, + TErrorCode creatingResponseApprovalFailedErrorCode, + ResponseApprovalService responseApprovalService, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + return new Result.Error( + NewPayload( + data, + [NewError( + creatingResponseApprovalFailedErrorCode, + $"Signing failed with message: {exception.Message}", + [] + )] + ) + ); + } + return new Result.Data(true); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataMutations.cs b/backend/src/GraphQl/DataX/DataMutations.cs deleted file mode 100644 index 39bd5b7a..00000000 --- a/backend/src/GraphQl/DataX/DataMutations.cs +++ /dev/null @@ -1,410 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Enumerations; -using Database.Services; -using Database.Utilities; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate; -using HotChocolate.Types; -using static Database.ApiRequests.QueryCurrentUser; - -namespace Database.GraphQl.DataX; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DataMutations -: DataMutationsBase -{ - // public sealed record CreateDataInput( - // [GraphQLType>] string Locale, - // DataKind DataKind, - // Guid ComponentId, - // string? Name, - // string? Description, - // string[] Warnings, - // DateTime CreatedAt, - // Guid CreatorId, - // AppliedMethodInput AppliedMethod, - // RootGetHttpsResourceInput RootResource - // ); - - // [SuppressMessage("Naming", "CA1707")] - // public enum CreateDataErrorCode - // { - // UNAUTHENTICATED, - // UNAUTHORIZED, - // UNKNOWN_DATA, - // CREATING_RESPONSE_APPROVAL_FAILED - // } - - // public sealed record CreateDataError( - // CreateDataErrorCode Code, - // string Message, - // IReadOnlyList Path - // ) : UserErrorBase(Code, Message, Path); - - // public sealed record CreateDataPayload( - // IData? Data, - // IReadOnlyCollection? Errors - // ); - - // public Task CreateDataAsync( - // CreateDataInput input, - // ApplicationDbContext context, - // UserService userService, - // ResponseApprovalService responseApprovalService, - // CancellationToken cancellationToken - // ) - // { - // static CreateDataPayload NewPayload( - // IData? data, - // IReadOnlyCollection? errors - // ) => new(data, errors); - // static CreateDataError NewError( - // CreateDataErrorCode code, - // string message, - // IReadOnlyList path - // ) => new(code, message, path); - // return Authorize( - // unauthenticatedErrorCode: CreateDataErrorCode.UNAUTHENTICATED, - // unauthorizedErrorCode: CreateDataErrorCode.UNAUTHORIZED, - // NewPayload, - // NewError, - // userService, - // then: async currentUser => - // { - // var data = DataFactory.Create( - // input.DataKind, - // currentUser.Uuid, - // input.Locale, - // input.ComponentId, - // input.Name, - // input.Description, - // input.Warnings, - // input.CreatorId, - // input.CreatedAt, - // new AppliedMethod( - // input.AppliedMethod.MethodId, - // input.AppliedMethod.Arguments - // .Select(a => new NamedMethodArgument( - // a.Name, - // a.Value - // )) - // .ToList(), - // input.AppliedMethod.Sources - // .Select(s => new NamedMethodSource( - // s.Name, - // new CrossDatabaseDataReference( - // s.Value.DataId, - // s.Value.DataTimestamp, - // s.Value.DataKind, - // s.Value.DatabaseId - // ) - // )) - // .ToList() - // ) - // ); - // var resource = new GetHttpsResource( - // input.RootResource.Description, - // Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - // input.RootResource.DataFormatId, - // null, - // input.RootResource.ArchivedFilesMetaInformation.Select(i => - // new FileMetaInformation( - // i.Path, - // i.DataFormatId - // ) - // ).ToList(), - // input.RootResource.AppliedConversionMethod is null - // ? null - // : new ToTreeVertexAppliedConversionMethod( - // input.RootResource.AppliedConversionMethod.MethodId, - // input.RootResource.AppliedConversionMethod.Arguments.Select(a => - // new NamedMethodArgument( - // a.Name, - // a.Value - // ) - // ).ToList(), - // input.RootResource.AppliedConversionMethod.SourceName - // ) - // ); - // data.Resources.Add(resource); - // switch (input.DataKind) - // { - // case DataKind.CALORIMETRIC_DATA: - // context.CalorimetricData.Add(data); - // break; - // case DataKind.GEOMETRIC_DATA: - // context.GeometricData.Add(data); - // break; - // case DataKind.HYGROTHERMAL_DATA: - // context.HygrothermalData.Add(data); - // break; - // case DataKind.OPTICAL_DATA: - // context.OpticalData.Add(data); - // break; - // case DataKind.PHOTOVOLTAIC_DATA: - // context.PhotovoltaicData.Add(data); - // break; - // default: - // throw new NotSupportedException($"Unsupported data kind {input.DataKind}."); - // } - // await context.SaveChangesAsync(cancellationToken); - // try - // { - // data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - // await context.SaveChangesAsync(cancellationToken); - // } - // catch (Exception exception) - // { - // context.Remove(data); - // await context.SaveChangesAsync(cancellationToken); - // return NewPayload( - // data, - // [NewError( - // CreateDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - // $"Signing failed with message: {exception.Message}", - // [] - // )] - // ); - // } - // return NewPayload(data, null); - // }, - // cancellationToken - // ); - // } - - public sealed record UpdateDataInput( - [GraphQLType>] string Locale, - Guid DataId, - DataKind DataKind, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId - ) : IChangeDataInput; - - [SuppressMessage("Naming", "CA1707")] - public enum UpdateDataErrorCode - { - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - CREATING_RESPONSE_APPROVAL_FAILED - } - - public sealed record UpdateDataError( - UpdateDataErrorCode Code, - string Message, - IReadOnlyList Path - ) : UserErrorBase(Code, Message, Path); - - public sealed record UpdateDataPayload( - IData? data, - IReadOnlyCollection? Errors - ); - - public Task UpdateDataAsync( - UpdateDataInput input, - ApplicationDbContext context, - UserService userService, - ResponseApprovalService responseApprovalService, - CancellationToken cancellationToken - ) - { - static UpdateDataPayload NewPayload( - IData? data, - IReadOnlyCollection? errors - ) => new(data, errors); - static UpdateDataError NewError( - UpdateDataErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - return AuthorizeAsync( - unauthenticatedErrorCode: UpdateDataErrorCode.UNAUTHENTICATED, - unauthorizedErrorCode: UpdateDataErrorCode.UNAUTHORIZED, - errors => NewPayload(null, errors), - NewError, - userService, - then: currentUser => FetchDataAsync( - input, - unknownDataErrorCode: UpdateDataErrorCode.UNKNOWN_DATA, - NewPayload, - NewError, - context, - then: async data => - { - var rememberedValues = new UpdateDataInput( - data.Locale, - input.DataId, - input.DataKind, - data.ComponentId, - data.Name, - data.Description, - data.Warnings, - data.CreatedAt, - data.CreatorId - ); - return await ActAndThenCreateResponseApprovalAsync( - UpdateDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - NewPayload, - NewError, - context, - responseApprovalService, - act: async () => - { - data.Update( - input.Locale, - input.ComponentId, - input.Name, - input.Description, - input.Warnings, - input.CreatedAt, - input.CreatorId - ); - await context.SaveChangesAsync(cancellationToken); - return data; - }, - undo: async data => - { - data.Update( - rememberedValues.Locale, - rememberedValues.ComponentId, - rememberedValues.Name, - rememberedValues.Description, - rememberedValues.Warnings, - rememberedValues.CreatedAt, - rememberedValues.CreatorId - ); - await context.SaveChangesAsync(cancellationToken); - }, - cancellationToken - ); - }, - cancellationToken - ), - cancellationToken - ); - } - - public sealed record DeleteDataInput( - Guid DataId, - DataKind DataKind - ) : IChangeDataInput; - - [SuppressMessage("Naming", "CA1707")] - public enum DeleteDataErrorCode - { - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA - } - - public sealed record DeleteDataError( - DeleteDataErrorCode Code, - string Message, - IReadOnlyList Path - ) : UserErrorBase(Code, Message, Path); - - public sealed record DeleteDataPayload( - IReadOnlyCollection? Errors - ); - - public Task DeleteDataAsync( - DeleteDataInput input, - ApplicationDbContext context, - UserService userService, - CancellationToken cancellationToken - ) - { - static DeleteDataPayload NewPayload( - IData? data, - IReadOnlyCollection? errors - ) => new(errors); - static DeleteDataError NewError( - DeleteDataErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - return AuthorizeAsync( - unauthenticatedErrorCode: DeleteDataErrorCode.UNAUTHENTICATED, - unauthorizedErrorCode: DeleteDataErrorCode.UNAUTHORIZED, - errors => NewPayload(null, errors), - NewError, - userService, - then: currentUser => FetchDataAsync( - input, - unknownDataErrorCode: DeleteDataErrorCode.UNKNOWN_DATA, - NewPayload, - NewError, - context, - then: async data => - { - switch (data) - { - case CalorimetricData calorimetricData: - context.CalorimetricData.Remove(calorimetricData); - break; - case GeometricData geometricData: - context.GeometricData.Remove(geometricData); - break; - case HygrothermalData hygrothermalData: - context.HygrothermalData.Remove(hygrothermalData); - break; - case PhotovoltaicData photovoltaicData: - context.PhotovoltaicData.Remove(photovoltaicData); - break; - case OpticalData opticalData: - context.OpticalData.Remove(opticalData); - break; - default: - throw new NotSupportedException($"Data kind '{input.DataKind}' is not supported for deletion."); - } - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data, null); - }, - cancellationToken - ), - cancellationToken - ); - } - - // public async Task ExtractMirroredValuesFromFilesAsync( - // ExtractIntegralValuesFromFilesInput input, - // ApplicationDbContext context, - // UserService userService, - // ResponseApprovalService responseApprovalService, - // CancellationToken cancellationToken - // ) - // { - // var currentUser = await userService.GetCurrentUser(cancellationToken); - // if (currentUser is null) - // { - // return new ExtractIntegralValuesFromFilesPayload( - // new ExtractIntegralValuesFromFilesError( - // ExtractIntegralValuesFromFilesErrorCode.UNAUTHENTICATED, - // $"The user is not authenticated.", - // [] - // ) - // ); - // } - // if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - // { - // return new ExtractIntegralValuesFromFilesPayload( - // new ExtractIntegralValuesFromFilesError( - // ExtractIntegralValuesFromFilesErrorCode.UNAUTHORIZED, - // $"The current user is not authorized to set mirrored values from files in this database.", - // [] - // ) - // ); - // } - // } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataMutationsBase.cs b/backend/src/GraphQl/DataX/DataMutationsBase.cs deleted file mode 100644 index a53b0776..00000000 --- a/backend/src/GraphQl/DataX/DataMutationsBase.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Database.Enumerations; -using Database.Services; -using GraphQL.Client.Abstractions.Utilities; - -namespace Database.GraphQl.DataX; - -public abstract class DataMutationsBase -: MutationsBase -{ - public async Task ActAndThenCreateResponseApprovalAsync( - TErrorCode creatingResponseApprovalFailedErrorCode, - Func?, TPayload> newPayload, - Func, TError> newError, - ApplicationDbContext context, - ResponseApprovalService responseApprovalService, - Func> act, - Func undo, - CancellationToken cancellationToken - ) where TData : IData - { - var data = await act(); - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) - { - await undo(data); - return newPayload( - data, - [newError( - creatingResponseApprovalFailedErrorCode, - $"Signing failed with message: {exception.Message}", - [] - )] - ); - } - return newPayload(data, null); - } - - protected interface IChangeDataInput - { - public Guid DataId { get; } - public DataKind DataKind { get; } - } - - protected static async Task FetchDataAsync( - IChangeDataInput input, - TErrorCode unknownDataErrorCode, - Func?, TPayload> newPayload, - Func, TError> newError, - ApplicationDbContext context, - Func> then, - CancellationToken cancellationToken - ) - { - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) - { - return newPayload(null, [ - newError( - unknownDataErrorCode, - $"Data with ID '{input.DataId}' and kind '{input.DataKind}' was not found.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ]); - } - return await then(data); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs new file mode 100644 index 00000000..428b88fc --- /dev/null +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate.Types; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Extensions; + +namespace Database.GraphQl.DataX; + +public sealed record DeleteDataInput( + Guid DataId, + DataKind DataKind +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataErrorCode +{ + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record DeleteDataError( + DeleteDataErrorCode Code, + string Message, + IReadOnlyList Path +) : UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataPayload( + IReadOnlyCollection? Errors +); + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteDataMutation +: DataMutationBase +{ + protected override DeleteDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(errors); + + protected override DeleteDataError NewError( + DeleteDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteDataAsync( + DeleteDataInput input, + CommonAuthorization authorization, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteDataErrorCode.UNAUTHENTICATED, + DeleteDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + DeleteDataErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + switch (data) + { + case CalorimetricData calorimetricData: + context.CalorimetricData.Remove(calorimetricData); + break; + case GeometricData geometricData: + context.GeometricData.Remove(geometricData); + break; + case HygrothermalData hygrothermalData: + context.HygrothermalData.Remove(hygrothermalData); + break; + case PhotovoltaicData photovoltaicData: + context.PhotovoltaicData.Remove(photovoltaicData); + break; + case OpticalData opticalData: + context.OpticalData.Remove(opticalData); + break; + default: + throw new NotSupportedException($"The data kind '{input.DataKind}' is not supported for deletion."); + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/ExtractIntegralValuesFromFile.cs b/backend/src/GraphQl/DataX/ExtractIntegralValuesFromFile.cs new file mode 100644 index 00000000..a6c82641 --- /dev/null +++ b/backend/src/GraphQl/DataX/ExtractIntegralValuesFromFile.cs @@ -0,0 +1,32 @@ + + +// public async Task ExtractMirroredValuesFromFilesAsync( +// ExtractIntegralValuesFromFilesInput input, +// ApplicationDbContext context, +// UserService userService, +// ResponseApprovalService responseApprovalService, +// CancellationToken cancellationToken +// ) +// { +// var currentUser = await userService.GetCurrentUser(cancellationToken); +// if (currentUser is null) +// { +// return new ExtractIntegralValuesFromFilesPayload( +// new ExtractIntegralValuesFromFilesError( +// ExtractIntegralValuesFromFilesErrorCode.UNAUTHENTICATED, +// $"The user is not authenticated.", +// [] +// ) +// ); +// } +// if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) +// { +// return new ExtractIntegralValuesFromFilesPayload( +// new ExtractIntegralValuesFromFilesError( +// ExtractIntegralValuesFromFilesErrorCode.UNAUTHORIZED, +// $"The current user is not authorized to set mirrored values from files in this database.", +// [] +// ) +// ); +// } +// } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index 12d268c3..81b5ac54 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -13,5 +13,10 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(x => x.CalorimetricData).Ignore(); + descriptor.Field(x => x.GeometricData).Ignore(); + descriptor.Field(x => x.HygrothermalData).Ignore(); + descriptor.Field(x => x.OpticalData).Ignore(); + descriptor.Field(x => x.PhotovoltaicData).Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs new file mode 100644 index 00000000..da7f36dc --- /dev/null +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Types; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Extensions; +using Database.Services; + +namespace Database.GraphQl.DataX; + +public sealed record UpdateDataInput( + [GraphQLType>] string Locale, + Guid DataId, + DataKind DataKind, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateDataErrorCode +{ + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record UpdateDataError( + UpdateDataErrorCode Code, + string Message, + IReadOnlyList Path +) : UserErrorBase(Code, Message, Path); + +public sealed record UpdateDataPayload( + IData? Data, + IReadOnlyCollection? Errors +); + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateDataMutation +: DataMutationBase +{ + protected override UpdateDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateDataError NewError( + UpdateDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UpdateDataAsync( + UpdateDataInput input, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UpdateDataErrorCode.UNAUTHENTICATED, + UpdateDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + UpdateDataErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + var rememberedValues = new UpdateDataInput( + data.Locale, + input.DataId, + input.DataKind, + data.ComponentId, + data.Name, + data.Description, + data.Warnings, + data.CreatedAt, + data.CreatorId + ); + data.Update( + input.Locale, + input.ComponentId, + input.Name, + input.Description, + input.Warnings, + input.CreatedAt, + input.CreatorId + ); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + data, + UpdateDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + data.Update( + rememberedValues.Locale, + rememberedValues.ComponentId, + rememberedValues.Name, + rememberedValues.Description, + rememberedValues.Warnings, + rememberedValues.CreatedAt, + rememberedValues.CreatorId + ); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(data, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs index 45e1efa0..478d2b65 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs @@ -19,6 +19,7 @@ public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -35,7 +36,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateGeometricDataPayload( new CreateGeometricDataError( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 830cac46..c74b71ad 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -18,5 +18,11 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.AppliedConversionMethod); descriptor.Field(x => x.ArchivedFilesMetaInformation); descriptor.Field(x => x.Parent); + // descriptor.Field(x => x.Data); + descriptor.Field(x => x.CalorimetricData); + descriptor.Field(x => x.GeometricData); + descriptor.Field(x => x.HygrothermalData); + descriptor.Field(x => x.OpticalData); + descriptor.Field(x => x.PhotovoltaicData); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs index 9a869a7f..c15d4338 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs @@ -36,6 +36,7 @@ public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -50,7 +51,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateGetHttpsResourcePayload( new CreateGetHttpsResourceError( @@ -120,6 +121,7 @@ public async Task RecomputeGetHttpsR ApplicationDbContext context, QueryContext queryContext, UserService userService, + CommonAuthorization authorization, ILogger logger, CancellationToken cancellationToken ) @@ -135,7 +137,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new RecomputeGetHttpsResourceHashValuesPayload( new RecomputeGetHttpsResourceHashValuesError( diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs index b871aa08..f3d4c6dc 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs @@ -12,6 +12,5 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(RecomputeGetHttpsResourceHashValuesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Data); } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs index f32982c6..00ba123b 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs @@ -18,6 +18,7 @@ public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -34,7 +35,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateHygrothermalDataPayload( new CreateHygrothermalDataError( diff --git a/backend/src/GraphQl/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs new file mode 100644 index 00000000..3bf8037b --- /dev/null +++ b/backend/src/GraphQl/MutationBase.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using static Database.ApiRequests.QueryCurrentUser; + +namespace Database.GraphQl; + +public abstract class MutationBase +where TData : class +where TPayload : class +{ + protected abstract TPayload NewPayload(TData? data, IReadOnlyCollection? errors); + protected abstract TError NewError(TErrorCode errorCode, string message, IReadOnlyList path); + + protected async Task> AuthorizeAsync( + TErrorCode unauthenticatedErrorCode, + TErrorCode unauthorizedErrorCode, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + var currentUser = await authorization.GetCurrentUserAsync(cancellationToken); + if (currentUser is null) + { + return new Result.Error( + NewPayload(null, [ + NewError( + unauthenticatedErrorCode, + "The user is not authenticated.", + [] + ) + ]) + ); + } + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new Result.Error( + NewPayload(null, [ + NewError( + unauthorizedErrorCode, + "The current user is not authorized to perform this mutation in this database.", + [] + ) + ]) + ); + } + return new Result.Data(currentUser); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/MutationsBase.cs b/backend/src/GraphQl/MutationsBase.cs deleted file mode 100644 index 20a17ace..00000000 --- a/backend/src/GraphQl/MutationsBase.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Services; -using static Database.ApiRequests.QueryCurrentUser; - -namespace Database.GraphQl; - -public abstract class MutationsBase -{ - protected static async Task AuthorizeAsync( - TErrorCode unauthenticatedErrorCode, - TErrorCode unauthorizedErrorCode, - Func?, TPayload> newPayload, - Func, TError> newError, - UserService userService, - Func> then, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return newPayload([ - newError( - unauthenticatedErrorCode, - "The user is not authenticated.", - [] - ) - ]); - } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return newPayload([ - newError( - unauthorizedErrorCode, - "The current user is not authorized to perform this mutation in this database.", - [] - ) - ]); - } - return await then(currentUser); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs new file mode 100644 index 00000000..5363a898 --- /dev/null +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -0,0 +1,70 @@ +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using HotChocolate.Types; +using Database.Authorization; +using Database.Extensions; +using Database.Data; +using Database.Services; +using Database.GraphQl.DataX; + +namespace Database.GraphQl.OpticalDataX; + +[ExtendObjectType(nameof(Mutation))] +public sealed class OpticalDataMutations +: DataMutationBase +{ + protected override CreateOpticalDataPayload NewPayload( + OpticalData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateOpticalDataError NewError( + CreateOpticalDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + public async Task CreateOpticalDataAsync( + CreateOpticalDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateOpticalDataErrorCode.UNAUTHENTICATED, + CreateOpticalDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var opticalData = input.ToDomainModel(currentUser.Uuid); + context.OpticalData.Add(opticalData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + opticalData, + CreateOpticalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(opticalData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(opticalData, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs deleted file mode 100644 index d3f29ed7..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataMutations.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using HotChocolate.Types; -using Database.Data; -using Database.Services; -using Database.GraphQl.DataX; -using System.Collections.Generic; - -namespace Database.GraphQl.OpticalDataX; - -[ExtendObjectType(nameof(Mutation))] -public sealed class OpticalDataMutations -: DataMutationsBase -{ - // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] - public Task CreateOpticalDataAsync( - CreateOpticalDataInput input, - ApplicationDbContext context, - UserService userService, - ResponseApprovalService responseApprovalService, - CancellationToken cancellationToken - ) - { - static CreateOpticalDataPayload NewPayload( - OpticalData? data, - IReadOnlyCollection? errors - ) => new(data, errors); - static CreateOpticalDataError NewError( - CreateOpticalDataErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - return AuthorizeAsync( - unauthenticatedErrorCode: CreateOpticalDataErrorCode.UNAUTHENTICATED, - unauthorizedErrorCode: CreateOpticalDataErrorCode.UNAUTHORIZED, - errors => NewPayload(null, errors), - NewError, - userService, - then: currentUser => ActAndThenCreateResponseApprovalAsync( - creatingResponseApprovalFailedErrorCode: CreateOpticalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - NewPayload, - NewError, - context, - responseApprovalService, - act: async () => - { - var opticalData = input.ToDomainModel(currentUser.Uuid); - context.OpticalData.Add(opticalData); - await context.SaveChangesAsync(cancellationToken); - return opticalData; - }, - undo: async opticalData => - { - context.Remove(opticalData); - await context.SaveChangesAsync(cancellationToken); - }, - cancellationToken - ), - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs index 3eaa979c..2f97b01d 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs @@ -18,6 +18,7 @@ public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -35,7 +36,7 @@ CancellationToken cancellationToken ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreatePhotovoltaicDataPayload( new CreatePhotovoltaicDataError( diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs index e0a23fa1..b411edd1 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs @@ -33,6 +33,7 @@ public sealed class ResponseApprovalMutations public async Task CreateResponseApprovalsAsync( ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken @@ -49,7 +50,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new CreateResponseApprovalsPayload( new CreateResponseApprovalsError( @@ -99,6 +100,7 @@ await Parallel.ForEachAsync( public async Task UpdateResponseApprovalsAsync( ApplicationDbContext context, UserService userService, + CommonAuthorization authorization, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken @@ -115,7 +117,7 @@ CancellationToken cancellationToken ) ); } - if (!CommonAuthorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) { return new UpdateResponseApprovalsPayload( new UpdateResponseApprovalsError( diff --git a/backend/src/Result.cs b/backend/src/Result.cs new file mode 100644 index 00000000..a58ac1c5 --- /dev/null +++ b/backend/src/Result.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database; + +public abstract record Result +{ + public sealed record Data(TData Value) + : Result; + + [SuppressMessage("Naming", "CA1716")] + public sealed record Error(TError Value) + : Result; +} \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index ff0264a9..2ac9f766 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -1115,6 +1115,11 @@ input GetHttpsResourcePropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput parent: GetHttpsResourceTreeNonRootVertexFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput } input GetHttpsResourceSortInput { @@ -1472,7 +1477,11 @@ input RecomputeGetHttpsResourceHashValuesFilterInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput parent: GetHttpsResourceTreeNonRootVertexFilterInput - data: DataFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput } input ReferenceInput { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 110173ec..2ac9f766 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -638,7 +638,7 @@ type Query { database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") @@ -792,7 +792,7 @@ input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] or: [AppliedMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListNamedMethodArgumentFilterTypePropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput } @@ -816,7 +816,7 @@ input CalorimetricDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput gValues: FloatsPropositionInput @@ -1002,7 +1002,7 @@ input DataFilterInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput } @@ -1088,7 +1088,7 @@ input GeometricDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput thicknesses: FloatsPropositionInput @@ -1114,7 +1114,12 @@ input GetHttpsResourcePropositionInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput } input GetHttpsResourceSortInput { @@ -1123,7 +1128,7 @@ input GetHttpsResourceSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertSortInput { @@ -1132,7 +1137,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertexFilterInput { @@ -1144,7 +1149,7 @@ input GetHttpsResourceTreeNonRootVertexFilterInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput } input HygrothermalDataPropositionInput { @@ -1158,7 +1163,7 @@ input HygrothermalDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput } @@ -1235,27 +1240,27 @@ input ListFilterInputTypeOfFileMetaInformationPropositionInput { any: Boolean @cost(weight: "10") } -input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListGetHttpsResourceFilterTypePropositionInput { - all: GetHttpsResourcePropositionInput @cost(weight: "10") - none: GetHttpsResourcePropositionInput @cost(weight: "10") - some: GetHttpsResourcePropositionInput @cost(weight: "10") +input ListFilterInputTypeOfGetHttpsResourcePropositionInput { + all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") any: Boolean @cost(weight: "10") } -input ListNamedMethodArgumentFilterTypePropositionInput { +input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") none: NamedMethodArgumentPropositionInput @cost(weight: "10") some: NamedMethodArgumentPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") } +input ListFilterInputTypeOfNamedMethodSourcePropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input ListStandardizerPropositionInput { all: StandardizerPropositionInput @cost(weight: "10") none: StandardizerPropositionInput @cost(weight: "10") @@ -1365,7 +1370,7 @@ input OpticalDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput type: NullableOfOpticalComponentTypePropositionInput @@ -1402,7 +1407,7 @@ input PhotovoltaicDataPropositionInput { createdAt: DateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListGetHttpsResourceFilterTypePropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput id: UuidPropositionInput } @@ -1471,8 +1476,12 @@ input RecomputeGetHttpsResourceHashValuesFilterInput { dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourcePropositionInput - data: DataFilterInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput } input ReferenceInput { @@ -1575,7 +1584,7 @@ input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] or: [ToTreeVertexAppliedConversionMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListNamedMethodArgumentFilterTypePropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput sourceName: StringPropositionInput } From 401d5bee9e5acbcab7383ffaa5c6fdbd173c59ed Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Oct 2025 13:59:11 +0100 Subject: [PATCH 526/878] Be explicit about filter and sort types (because there are multiple options in general and auto-discovery may decide for the wrong one) --- backend/src/Configuration/GraphQlConfiguration.cs | 10 +++++----- .../CalorimetricDataX/CalorimetricDataQueries.cs | 6 +++--- .../src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 6 +++--- .../GetHttpsResources/GetHttpsResourceQueries.cs | 4 ++-- .../HygrothermalDataX/HygrothermalDataQueries.cs | 6 +++--- backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 6 +++--- .../PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 6 +++--- .../__snapshots__/GraphQlSchemaTests.IsUnchanged.snap | 2 +- 8 files changed, 23 insertions(+), 23 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 95b5f487..523da221 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -116,11 +116,11 @@ IWebHostEnvironment environment .AddType(new LocaleType()) // Object Types .AddType() - // Query, Mutation and Subscription Types - .AddQueryType(d => d.Name(nameof(Query))) - .AddMutationType(d => d.Name(nameof(Mutation))) - // .AddSubscriptionType(d => d.Name(nameof(Subscription))) - // auto-discover types using `HotChocolate.Types.Analyzers` + // Query, Mutation, Subscription, Object, and Input Types + .AddQueryType(_ => _.Name(nameof(Query))) + .AddMutationType(_ => _.Name(nameof(Mutation))) + // .AddSubscriptionType(_ => _.Name(nameof(Subscription))) + // auto-discover using `HotChocolate.Types.Analyzers` .AddTypes() // Paging .AddDbContextCursorPagingProvider() diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 8d181bb3..02db30f1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -20,8 +20,8 @@ public sealed class CalorimetricDataQueries [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public async Task> GetAllCalorimetricData( [GraphQLType] string? locale, ApplicationDbContext context, @@ -43,7 +43,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - [UseFiltering(typeof(CalorimetricData))] + [UseFiltering] public Task GetHasCalorimetricData( [GraphQLType] string? locale, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index eb846077..e7e38dc9 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -18,8 +18,8 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataQueries { [UsePaging] - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public async Task> GetAllGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, @@ -41,7 +41,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - [UseFiltering(typeof(GeometricData))] + [UseFiltering] public Task GetHasGeometricData( [GraphQLType] string? locale, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index bd9781da..94688fb8 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -16,8 +16,8 @@ public sealed class GetHttpsResourceQueries { [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public IQueryable GetGetHttpsResources( ApplicationDbContext context, ISortingContext sorting diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 34c2da7a..4d5ea769 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -20,8 +20,8 @@ public sealed class HygrothermalDataQueries [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public async Task> GetAllHygrothermalData( [GraphQLType] string? locale, ApplicationDbContext context, @@ -43,7 +43,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - [UseFiltering(typeof(HygrothermalData))] + [UseFiltering] public Task GetHasHygrothermalData( [GraphQLType] string? locale, ApplicationDbContext context, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 85c6105c..74d98d1c 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -20,8 +20,8 @@ public sealed class OpticalDataQueries [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public async Task> GetAllOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, @@ -43,7 +43,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - [UseFiltering(typeof(OpticalData))] + [UseFiltering] public Task GetHasOpticalData( [GraphQLType] string? locale, ApplicationDbContext context, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 4d72444c..a11f23be 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -20,8 +20,8 @@ public sealed class PhotovoltaicDataQueries [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. - [UseFiltering] - [UseSorting] + [UseFiltering] + [UseSorting] public async Task> GetAllPhotovoltaicData( [GraphQLType] string? locale, ApplicationDbContext context, @@ -43,7 +43,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - [UseFiltering(typeof(PhotovoltaicData))] + [UseFiltering] public Task GetHasPhotovoltaicData( [GraphQLType] string? locale, ApplicationDbContext context, diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 2ac9f766..c931069e 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -638,7 +638,7 @@ type Query { database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") From 923eb5527aa7ba72d1deca36591047fd73e52af3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Oct 2025 14:51:44 +0100 Subject: [PATCH 527/878] Add attempt to create generic error and payload type as described in https://chillicream.com/docs/hotchocolate/v15/defining-a-schema/object-types/#generics --- .../src/GraphQl/DataX/DeleteDataMutation.cs | 2 ++ backend/src/GraphQl/Error.cs | 14 +++++++++++++ backend/src/GraphQl/ErrorType.cs | 20 +++++++++++++++++++ backend/src/GraphQl/Payload.cs | 8 +++++++- backend/src/GraphQl/PayloadType.cs | 18 +++++++++++++++++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 backend/src/GraphQl/Error.cs create mode 100644 backend/src/GraphQl/ErrorType.cs create mode 100644 backend/src/GraphQl/PayloadType.cs diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 428b88fc..e3347487 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -31,6 +31,7 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); public sealed record DeleteDataPayload( + // [GraphQLType>>] IReadOnlyCollection? Errors ); @@ -49,6 +50,7 @@ protected override DeleteDataError NewError( IReadOnlyList path ) => new(code, message, path); + // [GraphQLType>] public async Task DeleteDataAsync( DeleteDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/Error.cs b/backend/src/GraphQl/Error.cs new file mode 100644 index 00000000..e3e525e3 --- /dev/null +++ b/backend/src/GraphQl/Error.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace Database.GraphQl; + +[SuppressMessage("Naming", "CA1716")] +public sealed record Error( + TErrorCode Code, + string Message, + IReadOnlyList Path +) +: IUserError +where TErrorCode : struct, Enum; \ No newline at end of file diff --git a/backend/src/GraphQl/ErrorType.cs b/backend/src/GraphQl/ErrorType.cs new file mode 100644 index 00000000..d76841a3 --- /dev/null +++ b/backend/src/GraphQl/ErrorType.cs @@ -0,0 +1,20 @@ +using System; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class ErrorType + : ObjectType> + where TErrorCode : struct, Enum +{ + protected override void Configure( + IObjectTypeDescriptor> descriptor) + { + descriptor + .Name(dependency => dependency.Name[..^"Code".Length]) + .DependsOn>(); + descriptor + .Field(_ => _.Code) + .Type>(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Payload.cs b/backend/src/GraphQl/Payload.cs index 56fe2b41..9ba644dd 100644 --- a/backend/src/GraphQl/Payload.cs +++ b/backend/src/GraphQl/Payload.cs @@ -1,5 +1,11 @@ +using System.Collections.Generic; + namespace Database.GraphQl; public abstract class Payload { -} \ No newline at end of file +} + +public sealed record Payload( + IReadOnlyCollection Errors +); \ No newline at end of file diff --git a/backend/src/GraphQl/PayloadType.cs b/backend/src/GraphQl/PayloadType.cs new file mode 100644 index 00000000..405b52ba --- /dev/null +++ b/backend/src/GraphQl/PayloadType.cs @@ -0,0 +1,18 @@ +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class PayloadType + : ObjectType> +{ + protected override void Configure( + IObjectTypeDescriptor> descriptor) + { + descriptor + .Name(dependency => dependency.Name[..^"Error".Length] + nameof(Payload)) + .DependsOn>(); + descriptor + .Field(_ => _.Errors) + .Type>(); + } +} \ No newline at end of file From 1c907b0b1c2fd90b613014d7092317bc58710135 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Oct 2025 18:08:43 +0100 Subject: [PATCH 528/878] Put each mutation into its own class alongside with its error, error codes, input, and payload --- backend/src/Data/Entity.cs | 2 +- backend/src/Data/IEntity.cs | 2 +- .../AddInstitutionAccessRightsMutation.cs | 116 ++++++++++++ .../AddInstitutionAccessRightsPayload.cs | 19 -- .../AccessRights/DataAccessRightsInput.cs | 14 -- .../AccessRights/DataAccessRightsMutations.cs | 172 ------------------ .../InstitutionAccessRightsError.cs | 10 - .../InstitutionAccessRightsErrorCode.cs | 13 -- .../InstitutionAccessRightsInput.cs | 14 -- .../AccessRights/UpdateAccessRightsPayload.cs | 19 -- .../UpdateDataAccessRightsError.cs | 10 - .../UpdateDataAccessRightsErrorCode.cs | 12 -- .../UpdateDataAccessRightsMutation.cs | 111 +++++++++++ .../UpdateDataAccessRightsPayload.cs | 21 --- .../UpdateInstitutionAccessRightsMutation.cs | 113 ++++++++++++ .../CreateCalorimetricDataError.cs | 10 - .../CreateCalorimetricDataErrorCode.cs | 12 -- .../CreateCalorimetricDataInput.cs | 44 ----- ...s.cs => CreateCalorimetricDataMutation.cs} | 82 ++++++++- .../CreateCalorimetricDataPayload.cs | 29 --- .../DataApprovals/AddDataApprovalError.cs | 11 -- .../DataApprovals/AddDataApprovalErrorCode.cs | 23 --- ...utations.cs => AddDataApprovalMutation.cs} | 170 +++++++++-------- .../DataApprovals/AddDataApprovalPayload.cs | 37 ---- .../DataApprovals/DataApprovalInput.cs | 20 -- .../DataApprovals/RemoveDataApprovalError.cs | 11 -- .../RemoveDataApprovalErrorCode.cs | 14 -- .../DataApprovals/RemoveDataApprovalInput.cs | 9 - .../RemoveDataApprovalMutation.cs | 152 ++++++++++++++++ .../RemoveDataApprovalPayload.cs | 29 --- ...Mutations.cs => UpdateDatabaseMutation.cs} | 2 +- .../AssociationsByAssociateIdDataLoader.cs | 1 - .../CreateGeometricDataError.cs | 10 - .../CreateGeometricDataErrorCode.cs | 12 -- .../CreateGeometricDataInput.cs | 41 ----- ...ions.cs => CreateGeometricDataMutation.cs} | 79 +++++++- .../CreateGeometricDataPayload.cs | 29 --- .../CreateGetHttpsResourceError.cs | 10 - .../CreateGetHttpsResourceErrorCode.cs | 12 -- .../CreateGetHttpsResourceInput.cs | 15 -- ...s.cs => CreateGetHttpsResourceMutation.cs} | 119 +++++------- .../CreateGetHttpsResourcePayload.cs | 21 --- ...ecomputeGetHttpsResourceHashValuesError.cs | 10 - ...puteGetHttpsResourceHashValuesErrorCode.cs | 12 -- ...mputeGetHttpsResourceHashValuesMutation.cs | 148 +++++++++++++++ ...omputeGetHttpsResourceHashValuesPayload.cs | 34 ---- .../CreateHygrothermalDataError.cs | 10 - .../CreateHygrothermalDataErrorCode.cs | 12 -- .../CreateHygrothermalDataInput.cs | 40 ---- ...s.cs => CreateHygrothermalDataMutation.cs} | 78 +++++++- .../CreateHygrothermalDataPayload.cs | 29 --- .../GraphQl/Methods/CalculateMethodError.cs | 10 - .../Methods/CalculateMethodErrorCode.cs | 11 -- .../GraphQl/Methods/CalculateMethodPayload.cs | 52 ------ backend/src/GraphQl/Methods/MethodQueries.cs | 65 +++++++ .../Numerations/CreateNumerationInput.cs | 7 - ...eNumerationInput.cs => NumerationInput.cs} | 2 +- .../OpticalDataX/CreateOpticalDataError.cs | 10 - .../CreateOpticalDataErrorCode.cs | 12 -- .../OpticalDataX/CreateOpticalDataInput.cs | 60 ------ .../OpticalDataX/CreateOpticalDataMutation.cs | 80 +++++++- .../OpticalDataX/CreateOpticalDataPayload.cs | 10 - .../CreatePhotovoltaicDataError.cs | 10 - .../CreatePhotovoltaicDataErrorCode.cs | 12 -- .../CreatePhotovoltaicDataInput.cs | 40 ---- ...s.cs => CreatePhotovoltaicDataMutation.cs} | 78 +++++++- .../CreatePhotovoltaicDataPayload.cs | 29 --- .../CreateResponseApprovalsError.cs | 10 - .../CreateResponseApprovalsErrorCode.cs | 13 -- ....cs => CreateResponseApprovalsMutation.cs} | 127 ++++++------- .../CreateResponseApprovalsPayload.cs | 33 ---- .../UpdateResponseApprovalsError.cs | 10 - .../UpdateResponseApprovalsErrorCode.cs | 13 -- .../UpdateResponseApprovalsMutation.cs | 140 ++++++++++++++ .../UpdateResponseApprovalsPayload.cs | 33 ---- .../src/GraphQl/Standards/StandardInput.cs | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 147 ++++++++------- frontend/type-defs.graphqls | 149 ++++++++------- 78 files changed, 1597 insertions(+), 1593 deletions(-) create mode 100644 backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs delete mode 100644 backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs delete mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs delete mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs delete mode 100644 backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs delete mode 100644 backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs delete mode 100644 backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs create mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs create mode 100644 backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs rename backend/src/GraphQl/CalorimetricDataX/{CalorimetricDataMutations.cs => CreateCalorimetricDataMutation.cs} (55%) delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs delete mode 100644 backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs delete mode 100644 backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs rename backend/src/GraphQl/DataApprovals/{DataApprovalMutations.cs => AddDataApprovalMutation.cs} (79%) delete mode 100644 backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs delete mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalInput.cs delete mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs delete mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs delete mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs create mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs delete mode 100644 backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs rename backend/src/GraphQl/Databases/{DatabaseMutations.cs => UpdateDatabaseMutation.cs} (96%) delete mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs rename backend/src/GraphQl/GeometricDataX/{GeometricDataMutations.cs => CreateGeometricDataMutation.cs} (57%) delete mode 100644 backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs rename backend/src/GraphQl/GetHttpsResources/{GetHttpsResourceMutations.cs => CreateGetHttpsResourceMutation.cs} (62%) delete mode 100644 backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs rename backend/src/GraphQl/HygrothermalDataX/{HygrothermalDataMutations.cs => CreateHygrothermalDataMutation.cs} (56%) delete mode 100644 backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs delete mode 100644 backend/src/GraphQl/Methods/CalculateMethodError.cs delete mode 100644 backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs delete mode 100644 backend/src/GraphQl/Methods/CalculateMethodPayload.cs delete mode 100644 backend/src/GraphQl/Numerations/CreateNumerationInput.cs rename backend/src/GraphQl/Numerations/{UpdateNumerationInput.cs => NumerationInput.cs} (70%) delete mode 100644 backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs rename backend/src/GraphQl/PhotovoltaicDataX/{PhotovoltaicDataMutations.cs => CreatePhotovoltaicDataMutation.cs} (56%) delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs rename backend/src/GraphQl/ResponseApprovals/{ResponseApprovalMutations.cs => CreateResponseApprovalsMutation.cs} (50%) delete mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index 87016107..b3c54bfe 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -1,4 +1,4 @@ -using Guid = System.Guid; +using System; // using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/IEntity.cs b/backend/src/Data/IEntity.cs index 3e065f5a..4e86955f 100644 --- a/backend/src/Data/IEntity.cs +++ b/backend/src/Data/IEntity.cs @@ -1,4 +1,4 @@ -using Guid = System.Guid; +using System; namespace Database.Data; diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs new file mode 100644 index 00000000..823ae31b --- /dev/null +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public sealed record AddInstitutionAccessRightsInput +( + // Id of institution + Guid InstitutionId, + // Count of allowed user for institution. Null is unlimited + uint? AllowedUserCount, + // Count of allowed datasets for institution. Null is unlimited + uint? AllowedDatasetsPerTimeSpan, + int PeriodInDays +); + +[SuppressMessage("Naming", "CA1707")] +public enum AddInstitutionAccessRightsErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + ALREADY_EXISTS, +} + +public sealed record AddInstitutionAccessRightsError( + AddInstitutionAccessRightsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class AddInstitutionAccessRightsPayload + : InstitutionAccessRightsPayload +{ + public AddInstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights + ) + : base(accessRights) + { + } + + public AddInstitutionAccessRightsPayload( + AddInstitutionAccessRightsError error + ) + : base(error) + { + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class AddInstitutionAccessRightsMutation +{ + public async Task AddInstitutionAccessRightsAsync( + AddInstitutionAccessRightsInput input, + ApplicationDbContext context, + UserService userService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + cancellationToken); + if (currentUser is null) + { + return new AddInstitutionAccessRightsPayload( + new AddInstitutionAccessRightsError( + AddInstitutionAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new AddInstitutionAccessRightsPayload( + new AddInstitutionAccessRightsError( + AddInstitutionAccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to add access rights for the institution.", + [] + ) + ); + } + + var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); + + if (accessRights is not null) + { + return new AddInstitutionAccessRightsPayload( + new AddInstitutionAccessRightsError( + AddInstitutionAccessRightsErrorCode.ALREADY_EXISTS, + $"The access rights for this institution already exist.", + [] + ) + ); + } + + accessRights = new Data.InstitutionAccessRights( + input.InstitutionId, + input.AllowedUserCount, + input.AllowedDatasetsPerTimeSpan, + TimeSpan.FromDays(input.PeriodInDays)); + context.InstitutionAccessRights.Add(accessRights); + await context.SaveChangesAsync(cancellationToken); + return new AddInstitutionAccessRightsPayload(accessRights); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs deleted file mode 100644 index ac2d1530..00000000 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsPayload.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Database.GraphQl.AccessRights; - -public sealed class AddInstitutionAccessRightsPayload - : InstitutionAccessRightsPayload -{ - public AddInstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights - ) - : base(accessRights) - { - } - - public AddInstitutionAccessRightsPayload( - InstitutionAccessRightsError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs deleted file mode 100644 index ae3ec80c..00000000 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsInput.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.Enumerations; - -namespace Database.GraphQl.AccessRights; - -public sealed record DataAccessRightsInput -( - Guid DataId, - DataKind DataKind, - IReadOnlyDictionary? AllowedUserAndQuantity, - IReadOnlyList? AllowedInstitutions, - IReadOnlyList? AllowedApplications -); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs deleted file mode 100644 index 6c240a47..00000000 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsMutations.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Services; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessRights; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DataAccessRightsMutations -{ - public async Task UpdateDataAccessRightsAsync( - DataAccessRightsInput input, - ApplicationDbContext context, - UserService userService, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update access rights for the data.", - [] - ) - ); - } - - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) - { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [] - ) - ); - } - - data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; - data.DataAccessRights.AllowedApplications = input.AllowedApplications; - data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; - - await context.SaveChangesAsync(cancellationToken); - return new UpdateDataAccessRightsPayload(data.DataAccessRights); - } - - public async Task AddAccessRightsAsync( - InstitutionAccessRightsInput input, - ApplicationDbContext context, - UserService userService, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) - { - return new AddInstitutionAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new AddInstitutionAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add access rights for the institution.", - [] - ) - ); - } - - var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); - - if (accessRights is not null) - { - return new AddInstitutionAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.ALREADY_EXISTS, - $"The access rights for this institution already exist.", - [] - ) - ); - } - - accessRights = new Data.InstitutionAccessRights( - input.InstitutionId, - input.AllowedUserCount, - input.AllowedDatasetsPerTimeSpan, - TimeSpan.FromDays(input.PeriodInDays)); - context.InstitutionAccessRights.Add(accessRights); - await context.SaveChangesAsync(cancellationToken); - return new AddInstitutionAccessRightsPayload(accessRights); - } - - public async Task UpdateAccessRightsAsync( - InstitutionAccessRightsInput input, - ApplicationDbContext context, - UserService userService, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) - { - return new UpdateAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new UpdateAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update access rights for the institution.", - [] - ) - ); - } - - var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); - - if (accessRights is null) - { - return new UpdateAccessRightsPayload( - new InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode.UNKNOWN_ACCESS_RIGHTS, - $"There are no access rights for this institution.", - [] - ) - ); - } - - accessRights.AllowedUserCount = input.AllowedUserCount; - accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); - await context.SaveChangesAsync(cancellationToken); - return new UpdateAccessRightsPayload(accessRights); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs deleted file mode 100644 index 70496d01..00000000 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.AccessRights; - -public sealed record InstitutionAccessRightsError( - InstitutionAccessRightsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs deleted file mode 100644 index a2b92fa0..00000000 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsErrorCode.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.AccessRights; - -[SuppressMessage("Naming", "CA1707")] -public enum InstitutionAccessRightsErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - ALREADY_EXISTS, - UNKNOWN_ACCESS_RIGHTS -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs deleted file mode 100644 index de9269e0..00000000 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsInput.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Database.GraphQl.AccessRights; - -public sealed record InstitutionAccessRightsInput -( - // Id of institution - Guid InstitutionId, - // Count of allowed user for institution. Null is unlimited - uint? AllowedUserCount, - // Count of allowed datasets for institution. Null is unlimited - uint? AllowedDatasetsPerTimeSpan, - int PeriodInDays -); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs deleted file mode 100644 index 6566feea..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateAccessRightsPayload.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Database.GraphQl.AccessRights; - -public sealed class UpdateAccessRightsPayload - : InstitutionAccessRightsPayload -{ - public UpdateAccessRightsPayload( - Data.InstitutionAccessRights accessRights - ) - : base(accessRights) - { - } - - public UpdateAccessRightsPayload( - InstitutionAccessRightsError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs deleted file mode 100644 index 68dfbc77..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.AccessRights; - -public sealed record UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs deleted file mode 100644 index cb6842ae..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.AccessRights; - -[SuppressMessage("Naming", "CA1707")] -public enum UpdateDataAccessRightsErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs new file mode 100644 index 00000000..5b0e44b2 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -0,0 +1,111 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Types; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +using Database.Enumerations; + +namespace Database.GraphQl.AccessRights; + +public sealed record UpdateDataAccessRightsInput +( + Guid DataId, + DataKind DataKind, + IReadOnlyDictionary? AllowedUserAndQuantity, + IReadOnlyList? AllowedInstitutions, + IReadOnlyList? AllowedApplications +); + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateDataAccessRightsErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class UpdateDataAccessRightsPayload + : DataAccessRightsPayload +{ + public UpdateDataAccessRightsPayload( + DataAccessRights dataAccessRights + ) + : base(dataAccessRights) + { + } + + public UpdateDataAccessRightsPayload( + UpdateDataAccessRightsError error + ) + : base(error) + { + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateDataAccessRightsMutation +{ + public async Task UpdateDataAccessRightsAsync( + UpdateDataAccessRightsInput input, + ApplicationDbContext context, + UserService userService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update access rights for the data.", + [] + ) + ); + } + + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); + if (data is null) + { + return new UpdateDataAccessRightsPayload( + new UpdateDataAccessRightsError( + UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [] + ) + ); + } + + data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; + data.DataAccessRights.AllowedApplications = input.AllowedApplications; + data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; + + await context.SaveChangesAsync(cancellationToken); + return new UpdateDataAccessRightsPayload(data.DataAccessRights); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs deleted file mode 100644 index a864fd16..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsPayload.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.AccessRights; - -public sealed class UpdateDataAccessRightsPayload - : DataAccessRightsPayload -{ - public UpdateDataAccessRightsPayload( - DataAccessRights dataAccessRights - ) - : base(dataAccessRights) - { - } - - public UpdateDataAccessRightsPayload( - UpdateDataAccessRightsError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs new file mode 100644 index 00000000..f540b0c2 --- /dev/null +++ b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs @@ -0,0 +1,113 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; + +namespace Database.GraphQl.AccessRights; + +public sealed record UpdateInstitutionAccessRightsInput +( + // Id of institution + Guid InstitutionId, + // Count of allowed user for institution. Null is unlimited + uint? AllowedUserCount, + // Count of allowed datasets for institution. Null is unlimited + uint? AllowedDatasetsPerTimeSpan, + int PeriodInDays +); + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateInstitutionAccessRightsErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_ACCESS_RIGHTS +} + +public sealed record UpdateInstitutionAccessRightsError( + UpdateInstitutionAccessRightsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class UpdateInstitutionAccessRightsPayload + : InstitutionAccessRightsPayload +{ + public UpdateInstitutionAccessRightsPayload( + Data.InstitutionAccessRights accessRights + ) + : base(accessRights) + { + } + + public UpdateInstitutionAccessRightsPayload( + UpdateInstitutionAccessRightsError error + ) + : base(error) + { + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateInstitutionAccessRightsMutation +{ + public async Task UpdateInstitutionAccessRightsAsync( + UpdateInstitutionAccessRightsInput input, + ApplicationDbContext context, + UserService userService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser( + cancellationToken); + if (currentUser is null) + { + return new UpdateInstitutionAccessRightsPayload( + new UpdateInstitutionAccessRightsError( + UpdateInstitutionAccessRightsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new UpdateInstitutionAccessRightsPayload( + new UpdateInstitutionAccessRightsError( + UpdateInstitutionAccessRightsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update access rights for the institution.", + [] + ) + ); + } + + var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); + + if (accessRights is null) + { + return new UpdateInstitutionAccessRightsPayload( + new UpdateInstitutionAccessRightsError( + UpdateInstitutionAccessRightsErrorCode.UNKNOWN_ACCESS_RIGHTS, + $"There are no access rights for this institution.", + [] + ) + ); + } + + accessRights.AllowedUserCount = input.AllowedUserCount; + accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; + accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); + await context.SaveChangesAsync(cancellationToken); + return new UpdateInstitutionAccessRightsPayload(accessRights); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs deleted file mode 100644 index a371a6c4..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.CalorimetricDataX; - -public sealed record CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs deleted file mode 100644 index f25cc94f..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.CalorimetricDataX; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateCalorimetricDataErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs deleted file mode 100644 index fdf4235c..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataInput.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using Database.Enumerations; -using Database.Data; - -namespace Database.GraphQl.CalorimetricDataX; - -public sealed record CreateCalorimetricDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, - double[] GValues, - double[] UValues -) -{ - public CalorimetricData ToDomainModel(Guid userId) - { - var calorimetricData = new CalorimetricData( - userId, - Locale, - ComponentId, - Name, - Description, - Warnings, - CreatorId, - CreatedAt, - AppliedMethod.ToDomainModel(), - GValues, - UValues - ); - calorimetricData.Resources.Add(RootResource.ToDomainModel()); - return calorimetricData; - } -}; \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs similarity index 55% rename from backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs rename to backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 921cfbfd..9b91c10e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataMutations.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -7,11 +7,91 @@ using Database.Data; using Database.Services; using Database.Utilities; +using HotChocolate; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; namespace Database.GraphQl.CalorimetricDataX; +public sealed record CreateCalorimetricDataInput( + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, + double[] GValues, + double[] UValues +) +{ + public CalorimetricData ToDomainModel(Guid userId) + { + var calorimetricData = new CalorimetricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + GValues, + UValues + ); + calorimetricData.Resources.Add(RootResource.ToDomainModel()); + return calorimetricData; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateCalorimetricDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateCalorimetricDataError( + CreateCalorimetricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CreateCalorimetricDataPayload + : CalorimetricDataPayload +{ + public CreateCalorimetricDataPayload( + CalorimetricData calorimetricData + ) + : base(calorimetricData) + { + } + + public CreateCalorimetricDataPayload( + CreateCalorimetricDataError error + ) + : base(error) + { + } + + public CreateCalorimetricDataPayload( + CalorimetricData calorimetricData, + CreateCalorimetricDataError error + ) + : base(calorimetricData, error) + { + } +} + [ExtendObjectType(nameof(Mutation))] -public sealed class CalorimetricDataMutations +public sealed class CreateCalorimetricDataMutation { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateCalorimetricDataAsync( diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs deleted file mode 100644 index 197a9979..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.CalorimetricDataX; - -public sealed class CreateCalorimetricDataPayload - : CalorimetricDataPayload -{ - public CreateCalorimetricDataPayload( - CalorimetricData calorimetricData - ) - : base(calorimetricData) - { - } - - public CreateCalorimetricDataPayload( - CreateCalorimetricDataError error - ) - : base(error) - { - } - - public CreateCalorimetricDataPayload( - CalorimetricData calorimetricData, - CreateCalorimetricDataError error - ) - : base(calorimetricData, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs deleted file mode 100644 index 7ec893ba..00000000 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalError.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Database.GraphQl.GeometricDataX; -using System.Collections.Generic; - -namespace Database.GraphQl.DataApprovals; - -public sealed record AddDataApprovalError( - AddDataApprovalErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs deleted file mode 100644 index 7dd6c382..00000000 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalErrorCode.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.DataApprovals; - -[SuppressMessage("Naming", "CA1707")] -public enum AddDataApprovalErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - CREATING_RESPONSE_APPROVAL_FAILED, - AMBIGUOUS_STATEMENT, - MISSING_STATEMENT, - INVALID_KEY_FINGERPRINT, - FAILED_RECEIVING_KEY, - BAD_SIGNATURE, - FAILED_VERIFYING_SIGNATURE, - FAILED_VERIFYING_RESPONSE, - ILLEGAL_MESSAGE, - WRONG_RESPONSE, - WRONG_STATEMENT -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs similarity index 79% rename from backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs rename to backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index ac296260..bb734540 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalMutations.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -13,22 +13,98 @@ using Database.Services; using GraphQL; using GraphQL.Client.Abstractions.Utilities; -using GreenDonut.Data; using HotChocolate.Types; using Microsoft.Extensions.Logging; +using System.Diagnostics.CodeAnalysis; +using Database.Enumerations; +using Database.GraphQl.References; namespace Database.GraphQl.DataApprovals; -public static partial class DataApprovalMutationsLogging +public sealed record AddDataApprovalInput +( + Guid DataId, + DataKind DataKind, + DateTime Timestamp, + string Signature, + string KeyFingerprint, + string Query, + JsonElement Variables, + string Message, + Guid ApproverId, + ReferenceInput Statement +); + +[SuppressMessage("Naming", "CA1707")] +public enum AddDataApprovalErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED, + AMBIGUOUS_STATEMENT, + MISSING_STATEMENT, + INVALID_KEY_FINGERPRINT, + FAILED_RECEIVING_KEY, + BAD_SIGNATURE, + FAILED_VERIFYING_SIGNATURE, + FAILED_VERIFYING_RESPONSE, + ILLEGAL_MESSAGE, + WRONG_RESPONSE, + WRONG_STATEMENT +} + +public sealed record AddDataApprovalError( + AddDataApprovalErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class AddDataApprovalPayload + : DataApprovalPayload +{ + public AddDataApprovalPayload( + DataApproval dataApproval + ) + : base(dataApproval) + { + } + + public AddDataApprovalPayload( + AddDataApprovalError error + ) + : base(error) + { + } + + public AddDataApprovalPayload( + IReadOnlyCollection errors + ) + : base(errors) + { + } + + public AddDataApprovalPayload( + DataApproval dataApproval, + AddDataApprovalError error + ) + : base(dataApproval, error) + { + } +} + +public static partial class AddDataApprovalMutationLogging { [LoggerMessage( Level = LogLevel.Warning, Message = "Unknown signature verification result: {SigantureVerificationResult}")] - public static partial void UnknownSignatureVerificationResult(this ILogger logger, SigantureVerificationResult sigantureVerificationResult); + public static partial void UnknownSignatureVerificationResult(this ILogger logger, SigantureVerificationResult sigantureVerificationResult); } [ExtendObjectType(nameof(Mutation))] -public sealed class DataApprovalMutations +public sealed class AddDataApprovalMutation { private sealed record Message( JsonElement Statement, @@ -37,7 +113,7 @@ JsonElement Response //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task AddDataApprovalAsync( - DataApprovalInput input, + AddDataApprovalInput input, ApplicationDbContext context, UserService userService, CommonAuthorization authorization, @@ -45,7 +121,7 @@ public async Task AddDataApprovalAsync( ResponseApprovalService responseApprovalService, SigningService signingService, ApiRequestService apiRequestService, - ILogger logger, + ILogger logger, AppSettings appSettings, CancellationToken cancellationToken ) @@ -292,86 +368,4 @@ CancellationToken cancellationToken } return new AddDataApprovalPayload(approval); } - - public async Task RemoveDataApprovalAsync( - DataApprovalInput input, - ApplicationDbContext context, - UserService userService, - CommonAuthorization authorization, - ResponseApprovalService responseApprovalService, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add data approvals to this database.", - [] - ) - ); - } - - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) - { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); - } - - var approval = data.Approvals - .Where(a => a.Signature.Trim() == input.Signature.Trim()) - .SingleOrDefault(); - if (approval is null) - { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNKNOWN_APPROVAL, - $"Unknown data approval.", - [nameof(input), nameof(input.Signature).ToLowerFirst()] - ) - ); - } - - data.Approvals.Remove(approval); - await context.SaveChangesAsync(cancellationToken); - - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) - { - data.Approvals.Add(approval); - await context.SaveChangesAsync(cancellationToken); - - return new RemoveDataApprovalPayload( - approval, - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); - } - return new RemoveDataApprovalPayload(approval); - } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs deleted file mode 100644 index 4b23e83d..00000000 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalPayload.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.DataApprovals; - -public sealed class AddDataApprovalPayload - : DataApprovalPayload -{ - public AddDataApprovalPayload( - DataApproval dataApproval - ) - : base(dataApproval) - { - } - - public AddDataApprovalPayload( - AddDataApprovalError error - ) - : base(error) - { - } - - public AddDataApprovalPayload( - IReadOnlyCollection errors - ) - : base(errors) - { - } - - public AddDataApprovalPayload( - DataApproval dataApproval, - AddDataApprovalError error - ) - : base(dataApproval, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs deleted file mode 100644 index a1cb47f5..00000000 --- a/backend/src/GraphQl/DataApprovals/DataApprovalInput.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Text.Json; -using Database.Enumerations; -using Database.GraphQl.References; - -namespace Database.GraphQl.DataApprovals; - -public sealed record DataApprovalInput -( - Guid DataId, - DataKind DataKind, - DateTime Timestamp, - string Signature, - string KeyFingerprint, - string Query, - JsonElement Variables, - string Message, - Guid ApproverId, - ReferenceInput Statement -); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs deleted file mode 100644 index 802b026c..00000000 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalError.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Database.GraphQl.GeometricDataX; -using System.Collections.Generic; - -namespace Database.GraphQl.DataApprovals; - -public sealed record RemoveDataApprovalError( - RemoveDataApprovalErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs deleted file mode 100644 index 3545d052..00000000 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalErrorCode.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.DataApprovals; - -[SuppressMessage("Naming", "CA1707")] -public enum RemoveDataApprovalErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_APPROVAL, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs deleted file mode 100644 index 2722b57b..00000000 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalInput.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Database.GraphQl.DataApprovals; - -public sealed record RemoveDataApprovalInput -( - Guid DataId, - string Signature -); \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs new file mode 100644 index 00000000..12c801eb --- /dev/null +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; +using System.Diagnostics.CodeAnalysis; +using Database.Enumerations; + +namespace Database.GraphQl.DataApprovals; + +public sealed record RemoveDataApprovalInput +( + Guid DataId, + DataKind DataKind, + string Signature +); + +[SuppressMessage("Naming", "CA1707")] +public enum RemoveDataApprovalErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_APPROVAL, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record RemoveDataApprovalError( + RemoveDataApprovalErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class RemoveDataApprovalPayload + : DataApprovalPayload +{ + public RemoveDataApprovalPayload( + DataApproval dataApproval + ) + : base(dataApproval) + { + } + + public RemoveDataApprovalPayload( + RemoveDataApprovalError error + ) + : base(error) + { + } + + public RemoveDataApprovalPayload( + DataApproval dataApproval, + RemoveDataApprovalError error + ) + : base(dataApproval, error) + { + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class RemoveDataApprovalMutation +{ + public async Task RemoveDataApprovalAsync( + RemoveDataApprovalInput input, + ApplicationDbContext context, + UserService userService, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNAUTHORIZED, + $"The current user is not authorized to add data approvals to this database.", + [] + ) + ); + } + + var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); + if (data is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNKNOWN_DATA, + $"Unknown data.", + [nameof(input), nameof(input.DataId).ToLowerFirst()] + ) + ); + } + + var approval = data.Approvals + .Where(a => a.Signature.Trim() == input.Signature.Trim()) + .SingleOrDefault(); + if (approval is null) + { + return new RemoveDataApprovalPayload( + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.UNKNOWN_APPROVAL, + $"Unknown data approval.", + [nameof(input), nameof(input.Signature).ToLowerFirst()] + ) + ); + } + + data.Approvals.Remove(approval); + await context.SaveChangesAsync(cancellationToken); + + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + data.Approvals.Add(approval); + await context.SaveChangesAsync(cancellationToken); + + return new RemoveDataApprovalPayload( + approval, + new RemoveDataApprovalError( + RemoveDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Signing failed with message: {exception.Message}", + [] + ) + ); + } + return new RemoveDataApprovalPayload(approval); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs deleted file mode 100644 index bc562e1a..00000000 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.DataApprovals; - -public sealed class RemoveDataApprovalPayload - : DataApprovalPayload -{ - public RemoveDataApprovalPayload( - DataApproval dataApproval - ) - : base(dataApproval) - { - } - - public RemoveDataApprovalPayload( - RemoveDataApprovalError error - ) - : base(error) - { - } - - public RemoveDataApprovalPayload( - DataApproval dataApproval, - RemoveDataApprovalError error - ) - : base(dataApproval, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Databases/DatabaseMutations.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs similarity index 96% rename from backend/src/GraphQl/Databases/DatabaseMutations.cs rename to backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index e85a32e5..a34ba85b 100644 --- a/backend/src/GraphQl/Databases/DatabaseMutations.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -9,7 +9,7 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Mutation))] -public sealed class DatabaseMutations +public sealed class UpdateDatabaseMutation { public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs index b1ce135a..5efb927d 100644 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs @@ -6,7 +6,6 @@ using Database.Data; using GreenDonut; using Microsoft.EntityFrameworkCore; -using Guid = System.Guid; namespace Database.GraphQl.Entities; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs deleted file mode 100644 index 8abc5acf..00000000 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.GeometricDataX; - -public sealed record CreateGeometricDataError( - CreateGeometricDataErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs deleted file mode 100644 index 8c7558ec..00000000 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.GeometricDataX; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateGeometricDataErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs deleted file mode 100644 index 52f5c015..00000000 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataInput.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using Database.Enumerations; -using Database.Data; - -namespace Database.GraphQl.GeometricDataX; - -public sealed record CreateGeometricDataInput( - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, - double[] Thicknesses -) -{ - public GeometricData ToDomainModel(Guid userId) - { - var geometricData = new GeometricData( - userId, - Locale, - ComponentId, - Name, - Description, - Warnings, - CreatorId, - CreatedAt, - AppliedMethod.ToDomainModel(), - Thicknesses - ); - geometricData.Resources.Add(RootResource.ToDomainModel()); - return geometricData; - } -}; \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs similarity index 57% rename from backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs rename to backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 478d2b65..62b27865 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataMutations.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -7,11 +7,88 @@ using Database.Data; using Database.Services; using Database.Utilities; +using System.Diagnostics.CodeAnalysis; +using HotChocolate; +using System.Collections.Generic; namespace Database.GraphQl.GeometricDataX; +public sealed record CreateGeometricDataInput( + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, + double[] Thicknesses +) +{ + public GeometricData ToDomainModel(Guid userId) + { + var geometricData = new GeometricData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel(), + Thicknesses + ); + geometricData.Resources.Add(RootResource.ToDomainModel()); + return geometricData; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateGeometricDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateGeometricDataError( + CreateGeometricDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CreateGeometricDataPayload + : GeometricDataPayload +{ + public CreateGeometricDataPayload( + GeometricData geometricData + ) + : base(geometricData) + { + } + + public CreateGeometricDataPayload( + CreateGeometricDataError error + ) + : base(error) + { + } + + public CreateGeometricDataPayload( + GeometricData geometricData, + CreateGeometricDataError error + ) + : base(geometricData, error) + { + } +} + [ExtendObjectType(nameof(Mutation))] -public sealed class GeometricDataMutations +public sealed class CreateGeometricDataMutation { // [UseUserManager] //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs deleted file mode 100644 index b49c59ca..00000000 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.GeometricDataX; - -public sealed class CreateGeometricDataPayload - : GeometricDataPayload -{ - public CreateGeometricDataPayload( - GeometricData geometricData - ) - : base(geometricData) - { - } - - public CreateGeometricDataPayload( - CreateGeometricDataError error - ) - : base(error) - { - } - - public CreateGeometricDataPayload( - GeometricData geometricData, - CreateGeometricDataError error - ) - : base(geometricData, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs deleted file mode 100644 index 242e3736..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed record CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs deleted file mode 100644 index 96f48acd..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.GetHttpsResources; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateGetHttpsResourceErrorCode -{ - UNKNOWN, - UNKNOWN_DATA, - UNAUTHORIZED, - UNAUTHENTICATED, -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs deleted file mode 100644 index e5a8e5ca..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceInput.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using Database.Enumerations; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed record CreateGetHttpsResourceInput( - string Description, - Guid DataFormatId, - Guid DataId, - DataKind DataKind, - Guid? ParentId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod -); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs similarity index 62% rename from backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs rename to backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index c15d4338..fd9ba77d 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceMutations.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -16,20 +16,66 @@ using GreenDonut.Data; using Microsoft.EntityFrameworkCore.Query; using Database.GraphQl.Extensions; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; namespace Database.GraphQl.GetHttpsResources; -public static partial class GetHttpsResourceMutationsLogging +public sealed record CreateGetHttpsResourceInput( + string Description, + Guid DataFormatId, + Guid DataId, + DataKind DataKind, + Guid? ParentId, + IReadOnlyList ArchivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod +); + +[SuppressMessage("Naming", "CA1707")] +public enum CreateGetHttpsResourceErrorCode +{ + UNKNOWN, + UNKNOWN_DATA, + UNAUTHORIZED, + UNAUTHENTICATED, +} + +public sealed record CreateGetHttpsResourceError( + CreateGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CreateGetHttpsResourcePayload + : GetHttpsResourcePayload +{ + public CreateGetHttpsResourcePayload( + GetHttpsResource getHttpsResource + ) + : base(getHttpsResource) + { + } + + public CreateGetHttpsResourcePayload( + CreateGetHttpsResourceError error + ) + : base(error) + { + } +} + +public static partial class CreateGetHttpsResourceMutationLogging { [LoggerMessage( Level = LogLevel.Warning, Message = "Recomputing the hash value of the GET HTTPS resource with ID {Id} failed." )] - public static partial void FailedRecomputingHashValue(this ILogger logger, Guid id, Exception exception); + public static partial void FailedRecomputingHashValue(this ILogger logger, Guid id, Exception exception); } [ExtendObjectType(nameof(Mutation))] -public sealed class GetHttpsResourceMutations +public sealed class CreateGetHttpsResourceMutation { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateGetHttpsResourceAsync( @@ -115,71 +161,4 @@ input.AppliedConversionMethod is null await context.SaveChangesAsync(cancellationToken); return new CreateGetHttpsResourcePayload(getHttpsResource); } - - [UseFiltering] - public async Task RecomputeGetHttpsResourceHashValuesAsync( - ApplicationDbContext context, - QueryContext queryContext, - UserService userService, - CommonAuthorization authorization, - ILogger logger, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new RecomputeGetHttpsResourceHashValuesPayload( - new RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new RecomputeGetHttpsResourceHashValuesPayload( - new RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, - $"The current user is not authorized to recompute GET HTTPS resource hash values in this database.", - [] - ) - ); - } - var resources = - await context.GetHttpsResources - .With(queryContext, sort => sort.StabilizeOrder()) - .ToListAsync(cancellationToken); - var errors = new ConcurrentBag(); - await Parallel.ForEachAsync( - resources, - cancellationToken, - async (resource, cancellationToken) => - { - try - { - await resource.RecomputeHashValue(cancellationToken); - } - catch (Exception exception) - { - logger.FailedRecomputingHashValue(resource.Id, exception); - errors.Add( - new RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode.FAILED, - $"Recomputing the hash value for the GET HTTPS resource with ID {resource.Id} failed with message: {exception.Message}", - [] - ) - ); - } - - } - ); - await context.SaveChangesAsync(cancellationToken); - if (!errors.IsEmpty) - { - return new RecomputeGetHttpsResourceHashValuesPayload(resources, errors); - } - return new RecomputeGetHttpsResourceHashValuesPayload(resources); - } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs deleted file mode 100644 index 6eaca41a..00000000 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourcePayload.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed class CreateGetHttpsResourcePayload - : GetHttpsResourcePayload -{ - public CreateGetHttpsResourcePayload( - GetHttpsResource getHttpsResource - ) - : base(getHttpsResource) - { - } - - public CreateGetHttpsResourcePayload( - CreateGetHttpsResourceError error - ) - : base(error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs deleted file mode 100644 index ec572cc0..00000000 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed record RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs deleted file mode 100644 index 3d64876d..00000000 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.GetHttpsResources; - -[SuppressMessage("Naming", "CA1707")] -public enum RecomputeGetHttpsResourceHashValuesErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs new file mode 100644 index 00000000..efa88977 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -0,0 +1,148 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Authorization; +using Database.Enumerations; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; +using Database.Utilities; +using HotChocolate.Data; +using GreenDonut.Data; +using Microsoft.EntityFrameworkCore.Query; +using Database.GraphQl.Extensions; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; + +namespace Database.GraphQl.GetHttpsResources; + +[SuppressMessage("Naming", "CA1707")] +public enum RecomputeGetHttpsResourceHashValuesErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + FAILED +} + +public sealed record RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class RecomputeGetHttpsResourceHashValuesPayload + : Payload +{ + public IReadOnlyCollection? GetHttpsResources { get; } + public IReadOnlyCollection? Errors { get; } + + public RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection? getHttpsResources + ) + { + GetHttpsResources = getHttpsResources; + } + + public RecomputeGetHttpsResourceHashValuesPayload( + RecomputeGetHttpsResourceHashValuesError error + ) + { + Errors = [error]; + } + + public RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection getHttpsResources, + IReadOnlyCollection errors + ) + : this(getHttpsResources) + { + Errors = errors; + } +} + +public static partial class RecomputeGetHttpsResourceHashValuesMutationLogging +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Recomputing the hash value of the GET HTTPS resource with ID {Id} failed." + )] + public static partial void FailedRecomputingHashValue(this ILogger logger, Guid id, Exception exception); +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class RecomputeGetHttpsResourceHashValuesMutation +{ + [UseFiltering] + public async Task RecomputeGetHttpsResourceHashValuesAsync( + ApplicationDbContext context, + QueryContext queryContext, + UserService userService, + CommonAuthorization authorization, + ILogger logger, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new RecomputeGetHttpsResourceHashValuesPayload( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new RecomputeGetHttpsResourceHashValuesPayload( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, + $"The current user is not authorized to recompute GET HTTPS resource hash values in this database.", + [] + ) + ); + } + var resources = + await context.GetHttpsResources + .With(queryContext, sort => sort.StabilizeOrder()) + .ToListAsync(cancellationToken); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + resources, + cancellationToken, + async (resource, cancellationToken) => + { + try + { + await resource.RecomputeHashValue(cancellationToken); + } + catch (Exception exception) + { + logger.FailedRecomputingHashValue(resource.Id, exception); + errors.Add( + new RecomputeGetHttpsResourceHashValuesError( + RecomputeGetHttpsResourceHashValuesErrorCode.FAILED, + $"Recomputing the hash value for the GET HTTPS resource with ID {resource.Id} failed with message: {exception.Message}", + [] + ) + ); + } + + } + ); + await context.SaveChangesAsync(cancellationToken); + if (!errors.IsEmpty) + { + return new RecomputeGetHttpsResourceHashValuesPayload(resources, errors); + } + return new RecomputeGetHttpsResourceHashValuesPayload(resources); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs deleted file mode 100644 index a6a53505..00000000 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesPayload.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed class RecomputeGetHttpsResourceHashValuesPayload - : Payload -{ - public IReadOnlyCollection? GetHttpsResources { get; } - public IReadOnlyCollection? Errors { get; } - - public RecomputeGetHttpsResourceHashValuesPayload( - IReadOnlyCollection? getHttpsResources - ) - { - GetHttpsResources = getHttpsResources; - } - - public RecomputeGetHttpsResourceHashValuesPayload( - RecomputeGetHttpsResourceHashValuesError error - ) - { - Errors = [error]; - } - - public RecomputeGetHttpsResourceHashValuesPayload( - IReadOnlyCollection getHttpsResources, - IReadOnlyCollection errors - ) - : this(getHttpsResources) - { - Errors = errors; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs deleted file mode 100644 index 72446cf3..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.HygrothermalDataX; - -public sealed record CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs deleted file mode 100644 index 2b98faa3..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.HygrothermalDataX; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateHygrothermalDataErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs deleted file mode 100644 index 297869f7..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataInput.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using Database.Enumerations; -using Database.Data; - -namespace Database.GraphQl.HygrothermalDataX; - -public sealed record CreateHygrothermalDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource -) -{ - public HygrothermalData ToDomainModel(Guid userId) - { - var hygrothermalData = new HygrothermalData( - userId, - Locale, - ComponentId, - Name, - Description, - Warnings, - CreatorId, - CreatedAt, - AppliedMethod.ToDomainModel() - ); - hygrothermalData.Resources.Add(RootResource.ToDomainModel()); - return hygrothermalData; - } -}; \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs similarity index 56% rename from backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs rename to backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 00ba123b..3261f011 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataMutations.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -7,11 +7,87 @@ using Database.Data; using Database.Services; using Database.Utilities; +using HotChocolate; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; namespace Database.GraphQl.HygrothermalDataX; +public sealed record CreateHygrothermalDataInput( + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource +) +{ + public HygrothermalData ToDomainModel(Guid userId) + { + var hygrothermalData = new HygrothermalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + hygrothermalData.Resources.Add(RootResource.ToDomainModel()); + return hygrothermalData; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateHygrothermalDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateHygrothermalDataError( + CreateHygrothermalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CreateHygrothermalDataPayload + : HygrothermalDataPayload +{ + public CreateHygrothermalDataPayload( + HygrothermalData hygrothermalData + ) + : base(hygrothermalData) + { + } + + public CreateHygrothermalDataPayload( + CreateHygrothermalDataError error + ) + : base(error) + { + } + + public CreateHygrothermalDataPayload( + HygrothermalData hygrothermalData, + CreateHygrothermalDataError error + ) + : base(hygrothermalData, error) + { + } +} + [ExtendObjectType(nameof(Mutation))] -public sealed class HygrothermalDataMutations +public sealed class CreateHygrothermalDataMutation { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreateHygrothermalDataAsync( diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs deleted file mode 100644 index 3f24825b..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.HygrothermalDataX; - -public sealed class CreateHygrothermalDataPayload - : HygrothermalDataPayload -{ - public CreateHygrothermalDataPayload( - HygrothermalData hygrothermalData - ) - : base(hygrothermalData) - { - } - - public CreateHygrothermalDataPayload( - CreateHygrothermalDataError error - ) - : base(error) - { - } - - public CreateHygrothermalDataPayload( - HygrothermalData hygrothermalData, - CreateHygrothermalDataError error - ) - : base(hygrothermalData, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodError.cs b/backend/src/GraphQl/Methods/CalculateMethodError.cs deleted file mode 100644 index f2fa4a1a..00000000 --- a/backend/src/GraphQl/Methods/CalculateMethodError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.Methods; - -public sealed record CalculateMethodError( - CalculateMethodErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs b/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs deleted file mode 100644 index b1619346..00000000 --- a/backend/src/GraphQl/Methods/CalculateMethodErrorCode.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.Methods; - -[SuppressMessage("Naming", "CA1707")] -public enum CalculateMethodErrorCode -{ - UNKNOWN_METHOD, - UNKNOWN_DATABASE, - DATA_QUERY_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/CalculateMethodPayload.cs b/backend/src/GraphQl/Methods/CalculateMethodPayload.cs deleted file mode 100644 index de61d1fd..00000000 --- a/backend/src/GraphQl/Methods/CalculateMethodPayload.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json; - -namespace Database.GraphQl.Methods; - -public sealed class CalculateMethodPayload - : Payload -{ - public CalculateMethodPayload( - JsonElement result - ) - { - Result = result; - } - - public CalculateMethodPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - public CalculateMethodPayload( - CalculateMethodError error - ) - : this([error]) - { - } - - public CalculateMethodPayload( - JsonElement result, - IReadOnlyCollection errors - ) - { - Result = result; - Errors = errors; - } - - public CalculateMethodPayload( - JsonElement result, - CalculateMethodError error - ) - : this( - result, - [error] - ) - { - } - - public JsonElement? Result { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 796feb26..ab02493a 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -11,9 +11,74 @@ using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; namespace Database.GraphQl.Methods; +[SuppressMessage("Naming", "CA1707")] +public enum CalculateMethodErrorCode +{ + UNKNOWN_METHOD, + UNKNOWN_DATABASE, + DATA_QUERY_FAILED +} + +public sealed record CalculateMethodError( + CalculateMethodErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CalculateMethodPayload + : Payload +{ + public CalculateMethodPayload( + JsonElement result + ) + { + Result = result; + } + + public CalculateMethodPayload( + IReadOnlyCollection errors + ) + { + Errors = errors; + } + + public CalculateMethodPayload( + CalculateMethodError error + ) + : this([error]) + { + } + + public CalculateMethodPayload( + JsonElement result, + IReadOnlyCollection errors + ) + { + Result = result; + Errors = errors; + } + + public CalculateMethodPayload( + JsonElement result, + CalculateMethodError error + ) + : this( + result, + [error] + ) + { + } + + public JsonElement? Result { get; } + public IReadOnlyCollection? Errors { get; } +} + [ExtendObjectType(nameof(Query))] public sealed class MethodQueries { diff --git a/backend/src/GraphQl/Numerations/CreateNumerationInput.cs b/backend/src/GraphQl/Numerations/CreateNumerationInput.cs deleted file mode 100644 index 39695820..00000000 --- a/backend/src/GraphQl/Numerations/CreateNumerationInput.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Database.GraphQl.Numerations; - -public sealed record CreateNumerationInput( - string? Prefix, - string MainNumber, - string? Suffix -); \ No newline at end of file diff --git a/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs b/backend/src/GraphQl/Numerations/NumerationInput.cs similarity index 70% rename from backend/src/GraphQl/Numerations/UpdateNumerationInput.cs rename to backend/src/GraphQl/Numerations/NumerationInput.cs index 44990194..b2479108 100644 --- a/backend/src/GraphQl/Numerations/UpdateNumerationInput.cs +++ b/backend/src/GraphQl/Numerations/NumerationInput.cs @@ -1,6 +1,6 @@ namespace Database.GraphQl.Numerations; -public sealed record UpdateNumerationInput( +public sealed record NumerationInput( string? Prefix, string MainNumber, string? Suffix diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs deleted file mode 100644 index 0a8b0235..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.OpticalDataX; - -public sealed record CreateOpticalDataError( - CreateOpticalDataErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs deleted file mode 100644 index 54048097..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.OpticalDataX; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateOpticalDataErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs deleted file mode 100644 index dad6eb04..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataInput.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using Database.Enumerations; -using Database.Data; - -namespace Database.GraphQl.OpticalDataX; - -public sealed record CreateOpticalDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - OpticalComponentType? Type, - OpticalComponentSubtype? Subtype, - CoatedSide? CoatedSide, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, - double[] NearnormalHemisphericalVisibleTransmittances, - double[] NearnormalHemisphericalVisibleReflectances, - double[] NearnormalHemisphericalSolarTransmittances, - double[] NearnormalHemisphericalSolarReflectances, - double[] InfraredEmittances, - double[] ColorRenderingIndices, - IReadOnlyList CielabColors -) -{ - public OpticalData ToDomainModel(Guid userId) - { - var opticalData = new OpticalData( - userId, - Locale, - ComponentId, - Name, - Description, - Warnings, - CreatorId, - CreatedAt, - Type, - Subtype, - CoatedSide, - AppliedMethod.ToDomainModel(), - NearnormalHemisphericalVisibleTransmittances, - NearnormalHemisphericalVisibleReflectances, - NearnormalHemisphericalSolarTransmittances, - NearnormalHemisphericalSolarReflectances, - InfraredEmittances, - ColorRenderingIndices, - CielabColors.Select(c => c.ToDomainModel()).ToList() - ); - opticalData.Resources.Add(RootResource.ToDomainModel()); - return opticalData; - } -}; \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 5363a898..74b71855 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -1,3 +1,5 @@ +using System; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; @@ -7,11 +9,87 @@ using Database.Data; using Database.Services; using Database.GraphQl.DataX; +using HotChocolate; +using Database.Enumerations; +using System.Linq; namespace Database.GraphQl.OpticalDataX; +public sealed record CreateOpticalDataInput( + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + OpticalComponentType? Type, + OpticalComponentSubtype? Subtype, + CoatedSide? CoatedSide, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, + double[] NearnormalHemisphericalVisibleTransmittances, + double[] NearnormalHemisphericalVisibleReflectances, + double[] NearnormalHemisphericalSolarTransmittances, + double[] NearnormalHemisphericalSolarReflectances, + double[] InfraredEmittances, + double[] ColorRenderingIndices, + IReadOnlyList CielabColors +) +{ + public OpticalData ToDomainModel(Guid userId) + { + var opticalData = new OpticalData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + Type, + Subtype, + CoatedSide, + AppliedMethod.ToDomainModel(), + NearnormalHemisphericalVisibleTransmittances, + NearnormalHemisphericalVisibleReflectances, + NearnormalHemisphericalSolarTransmittances, + NearnormalHemisphericalSolarReflectances, + InfraredEmittances, + ColorRenderingIndices, + CielabColors.Select(c => c.ToDomainModel()).ToList() + ); + opticalData.Resources.Add(RootResource.ToDomainModel()); + return opticalData; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateOpticalDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateOpticalDataError( + CreateOpticalDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateOpticalDataPayload( + OpticalData? OpticalData, + IReadOnlyCollection? Errors +) +: OpticalDataPayload(OpticalData, Errors); + [ExtendObjectType(nameof(Mutation))] -public sealed class OpticalDataMutations +public sealed class CreateOpticalDataMutation : DataMutationBase { protected override CreateOpticalDataPayload NewPayload( diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs deleted file mode 100644 index 4098309b..00000000 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataPayload.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.OpticalDataX; - -public sealed record CreateOpticalDataPayload( - OpticalData? OpticalData, - IReadOnlyCollection? Errors -) -: OpticalDataPayload(OpticalData, Errors); \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs deleted file mode 100644 index 889d1cd8..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public sealed record CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs deleted file mode 100644 index 4f39811e..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataErrorCode.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.PhotovoltaicDataX; - -[SuppressMessage("Naming", "CA1707")] -public enum CreatePhotovoltaicDataErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs deleted file mode 100644 index a02c87f6..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataInput.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using HotChocolate; -using HotChocolate.Types; -using Database.Enumerations; -using Database.Data; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public sealed record CreatePhotovoltaicDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, - Guid ComponentId, - string? Name, - string? Description, - string[] Warnings, - DateTime CreatedAt, - Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource -) -{ - public PhotovoltaicData ToDomainModel(Guid userId) - { - var photovoltaicData = new PhotovoltaicData( - userId, - Locale, - ComponentId, - Name, - Description, - Warnings, - CreatorId, - CreatedAt, - AppliedMethod.ToDomainModel() - ); - photovoltaicData.Resources.Add(RootResource.ToDomainModel()); - return photovoltaicData; - } -}; \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs similarity index 56% rename from backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs rename to backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 2f97b01d..f6ecffa0 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataMutations.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -7,11 +7,87 @@ using Database.Data; using Database.Services; using Database.Utilities; +using HotChocolate; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; namespace Database.GraphQl.PhotovoltaicDataX; +public sealed record CreatePhotovoltaicDataInput( + // TODO Why does specifying the type with an attribute not work here? + [GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + DateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource +) +{ + public PhotovoltaicData ToDomainModel(Guid userId) + { + var photovoltaicData = new PhotovoltaicData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + photovoltaicData.Resources.Add(RootResource.ToDomainModel()); + return photovoltaicData; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreatePhotovoltaicDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreatePhotovoltaicDataError( + CreatePhotovoltaicDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed class CreatePhotovoltaicDataPayload + : PhotovoltaicDataPayload +{ + public CreatePhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData + ) + : base(photovoltaicData) + { + } + + public CreatePhotovoltaicDataPayload( + CreatePhotovoltaicDataError error + ) + : base(error) + { + } + + public CreatePhotovoltaicDataPayload( + PhotovoltaicData photovoltaicData, + CreatePhotovoltaicDataError error + ) + : base(photovoltaicData, error) + { + } +} + [ExtendObjectType(nameof(Mutation))] -public sealed class PhotovoltaicDataMutations +public sealed class CreatePhotovoltaicDataMutation { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] public async Task CreatePhotovoltaicDataAsync( diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs deleted file mode 100644 index ca418f7f..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataPayload.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public sealed class CreatePhotovoltaicDataPayload - : PhotovoltaicDataPayload -{ - public CreatePhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData - ) - : base(photovoltaicData) - { - } - - public CreatePhotovoltaicDataPayload( - CreatePhotovoltaicDataError error - ) - : base(error) - { - } - - public CreatePhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData, - CreatePhotovoltaicDataError error - ) - : base(photovoltaicData, error) - { - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs deleted file mode 100644 index d9eeed54..00000000 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed record CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs deleted file mode 100644 index 3f4a55d3..00000000 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsErrorCode.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.ResponseApprovals; - -[SuppressMessage("Naming", "CA1707")] -public enum CreateResponseApprovalsErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - UNKNOWN_DATABASE, - CREATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs similarity index 50% rename from backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs rename to backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index b411edd1..b7840bbc 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalMutations.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,23 +13,63 @@ namespace Database.GraphQl.ResponseApprovals; -public static partial class ResponseApprovalMutationsLogging +[SuppressMessage("Naming", "CA1707")] +public enum CreateResponseApprovalsErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATABASE, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateResponseApprovalsError( + CreateResponseApprovalsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public static partial class CreateResponseApprovalsMutationLogging { [LoggerMessage( Level = LogLevel.Warning, Message = "Creating response approval for data of type {Type} with ID {Id} failed." )] - public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); + public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); +} - [LoggerMessage( - Level = LogLevel.Warning, - Message = "Updating response approval for data of type {Type} with ID {Id} failed." - )] - public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); +public sealed class CreateResponseApprovalsPayload + : Payload +{ + public IReadOnlyCollection? Data { get; } + public IReadOnlyCollection? Errors { get; } + + public CreateResponseApprovalsPayload( + IReadOnlyCollection data + ) + { + Data = data; + } + + public CreateResponseApprovalsPayload( + CreateResponseApprovalsError error + ) + { + Errors = [error]; + } + + public CreateResponseApprovalsPayload( + IReadOnlyCollection data, + IReadOnlyCollection errors + ) : this(data) + { + Errors = errors; + } } [ExtendObjectType(nameof(Mutation))] -public sealed class ResponseApprovalMutations +public sealed class CreateResponseApprovalsMutation { //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] public async Task CreateResponseApprovalsAsync( @@ -35,7 +77,7 @@ public async Task CreateResponseApprovalsAsync( UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, - ILogger logger, + ILogger logger, CancellationToken cancellationToken ) { @@ -96,71 +138,4 @@ await Parallel.ForEachAsync( } return new CreateResponseApprovalsPayload(dataSets); } - - public async Task UpdateResponseApprovalsAsync( - ApplicationDbContext context, - UserService userService, - CommonAuthorization authorization, - ResponseApprovalService responseApprovalService, - ILogger logger, - CancellationToken cancellationToken - ) - { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new UpdateResponseApprovalsPayload( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new UpdateResponseApprovalsPayload( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create response approvals in this database.", - [] - ) - ); - } - var dataSets = new ConcurrentBag(); - var errors = new ConcurrentBag(); - await Parallel.ForEachAsync( - context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() - .Union(context.GeometricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.OpticalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()), - cancellationToken, - async (data, cancellationToken) => - { - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - dataSets.Add(data); - } - catch (Exception exception) - { - logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, exception); - errors.Add( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, - $"Updating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", - [] - ) - ); - } - } - ); - await context.SaveChangesAsync(cancellationToken); - if (!errors.IsEmpty) - { - return new UpdateResponseApprovalsPayload(dataSets, errors); - } - return new UpdateResponseApprovalsPayload(dataSets); - } } \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs deleted file mode 100644 index d874a7e6..00000000 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsPayload.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed class CreateResponseApprovalsPayload - : Payload -{ - public IReadOnlyCollection? Data { get; } - public IReadOnlyCollection? Errors { get; } - - public CreateResponseApprovalsPayload( - IReadOnlyCollection data - ) - { - Data = data; - } - - public CreateResponseApprovalsPayload( - CreateResponseApprovalsError error - ) - { - Errors = [error]; - } - - public CreateResponseApprovalsPayload( - IReadOnlyCollection data, - IReadOnlyCollection errors - ) : this(data) - { - Errors = errors; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs deleted file mode 100644 index 91fbcc3f..00000000 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsError.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed record UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs deleted file mode 100644 index d5d72ac0..00000000 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsErrorCode.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Database.GraphQl.ResponseApprovals; - -[SuppressMessage("Naming", "CA1707")] -public enum UpdateResponseApprovalsErrorCode -{ - UNKNOWN, - UNAUTHORIZED, - UNAUTHENTICATED, - UNKNOWN_DATABASE, - UPDATING_RESPONSE_APPROVAL_FAILED -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs new file mode 100644 index 00000000..c00dbb89 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Services; +using HotChocolate.Types; +using Microsoft.Extensions.Logging; + +namespace Database.GraphQl.ResponseApprovals; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateResponseApprovalsErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_DATABASE, + UPDATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public static partial class UpdateResponseApprovalsMutationLogging +{ + [LoggerMessage( + Level = LogLevel.Warning, + Message = "Updating response approval for data of type {Type} with ID {Id} failed." + )] + public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); +} + +public sealed class UpdateResponseApprovalsPayload + : Payload +{ + public IReadOnlyCollection? Data { get; } + public IReadOnlyCollection? Errors { get; } + + public UpdateResponseApprovalsPayload( + IReadOnlyCollection data + ) + { + Data = data; + } + + public UpdateResponseApprovalsPayload( + UpdateResponseApprovalsError error + ) + { + Errors = [error]; + } + + public UpdateResponseApprovalsPayload( + IReadOnlyCollection data, + IReadOnlyCollection errors + ) : this(data) + { + Errors = errors; + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateResponseApprovalsMutation +{ + public async Task UpdateResponseApprovalsAsync( + ApplicationDbContext context, + UserService userService, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ILogger logger, + CancellationToken cancellationToken + ) + { + var currentUser = await userService.GetCurrentUser(cancellationToken); + if (currentUser is null) + { + return new UpdateResponseApprovalsPayload( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, + $"The user is not authenticated.", + [] + ) + ); + } + if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + { + return new UpdateResponseApprovalsPayload( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UNAUTHORIZED, + $"The current user is not authorized to update response approvals in this database.", + [] + ) + ); + } + var dataSets = new ConcurrentBag(); + var errors = new ConcurrentBag(); + await Parallel.ForEachAsync( + context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() + .Union(context.GeometricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.OpticalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) + .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()), + cancellationToken, + async (data, cancellationToken) => + { + try + { + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); + } + catch (Exception exception) + { + logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, exception); + errors.Add( + new UpdateResponseApprovalsError( + UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, + $"Updating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", + [] + ) + ); + } + } + ); + await context.SaveChangesAsync(cancellationToken); + if (!errors.IsEmpty) + { + return new UpdateResponseApprovalsPayload(dataSets, errors); + } + return new UpdateResponseApprovalsPayload(dataSets); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs deleted file mode 100644 index a4f6f113..00000000 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsPayload.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed class UpdateResponseApprovalsPayload - : Payload -{ - public IReadOnlyCollection? Data { get; } - public IReadOnlyCollection? Errors { get; } - - public UpdateResponseApprovalsPayload( - IReadOnlyCollection data - ) - { - Data = data; - } - - public UpdateResponseApprovalsPayload( - UpdateResponseApprovalsError error - ) - { - Errors = [error]; - } - - public UpdateResponseApprovalsPayload( - IReadOnlyCollection data, - IReadOnlyCollection errors - ) : this(data) - { - Errors = errors; - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Standards/StandardInput.cs b/backend/src/GraphQl/Standards/StandardInput.cs index 86bffe52..4eed09ee 100644 --- a/backend/src/GraphQl/Standards/StandardInput.cs +++ b/backend/src/GraphQl/Standards/StandardInput.cs @@ -10,7 +10,7 @@ public sealed record StandardInput( string? Abstract, string? Section, int? Year, - CreateNumerationInput Numeration, + NumerationInput Numeration, Standardizer[] Standardizers, Uri? Locator ) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index c931069e..1290d4ca 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -63,9 +63,15 @@ type AddDataApprovalPayload { query: Query! } +type AddInstitutionAccessRightsError implements UserError { + code: AddInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + type AddInstitutionAccessRightsPayload { accessRights: InstitutionAccessRights - errors: [InstitutionAccessRightsError!] + errors: [AddInstitutionAccessRightsError!] query: Query! } @@ -449,20 +455,14 @@ type InstitutionAccessRights { version: NonNegativeInt! } -type InstitutionAccessRightsError implements UserError { - code: InstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - type KeyValuePairOfGuidAndNullableOfUInt32 { key: Uuid! value: NonNegativeInt } type Mutation { - addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - addDataApproval(input: DataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") @@ -472,11 +472,11 @@ type Mutation { createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") - updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") } @@ -707,12 +707,6 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } -type UpdateAccessRightsPayload { - accessRights: InstitutionAccessRights - errors: [InstitutionAccessRightsError!] - query: Query! -} - type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! @@ -749,6 +743,18 @@ type UpdateDatabasePayload { query: Query! } +type UpdateInstitutionAccessRightsError implements UserError { + code: UpdateInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateInstitutionAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [UpdateInstitutionAccessRightsError!] + query: Query! +} + type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! @@ -782,6 +788,26 @@ type UserInfo { union Reference = Publication | Standard +input AddDataApprovalInput { + approverId: Uuid! + dataId: Uuid! + dataKind: DataKind! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: ReferenceInput! + timestamp: DateTime! + variables: Any! +} + +input AddInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -901,12 +927,6 @@ input CreateHygrothermalDataInput { warnings: [String!]! } -input CreateNumerationInput { - mainNumber: String! - prefix: String - suffix: String -} - input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! cielabColors: [CielabColorInput!]! @@ -957,27 +977,6 @@ input CrossDatabaseDataReferencePropositionInput { databaseId: UuidPropositionInput } -input DataAccessRightsInput { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] - dataId: Uuid! - dataKind: DataKind! -} - -input DataApprovalInput { - approverId: Uuid! - dataId: Uuid! - dataKind: DataKind! - keyFingerprint: String! - message: String! - query: String! - signature: String! - statement: ReferenceInput! - timestamp: DateTime! - variables: Any! -} - input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] or: [DataApprovalPropositionInput!] @@ -1179,13 +1178,6 @@ input HygrothermalDataSortInput { id: SortEnumType @cost(weight: "10") } -input InstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input Int32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -1345,6 +1337,12 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } +input NumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + input NumerationPropositionInput { and: [NumerationPropositionInput!] or: [NumerationPropositionInput!] @@ -1501,6 +1499,12 @@ input ReferenceSortInput { publication: PublicationSortInput @cost(weight: "10") } +input RemoveDataApprovalInput { + dataId: Uuid! + dataKind: DataKind! + signature: String! +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1511,7 +1515,7 @@ input RootGetHttpsResourceInput { input StandardInput { abstract: String locator: Url - numeration: CreateNumerationInput! + numeration: NumerationInput! section: String standardizers: [Standardizer!]! title: String @@ -1593,6 +1597,14 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateDataAccessRightsInput { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + dataId: Uuid! + dataKind: DataKind! +} + input UpdateDataInput { componentId: Uuid! createdAt: DateTime! @@ -1612,6 +1624,13 @@ input UpdateDatabaseInput { name: String! } +input UpdateInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -1703,6 +1722,13 @@ enum AddDataApprovalErrorCode { WRONG_STATEMENT } +enum AddInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + ALREADY_EXISTS +} + "Defines when a policy shall be executed." enum ApplyPolicy { "Before the resolver was executed." @@ -1807,14 +1833,6 @@ enum Illuminant { D65 } -enum InstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - ALREADY_EXISTS - UNKNOWN_ACCESS_RIGHTS -} - enum JsonValueKind { UNDEFINED OBJECT @@ -1924,6 +1942,13 @@ enum UpdateDatabaseErrorCode { UNKNOWN_DATABASE } +enum UpdateInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_ACCESS_RIGHTS +} + enum UpdateResponseApprovalsErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 2ac9f766..1290d4ca 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -63,9 +63,15 @@ type AddDataApprovalPayload { query: Query! } +type AddInstitutionAccessRightsError implements UserError { + code: AddInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + type AddInstitutionAccessRightsPayload { accessRights: InstitutionAccessRights - errors: [InstitutionAccessRightsError!] + errors: [AddInstitutionAccessRightsError!] query: Query! } @@ -449,20 +455,14 @@ type InstitutionAccessRights { version: NonNegativeInt! } -type InstitutionAccessRightsError implements UserError { - code: InstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - type KeyValuePairOfGuidAndNullableOfUInt32 { key: Uuid! value: NonNegativeInt } type Mutation { - addAccessRights(input: InstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - addDataApproval(input: DataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") @@ -472,11 +472,11 @@ type Mutation { createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval(input: DataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") - updateAccessRights(input: InstitutionAccessRightsInput!): UpdateAccessRightsPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: DataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") } @@ -638,7 +638,7 @@ type Query { database: Database! @cost(weight: "10") geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") @@ -707,12 +707,6 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } -type UpdateAccessRightsPayload { - accessRights: InstitutionAccessRights - errors: [InstitutionAccessRightsError!] - query: Query! -} - type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! @@ -749,6 +743,18 @@ type UpdateDatabasePayload { query: Query! } +type UpdateInstitutionAccessRightsError implements UserError { + code: UpdateInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateInstitutionAccessRightsPayload { + accessRights: InstitutionAccessRights + errors: [UpdateInstitutionAccessRightsError!] + query: Query! +} + type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! @@ -782,6 +788,26 @@ type UserInfo { union Reference = Publication | Standard +input AddDataApprovalInput { + approverId: Uuid! + dataId: Uuid! + dataKind: DataKind! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: ReferenceInput! + timestamp: DateTime! + variables: Any! +} + +input AddInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -901,12 +927,6 @@ input CreateHygrothermalDataInput { warnings: [String!]! } -input CreateNumerationInput { - mainNumber: String! - prefix: String - suffix: String -} - input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! cielabColors: [CielabColorInput!]! @@ -957,27 +977,6 @@ input CrossDatabaseDataReferencePropositionInput { databaseId: UuidPropositionInput } -input DataAccessRightsInput { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] - dataId: Uuid! - dataKind: DataKind! -} - -input DataApprovalInput { - approverId: Uuid! - dataId: Uuid! - dataKind: DataKind! - keyFingerprint: String! - message: String! - query: String! - signature: String! - statement: ReferenceInput! - timestamp: DateTime! - variables: Any! -} - input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] or: [DataApprovalPropositionInput!] @@ -1179,13 +1178,6 @@ input HygrothermalDataSortInput { id: SortEnumType @cost(weight: "10") } -input InstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input Int32PropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") @@ -1345,6 +1337,12 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } +input NumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + input NumerationPropositionInput { and: [NumerationPropositionInput!] or: [NumerationPropositionInput!] @@ -1501,6 +1499,12 @@ input ReferenceSortInput { publication: PublicationSortInput @cost(weight: "10") } +input RemoveDataApprovalInput { + dataId: Uuid! + dataKind: DataKind! + signature: String! +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1511,7 +1515,7 @@ input RootGetHttpsResourceInput { input StandardInput { abstract: String locator: Url - numeration: CreateNumerationInput! + numeration: NumerationInput! section: String standardizers: [Standardizer!]! title: String @@ -1593,6 +1597,14 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateDataAccessRightsInput { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + dataId: Uuid! + dataKind: DataKind! +} + input UpdateDataInput { componentId: Uuid! createdAt: DateTime! @@ -1612,6 +1624,13 @@ input UpdateDatabaseInput { name: String! } +input UpdateInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -1703,6 +1722,13 @@ enum AddDataApprovalErrorCode { WRONG_STATEMENT } +enum AddInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + ALREADY_EXISTS +} + "Defines when a policy shall be executed." enum ApplyPolicy { "Before the resolver was executed." @@ -1807,14 +1833,6 @@ enum Illuminant { D65 } -enum InstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - ALREADY_EXISTS - UNKNOWN_ACCESS_RIGHTS -} - enum JsonValueKind { UNDEFINED OBJECT @@ -1924,6 +1942,13 @@ enum UpdateDatabaseErrorCode { UNKNOWN_DATABASE } +enum UpdateInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_ACCESS_RIGHTS +} + enum UpdateResponseApprovalsErrorCode { UNKNOWN UNAUTHORIZED From 5ac6f3a620ac4a98ddeadbe5b776228ebd9258e0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 29 Oct 2025 18:25:08 +0100 Subject: [PATCH 529/878] Use methods in `MutationBase` and `DataMutationBase` to authorize user, fetch data, and (re)create response approvals --- backend/.vscode/settings.json | 4 +- .../src/Extensions/ConcurrentBagExtensions.cs | 18 +++ .../AddInstitutionAccessRightsMutation.cs | 79 ++++----- .../AccessRights/DataAccessRightsPayload.cs | 53 ------ .../InstitutionAccessRightsPayload.cs | 52 ------ .../UpdateDataAccessRightsMutation.cs | 86 +++++----- .../UpdateInstitutionAccessRightsMutation.cs | 79 ++++----- .../CalorimetricDataPayload.cs | 53 ------ .../CreateCalorimetricDataMutation.cs | 102 +++++------- .../DataApprovals/AddDataApprovalMutation.cs | 150 +++++++---------- .../DataApprovals/DataApprovalPayload.cs | 53 ------ .../RemoveDataApprovalMutation.cs | 126 ++++++--------- .../GraphQl/{DataX => }/DataMutationBase.cs | 10 +- backend/src/GraphQl/DataX/DataFilterType.cs | 2 +- backend/src/GraphQl/DataX/DataType.cs | 3 + backend/src/GraphQl/DataX/DataTypeBase.cs | 3 + .../src/GraphQl/DataX/DeleteDataMutation.cs | 2 +- .../src/GraphQl/DataX/UpdateDataMutation.cs | 2 +- .../CreateGeometricDataMutation.cs | 99 +++++------- .../GeometricDataX/GeometricDataPayload.cs | 53 ------ .../CreateGetHttpsResourceMutation.cs | 133 +++++++-------- .../GetHttpsResourcePayload.cs | 53 ------ ...mputeGetHttpsResourceHashValuesMutation.cs | 110 ++++++------- .../CreateHygrothermalDataMutation.cs | 100 +++++------- .../HygrothermalDataPayload.cs | 53 ------ backend/src/GraphQl/Methods/MethodQueries.cs | 75 +++------ .../OpticalDataX/CreateOpticalDataMutation.cs | 3 +- .../OpticalDataX/OpticalDataPayload.cs | 10 -- backend/src/GraphQl/Payload.cs | 2 +- .../CreatePhotovoltaicDataMutation.cs | 101 +++++------- .../PhotovoltaicDataPayload.cs | 53 ------ .../CreateResponseApprovalsFilterType.cs | 16 ++ .../CreateResponseApprovalsMutation.cs | 149 +++++++++-------- .../ResponseApprovalFilterType.cs | 15 ++ .../UpdateResponseApprovalsFilterType.cs | 16 ++ .../UpdateResponseApprovalsMutation.cs | 152 ++++++++++-------- .../GraphQlSchemaTests.IsUnchanged.snap | 36 +++-- frontend/type-defs.graphqls | 36 +++-- 38 files changed, 793 insertions(+), 1349 deletions(-) create mode 100644 backend/src/Extensions/ConcurrentBagExtensions.cs delete mode 100644 backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs delete mode 100644 backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs delete mode 100644 backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs rename backend/src/GraphQl/{DataX => }/DataMutationBase.cs (89%) delete mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs create mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index a858e309..388b01e1 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -2,5 +2,7 @@ "editor.formatOnSave": true, "[csharp]": { "editor.defaultFormatter": "ms-dotnettools.csharp" - } + }, + "workbench.editor.enablePreview": false, + "workbench.editor.enablePreviewFromQuickOpen": false } \ No newline at end of file diff --git a/backend/src/Extensions/ConcurrentBagExtensions.cs b/backend/src/Extensions/ConcurrentBagExtensions.cs new file mode 100644 index 00000000..69a5bd24 --- /dev/null +++ b/backend/src/Extensions/ConcurrentBagExtensions.cs @@ -0,0 +1,18 @@ +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Database.Extensions; + +public static class ConcurrentBagExtensions +{ + public static void AddRange(this ConcurrentBag bag, IEnumerable? toAdd) + { + if (toAdd is not null) + { + foreach (var item in toAdd) + { + bag.Add(item); + } + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs index 823ae31b..d23f5621 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.AccessRights; @@ -38,69 +39,59 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class AddInstitutionAccessRightsPayload - : InstitutionAccessRightsPayload -{ - public AddInstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights - ) - : base(accessRights) - { - } - - public AddInstitutionAccessRightsPayload( - AddInstitutionAccessRightsError error - ) - : base(error) - { - } -} +public sealed record AddInstitutionAccessRightsPayload( + Data.InstitutionAccessRights? InstitutionAccessRights, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class AddInstitutionAccessRightsMutation +: MutationBase { + protected override AddInstitutionAccessRightsPayload NewPayload( + Data.InstitutionAccessRights? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override AddInstitutionAccessRightsError NewError( + AddInstitutionAccessRightsErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task AddInstitutionAccessRightsAsync( AddInstitutionAccessRightsInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + AddInstitutionAccessRightsErrorCode.UNAUTHENTICATED, + AddInstitutionAccessRightsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new AddInstitutionAccessRightsPayload( - new AddInstitutionAccessRightsError( - AddInstitutionAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); + return authorizeErrorPayload; } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new AddInstitutionAccessRightsPayload( - new AddInstitutionAccessRightsError( - AddInstitutionAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add access rights for the institution.", - [] - ) + var accessRights = await context.InstitutionAccessRights + .SingleOrDefaultAsync(x => + x.InstitutionId == input.InstitutionId, + cancellationToken ); - } - - var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); - if (accessRights is not null) { - return new AddInstitutionAccessRightsPayload( - new AddInstitutionAccessRightsError( + return NewPayload( + null, + [NewError( AddInstitutionAccessRightsErrorCode.ALREADY_EXISTS, $"The access rights for this institution already exist.", [] - ) + )] ); } @@ -111,6 +102,6 @@ CancellationToken cancellationToken TimeSpan.FromDays(input.PeriodInDays)); context.InstitutionAccessRights.Add(accessRights); await context.SaveChangesAsync(cancellationToken); - return new AddInstitutionAccessRightsPayload(accessRights); + return NewPayload(accessRights, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs deleted file mode 100644 index b6389e86..00000000 --- a/backend/src/GraphQl/AccessRights/DataAccessRightsPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Database.Data; -using System.Collections.Generic; - -namespace Database.GraphQl.AccessRights; - -public abstract class DataAccessRightsPayload - : Payload - where TDataAccessRightsError : IUserError -{ - protected DataAccessRightsPayload( - DataAccessRights dataAccessRights - ) - { - DataAccessRights = dataAccessRights; - } - - protected DataAccessRightsPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected DataAccessRightsPayload( - TDataAccessRightsError error - ) - : this([error]) - { - } - - protected DataAccessRightsPayload( - DataAccessRights dataAccessRights, - IReadOnlyCollection errors - ) - { - DataAccessRights = dataAccessRights; - Errors = errors; - } - - protected DataAccessRightsPayload( - DataAccessRights dataAccessRights, - TDataAccessRightsError error - ) - : this( - dataAccessRights, - [error] - ) - { - } - - public DataAccessRights? DataAccessRights { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs b/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs deleted file mode 100644 index cd295a5b..00000000 --- a/backend/src/GraphQl/AccessRights/InstitutionAccessRightsPayload.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Collections.Generic; - -namespace Database.GraphQl.AccessRights; - -public abstract class InstitutionAccessRightsPayload - : Payload - where TInstitutionAccessRightsError : IUserError -{ - protected InstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights - ) - { - AccessRights = accessRights; - } - - protected InstitutionAccessRightsPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected InstitutionAccessRightsPayload( - TInstitutionAccessRightsError error - ) - : this([error]) - { - } - - protected InstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights, - IReadOnlyCollection errors - ) - { - AccessRights = accessRights; - Errors = errors; - } - - protected InstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights, - TInstitutionAccessRightsError error - ) - : this( - accessRights, - [error] - ) - { - } - - public Data.InstitutionAccessRights? AccessRights { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index 5b0e44b2..a1ac1eef 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -7,6 +7,7 @@ using HotChocolate.Types; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Database.Extensions; using Database.Enumerations; @@ -19,7 +20,7 @@ public sealed record UpdateDataAccessRightsInput IReadOnlyDictionary? AllowedUserAndQuantity, IReadOnlyList? AllowedInstitutions, IReadOnlyList? AllowedApplications -); +) : IIdentifyDataInput; [SuppressMessage("Naming", "CA1707")] public enum UpdateDataAccessRightsErrorCode @@ -37,68 +38,55 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class UpdateDataAccessRightsPayload - : DataAccessRightsPayload -{ - public UpdateDataAccessRightsPayload( - DataAccessRights dataAccessRights - ) - : base(dataAccessRights) - { - } - - public UpdateDataAccessRightsPayload( - UpdateDataAccessRightsError error - ) - : base(error) - { - } -} +public sealed record UpdateDataAccessRightsPayload( + DataAccessRights? DataAccessRights, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateDataAccessRightsMutation +: DataMutationBase { + protected override UpdateDataAccessRightsPayload NewPayload( + DataAccessRights? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateDataAccessRightsError NewError( + UpdateDataAccessRightsErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task UpdateDataAccessRightsAsync( UpdateDataAccessRightsInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, + UpdateDataAccessRightsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update access rights for the data.", - [] - ) - ); + return authorizeErrorPayload; } - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) + if ((await FetchDataAsync( + input, + UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) { - return new UpdateDataAccessRightsPayload( - new UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [] - ) - ); + return fetchDataErrorPayload; } data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; @@ -106,6 +94,6 @@ CancellationToken cancellationToken data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; await context.SaveChangesAsync(cancellationToken); - return new UpdateDataAccessRightsPayload(data.DataAccessRights); + return NewPayload(data.DataAccessRights, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs index f540b0c2..5702fc81 100644 --- a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs @@ -8,6 +8,7 @@ using Microsoft.EntityFrameworkCore; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.AccessRights; @@ -38,69 +39,59 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class UpdateInstitutionAccessRightsPayload - : InstitutionAccessRightsPayload -{ - public UpdateInstitutionAccessRightsPayload( - Data.InstitutionAccessRights accessRights - ) - : base(accessRights) - { - } - - public UpdateInstitutionAccessRightsPayload( - UpdateInstitutionAccessRightsError error - ) - : base(error) - { - } -} +public sealed record UpdateInstitutionAccessRightsPayload( + Data.InstitutionAccessRights? InstitutionAccessRights, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateInstitutionAccessRightsMutation +: MutationBase { + protected override UpdateInstitutionAccessRightsPayload NewPayload( + Data.InstitutionAccessRights? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateInstitutionAccessRightsError NewError( + UpdateInstitutionAccessRightsErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task UpdateInstitutionAccessRightsAsync( UpdateInstitutionAccessRightsInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + UpdateInstitutionAccessRightsErrorCode.UNAUTHENTICATED, + UpdateInstitutionAccessRightsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new UpdateInstitutionAccessRightsPayload( - new UpdateInstitutionAccessRightsError( - UpdateInstitutionAccessRightsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); + return authorizeErrorPayload; } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new UpdateInstitutionAccessRightsPayload( - new UpdateInstitutionAccessRightsError( - UpdateInstitutionAccessRightsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update access rights for the institution.", - [] - ) + var accessRights = await context.InstitutionAccessRights + .SingleOrDefaultAsync(x => + x.InstitutionId == input.InstitutionId, + cancellationToken ); - } - - var accessRights = await context.InstitutionAccessRights.SingleOrDefaultAsync(x => x.InstitutionId == input.InstitutionId, cancellationToken); - if (accessRights is null) { - return new UpdateInstitutionAccessRightsPayload( - new UpdateInstitutionAccessRightsError( + return NewPayload( + null, + [NewError( UpdateInstitutionAccessRightsErrorCode.UNKNOWN_ACCESS_RIGHTS, $"There are no access rights for this institution.", [] - ) + )] ); } @@ -108,6 +99,6 @@ CancellationToken cancellationToken accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); await context.SaveChangesAsync(cancellationToken); - return new UpdateInstitutionAccessRightsPayload(accessRights); + return NewPayload(accessRights, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs deleted file mode 100644 index e9636c1d..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.CalorimetricDataX; - -public abstract class CalorimetricDataPayload - : Payload - where TCalorimetricDataError : IUserError -{ - protected CalorimetricDataPayload( - CalorimetricData calorimetricData - ) - { - CalorimetricData = calorimetricData; - } - - protected CalorimetricDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected CalorimetricDataPayload( - TCalorimetricDataError error - ) - : this([error]) - { - } - - protected CalorimetricDataPayload( - CalorimetricData calorimetricData, - IReadOnlyCollection errors - ) - { - CalorimetricData = calorimetricData; - Errors = errors; - } - - protected CalorimetricDataPayload( - CalorimetricData calorimetricData, - TCalorimetricDataError error - ) - : this( - calorimetricData, - [error] - ) - { - } - - public CalorimetricData? CalorimetricData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 9b91c10e..285c3c12 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -10,6 +10,8 @@ using HotChocolate; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using Database.Extensions; +using Database.Extensions; namespace Database.GraphQl.CalorimetricDataX; @@ -64,91 +66,65 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CreateCalorimetricDataPayload - : CalorimetricDataPayload -{ - public CreateCalorimetricDataPayload( - CalorimetricData calorimetricData - ) - : base(calorimetricData) - { - } - - public CreateCalorimetricDataPayload( - CreateCalorimetricDataError error - ) - : base(error) - { - } - - public CreateCalorimetricDataPayload( - CalorimetricData calorimetricData, - CreateCalorimetricDataError error - ) - : base(calorimetricData, error) - { - } -} +public sealed record CreateCalorimetricDataPayload( + CalorimetricData? CalorimetricData, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreateCalorimetricDataMutation +: DataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override CreateCalorimetricDataPayload NewPayload( + CalorimetricData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateCalorimetricDataError NewError( + CreateCalorimetricDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) - { - return new CreateCalorimetricDataPayload( - new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + CreateCalorimetricDataErrorCode.UNAUTHENTICATED, + CreateCalorimetricDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreateCalorimetricDataPayload( - new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create calorimetric data in this database.", - [] - ) - ); + return authorizeErrorPayload; } var calorimetricData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); - try - { - calorimetricData.Approval = await responseApprovalService.CreateResponseApproval(calorimetricData, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + calorimetricData, + CreateCalorimetricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { context.Remove(calorimetricData); await context.SaveChangesAsync(cancellationToken); - - return new CreateCalorimetricDataPayload( - calorimetricData, - new CreateCalorimetricDataError( - CreateCalorimetricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new CreateCalorimetricDataPayload(calorimetricData); + + return NewPayload(calorimetricData, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index bb734540..f276a946 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -18,6 +18,7 @@ using System.Diagnostics.CodeAnalysis; using Database.Enumerations; using Database.GraphQl.References; +using Database.Extensions; namespace Database.GraphQl.DataApprovals; @@ -33,7 +34,7 @@ public sealed record AddDataApprovalInput string Message, Guid ApproverId, ReferenceInput Statement -); +) : IIdentifyDataInput; [SuppressMessage("Naming", "CA1707")] public enum AddDataApprovalErrorCode @@ -62,38 +63,10 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class AddDataApprovalPayload - : DataApprovalPayload -{ - public AddDataApprovalPayload( - DataApproval dataApproval - ) - : base(dataApproval) - { - } - - public AddDataApprovalPayload( - AddDataApprovalError error - ) - : base(error) - { - } - - public AddDataApprovalPayload( - IReadOnlyCollection errors - ) - : base(errors) - { - } - - public AddDataApprovalPayload( - DataApproval dataApproval, - AddDataApprovalError error - ) - : base(dataApproval, error) - { - } -} +public sealed record AddDataApprovalPayload( + DataApproval? DataApproval, + IReadOnlyCollection? Errors +) : Payload; public static partial class AddDataApprovalMutationLogging { @@ -105,6 +78,7 @@ public static partial class AddDataApprovalMutationLogging [ExtendObjectType(nameof(Mutation))] public sealed class AddDataApprovalMutation +: DataMutationBase { private sealed record Message( JsonElement Statement, @@ -112,10 +86,20 @@ JsonElement Response ); //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + protected override AddDataApprovalPayload NewPayload( + DataApproval? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override AddDataApprovalError NewError( + AddDataApprovalErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task AddDataApprovalAsync( AddDataApprovalInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, DataApprovalService dataApprovalService, ResponseApprovalService responseApprovalService, @@ -126,26 +110,16 @@ public async Task AddDataApprovalAsync( CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + AddDataApprovalErrorCode.UNAUTHENTICATED, + AddDataApprovalErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add data approvals to this database.", - [] - ) - ); + return authorizeErrorPayload; } var errors = new List(); if (!await dataApprovalService.IsGnuPgFingerprintValid( @@ -156,7 +130,7 @@ CancellationToken cancellationToken )) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.INVALID_KEY_FINGERPRINT, $"The key fingerprint does not exist for the institution '{input.ApproverId}' or was not allowed before or was forbidden before the timestamp {input.Timestamp}.", [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] @@ -168,7 +142,7 @@ CancellationToken cancellationToken { case SigantureVerificationResult.FAILED_RECEIVING_KEY: errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.FAILED_RECEIVING_KEY, $"Failed to receive the key '{input.KeyFingerprint}' from the keyserver '{SigningService.KeyServerUrl.AbsoluteUri}'.", [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] @@ -177,7 +151,7 @@ CancellationToken cancellationToken break; case SigantureVerificationResult.BAD_SIGNATURE: errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.BAD_SIGNATURE, $"The signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' is bad. Maybe the whitespace in the message differs from that in the original message file. For example, is there a newline at the end of the message and/or are the control characters for newlines identical? Note that in the WYSIWYG GraphQL interface, using triple double quotes for multiline strings changes the newline character at the very end of the string, which may not be how it was originally. Instead of using a multiline string, you may use a oneline string with escaped characters. To turn your message into such a JSON-escaped string, you may use the command-line tool `jq` as follows: `jq --raw-input --slurp '.' < ./message.json`", [nameof(input), nameof(input.Signature).ToLowerFirst()] @@ -188,7 +162,7 @@ CancellationToken cancellationToken break; default: errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.FAILED_VERIFYING_SIGNATURE, $"Failed to verify the signature '{input.Signature}' for the message '{input.Message}' with the fingerprint '{input.KeyFingerprint}' for the reason '{sigantureVerificationResult}.", [nameof(input), nameof(input.KeyFingerprint).ToLowerFirst()] @@ -202,7 +176,7 @@ CancellationToken cancellationToken && input.Statement.Publication is null) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.MISSING_STATEMENT, "Both standard and publication are null.", [nameof(input), nameof(input.Statement).ToLowerFirst()] @@ -213,7 +187,7 @@ CancellationToken cancellationToken && input.Statement.Publication is not null) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.AMBIGUOUS_STATEMENT, "Both standard and publication are non-null.", [nameof(input), nameof(input.Statement).ToLowerFirst()] @@ -233,7 +207,7 @@ CancellationToken cancellationToken catch (Exception exception) when (exception is JsonException || exception is KeyNotFoundException) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.ILLEGAL_MESSAGE, $"The message is not valid JSON or does not have the required structure '{{\"statement\": ..., \"response\": ...}}: {exception}", [nameof(input), nameof(input.Message).ToLowerFirst()] @@ -252,7 +226,7 @@ CancellationToken cancellationToken )) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.WRONG_STATEMENT, $"The message statement '{JsonSerializer.Serialize(messagePublication)}' is not equal to the input publication statement '{JsonSerializer.Serialize(input.Statement.Publication)}'.", [nameof(input), nameof(input.Statement).ToLowerFirst(), nameof(input.Statement.Publication).ToLowerFirst()] @@ -272,7 +246,7 @@ CancellationToken cancellationToken )) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.WRONG_STATEMENT, $"The message statement '{JsonSerializer.Serialize(messageStandard)}' is not equal to the input standard statement '{JsonSerializer.Serialize(input.Statement.Standard)}'.", [nameof(input), nameof(input.Statement).ToLowerFirst(), nameof(input.Statement.Standard).ToLowerFirst()] @@ -296,7 +270,7 @@ CancellationToken cancellationToken catch (Exception exception) when (exception is HttpRequestException || exception is JsonException) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.FAILED_VERIFYING_RESPONSE, $"Failed verifying the response due to the exception: {exception}", [nameof(input), nameof(input.Query).ToLowerFirst()] @@ -309,7 +283,7 @@ CancellationToken cancellationToken ) { errors.Add( - new AddDataApprovalError( + NewError( AddDataApprovalErrorCode.WRONG_RESPONSE, $"The message response '{JsonSerializer.Serialize(message.Response)}' is not equal to the response '{JsonSerializer.Serialize(response)}' of the query '{input.Query}' against the GraphQL endpoint '{appSettings.DatabaseGraphQlEndpoint}'.", [nameof(input), nameof(input.Message).ToLowerFirst()] @@ -318,19 +292,19 @@ CancellationToken cancellationToken } if (errors.Count >= 1) { - return new AddDataApprovalPayload(errors); + return NewPayload(null, errors); } - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) + if ((await FetchDataAsync( + input, + AddDataApprovalErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) { - return new AddDataApprovalPayload( - new AddDataApprovalError( - AddDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); + return fetchDataErrorPayload; } var approval = new DataApproval( @@ -347,25 +321,21 @@ CancellationToken cancellationToken data.Approvals.Add(approval); await context.SaveChangesAsync(cancellationToken); - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + data, + AddDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { data.Approvals.Remove(approval); await context.SaveChangesAsync(cancellationToken); - - return new AddDataApprovalPayload( - approval, - new AddDataApprovalError( - AddDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new AddDataApprovalPayload(approval); + + return NewPayload(approval, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs b/backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs deleted file mode 100644 index 9ae1ee0c..00000000 --- a/backend/src/GraphQl/DataApprovals/DataApprovalPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Database.Data; -using System.Collections.Generic; - -namespace Database.GraphQl.DataApprovals; - -public abstract class DataApprovalPayload - : Payload - where TApprovalError : IUserError -{ - protected DataApprovalPayload( - DataApproval dataApproval - ) - { - DataApproval = dataApproval; - } - - protected DataApprovalPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected DataApprovalPayload( - TApprovalError error - ) - : this([error]) - { - } - - protected DataApprovalPayload( - DataApproval dataApproval, - IReadOnlyCollection errors - ) - { - DataApproval = dataApproval; - Errors = errors; - } - - protected DataApprovalPayload( - DataApproval dataApproval, - TApprovalError error - ) - : this( - dataApproval, - [error] - ) - { - } - - public DataApproval? DataApproval { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs index 12c801eb..f8a924c2 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -11,6 +11,7 @@ using HotChocolate.Types; using System.Diagnostics.CodeAnalysis; using Database.Enumerations; +using Database.Extensions; namespace Database.GraphQl.DataApprovals; @@ -19,7 +20,7 @@ public sealed record RemoveDataApprovalInput Guid DataId, DataKind DataKind, string Signature -); +) : IIdentifyDataInput; [SuppressMessage("Naming", "CA1707")] public enum RemoveDataApprovalErrorCode @@ -39,76 +40,56 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class RemoveDataApprovalPayload - : DataApprovalPayload -{ - public RemoveDataApprovalPayload( - DataApproval dataApproval - ) - : base(dataApproval) - { - } - - public RemoveDataApprovalPayload( - RemoveDataApprovalError error - ) - : base(error) - { - } - - public RemoveDataApprovalPayload( - DataApproval dataApproval, - RemoveDataApprovalError error - ) - : base(dataApproval, error) - { - } -} +public sealed record RemoveDataApprovalPayload( + DataApproval? DataApproval, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class RemoveDataApprovalMutation +: DataMutationBase { + protected override RemoveDataApprovalPayload NewPayload( + DataApproval? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override RemoveDataApprovalError NewError( + RemoveDataApprovalErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task RemoveDataApprovalAsync( RemoveDataApprovalInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + RemoveDataApprovalErrorCode.UNAUTHENTICATED, + RemoveDataApprovalErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNAUTHORIZED, - $"The current user is not authorized to add data approvals to this database.", - [] - ) - ); + return authorizeErrorPayload; } - var data = await context.GetDataAsync(input.DataId, input.DataKind, cancellationToken); - if (data is null) + if ((await FetchDataAsync( + input, + RemoveDataApprovalErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.UNKNOWN_DATA, - $"Unknown data.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); + return fetchDataErrorPayload; } var approval = data.Approvals @@ -116,37 +97,34 @@ CancellationToken cancellationToken .SingleOrDefault(); if (approval is null) { - return new RemoveDataApprovalPayload( - new RemoveDataApprovalError( + return NewPayload( + null, + [NewError( RemoveDataApprovalErrorCode.UNKNOWN_APPROVAL, $"Unknown data approval.", [nameof(input), nameof(input.Signature).ToLowerFirst()] - ) + )] ); } data.Approvals.Remove(approval); await context.SaveChangesAsync(cancellationToken); - try - { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + data, + RemoveDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { data.Approvals.Add(approval); await context.SaveChangesAsync(cancellationToken); - - return new RemoveDataApprovalPayload( - approval, - new RemoveDataApprovalError( - RemoveDataApprovalErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new RemoveDataApprovalPayload(approval); + + return NewPayload(approval, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs similarity index 89% rename from backend/src/GraphQl/DataX/DataMutationBase.cs rename to backend/src/GraphQl/DataMutationBase.cs index 69432353..f53cbda2 100644 --- a/backend/src/GraphQl/DataX/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -6,7 +6,7 @@ using Database.Enumerations; using Database.Services; -namespace Database.GraphQl.DataX; +namespace Database.GraphQl; public interface IIdentifyDataInput { @@ -16,7 +16,7 @@ public interface IIdentifyDataInput public abstract class DataMutationBase : MutationBase -where TData : class, IData +where TData : class where TPayload : class { protected async Task> FetchDataAsync( @@ -43,7 +43,7 @@ CancellationToken cancellationToken } protected async Task> CreateResponseApprovalAsync( - TData data, + IData data, TErrorCode creatingResponseApprovalFailedErrorCode, ResponseApprovalService responseApprovalService, ApplicationDbContext context, @@ -59,10 +59,10 @@ CancellationToken cancellationToken { return new Result.Error( NewPayload( - data, + null, [NewError( creatingResponseApprovalFailedErrorCode, - $"Signing failed with message: {exception.Message}", + $"Failed creating a response approval for the data set {data.Id} named '{data.Name}' with the error message: {exception.Message}", [] )] ) diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index 741845e6..96cced53 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -11,6 +11,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Name(nameof(IData)[1..] + GraphQlConstants.FilterInputSuffix); + descriptor.Name(nameof(DataFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 05975da6..646d6925 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -32,5 +32,8 @@ IInterfaceTypeDescriptor descriptor descriptor .Field(x => x.Locale) .Type>(); + descriptor + .Field(x => x.Approval) + .Type>>(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index dff31d87..f5067c70 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -28,5 +28,8 @@ IObjectTypeDescriptor descriptor .Field(DataType.TimestampFieldName) .Type>() .ResolveWith(t => t.GetTimestamp()); + descriptor + .Field(x => x.Approval) + .Type>>(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index e3347487..dd628d3b 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -33,7 +33,7 @@ IReadOnlyList Path public sealed record DeleteDataPayload( // [GraphQLType>>] IReadOnlyCollection? Errors -); +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class DeleteDataMutation diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index da7f36dc..35391342 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -43,7 +43,7 @@ IReadOnlyList Path public sealed record UpdateDataPayload( IData? Data, IReadOnlyCollection? Errors -); +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateDataMutation diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 62b27865..6d651303 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -10,6 +10,7 @@ using System.Diagnostics.CodeAnalysis; using HotChocolate; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.GeometricDataX; @@ -61,93 +62,67 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CreateGeometricDataPayload - : GeometricDataPayload -{ - public CreateGeometricDataPayload( - GeometricData geometricData - ) - : base(geometricData) - { - } - - public CreateGeometricDataPayload( - CreateGeometricDataError error - ) - : base(error) - { - } - - public CreateGeometricDataPayload( - GeometricData geometricData, - CreateGeometricDataError error - ) - : base(geometricData, error) - { - } -} +public sealed record CreateGeometricDataPayload( + GeometricData? GeometricData, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreateGeometricDataMutation +: DataMutationBase { // [UseUserManager] //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + protected override CreateGeometricDataPayload NewPayload( + GeometricData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateGeometricDataError NewError( + CreateGeometricDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new CreateGeometricDataPayload( - new CreateGeometricDataError( - CreateGeometricDataErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + CreateGeometricDataErrorCode.UNAUTHENTICATED, + CreateGeometricDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreateGeometricDataPayload( - new CreateGeometricDataError( - CreateGeometricDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create geometric data in this database.", - [] - ) - ); + return authorizeErrorPayload; } var geometricData = input.ToDomainModel(currentUser.Uuid); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken); - try - { - geometricData.Approval = await responseApprovalService.CreateResponseApproval(geometricData, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + geometricData, + CreateGeometricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { context.Remove(geometricData); await context.SaveChangesAsync(cancellationToken); - - return new CreateGeometricDataPayload( - geometricData, - new CreateGeometricDataError( - CreateGeometricDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new CreateGeometricDataPayload(geometricData); + return NewPayload(geometricData, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs deleted file mode 100644 index ff16e1db..00000000 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.GeometricDataX; - -public abstract class GeometricDataPayload - : Payload - where TGeometricDataError : IUserError -{ - protected GeometricDataPayload( - GeometricData geometricData - ) - { - GeometricData = geometricData; - } - - protected GeometricDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected GeometricDataPayload( - TGeometricDataError error - ) - : this([error]) - { - } - - protected GeometricDataPayload( - GeometricData geometricData, - IReadOnlyCollection errors - ) - { - GeometricData = geometricData; - Errors = errors; - } - - protected GeometricDataPayload( - GeometricData geometricData, - TGeometricDataError error - ) - : this( - geometricData, - [error] - ) - { - } - - public GeometricData? GeometricData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index fd9ba77d..7e4fd94e 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -18,6 +18,7 @@ using Database.GraphQl.Extensions; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.GetHttpsResources; @@ -29,15 +30,16 @@ public sealed record CreateGetHttpsResourceInput( Guid? ParentId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod -); +) : IIdentifyDataInput; [SuppressMessage("Naming", "CA1707")] public enum CreateGetHttpsResourceErrorCode { UNKNOWN, - UNKNOWN_DATA, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED } public sealed record CreateGetHttpsResourceError( @@ -47,23 +49,10 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CreateGetHttpsResourcePayload - : GetHttpsResourcePayload -{ - public CreateGetHttpsResourcePayload( - GetHttpsResource getHttpsResource - ) - : base(getHttpsResource) - { - } - - public CreateGetHttpsResourcePayload( - CreateGetHttpsResourceError error - ) - : base(error) - { - } -} +public sealed record CreateGetHttpsResourcePayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; public static partial class CreateGetHttpsResourceMutationLogging { @@ -76,56 +65,50 @@ public static partial class CreateGetHttpsResourceMutationLogging [ExtendObjectType(nameof(Mutation))] public sealed class CreateGetHttpsResourceMutation +: DataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override CreateGetHttpsResourcePayload NewPayload( + GetHttpsResource? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateGetHttpsResourceError NewError( + CreateGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, - UserService userService, + ResponseApprovalService responseApprovalService, CommonAuthorization authorization, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - return new CreateGetHttpsResourcePayload( - new CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + CreateGetHttpsResourceErrorCode.UNAUTHENTICATED, + CreateGetHttpsResourceErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreateGetHttpsResourcePayload( - new CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create GET HTTPS resource in this database.", - [] - ) - ); + return authorizeErrorPayload; } - IData? data = input.DataKind switch + if ((await FetchDataAsync( + input, + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) { - DataKind.CALORIMETRIC_DATA => await context.CalorimetricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.GEOMETRIC_DATA => await context.GeometricData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.HYGROTHERMAL_DATA => await context.HygrothermalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.OPTICAL_DATA => await context.OpticalData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - DataKind.PHOTOVOLTAIC_DATA => await context.PhotovoltaicData.AsQueryable().SingleOrDefaultAsync(d => d.Id == input.DataId, cancellationToken), - _ => throw new ArgumentException($"The data kind {input.DataKind} is not supported.") - }; - if (data is null) - { - return new CreateGetHttpsResourcePayload( - new CreateGetHttpsResourceError( - CreateGetHttpsResourceErrorCode.UNKNOWN_DATA, - $"There is no data of kind {input.DataKind} with identifier {input.DataId}.", - [nameof(input), nameof(input.DataId).ToLowerFirst()] - ) - ); + return fetchDataErrorPayload; } var getHttpsResource = new GetHttpsResource( @@ -138,27 +121,27 @@ CancellationToken cancellationToken input.DataKind == DataKind.OPTICAL_DATA ? input.DataId : null, input.DataKind == DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, input.ParentId, - input.ArchivedFilesMetaInformation.Select(i => - new FileMetaInformation( - i.Path, - i.DataFormatId - ) - ).ToList(), - input.AppliedConversionMethod is null - ? null - : new ToTreeVertexAppliedConversionMethod( - input.AppliedConversionMethod.MethodId, - input.AppliedConversionMethod.Arguments.Select(a => - new NamedMethodArgument( - a.Name, - a.Value - ) - ).ToList(), - input.AppliedConversionMethod.SourceName - ) + input.ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), + input?.AppliedConversionMethod.ToDomainModel() ); context.GetHttpsResources.Add(getHttpsResource); await context.SaveChangesAsync(cancellationToken); - return new CreateGetHttpsResourcePayload(getHttpsResource); + + if ((await CreateResponseApprovalAsync( + data, + CreateGetHttpsResourceErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(getHttpsResource); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(getHttpsResource, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs deleted file mode 100644 index 031685e7..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourcePayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.GetHttpsResources; - -public abstract class GetHttpsResourcePayload - : Payload - where TGetHttpsResourceError : IUserError -{ - protected GetHttpsResourcePayload( - GetHttpsResource getHttpsResource - ) - { - GetHttpsResource = getHttpsResource; - } - - protected GetHttpsResourcePayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected GetHttpsResourcePayload( - TGetHttpsResourceError error - ) - : this([error]) - { - } - - protected GetHttpsResourcePayload( - GetHttpsResource getHttpsResource, - IReadOnlyCollection errors - ) - { - GetHttpsResource = getHttpsResource; - Errors = errors; - } - - protected GetHttpsResourcePayload( - GetHttpsResource getHttpsResource, - TGetHttpsResourceError error - ) - : this( - getHttpsResource, - [error] - ) - { - } - - public GetHttpsResource? GetHttpsResource { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index efa88977..44b22369 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -4,20 +4,17 @@ using System.Threading.Tasks; using Database.Data; using Database.Authorization; -using Database.Enumerations; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using Database.Services; -using GraphQL.Client.Abstractions.Utilities; using System.Collections.Concurrent; using Microsoft.Extensions.Logging; -using Database.Utilities; using HotChocolate.Data; using GreenDonut.Data; -using Microsoft.EntityFrameworkCore.Query; using Database.GraphQl.Extensions; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.GetHttpsResources; @@ -27,7 +24,8 @@ public enum RecomputeGetHttpsResourceHashValuesErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, - FAILED + FAILED, + CREATING_RESPONSE_APPROVAL_FAILED } public sealed record RecomputeGetHttpsResourceHashValuesError( @@ -37,35 +35,10 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class RecomputeGetHttpsResourceHashValuesPayload - : Payload -{ - public IReadOnlyCollection? GetHttpsResources { get; } - public IReadOnlyCollection? Errors { get; } - - public RecomputeGetHttpsResourceHashValuesPayload( - IReadOnlyCollection? getHttpsResources - ) - { - GetHttpsResources = getHttpsResources; - } - - public RecomputeGetHttpsResourceHashValuesPayload( - RecomputeGetHttpsResourceHashValuesError error - ) - { - Errors = [error]; - } - - public RecomputeGetHttpsResourceHashValuesPayload( - IReadOnlyCollection getHttpsResources, - IReadOnlyCollection errors - ) - : this(getHttpsResources) - { - Errors = errors; - } -} +public sealed record RecomputeGetHttpsResourceHashValuesPayload( + IReadOnlyCollection? GetHttpsResources, + IReadOnlyCollection? Errors +) : Payload; public static partial class RecomputeGetHttpsResourceHashValuesMutationLogging { @@ -78,40 +51,48 @@ public static partial class RecomputeGetHttpsResourceHashValuesMutationLogging [ExtendObjectType(nameof(Mutation))] public sealed class RecomputeGetHttpsResourceHashValuesMutation +: DataMutationBase, RecomputeGetHttpsResourceHashValuesPayload, RecomputeGetHttpsResourceHashValuesError, RecomputeGetHttpsResourceHashValuesErrorCode> { + protected override RecomputeGetHttpsResourceHashValuesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override RecomputeGetHttpsResourceHashValuesError NewError( + RecomputeGetHttpsResourceHashValuesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + [UseFiltering] public async Task RecomputeGetHttpsResourceHashValuesAsync( ApplicationDbContext context, QueryContext queryContext, - UserService userService, CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, + RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new RecomputeGetHttpsResourceHashValuesPayload( - new RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new RecomputeGetHttpsResourceHashValuesPayload( - new RecomputeGetHttpsResourceHashValuesError( - RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, - $"The current user is not authorized to recompute GET HTTPS resource hash values in this database.", - [] - ) - ); + return authorizeErrorPayload; } + var resources = await context.GetHttpsResources + .Include(r => r.CalorimetricData) + .Include(r => r.GeometricData) + .Include(r => r.HygrothermalData) + .Include(r => r.OpticalData) + .Include(r => r.PhotovoltaicData) .With(queryContext, sort => sort.StabilizeOrder()) .ToListAsync(cancellationToken); var errors = new ConcurrentBag(); @@ -123,26 +104,39 @@ await Parallel.ForEachAsync( try { await resource.RecomputeHashValue(cancellationToken); + if ((await CreateResponseApprovalAsync( + resource.Data ?? throw new InvalidOperationException("Impossible!"), + RecomputeGetHttpsResourceHashValuesErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + // TODO Undo hash value recomputation? + // await context.SaveChangesAsync(cancellationToken); + errors.AddRange(createResponseApprovalErrorPayload.Errors); + } } catch (Exception exception) { logger.FailedRecomputingHashValue(resource.Id, exception); errors.Add( - new RecomputeGetHttpsResourceHashValuesError( + NewError( RecomputeGetHttpsResourceHashValuesErrorCode.FAILED, $"Recomputing the hash value for the GET HTTPS resource with ID {resource.Id} failed with message: {exception.Message}", [] ) ); } - } ); await context.SaveChangesAsync(cancellationToken); if (!errors.IsEmpty) { - return new RecomputeGetHttpsResourceHashValuesPayload(resources, errors); + return NewPayload(resources, errors); } - return new RecomputeGetHttpsResourceHashValuesPayload(resources); + return NewPayload(resources, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 3261f011..d240890f 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -10,6 +10,7 @@ using HotChocolate; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.HygrothermalDataX; @@ -60,90 +61,65 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CreateHygrothermalDataPayload - : HygrothermalDataPayload -{ - public CreateHygrothermalDataPayload( - HygrothermalData hygrothermalData - ) - : base(hygrothermalData) - { - } - - public CreateHygrothermalDataPayload( - CreateHygrothermalDataError error - ) - : base(error) - { - } - - public CreateHygrothermalDataPayload( - HygrothermalData hygrothermalData, - CreateHygrothermalDataError error - ) - : base(hygrothermalData, error) - { - } -} +public sealed record CreateHygrothermalDataPayload( + HygrothermalData? HygrothermalData, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreateHygrothermalDataMutation +: DataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override CreateHygrothermalDataPayload NewPayload( + HygrothermalData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateHygrothermalDataError NewError( + CreateHygrothermalDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + CreateHygrothermalDataErrorCode.UNAUTHENTICATED, + CreateHygrothermalDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreateHygrothermalDataPayload( - new CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new CreateHygrothermalDataPayload( - new CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create hygrothermal data in this database.", - [] - ) - ); + return authorizeErrorPayload; } var hygrothermalData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); - try - { - hygrothermalData.Approval = await responseApprovalService.CreateResponseApproval(hygrothermalData, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + hygrothermalData, + CreateHygrothermalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { context.Remove(hygrothermalData); await context.SaveChangesAsync(cancellationToken); - - return new CreateHygrothermalDataPayload( - hygrothermalData, - new CreateHygrothermalDataError( - CreateHygrothermalDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new CreateHygrothermalDataPayload(hygrothermalData); + + return NewPayload(hygrothermalData, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs deleted file mode 100644 index c952cde0..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.HygrothermalDataX; - -public abstract class HygrothermalDataPayload - : Payload - where THygrothermalDataError : IUserError -{ - protected HygrothermalDataPayload( - HygrothermalData hygrothermalData - ) - { - HygrothermalData = hygrothermalData; - } - - protected HygrothermalDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected HygrothermalDataPayload( - THygrothermalDataError error - ) - : this([error]) - { - } - - protected HygrothermalDataPayload( - HygrothermalData hygrothermalData, - IReadOnlyCollection errors - ) - { - HygrothermalData = hygrothermalData; - Errors = errors; - } - - protected HygrothermalDataPayload( - HygrothermalData hygrothermalData, - THygrothermalDataError error - ) - : this( - hygrothermalData, - [error] - ) - { - } - - public HygrothermalData? HygrothermalData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index ab02493a..d60dc617 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -31,53 +31,10 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CalculateMethodPayload - : Payload -{ - public CalculateMethodPayload( - JsonElement result - ) - { - Result = result; - } - - public CalculateMethodPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - public CalculateMethodPayload( - CalculateMethodError error - ) - : this([error]) - { - } - - public CalculateMethodPayload( - JsonElement result, - IReadOnlyCollection errors - ) - { - Result = result; - Errors = errors; - } - - public CalculateMethodPayload( - JsonElement result, - CalculateMethodError error - ) - : this( - result, - [error] - ) - { - } - - public JsonElement? Result { get; } - public IReadOnlyCollection? Errors { get; } -} +public sealed record CalculateMethodPayload( + JsonElement? Result, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Query))] public sealed class MethodQueries @@ -115,11 +72,12 @@ CancellationToken cancellationToken if (method is null) { return new CalculateMethodPayload( - new CalculateMethodError( + null, + [new CalculateMethodError( CalculateMethodErrorCode.UNKNOWN_METHOD, $"The method is unknown.", [nameof(methodId)] - ) + )] ); } using var stream = data.OpenReadStream(); @@ -129,7 +87,7 @@ CancellationToken cancellationToken cancellationToken ); var result = method.Calculate(jsonData.RootElement); - return new CalculateMethodPayload(result); + return new CalculateMethodPayload(result, null); } public async Task CalculateMethodAsync( @@ -146,11 +104,12 @@ CancellationToken cancellationToken if (method is null) { return new CalculateMethodPayload( - new CalculateMethodError( + null, + [new CalculateMethodError( CalculateMethodErrorCode.UNKNOWN_METHOD, $"The method is unknown.", [nameof(methodId)] - ) + )] ); } var database = await GraphQlRequestHelper.TransformExceptionsAsync( @@ -166,11 +125,12 @@ CancellationToken cancellationToken if (database is null) { return new CalculateMethodPayload( - new CalculateMethodError( + null, + [new CalculateMethodError( CalculateMethodErrorCode.UNKNOWN_DATABASE, $"The database is unknown.", [nameof(dataReference), nameof(dataReference.DatabaseId).ToLowerFirst()] - ) + )] ); } var query = ConstructQuery(dataReference.DataKind); @@ -200,11 +160,12 @@ CancellationToken cancellationToken } } return new CalculateMethodPayload( - new CalculateMethodError( + null, + [new CalculateMethodError( CalculateMethodErrorCode.DATA_QUERY_FAILED, $"Failed to query database {database.Locator} for the data.", [nameof(dataReference)] - ) + )] ); } var locator = response.Data.Data.ResourceTree.Root.Value.Locator; @@ -214,7 +175,7 @@ CancellationToken cancellationToken locator, cancellationToken ); var result = method.Calculate(data); - return new CalculateMethodPayload(result); + return new CalculateMethodPayload(result, null); } private static string ConstructQuery(DataKind dataKind) diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 74b71855..a32c923e 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -85,8 +85,7 @@ IReadOnlyList Path public sealed record CreateOpticalDataPayload( OpticalData? OpticalData, IReadOnlyCollection? Errors -) -: OpticalDataPayload(OpticalData, Errors); +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreateOpticalDataMutation diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs deleted file mode 100644 index d0c552f9..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataPayload.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.OpticalDataX; - -public abstract record OpticalDataPayload( - OpticalData? OpticalData, - IReadOnlyCollection? Errors -) -where TOpticalDataError : IUserError; \ No newline at end of file diff --git a/backend/src/GraphQl/Payload.cs b/backend/src/GraphQl/Payload.cs index 9ba644dd..86ca2954 100644 --- a/backend/src/GraphQl/Payload.cs +++ b/backend/src/GraphQl/Payload.cs @@ -2,7 +2,7 @@ namespace Database.GraphQl; -public abstract class Payload +public abstract record Payload { } diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index f6ecffa0..5123047c 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -10,6 +10,7 @@ using HotChocolate; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.Extensions; namespace Database.GraphQl.PhotovoltaicDataX; @@ -60,91 +61,65 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public sealed class CreatePhotovoltaicDataPayload - : PhotovoltaicDataPayload -{ - public CreatePhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData - ) - : base(photovoltaicData) - { - } - - public CreatePhotovoltaicDataPayload( - CreatePhotovoltaicDataError error - ) - : base(error) - { - } - - public CreatePhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData, - CreatePhotovoltaicDataError error - ) - : base(photovoltaicData, error) - { - } -} +public sealed record CreatePhotovoltaicDataPayload( + PhotovoltaicData? PhotovoltaicData, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreatePhotovoltaicDataMutation +: DataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override CreatePhotovoltaicDataPayload NewPayload( + PhotovoltaicData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreatePhotovoltaicDataError NewError( + CreatePhotovoltaicDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser( - cancellationToken); - if (currentUser is null) - { - return new CreatePhotovoltaicDataPayload( - new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if ((await AuthorizeAsync( + CreatePhotovoltaicDataErrorCode.UNAUTHENTICATED, + CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreatePhotovoltaicDataPayload( - new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create photovoltaic data in this database.", - [] - ) - ); + return authorizeErrorPayload; } var photovoltaicData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); - try - { - photovoltaicData.Approval = await responseApprovalService.CreateResponseApproval(photovoltaicData, cancellationToken); - await context.SaveChangesAsync(cancellationToken); - } - catch (Exception exception) + if ((await CreateResponseApprovalAsync( + photovoltaicData, + CreatePhotovoltaicDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { context.Remove(photovoltaicData); await context.SaveChangesAsync(cancellationToken); - - return new CreatePhotovoltaicDataPayload( - photovoltaicData, - new CreatePhotovoltaicDataError( - CreatePhotovoltaicDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Signing failed with message: {exception.Message}", - [] - ) - ); + return createResponseApprovalErrorPayload; } - return new CreatePhotovoltaicDataPayload(photovoltaicData); + + return NewPayload(photovoltaicData, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs deleted file mode 100644 index 22be4489..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataPayload.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using Database.Data; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public abstract class PhotovoltaicDataPayload - : Payload - where TPhotovoltaicDataError : IUserError -{ - protected PhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData - ) - { - PhotovoltaicData = photovoltaicData; - } - - protected PhotovoltaicDataPayload( - IReadOnlyCollection errors - ) - { - Errors = errors; - } - - protected PhotovoltaicDataPayload( - TPhotovoltaicDataError error - ) - : this([error]) - { - } - - protected PhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData, - IReadOnlyCollection errors - ) - { - PhotovoltaicData = photovoltaicData; - Errors = errors; - } - - protected PhotovoltaicDataPayload( - PhotovoltaicData photovoltaicData, - TPhotovoltaicDataError error - ) - : this( - photovoltaicData, - [error] - ) - { - } - - public PhotovoltaicData? PhotovoltaicData { get; } - public IReadOnlyCollection? Errors { get; } -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs new file mode 100644 index 00000000..f31de19b --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class CreateResponseApprovalsFilterType +: ResponseApprovalFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(CreateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index b7840bbc..f35ecc5a 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -5,11 +5,17 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using GreenDonut.Data; using Database.Authorization; using Database.Data; using Database.Services; +using HotChocolate; +using HotChocolate.Data; using HotChocolate.Types; +using HotChocolate.Data.Filters; using Microsoft.Extensions.Logging; +using Database.GraphQl.Extensions; +using Database.Extensions; namespace Database.GraphQl.ResponseApprovals; @@ -34,108 +40,113 @@ public static partial class CreateResponseApprovalsMutationLogging { [LoggerMessage( Level = LogLevel.Warning, - Message = "Creating response approval for data of type {Type} with ID {Id} failed." + Message = "Creating response approval for data of type {Type} with ID {Id} failed with the errors {Errors}." )] - public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); + public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, IEnumerable? errors); } -public sealed class CreateResponseApprovalsPayload - : Payload -{ - public IReadOnlyCollection? Data { get; } - public IReadOnlyCollection? Errors { get; } - - public CreateResponseApprovalsPayload( - IReadOnlyCollection data - ) - { - Data = data; - } - - public CreateResponseApprovalsPayload( - CreateResponseApprovalsError error - ) - { - Errors = [error]; - } - - public CreateResponseApprovalsPayload( - IReadOnlyCollection data, - IReadOnlyCollection errors - ) : this(data) - { - Errors = errors; - } -} +public sealed record CreateResponseApprovalsPayload( + IReadOnlyCollection? Data, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class CreateResponseApprovalsMutation +: DataMutationBase, CreateResponseApprovalsPayload, CreateResponseApprovalsError, CreateResponseApprovalsErrorCode> { //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] + protected override CreateResponseApprovalsPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateResponseApprovalsError NewError( + CreateResponseApprovalsErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [UseFiltering] public async Task CreateResponseApprovalsAsync( + QueryContext queryContext, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + CreateResponseApprovalsErrorCode.UNAUTHENTICATED, + CreateResponseApprovalsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new CreateResponseApprovalsPayload( - new CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new CreateResponseApprovalsPayload( - new CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to create response approvals in this database.", - [] - ) - ); + return authorizeErrorPayload; } var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - context.CalorimetricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable() - .Union(context.GeometricData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) - .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) - .Union(context.OpticalData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()) - .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval == null).ToAsyncEnumerable()), + ( + context.CalorimetricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval == null) + .ToAsyncEnumerable() + ) + .Union( + context.GeometricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval == null) + .ToAsyncEnumerable() + ) + .Union( + context.HygrothermalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval == null) + .ToAsyncEnumerable() + ) + .Union( + context.OpticalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval == null) + .ToAsyncEnumerable() + ) + .Union( + context.PhotovoltaicData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval == null) + .ToAsyncEnumerable() + ), cancellationToken, async (data, cancellationToken) => { - try + if ((await CreateResponseApprovalAsync( + data, + CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - dataSets.Add(data); + logger.FailedCreatingResponseApproval(data.GetType(), data.Id, createResponseApprovalErrorPayload.Errors?.Select(_ => _.Message)); + errors.AddRange(createResponseApprovalErrorPayload.Errors); } - catch (Exception exception) + else { - logger.FailedCreatingResponseApproval(data.GetType(), data.Id, exception); - errors.Add( - new CreateResponseApprovalsError( - CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - $"Creating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", - [] - ) - ); + dataSets.Add(data); } } ); await context.SaveChangesAsync(cancellationToken); if (!errors.IsEmpty) { - return new CreateResponseApprovalsPayload(dataSets, errors); + return NewPayload(dataSets, errors); } - return new CreateResponseApprovalsPayload(dataSets); + return NewPayload(dataSets, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs new file mode 100644 index 00000000..894d3206 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -0,0 +1,15 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.ResponseApprovals; + +public abstract class ResponseApprovalFilterType +: FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs new file mode 100644 index 00000000..ca9c95d6 --- /dev/null +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.ResponseApprovals; + +public sealed class UpdateResponseApprovalsFilterType +: ResponseApprovalFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(UpdateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index c00dbb89..7baa5256 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -5,11 +5,17 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using GreenDonut.Data; using Database.Authorization; using Database.Data; using Database.Services; +using HotChocolate; using HotChocolate.Types; +using HotChocolate.Data; +using HotChocolate.Data.Filters; using Microsoft.Extensions.Logging; +using Database.GraphQl.Extensions; +using Database.Extensions; namespace Database.GraphQl.ResponseApprovals; @@ -20,7 +26,7 @@ public enum UpdateResponseApprovalsErrorCode UNAUTHORIZED, UNAUTHENTICATED, UNKNOWN_DATABASE, - UPDATING_RESPONSE_APPROVAL_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } public sealed record UpdateResponseApprovalsError( @@ -34,107 +40,113 @@ public static partial class UpdateResponseApprovalsMutationLogging { [LoggerMessage( Level = LogLevel.Warning, - Message = "Updating response approval for data of type {Type} with ID {Id} failed." + Message = "Updating response approval for data of type {Type} with ID {Id} failed with the errors {Errors}." )] - public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); + public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, IEnumerable? errors); } -public sealed class UpdateResponseApprovalsPayload - : Payload -{ - public IReadOnlyCollection? Data { get; } - public IReadOnlyCollection? Errors { get; } - - public UpdateResponseApprovalsPayload( - IReadOnlyCollection data - ) - { - Data = data; - } - - public UpdateResponseApprovalsPayload( - UpdateResponseApprovalsError error - ) - { - Errors = [error]; - } - - public UpdateResponseApprovalsPayload( - IReadOnlyCollection data, - IReadOnlyCollection errors - ) : this(data) - { - Errors = errors; - } -} +public sealed record UpdateResponseApprovalsPayload( + IReadOnlyCollection? Data, + IReadOnlyCollection? Errors +) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateResponseApprovalsMutation +: DataMutationBase, UpdateResponseApprovalsPayload, UpdateResponseApprovalsError, UpdateResponseApprovalsErrorCode> { + protected override UpdateResponseApprovalsPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateResponseApprovalsError NewError( + UpdateResponseApprovalsErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [UseFiltering] public async Task UpdateResponseApprovalsAsync( + QueryContext queryContext, ApplicationDbContext context, - UserService userService, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) + if ((await AuthorizeAsync( + UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, + UpdateResponseApprovalsErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUser, out var authorizeErrorPayload) + ) { - return new UpdateResponseApprovalsPayload( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, - $"The user is not authenticated.", - [] - ) - ); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) - { - return new UpdateResponseApprovalsPayload( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UNAUTHORIZED, - $"The current user is not authorized to update response approvals in this database.", - [] - ) - ); + return authorizeErrorPayload; } + var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - context.CalorimetricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable() - .Union(context.GeometricData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.HygrothermalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.OpticalData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()) - .Union(context.PhotovoltaicData.AsQueryable().Where(d => d.Approval != null).ToAsyncEnumerable()), + ( + context.CalorimetricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval != null) + .ToAsyncEnumerable() + ) + .Union( + context.GeometricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval != null) + .ToAsyncEnumerable() + ) + .Union( + context.HygrothermalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval != null) + .ToAsyncEnumerable() + ) + .Union( + context.OpticalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval != null) + .ToAsyncEnumerable() + ) + .Union( + context.PhotovoltaicData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(d => d.Approval != null) + .ToAsyncEnumerable() + ), cancellationToken, async (data, cancellationToken) => { - try + if ((await CreateResponseApprovalAsync( + data, + UpdateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) { - data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); - dataSets.Add(data); + logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, createResponseApprovalErrorPayload.Errors?.Select(_ => _.Message)); + errors.AddRange(createResponseApprovalErrorPayload.Errors); } - catch (Exception exception) + else { - logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, exception); - errors.Add( - new UpdateResponseApprovalsError( - UpdateResponseApprovalsErrorCode.UPDATING_RESPONSE_APPROVAL_FAILED, - $"Updating response approval for data `{data}` with ID {data.Id} failed with message: {exception.Message}", - [] - ) - ); + dataSets.Add(data); } } ); await context.SaveChangesAsync(cancellationToken); if (!errors.IsEmpty) { - return new UpdateResponseApprovalsPayload(dataSets, errors); + return NewPayload(dataSets, errors); } - return new UpdateResponseApprovalsPayload(dataSets); + return NewPayload(dataSets, null); } } \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 1290d4ca..ad882765 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -15,7 +15,7 @@ interface Approval { interface Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -70,8 +70,8 @@ type AddInstitutionAccessRightsError implements UserError { } type AddInstitutionAccessRightsPayload { - accessRights: InstitutionAccessRights errors: [AddInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights query: Query! } @@ -98,7 +98,7 @@ type CalculateMethodPayload { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -305,7 +305,7 @@ type FileMetaInformation { type GeometricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -398,7 +398,7 @@ type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -469,7 +469,7 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") @@ -477,7 +477,7 @@ type Mutation { updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") } type NamedMethodArgument { @@ -503,7 +503,7 @@ type OpenEndedDateTimeRange { type OpticalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! cielabColors: [CielabColor!]! coatedSide: CoatedSide @@ -567,7 +567,7 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -750,8 +750,8 @@ type UpdateInstitutionAccessRightsError implements UserError { } type UpdateInstitutionAccessRightsPayload { - accessRights: InstitutionAccessRights errors: [UpdateInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights query: Query! } @@ -961,6 +961,11 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } +input CreateResponseApprovalsFilterInput { + and: [CreateResponseApprovalsFilterInput!] + or: [CreateResponseApprovalsFilterInput!] +} + input CrossDatabaseDataReferenceInput { databaseId: Uuid! dataId: Uuid! @@ -1631,6 +1636,11 @@ input UpdateInstitutionAccessRightsInput { periodInDays: Int! } +input UpdateResponseApprovalsFilterInput { + and: [UpdateResponseApprovalsFilterInput!] + or: [UpdateResponseApprovalsFilterInput!] +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -1775,9 +1785,10 @@ enum CreateGeometricDataErrorCode { enum CreateGetHttpsResourceErrorCode { UNKNOWN - UNKNOWN_DATA UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateHygrothermalDataErrorCode { @@ -1879,6 +1890,7 @@ enum RecomputeGetHttpsResourceHashValuesErrorCode { UNAUTHORIZED UNAUTHENTICATED FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum RemoveDataApprovalErrorCode { @@ -1954,7 +1966,7 @@ enum UpdateResponseApprovalsErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATABASE - UPDATING_RESPONSE_APPROVAL_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum UriHostNameType { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 1290d4ca..ad882765 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -15,7 +15,7 @@ interface Approval { interface Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -70,8 +70,8 @@ type AddInstitutionAccessRightsError implements UserError { } type AddInstitutionAccessRightsPayload { - accessRights: InstitutionAccessRights errors: [AddInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights query: Query! } @@ -98,7 +98,7 @@ type CalculateMethodPayload { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -305,7 +305,7 @@ type FileMetaInformation { type GeometricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -398,7 +398,7 @@ type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -469,7 +469,7 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals: CreateResponseApprovalsPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") @@ -477,7 +477,7 @@ type Mutation { updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals: UpdateResponseApprovalsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") } type NamedMethodArgument { @@ -503,7 +503,7 @@ type OpenEndedDateTimeRange { type OpticalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! cielabColors: [CielabColor!]! coatedSide: CoatedSide @@ -567,7 +567,7 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval + approval: ResponseApproval! approvals: [DataApproval!]! componentId: Uuid! createdAt: DateTime! @@ -750,8 +750,8 @@ type UpdateInstitutionAccessRightsError implements UserError { } type UpdateInstitutionAccessRightsPayload { - accessRights: InstitutionAccessRights errors: [UpdateInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights query: Query! } @@ -961,6 +961,11 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } +input CreateResponseApprovalsFilterInput { + and: [CreateResponseApprovalsFilterInput!] + or: [CreateResponseApprovalsFilterInput!] +} + input CrossDatabaseDataReferenceInput { databaseId: Uuid! dataId: Uuid! @@ -1631,6 +1636,11 @@ input UpdateInstitutionAccessRightsInput { periodInDays: Int! } +input UpdateResponseApprovalsFilterInput { + and: [UpdateResponseApprovalsFilterInput!] + or: [UpdateResponseApprovalsFilterInput!] +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -1775,9 +1785,10 @@ enum CreateGeometricDataErrorCode { enum CreateGetHttpsResourceErrorCode { UNKNOWN - UNKNOWN_DATA UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateHygrothermalDataErrorCode { @@ -1879,6 +1890,7 @@ enum RecomputeGetHttpsResourceHashValuesErrorCode { UNAUTHORIZED UNAUTHENTICATED FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum RemoveDataApprovalErrorCode { @@ -1954,7 +1966,7 @@ enum UpdateResponseApprovalsErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATABASE - UPDATING_RESPONSE_APPROVAL_FAILED + CREATING_RESPONSE_APPROVAL_FAILED } enum UriHostNameType { From 26c50e2baa164eeeda72800e452715b054070db1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 30 Oct 2025 18:08:26 +0100 Subject: [PATCH 530/878] Align model snapshot and one migration with the actually applied migration script `migrate.sql` --- .../ApplicationDbContextModelSnapshot.cs | 6 +----- ...24734_AddObserverAndIlluminantToCielabColor.sql | 14 -------------- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 8437722d..640d70d6 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1858,10 +1858,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.ToTable("CielabColor", "database", t => { - t.HasCheckConstraint("CK_OpticalData_CielabColors_AStar", "\"AStar\" >= 0.0 AND \"AStar\" <= 100.0"); - - t.HasCheckConstraint("CK_OpticalData_CielabColors_BStar", "\"BStar\" >= 0.0 AND \"BStar\" <= 100.0"); - t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); }); @@ -2641,4 +2637,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql b/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql index 32290ef5..d739b8f3 100644 --- a/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql +++ b/backend/src/Migrations/migrate_from_20251023152609_RememberUserWhoCreatedDataSet_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql @@ -51,20 +51,6 @@ BEGIN END IF; END $EF$; -DO $EF$ -BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN - ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_AStar" CHECK ("AStar" >= 0.0 AND "AStar" <= 100.0); - END IF; -END $EF$; - -DO $EF$ -BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN - ALTER TABLE database."CielabColor" ADD CONSTRAINT "CK_OpticalData_CielabColors_BStar" CHECK ("BStar" >= 0.0 AND "BStar" <= 100.0); - END IF; -END $EF$; - DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251024124734_AddObserverAndIlluminantToCielabColor') THEN From 2be050a4a235117ab74df6a76633a6e4d8ab52ba Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 30 Oct 2025 18:10:10 +0100 Subject: [PATCH 531/878] Extract fetching of multiple data sets of any kind into the database context --- backend/src/Data/ApplicationDbContext.cs | 42 +++++++++++++++++++ .../CreateResponseApprovalsMutation.cs | 34 +-------------- .../UpdateResponseApprovalsMutation.cs | 34 +-------------- 3 files changed, 44 insertions(+), 66 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 311b8545..b8b125ab 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -1,8 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; using Database.Enumerations; +using Database.GraphQl.Extensions; +using GreenDonut.Data; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -26,6 +30,7 @@ public sealed class ApplicationDbContext internal const string IlluminantTypeName = "illuminant"; internal const string OpticalComponentSubtypeTypeName = "optical_component_subtype"; internal const string OpticalComponentTypeTypeName = "optical_component_type"; + internal const string PublishingStateTypeName = "publishing_state"; internal const string StandardizerTypeName = "standardizer"; public ApplicationDbContext( @@ -70,6 +75,43 @@ public IQueryable Data(DataKind dataKind) return Data(dataKind).SingleOrDefaultAsync(x => x.Id == id, cancellationToken); } + public IAsyncEnumerable GetAllDataAsync( + Expression> where, + QueryContext queryContext + ) + { + return ( + CalorimetricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ) + .Union( + GeometricData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ) + .Union( + HygrothermalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ) + .Union( + OpticalData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ) + .Union( + PhotovoltaicData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ); + } + private static void CreateEnumerations(ModelBuilder builder, string schemaName) { // https://www.npgsql.org/efcore/mapping/enum.html?tabs=with-datasource#mapping-your-enum diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index f35ecc5a..ad02682b 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -9,12 +9,9 @@ using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate; using HotChocolate.Data; using HotChocolate.Types; -using HotChocolate.Data.Filters; using Microsoft.Extensions.Logging; -using Database.GraphQl.Extensions; using Database.Extensions; namespace Database.GraphQl.ResponseApprovals; @@ -90,36 +87,7 @@ CancellationToken cancellationToken var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - ( - context.CalorimetricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval == null) - .ToAsyncEnumerable() - ) - .Union( - context.GeometricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval == null) - .ToAsyncEnumerable() - ) - .Union( - context.HygrothermalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval == null) - .ToAsyncEnumerable() - ) - .Union( - context.OpticalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval == null) - .ToAsyncEnumerable() - ) - .Union( - context.PhotovoltaicData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval == null) - .ToAsyncEnumerable() - ), + context.GetAllDataAsync(_ => _.Approval == null, queryContext), cancellationToken, async (data, cancellationToken) => { diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 7baa5256..9c1d7292 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -9,12 +9,9 @@ using Database.Authorization; using Database.Data; using Database.Services; -using HotChocolate; using HotChocolate.Types; using HotChocolate.Data; -using HotChocolate.Data.Filters; using Microsoft.Extensions.Logging; -using Database.GraphQl.Extensions; using Database.Extensions; namespace Database.GraphQl.ResponseApprovals; @@ -90,36 +87,7 @@ CancellationToken cancellationToken var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - ( - context.CalorimetricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval != null) - .ToAsyncEnumerable() - ) - .Union( - context.GeometricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval != null) - .ToAsyncEnumerable() - ) - .Union( - context.HygrothermalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval != null) - .ToAsyncEnumerable() - ) - .Union( - context.OpticalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval != null) - .ToAsyncEnumerable() - ) - .Union( - context.PhotovoltaicData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) - .Where(d => d.Approval != null) - .ToAsyncEnumerable() - ), + context.GetAllDataAsync(_ => _.Approval != null, queryContext), cancellationToken, async (data, cancellationToken) => { From d52534bb5e21e3d5a74663942711a20b6855fdeb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 30 Oct 2025 18:11:26 +0100 Subject: [PATCH 532/878] Check input of mutation that creates a GET HTTPS resource for consistency --- .../CreateGetHttpsResourceMutation.cs | 88 ++++++++++++++----- 1 file changed, 67 insertions(+), 21 deletions(-) diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 7e4fd94e..d3cfbb4b 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -6,19 +6,15 @@ using Database.Authorization; using Database.Enumerations; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; using Database.Services; -using GraphQL.Client.Abstractions.Utilities; -using System.Collections.Concurrent; using Microsoft.Extensions.Logging; using Database.Utilities; -using HotChocolate.Data; using GreenDonut.Data; -using Microsoft.EntityFrameworkCore.Query; -using Database.GraphQl.Extensions; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -30,7 +26,25 @@ public sealed record CreateGetHttpsResourceInput( Guid? ParentId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod -) : IIdentifyDataInput; +) : IIdentifyDataInput +{ + public GetHttpsResource ToDomainModel() + { + return new GetHttpsResource( + Description, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + DataFormatId, + DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, + DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, + DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, + DataKind == DataKind.OPTICAL_DATA ? DataId : null, + DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, + ParentId, + ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), + AppliedConversionMethod?.ToDomainModel() + ); + } +} [SuppressMessage("Naming", "CA1707")] public enum CreateGetHttpsResourceErrorCode @@ -39,7 +53,10 @@ public enum CreateGetHttpsResourceErrorCode UNAUTHORIZED, UNAUTHENTICATED, UNKNOWN_DATA, - CREATING_RESPONSE_APPROVAL_FAILED + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, + CREATING_RESPONSE_APPROVAL_FAILED, + UNKNOWN_PARENT, + ILLEGAL_PARENT } public sealed record CreateGetHttpsResourceError( @@ -111,19 +128,48 @@ CancellationToken cancellationToken return fetchDataErrorPayload; } - var getHttpsResource = new GetHttpsResource( - input.Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - input.DataFormatId, - input.DataKind == DataKind.CALORIMETRIC_DATA ? input.DataId : null, - input.DataKind == DataKind.GEOMETRIC_DATA ? input.DataId : null, - input.DataKind == DataKind.HYGROTHERMAL_DATA ? input.DataId : null, - input.DataKind == DataKind.OPTICAL_DATA ? input.DataId : null, - input.DataKind == DataKind.PHOTOVOLTAIC_DATA ? input.DataId : null, - input.ParentId, - input.ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), - input?.AppliedConversionMethod.ToDomainModel() - ); + var errors = new List(); + var parent = await context.GetHttpsResources + .SingleOrDefaultAsync(_ => + _.Id == input.ParentId, + cancellationToken + ); + if (parent is null) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.UNKNOWN_PARENT, + "There is not GET HTTPS resource with the parent ID.", + [nameof(input), nameof(input.ParentId).ToLowerFirst()] + ) + ); + } + if (parent is not null && parent.DataId != input.DataId) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.ILLEGAL_PARENT, + $"The parent belongs to the data set with ID {parent.DataId} which is not the data set of the resource to be created, namely the one with the ID {input.DataId}.", + [nameof(input), nameof(input.ParentId).ToLowerFirst()] + ) + ); + } + if (input.AppliedConversionMethod is not null && input.ParentId is null) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, + "The resource does not have a parent yet an applied conversion method was given. This method is supposed to have been applied to this resource's parent.", + [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst()] + ) + ); + } + if (errors.Count >= 1) + { + return NewPayload(null, errors); + } + + var getHttpsResource = input.ToDomainModel(); context.GetHttpsResources.Add(getHttpsResource); await context.SaveChangesAsync(cancellationToken); From a793324222ca29dc4d6e29effc6bdb6142beb742 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 30 Oct 2025 18:18:22 +0100 Subject: [PATCH 533/878] Add publishing state to data sets with the default state `pending` --- use the mutation `publishData` to switch the state to `published` --- .../src/Authorization/CommonAuthorization.cs | 10 + backend/src/Data/DataX.cs | 13 + backend/src/Data/IData.cs | 6 + backend/src/Enumerations/PublishingState.cs | 8 + .../CalorimetricDataQueries.cs | 76 +- .../CreateCalorimetricDataMutation.cs | 1 - backend/src/GraphQl/DataX/DataQueriesBase.cs | 105 + .../src/GraphQl/DataX/PublishDataMutation.cs | 112 + .../src/GraphQl/DataX/RetractDataMutation.cs | 112 + .../GeometricDataX/GeometricDataQueries.cs | 74 +- .../GetHttpsResourceQueries.cs | 8 +- .../HygrothermalDataQueries.cs | 78 +- .../OpticalDataX/OpticalDataQueries.cs | 78 +- .../PhotovoltaicDataQueries.cs | 76 +- ...9_AddPublishingStateToDataSets.Designer.cs | 2659 +++++++++++++++++ ...1030170839_AddPublishingStateToDataSets.cs | 90 + .../ApplicationDbContextModelSnapshot.cs | 18 +- backend/src/Migrations/migrate.sql | 53 + ...030170839_AddPublishingStateToDataSets.sql | 53 + ..._AddObserverAndIlluminantToCielabColor.sql | 46 + backend/src/Startup.cs | 1 + .../GraphQlSchemaTests.IsUnchanged.snap | 70 + frontend/type-defs.graphqls | 70 + 23 files changed, 3680 insertions(+), 137 deletions(-) create mode 100644 backend/src/Enumerations/PublishingState.cs create mode 100644 backend/src/GraphQl/DataX/DataQueriesBase.cs create mode 100644 backend/src/GraphQl/DataX/PublishDataMutation.cs create mode 100644 backend/src/GraphQl/DataX/RetractDataMutation.cs create mode 100644 backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs create mode 100644 backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs create mode 100644 backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql create mode 100644 backend/src/Migrations/rollback_from_20251030170839_AddPublishingStateToDataSets_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 21990615..60adacd2 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -16,6 +16,16 @@ UserService userService return userService.GetCurrentUser(cancellationToken); } + public async Task IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(CancellationToken cancellationToken) + { + var currentUser = await GetCurrentUserAsync(cancellationToken); + if (currentUser is null) + { + return false; + } + return IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser); + } + public bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( CurrentUser currentUser ) diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index da240627..39c92ce8 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Database.Enumerations; namespace Database.Data; @@ -59,6 +60,16 @@ Guid creatorId CreatorId = creatorId; } + public void Publish() + { + PublishingState = PublishingState.PUBLISHED; + } + + public void Retract() + { + PublishingState = PublishingState.RETRACTED; + } + public Guid UserId { get; private set; } = userId; public string Locale { get; private set; } = locale; public Guid ComponentId { get; private set; } = componentId; @@ -78,6 +89,8 @@ Guid creatorId public DataAccessRights DataAccessRights { get; private set; } = new DataAccessRights(); + public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; + /// public bool IsRestrictedByApplication(string applicationId) { diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index ff091dd6..8d9b7442 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Database.Enumerations; using Database.GraphQl; namespace Database.Data; @@ -26,6 +27,7 @@ public interface IData : IEntity ResponseApproval? Approval { get; set; } string Locale { get; } DataAccessRights DataAccessRights { get; } + PublishingState PublishingState { get; } /// /// Check if dataset is restricted for passed application id. @@ -58,4 +60,8 @@ void Update( DateTime createdAt, Guid creatorId ); + + void Publish(); + + void Retract(); } \ No newline at end of file diff --git a/backend/src/Enumerations/PublishingState.cs b/backend/src/Enumerations/PublishingState.cs new file mode 100644 index 00000000..2af7539c --- /dev/null +++ b/backend/src/Enumerations/PublishingState.cs @@ -0,0 +1,8 @@ +namespace Database.Enumerations; + +public enum PublishingState +{ + PENDING, + PUBLISHED, + RETRACTED +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 02db30f1..4bd1a0bd 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; +using Database.GraphQl.DataX; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -11,18 +12,20 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; +using Database.Authorization; namespace Database.GraphQl.CalorimetricDataX; [ExtendObjectType(nameof(Query))] public sealed class CalorimetricDataQueries +: DataQueriesBase { [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetAllCalorimetricData( + public Task> GetAllCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -31,32 +34,59 @@ public async Task> GetAllCalorimetricData( CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = - context.CalorimetricData.AsNoTracking() - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) - { - return filteredData; - } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return GetAllDataAsync( + context.CalorimetricData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); } + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] - public Task GetHasCalorimetricData( + [UseSorting] + public Task> GetAllPendingCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessRightsService accessRightsService, + ISortingContext sorting, IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return context.CalorimetricData.AsNoTracking() - .Filter(resolverContext) - .AnyAsync(cancellationToken); + return GetAllPendingDataAsync( + context.CalorimetricData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task GetHasCalorimetricDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetHasDataAsync( + context.CalorimetricData, + locale, + resolverContext, + cancellationToken + ); } - public async Task GetCalorimetricDataAsync( + public Task GetCalorimetricDataAsync( Guid id, [GraphQLType] string? locale, CalorimetricDataByIdDataLoader byId, @@ -64,18 +94,12 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - var calorimetricData = await byId.LoadAsync( + return GetDataAsync( id, + locale, + byId, + accessRightsService, cancellationToken ); - if (calorimetricData is null) - { - return null; - } - if (!calorimetricData.DataAccessRights.HasRestrictions) - { - return calorimetricData; - } - return await accessRightsService.ApplyAccessRightsOnData(calorimetricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 285c3c12..da234044 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -11,7 +11,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Database.Extensions; -using Database.Extensions; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs new file mode 100644 index 00000000..e7935031 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; +using Database.Data; +using Database.GraphQl.Extensions; +using Database.Services; +using Database.GraphQl.Entities; +using Database.Authorization; + +namespace Database.GraphQl.DataX; + +public abstract class DataQueriesBase +where TData : class, IData +{ + protected async Task> GetAllDataAsync( + DbSet data, + [GraphQLType] string? locale, + AccessRightsService accessRightsService, + ISortingContext sorting, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + sorting.StabilizeOrder(); + var filteredData = + data.AsNoTracking() + .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) + { + return filteredData; + } + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + } + + protected async Task> GetAllPendingDataAsync( + DbSet data, + [GraphQLType] string? locale, + AccessRightsService accessRightsService, + ISortingContext sorting, + IResolverContext resolverContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(cancellationToken)) + { + return Enumerable.Empty().AsQueryable(); + } + sorting.StabilizeOrder(); + var filteredData = + data.AsNoTracking() + .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) + .Sort(resolverContext) + .Filter(resolverContext); + if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) + { + return filteredData; + } + return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + } + + protected Task GetHasDataAsync( + DbSet data, + [GraphQLType] string? locale, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return data.AsNoTracking() + .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) + .Filter(resolverContext) + .AnyAsync(cancellationToken); + } + + protected async Task GetDataAsync( + Guid id, + [GraphQLType] string? locale, + EntityByIdDataLoader byId, + AccessRightsService accessRightsService, + CancellationToken cancellationToken + ) + { + var data = await byId.LoadAsync( + id, + cancellationToken + ); + if (data is null) + { + return null; + } + if (!data.DataAccessRights.HasRestrictions) + { + return data; + } + return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs new file mode 100644 index 00000000..de60db31 --- /dev/null +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Types; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Extensions; +using Database.Services; + +namespace Database.GraphQl.DataX; + +public sealed record PublishDataInput( + Guid DataId, + DataKind DataKind +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum PublishDataErrorCode +{ + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record PublishDataError( + PublishDataErrorCode Code, + string Message, + IReadOnlyList Path +) : UserErrorBase(Code, Message, Path); + +public sealed record PublishDataPayload( + IData? Data, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class PublishDataMutation +: DataMutationBase +{ + protected override PublishDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override PublishDataError NewError( + PublishDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task PublishDataAsync( + PublishDataInput input, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + PublishDataErrorCode.UNAUTHENTICATED, + PublishDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + PublishDataErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + var rememberedPublishingState = data.PublishingState; + data.Publish(); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + data, + PublishDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + if (rememberedPublishingState is PublishingState.RETRACTED) + { + data.Retract(); + } + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(data, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs new file mode 100644 index 00000000..12792e9c --- /dev/null +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using HotChocolate.Types; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.Extensions; +using Database.Services; + +namespace Database.GraphQl.DataX; + +public sealed record RetractDataInput( + Guid DataId, + DataKind DataKind +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum RetractDataErrorCode +{ + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record RetractDataError( + RetractDataErrorCode Code, + string Message, + IReadOnlyList Path +) : UserErrorBase(Code, Message, Path); + +public sealed record RetractDataPayload( + IData? Data, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class RetractDataMutation +: DataMutationBase +{ + protected override RetractDataPayload NewPayload( + IData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override RetractDataError NewError( + RetractDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task RetractDataAsync( + RetractDataInput input, + CommonAuthorization authorization, + ResponseApprovalService responseApprovalService, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + RetractDataErrorCode.UNAUTHENTICATED, + RetractDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + RetractDataErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + var rememberedPublishingState = data.PublishingState; + data.Retract(); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + data, + RetractDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + if (rememberedPublishingState is PublishingState.PUBLISHED) + { + data.Publish(); + } + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(data, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index e7e38dc9..71b69c0f 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; +using Database.GraphQl.DataX; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -11,16 +12,18 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; +using Database.Authorization; namespace Database.GraphQl.GeometricDataX; [ExtendObjectType(nameof(Query))] public sealed class GeometricDataQueries +: DataQueriesBase { [UsePaging] [UseFiltering] [UseSorting] - public async Task> GetAllGeometricData( + public Task> GetAllGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -29,32 +32,57 @@ public async Task> GetAllGeometricData( CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = - context.GeometricData.AsNoTracking() - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) - { - return filteredData; - } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return GetAllDataAsync( + context.GeometricData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); } + [UsePaging] [UseFiltering] - public Task GetHasGeometricData( + [UseSorting] + public Task> GetAllPendingGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessRightsService accessRightsService, + ISortingContext sorting, IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return context.GeometricData.AsNoTracking() - .Filter(resolverContext) - .AnyAsync(cancellationToken); + return GetAllPendingDataAsync( + context.GeometricData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task GetHasGeometricDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetHasDataAsync( + context.GeometricData, + locale, + resolverContext, + cancellationToken + ); } - public async Task GetGeometricDataAsync( + public Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, GeometricDataByIdDataLoader byId, @@ -62,18 +90,12 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - var geometricData = await byId.LoadAsync( + return GetDataAsync( id, + locale, + byId, + accessRightsService, cancellationToken ); - if (geometricData is null) - { - return null; - } - if (!geometricData.DataAccessRights.HasRestrictions) - { - return geometricData; - } - return await accessRightsService.ApplyAccessRightsOnData(geometricData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 94688fb8..62036a3b 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -8,6 +8,7 @@ using HotChocolate.Types; using Database.Data; using Database.GraphQl.Extensions; +using Database.Enumerations; namespace Database.GraphQl.GetHttpsResources; @@ -24,7 +25,12 @@ ISortingContext sorting ) { sorting.StabilizeOrder(); - return context.GetHttpsResources.AsNoTracking(); + return context.GetHttpsResources.AsNoTracking() + .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState != PublishingState.PENDING) + .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState != PublishingState.PENDING) + .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState != PublishingState.PENDING) + .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState != PublishingState.PENDING) + .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING); } public Task GetGetHttpsResourceAsync( diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 4d5ea769..048c1520 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -3,26 +3,27 @@ using System.Threading; using System.Threading.Tasks; using Database.Data; -using Database.GraphQl.Extensions; +using Database.GraphQl.DataX; using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; +using Database.Authorization; namespace Database.GraphQl.HygrothermalDataX; [ExtendObjectType(nameof(Query))] public sealed class HygrothermalDataQueries +: DataQueriesBase { [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetAllHygrothermalData( + public Task> GetAllHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -31,32 +32,59 @@ public async Task> GetAllHygrothermalData( CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = - context.HygrothermalData.AsNoTracking() - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) - { - return filteredData; - } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return GetAllDataAsync( + context.HygrothermalData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); } + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] - public Task GetHasHygrothermalData( + [UseSorting] + public Task> GetAllPendingHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessRightsService accessRightsService, IResolverContext resolverContext, + ISortingContext sorting, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return context.HygrothermalData.AsNoTracking() - .Filter(resolverContext) - .AnyAsync(cancellationToken); + return GetAllPendingDataAsync( + context.HygrothermalData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task GetHasHygrothermalDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetHasDataAsync( + context.HygrothermalData, + locale, + resolverContext, + cancellationToken + ); } - public async Task GetHygrothermalDataAsync( + public Task GetHygrothermalDataAsync( Guid id, [GraphQLType] string? locale, HygrothermalDataByIdDataLoader byId, @@ -64,18 +92,12 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - var hygrothermalData = await byId.LoadAsync( + return GetDataAsync( id, + locale, + byId, + accessRightsService, cancellationToken ); - if (hygrothermalData is null) - { - return null; - } - if (!hygrothermalData.DataAccessRights.HasRestrictions) - { - return hygrothermalData; - } - return await accessRightsService.ApplyAccessRightsOnData(hygrothermalData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 74d98d1c..25e8a8a0 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -2,27 +2,28 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; using Database.Data; -using Database.GraphQl.Extensions; +using Database.GraphQl.DataX; using Database.Services; +using Database.Authorization; namespace Database.GraphQl.OpticalDataX; [ExtendObjectType(nameof(Query))] public sealed class OpticalDataQueries +: DataQueriesBase { [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetAllOpticalData( + public Task> GetAllOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -31,32 +32,59 @@ public async Task> GetAllOpticalData( CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = - context.OpticalData.AsNoTracking() - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) - { - return filteredData; - } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return GetAllDataAsync( + context.OpticalData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); } + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] - public Task GetHasOpticalData( + [UseSorting] + public Task> GetAllPendingOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessRightsService accessRightsService, + ISortingContext sorting, IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return context.OpticalData.AsNoTracking() - .Filter(resolverContext) - .AnyAsync(cancellationToken); + return GetAllPendingDataAsync( + context.OpticalData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task GetHasOpticalDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetHasDataAsync( + context.OpticalData, + locale, + resolverContext, + cancellationToken + ); } - public async Task GetOpticalDataAsync( + public Task GetOpticalDataAsync( Guid id, [GraphQLType] string? locale, OpticalDataByIdDataLoader byId, @@ -64,18 +92,12 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - var opticalData = await byId.LoadAsync( + return GetDataAsync( id, + locale, + byId, + accessRightsService, cancellationToken ); - if (opticalData is null) - { - return null; - } - if (!opticalData.DataAccessRights.HasRestrictions) - { - return opticalData; - } - return await accessRightsService.ApplyAccessRightsOnData(opticalData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index a11f23be..5051806f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; +using Database.GraphQl.DataX; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -11,18 +12,20 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; +using Database.Authorization; namespace Database.GraphQl.PhotovoltaicDataX; [ExtendObjectType(nameof(Query))] public sealed class PhotovoltaicDataQueries +: DataQueriesBase { [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetAllPhotovoltaicData( + public Task> GetAllPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -31,32 +34,59 @@ public async Task> GetAllPhotovoltaicData( CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = - context.PhotovoltaicData.AsNoTracking() - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) - { - return filteredData; - } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return GetAllDataAsync( + context.PhotovoltaicData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); } + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] - public Task GetHasPhotovoltaicData( + [UseSorting] + public Task> GetAllPendingPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessRightsService accessRightsService, + ISortingContext sorting, IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return context.PhotovoltaicData.AsNoTracking() - .Filter(resolverContext) - .AnyAsync(cancellationToken); + return GetAllPendingDataAsync( + context.PhotovoltaicData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task GetHasPhotovoltaicDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return GetHasDataAsync( + context.PhotovoltaicData, + locale, + resolverContext, + cancellationToken + ); } - public async Task GetPhotovoltaicDataAsync( + public Task GetPhotovoltaicDataAsync( Guid id, [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, @@ -64,18 +94,12 @@ CancellationToken cancellationToken CancellationToken cancellationToken ) { - var photovoltaicData = await byId.LoadAsync( + return GetDataAsync( id, + locale, + byId, + accessRightsService, cancellationToken ); - if (photovoltaicData is null) - { - return null; - } - if (!photovoltaicData.DataAccessRights.HasRestrictions) - { - return photovoltaicData; - } - return await accessRightsService.ApplyAccessRightsOnData(photovoltaicData, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs new file mode 100644 index 00000000..08762d26 --- /dev/null +++ b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs @@ -0,0 +1,2659 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251030170839_AddPublishingStateToDataSets")] + partial class AddPublishingStateToDataSets + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs new file mode 100644 index 00000000..a09f678b --- /dev/null +++ b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs @@ -0,0 +1,90 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddPublishingStateToDataSets : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.publishing_state", "pending,published,retracted"); + + migrationBuilder.AddColumn( + name: "PublishingState", + schema: "database", + table: "photovoltaic_data", + type: "database.publishing_state", + nullable: false, + defaultValue: PublishingState.PENDING); + + migrationBuilder.AddColumn( + name: "PublishingState", + schema: "database", + table: "optical_data", + type: "database.publishing_state", + nullable: false, + defaultValue: PublishingState.PENDING); + + migrationBuilder.AddColumn( + name: "PublishingState", + schema: "database", + table: "hygrothermal_data", + type: "database.publishing_state", + nullable: false, + defaultValue: PublishingState.PENDING); + + migrationBuilder.AddColumn( + name: "PublishingState", + schema: "database", + table: "geometric_data", + type: "database.publishing_state", + nullable: false, + defaultValue: PublishingState.PENDING); + + migrationBuilder.AddColumn( + name: "PublishingState", + schema: "database", + table: "calorimetric_data", + type: "database.publishing_state", + nullable: false, + defaultValue: PublishingState.PENDING); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PublishingState", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "PublishingState", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "PublishingState", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "PublishingState", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "PublishingState", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterDatabase() + .OldAnnotation("Npgsql:Enum:database.publishing_state", "pending,published,retracted"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 640d70d6..f77cd21f 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -30,6 +30,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -64,6 +65,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + b.PrimitiveCollection("UValues") .IsRequired() .HasColumnType("double precision[]"); @@ -112,6 +116,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + b.PrimitiveCollection("Thicknesses") .IsRequired() .HasColumnType("double precision[]"); @@ -218,6 +225,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + b.Property("UserId") .HasColumnType("uuid"); @@ -326,6 +336,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + b.Property("Subtype") .HasColumnType("database.optical_component_subtype"); @@ -376,6 +389,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Name") .HasColumnType("text"); + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + b.Property("UserId") .HasColumnType("uuid"); @@ -2637,4 +2653,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} \ No newline at end of file +} diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 700385e9..3e0ea285 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1469,3 +1469,56 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + CREATE TYPE database.publishing_state AS ENUM ('pending', 'published', 'retracted'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.photovoltaic_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.optical_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.hygrothermal_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.geometric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.calorimetric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251030170839_AddPublishingStateToDataSets', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql new file mode 100644 index 00000000..460204c1 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql @@ -0,0 +1,53 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + CREATE TYPE database.publishing_state AS ENUM ('pending', 'published', 'retracted'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.photovoltaic_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.optical_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.hygrothermal_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.geometric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.calorimetric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251030170839_AddPublishingStateToDataSets', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251030170839_AddPublishingStateToDataSets_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql b/backend/src/Migrations/rollback_from_20251030170839_AddPublishingStateToDataSets_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql new file mode 100644 index 00000000..f52c3f61 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251030170839_AddPublishingStateToDataSets_to_20251024124734_AddObserverAndIlluminantToCielabColor.sql @@ -0,0 +1,46 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "PublishingState"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.optical_data DROP COLUMN "PublishingState"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "PublishingState"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.geometric_data DROP COLUMN "PublishingState"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "PublishingState"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + DROP TYPE database.publishing_state; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 96ca0b9f..7f1dbf19 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -181,6 +181,7 @@ AppSettings appSettings .MapEnum(ApplicationDbContext.IlluminantTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.PublishingStateTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) // .UseNodaTime(); ) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index ad882765..73b19ac5 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -28,6 +28,7 @@ interface Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -113,6 +114,7 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -319,6 +321,7 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! @@ -412,6 +415,7 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -471,8 +475,10 @@ type Mutation { createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") @@ -525,6 +531,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype @@ -581,6 +588,7 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -623,11 +631,28 @@ type Publication { webAddress: Url } +type PublishDataError implements UserError { + code: PublishDataErrorCode! + message: String! + path: [String!]! +} + +type PublishDataPayload { + data: Data + errors: [PublishDataError!] + query: Query! +} + type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") @@ -688,6 +713,18 @@ type ResponseApproval implements Approval { variables: Any! } +type RetractDataError implements UserError { + code: RetractDataErrorCode! + message: String! + path: [String!]! +} + +type RetractDataPayload { + data: Data + errors: [RetractDataError!] + query: Query! +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -1470,6 +1507,11 @@ input PublicationSortInput { section: SortEnumType @cost(weight: "10") } +input PublishDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input RecomputeGetHttpsResourceHashValuesFilterInput { and: [RecomputeGetHttpsResourceHashValuesFilterInput!] or: [RecomputeGetHttpsResourceHashValuesFilterInput!] @@ -1510,6 +1552,11 @@ input RemoveDataApprovalInput { signature: String! } +input RetractDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1788,7 +1835,10 @@ enum CreateGetHttpsResourceErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATA + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT CREATING_RESPONSE_APPROVAL_FAILED + UNKNOWN_PARENT + ILLEGAL_PARENT } enum CreateHygrothermalDataErrorCode { @@ -1885,6 +1935,19 @@ enum OpticalComponentType { SHADING } +enum PublishDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum PublishingState { + PENDING + PUBLISHED + RETRACTED +} + enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED @@ -1902,6 +1965,13 @@ enum RemoveDataApprovalErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum RetractDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + enum SortEnumType { ASC DESC diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index ad882765..73b19ac5 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -28,6 +28,7 @@ interface Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -113,6 +114,7 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -319,6 +321,7 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! @@ -412,6 +415,7 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -471,8 +475,10 @@ type Mutation { createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") @@ -525,6 +531,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype @@ -581,6 +588,7 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String + publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -623,11 +631,28 @@ type Publication { webAddress: Url } +type PublishDataError implements UserError { + code: PublishDataErrorCode! + message: String! + path: [String!]! +} + +type PublishDataPayload { + data: Data + errors: [PublishDataError!] + query: Query! +} + type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") @@ -688,6 +713,18 @@ type ResponseApproval implements Approval { variables: Any! } +type RetractDataError implements UserError { + code: RetractDataErrorCode! + message: String! + path: [String!]! +} + +type RetractDataPayload { + data: Data + errors: [RetractDataError!] + query: Query! +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -1470,6 +1507,11 @@ input PublicationSortInput { section: SortEnumType @cost(weight: "10") } +input PublishDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input RecomputeGetHttpsResourceHashValuesFilterInput { and: [RecomputeGetHttpsResourceHashValuesFilterInput!] or: [RecomputeGetHttpsResourceHashValuesFilterInput!] @@ -1510,6 +1552,11 @@ input RemoveDataApprovalInput { signature: String! } +input RetractDataInput { + dataId: Uuid! + dataKind: DataKind! +} + input RootGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -1788,7 +1835,10 @@ enum CreateGetHttpsResourceErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATA + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT CREATING_RESPONSE_APPROVAL_FAILED + UNKNOWN_PARENT + ILLEGAL_PARENT } enum CreateHygrothermalDataErrorCode { @@ -1885,6 +1935,19 @@ enum OpticalComponentType { SHADING } +enum PublishDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum PublishingState { + PENDING + PUBLISHED + RETRACTED +} + enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED @@ -1902,6 +1965,13 @@ enum RemoveDataApprovalErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum RetractDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + enum SortEnumType { ASC DESC From ffff4ad5821865e590aec44ca59a5f8e2bb390e9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 30 Oct 2025 18:24:52 +0100 Subject: [PATCH 534/878] Add method to check whether a backing file exists to GET HTTPS resources --- backend/src/Data/GetHttpsResource.cs | 9 +++++++-- .../GetHttpsResourceResolvers.cs | 15 +++++++-------- .../GetHttpsResources/GetHttpsResourceType.cs | 3 +++ .../GraphQlSchemaTests.IsUnchanged.snap | 2 +- frontend/type-defs.graphqls | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index c3bd4fd2..bd5cc481 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -166,13 +166,18 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(Parent))] public ICollection Children { get; } = []; - public static string ConstructVertexId(Guid id) + public bool DoesFileExist() { - return id.ToString("D").Base64Encode(); + return File.Exists(FilePath); } public async Task RecomputeHashValue(CancellationToken cancellationToken) { HashValue = await Sha256FileHasher.ComputeForFile(FilePath, cancellationToken); } + + public static string ConstructVertexId(Guid id) + { + return id.ToString("D").Base64Encode(); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index bea56652..fa7d53e5 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -30,6 +30,13 @@ CancellationToken cancellationToken cancellationToken ); } + if (getHttpsResource.GeometricDataId is not null) + { + return await geometricDataById.LoadAsync( + getHttpsResource.GeometricDataId ?? Guid.Empty, + cancellationToken + ); + } if (getHttpsResource.HygrothermalDataId is not null) { return await hygrothermalDataById.LoadAsync( @@ -51,13 +58,6 @@ CancellationToken cancellationToken cancellationToken ); } - if (getHttpsResource.GeometricDataId is not null) - { - return await geometricDataById.LoadAsync( - getHttpsResource.GeometricDataId ?? Guid.Empty, - cancellationToken - ); - } return null; } @@ -78,7 +78,6 @@ AppSettings appSettings CancellationToken cancellationToken ) { - // TODO Why is `?? Guid.Empty` below necessary although `getHttpsResource.ParentId` is not null? return getHttpsResource.ParentId is null ? null : await byId.LoadAsync(getHttpsResource.ParentId ?? Guid.Empty, cancellationToken)!; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index c719e5df..33cd45ac 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -14,6 +14,9 @@ IObjectTypeDescriptor descriptor descriptor .Field("locator") .ResolveWith(t => t.GetLocator(default!, default!)); + descriptor + .Field(_ => _.FilePath) + .Ignore(); descriptor .Field(x => x.ParentId) .Ignore(); diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 73b19ac5..39dd89db 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -356,7 +356,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "10") dataFormatId: Uuid! description: String - filePath: String! + doesFileExist: Boolean! hashValue: String! id: ID! locator: Url! diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 73b19ac5..39dd89db 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -356,7 +356,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "10") dataFormatId: Uuid! description: String - filePath: String! + doesFileExist: Boolean! hashValue: String! id: ID! locator: Url! From 0e3e4e28d2d1f167f30bc895b8c5fa0faf113bd7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 31 Oct 2025 13:06:08 +0100 Subject: [PATCH 535/878] Validate data creation input, in particular, check existence of entities in metabase, and make user create root resource separately using the create resource mutation --- .../src/ApiRequests/ComponentDataLoader.cs | 41 +++++++++++ .../src/ApiRequests/DataFormatDataLoader.cs | 45 ++++++++++++ .../src/ApiRequests/InstitutionDataLoader.cs | 41 +++++++++++ .../ApiRequests/Queries/Components.graphql | 9 +++ .../ApiRequests/Queries/DataFormats.graphql | 13 ++++ .../ApiRequests/Queries/Institutions.graphql | 9 +++ .../src/ApiRequests/QueryByIdDataLoader.cs | 65 +++++++++++++++++ backend/src/ApiRequests/QueryCurrentUser.cs | 4 +- backend/src/Data/GetHttpsResource.cs | 70 +++++------------- .../CreateCalorimetricDataMutation.cs | 28 ++++++- .../GraphQl/DataX/CreateDataMutationBase.cs | 73 +++++++++++++++++++ .../CreateGeometricDataMutation.cs | 28 ++++++- .../CreateGetHttpsResourceMutation.cs | 21 +++++- .../GetHttpsResourceResolvers.cs | 2 +- .../GetHttpsResources/GetHttpsResourceType.cs | 6 ++ .../CreateHygrothermalDataMutation.cs | 30 ++++++-- .../OpticalDataX/CreateOpticalDataMutation.cs | 27 ++++++- .../CreatePhotovoltaicDataMutation.cs | 30 ++++++-- .../src/GraphQl/RootGetHttpsResourceInput.cs | 27 ------- .../GraphQlSchemaTests.IsUnchanged.snap | 28 ++++--- frontend/type-defs.graphqls | 28 ++++--- 21 files changed, 493 insertions(+), 132 deletions(-) create mode 100644 backend/src/ApiRequests/ComponentDataLoader.cs create mode 100644 backend/src/ApiRequests/DataFormatDataLoader.cs create mode 100644 backend/src/ApiRequests/InstitutionDataLoader.cs create mode 100644 backend/src/ApiRequests/Queries/Components.graphql create mode 100644 backend/src/ApiRequests/Queries/DataFormats.graphql create mode 100644 backend/src/ApiRequests/Queries/Institutions.graphql create mode 100644 backend/src/ApiRequests/QueryByIdDataLoader.cs create mode 100644 backend/src/GraphQl/DataX/CreateDataMutationBase.cs delete mode 100644 backend/src/GraphQl/RootGetHttpsResourceInput.cs diff --git a/backend/src/ApiRequests/ComponentDataLoader.cs b/backend/src/ApiRequests/ComponentDataLoader.cs new file mode 100644 index 00000000..fd69cf01 --- /dev/null +++ b/backend/src/ApiRequests/ComponentDataLoader.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; + +namespace Database.ApiRequests; + +public static class ComponentDataLoader +{ + private const string QueryFileName = "Components.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Component( + Guid Uuid + ) : IIdNode; + + private sealed record ComponentsData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetComponentByIdAsync( + IReadOnlyList componentIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + componentIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/DataFormatDataLoader.cs b/backend/src/ApiRequests/DataFormatDataLoader.cs new file mode 100644 index 00000000..1bb2a46e --- /dev/null +++ b/backend/src/ApiRequests/DataFormatDataLoader.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; + +namespace Database.ApiRequests; + +public static class DataFormatDataLoader +{ + private const string QueryFileName = "DataFormats.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record DataFormat( + Guid Uuid, + string Name, + string? Extension, + string MediaType, + Uri? SchemaLocator + ) : IIdNode; + + private sealed record DataFormatsData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetDataFormatByIdAsync( + IReadOnlyList dataFormatIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + dataFormatIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/InstitutionDataLoader.cs b/backend/src/ApiRequests/InstitutionDataLoader.cs new file mode 100644 index 00000000..754c844c --- /dev/null +++ b/backend/src/ApiRequests/InstitutionDataLoader.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; + +namespace Database.ApiRequests; + +public static class InstitutionDataLoader +{ + private const string QueryFileName = "Institutions.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Institution( + Guid Uuid + ) : IIdNode; + + private sealed record InstitutionsData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetInstitutionByIdAsync( + IReadOnlyList institutionIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + institutionIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Components.graphql b/backend/src/ApiRequests/Queries/Components.graphql new file mode 100644 index 00000000..b67fefa1 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Components.graphql @@ -0,0 +1,9 @@ +query Components($ids: [Uuid!]!) { + connection: components(where: { id: { in: $ids } }) { + edges { + node { + uuid + } + } + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/DataFormats.graphql b/backend/src/ApiRequests/Queries/DataFormats.graphql new file mode 100644 index 00000000..7a7f0e27 --- /dev/null +++ b/backend/src/ApiRequests/Queries/DataFormats.graphql @@ -0,0 +1,13 @@ +query DataFormats($ids: [Uuid!]!) { + connection: dataFormats(where: { id: { in: $ids } }) { + edges { + node { + uuid + name + extension + mediaType + schemaLocator + } + } + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Institutions.graphql b/backend/src/ApiRequests/Queries/Institutions.graphql new file mode 100644 index 00000000..69389195 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Institutions.graphql @@ -0,0 +1,9 @@ +query Institutions($ids: [Uuid!]!) { + connection: institutions(where: { id: { in: $ids } }) { + edges { + node { + uuid + } + } + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryByIdDataLoader.cs b/backend/src/ApiRequests/QueryByIdDataLoader.cs new file mode 100644 index 00000000..057e54f7 --- /dev/null +++ b/backend/src/ApiRequests/QueryByIdDataLoader.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GraphQL; + +namespace Database.ApiRequests; + +public interface IIdNode +{ + Guid Uuid { get; } +} + +public sealed record Edge( + TNode Node +); + +public sealed record Connection( + IReadOnlyCollection>? Edges +); + +public interface IConnectionData +{ + Connection? Connection { get; } +} + + +public static class QueryByIdDataLoader +{ + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + appSettings.MetabaseGraphQlEndpoint; + + public static async Task> GetByIdAsync( + IReadOnlyList ids, + string[] queryFileNames, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + where TData : class, IConnectionData + where TNode : IIdNode + { + return (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), + new GraphQLRequest( + await apiRequestService.ConstructGraphQlQuery(queryFileNames), + new + { + ids + }, + null + ), + cancellationToken + )) + .Data + ?.Connection + ?.Edges + ?.ToDictionary( + _ => _.Node.Uuid, + _ => _.Node + ) ?? Enumerable.Empty().ToDictionary(_ => _.Uuid); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUser.cs b/backend/src/ApiRequests/QueryCurrentUser.cs index 48d2e4f1..9735cce9 100644 --- a/backend/src/ApiRequests/QueryCurrentUser.cs +++ b/backend/src/ApiRequests/QueryCurrentUser.cs @@ -12,7 +12,7 @@ namespace Database.ApiRequests; /// public sealed class QueryCurrentUser { - private const string CurrentUserFileName = "CurrentUser.graphql"; + private const string QueryFileName = "CurrentUser.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; @@ -74,7 +74,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(CurrentUserFileName), + await apiRequestService.ConstructGraphQlQuery(QueryFileName), new { databaseId = appSettings.DatabaseId diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index bd5cc481..1824532c 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -10,13 +10,14 @@ namespace Database.Data; +// `DbContext` needs this constructor without owned entities. public sealed class GetHttpsResource( string? description, string hashValue, Guid dataFormatId, - Guid? parentId - ) - : Entity + string? fileExtension +) +: Entity { public const string FilesDirectoryPath = "./files/"; @@ -24,6 +25,7 @@ public GetHttpsResource( string? description, string hashValue, Guid dataFormatId, + string? fileExtension, Guid? calorimetricDataId, Guid? geometricDataId, Guid? hygrothermalDataId, @@ -37,62 +39,17 @@ public GetHttpsResource( description, hashValue, dataFormatId, - parentId, - archivedFilesMetaInformation, - appliedConversionMethod + fileExtension ) { CalorimetricDataId = calorimetricDataId; - GeometricDataId = geometricDataId; HygrothermalDataId = hygrothermalDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; - AssertThatExactlyOneDataIdIsNonNull(); - } - - public GetHttpsResource( - string? description, - string hashValue, - Guid dataFormatId, - Guid? parentId, - ICollection archivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethod? appliedConversionMethod - ) - : this( - description, - hashValue, - dataFormatId, - parentId - ) - { + GeometricDataId = geometricDataId; + ParentId = parentId; ArchivedFilesMetaInformation = archivedFilesMetaInformation; AppliedConversionMethod = appliedConversionMethod; - } - - // `DbContext` needs this constructor without owned entities. - public GetHttpsResource( - string? description, - string hashValue, - Guid dataFormatId, - Guid? calorimetricDataId, - Guid? hygrothermalDataId, - Guid? opticalDataId, - Guid? photovoltaicDataId, - Guid? geometricDataId, - Guid? parentId - ) - : this( - description, - hashValue, - dataFormatId, - parentId - ) - { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; - GeometricDataId = geometricDataId; AssertThatExactlyOneDataIdIsNonNull(); } @@ -117,10 +74,17 @@ private void AssertThatExactlyOneDataIdIsNonNull() } } - public string FilePath { get => Path.Combine(FilesDirectoryPath, Id.ToString("D")); } public string? Description { get; private set; } = description; public string HashValue { get; private set; } = hashValue; public Guid DataFormatId { get; private set; } = dataFormatId; + public string? FileExtension { get; private set; } = fileExtension; + + public string FileName => + Id.ToString("D") + + (FileExtension is null ? "" : $".{FileExtension}"); + + public string FilePath => + Path.Combine(FilesDirectoryPath, FileName); public ICollection ArchivedFilesMetaInformation { get; private set; } = []; @@ -155,7 +119,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(GeometricData.Resources))] public GeometricData? GeometricData { get; set; } - public Guid? ParentId { get; private set; } = parentId; + public Guid? ParentId { get; private set; } // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index da234044..7fe9afa5 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -11,6 +11,8 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Database.Extensions; +using Database.GraphQl.DataX; +using Database.ApiRequests; namespace Database.GraphQl.CalorimetricDataX; @@ -24,10 +26,9 @@ public sealed record CreateCalorimetricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, double[] GValues, double[] UValues -) +) : IValidateCreateInput { public CalorimetricData ToDomainModel(Guid userId) { @@ -44,7 +45,6 @@ public CalorimetricData ToDomainModel(Guid userId) GValues, UValues ); - calorimetricData.Resources.Add(RootResource.ToDomainModel()); return calorimetricData; } }; @@ -55,6 +55,9 @@ public enum CreateCalorimetricDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, CREATING_RESPONSE_APPROVAL_FAILED } @@ -72,7 +75,7 @@ public sealed record CreateCalorimetricDataPayload( [ExtendObjectType(nameof(Mutation))] public sealed class CreateCalorimetricDataMutation -: DataMutationBase +: CreateDataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] protected override CreateCalorimetricDataPayload NewPayload( @@ -90,6 +93,8 @@ public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -106,6 +111,21 @@ CancellationToken cancellationToken return authorizeErrorPayload; } + if ((await ValidateAsync( + input, + CreateCalorimetricDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_CREATOR, + cancellationToken + ) + ).Failed(out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + var calorimetricData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs new file mode 100644 index 00000000..237dd20e --- /dev/null +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -0,0 +1,73 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using Database.ApiRequests; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.GraphQl.DataX; + +public interface IValidateCreateInput +{ + Guid ComponentId { get; } + DateTime CreatedAt { get; } + Guid CreatorId { get; } + AppliedMethodInput AppliedMethod { get; } +} + +public abstract class CreateDataMutationBase +: DataMutationBase +where TData : class +where TPayload : class +{ + protected async Task> ValidateAsync( + IValidateCreateInput input, + TErrorCode illegalCreatedAtErrorCode, + IComponentByIdDataLoader componentByIdDataLoader, + TErrorCode unknownComponentErrorCode, + IInstitutionByIdDataLoader institutionByIdDataLoader, + TErrorCode unknownCreatorErrorCode, + CancellationToken cancellationToken + ) + { + var errors = new List(); + if (await componentByIdDataLoader.LoadAsync(input.ComponentId, cancellationToken) is null) + { + errors.Add( + NewError( + unknownComponentErrorCode, + "The data format does not exist.", + [nameof(input), nameof(input.ComponentId).ToLowerFirst()] + ) + ); + } + if (input.CreatedAt > DateTime.UtcNow) + { + errors.Add( + NewError( + illegalCreatedAtErrorCode, + "The creation date is in the future.", + [nameof(input), nameof(input.CreatedAt).ToLowerFirst()] + ) + ); + } + if (await institutionByIdDataLoader.LoadAsync(input.CreatorId, cancellationToken) is null) + { + errors.Add( + NewError( + unknownCreatorErrorCode, + "The creator does not exist.", + [nameof(input), nameof(input.CreatorId).ToLowerFirst()] + ) + ); + } + // TODO check applied method sources + if (errors.Count >= 1) + { + return new Result.Error( + NewPayload(null, errors) + ); + } + return new Result.Data(true); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 6d651303..fa065eb5 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -10,7 +10,9 @@ using System.Diagnostics.CodeAnalysis; using HotChocolate; using System.Collections.Generic; +using Database.GraphQl.DataX; using Database.Extensions; +using Database.ApiRequests; namespace Database.GraphQl.GeometricDataX; @@ -23,9 +25,8 @@ public sealed record CreateGeometricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, double[] Thicknesses -) +) : IValidateCreateInput { public GeometricData ToDomainModel(Guid userId) { @@ -41,7 +42,6 @@ public GeometricData ToDomainModel(Guid userId) AppliedMethod.ToDomainModel(), Thicknesses ); - geometricData.Resources.Add(RootResource.ToDomainModel()); return geometricData; } }; @@ -52,6 +52,9 @@ public enum CreateGeometricDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, CREATING_RESPONSE_APPROVAL_FAILED } @@ -69,7 +72,7 @@ public sealed record CreateGeometricDataPayload( [ExtendObjectType(nameof(Mutation))] public sealed class CreateGeometricDataMutation -: DataMutationBase +: CreateDataMutationBase { // [UseUserManager] //[Authorize(Policy = Configuration.AuthConfiguration.WriteApiScope)] @@ -88,6 +91,8 @@ public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -104,6 +109,21 @@ CancellationToken cancellationToken return authorizeErrorPayload; } + if ((await ValidateAsync( + input, + CreateGeometricDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_CREATOR, + cancellationToken + ) + ).Failed(out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + var geometricData = input.ToDomainModel(currentUser.Uuid); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index d3cfbb4b..b0c0ed02 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -15,6 +15,7 @@ using Database.Extensions; using GraphQL.Client.Abstractions.Utilities; using Microsoft.EntityFrameworkCore; +using Database.ApiRequests; namespace Database.GraphQl.GetHttpsResources; @@ -28,12 +29,13 @@ public sealed record CreateGetHttpsResourceInput( ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod ) : IIdentifyDataInput { - public GetHttpsResource ToDomainModel() + public GetHttpsResource ToDomainModel(string? fileExtension) { return new GetHttpsResource( Description, Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. DataFormatId, + fileExtension, DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, @@ -56,7 +58,8 @@ public enum CreateGetHttpsResourceErrorCode APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, CREATING_RESPONSE_APPROVAL_FAILED, UNKNOWN_PARENT, - ILLEGAL_PARENT + ILLEGAL_PARENT, + UNKNOWN_DATA_FORMAT } public sealed record CreateGetHttpsResourceError( @@ -99,6 +102,7 @@ IReadOnlyList path public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CommonAuthorization authorization, CancellationToken cancellationToken @@ -129,6 +133,17 @@ CancellationToken cancellationToken } var errors = new List(); + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); + if (dataFormat is null) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA_FORMAT, + "There is no data format with the format ID.", + [nameof(input), nameof(input.DataFormatId).ToLowerFirst()] + ) + ); + } var parent = await context.GetHttpsResources .SingleOrDefaultAsync(_ => _.Id == input.ParentId, @@ -169,7 +184,7 @@ CancellationToken cancellationToken return NewPayload(null, errors); } - var getHttpsResource = input.ToDomainModel(); + var getHttpsResource = input.ToDomainModel(dataFormat?.Extension); context.GetHttpsResources.Add(getHttpsResource); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index fa7d53e5..20b9df77 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -68,7 +68,7 @@ AppSettings appSettings { return new UriBuilder(appSettings.HostUri) { - Path = $"/api/resources/{getHttpsResource.Id}" + Path = $"/api/resources/{getHttpsResource.FileName}" }.Uri; } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 33cd45ac..e1b4b0bf 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -14,9 +14,15 @@ IObjectTypeDescriptor descriptor descriptor .Field("locator") .ResolveWith(t => t.GetLocator(default!, default!)); + descriptor + .Field(_ => _.FileName) + .Ignore(); descriptor .Field(_ => _.FilePath) .Ignore(); + descriptor + .Field(_ => _.FileExtension) + .Ignore(); descriptor .Field(x => x.ParentId) .Ignore(); diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index d240890f..f06a0ca8 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -10,7 +10,9 @@ using HotChocolate; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using Database.GraphQl.DataX; using Database.Extensions; +using Database.ApiRequests; namespace Database.GraphQl.HygrothermalDataX; @@ -23,9 +25,8 @@ public sealed record CreateHygrothermalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource -) + AppliedMethodInput AppliedMethod +) : IValidateCreateInput { public HygrothermalData ToDomainModel(Guid userId) { @@ -40,7 +41,6 @@ public HygrothermalData ToDomainModel(Guid userId) CreatedAt, AppliedMethod.ToDomainModel() ); - hygrothermalData.Resources.Add(RootResource.ToDomainModel()); return hygrothermalData; } }; @@ -51,6 +51,9 @@ public enum CreateHygrothermalDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, CREATING_RESPONSE_APPROVAL_FAILED } @@ -68,7 +71,7 @@ public sealed record CreateHygrothermalDataPayload( [ExtendObjectType(nameof(Mutation))] public sealed class CreateHygrothermalDataMutation -: DataMutationBase +: CreateDataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] protected override CreateHygrothermalDataPayload NewPayload( @@ -86,6 +89,8 @@ public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -102,6 +107,21 @@ CancellationToken cancellationToken return authorizeErrorPayload; } + if ((await ValidateAsync( + input, + CreateHygrothermalDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_CREATOR, + cancellationToken + ) + ).Failed(out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + var hygrothermalData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index a32c923e..70fe5799 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -12,6 +12,7 @@ using HotChocolate; using Database.Enumerations; using System.Linq; +using Database.ApiRequests; namespace Database.GraphQl.OpticalDataX; @@ -28,7 +29,6 @@ public sealed record CreateOpticalDataInput( OpticalComponentSubtype? Subtype, CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource, double[] NearnormalHemisphericalVisibleTransmittances, double[] NearnormalHemisphericalVisibleReflectances, double[] NearnormalHemisphericalSolarTransmittances, @@ -36,7 +36,7 @@ public sealed record CreateOpticalDataInput( double[] InfraredEmittances, double[] ColorRenderingIndices, IReadOnlyList CielabColors -) +) : IValidateCreateInput { public OpticalData ToDomainModel(Guid userId) { @@ -61,7 +61,6 @@ public OpticalData ToDomainModel(Guid userId) ColorRenderingIndices, CielabColors.Select(c => c.ToDomainModel()).ToList() ); - opticalData.Resources.Add(RootResource.ToDomainModel()); return opticalData; } }; @@ -72,6 +71,9 @@ public enum CreateOpticalDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, CREATING_RESPONSE_APPROVAL_FAILED } @@ -89,7 +91,7 @@ public sealed record CreateOpticalDataPayload( [ExtendObjectType(nameof(Mutation))] public sealed class CreateOpticalDataMutation -: DataMutationBase +: CreateDataMutationBase { protected override CreateOpticalDataPayload NewPayload( OpticalData? data, @@ -107,6 +109,8 @@ public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -123,6 +127,21 @@ CancellationToken cancellationToken return authorizeErrorPayload; } + if ((await ValidateAsync( + input, + CreateOpticalDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_CREATOR, + cancellationToken + ) + ).Failed(out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + var opticalData = input.ToDomainModel(currentUser.Uuid); context.OpticalData.Add(opticalData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 5123047c..2d86b5fd 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -11,6 +11,8 @@ using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using Database.Extensions; +using Database.GraphQl.DataX; +using Database.ApiRequests; namespace Database.GraphQl.PhotovoltaicDataX; @@ -23,9 +25,8 @@ public sealed record CreatePhotovoltaicDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - AppliedMethodInput AppliedMethod, - RootGetHttpsResourceInput RootResource -) + AppliedMethodInput AppliedMethod +) : IValidateCreateInput { public PhotovoltaicData ToDomainModel(Guid userId) { @@ -40,7 +41,6 @@ public PhotovoltaicData ToDomainModel(Guid userId) CreatedAt, AppliedMethod.ToDomainModel() ); - photovoltaicData.Resources.Add(RootResource.ToDomainModel()); return photovoltaicData; } }; @@ -51,6 +51,9 @@ public enum CreatePhotovoltaicDataErrorCode UNKNOWN, UNAUTHORIZED, UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, CREATING_RESPONSE_APPROVAL_FAILED } @@ -68,7 +71,7 @@ public sealed record CreatePhotovoltaicDataPayload( [ExtendObjectType(nameof(Mutation))] public sealed class CreatePhotovoltaicDataMutation -: DataMutationBase +: CreateDataMutationBase { // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] protected override CreatePhotovoltaicDataPayload NewPayload( @@ -86,6 +89,8 @@ public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -102,6 +107,21 @@ CancellationToken cancellationToken return authorizeErrorPayload; } + if ((await ValidateAsync( + input, + CreatePhotovoltaicDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_CREATOR, + cancellationToken + ) + ).Failed(out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + var photovoltaicData = input.ToDomainModel(currentUser.Uuid); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs deleted file mode 100644 index a04fb72d..00000000 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Database.Data; -using Database.Utilities; - -namespace Database.GraphQl; - -public sealed record RootGetHttpsResourceInput( - string Description, - Guid DataFormatId, - IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod -) -{ - public GetHttpsResource ToDomainModel() - { - return new GetHttpsResource( - Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - DataFormatId, - null, - ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), - AppliedConversionMethod?.ToDomainModel() - ); - } -}; \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 39dd89db..3b5a1a54 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -924,7 +924,6 @@ input CreateCalorimetricDataInput { gValues: [Float!]! locale: String! name: String - rootResource: RootGetHttpsResourceInput! uValues: [Float!]! warnings: [String!]! } @@ -937,7 +936,6 @@ input CreateGeometricDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! } @@ -960,7 +958,6 @@ input CreateHygrothermalDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -980,7 +977,6 @@ input CreateOpticalDataInput { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - rootResource: RootGetHttpsResourceInput! subtype: OpticalComponentSubtype type: OpticalComponentType warnings: [String!]! @@ -994,7 +990,6 @@ input CreatePhotovoltaicDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -1557,13 +1552,6 @@ input RetractDataInput { dataKind: DataKind! } -input RootGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - description: String! -} - input StandardInput { abstract: String locator: Url @@ -1820,6 +1808,9 @@ enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1827,6 +1818,9 @@ enum CreateGeometricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1839,12 +1833,16 @@ enum CreateGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED UNKNOWN_PARENT ILLEGAL_PARENT + UNKNOWN_DATA_FORMAT } enum CreateHygrothermalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1852,6 +1850,9 @@ enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1859,6 +1860,9 @@ enum CreatePhotovoltaicDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 39dd89db..3b5a1a54 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -924,7 +924,6 @@ input CreateCalorimetricDataInput { gValues: [Float!]! locale: String! name: String - rootResource: RootGetHttpsResourceInput! uValues: [Float!]! warnings: [String!]! } @@ -937,7 +936,6 @@ input CreateGeometricDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! } @@ -960,7 +958,6 @@ input CreateHygrothermalDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -980,7 +977,6 @@ input CreateOpticalDataInput { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - rootResource: RootGetHttpsResourceInput! subtype: OpticalComponentSubtype type: OpticalComponentType warnings: [String!]! @@ -994,7 +990,6 @@ input CreatePhotovoltaicDataInput { description: String locale: String! name: String - rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -1557,13 +1552,6 @@ input RetractDataInput { dataKind: DataKind! } -input RootGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - description: String! -} - input StandardInput { abstract: String locator: Url @@ -1820,6 +1808,9 @@ enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1827,6 +1818,9 @@ enum CreateGeometricDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1839,12 +1833,16 @@ enum CreateGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED UNKNOWN_PARENT ILLEGAL_PARENT + UNKNOWN_DATA_FORMAT } enum CreateHygrothermalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1852,6 +1850,9 @@ enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } @@ -1859,6 +1860,9 @@ enum CreatePhotovoltaicDataErrorCode { UNKNOWN UNAUTHORIZED UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR CREATING_RESPONSE_APPROVAL_FAILED } From 83388f4962bbe05291f6818acfd40fec26929035 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 31 Oct 2025 15:43:12 +0100 Subject: [PATCH 536/878] Add file extension to resources and to their backing files, check validity of inputs before creating entities, set publishing state of existing data sets to `published`, construct query context manually from resolver context as otherwise various errors occur from GreenDonut requiring projections/selections to work, and do not try to save database contexts in parallel --- backend/src/Data/GetHttpsResource.cs | 57 +- .../CreateCalorimetricDataMutation.cs | 4 +- backend/src/GraphQl/DataX/DataResolvers.cs | 5 +- backend/src/GraphQl/DataX/DataType.cs | 3 + backend/src/GraphQl/DataX/DataTypeBase.cs | 3 + .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 5 +- .../GetHttpsResourceTreeNonRootVertex.cs | 12 +- .../src/GraphQl/DataX/PublishDataMutation.cs | 54 +- .../CreateGeometricDataMutation.cs | 3 +- .../CreateGetHttpsResourceMutation.cs | 40 +- ...mputeGetHttpsResourceHashValuesMutation.cs | 5 +- .../CreateHygrothermalDataMutation.cs | 4 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 3 +- .../CreatePhotovoltaicDataMutation.cs | 4 +- .../CreateResponseApprovalsFilterType.cs | 2 +- .../CreateResponseApprovalsMutation.cs | 34 +- .../ResponseApprovalFilterType.cs | 14 +- .../UpdateResponseApprovalsFilterType.cs | 2 +- .../UpdateResponseApprovalsMutation.cs | 34 +- ...736_AddFileExtensionToResource.Designer.cs | 2662 +++++++++++++++++ ...251031125736_AddFileExtensionToResource.cs | 30 + .../ApplicationDbContextModelSnapshot.cs | 3 + backend/src/Migrations/migrate.sql | 23 + ...030170839_AddPublishingStateToDataSets.sql | 5 + ...51031125736_AddFileExtensionToResource.sql | 18 + ...030170839_AddPublishingStateToDataSets.sql | 16 + .../GraphQlSchemaTests.IsUnchanged.snap | 22 +- frontend/type-defs.graphqls | 22 +- 28 files changed, 3012 insertions(+), 77 deletions(-) create mode 100644 backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs create mode 100644 backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs create mode 100644 backend/src/Migrations/migrate_from_20251030170839_AddPublishingStateToDataSets_to_20251031125736_AddFileExtensionToResource.sql create mode 100644 backend/src/Migrations/rollback_from_20251031125736_AddFileExtensionToResource_to_20251030170839_AddPublishingStateToDataSets.sql diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 1824532c..14de4ff4 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -5,22 +5,32 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Enumerations; using Database.Extensions; using Database.Utilities; +using EntityFrameworkCore.Projectables; namespace Database.Data; // `DbContext` needs this constructor without owned entities. -public sealed class GetHttpsResource( - string? description, - string hashValue, - Guid dataFormatId, - string? fileExtension -) +public sealed class GetHttpsResource : Entity { public const string FilesDirectoryPath = "./files/"; + public GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + string? fileExtension + ) + { + Description = description; + HashValue = hashValue; + DataFormatId = dataFormatId; + FileExtension = fileExtension; + } + public GetHttpsResource( string? description, string hashValue, @@ -74,10 +84,10 @@ private void AssertThatExactlyOneDataIdIsNonNull() } } - public string? Description { get; private set; } = description; - public string HashValue { get; private set; } = hashValue; - public Guid DataFormatId { get; private set; } = dataFormatId; - public string? FileExtension { get; private set; } = fileExtension; + public string? Description { get; private set; } + public string HashValue { get; private set; } + public Guid DataFormatId { get; private set; } + public string? FileExtension { get; private set; } public string FileName => Id.ToString("D") @@ -92,7 +102,32 @@ private void AssertThatExactlyOneDataIdIsNonNull() [NotMapped] public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; - [NotMapped] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; + [NotMapped] + public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; + + [Projectable] + public Guid? GetDataId(DataKind dataKind) => + dataKind switch + { + DataKind.CALORIMETRIC_DATA => CalorimetricDataId, + DataKind.GEOMETRIC_DATA => GeometricDataId, + DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + DataKind.OPTICAL_DATA => OpticalDataId, + DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, + _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), + }; + + [Projectable] + public IData? GetData(DataKind dataKind) => + dataKind switch + { + DataKind.CALORIMETRIC_DATA => CalorimetricData, + DataKind.GEOMETRIC_DATA => GeometricData, + DataKind.HYGROTHERMAL_DATA => HygrothermalData, + DataKind.OPTICAL_DATA => OpticalData, + DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), + }; public Guid? CalorimetricDataId { get; private set; } diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 7fe9afa5..cc3fa0da 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -32,7 +32,7 @@ double[] UValues { public CalorimetricData ToDomainModel(Guid userId) { - var calorimetricData = new CalorimetricData( + return new( userId, Locale, ComponentId, @@ -45,7 +45,6 @@ public CalorimetricData ToDomainModel(Guid userId) GValues, UValues ); - return calorimetricData; } }; @@ -127,6 +126,7 @@ CancellationToken cancellationToken } var calorimetricData = input.ToDomainModel(currentUser.Uuid); + context.CalorimetricData.Add(calorimetricData); await context.SaveChangesAsync(cancellationToken); if ((await CreateResponseApprovalAsync( diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 77fc9132..6f7b6c59 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -7,6 +7,8 @@ using HotChocolate.Data; using GreenDonut; using Database.GraphQl.GetHttpsResources; +using HotChocolate.Resolvers; +using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; @@ -16,11 +18,12 @@ public sealed class DataResolvers [UseSorting] public async Task GetGetHttpsResources( [Parent] IData data, - QueryContext queryContext, + IResolverContext resolverContext, GetHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { + var queryContext = resolverContext.GetQueryContext(); return await byId .With(queryContext) diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 646d6925..321f2536 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -35,5 +35,8 @@ IInterfaceTypeDescriptor descriptor descriptor .Field(x => x.Approval) .Type>>(); + descriptor + .Field(_ => _.PublishingState) + .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index f5067c70..f7711e56 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -31,5 +31,8 @@ IObjectTypeDescriptor descriptor descriptor .Field(x => x.Approval) .Type>>(); + descriptor + .Field(_ => _.PublishingState) + .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index ef9aca1d..13a23a55 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -6,6 +6,8 @@ using GreenDonut.Data; using Database.Data; using HotChocolate.Data; +using HotChocolate.Resolvers; +using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; @@ -27,11 +29,12 @@ CancellationToken cancellationToken [UseFiltering] [UseSorting] public async Task> GetNonRootVertices( - QueryContext queryContext, + IResolverContext resolverContext, GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, CancellationToken cancellationToken ) { + var queryContext = resolverContext.GetQueryContext(); return ( await byId .With(queryContext) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs index 49d551b0..56403530 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -7,8 +7,8 @@ namespace Database.GraphQl.DataX; public sealed class GetHttpsResourceTreeNonRootVertex( GetHttpsResource value - ) - : IGetHttpsResourceTreeVertex +) +: IGetHttpsResourceTreeVertex { [GraphQLType>] public string ParentId => @@ -17,11 +17,11 @@ GetHttpsResource value ?? throw new InvalidOperationException("Impossible! Each non-root vertex has a parent.") ); - public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } = value.AppliedConversionMethod ?? - throw new InvalidOperationException( - "Each non-root vertex has an applied conversion method."); + public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } = + value.AppliedConversionMethod ?? throw new InvalidOperationException("Each non-root vertex has an applied conversion method."); - [GraphQLType>] public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); + [GraphQLType>] public string VertexId => + GetHttpsResource.ConstructVertexId(Value.Id); public GetHttpsResource Value { get; } = value; } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index de60db31..28f568c8 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -3,13 +3,14 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate; using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; using Database.Services; +using Microsoft.EntityFrameworkCore; +using System.Linq; namespace Database.GraphQl.DataX; @@ -24,7 +25,10 @@ public enum PublishDataErrorCode UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, - CREATING_RESPONSE_APPROVAL_FAILED + CREATING_RESPONSE_APPROVAL_FAILED, + MISSING_ROOT_RESOURCE, + DUPLICATE_ROOT_RESOURCE, + RESOURCES_WITHOUT_FILE } public sealed record PublishDataError( @@ -85,6 +89,52 @@ CancellationToken cancellationToken return fetchDataErrorPayload; } + var errors = new List(); + var rootResources = + await context.GetHttpsResources.AsQueryable() + .Where(_ => _.ParentId == null) + .Where(_ => _.GetDataId(input.DataKind) == input.DataId) + .ToListAsync(cancellationToken); + if (rootResources.Count == 0) + { + errors.Add( + NewError( + PublishDataErrorCode.MISSING_ROOT_RESOURCE, + "The data set does not have a root resource.", + [] + ) + ); + } + if (rootResources.Count >= 2) + { + errors.Add( + NewError( + PublishDataErrorCode.DUPLICATE_ROOT_RESOURCE, + $"The data set has more than one root resource, namely those with the IDs {string.Join(", ", rootResources.Select(_ => _.Id.ToString("D")))}.", + [] + ) + ); + } + var resources = + await context.GetHttpsResources.AsQueryable() + .Where(_ => _.GetDataId(input.DataKind) == input.DataId) + .ToListAsync(cancellationToken); + var resourcesWithoutFile = resources.Where(_ => !_.DoesFileExist()).ToList(); + if (resourcesWithoutFile.Count >= 1) + { + errors.Add( + NewError( + PublishDataErrorCode.RESOURCES_WITHOUT_FILE, + $"The data set has at least one resource without a file, namely those with the IDs {string.Join(", ", resourcesWithoutFile.Select(_ => _.Id.ToString("D")))}.", + [] + ) + ); + } + if (errors.Count >= 1) + { + return NewPayload(null, errors); + } + var rememberedPublishingState = data.PublishingState; data.Publish(); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index fa065eb5..55519401 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -30,7 +30,7 @@ double[] Thicknesses { public GeometricData ToDomainModel(Guid userId) { - var geometricData = new GeometricData( + return new( userId, Locale, ComponentId, @@ -42,7 +42,6 @@ public GeometricData ToDomainModel(Guid userId) AppliedMethod.ToDomainModel(), Thicknesses ); - return geometricData; } }; diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index b0c0ed02..784c80be 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -55,11 +55,13 @@ public enum CreateGetHttpsResourceErrorCode UNAUTHORIZED, UNAUTHENTICATED, UNKNOWN_DATA, - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, - CREATING_RESPONSE_APPROVAL_FAILED, UNKNOWN_PARENT, ILLEGAL_PARENT, - UNKNOWN_DATA_FORMAT + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, + UNKNOWN_DATA_FORMAT, + DUPLICATE_ROOT_RESOURCE, + CREATING_RESPONSE_APPROVAL_FAILED, + PARENT_WITHOUT_APPLIED_CONVERSION_METHOD } public sealed record CreateGetHttpsResourceError( @@ -144,12 +146,15 @@ CancellationToken cancellationToken ) ); } - var parent = await context.GetHttpsResources + var parent = + input.ParentId is null + ? null + : await context.GetHttpsResources .SingleOrDefaultAsync(_ => _.Id == input.ParentId, cancellationToken ); - if (parent is null) + if (parent is null && input.ParentId is not null) { errors.Add( NewError( @@ -179,6 +184,31 @@ CancellationToken cancellationToken ) ); } + if (input.ParentId is not null && input.AppliedConversionMethod is null) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.PARENT_WITHOUT_APPLIED_CONVERSION_METHOD, + "The resource has a parent yet no applied conversion method was given. How did this resource come about from its parent?", + [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst()] + ) + ); + } + if (input.ParentId is null && + await context.GetHttpsResources.AsNoTracking() + .Where(_ => _.ParentId == null) + .Where(_ => _.GetDataId(input.DataKind) == input.DataId) + .AnyAsync(cancellationToken) + ) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.DUPLICATE_ROOT_RESOURCE, + "The data set does already have a root resource.", + [nameof(input), nameof(input.ParentId).ToLowerFirst()] + ) + ); + } if (errors.Count >= 1) { return NewPayload(null, errors); diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 44b22369..8b875634 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -15,6 +15,7 @@ using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using Database.Extensions; +using HotChocolate.Resolvers; namespace Database.GraphQl.GetHttpsResources; @@ -67,13 +68,15 @@ IReadOnlyList path [UseFiltering] public async Task RecomputeGetHttpsResourceHashValuesAsync( ApplicationDbContext context, - QueryContext queryContext, + IResolverContext resolverContext, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, ILogger logger, CancellationToken cancellationToken ) { + var queryContext = resolverContext.GetQueryContext(); + if ((await AuthorizeAsync( RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index f06a0ca8..8b044dcc 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -30,7 +30,7 @@ AppliedMethodInput AppliedMethod { public HygrothermalData ToDomainModel(Guid userId) { - var hygrothermalData = new HygrothermalData( + return new( userId, Locale, ComponentId, @@ -41,7 +41,6 @@ public HygrothermalData ToDomainModel(Guid userId) CreatedAt, AppliedMethod.ToDomainModel() ); - return hygrothermalData; } }; @@ -123,6 +122,7 @@ CancellationToken cancellationToken } var hygrothermalData = input.ToDomainModel(currentUser.Uuid); + context.HygrothermalData.Add(hygrothermalData); await context.SaveChangesAsync(cancellationToken); if ((await CreateResponseApprovalAsync( diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 70fe5799..25ca43c0 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -40,7 +40,7 @@ IReadOnlyList CielabColors { public OpticalData ToDomainModel(Guid userId) { - var opticalData = new OpticalData( + return new( userId, Locale, ComponentId, @@ -61,7 +61,6 @@ public OpticalData ToDomainModel(Guid userId) ColorRenderingIndices, CielabColors.Select(c => c.ToDomainModel()).ToList() ); - return opticalData; } }; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 2d86b5fd..ee618786 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -30,7 +30,7 @@ AppliedMethodInput AppliedMethod { public PhotovoltaicData ToDomainModel(Guid userId) { - var photovoltaicData = new PhotovoltaicData( + return new( userId, Locale, ComponentId, @@ -41,7 +41,6 @@ public PhotovoltaicData ToDomainModel(Guid userId) CreatedAt, AppliedMethod.ToDomainModel() ); - return photovoltaicData; } }; @@ -123,6 +122,7 @@ CancellationToken cancellationToken } var photovoltaicData = input.ToDomainModel(currentUser.Uuid); + context.PhotovoltaicData.Add(photovoltaicData); await context.SaveChangesAsync(cancellationToken); if ((await CreateResponseApprovalAsync( diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs index f31de19b..0b131968 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs @@ -7,7 +7,7 @@ public sealed class CreateResponseApprovalsFilterType : ResponseApprovalFilterType { protected override void Configure( - IFilterInputTypeDescriptor descriptor + IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index ad02682b..c16e427b 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -13,6 +13,8 @@ using HotChocolate.Types; using Microsoft.Extensions.Logging; using Database.Extensions; +using Database.GraphQl.Extensions; +using HotChocolate.Resolvers; namespace Database.GraphQl.ResponseApprovals; @@ -37,9 +39,9 @@ public static partial class CreateResponseApprovalsMutationLogging { [LoggerMessage( Level = LogLevel.Warning, - Message = "Creating response approval for data of type {Type} with ID {Id} failed with the errors {Errors}." + Message = "Creating response approval for data of type {Type} with ID {Id}." )] - public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, IEnumerable? errors); + public static partial void FailedCreatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); } public sealed record CreateResponseApprovalsPayload( @@ -65,7 +67,7 @@ IReadOnlyList path [UseFiltering] public async Task CreateResponseApprovalsAsync( - QueryContext queryContext, + IResolverContext resolverContext, ApplicationDbContext context, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, @@ -73,6 +75,7 @@ public async Task CreateResponseApprovalsAsync( CancellationToken cancellationToken ) { + var queryContext = resolverContext.GetQueryContext(); if ((await AuthorizeAsync( CreateResponseApprovalsErrorCode.UNAUTHENTICATED, CreateResponseApprovalsErrorCode.UNAUTHORIZED, @@ -91,22 +94,21 @@ await Parallel.ForEachAsync( cancellationToken, async (data, cancellationToken) => { - if ((await CreateResponseApprovalAsync( - data, - CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - responseApprovalService, - context, - cancellationToken - ) - ).Failed(out var createResponseApprovalErrorPayload) - ) + try { - logger.FailedCreatingResponseApproval(data.GetType(), data.Id, createResponseApprovalErrorPayload.Errors?.Select(_ => _.Message)); - errors.AddRange(createResponseApprovalErrorPayload.Errors); + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); } - else + catch (Exception exception) { - dataSets.Add(data); + logger.FailedCreatingResponseApproval(data.GetType(), data.Id, exception); + errors.Add( + NewError( + CreateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Failed creating a response approval for the data set {data.Id} named '{data.Name}' with the error message: {exception.Message}", + [] + ) + ); } } ); diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index 894d3206..2724d8e9 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -4,12 +4,22 @@ namespace Database.GraphQl.ResponseApprovals; public abstract class ResponseApprovalFilterType -: FilterInputType +: FilterInputType { protected override void Configure( - IFilterInputTypeDescriptor descriptor + IFilterInputTypeDescriptor descriptor ) { descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs index ca9c95d6..855deddc 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs @@ -7,7 +7,7 @@ public sealed class UpdateResponseApprovalsFilterType : ResponseApprovalFilterType { protected override void Configure( - IFilterInputTypeDescriptor descriptor + IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 9c1d7292..e5cd1c09 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -13,6 +13,8 @@ using HotChocolate.Data; using Microsoft.Extensions.Logging; using Database.Extensions; +using Database.GraphQl.Extensions; +using HotChocolate.Resolvers; namespace Database.GraphQl.ResponseApprovals; @@ -37,9 +39,9 @@ public static partial class UpdateResponseApprovalsMutationLogging { [LoggerMessage( Level = LogLevel.Warning, - Message = "Updating response approval for data of type {Type} with ID {Id} failed with the errors {Errors}." + Message = "Updating response approval for data of type {Type} with ID {Id} failed." )] - public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, IEnumerable? errors); + public static partial void FailedUpdatingResponseApproval(this ILogger logger, Type type, Guid id, Exception exception); } public sealed record UpdateResponseApprovalsPayload( @@ -64,7 +66,7 @@ IReadOnlyList path [UseFiltering] public async Task UpdateResponseApprovalsAsync( - QueryContext queryContext, + IResolverContext resolverContext, ApplicationDbContext context, CommonAuthorization authorization, ResponseApprovalService responseApprovalService, @@ -72,6 +74,7 @@ public async Task UpdateResponseApprovalsAsync( CancellationToken cancellationToken ) { + var queryContext = resolverContext.GetQueryContext(); if ((await AuthorizeAsync( UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, UpdateResponseApprovalsErrorCode.UNAUTHORIZED, @@ -91,22 +94,21 @@ await Parallel.ForEachAsync( cancellationToken, async (data, cancellationToken) => { - if ((await CreateResponseApprovalAsync( - data, - UpdateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, - responseApprovalService, - context, - cancellationToken - ) - ).Failed(out var createResponseApprovalErrorPayload) - ) + try { - logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, createResponseApprovalErrorPayload.Errors?.Select(_ => _.Message)); - errors.AddRange(createResponseApprovalErrorPayload.Errors); + data.Approval = await responseApprovalService.CreateResponseApproval(data, cancellationToken); + dataSets.Add(data); } - else + catch (Exception exception) { - dataSets.Add(data); + logger.FailedUpdatingResponseApproval(data.GetType(), data.Id, exception); + errors.Add( + NewError( + UpdateResponseApprovalsErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + $"Failed creating a response approval for the data set {data.Id} named '{data.Name}' with the error message: {exception.Message}", + [] + ) + ); } } ); diff --git a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs new file mode 100644 index 00000000..70f2055b --- /dev/null +++ b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs @@ -0,0 +1,2662 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251031125736_AddFileExtensionToResource")] + partial class AddFileExtensionToResource + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId"); + + b.HasIndex("GeometricDataId"); + + b.HasIndex("HygrothermalDataId"); + + b.HasIndex("OpticalDataId"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId"); + + b.ToTable("get_https_resource", "database"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId"); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId"); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId"); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId"); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId"); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs new file mode 100644 index 00000000..c16db1ba --- /dev/null +++ b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddFileExtensionToResource : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "FileExtension", + schema: "database", + table: "get_https_resource", + type: "text", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "FileExtension", + schema: "database", + table: "get_https_resource"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index f77cd21f..257a132c 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -157,6 +157,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); + b.Property("FileExtension") + .HasColumnType("text"); + b.Property("GeometricDataId") .HasColumnType("uuid"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 3e0ea285..b939cb1b 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1482,6 +1482,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.photovoltaic_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.photovoltaic_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -1489,6 +1490,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.optical_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.optical_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -1496,6 +1498,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.hygrothermal_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.hygrothermal_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -1503,6 +1506,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.geometric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.geometric_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -1510,6 +1514,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.calorimetric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.calorimetric_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -1522,3 +1527,21 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + ALTER TABLE database.get_https_resource ADD "FileExtension" text; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251031125736_AddFileExtensionToResource', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql index 460204c1..2d7abad2 100644 --- a/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql +++ b/backend/src/Migrations/migrate_from_20251024124734_AddObserverAndIlluminantToCielabColor_to_20251030170839_AddPublishingStateToDataSets.sql @@ -11,6 +11,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.photovoltaic_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.photovoltaic_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -18,6 +19,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.optical_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.optical_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -25,6 +27,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.hygrothermal_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.hygrothermal_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -32,6 +35,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.geometric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.geometric_data SET "PublishingState" = 'published'; END IF; END $EF$; @@ -39,6 +43,7 @@ DO $EF$ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251030170839_AddPublishingStateToDataSets') THEN ALTER TABLE database.calorimetric_data ADD "PublishingState" database.publishing_state NOT NULL DEFAULT 'pending'::database.publishing_state; + UPDATE database.calorimetric_data SET "PublishingState" = 'published'; END IF; END $EF$; diff --git a/backend/src/Migrations/migrate_from_20251030170839_AddPublishingStateToDataSets_to_20251031125736_AddFileExtensionToResource.sql b/backend/src/Migrations/migrate_from_20251030170839_AddPublishingStateToDataSets_to_20251031125736_AddFileExtensionToResource.sql new file mode 100644 index 00000000..03bfb9c5 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251030170839_AddPublishingStateToDataSets_to_20251031125736_AddFileExtensionToResource.sql @@ -0,0 +1,18 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + ALTER TABLE database.get_https_resource ADD "FileExtension" text; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251031125736_AddFileExtensionToResource', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251031125736_AddFileExtensionToResource_to_20251030170839_AddPublishingStateToDataSets.sql b/backend/src/Migrations/rollback_from_20251031125736_AddFileExtensionToResource_to_20251030170839_AddPublishingStateToDataSets.sql new file mode 100644 index 00000000..0b4941ab --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251031125736_AddFileExtensionToResource_to_20251030170839_AddPublishingStateToDataSets.sql @@ -0,0 +1,16 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + ALTER TABLE database.get_https_resource DROP COLUMN "FileExtension"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251031125736_AddFileExtensionToResource'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 3b5a1a54..398ccd18 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -996,6 +996,13 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] + timestamp: DateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + approverId: UuidPropositionInput } input CrossDatabaseDataReferenceInput { @@ -1674,6 +1681,13 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] + timestamp: DateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + approverId: UuidPropositionInput } input UriHostNameTypePropositionInput { @@ -1829,11 +1843,12 @@ enum CreateGetHttpsResourceErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATA - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT - CREATING_RESPONSE_APPROVAL_FAILED UNKNOWN_PARENT ILLEGAL_PARENT + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT UNKNOWN_DATA_FORMAT + DUPLICATE_ROOT_RESOURCE + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateHygrothermalDataErrorCode { @@ -1944,6 +1959,9 @@ enum PublishDataErrorCode { UNAUTHORIZED UNKNOWN_DATA CREATING_RESPONSE_APPROVAL_FAILED + MISSING_ROOT_RESOURCE + DUPLICATE_ROOT_RESOURCE + RESOURCES_WITHOUT_FILE } enum PublishingState { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 3b5a1a54..398ccd18 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -996,6 +996,13 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] + timestamp: DateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + approverId: UuidPropositionInput } input CrossDatabaseDataReferenceInput { @@ -1674,6 +1681,13 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] + timestamp: DateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + approverId: UuidPropositionInput } input UriHostNameTypePropositionInput { @@ -1829,11 +1843,12 @@ enum CreateGetHttpsResourceErrorCode { UNAUTHORIZED UNAUTHENTICATED UNKNOWN_DATA - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT - CREATING_RESPONSE_APPROVAL_FAILED UNKNOWN_PARENT ILLEGAL_PARENT + APPLIED_CONVERSION_METHOD_WITHOUT_PARENT UNKNOWN_DATA_FORMAT + DUPLICATE_ROOT_RESOURCE + CREATING_RESPONSE_APPROVAL_FAILED } enum CreateHygrothermalDataErrorCode { @@ -1944,6 +1959,9 @@ enum PublishDataErrorCode { UNAUTHORIZED UNKNOWN_DATA CREATING_RESPONSE_APPROVAL_FAILED + MISSING_ROOT_RESOURCE + DUPLICATE_ROOT_RESOURCE + RESOURCES_WITHOUT_FILE } enum PublishingState { From 5d7911544563bf7cc77e6d708cb7af376b21354f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 31 Oct 2025 15:47:48 +0100 Subject: [PATCH 537/878] Use `is` to compare number with `0` --- backend/src/GraphQl/DataX/PublishDataMutation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index 28f568c8..736916c9 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -95,7 +95,7 @@ await context.GetHttpsResources.AsQueryable() .Where(_ => _.ParentId == null) .Where(_ => _.GetDataId(input.DataKind) == input.DataId) .ToListAsync(cancellationToken); - if (rootResources.Count == 0) + if (rootResources.Count is 0) { errors.Add( NewError( From 5206d26313e226d1dd744da92ebe47389bc35243 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 16:21:28 +0100 Subject: [PATCH 538/878] Delete resources associated with a data set when the data set is being deleted --- backend/src/Data/ApplicationDbContext.cs | 104 +++++++++++++++++++---- 1 file changed, 87 insertions(+), 17 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b8b125ab..1d823019 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -10,7 +10,6 @@ using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Org.BouncyCastle.Math.EC.Rfc7748; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; namespace Database.Data; @@ -183,12 +182,59 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigureCalorimetricData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.CalorimetricData) + .HasForeignKey(_ => _.CalorimetricDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + + private static + EntityTypeBuilder + ConfigureGeometricData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.GeometricData) + .HasForeignKey(_ => _.GeometricDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + + private static + EntityTypeBuilder + ConfigureHygrothermalData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.HygrothermalData) + .HasForeignKey(_ => _.HygrothermalDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + private static EntityTypeBuilder ConfigureOpticalData( EntityTypeBuilder builder ) { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.OpticalData) + .HasForeignKey(_ => _.OpticalDataId) + .OnDelete(DeleteBehavior.Cascade); builder.OwnsMany( data => data.CielabColors ).ToTable( @@ -203,6 +249,20 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigurePhotovoltaicData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.PhotovoltaicData) + .HasForeignKey(_ => _.PhotovoltaicDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + private static void ConfigureIdentityEntities( ModelBuilder builder ) @@ -218,24 +278,34 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) CreateEnumerations(modelBuilder, _schemaName); ConfigureIdentityEntities(modelBuilder); ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("get_https_resource"); - ConfigureData( - ConfigureEntity(modelBuilder.Entity()) - ).ToTable("calorimetric_data"); - ConfigureData( - ConfigureEntity(modelBuilder.Entity()) - ).ToTable("geometric_data"); - ConfigureData( - ConfigureEntity(modelBuilder.Entity()) - ).ToTable("hygrothermal_data"); + modelBuilder.Entity() + ) + .ToTable("get_https_resource"); + ConfigureCalorimetricData( + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("calorimetric_data") + ); + ConfigureGeometricData( + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("geometric_data") + ); + ConfigureHygrothermalData( + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("hygrothermal_data") + ); ConfigureOpticalData( - ConfigureEntity(modelBuilder.Entity()) + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ) ).ToTable("optical_data"); - ConfigureData( - ConfigureEntity(modelBuilder.Entity()) - ).ToTable("photovoltaic_data"); + ConfigurePhotovoltaicData( + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("photovoltaic_data") + ); ConfigureEntity( modelBuilder.Entity() ) From 4634a0689d7f46a04250cb1c0d1fb9097511520e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 16:21:52 +0100 Subject: [PATCH 539/878] Forbid non-pending data sets to be deleted --- backend/src/GraphQl/DataX/DeleteDataMutation.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index dd628d3b..56966357 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -21,7 +21,8 @@ public enum DeleteDataErrorCode { UNAUTHENTICATED, UNAUTHORIZED, - UNKNOWN_DATA + UNKNOWN_DATA, + NOT_PENDING } public sealed record DeleteDataError( @@ -82,6 +83,18 @@ CancellationToken cancellationToken return fetchDataErrorPayload; } + if (data.PublishingState is not PublishingState.PENDING) + { + return NewPayload( + null, + [NewError( + DeleteDataErrorCode.NOT_PENDING, + $"The publishing state is not pending but {data.PublishingState}. If it is published, you may retract the data set. A retracted data set does not show up in queries for all data.", + [] + )] + ); + } + switch (data) { case CalorimetricData calorimetricData: From 8538cad30e4ce9c653fbfba7ff195556651995ef Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 16:31:10 +0100 Subject: [PATCH 540/878] Default to splitting queries --- see https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally --- backend/src/Startup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7f1dbf19..c3e4d9bb 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -175,6 +175,7 @@ AppSettings appSettings appSettings.Database.ConnectionString, _ => _ .SetPostgresVersion(13, 13) + .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) From bf7b76a1badc9768ab9edc1acb352bd8124439a7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 17:02:50 +0100 Subject: [PATCH 541/878] Delete resource files when resources are deleted --- backend/src/Data/GetHttpsResource.cs | 5 +++++ backend/src/GraphQl/DataX/DeleteDataMutation.cs | 14 ++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 14de4ff4..b8da7fa0 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -170,6 +170,11 @@ public bool DoesFileExist() return File.Exists(FilePath); } + public void DeleteFile() + { + File.Delete(FilePath); + } + public async Task RecomputeHashValue(CancellationToken cancellationToken) { HashValue = await Sha256FileHasher.ComputeForFile(FilePath, cancellationToken); diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 56966357..5ab4239a 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -8,6 +8,9 @@ using Database.Data; using Database.Enumerations; using Database.Extensions; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using System.IO; namespace Database.GraphQl.DataX; @@ -95,6 +98,17 @@ CancellationToken cancellationToken ); } + // Delete resource files if they exist. + // The resources themselves are deleted through cascading deletes. + await context.Entry(data) + .Collection(_ => _.Resources) + .LoadAsync(cancellationToken); + foreach (var resource in data.Resources) + { + resource.DeleteFile(); + } + + // TODO Would a simple `context.Remove(data);` also work? switch (data) { case CalorimetricData calorimetricData: From c5e4eda3ca160211ed5ab769e292a1a7a020b797 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 17:05:52 +0100 Subject: [PATCH 542/878] Move file when extension is updated --- backend/src/Data/GetHttpsResource.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index b8da7fa0..21156a95 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -165,6 +165,13 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(Parent))] public ICollection Children { get; } = []; + public void UpdateFileExtension(string fileExtension) + { + var oldFilePath = FilePath; + FileExtension = fileExtension; + File.Move(oldFilePath, FilePath); + } + public bool DoesFileExist() { return File.Exists(FilePath); From 76ea63377ddd947b1e35daff081ed75b2946cc9d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 1 Nov 2025 18:00:52 +0100 Subject: [PATCH 543/878] Check that there is at most one root resource for each data set and that non-root resources have an applied conversion method --- backend/src/Data/ApplicationDbContext.cs | 41 +++++++++++++++-- backend/src/Data/GetHttpsResource.cs | 46 +++++++++++++++++-- backend/src/Database.csproj | 1 + .../CreateGetHttpsResourceMutation.cs | 19 ++++++-- 4 files changed, 97 insertions(+), 10 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 1d823019..6a27b752 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -182,6 +182,31 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigureGetHttpsResource( + EntityTypeBuilder builder + ) + { + // add partial indexes, see https://www.postgresql.org/docs/18/indexes-partial.html + builder + .HasIndex(_ => new { _.CalorimetricDataId, _.ParentId }) + .HasFilter($"{nameof(GetHttpsResource.CalorimetricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + builder + .HasIndex(_ => new { _.GeometricDataId, _.ParentId }) + .HasFilter($"{nameof(GetHttpsResource.GeometricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + builder + .HasIndex(_ => new { _.HygrothermalDataId, _.ParentId }) + .HasFilter($"{nameof(GetHttpsResource.HygrothermalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + builder + .HasIndex(_ => new { _.OpticalDataId, _.ParentId }) + .HasFilter($"{nameof(GetHttpsResource.OpticalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + builder + .HasIndex(_ => new { _.PhotovoltaicDataId, _.ParentId }) + .HasFilter($"{nameof(GetHttpsResource.PhotovoltaicDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + return builder; + } + private static EntityTypeBuilder ConfigureCalorimetricData( @@ -241,7 +266,7 @@ EntityTypeBuilder builder color => { color.HasCheckConstraint( - $"CK_OpticalData_CielabColors_{nameof(CielabColor.LStar)}", + $"CK_{nameof(OpticalData)}_{nameof(CielabColor)}s_{nameof(CielabColor.LStar)}", $"\"{nameof(CielabColor.LStar)}\" >= 0.0 AND \"{nameof(CielabColor.LStar)}\" <= 100.0" ); } @@ -278,9 +303,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) CreateEnumerations(modelBuilder, _schemaName); ConfigureIdentityEntities(modelBuilder); ConfigureEntity( - modelBuilder.Entity() + ConfigureGetHttpsResource(modelBuilder.Entity()) ) - .ToTable("get_https_resource"); + .ToTable( + "get_https_resource", + _ => + { + _.HasCheckConstraint( + $"CK_{nameof(GetHttpsResource)}_Root_Or_Child", + $"(\"{nameof(GetHttpsResource.ParentId)}\" IS NULL AND \"{nameof(GetHttpsResource.AppliedConversionMethod)}\" IS NULL)" + + $"OR (\"{nameof(GetHttpsResource.ParentId)}\" IS NOT NULL AND \"{nameof(GetHttpsResource.AppliedConversionMethod)}\" IS NOT NULL)" + ); + } + ); ConfigureCalorimetricData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 21156a95..a58544fd 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -9,15 +9,16 @@ using Database.Extensions; using Database.Utilities; using EntityFrameworkCore.Projectables; +using Microsoft.EntityFrameworkCore; namespace Database.Data; -// `DbContext` needs this constructor without owned entities. public sealed class GetHttpsResource : Entity { public const string FilesDirectoryPath = "./files/"; + // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties public GetHttpsResource( string? description, string hashValue, @@ -31,6 +32,9 @@ public GetHttpsResource( FileExtension = fileExtension; } + /// + /// Construct a root resource. + /// public GetHttpsResource( string? description, string hashValue, @@ -41,9 +45,42 @@ public GetHttpsResource( Guid? hygrothermalDataId, Guid? opticalDataId, Guid? photovoltaicDataId, - Guid? parentId, + ICollection archivedFilesMetaInformation + ) + : this( + description, + hashValue, + dataFormatId, + fileExtension + ) + { + CalorimetricDataId = calorimetricDataId; + HygrothermalDataId = hygrothermalDataId; + OpticalDataId = opticalDataId; + PhotovoltaicDataId = photovoltaicDataId; + GeometricDataId = geometricDataId; + ParentId = null; + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + AppliedConversionMethod = null; + AssertThatExactlyOneDataIdIsNonNull(); + } + + /// + /// Construct a child resource. + /// + public GetHttpsResource( + string? description, + string hashValue, + Guid dataFormatId, + string? fileExtension, + Guid? calorimetricDataId, + Guid? geometricDataId, + Guid? hygrothermalDataId, + Guid? opticalDataId, + Guid? photovoltaicDataId, + Guid parentId, ICollection archivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethod? appliedConversionMethod + ToTreeVertexAppliedConversionMethod appliedConversionMethod ) : this( description, @@ -100,9 +137,11 @@ private void AssertThatExactlyOneDataIdIsNonNull() // Note that at least one data ID is always present. So `Guid.Empty` will never be used. [NotMapped] + [Projectable] public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; [NotMapped] + [Projectable] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; [Projectable] @@ -156,7 +195,6 @@ private void AssertThatExactlyOneDataIdIsNonNull() public Guid? ParentId { get; private set; } - // TODO Require the conversion method to be given whenever there is a parent. In other words, either both are `null` or both are non-`null`. public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } // TODO The parent's `Data` must be the same as this resource's `Data`. diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 4c9d55a5..af98cab8 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -35,6 +35,7 @@ + diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 784c80be..4e15333d 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -31,7 +31,8 @@ public sealed record CreateGetHttpsResourceInput( { public GetHttpsResource ToDomainModel(string? fileExtension) { - return new GetHttpsResource( + return ParentId is null || AppliedConversionMethod is null + ? new( Description, Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. DataFormatId, @@ -41,9 +42,21 @@ public GetHttpsResource ToDomainModel(string? fileExtension) DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, DataKind == DataKind.OPTICAL_DATA ? DataId : null, DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, - ParentId, + ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList() + ) + : new( + Description, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + DataFormatId, + fileExtension, + DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, + DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, + DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, + DataKind == DataKind.OPTICAL_DATA ? DataId : null, + DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, + ParentId ?? Guid.Empty, ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), - AppliedConversionMethod?.ToDomainModel() + AppliedConversionMethod.ToDomainModel() ); } } From f0819bbf4d997a8c5d56e82286fdcb53f5b7c437 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 7 Nov 2025 17:22:08 +0100 Subject: [PATCH 544/878] Simplify partial index to make sure that root resource is unique --- backend/src/Data/ApplicationDbContext.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 6a27b752..97f366e6 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -190,19 +190,19 @@ EntityTypeBuilder builder { // add partial indexes, see https://www.postgresql.org/docs/18/indexes-partial.html builder - .HasIndex(_ => new { _.CalorimetricDataId, _.ParentId }) + .HasIndex(_ => new { _.CalorimetricDataId }) .HasFilter($"{nameof(GetHttpsResource.CalorimetricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); builder - .HasIndex(_ => new { _.GeometricDataId, _.ParentId }) + .HasIndex(_ => new { _.GeometricDataId }) .HasFilter($"{nameof(GetHttpsResource.GeometricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); builder - .HasIndex(_ => new { _.HygrothermalDataId, _.ParentId }) + .HasIndex(_ => new { _.HygrothermalDataId }) .HasFilter($"{nameof(GetHttpsResource.HygrothermalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); builder - .HasIndex(_ => new { _.OpticalDataId, _.ParentId }) + .HasIndex(_ => new { _.OpticalDataId }) .HasFilter($"{nameof(GetHttpsResource.OpticalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); builder - .HasIndex(_ => new { _.PhotovoltaicDataId, _.ParentId }) + .HasIndex(_ => new { _.PhotovoltaicDataId }) .HasFilter($"{nameof(GetHttpsResource.PhotovoltaicDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); return builder; } From d14bdd3740db5ebb2e8e176d07f3f63cf990e2a7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 7 Nov 2025 18:59:05 +0100 Subject: [PATCH 545/878] Add triggers to make sure that resources in the same tree belong to the same data set --- backend/src/Data/ApplicationDbContext.cs | 85 +++++++++++++++++++--- backend/src/Data/GetHttpsResource.cs | 18 ++++- backend/src/Database.csproj | 2 +- backend/src/Extensions/StringExtensions.cs | 5 ++ backend/src/Startup.cs | 4 +- 5 files changed, 102 insertions(+), 12 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 97f366e6..b3f6f4d0 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -5,8 +5,10 @@ using System.Threading; using System.Threading.Tasks; using Database.Enumerations; +using Database.Extensions; using Database.GraphQl.Extensions; using GreenDonut.Data; +using Laraue.EfCoreTriggers.Common.Extensions; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -191,19 +193,74 @@ EntityTypeBuilder builder // add partial indexes, see https://www.postgresql.org/docs/18/indexes-partial.html builder .HasIndex(_ => new { _.CalorimetricDataId }) - .HasFilter($"{nameof(GetHttpsResource.CalorimetricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + .HasFilter( + $""" + {nameof(GetHttpsResource.CalorimetricDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ); builder .HasIndex(_ => new { _.GeometricDataId }) - .HasFilter($"{nameof(GetHttpsResource.GeometricDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + .HasFilter( + $""" + {nameof(GetHttpsResource.GeometricDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ); builder .HasIndex(_ => new { _.HygrothermalDataId }) - .HasFilter($"{nameof(GetHttpsResource.HygrothermalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + .HasFilter( + $""" + {nameof(GetHttpsResource.HygrothermalDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ); builder .HasIndex(_ => new { _.OpticalDataId }) - .HasFilter($"{nameof(GetHttpsResource.OpticalDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + .HasFilter( + $""" + {nameof(GetHttpsResource.OpticalDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ); builder .HasIndex(_ => new { _.PhotovoltaicDataId }) - .HasFilter($"{nameof(GetHttpsResource.PhotovoltaicDataId)} IS NOT NULL AND {nameof(GetHttpsResource.ParentId)} IS NULL"); + .HasFilter( + $""" + {nameof(GetHttpsResource.PhotovoltaicDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ); + builder + .BeforeInsert(trigger => trigger + .SetTriggerName(GetHttpsResource.DataIdsMustMatchTriggerName) + .Action(action => action + // .Condition(_ => _.New.ParentId != null) + .ExecuteRawSql( + $""" + IF NEW.{nameof(GetHttpsResource.ParentId).Enquote()} IS NOT NULL + AND ( + SELECT COUNT({nameof(GetHttpsResource.Id).Enquote()}) + FROM "{GetHttpsResource.TableName}" + WHERE COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + """ + ) + ) + ); + builder + .BeforeUpdate(trigger => trigger + .SetTriggerName(GetHttpsResource.DataIdCannotChangeTriggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"OLD.{_.Enquote()}"))}) <> COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + """ + ) + ) + ); return builder; } @@ -267,7 +324,7 @@ EntityTypeBuilder builder { color.HasCheckConstraint( $"CK_{nameof(OpticalData)}_{nameof(CielabColor)}s_{nameof(CielabColor.LStar)}", - $"\"{nameof(CielabColor.LStar)}\" >= 0.0 AND \"{nameof(CielabColor.LStar)}\" <= 100.0" + $"{nameof(CielabColor.LStar)}.Enquote() >= 0.0 AND {nameof(CielabColor.LStar).Enquote()} <= 100.0" ); } ); @@ -306,14 +363,24 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureGetHttpsResource(modelBuilder.Entity()) ) .ToTable( - "get_https_resource", + GetHttpsResource.TableName, _ => { _.HasCheckConstraint( $"CK_{nameof(GetHttpsResource)}_Root_Or_Child", - $"(\"{nameof(GetHttpsResource.ParentId)}\" IS NULL AND \"{nameof(GetHttpsResource.AppliedConversionMethod)}\" IS NULL)" + - $"OR (\"{nameof(GetHttpsResource.ParentId)}\" IS NOT NULL AND \"{nameof(GetHttpsResource.AppliedConversionMethod)}\" IS NOT NULL)" + $""" + ({nameof(GetHttpsResource.ParentId).Enquote()} IS NULL AND "{nameof(GetHttpsResource.AppliedConversionMethod)}_{nameof(ToTreeVertexAppliedConversionMethod.MethodId)}" IS NULL) + OR ({nameof(GetHttpsResource.ParentId).Enquote()} IS NOT NULL AND "{nameof(GetHttpsResource.AppliedConversionMethod)}_{nameof(ToTreeVertexAppliedConversionMethod.MethodId)}" IS NOT NULL) + """ + ); + _.HasCheckConstraint( + $"CK_{nameof(GetHttpsResource)}_Exactly_One_Data_Set", + $"NUM_NONNULLS({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = 1" ); + foreach (var triggerName in GetHttpsResource.TriggerNames) + { + _.HasTrigger(triggerName); + } } ); ConfigureCalorimetricData( diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index a58544fd..bdeb6c6b 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.IO; using System.Linq; @@ -9,7 +10,6 @@ using Database.Extensions; using Database.Utilities; using EntityFrameworkCore.Projectables; -using Microsoft.EntityFrameworkCore; namespace Database.Data; @@ -17,6 +17,22 @@ public sealed class GetHttpsResource : Entity { public const string FilesDirectoryPath = "./files/"; + public const string TableName = "get_https_resource"; + + public const string DataIdsMustMatchTriggerName = "data_ids_must_match"; + public const string DataIdCannotChangeTriggerName = "data_id_cannot_change"; + public static readonly ImmutableArray TriggerNames = [ + DataIdsMustMatchTriggerName, + DataIdCannotChangeTriggerName + ]; + + public static readonly ImmutableArray DataIdFieldNames = [ + nameof(CalorimetricDataId), + nameof(GeometricDataId), + nameof(HygrothermalDataId), + nameof(OpticalDataId), + nameof(PhotovoltaicDataId) + ]; // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties public GetHttpsResource( diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index af98cab8..3cfde1f8 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -35,7 +35,7 @@ - + diff --git a/backend/src/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 5f5c07c4..435228db 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -18,4 +18,9 @@ public static string Base64Decode(this string base64EncodedData) Convert.FromBase64String(base64EncodedData) ); } + + public static string Enquote(this string str) + { + return "\"" + str + "\""; + } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index c3e4d9bb..60f4f224 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -26,6 +26,7 @@ using Database.Data.Extensions; using Database.Enumerations; using Database.Services; +using Laraue.EfCoreTriggers.PostgreSql.Extensions; namespace Database; @@ -188,7 +189,8 @@ AppSettings appSettings ) .UseSchemaName(appSettings.Database.SchemaName) .UseOpenIddict() - .UseProjectables(); + .UseProjectables() + .UsePostgreSqlTriggers(); if (!environment.IsProduction()) { options From 440df6729cf8249c6e8c53084271528100e847ae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 11 Nov 2025 19:14:28 +0100 Subject: [PATCH 546/878] Add `dotnet` CLI completion --- see https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash --- backend/Dockerfile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/backend/Dockerfile b/backend/Dockerfile index ac7fa180..919c600d 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -341,6 +341,22 @@ RUN \ --store-name Root \ --password ${CERTIFICATE_AUTHORITY_PASSWORD} +# bash parameter completion for the dotnet CLI +# see https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash +# for the here-document syntax used to append to the ~/.bashrc file +# see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 +RUN \ + cat <<-"EOT" >> ${HOME}/.bashrc + function _dotnet_bash_complete() + { + local cur="${COMP_WORDS[COMP_CWORD]}" IFS=$'\n' + local candidates + read -d '' -ra candidates < <(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2>/dev/null) + read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]:-}" -- "$cur") + } + complete -f -F _dotnet_bash_complete dotnet +EOT + # COPY \ # --chown=me:us \ # ./package.json ./package-lock.json ./ From de2922fa3a287edc976159f9cb3200eb4f61ad5b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 11 Nov 2025 19:23:13 +0100 Subject: [PATCH 547/878] Use absolute path to designate app to run --- docker-compose.production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 7df75373..1588acc8 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -57,7 +57,7 @@ services: dockerfile: ./Dockerfile-production # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] - command: ["dotnet", "App.dll"] + command: ["dotnet", "/app/App.dll"] healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] From acd6fe08eb0e6677b5084a6961abf1d81480490f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 17:04:21 +0100 Subject: [PATCH 548/878] Use `isAuthorizedTo` instead of `canCurrentUser` because the authorization may not involve a user but only an OpenId Connect client --- backend/src/ApiRequests/Queries/Database.graphql | 4 ++-- backend/src/ApiRequests/Queries/UpdateDatabase.graphql | 4 ++-- backend/src/ApiRequests/QueryDatabase.cs | 4 ++-- .../GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap | 4 ++-- frontend/components/databases/Database.tsx | 4 ++-- frontend/queries/databases.graphql | 4 ++-- frontend/type-defs.graphqls | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/backend/src/ApiRequests/Queries/Database.graphql b/backend/src/ApiRequests/Queries/Database.graphql index b8b375a8..5ba53c63 100644 --- a/backend/src/ApiRequests/Queries/Database.graphql +++ b/backend/src/ApiRequests/Queries/Database.graphql @@ -13,7 +13,7 @@ query Database( uuid } } - canCurrentUserUpdateNode - canCurrentUserVerifyNode + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode } } \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql index 7d1d4fd2..05c7eba1 100644 --- a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql +++ b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql @@ -14,8 +14,8 @@ mutation UpdateDatabase( uuid } } - canCurrentUserUpdateNode - canCurrentUserVerifyNode + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode } errors { code diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index e0d0aad0..e5b79177 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -21,8 +21,8 @@ public sealed record Database( DatabaseVerificationState VerificationState, string VerificationCode, DatabaseOperatorEdge Operator, - bool CanCurrentUserUpdateNode, - bool CanCurrentUserVerifyNode + bool IsAuthorizedToUpdateNode, + bool IsAuthorizedToVerifyNode ); public enum DatabaseVerificationState diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 398ccd18..bb7cf785 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -274,8 +274,8 @@ type DataEdge { } type Database { - canCurrentUserUpdateNode: Boolean! - canCurrentUserVerifyNode: Boolean! + isAuthorizedToUpdateNode: Boolean! + isAuthorizedToVerifyNode: Boolean! description: String! locator: Url! name: String! diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index 1d72ba15..803c9eb4 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -38,7 +38,7 @@ export default function Database({}: DatabaseProps) { title={database.name} subTitle={database.description} extra={([] as ReactNode[]).concat( - database.canCurrentUserUpdateNode + database.isAuthorizedToUpdateNode ? [ - {database.canCurrentUserVerifyNode && + {database.isAuthorizedToVerifyNode && database.verificationState == DatabaseVerificationState.Pending && ( Have your database's GraphQL endpoint return the verification diff --git a/frontend/queries/databases.graphql b/frontend/queries/databases.graphql index 11edeb30..08c3b655 100644 --- a/frontend/queries/databases.graphql +++ b/frontend/queries/databases.graphql @@ -11,8 +11,8 @@ fragment DatabasePartial on Database { # } verificationState verificationCode - canCurrentUserUpdateNode - canCurrentUserVerifyNode + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode } query Database { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 398ccd18..bb7cf785 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -274,8 +274,8 @@ type DataEdge { } type Database { - canCurrentUserUpdateNode: Boolean! - canCurrentUserVerifyNode: Boolean! + isAuthorizedToUpdateNode: Boolean! + isAuthorizedToVerifyNode: Boolean! description: String! locator: Url! name: String! From 132b755b18b7b2cf8a94ea66ee38676fae0d7af0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 19:32:48 +0100 Subject: [PATCH 549/878] Support machine-to-machine authentication and authorization, that is, authentication that does not involve a human user but only an OpenId Connect application client --- ...aphql => CurrentUserOrApplication.graphql} | 33 +++++++++- ...er.cs => QueryCurrentUserOrApplication.cs} | 58 ++++++++++++++--- .../src/Authorization/CommonAuthorization.cs | 35 ++++++----- .../src/Controllers/FileUploadController.cs | 10 +-- backend/src/Data/CalorimetricData.cs | 4 +- backend/src/Data/DataX.cs | 6 +- backend/src/Data/GeometricData.cs | 4 +- backend/src/Data/HygrothermalData.cs | 4 +- backend/src/Data/IData.cs | 2 +- backend/src/Data/OpticalData.cs | 4 +- backend/src/Data/PhotovoltaicData.cs | 4 +- .../AddInstitutionAccessRightsMutation.cs | 2 +- .../UpdateDataAccessRightsMutation.cs | 2 +- .../UpdateInstitutionAccessRightsMutation.cs | 2 +- .../CreateCalorimetricDataMutation.cs | 6 +- .../DataApprovals/AddDataApprovalMutation.cs | 2 +- .../RemoveDataApprovalMutation.cs | 2 +- backend/src/GraphQl/DataX/DataQueriesBase.cs | 2 +- .../CreateGeometricDataMutation.cs | 6 +- .../CreateGetHttpsResourceMutation.cs | 2 +- ...mputeGetHttpsResourceHashValuesMutation.cs | 2 +- .../CreateHygrothermalDataMutation.cs | 6 +- backend/src/GraphQl/MutationBase.cs | 21 ++++--- .../OpticalDataX/CreateOpticalDataMutation.cs | 6 +- .../CreatePhotovoltaicDataMutation.cs | 6 +- .../CreateResponseApprovalsMutation.cs | 2 +- .../UpdateResponseApprovalsMutation.cs | 2 +- backend/src/Services/AccessRightsService.cs | 59 +++++++++++------- backend/src/Services/CacheService.cs | 13 ++-- backend/src/Services/UserService.cs | 44 +++++++++---- .../GraphQlSchemaTests.IsUnchanged.snap | 62 +++++++++---------- frontend/type-defs.graphqls | 62 +++++++++---------- 32 files changed, 287 insertions(+), 188 deletions(-) rename backend/src/ApiRequests/Queries/{CurrentUser.graphql => CurrentUserOrApplication.graphql} (69%) rename backend/src/ApiRequests/{QueryCurrentUser.cs => QueryCurrentUserOrApplication.cs} (58%) diff --git a/backend/src/ApiRequests/Queries/CurrentUser.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql similarity index 69% rename from backend/src/ApiRequests/Queries/CurrentUser.graphql rename to backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql index d58ac0b9..73baee45 100644 --- a/backend/src/ApiRequests/Queries/CurrentUser.graphql +++ b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql @@ -1,4 +1,4 @@ -query CurrentUser( +query CurrentUserOrApplication( $databaseId: Uuid! ) { currentUser { @@ -73,4 +73,35 @@ query CurrentUser( totalCount } } + currentApplication: currentOpenIdConnectApplication { + uuid + clientId + owner { + node { + uuid + name + databaseOperatingDatabases: operatedDatabases( + where: { + id: { equalTo: $databaseId } + } + ) { + totalCount + } + databaseOperatingManagedInstitutions: managedInstitutions( + where: { + operatedDatabases: { + some: { + id: { + equalTo: $databaseId + } + } + } + } + ) + { + totalCount + } + } + } + } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUser.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs similarity index 58% rename from backend/src/ApiRequests/QueryCurrentUser.cs rename to backend/src/ApiRequests/QueryCurrentUserOrApplication.cs index 9735cce9..6cbede94 100644 --- a/backend/src/ApiRequests/QueryCurrentUser.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -10,9 +10,9 @@ namespace Database.ApiRequests; /// /// Class to request user info from Metabase API. /// -public sealed class QueryCurrentUser +public sealed class QueryCurrentUserOrApplication { - private const string QueryFileName = "CurrentUser.graphql"; + private const string QueryFileName = "CurrentUserOrApplication.graphql"; public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; @@ -22,7 +22,13 @@ public sealed record CurrentUser( string Name, UserRepresentedInstitutionConnection RepresentedInstitutions, UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions - ); + ) + { + public bool IsAtLeastAssistantManagerOfDatabaseOperator() + { + return DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; + } + }; public sealed record UserRepresentedInstitutionConnection( IReadOnlyList Edges, @@ -60,18 +66,53 @@ public sealed record InstitutionManagedInstitutionNode( string Name ); - private sealed record CurrentUserData(CurrentUser? CurrentUser); + public sealed record CurrentOpenIdConnectApplication( + Guid Uuid, + string ClientId, + OpenIdConnectApplicationOwnerEdge Owner + ) + { + public bool IsOwnedByDatabaseOperator() + { + return Owner.Node.DatabaseOperatingDatabases.TotalCount >= 1 + || Owner.Node.DatabaseOperatingManagedInstitutions.TotalCount >= 1; + } + }; + + public sealed record OpenIdConnectApplicationOwnerEdge( + OpenIdConnectApplicationOwnerNode Node + ); + + public sealed record OpenIdConnectApplicationOwnerNode( + Guid Uuid, + string Name, + DatabaseOperatingDatabaseConnection DatabaseOperatingDatabases, + DatabaseOperatingManagedInstitutionConnection DatabaseOperatingManagedInstitutions + ); + + public sealed record DatabaseOperatingDatabaseConnection( + uint TotalCount + ); + + public sealed record DatabaseOperatingManagedInstitutionConnection( + uint TotalCount + ); + + public sealed record CurrentUserOrApplication( + CurrentUser? CurrentUser, + CurrentOpenIdConnectApplication? CurrentApplication + ); /// /// Request current user from Metabase. /// - public static async Task Do( + public static async Task Do( AppSettings appSettings, ApiRequestService apiRequestService, CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( + return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( await apiRequestService.ConstructGraphQlQuery(QueryFileName), @@ -79,11 +120,10 @@ await apiRequestService.ConstructGraphQlQuery(QueryFileName), { databaseId = appSettings.DatabaseId }, - "CurrentUser" + "CurrentUserOrApplication" ), cancellationToken )) - .Data - .CurrentUser; + .Data; } } \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 60adacd2..36915d8f 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -3,7 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Services; -using static Database.ApiRequests.QueryCurrentUser; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Authorization; @@ -11,26 +11,33 @@ public sealed class CommonAuthorization( UserService userService ) { - public Task GetCurrentUserAsync(CancellationToken cancellationToken) + public Task GetCurrentUserOrApplicationAsync(CancellationToken cancellationToken) { - return userService.GetCurrentUser(cancellationToken); + return userService.GetCurrentUserOrApplicationAsync(cancellationToken); } - public async Task IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(CancellationToken cancellationToken) + public Task IsAuthenticated(CancellationToken cancellationToken) { - var currentUser = await GetCurrentUserAsync(cancellationToken); - if (currentUser is null) - { - return false; - } - return IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser); + return userService.UserOrApplicationAsync( + user => Task.FromResult(user is not null), + application => Task.FromResult(application is not null), + cancellationToken + ); } - public bool IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator( - CurrentUser currentUser - ) + public Task IsDatabaseOperator(CancellationToken cancellationToken) { - return currentUser.DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; + return userService.UserOrApplicationAsync( + user => Task.FromResult( + user is not null + && user.IsAtLeastAssistantManagerOfDatabaseOperator() + ), + application => Task.FromResult( + application is not null + && application.IsOwnedByDatabaseOperator() + ), + cancellationToken + ); } public bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 2b863c6f..be7228ce 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -91,18 +91,10 @@ public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, [FromServices] ApplicationDbContext context, [FromServices] CommonAuthorization authorization, - [FromServices] UserService userService, CancellationToken cancellationToken ) { - var currentUser = await userService.GetCurrentUser(cancellationToken); - if (currentUser is null) - { - ModelState.AddModelError("CurrentUser", - $"User is not authenticated."); - return BadRequest(ModelState); - } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!await authorization.IsDatabaseOperator(cancellationToken)) { return Unauthorized(); } diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 266aee39..c372934d 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -8,7 +8,7 @@ public sealed class CalorimetricData : DataX { public CalorimetricData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -37,7 +37,7 @@ double[] uValues // `DbContext` needs this constructor without owned entities. public CalorimetricData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 39c92ce8..67145a5e 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -6,7 +6,7 @@ namespace Database.Data; public abstract class DataX( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -17,7 +17,7 @@ DateTime createdAt ) : Entity, IData { protected DataX( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -70,7 +70,7 @@ public void Retract() PublishingState = PublishingState.RETRACTED; } - public Guid UserId { get; private set; } = userId; + public Guid? UserId { get; private set; } = userId; public string Locale { get; private set; } = locale; public Guid ComponentId { get; private set; } = componentId; public string? Name { get; private set; } = name; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index bce9b60e..750d0608 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -8,7 +8,7 @@ public sealed class GeometricData : DataX { public GeometricData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -34,7 +34,7 @@ double[] thicknesses } public GeometricData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 64ee49a7..c30ae539 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -8,7 +8,7 @@ public sealed class HygrothermalData : DataX { public HygrothermalData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -33,7 +33,7 @@ AppliedMethod appliedMethod // `DbContext` needs this constructor without owned entities. public HygrothermalData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 8d9b7442..802105d4 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -14,7 +14,7 @@ namespace Database.Data; [JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] public interface IData : IEntity { - Guid UserId { get; } + Guid? UserId { get; } Guid ComponentId { get; } string? Name { get; } string? Description { get; } diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index cba18f28..11762b9e 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -14,7 +14,7 @@ public sealed class OpticalData public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); public OpticalData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -59,7 +59,7 @@ ICollection cielabColors // `DbContext` needs this constructor without owned entities. public OpticalData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 07cc5561..0d159caa 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -8,7 +8,7 @@ public sealed class PhotovoltaicData : DataX { public PhotovoltaicData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, @@ -33,7 +33,7 @@ AppliedMethod appliedMethod // `DbContext` needs this constructor without owned entities. public PhotovoltaicData( - Guid userId, + Guid? userId, string locale, Guid componentId, string? name, diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs index d23f5621..7246620d 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs @@ -72,7 +72,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index a1ac1eef..a0f8f94f 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -71,7 +71,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs index 5702fc81..5fc0f6b6 100644 --- a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs @@ -72,7 +72,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index cc3fa0da..3fbebca0 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -30,7 +30,7 @@ public sealed record CreateCalorimetricDataInput( double[] UValues ) : IValidateCreateInput { - public CalorimetricData ToDomainModel(Guid userId) + public CalorimetricData ToDomainModel(Guid? userId) { return new( userId, @@ -104,7 +104,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -125,7 +125,7 @@ CancellationToken cancellationToken return validateErrorPayload; } - var calorimetricData = input.ToDomainModel(currentUser.Uuid); + var calorimetricData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); context.CalorimetricData.Add(calorimetricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index f276a946..5ad1ed16 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -116,7 +116,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs index f8a924c2..8a66999d 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -74,7 +74,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index e7935031..11f2f351 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -50,7 +50,7 @@ protected async Task> GetAllPendingDataAsync( CancellationToken cancellationToken ) { - if (!await authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(cancellationToken)) + if (!await authorization.IsDatabaseOperator(cancellationToken)) { return Enumerable.Empty().AsQueryable(); } diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 55519401..13f6ca0c 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -28,7 +28,7 @@ public sealed record CreateGeometricDataInput( double[] Thicknesses ) : IValidateCreateInput { - public GeometricData ToDomainModel(Guid userId) + public GeometricData ToDomainModel(Guid? userId) { return new( userId, @@ -102,7 +102,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -123,7 +123,7 @@ CancellationToken cancellationToken return validateErrorPayload; } - var geometricData = input.ToDomainModel(currentUser.Uuid); + var geometricData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 4e15333d..51b9d7d0 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -129,7 +129,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 8b875634..66c61545 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -83,7 +83,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 8b044dcc..4283d3c1 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -28,7 +28,7 @@ public sealed record CreateHygrothermalDataInput( AppliedMethodInput AppliedMethod ) : IValidateCreateInput { - public HygrothermalData ToDomainModel(Guid userId) + public HygrothermalData ToDomainModel(Guid? userId) { return new( userId, @@ -100,7 +100,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -121,7 +121,7 @@ CancellationToken cancellationToken return validateErrorPayload; } - var hygrothermalData = input.ToDomainModel(currentUser.Uuid); + var hygrothermalData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); context.HygrothermalData.Add(hygrothermalData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs index 3bf8037b..6500b287 100644 --- a/backend/src/GraphQl/MutationBase.cs +++ b/backend/src/GraphQl/MutationBase.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Authorization; -using static Database.ApiRequests.QueryCurrentUser; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.GraphQl; @@ -13,38 +13,39 @@ public abstract class MutationBase protected abstract TPayload NewPayload(TData? data, IReadOnlyCollection? errors); protected abstract TError NewError(TErrorCode errorCode, string message, IReadOnlyList path); - protected async Task> AuthorizeAsync( + protected async Task> AuthorizeAsync( TErrorCode unauthenticatedErrorCode, TErrorCode unauthorizedErrorCode, CommonAuthorization authorization, CancellationToken cancellationToken ) { - var currentUser = await authorization.GetCurrentUserAsync(cancellationToken); - if (currentUser is null) + if (!await authorization.IsAuthenticated(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthenticatedErrorCode, - "The user is not authenticated.", + "You are not authenticated.", [] ) ]) ); } - if (!authorization.IsCurrentUserAtLeastAssistantManagerOfDatabaseOperator(currentUser)) + if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthorizedErrorCode, - "The current user is not authorized to perform this mutation in this database.", + "You are not authorized to perform this mutation in this database.", [] ) ]) ); } - return new Result.Data(currentUser); + return new Result.Data( + await authorization.GetCurrentUserOrApplicationAsync(cancellationToken) + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 25ca43c0..aef0a22f 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -38,7 +38,7 @@ public sealed record CreateOpticalDataInput( IReadOnlyList CielabColors ) : IValidateCreateInput { - public OpticalData ToDomainModel(Guid userId) + public OpticalData ToDomainModel(Guid? userId) { return new( userId, @@ -120,7 +120,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -141,7 +141,7 @@ CancellationToken cancellationToken return validateErrorPayload; } - var opticalData = input.ToDomainModel(currentUser.Uuid); + var opticalData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); context.OpticalData.Add(opticalData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index ee618786..16d62710 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -28,7 +28,7 @@ public sealed record CreatePhotovoltaicDataInput( AppliedMethodInput AppliedMethod ) : IValidateCreateInput { - public PhotovoltaicData ToDomainModel(Guid userId) + public PhotovoltaicData ToDomainModel(Guid? userId) { return new( userId, @@ -100,7 +100,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -121,7 +121,7 @@ CancellationToken cancellationToken return validateErrorPayload; } - var photovoltaicData = input.ToDomainModel(currentUser.Uuid); + var photovoltaicData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); context.PhotovoltaicData.Add(photovoltaicData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index c16e427b..ea4e2505 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -82,7 +82,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index e5cd1c09..e92d609d 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -81,7 +81,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUser, out var authorizeErrorPayload) + ).Failed(out var _, out var authorizeErrorPayload) ) { return authorizeErrorPayload; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 4237ab91..0f17bfb0 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -6,7 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Database.Data; -using static Database.ApiRequests.QueryCurrentUser; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Services; @@ -44,25 +44,28 @@ CancellationToken cancellationToken where T : IData { ApplicationDbContext? context = null; - IReadOnlyList? representedInstitutionIds = null; + uint? alreadyAccessedByUserCount = null; + IReadOnlyList? institutionIds = null; IReadOnlyList? institutionAccessRights = null; - uint? alreadyAccessedCount = null; - var currentUser = await userService.GetCurrentUser(cancellationToken); + var currentUserOrApplication = await userService.GetCurrentUserOrApplicationAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectClientId(); - if (currentUser is not null) + if (currentUserOrApplication.CurrentUser is not null) { context = dbContextFactory.CreateDbContext(); - representedInstitutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); - institutionAccessRights = (await context.InstitutionAccessRights.AsQueryable() - .Where(x => representedInstitutionIds.Contains(x.InstitutionId)) - .ToListAsync(cancellationToken)) - .AsReadOnly(); - alreadyAccessedCount = cacheService.GetAccessCountForUser(currentUser.Uuid); + alreadyAccessedByUserCount = cacheService.GetAccessCountForUser(currentUserOrApplication.CurrentUser.Uuid); + institutionIds = currentUserOrApplication.CurrentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); + institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); + } + else if (currentUserOrApplication.CurrentApplication is not null) + { + context = dbContextFactory.CreateDbContext(); + institutionIds = [currentUserOrApplication.CurrentApplication.Owner.Node.Uuid]; + institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); } - var result = ProcessData(data, currentUser, openIdConnectClientId, representedInstitutionIds, institutionAccessRights, alreadyAccessedCount, cacheService); + var result = ProcessData(data, currentUserOrApplication.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount, cacheService); if (context is not null) { @@ -72,13 +75,27 @@ CancellationToken cancellationToken return result.AsQueryable(); } + private static async Task> GetInstitutionAccessRightsAsync( + IReadOnlyList institutionIds, + ApplicationDbContext context, + CancellationToken cancellationToken + ) + { + return ( + await context.InstitutionAccessRights.AsQueryable() + .Where(x => institutionIds.Contains(x.InstitutionId)) + .ToListAsync(cancellationToken) + ) + .AsReadOnly(); + } + private IEnumerable ProcessData( IQueryable data, CurrentUser? currentUser, string? openIdConnectClientId, - IReadOnlyList? representedInstitutionIds, + IReadOnlyList? institutionIds, IReadOnlyList? institutionAccessRights, - uint? alreadyAccessedCount, + uint? alreadyAccessedByUserCount, CacheService cacheService ) where T : IData @@ -99,8 +116,8 @@ openIdConnectClientId is null } if (dataItem.DataAccessRights.HasRestrictionsByInstitution && ( - representedInstitutionIds is null - || dataItem.IsRestrictedByInstitutions(representedInstitutionIds) + institutionIds is null + || dataItem.IsRestrictedByInstitutions(institutionIds) ) ) { @@ -110,18 +127,18 @@ representedInstitutionIds is null if (dataItem.DataAccessRights.HasRestrictionsByUser && ( currentUser is null - || alreadyAccessedCount is null - || dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedCount ?? 0) + || alreadyAccessedByUserCount is null + || dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedByUserCount ?? 0) ) ) { logger.DataRestriction(dataItem.Id, "Allowed accesses for user reached."); continue; } - if (currentUser is not null && alreadyAccessedCount is not null) + if (currentUser is not null && alreadyAccessedByUserCount is not null) { - alreadyAccessedCount = alreadyAccessedCount + 1; - cacheService.SetAccessCountForUser(currentUser.Uuid, alreadyAccessedCount ?? 0); + alreadyAccessedByUserCount = alreadyAccessedByUserCount + 1; + cacheService.SetAccessCountForUser(currentUser.Uuid, alreadyAccessedByUserCount ?? 0); } var reason = ""; if (institutionAccessRights is not null diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 3be29760..d58f1a0a 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,24 +1,25 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Caching.Memory; -using static Database.ApiRequests.QueryCurrentUser; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Services; public sealed class CacheService( - IMemoryCache currentUserCache, + IMemoryCache currentUserOrApplicationCache, IMemoryCache accessCountCache, IMemoryCache timePeriodCountCache) { - public CurrentUser? SetUser(string token, CurrentUser? cacheUser) + public CurrentUserOrApplication? SetCurrentUserOrApplication(string token, CurrentUserOrApplication cachedUserOrApplication) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - return currentUserCache.Set(token, cacheUser, cacheEntryOptions); + return currentUserOrApplicationCache.Set(token, cachedUserOrApplication, cacheEntryOptions); } - public bool TryGetUser(string token, out CurrentUser? cacheUser) + public bool TryGetCurrentUserOrApplication(string token, [NotNullWhen(true)] out CurrentUserOrApplication? cachedUserOrApplication) { - return currentUserCache.TryGetValue(token, out cacheUser); + return currentUserOrApplicationCache.TryGetValue(token, out cachedUserOrApplication); } public uint GetAccessCountForUser(Guid userId) diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index c7e2eaf2..aa54094e 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -7,7 +7,8 @@ using static OpenIddict.Abstractions.OpenIddictConstants; using Database.ApiRequests; using Database.Extensions; -using static Database.ApiRequests.QueryCurrentUser; +using static Database.ApiRequests.QueryCurrentUserOrApplication; +using System; namespace Database.Services; @@ -32,47 +33,64 @@ public sealed class UserService( ApiRequestService apiRequestService, IHttpContextAccessor httpContextAccessor, CacheService cacheService, - ILogger logger) + ILogger logger +) { /// - /// Get application from user calims. + /// Get client ID from user claims. /// - /// ClientId from claims as applicationId. public string? GetOpenIdConnectClientId() { return httpContextAccessor.HttpContext?.User.GetClaim(Claims.AuthorizedParty); } + public async Task UserOrApplicationAsync( + Func> authorizeUser, + Func> authorizeApplication, + CancellationToken cancellationToken + ) + { + var userOrApplication = await GetCurrentUserOrApplicationAsync(cancellationToken); + if (userOrApplication.CurrentApplication is not null) + { + return await authorizeApplication(userOrApplication.CurrentApplication); + } + else + { + return await authorizeUser(userOrApplication.CurrentUser); + } + } + /// - /// Get current user from Metabase (or the local cache). + /// Get current user or OpenId Connect application from Metabase (or the local cache). /// - /// - /// - public async Task GetCurrentUser(CancellationToken cancellationToken) + public async Task GetCurrentUserOrApplicationAsync( + CancellationToken cancellationToken + ) { // Extract token var token = await httpContextAccessor.ExtractBearerToken(); logger.ExtractedToken(token); if (token is null) { - return await QueryCurrentUser.Do( + return await QueryCurrentUserOrApplication.Do( appSettings, apiRequestService, cancellationToken ); } // Check if there is already a user for token - if (!cacheService.TryGetUser(token, out var cacheUser)) + if (!cacheService.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) { // Get user from Metabase - cacheUser = await QueryCurrentUser.Do( + cachedUserOrApplication = await QueryCurrentUserOrApplication.Do( appSettings, apiRequestService, cancellationToken ); // Store user in cache - cacheService.SetUser(token, cacheUser); + cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); } - return cacheUser; + return cachedUserOrApplication; } } \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index bb7cf785..b8714ca9 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -28,11 +28,10 @@ interface Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! warnings: [String!]! } @@ -114,11 +113,10 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -274,9 +272,9 @@ type DataEdge { } type Database { + description: String! isAuthorizedToUpdateNode: Boolean! isAuthorizedToVerifyNode: Boolean! - description: String! locator: Url! name: String! operator: DatabaseOperatorEdge! @@ -321,12 +319,11 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -415,11 +412,10 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -531,13 +527,12 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! type: OpticalComponentType - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -588,11 +583,10 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -996,13 +990,16 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] - timestamp: DateTimePropositionInput - signature: StringPropositionInput - keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput - message: StringPropositionInput - approverId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput } input CrossDatabaseDataReferenceInput { @@ -1681,13 +1678,16 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] - timestamp: DateTimePropositionInput - signature: StringPropositionInput - keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput - message: StringPropositionInput - approverId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput } input UriHostNameTypePropositionInput { @@ -1849,6 +1849,7 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_DATA_FORMAT DUPLICATE_ROOT_RESOURCE CREATING_RESPONSE_APPROVAL_FAILED + PARENT_WITHOUT_APPLIED_CONVERSION_METHOD } enum CreateHygrothermalDataErrorCode { @@ -1906,6 +1907,7 @@ enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + NOT_PENDING } enum Illuminant { @@ -1964,12 +1966,6 @@ enum PublishDataErrorCode { RESOURCES_WITHOUT_FILE } -enum PublishingState { - PENDING - PUBLISHED - RETRACTED -} - enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index bb7cf785..b8714ca9 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -28,11 +28,10 @@ interface Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! warnings: [String!]! } @@ -114,11 +113,10 @@ type CalorimetricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") uValues: [Float!]! warnings: [String!]! @@ -274,9 +272,9 @@ type DataEdge { } type Database { + description: String! isAuthorizedToUpdateNode: Boolean! isAuthorizedToVerifyNode: Boolean! - description: String! locator: Url! name: String! operator: DatabaseOperatorEdge! @@ -321,12 +319,11 @@ type GeometricData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -415,11 +412,10 @@ type HygrothermalData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -531,13 +527,12 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! type: OpticalComponentType - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -588,11 +583,10 @@ type PhotovoltaicData implements Node & Data { isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - publishingState: PublishingState! resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! - userId: Uuid! + userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! } @@ -996,13 +990,16 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] - timestamp: DateTimePropositionInput - signature: StringPropositionInput - keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput - message: StringPropositionInput - approverId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput } input CrossDatabaseDataReferenceInput { @@ -1681,13 +1678,16 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] - timestamp: DateTimePropositionInput - signature: StringPropositionInput - keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput - message: StringPropositionInput - approverId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: DateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput } input UriHostNameTypePropositionInput { @@ -1849,6 +1849,7 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_DATA_FORMAT DUPLICATE_ROOT_RESOURCE CREATING_RESPONSE_APPROVAL_FAILED + PARENT_WITHOUT_APPLIED_CONVERSION_METHOD } enum CreateHygrothermalDataErrorCode { @@ -1906,6 +1907,7 @@ enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + NOT_PENDING } enum Illuminant { @@ -1964,12 +1966,6 @@ enum PublishDataErrorCode { RESOURCES_WITHOUT_FILE } -enum PublishingState { - PENDING - PUBLISHED - RETRACTED -} - enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED From 8ac575f61010dfc200e5f11618c2ef40cf111328 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 19:35:07 +0100 Subject: [PATCH 550/878] Remove misleading (and unnecessary) prefix "Get" before "Has" --- .../src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs | 4 ++-- backend/src/GraphQl/DataX/DataQueriesBase.cs | 2 +- backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs | 4 ++-- .../src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs | 4 ++-- backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs | 4 ++-- .../src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 4bd1a0bd..e4ddda3b 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -71,14 +71,14 @@ CancellationToken cancellationToken } [UseFiltering] - public Task GetHasCalorimetricDataAsync( + public Task HasCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return GetHasDataAsync( + return HasDataAsync( context.CalorimetricData, locale, resolverContext, diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 11f2f351..96006485 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -67,7 +67,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - protected Task GetHasDataAsync( + protected Task HasDataAsync( DbSet data, [GraphQLType] string? locale, IResolverContext resolverContext, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 71b69c0f..8f82808c 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -67,14 +67,14 @@ CancellationToken cancellationToken } [UseFiltering] - public Task GetHasGeometricDataAsync( + public Task HasGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return GetHasDataAsync( + return HasDataAsync( context.GeometricData, locale, resolverContext, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 048c1520..afa98581 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -69,14 +69,14 @@ CancellationToken cancellationToken } [UseFiltering] - public Task GetHasHygrothermalDataAsync( + public Task HasHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return GetHasDataAsync( + return HasDataAsync( context.HygrothermalData, locale, resolverContext, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 25e8a8a0..d2a49032 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -69,14 +69,14 @@ CancellationToken cancellationToken } [UseFiltering] - public Task GetHasOpticalDataAsync( + public Task HasOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return GetHasDataAsync( + return HasDataAsync( context.OpticalData, locale, resolverContext, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 5051806f..e5028377 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -71,14 +71,14 @@ CancellationToken cancellationToken } [UseFiltering] - public Task GetHasPhotovoltaicDataAsync( + public Task HasPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return GetHasDataAsync( + return HasDataAsync( context.PhotovoltaicData, locale, resolverContext, From 83ab5d760e1324221165253ee1509d9cc7559928 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 19:42:10 +0100 Subject: [PATCH 551/878] Extract mirrored values only from root resources (by default) --- backend/src/Controllers/FileUploadController.cs | 2 +- backend/src/Data/GetHttpsResource.cs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index be7228ce..2da6c903 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -180,7 +180,7 @@ CancellationToken cancellationToken // read the headers for the next section. section = await reader.ReadNextSectionAsync(cancellationToken); } - if (getHttpsResource.OpticalData is not null) + if (getHttpsResource.IsRoot() && getHttpsResource.OpticalData is not null) { await getHttpsResource.OpticalData.ExtractAndSetMirroredValuesFromFile( getHttpsResource.FilePath, diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index bdeb6c6b..d412bd51 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -241,6 +241,16 @@ public async Task RecomputeHashValue(CancellationToken cancellationToken) HashValue = await Sha256FileHasher.ComputeForFile(FilePath, cancellationToken); } + public bool IsRoot() + { + return ParentId is null; + } + + public bool IsChild() + { + return ParentId is not null; + } + public static string ConstructVertexId(Guid id) { return id.ToString("D").Base64Encode(); From e0c882f7d23a6bef0b0efcf5670a6854caab9100 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 20:17:55 +0100 Subject: [PATCH 552/878] Allow authorized users to query pending resources --- backend/src/Data/GetHttpsResource.cs | 2 -- .../GetHttpsResourceQueries.cs | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index d412bd51..3f3d3b88 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -153,11 +153,9 @@ private void AssertThatExactlyOneDataIdIsNonNull() // Note that at least one data ID is always present. So `Guid.Empty` will never be used. [NotMapped] - [Projectable] public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; [NotMapped] - [Projectable] public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; [Projectable] diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 62036a3b..3d5c048c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -9,6 +9,7 @@ using Database.Data; using Database.GraphQl.Extensions; using Database.Enumerations; +using Database.Authorization; namespace Database.GraphQl.GetHttpsResources; @@ -33,6 +34,30 @@ ISortingContext sorting .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING); } + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public async Task> GetPendingGetHttpsResources( + ApplicationDbContext context, + ISortingContext sorting, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return Enumerable.Empty().AsQueryable(); + } + sorting.StabilizeOrder(); + return context.GetHttpsResources.AsNoTracking() + .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState == PublishingState.PENDING) + .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState == PublishingState.PENDING) + .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState == PublishingState.PENDING) + .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState == PublishingState.PENDING) + .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState == PublishingState.PENDING); + } + public Task GetGetHttpsResourceAsync( Guid id, GetHttpsResourceByIdDataLoader byId, From 4c81663375563e654b88e8f88c15012da7d6bff1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 12 Nov 2025 20:22:29 +0100 Subject: [PATCH 553/878] Forbid retraction of data set for non-published data set (propose delete instead) --- backend/src/GraphQl/DataX/RetractDataMutation.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs index 12792e9c..0ea18077 100644 --- a/backend/src/GraphQl/DataX/RetractDataMutation.cs +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate; using HotChocolate.Types; using Database.Authorization; using Database.Data; @@ -24,7 +23,8 @@ public enum RetractDataErrorCode UNAUTHENTICATED, UNAUTHORIZED, UNKNOWN_DATA, - CREATING_RESPONSE_APPROVAL_FAILED + CREATING_RESPONSE_APPROVAL_FAILED, + NOT_PUBLISHED } public sealed record RetractDataError( @@ -85,6 +85,18 @@ CancellationToken cancellationToken return fetchDataErrorPayload; } + if (data.PublishingState is not PublishingState.PUBLISHED) + { + return NewPayload( + null, + [NewError( + RetractDataErrorCode.NOT_PUBLISHED, + $"The publishing state is not published but {data.PublishingState}. If it is pending, you may delete the data set instead.", + [] + )] + ); + } + var rememberedPublishingState = data.PublishingState; data.Retract(); await context.SaveChangesAsync(cancellationToken); From ec7dcdcedcdc7b7e1e013379732d8cf46017e62a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 17:24:08 +0100 Subject: [PATCH 554/878] Provide resources with included data sets in database context --- backend/src/Controllers/FileUploadController.cs | 9 ++------- backend/src/Data/ApplicationDbContext.cs | 9 ++++++++- .../RecomputeGetHttpsResourceHashValuesMutation.cs | 7 +------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 2da6c903..0035b1da 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -106,13 +106,8 @@ CancellationToken cancellationToken ); return BadRequest(ModelState); } - var getHttpsResource = await context.GetHttpsResources.AsQueryable() - .Include(e => e.CalorimetricData) - .Include(e => e.GeometricData) - .Include(e => e.HygrothermalData) - .Include(e => e.OpticalData) - .Include(e => e.PhotovoltaicData) - .Where(e => e.Id == getHttpsResourceUuid) + var getHttpsResource = await context.GetHttpsResourcesWithData.AsQueryable() + .Where(_ => _.Id == getHttpsResourceUuid) .SingleOrDefaultAsync(cancellationToken); if (getHttpsResource is null) { diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b3f6f4d0..90249f66 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -51,12 +51,19 @@ DbContextOptions options public DbSet DataProtectionKeys { get; private set; } = default!; public DbSet InstitutionAccessRights { get; private set; } = default!; + public IQueryable GetHttpsResourcesWithData => + GetHttpsResources.AsQueryable() + .Include(_ => _.CalorimetricData) + .Include(_ => _.GeometricData) + .Include(_ => _.HygrothermalData) + .Include(_ => _.OpticalData) + .Include(_ => _.PhotovoltaicData); public DbSet CalorimetricData { get; private set; } = default!; public DbSet GeometricData { get; private set; } = default!; public DbSet HygrothermalData { get; private set; } = default!; - public DbSet PhotovoltaicData { get; private set; } = default!; public DbSet OpticalData { get; private set; } = default!; + public DbSet PhotovoltaicData { get; private set; } = default!; public IQueryable Data(DataKind dataKind) { diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 66c61545..c96b9f0d 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -90,12 +90,7 @@ CancellationToken cancellationToken } var resources = - await context.GetHttpsResources - .Include(r => r.CalorimetricData) - .Include(r => r.GeometricData) - .Include(r => r.HygrothermalData) - .Include(r => r.OpticalData) - .Include(r => r.PhotovoltaicData) + await context.GetHttpsResourcesWithData .With(queryContext, sort => sort.StabilizeOrder()) .ToListAsync(cancellationToken); var errors = new ConcurrentBag(); From d6ae9020a1408ca2665b9495b71cdbd126ab7b8d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 17:24:22 +0100 Subject: [PATCH 555/878] Add mutation to delete resources --- .../DeleteGetHttpsResourceMutation.cs | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs new file mode 100644 index 00000000..7c9d29d5 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -0,0 +1,158 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Authorization; +using Database.Enumerations; +using HotChocolate.Types; +using Database.Services; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed record DeleteGetHttpsResourceInput( + Guid GetHttpsResourceId +); + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteGetHttpsResourceErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_RESOURCE, + ROOT_RESOURCE, + NOT_PENDING_DATA, + CREATING_RESPONSE_APPROVAL_FAILED, + HAS_CHILDREN +} + +public sealed record DeleteGetHttpsResourceError( + DeleteGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteGetHttpsResourcePayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteGetHttpsResourceMutation +: DataMutationBase +{ + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override DeleteGetHttpsResourcePayload NewPayload( + GetHttpsResource? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteGetHttpsResourceError NewError( + DeleteGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteGetHttpsResourceAsync( + DeleteGetHttpsResourceInput input, + ApplicationDbContext context, + ResponseApprovalService responseApprovalService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteGetHttpsResourceErrorCode.UNAUTHENTICATED, + DeleteGetHttpsResourceErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var resource = await context.GetHttpsResourcesWithData + .Where(_ => _.Id == input.GetHttpsResourceId) + .SingleOrDefaultAsync(cancellationToken); + if (resource is null) + { + return NewPayload( + null, + [NewError( + DeleteGetHttpsResourceErrorCode.UNKNOWN_RESOURCE, + "There is no resource with the given ID.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + )] + ); + } + + var errors = new List(); + if (resource.IsRoot()) + { + errors.Add( + NewError( + DeleteGetHttpsResourceErrorCode.ROOT_RESOURCE, + $"This is the root resource of the data set with the ID {resource.DataId}. You cannot delete it.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + ) + ); + } + if (resource.Data is not null && resource.Data.PublishingState is not PublishingState.PENDING) + { + errors.Add( + NewError( + DeleteGetHttpsResourceErrorCode.NOT_PENDING_DATA, + $"The data set of the resource is not pending but {resource.Data.PublishingState}. You cannot delete the data set's resources. However, you may retract the data set.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + ) + ); + } + if (await context.Entry(resource) + .Collection(_ => _.Children) + .Query() + .AnyAsync(cancellationToken) + ) + { + errors.Add( + NewError( + DeleteGetHttpsResourceErrorCode.HAS_CHILDREN, + $"The resource has children. Delete those first.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + ) + ); + } + if (errors.Count >= 1) + { + return NewPayload(null, errors); + } + + context.Remove(resource); + await context.SaveChangesAsync(cancellationToken); + + if (resource.Data is not null && (await CreateResponseApprovalAsync( + resource.Data, + DeleteGetHttpsResourceErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var deleteResponseApprovalErrorPayload) + ) + { + context.GetHttpsResources.Add(resource); + await context.SaveChangesAsync(cancellationToken); + return deleteResponseApprovalErrorPayload; + } + + return NewPayload(resource, null); + } +} \ No newline at end of file From 5a7bd7e84ba4d0ae6a93a5d9c08fa3436ee45800 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 17:30:28 +0100 Subject: [PATCH 556/878] Move mutation specific filter types into the mutation file --- ...omputeGetHttpsResourceHashValuesFilterType.cs | 16 ---------------- ...ecomputeGetHttpsResourceHashValuesMutation.cs | 13 +++++++++++++ .../CreateResponseApprovalsFilterType.cs | 16 ---------------- .../CreateResponseApprovalsMutation.cs | 13 +++++++++++++ .../UpdateResponseApprovalsFilterType.cs | 16 ---------------- .../UpdateResponseApprovalsMutation.cs | 13 +++++++++++++ 6 files changed, 39 insertions(+), 48 deletions(-) delete mode 100644 backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs delete mode 100644 backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs deleted file mode 100644 index f3d4c6dc..00000000 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesFilterType.cs +++ /dev/null @@ -1,16 +0,0 @@ -using HotChocolate.Data.Filters; -using Database.Data; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed class RecomputeGetHttpsResourceHashValuesFilterType - : GetHttpsResourceFilterType -{ - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Name(nameof(RecomputeGetHttpsResourceHashValuesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index c96b9f0d..1cd07c27 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using Database.Extensions; using HotChocolate.Resolvers; +using HotChocolate.Data.Filters; namespace Database.GraphQl.GetHttpsResources; @@ -41,6 +42,18 @@ public sealed record RecomputeGetHttpsResourceHashValuesPayload( IReadOnlyCollection? Errors ) : Payload; +public sealed class RecomputeGetHttpsResourceHashValuesFilterType + : GetHttpsResourceFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(RecomputeGetHttpsResourceHashValuesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} + public static partial class RecomputeGetHttpsResourceHashValuesMutationLogging { [LoggerMessage( diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs deleted file mode 100644 index 0b131968..00000000 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsFilterType.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Database.Data; -using HotChocolate.Data.Filters; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed class CreateResponseApprovalsFilterType -: ResponseApprovalFilterType -{ - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Name(nameof(CreateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index ea4e2505..baa9bd5e 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -15,6 +15,7 @@ using Database.Extensions; using Database.GraphQl.Extensions; using HotChocolate.Resolvers; +using HotChocolate.Data.Filters; namespace Database.GraphQl.ResponseApprovals; @@ -49,6 +50,18 @@ public sealed record CreateResponseApprovalsPayload( IReadOnlyCollection? Errors ) : Payload; +public sealed class CreateResponseApprovalsFilterType +: ResponseApprovalFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(CreateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} + [ExtendObjectType(nameof(Mutation))] public sealed class CreateResponseApprovalsMutation : DataMutationBase, CreateResponseApprovalsPayload, CreateResponseApprovalsError, CreateResponseApprovalsErrorCode> diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs deleted file mode 100644 index 855deddc..00000000 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsFilterType.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Database.Data; -using HotChocolate.Data.Filters; - -namespace Database.GraphQl.ResponseApprovals; - -public sealed class UpdateResponseApprovalsFilterType -: ResponseApprovalFilterType -{ - protected override void Configure( - IFilterInputTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - descriptor.Name(nameof(UpdateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index e92d609d..c777bbb3 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -15,6 +15,7 @@ using Database.Extensions; using Database.GraphQl.Extensions; using HotChocolate.Resolvers; +using HotChocolate.Data.Filters; namespace Database.GraphQl.ResponseApprovals; @@ -49,6 +50,18 @@ public sealed record UpdateResponseApprovalsPayload( IReadOnlyCollection? Errors ) : Payload; +public sealed class UpdateResponseApprovalsFilterType +: ResponseApprovalFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(UpdateResponseApprovalsFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} + [ExtendObjectType(nameof(Mutation))] public sealed class UpdateResponseApprovalsMutation : DataMutationBase, UpdateResponseApprovalsPayload, UpdateResponseApprovalsError, UpdateResponseApprovalsErrorCode> From 7baff92fb3009c639d8d5f6beb9015fa2283573b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 18:19:45 +0100 Subject: [PATCH 557/878] Correct consistency checks for resources --- backend/src/Data/ApplicationDbContext.cs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 90249f66..daf28fff 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -191,7 +191,7 @@ EntityTypeBuilder builder return builder; } - private static + private EntityTypeBuilder ConfigureGetHttpsResource( EntityTypeBuilder builder @@ -204,35 +204,40 @@ EntityTypeBuilder builder $""" {nameof(GetHttpsResource.CalorimetricDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL """ - ); + ) + .IsUnique(true); builder .HasIndex(_ => new { _.GeometricDataId }) .HasFilter( $""" {nameof(GetHttpsResource.GeometricDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL """ - ); + ) + .IsUnique(true); builder .HasIndex(_ => new { _.HygrothermalDataId }) .HasFilter( $""" {nameof(GetHttpsResource.HygrothermalDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL """ - ); + ) + .IsUnique(true); builder .HasIndex(_ => new { _.OpticalDataId }) .HasFilter( $""" {nameof(GetHttpsResource.OpticalDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL """ - ); + ) + .IsUnique(true); builder .HasIndex(_ => new { _.PhotovoltaicDataId }) .HasFilter( $""" {nameof(GetHttpsResource.PhotovoltaicDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL """ - ); + ) + .IsUnique(true); builder .BeforeInsert(trigger => trigger .SetTriggerName(GetHttpsResource.DataIdsMustMatchTriggerName) @@ -243,8 +248,8 @@ EntityTypeBuilder builder IF NEW.{nameof(GetHttpsResource.ParentId).Enquote()} IS NOT NULL AND ( SELECT COUNT({nameof(GetHttpsResource.Id).Enquote()}) - FROM "{GetHttpsResource.TableName}" - WHERE COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) + FROM {_schemaName}.{GetHttpsResource.TableName.Enquote()} + WHERE {nameof(GetHttpsResource.Id).Enquote()} = NEW.{nameof(GetHttpsResource.ParentId).Enquote()} AND COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) ) <> 1 THEN @@ -331,7 +336,7 @@ EntityTypeBuilder builder { color.HasCheckConstraint( $"CK_{nameof(OpticalData)}_{nameof(CielabColor)}s_{nameof(CielabColor.LStar)}", - $"{nameof(CielabColor.LStar)}.Enquote() >= 0.0 AND {nameof(CielabColor.LStar).Enquote()} <= 100.0" + $"{nameof(CielabColor.LStar).Enquote()} >= 0.0 AND {nameof(CielabColor.LStar).Enquote()} <= 100.0" ); } ); From 15c8771a16fcd04480c0879d9dcd6ae5d8d17642 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 18:32:59 +0100 Subject: [PATCH 558/878] Add migration for data consistency of GET HTTPS resources on the PostgreSQL level --- ...1_CheckResourcesForConsistency.Designer.cs | 2698 +++++++++++++++++ ...1113172901_CheckResourcesForConsistency.cs | 457 +++ .../ApplicationDbContextModelSnapshot.cs | 72 +- backend/src/Migrations/migrate.sql | 257 ++ ...113172901_CheckResourcesForConsistency.sql | 257 ++ ...51031125736_AddFileExtensionToResource.sql | 206 ++ 6 files changed, 3929 insertions(+), 18 deletions(-) create mode 100644 backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs create mode 100644 backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs create mode 100644 backend/src/Migrations/migrate_from_20251031125736_AddFileExtensionToResource_to_20251113172901_CheckResourcesForConsistency.sql create mode 100644 backend/src/Migrations/rollback_from_20251113172901_CheckResourcesForConsistency_to_20251031125736_AddFileExtensionToResource.sql diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs new file mode 100644 index 00000000..c4006733 --- /dev/null +++ b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs @@ -0,0 +1,2698 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251113172901_CheckResourcesForConsistency")] + partial class CheckResourcesForConsistency + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs new file mode 100644 index 00000000..0da87b49 --- /dev/null +++ b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs @@ -0,0 +1,457 @@ +using System; +using System.Text.Json; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class CheckResourcesForConsistency : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_optical_data_OpticalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_CalorimetricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_HygrothermalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_OpticalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + defaultValueSql: "'{}'", + oldClrType: typeof(JsonElement), + oldType: "jsonb"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + defaultValueSql: "'{}'", + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: true, + oldClrType: typeof(Guid), + oldType: "uuid"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_CalorimetricDataId", + schema: "database", + table: "get_https_resource", + column: "CalorimetricDataId", + unique: true, + filter: "\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId", + unique: true, + filter: "\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_HygrothermalDataId", + schema: "database", + table: "get_https_resource", + column: "HygrothermalDataId", + unique: true, + filter: "\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_OpticalDataId", + schema: "database", + table: "get_https_resource", + column: "OpticalDataId", + unique: true, + filter: "\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource", + column: "PhotovoltaicDataId", + unique: true, + filter: "\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource", + sql: "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GetHttpsResource_Root_Or_Child", + schema: "database", + table: "get_https_resource", + sql: "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", + schema: "database", + table: "get_https_resource", + column: "CalorimetricDataId", + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId", + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", + schema: "database", + table: "get_https_resource", + column: "HygrothermalDataId", + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_optical_data_OpticalDataId", + schema: "database", + table: "get_https_resource", + column: "OpticalDataId", + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource", + column: "PhotovoltaicDataId", + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() CASCADE;"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_optical_data_OpticalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_CalorimetricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_HygrothermalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_OpticalDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropCheckConstraint( + name: "CK_GetHttpsResource_Root_Or_Child", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Variables", + schema: "database", + table: "optical_data_Approvals", + type: "jsonb", + nullable: false, + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldDefaultValueSql: "'{}'"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Approval_Variables", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true, + oldClrType: typeof(JsonElement), + oldType: "jsonb", + oldNullable: true, + oldDefaultValueSql: "'{}'"); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "UserId", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "uuid", + oldNullable: true); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_CalorimetricDataId", + schema: "database", + table: "get_https_resource", + column: "CalorimetricDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_HygrothermalDataId", + schema: "database", + table: "get_https_resource", + column: "HygrothermalDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_OpticalDataId", + schema: "database", + table: "get_https_resource", + column: "OpticalDataId"); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource", + column: "PhotovoltaicDataId"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_calorimetric_data_CalorimetricDataId", + schema: "database", + table: "get_https_resource", + column: "CalorimetricDataId", + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_geometric_data_GeometricDataId", + schema: "database", + table: "get_https_resource", + column: "GeometricDataId", + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_hygrothermal_data_HygrothermalDataId", + schema: "database", + table: "get_https_resource", + column: "HygrothermalDataId", + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_optical_data_OpticalDataId", + schema: "database", + table: "get_https_resource", + column: "OpticalDataId", + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId", + schema: "database", + table: "get_https_resource", + column: "PhotovoltaicDataId", + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 257a132c..2d8ecc07 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -72,7 +72,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uuid"); b.Property("Version") @@ -123,7 +123,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uuid"); b.Property("Version") @@ -187,19 +187,46 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); - b.HasIndex("CalorimetricDataId"); + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); - b.HasIndex("GeometricDataId"); + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); - b.HasIndex("HygrothermalDataId"); + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); - b.HasIndex("OpticalDataId"); + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); b.HasIndex("ParentId"); - b.HasIndex("PhotovoltaicDataId"); + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); - b.ToTable("get_https_resource", "database"); + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => @@ -231,7 +258,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PublishingState") .HasColumnType("database.publishing_state"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uuid"); b.Property("Version") @@ -348,7 +375,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Type") .HasColumnType("database.optical_component_type"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uuid"); b.Property("Version") @@ -395,7 +422,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PublishingState") .HasColumnType("database.publishing_state"); - b.Property("UserId") + b.Property("UserId") .HasColumnType("uuid"); b.Property("Version") @@ -1378,19 +1405,23 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") .WithMany("Resources") - .HasForeignKey("CalorimetricDataId"); + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Database.Data.GeometricData", "GeometricData") .WithMany("Resources") - .HasForeignKey("GeometricDataId"); + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") .WithMany("Resources") - .HasForeignKey("HygrothermalDataId"); + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Database.Data.OpticalData", "OpticalData") .WithMany("Resources") - .HasForeignKey("OpticalDataId"); + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); b.HasOne("Database.Data.GetHttpsResource", "Parent") .WithMany("Children") @@ -1398,7 +1429,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") .WithMany("Resources") - .HasForeignKey("PhotovoltaicDataId"); + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => { @@ -2012,7 +2044,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone"); b1.Property("Variables") - .HasColumnType("jsonb"); + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); b1.HasKey("OpticalDataId"); @@ -2056,7 +2090,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamp with time zone"); b1.Property("Variables") - .HasColumnType("jsonb"); + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); b1.HasKey("OpticalDataId", "Id"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index b939cb1b..1bf231b9 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1545,3 +1545,260 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_CalorimetricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_GeometricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_HygrothermalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_OpticalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_PhotovoltaicDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.optical_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_CalorimetricDataId" ON database.get_https_resource ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_GeometricDataId" ON database.get_https_resource ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_HygrothermalDataId" ON database.get_https_resource ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_OpticalDataId" ON database.get_https_resource ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_PhotovoltaicDataId" ON database.get_https_resource ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "OpticalDataId", "PhotovoltaicDataId") = 1); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Root_Or_Child" CHECK (("ParentId" IS NULL AND "AppliedConversionMethod_MethodId" IS NULL) + OR ("ParentId" IS NOT NULL AND "AppliedConversionMethod_MethodId" IS NOT NULL)); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251113172901_CheckResourcesForConsistency', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251031125736_AddFileExtensionToResource_to_20251113172901_CheckResourcesForConsistency.sql b/backend/src/Migrations/migrate_from_20251031125736_AddFileExtensionToResource_to_20251113172901_CheckResourcesForConsistency.sql new file mode 100644 index 00000000..730c3a14 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251031125736_AddFileExtensionToResource_to_20251113172901_CheckResourcesForConsistency.sql @@ -0,0 +1,257 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_CalorimetricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_GeometricDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_HygrothermalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_OpticalDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_PhotovoltaicDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.optical_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" SET DEFAULT ('{}'); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "UserId" DROP NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_CalorimetricDataId" ON database.get_https_resource ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_GeometricDataId" ON database.get_https_resource ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_HygrothermalDataId" ON database.get_https_resource ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_OpticalDataId" ON database.get_https_resource ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_PhotovoltaicDataId" ON database.get_https_resource ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "OpticalDataId", "PhotovoltaicDataId") = 1); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Root_Or_Child" CHECK (("ParentId" IS NULL AND "AppliedConversionMethod_MethodId" IS NULL) + OR ("ParentId" IS NOT NULL AND "AppliedConversionMethod_MethodId" IS NOT NULL)); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251113172901_CheckResourcesForConsistency', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251113172901_CheckResourcesForConsistency_to_20251031125736_AddFileExtensionToResource.sql b/backend/src/Migrations/rollback_from_20251113172901_CheckResourcesForConsistency_to_20251031125736_AddFileExtensionToResource.sql new file mode 100644 index 00000000..2bb139be --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251113172901_CheckResourcesForConsistency_to_20251031125736_AddFileExtensionToResource.sql @@ -0,0 +1,206 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_CalorimetricDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_GeometricDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_HygrothermalDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_OpticalDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DROP INDEX database."IX_get_https_resource_PhotovoltaicDataId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "CK_GetHttpsResource_Root_Or_Child"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + UPDATE database.photovoltaic_data SET "UserId" = '00000000-0000-0000-0000-000000000000' WHERE "UserId" IS NULL; + ALTER TABLE database.photovoltaic_data ALTER COLUMN "UserId" SET NOT NULL; + ALTER TABLE database.photovoltaic_data ALTER COLUMN "UserId" SET DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database."optical_data_Approvals" ALTER COLUMN "Variables" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + UPDATE database.optical_data SET "UserId" = '00000000-0000-0000-0000-000000000000' WHERE "UserId" IS NULL; + ALTER TABLE database.optical_data ALTER COLUMN "UserId" SET NOT NULL; + ALTER TABLE database.optical_data ALTER COLUMN "UserId" SET DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Approval_Variables" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + UPDATE database.hygrothermal_data SET "UserId" = '00000000-0000-0000-0000-000000000000' WHERE "UserId" IS NULL; + ALTER TABLE database.hygrothermal_data ALTER COLUMN "UserId" SET NOT NULL; + ALTER TABLE database.hygrothermal_data ALTER COLUMN "UserId" SET DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + UPDATE database.geometric_data SET "UserId" = '00000000-0000-0000-0000-000000000000' WHERE "UserId" IS NULL; + ALTER TABLE database.geometric_data ALTER COLUMN "UserId" SET NOT NULL; + ALTER TABLE database.geometric_data ALTER COLUMN "UserId" SET DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + UPDATE database.calorimetric_data SET "UserId" = '00000000-0000-0000-0000-000000000000' WHERE "UserId" IS NULL; + ALTER TABLE database.calorimetric_data ALTER COLUMN "UserId" SET NOT NULL; + ALTER TABLE database.calorimetric_data ALTER COLUMN "UserId" SET DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE INDEX "IX_get_https_resource_CalorimetricDataId" ON database.get_https_resource ("CalorimetricDataId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE INDEX "IX_get_https_resource_GeometricDataId" ON database.get_https_resource ("GeometricDataId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE INDEX "IX_get_https_resource_HygrothermalDataId" ON database.get_https_resource ("HygrothermalDataId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE INDEX "IX_get_https_resource_OpticalDataId" ON database.get_https_resource ("OpticalDataId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + CREATE INDEX "IX_get_https_resource_PhotovoltaicDataId" ON database.get_https_resource ("PhotovoltaicDataId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251113172901_CheckResourcesForConsistency'; + END IF; +END $EF$; +COMMIT; + From 8895f1e43df0f3e58d5cb46c24fa057734bc4ed3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 13 Nov 2025 20:02:36 +0100 Subject: [PATCH 559/878] Create root resurce together with data set and check existence of method and data format IDs --- backend/src/ApiRequests/MethodDataLoader.cs | 41 +++++++++++ .../src/ApiRequests/Queries/Methods.graphql | 9 +++ backend/src/Data/GetHttpsResource.cs | 12 +--- .../CreateCalorimetricDataMutation.cs | 25 +++++-- .../GraphQl/DataX/CreateDataMutationBase.cs | 72 ++++++++++++++++++- .../CreateGeometricDataMutation.cs | 25 +++++-- .../CreateGetHttpsResourceMutation.cs | 67 ++--------------- .../CreateHygrothermalDataMutation.cs | 29 ++++++-- .../OpticalDataX/CreateOpticalDataMutation.cs | 25 +++++-- .../CreatePhotovoltaicDataMutation.cs | 27 +++++-- .../src/GraphQl/RootGetHttpsResourceInput.cs | 25 +++++++ .../GraphQlSchemaTests.IsUnchanged.snap | 60 ++++++++++++++-- frontend/type-defs.graphqls | 60 ++++++++++++++-- 13 files changed, 369 insertions(+), 108 deletions(-) create mode 100644 backend/src/ApiRequests/MethodDataLoader.cs create mode 100644 backend/src/ApiRequests/Queries/Methods.graphql create mode 100644 backend/src/GraphQl/RootGetHttpsResourceInput.cs diff --git a/backend/src/ApiRequests/MethodDataLoader.cs b/backend/src/ApiRequests/MethodDataLoader.cs new file mode 100644 index 00000000..32af4d39 --- /dev/null +++ b/backend/src/ApiRequests/MethodDataLoader.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; + +namespace Database.ApiRequests; + +public static class MethodDataLoader +{ + private const string QueryFileName = "Methods.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Method( + Guid Uuid + ) : IIdNode; + + private sealed record MethodsData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetMethodByIdAsync( + IReadOnlyList methodIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + methodIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Methods.graphql b/backend/src/ApiRequests/Queries/Methods.graphql new file mode 100644 index 00000000..1808c325 --- /dev/null +++ b/backend/src/ApiRequests/Queries/Methods.graphql @@ -0,0 +1,9 @@ +query Methods($ids: [Uuid!]!) { + connection: methods(where: { id: { in: $ids } }) { + edges { + node { + uuid + } + } + } +} \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 3f3d3b88..2ad860cf 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -56,11 +56,6 @@ public GetHttpsResource( string hashValue, Guid dataFormatId, string? fileExtension, - Guid? calorimetricDataId, - Guid? geometricDataId, - Guid? hygrothermalDataId, - Guid? opticalDataId, - Guid? photovoltaicDataId, ICollection archivedFilesMetaInformation ) : this( @@ -70,15 +65,10 @@ ICollection archivedFilesMetaInformation fileExtension ) { - CalorimetricDataId = calorimetricDataId; - HygrothermalDataId = hygrothermalDataId; - OpticalDataId = opticalDataId; - PhotovoltaicDataId = photovoltaicDataId; - GeometricDataId = geometricDataId; ParentId = null; ArchivedFilesMetaInformation = archivedFilesMetaInformation; AppliedConversionMethod = null; - AssertThatExactlyOneDataIdIsNonNull(); + // The data ID is set by EF Core. } /// diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 3fbebca0..b8b89f8f 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -26,13 +26,17 @@ public sealed record CreateCalorimetricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, double[] GValues, double[] UValues ) : IValidateCreateInput { - public CalorimetricData ToDomainModel(Guid? userId) + public CalorimetricData ToDomainModel( + Guid? userId, + string? fileExtension + ) { - return new( + var data = new CalorimetricData( userId, Locale, ComponentId, @@ -45,6 +49,8 @@ public CalorimetricData ToDomainModel(Guid? userId) GValues, UValues ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; } }; @@ -57,6 +63,8 @@ public enum CreateCalorimetricDataErrorCode UNKNOWN_COMPONENT, ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -94,6 +102,8 @@ public async Task CreateCalorimetricDataAsync( CommonAuthorization authorization, IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -117,15 +127,22 @@ CancellationToken cancellationToken CreateCalorimetricDataErrorCode.UNKNOWN_COMPONENT, institutionByIdDataLoader, CreateCalorimetricDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataFormatByIdDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken ) - ).Failed(out var validateErrorPayload) + ).Failed(out var dataFormat, out var validateErrorPayload) ) { return validateErrorPayload; } - var calorimetricData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); + var calorimetricData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); context.CalorimetricData.Add(calorimetricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 237dd20e..f681ae34 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -4,6 +4,10 @@ using System.Collections.Generic; using Database.ApiRequests; using GraphQL.Client.Abstractions.Utilities; +using Database.Extensions; +using System.Globalization; +using static Database.ApiRequests.DataFormatDataLoader; +using System.Linq; namespace Database.GraphQl.DataX; @@ -13,20 +17,26 @@ public interface IValidateCreateInput DateTime CreatedAt { get; } Guid CreatorId { get; } AppliedMethodInput AppliedMethod { get; } + RootGetHttpsResourceInput RootResource { get; } } public abstract class CreateDataMutationBase : DataMutationBase where TData : class where TPayload : class +where TError : class { - protected async Task> ValidateAsync( + protected async Task> ValidateAsync( IValidateCreateInput input, TErrorCode illegalCreatedAtErrorCode, IComponentByIdDataLoader componentByIdDataLoader, TErrorCode unknownComponentErrorCode, IInstitutionByIdDataLoader institutionByIdDataLoader, TErrorCode unknownCreatorErrorCode, + IMethodByIdDataLoader methodByIdDataLoader, + TErrorCode unknownAppliedMethodErrorCode, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + TErrorCode unknownDataFormatErrorCode, CancellationToken cancellationToken ) { @@ -61,13 +71,69 @@ CancellationToken cancellationToken ) ); } + if (await methodByIdDataLoader.LoadAsync(input.AppliedMethod.MethodId, cancellationToken) is null) + { + errors.Add( + NewError( + unknownAppliedMethodErrorCode, + "The applied method does not exist", + [nameof(input), nameof(input.AppliedMethod).ToLowerFirst(), nameof(input.AppliedMethod.MethodId).ToLowerFirst()] + ) + ); + } // TODO check applied method sources + var validateDataFormats = new Task>[1 + input.RootResource.ArchivedFilesMetaInformation.Count]; + validateDataFormats[0] = Task.Run>(async () => + { + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.RootResource.DataFormatId, cancellationToken); + if (dataFormat is null) + { + return new Result.Error( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [nameof(input), nameof(input.RootResource).ToLowerFirst(), nameof(input.RootResource.DataFormatId).ToLowerFirst()] + ) + ); + } + return new Result.Data(dataFormat); + }, + cancellationToken + ); + foreach (var (archivedFileMetaInformation, index) in input.RootResource.ArchivedFilesMetaInformation.WithIndex()) + { + validateDataFormats[index + 1] = Task.Run>(async () => + { + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); + if (dataFormat is null) + { + // TODO Is the index in the path formated correctly or should it be enclosed in square brackets? + return new Result.Error( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [nameof(input), nameof(input.RootResource).ToLowerFirst(), nameof(input.RootResource.ArchivedFilesMetaInformation).ToLowerFirst(), index.ToString(CultureInfo.InvariantCulture), nameof(archivedFileMetaInformation.DataFormatId).ToLowerFirst()] + ) + ); + } + return new Result.Data(dataFormat); + }, + cancellationToken + ); + } + var maybeDataFormats = await Task.WhenAll(validateDataFormats); + errors.AddRange( + maybeDataFormats + .Select(_ => _.Failed(out var error) ? error : null) + .NotNull() + ); if (errors.Count >= 1) { - return new Result.Error( + return new Result.Error( NewPayload(null, errors) ); } - return new Result.Data(true); + maybeDataFormats[0].Succeeded(out var dataFormat); + return new Result.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 13f6ca0c..2f7cdd25 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -25,12 +25,16 @@ public sealed record CreateGeometricDataInput( DateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, double[] Thicknesses ) : IValidateCreateInput { - public GeometricData ToDomainModel(Guid? userId) + public GeometricData ToDomainModel( + Guid? userId, + string? fileExtension + ) { - return new( + var data = new GeometricData( userId, Locale, ComponentId, @@ -42,6 +46,8 @@ public GeometricData ToDomainModel(Guid? userId) AppliedMethod.ToDomainModel(), Thicknesses ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; } }; @@ -54,6 +60,8 @@ public enum CreateGeometricDataErrorCode UNKNOWN_COMPONENT, ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -92,6 +100,8 @@ public async Task CreateGeometricDataAsync( CommonAuthorization authorization, IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -115,15 +125,22 @@ CancellationToken cancellationToken CreateGeometricDataErrorCode.UNKNOWN_COMPONENT, institutionByIdDataLoader, CreateGeometricDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataFormatByIdDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken ) - ).Failed(out var validateErrorPayload) + ).Failed(out var dataFormat, out var validateErrorPayload) ) { return validateErrorPayload; } - var geometricData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); + var geometricData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); context.GeometricData.Add(geometricData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 51b9d7d0..3483a042 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -24,15 +24,14 @@ public sealed record CreateGetHttpsResourceInput( Guid DataFormatId, Guid DataId, DataKind DataKind, - Guid? ParentId, + Guid ParentId, IReadOnlyList ArchivedFilesMetaInformation, - ToTreeVertexAppliedConversionMethodInput? AppliedConversionMethod + ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod ) : IIdentifyDataInput { public GetHttpsResource ToDomainModel(string? fileExtension) { - return ParentId is null || AppliedConversionMethod is null - ? new( + return new( Description, Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. DataFormatId, @@ -42,19 +41,7 @@ public GetHttpsResource ToDomainModel(string? fileExtension) DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, DataKind == DataKind.OPTICAL_DATA ? DataId : null, DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, - ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList() - ) - : new( - Description, - Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. - DataFormatId, - fileExtension, - DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, - DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, - DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, - DataKind == DataKind.OPTICAL_DATA ? DataId : null, - DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, - ParentId ?? Guid.Empty, + ParentId, ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), AppliedConversionMethod.ToDomainModel() ); @@ -70,11 +57,8 @@ public enum CreateGetHttpsResourceErrorCode UNKNOWN_DATA, UNKNOWN_PARENT, ILLEGAL_PARENT, - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, UNKNOWN_DATA_FORMAT, - DUPLICATE_ROOT_RESOURCE, - CREATING_RESPONSE_APPROVAL_FAILED, - PARENT_WITHOUT_APPLIED_CONVERSION_METHOD + CREATING_RESPONSE_APPROVAL_FAILED } public sealed record CreateGetHttpsResourceError( @@ -160,14 +144,12 @@ CancellationToken cancellationToken ); } var parent = - input.ParentId is null - ? null - : await context.GetHttpsResources + await context.GetHttpsResources .SingleOrDefaultAsync(_ => _.Id == input.ParentId, cancellationToken ); - if (parent is null && input.ParentId is not null) + if (parent is null) { errors.Add( NewError( @@ -187,41 +169,6 @@ input.ParentId is null ) ); } - if (input.AppliedConversionMethod is not null && input.ParentId is null) - { - errors.Add( - NewError( - CreateGetHttpsResourceErrorCode.APPLIED_CONVERSION_METHOD_WITHOUT_PARENT, - "The resource does not have a parent yet an applied conversion method was given. This method is supposed to have been applied to this resource's parent.", - [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst()] - ) - ); - } - if (input.ParentId is not null && input.AppliedConversionMethod is null) - { - errors.Add( - NewError( - CreateGetHttpsResourceErrorCode.PARENT_WITHOUT_APPLIED_CONVERSION_METHOD, - "The resource has a parent yet no applied conversion method was given. How did this resource come about from its parent?", - [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst()] - ) - ); - } - if (input.ParentId is null && - await context.GetHttpsResources.AsNoTracking() - .Where(_ => _.ParentId == null) - .Where(_ => _.GetDataId(input.DataKind) == input.DataId) - .AnyAsync(cancellationToken) - ) - { - errors.Add( - NewError( - CreateGetHttpsResourceErrorCode.DUPLICATE_ROOT_RESOURCE, - "The data set does already have a root resource.", - [nameof(input), nameof(input.ParentId).ToLowerFirst()] - ) - ); - } if (errors.Count >= 1) { return NewPayload(null, errors); diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 4283d3c1..f2fa8942 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -1,12 +1,10 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Services; -using Database.Utilities; using HotChocolate; using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; @@ -25,12 +23,16 @@ public sealed record CreateHygrothermalDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - AppliedMethodInput AppliedMethod + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource ) : IValidateCreateInput { - public HygrothermalData ToDomainModel(Guid? userId) + public HygrothermalData ToDomainModel( + Guid? userId, + string? fileExtension + ) { - return new( + var data = new HygrothermalData( userId, Locale, ComponentId, @@ -41,6 +43,8 @@ public HygrothermalData ToDomainModel(Guid? userId) CreatedAt, AppliedMethod.ToDomainModel() ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; } }; @@ -53,6 +57,8 @@ public enum CreateHygrothermalDataErrorCode UNKNOWN_COMPONENT, ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -90,6 +96,8 @@ public async Task CreateHygrothermalDataAsync( CommonAuthorization authorization, IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -113,15 +121,22 @@ CancellationToken cancellationToken CreateHygrothermalDataErrorCode.UNKNOWN_COMPONENT, institutionByIdDataLoader, CreateHygrothermalDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataFormatByIdDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken ) - ).Failed(out var validateErrorPayload) + ).Failed(out var dataFormat, out var validateErrorPayload) ) { return validateErrorPayload; } - var hygrothermalData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); + var hygrothermalData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); context.HygrothermalData.Add(hygrothermalData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index aef0a22f..b4b46ee8 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -29,6 +29,7 @@ public sealed record CreateOpticalDataInput( OpticalComponentSubtype? Subtype, CoatedSide? CoatedSide, AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource, double[] NearnormalHemisphericalVisibleTransmittances, double[] NearnormalHemisphericalVisibleReflectances, double[] NearnormalHemisphericalSolarTransmittances, @@ -38,9 +39,12 @@ public sealed record CreateOpticalDataInput( IReadOnlyList CielabColors ) : IValidateCreateInput { - public OpticalData ToDomainModel(Guid? userId) + public OpticalData ToDomainModel( + Guid? userId, + string? fileExtension + ) { - return new( + var data = new OpticalData( userId, Locale, ComponentId, @@ -61,6 +65,8 @@ public OpticalData ToDomainModel(Guid? userId) ColorRenderingIndices, CielabColors.Select(c => c.ToDomainModel()).ToList() ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; } }; @@ -73,6 +79,8 @@ public enum CreateOpticalDataErrorCode UNKNOWN_COMPONENT, ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -110,6 +118,8 @@ public async Task CreateOpticalDataAsync( CommonAuthorization authorization, IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -133,15 +143,22 @@ CancellationToken cancellationToken CreateOpticalDataErrorCode.UNKNOWN_COMPONENT, institutionByIdDataLoader, CreateOpticalDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataFormatByIdDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken ) - ).Failed(out var validateErrorPayload) + ).Failed(out var dataFormat, out var validateErrorPayload) ) { return validateErrorPayload; } - var opticalData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); + var opticalData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); context.OpticalData.Add(opticalData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 16d62710..78716be2 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -25,12 +25,16 @@ public sealed record CreatePhotovoltaicDataInput( string[] Warnings, DateTime CreatedAt, Guid CreatorId, - AppliedMethodInput AppliedMethod + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource ) : IValidateCreateInput { - public PhotovoltaicData ToDomainModel(Guid? userId) + public PhotovoltaicData ToDomainModel( + Guid? userId, + string? fileExtension + ) { - return new( + var data = new PhotovoltaicData( userId, Locale, ComponentId, @@ -41,6 +45,8 @@ public PhotovoltaicData ToDomainModel(Guid? userId) CreatedAt, AppliedMethod.ToDomainModel() ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; } }; @@ -53,6 +59,8 @@ public enum CreatePhotovoltaicDataErrorCode UNKNOWN_COMPONENT, ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -90,6 +98,8 @@ public async Task CreatePhotovoltaicDataAsync( CommonAuthorization authorization, IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken ) @@ -113,15 +123,22 @@ CancellationToken cancellationToken CreatePhotovoltaicDataErrorCode.UNKNOWN_COMPONENT, institutionByIdDataLoader, CreatePhotovoltaicDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataFormatByIdDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken ) - ).Failed(out var validateErrorPayload) + ).Failed(out var dataFormat, out var validateErrorPayload) ) { return validateErrorPayload; } - var photovoltaicData = input.ToDomainModel(currentUserOrApplication.CurrentUser?.Uuid); + var photovoltaicData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); context.PhotovoltaicData.Add(photovoltaicData); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs new file mode 100644 index 00000000..01215da6 --- /dev/null +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Database.Data; +using Database.Utilities; + +namespace Database.GraphQl; + +public sealed record RootGetHttpsResourceInput( + string Description, + Guid DataFormatId, + IReadOnlyList ArchivedFilesMetaInformation +) +{ + public GetHttpsResource ToDomainModel(string? fileExtension) + { + return new GetHttpsResource( + Description, + Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. + DataFormatId, + fileExtension, + ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList() + ); + } +}; \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index b8714ca9..f7f6611b 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -298,6 +298,18 @@ type DeleteDataPayload { query: Query! } +type DeleteGetHttpsResourceError implements UserError { + code: DeleteGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type DeleteGetHttpsResourcePayload { + errors: [DeleteGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -356,6 +368,8 @@ type GetHttpsResource implements Node { doesFileExist: Boolean! hashValue: String! id: ID! + isChild: Boolean! + isRoot: Boolean! locator: Url! parent: GetHttpsResource @cost(weight: "10") uuid: Uuid! @cost(weight: "10") @@ -471,6 +485,7 @@ type Mutation { createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") @@ -669,6 +684,7 @@ type Query { "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") verificationCode: String! } @@ -918,6 +934,7 @@ input CreateCalorimetricDataInput { gValues: [Float!]! locale: String! name: String + rootResource: RootGetHttpsResourceInput! uValues: [Float!]! warnings: [String!]! } @@ -930,18 +947,19 @@ input CreateGeometricDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! } input CreateGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! archivedFilesMetaInformation: [FileMetaInformationInput!]! dataFormatId: Uuid! dataId: Uuid! dataKind: DataKind! description: String! - parentId: Uuid + parentId: Uuid! } input CreateHygrothermalDataInput { @@ -952,6 +970,7 @@ input CreateHygrothermalDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -971,6 +990,7 @@ input CreateOpticalDataInput { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + rootResource: RootGetHttpsResourceInput! subtype: OpticalComponentSubtype type: OpticalComponentType warnings: [String!]! @@ -984,6 +1004,7 @@ input CreatePhotovoltaicDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -1083,6 +1104,10 @@ input DeleteDataInput { dataKind: DataKind! } +input DeleteGetHttpsResourceInput { + getHttpsResourceId: Uuid! +} + input FileMetaInformationInput { dataFormatId: Uuid! path: [String!]! @@ -1556,6 +1581,12 @@ input RetractDataInput { dataKind: DataKind! } +input RootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! +} + input StandardInput { abstract: String locator: Url @@ -1825,6 +1856,8 @@ enum CreateCalorimetricDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1835,6 +1868,8 @@ enum CreateGeometricDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1845,11 +1880,8 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_DATA UNKNOWN_PARENT ILLEGAL_PARENT - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT UNKNOWN_DATA_FORMAT - DUPLICATE_ROOT_RESOURCE CREATING_RESPONSE_APPROVAL_FAILED - PARENT_WITHOUT_APPLIED_CONVERSION_METHOD } enum CreateHygrothermalDataErrorCode { @@ -1859,6 +1891,8 @@ enum CreateHygrothermalDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1869,6 +1903,8 @@ enum CreateOpticalDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1879,6 +1915,8 @@ enum CreatePhotovoltaicDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1910,6 +1948,17 @@ enum DeleteDataErrorCode { NOT_PENDING } +enum DeleteGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + ROOT_RESOURCE + NOT_PENDING_DATA + CREATING_RESPONSE_APPROVAL_FAILED + HAS_CHILDREN +} + enum Illuminant { A D65 @@ -1988,6 +2037,7 @@ enum RetractDataErrorCode { UNAUTHORIZED UNKNOWN_DATA CREATING_RESPONSE_APPROVAL_FAILED + NOT_PUBLISHED } enum SortEnumType { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index b8714ca9..f7f6611b 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -298,6 +298,18 @@ type DeleteDataPayload { query: Query! } +type DeleteGetHttpsResourceError implements UserError { + code: DeleteGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type DeleteGetHttpsResourcePayload { + errors: [DeleteGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type FileMetaInformation { dataFormatId: Uuid! path: [String!]! @@ -356,6 +368,8 @@ type GetHttpsResource implements Node { doesFileExist: Boolean! hashValue: String! id: ID! + isChild: Boolean! + isRoot: Boolean! locator: Url! parent: GetHttpsResource @cost(weight: "10") uuid: Uuid! @cost(weight: "10") @@ -471,6 +485,7 @@ type Mutation { createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") @@ -669,6 +684,7 @@ type Query { "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") verificationCode: String! } @@ -918,6 +934,7 @@ input CreateCalorimetricDataInput { gValues: [Float!]! locale: String! name: String + rootResource: RootGetHttpsResourceInput! uValues: [Float!]! warnings: [String!]! } @@ -930,18 +947,19 @@ input CreateGeometricDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! } input CreateGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! archivedFilesMetaInformation: [FileMetaInformationInput!]! dataFormatId: Uuid! dataId: Uuid! dataKind: DataKind! description: String! - parentId: Uuid + parentId: Uuid! } input CreateHygrothermalDataInput { @@ -952,6 +970,7 @@ input CreateHygrothermalDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -971,6 +990,7 @@ input CreateOpticalDataInput { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + rootResource: RootGetHttpsResourceInput! subtype: OpticalComponentSubtype type: OpticalComponentType warnings: [String!]! @@ -984,6 +1004,7 @@ input CreatePhotovoltaicDataInput { description: String locale: String! name: String + rootResource: RootGetHttpsResourceInput! warnings: [String!]! } @@ -1083,6 +1104,10 @@ input DeleteDataInput { dataKind: DataKind! } +input DeleteGetHttpsResourceInput { + getHttpsResourceId: Uuid! +} + input FileMetaInformationInput { dataFormatId: Uuid! path: [String!]! @@ -1556,6 +1581,12 @@ input RetractDataInput { dataKind: DataKind! } +input RootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! +} + input StandardInput { abstract: String locator: Url @@ -1825,6 +1856,8 @@ enum CreateCalorimetricDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1835,6 +1868,8 @@ enum CreateGeometricDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1845,11 +1880,8 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_DATA UNKNOWN_PARENT ILLEGAL_PARENT - APPLIED_CONVERSION_METHOD_WITHOUT_PARENT UNKNOWN_DATA_FORMAT - DUPLICATE_ROOT_RESOURCE CREATING_RESPONSE_APPROVAL_FAILED - PARENT_WITHOUT_APPLIED_CONVERSION_METHOD } enum CreateHygrothermalDataErrorCode { @@ -1859,6 +1891,8 @@ enum CreateHygrothermalDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1869,6 +1903,8 @@ enum CreateOpticalDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1879,6 +1915,8 @@ enum CreatePhotovoltaicDataErrorCode { UNKNOWN_COMPONENT ILLEGAL_CREATED_AT UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1910,6 +1948,17 @@ enum DeleteDataErrorCode { NOT_PENDING } +enum DeleteGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + ROOT_RESOURCE + NOT_PENDING_DATA + CREATING_RESPONSE_APPROVAL_FAILED + HAS_CHILDREN +} + enum Illuminant { A D65 @@ -1988,6 +2037,7 @@ enum RetractDataErrorCode { UNAUTHORIZED UNKNOWN_DATA CREATING_RESPONSE_APPROVAL_FAILED + NOT_PUBLISHED } enum SortEnumType { From db426e63aedb5e3bc8bc4e3e8d490cd44e54d9df Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 15 Nov 2025 16:08:25 +0100 Subject: [PATCH 560/878] Remove legacy code to register enumerations with Npgsql --- backend/src/Data/ApplicationDbContext.cs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index daf28fff..a8d49fca 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -120,18 +120,6 @@ QueryContext queryContext ); } - private static void CreateEnumerations(ModelBuilder builder, string schemaName) - { - // https://www.npgsql.org/efcore/mapping/enum.html?tabs=with-datasource#mapping-your-enum - // Create enumerations in the same schema used by - // `NpgsqlDataSourceBuilder.MapEnum` in `Startup`. The format of how to - // specify the type name here and in `Startup` differ slightly. This - // was complained about in - // https://github.com/npgsql/efcore.pg/issues/2963#issuecomment-1818866360 - builder.HasPostgresEnum(schemaName, DataKindTypeName); - builder.HasPostgresEnum(schemaName, StandardizerTypeName); - } - private static EntityTypeBuilder ConfigureEntity( @@ -369,7 +357,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); modelBuilder.HasDefaultSchema(_schemaName); modelBuilder.HasPostgresExtension("pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - CreateEnumerations(modelBuilder, _schemaName); ConfigureIdentityEntities(modelBuilder); ConfigureEntity( ConfigureGetHttpsResource(modelBuilder.Entity()) From 83be3a170b95bb9b3381db290ca13a52c1fd3614 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 15 Nov 2025 16:44:05 +0100 Subject: [PATCH 561/878] Reuse validation code of root resource for child resources --- backend/src/GraphQl/DataMutationBase.cs | 68 +++++++++++++++++++ .../GraphQl/DataX/CreateDataMutationBase.cs | 56 +++------------ .../CreateGetHttpsResourceMutation.cs | 27 ++++---- .../src/GraphQl/RootGetHttpsResourceInput.cs | 7 ++ 4 files changed, 99 insertions(+), 59 deletions(-) diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index f53cbda2..0a6a42b8 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -5,6 +5,12 @@ using Database.Data; using Database.Enumerations; using Database.Services; +using System.Collections.Generic; +using static Database.ApiRequests.DataFormatDataLoader; +using Database.ApiRequests; +using Database.Extensions; +using System.Linq; +using System.Globalization; namespace Database.GraphQl; @@ -18,6 +24,7 @@ public abstract class DataMutationBase : MutationBase where TData : class where TPayload : class +where TError : class { protected async Task> FetchDataAsync( IIdentifyDataInput input, @@ -70,4 +77,65 @@ CancellationToken cancellationToken } return new Result.Data(true); } + + protected async Task>> ValidateGetHttpsResourceAsync( + IValidateGetHttpsResourceInput input, + IReadOnlyList getHttpsResourcePath, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + TErrorCode unknownDataFormatErrorCode, + CancellationToken cancellationToken + ) + { + var validateDataFormats = new Task>[1 + input.ArchivedFilesMetaInformation.Count]; + validateDataFormats[0] = Task.Run>(async () => + { + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); + if (dataFormat is null) + { + return new Result.Error( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [.. getHttpsResourcePath, nameof(input.DataFormatId).ToLowerFirst()] + ) + ); + } + return new Result.Data(dataFormat); + }, + cancellationToken + ); + foreach (var (archivedFileMetaInformation, index) in input.ArchivedFilesMetaInformation.WithIndex()) + { + validateDataFormats[index + 1] = Task.Run>(async () => + { + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); + if (dataFormat is null) + { + // TODO Is the index in the path formated correctly or should it be enclosed in square brackets? + return new Result.Error( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [.. getHttpsResourcePath, nameof(input.ArchivedFilesMetaInformation).ToLowerFirst(), index.ToString(CultureInfo.InvariantCulture), nameof(archivedFileMetaInformation.DataFormatId).ToLowerFirst()] + ) + ); + } + return new Result.Data(dataFormat); + }, + cancellationToken + ); + } + var maybeDataFormats = await Task.WhenAll(validateDataFormats); + var errors = maybeDataFormats + .Select(_ => _.Failed(out var error) ? error : null) + .NotNull() + .ToList() + .AsReadOnly(); + if (errors.Count >= 1) + { + return new Result>.Error(errors); + } + maybeDataFormats[0].Succeeded(out var dataFormat); + return new Result>.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index f681ae34..0f90be2f 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -5,9 +5,7 @@ using Database.ApiRequests; using GraphQL.Client.Abstractions.Utilities; using Database.Extensions; -using System.Globalization; using static Database.ApiRequests.DataFormatDataLoader; -using System.Linq; namespace Database.GraphQl.DataX; @@ -82,58 +80,24 @@ CancellationToken cancellationToken ); } // TODO check applied method sources - var validateDataFormats = new Task>[1 + input.RootResource.ArchivedFilesMetaInformation.Count]; - validateDataFormats[0] = Task.Run>(async () => - { - var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.RootResource.DataFormatId, cancellationToken); - if (dataFormat is null) - { - return new Result.Error( - NewError( - unknownDataFormatErrorCode, - "The data format does not exist", - [nameof(input), nameof(input.RootResource).ToLowerFirst(), nameof(input.RootResource.DataFormatId).ToLowerFirst()] - ) - ); - } - return new Result.Data(dataFormat); - }, + var validateRootResourceResult = await ValidateGetHttpsResourceAsync( + input.RootResource, + [nameof(input), nameof(input.RootResource)], + dataFormatByIdDataLoader, + unknownDataFormatErrorCode, cancellationToken ); - foreach (var (archivedFileMetaInformation, index) in input.RootResource.ArchivedFilesMetaInformation.WithIndex()) + if (validateRootResourceResult.Failed(out var dataFormat, out var validateRootResourceErrors)) { - validateDataFormats[index + 1] = Task.Run>(async () => - { - var dataFormat = await dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); - if (dataFormat is null) - { - // TODO Is the index in the path formated correctly or should it be enclosed in square brackets? - return new Result.Error( - NewError( - unknownDataFormatErrorCode, - "The data format does not exist", - [nameof(input), nameof(input.RootResource).ToLowerFirst(), nameof(input.RootResource.ArchivedFilesMetaInformation).ToLowerFirst(), index.ToString(CultureInfo.InvariantCulture), nameof(archivedFileMetaInformation.DataFormatId).ToLowerFirst()] - ) - ); - } - return new Result.Data(dataFormat); - }, - cancellationToken - ); + errors.AddRange(validateRootResourceErrors); } - var maybeDataFormats = await Task.WhenAll(validateDataFormats); - errors.AddRange( - maybeDataFormats - .Select(_ => _.Failed(out var error) ? error : null) - .NotNull() - ); - if (errors.Count >= 1) + // Note that `dataFormat` is only `null`, when `validateRootResourceResult` failed. + if (errors.Count >= 1 || dataFormat is null) { return new Result.Error( NewPayload(null, errors) ); } - maybeDataFormats[0].Succeeded(out var dataFormat); - return new Result.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); + return new Result.Data(dataFormat); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 3483a042..fd8178f4 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -27,7 +27,7 @@ public sealed record CreateGetHttpsResourceInput( Guid ParentId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod -) : IIdentifyDataInput +) : IIdentifyDataInput, IValidateGetHttpsResourceInput { public GetHttpsResource ToDomainModel(string? fileExtension) { @@ -132,17 +132,6 @@ CancellationToken cancellationToken } var errors = new List(); - var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); - if (dataFormat is null) - { - errors.Add( - NewError( - CreateGetHttpsResourceErrorCode.UNKNOWN_DATA_FORMAT, - "There is no data format with the format ID.", - [nameof(input), nameof(input.DataFormatId).ToLowerFirst()] - ) - ); - } var parent = await context.GetHttpsResources .SingleOrDefaultAsync(_ => @@ -169,7 +158,19 @@ await context.GetHttpsResources ) ); } - if (errors.Count >= 1) + var validateResourceResult = await ValidateGetHttpsResourceAsync( + input, + [nameof(input)], + dataFormatByIdDataLoader, + CreateGetHttpsResourceErrorCode.UNKNOWN_DATA_FORMAT, + cancellationToken + ); + if (validateResourceResult.Failed(out var dataFormat, out var validateResourceErrors)) + { + errors.AddRange(validateResourceErrors); + } + // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. + if (errors.Count >= 1 || dataFormat is null) { return NewPayload(null, errors); } diff --git a/backend/src/GraphQl/RootGetHttpsResourceInput.cs b/backend/src/GraphQl/RootGetHttpsResourceInput.cs index 01215da6..fbb03124 100644 --- a/backend/src/GraphQl/RootGetHttpsResourceInput.cs +++ b/backend/src/GraphQl/RootGetHttpsResourceInput.cs @@ -6,11 +6,18 @@ namespace Database.GraphQl; +public interface IValidateGetHttpsResourceInput +{ + public Guid DataFormatId { get; } + public IReadOnlyList ArchivedFilesMetaInformation { get; } +} + public sealed record RootGetHttpsResourceInput( string Description, Guid DataFormatId, IReadOnlyList ArchivedFilesMetaInformation ) +: IValidateGetHttpsResourceInput { public GetHttpsResource ToDomainModel(string? fileExtension) { From 2f3ca4e689eef92a3f830727e1cc90ef700b8b14 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 15 Nov 2025 17:39:41 +0100 Subject: [PATCH 562/878] Report GraphQL errors that may be in the response of an attempt to fetch the meta data for creating a response approval --- .../CalorimetricDataFilterType.cs | 1 - .../src/GraphQl/DataX/DataFilterTypeBase.cs | 6 ++++-- backend/src/GraphQl/DataX/DataSortType.cs | 2 +- backend/src/GraphQl/DataX/DataSortTypeBase.cs | 5 +++-- .../src/GraphQl/Entities/EntityFilterType.cs | 18 ++++++++++++++++++ backend/src/GraphQl/Entities/EntitySortType.cs | 18 ++++++++++++++++++ .../GeometricDataX/GeometricDataFilterType.cs | 1 - .../GetHttpsResourceFilterType.cs | 6 +++--- .../GetHttpsResourceSortType.cs | 6 +++--- .../HygrothermalDataFilterType.cs | 1 - .../OpticalDataX/OpticalDataFilterType.cs | 1 - .../PhotovoltaicDataFilterType.cs | 1 - .../ResponseApprovalFilterType.cs | 6 ++++-- .../UpdateResponseApprovalsMutation.cs | 1 - .../src/Services/ResponseApprovalService.cs | 14 ++++++++++++-- 15 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 backend/src/GraphQl/Entities/EntityFilterType.cs create mode 100644 backend/src/GraphQl/Entities/EntitySortType.cs diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index 620ad408..207d1755 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -12,7 +12,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); descriptor.Field(x => x.GValues); descriptor.Field(x => x.UValues); } diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index ceddfbcf..7f56ab86 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -1,17 +1,19 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; namespace Database.GraphQl.DataX; public abstract class DataFilterTypeBase - : FilterInputType + : EntityFilterType where TData : IData { protected override void Configure( IFilterInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); + descriptor.Field(x => x.UserId); descriptor.Field(x => x.Locale); descriptor.Field(x => x.Name); descriptor.Field(x => x.Description); diff --git a/backend/src/GraphQl/DataX/DataSortType.cs b/backend/src/GraphQl/DataX/DataSortType.cs index 42143914..3efa5258 100644 --- a/backend/src/GraphQl/DataX/DataSortType.cs +++ b/backend/src/GraphQl/DataX/DataSortType.cs @@ -10,7 +10,7 @@ protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.Name(nameof(IData)[1..] + GraphQlConstants.SortInputSuffix); base.Configure(descriptor); + descriptor.Name(nameof(IData)[1..] + GraphQlConstants.SortInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataSortTypeBase.cs b/backend/src/GraphQl/DataX/DataSortTypeBase.cs index 39d060c5..880630de 100644 --- a/backend/src/GraphQl/DataX/DataSortTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -1,17 +1,18 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.DataX; public abstract class DataSortTypeBase - : SortInputType + : EntitySortType where TData : IData { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); descriptor.Field(x => x.Locale); descriptor.Field(x => x.Name); descriptor.Field(x => x.Description); diff --git a/backend/src/GraphQl/Entities/EntityFilterType.cs b/backend/src/GraphQl/Entities/EntityFilterType.cs new file mode 100644 index 00000000..2b283f62 --- /dev/null +++ b/backend/src/GraphQl/Entities/EntityFilterType.cs @@ -0,0 +1,18 @@ +using Database.Data; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.Entities; + +public abstract class EntityFilterType + : FilterInputType + where TEntity : IEntity +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Id); + // TODO Do we want to filter by: descriptor.Field(x => x.Version); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntitySortType.cs b/backend/src/GraphQl/Entities/EntitySortType.cs new file mode 100644 index 00000000..f3bbdb68 --- /dev/null +++ b/backend/src/GraphQl/Entities/EntitySortType.cs @@ -0,0 +1,18 @@ +using Database.Data; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.Entities; + +public abstract class EntitySortType + : SortInputType + where TEntity : IEntity +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.Id); + // descriptor.Field(x => x.Version); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs index fb973368..a762b17b 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -12,7 +12,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); descriptor.Field(x => x.Thicknesses); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index c74b71ad..c511e2e6 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,17 +1,17 @@ using HotChocolate.Data.Filters; +using Database.GraphQl.Entities; using Database.Data; namespace Database.GraphQl.GetHttpsResources; public class GetHttpsResourceFilterType - : FilterInputType + : EntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Id); + base.Configure(descriptor); descriptor.Field(x => x.Description); descriptor.Field(x => x.HashValue); descriptor.Field(x => x.DataFormatId); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs index 5141ab43..c41a6e9f 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -1,17 +1,17 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.GetHttpsResources; public class GetHttpsResourceSortType - : SortInputType + : EntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Id); + base.Configure(descriptor); descriptor.Field(x => x.Description); descriptor.Field(x => x.HashValue); descriptor.Field(x => x.DataFormatId); diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index d05181f0..c1e2965d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -12,6 +12,5 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 41644c6c..4aa03e45 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -12,7 +12,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); descriptor.Field(x => x.Type); descriptor.Field(x => x.Subtype); descriptor.Field(x => x.CoatedSide); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index 373756a6..5a81a4cc 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -12,6 +12,5 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); } } \ No newline at end of file diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index 2724d8e9..75c79c90 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -1,16 +1,18 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; namespace Database.GraphQl.ResponseApprovals; public abstract class ResponseApprovalFilterType -: FilterInputType +: EntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); + descriptor.Field(x => x.UserId); descriptor.Field(x => x.Locale); descriptor.Field(x => x.Name); descriptor.Field(x => x.Description); diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index c777bbb3..dc6b8b5e 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using GreenDonut.Data; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 8cd08eed..03e93e39 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -4,6 +4,10 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Database.Data; +using GraphQL; +using Database.Json; +using HotChocolate; +using System.Linq; namespace Database.Services; @@ -41,6 +45,12 @@ public async Task CreateResponseApproval(T dataObject, Canc logger.QueryAllMetaData(dataObject.GetType(), dataObject.Id); var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); logger.QueryAndVariablesAndResponce(query, variables, response); + var typedResponse = JsonSerializer.Deserialize>(response, JsonSerializerSettings.GraphQl) + ?? throw new JsonException($"Failed to deserialize the GraphQL response: {response}"); + if (typedResponse.Errors is not null && typedResponse.Errors.Length >= 1) + { + throw new GraphQLException($"The GraphQL response contains the following errors: {string.Join(", ", typedResponse.Errors.Select(_ => $"'{_.Message}' [{string.Join(", ", _.Extensions?.Select(e => $"{e.Key}: '{e.Value}'") ?? [])}] ({string.Join(".", _.Path?.Select(p => p.ToString()) ?? [])})"))}"); + } var (signature, fingerprint) = await signingService.SignData(response); return new ResponseApproval( DateTime.UtcNow, @@ -58,11 +68,11 @@ public async Task CreateResponseApproval(T dataObject, Canc { return dataObject switch { - GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, cancellationToken), CalorimetricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.CalorimetricDataFileNames, appSettings, apiRequestService, cancellationToken), + GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, cancellationToken), HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, cancellationToken), - PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, cancellationToken), OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, cancellationToken), + PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, cancellationToken), _ => throw new ArgumentOutOfRangeException($"Unsupported data type {typeof(T)}"), }; } From 25f9d95bde7cc0ac1ab3120e137ac266915a6483 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Nov 2025 17:28:28 +0100 Subject: [PATCH 563/878] Upgrade Node.JS to LTS version 24.11 --- frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 6 +++--- frontend/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 0c9d3e24..bc924180 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -7,7 +7,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:22.14-bookworm-slim +FROM node:24.11-bookworm-slim ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 04b07fc3..b0477998 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -6,7 +6,7 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:22.14-bookworm-slim AS dependencies +FROM node:24.11-bookworm-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -18,7 +18,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:22.14-bookworm-slim AS build +FROM node:24.11-bookworm-slim AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -31,7 +31,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:22.14-bookworm-slim +FROM node:24.11-bookworm-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ diff --git a/frontend/package.json b/frontend/package.json index e4729189..7ba7f58d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,7 @@ "@graphql-tools/load-files": "^6.6.1", "@graphql-tools/merge": "^8.3.18", "@graphql-tools/schema": "^9.0.16", - "@types/node": "^20.14.9", + "@types/node": "^24.10.1", "@types/qrcode.react": "^1.0", "@types/react": "^18.0", "@types/react-dom": "^18.0", From 90ccaa9a7b066bec69ec194c2f6ef60e330caa47 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 17 Nov 2025 17:51:58 +0100 Subject: [PATCH 564/878] Upgrade Docker (base) images --- Makefile | 4 ++-- docker-compose.production.yml | 6 +++--- docker-compose.yml | 6 +++--- frontend/Dockerfile | 2 +- frontend/Dockerfile-production | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 6ba2d02e..859f61a4 100644 --- a/Makefile +++ b/Makefile @@ -431,7 +431,7 @@ generate-certificate-authority : ## Generate certificate authority ECDSA private --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.27-bookworm \ + nginx:1.29-trixie-otel \ bash -ceux " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ @@ -539,7 +539,7 @@ generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate sig --rm \ --user $(shell id --user):$(shell id --group) \ --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.27-bookworm \ + nginx:1.29-trixie-otel \ bash -ceux " \ echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ openssl ecparam \ diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 1588acc8..f56c2c30 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -3,7 +3,7 @@ services: database: # https://hub.docker.com/_/postgres restart: always # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` - image: postgres:13.13-bookworm + image: postgres:13.23-trixie # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -28,7 +28,7 @@ services: email: # https://hub.docker.com/r/rnwood/smtp4dev restart: always - image: rnwood/smtp4dev:v3.1 + image: rnwood/smtp4dev:v3.10 # For all configuration options see # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml healthcheck: @@ -111,7 +111,7 @@ services: nginx: # https://hub.docker.com/_/nginx restart: always - image: nginx:1.27-bookworm + image: nginx:1.29-trixie-otel command: [ "nginx", "-g", "daemon off;" diff --git a/docker-compose.yml b/docker-compose.yml index 1718928a..37a0fde1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: database: # https://hub.docker.com/_/postgres # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` - image: postgres:13.13-bookworm + image: postgres:13.23-trixie # ports: # - 5432:5432 # For possible command-line arguments see @@ -30,7 +30,7 @@ services: - data:/var/lib/postgresql/data email: # https://hub.docker.com/r/rnwood/smtp4dev - image: rnwood/smtp4dev:v3.1 + image: rnwood/smtp4dev:v3.10 # For all configuration options see # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml # ports: @@ -171,7 +171,7 @@ services: - backend nginx: # https://hub.docker.com/_/nginx - image: nginx:1.25-bookworm + image: nginx:1.29-trixie-otel command: [ "nginx-debug", "-g", "daemon off;" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index bc924180..8c6467be 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -7,7 +7,7 @@ # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:24.11-bookworm-slim +FROM node:24.11-trixie-slim ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index b0477998..c9869d93 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -6,7 +6,7 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:24.11-bookworm-slim AS dependencies +FROM node:24.11-trixie-slim AS dependencies RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -18,7 +18,7 @@ WORKDIR /app COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile -FROM node:24.11-bookworm-slim AS build +FROM node:24.11-trixie-slim AS build RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -31,7 +31,7 @@ COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:24.11-bookworm-slim +FROM node:24.11-trixie-slim WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ From 4dcd564b13f5bdf4b23be158b0ebd500cf5cc446 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 19 Nov 2025 18:58:27 +0100 Subject: [PATCH 565/878] Remove unmaintained package `graphql-let` and upgrade all frontend packages --- frontend/.vscode/settings.json | 5 + frontend/codegen.ts | 76 + frontend/codegen.yml | 11 - frontend/components/Footer.tsx | 1 - frontend/components/NavBar.tsx | 9 +- frontend/components/data/DataPageHeader.tsx | 18 +- .../data/calorimetric/CalorimetricData.tsx | 9 +- .../data/geometric/GeometricData.tsx | 9 +- .../data/hygrothermal/HygrothermalData.tsx | 9 +- .../components/data/optical/OpticalData.tsx | 9 +- .../data/photovoltaic/PhotovoltaicData.tsx | 9 +- frontend/components/databases/Database.tsx | 7 +- .../components/databases/UpdateDatabase.tsx | 22 +- frontend/eslint.config.mjs | 15 + frontend/lib/apollo.ts | 76 +- frontend/lib/form.ts | 13 +- frontend/lib/table.tsx | 149 +- frontend/next-env.d.ts | 3 +- frontend/next.config.js | 37 - frontend/next.config.ts | 33 + frontend/package.json | 64 +- frontend/pages/_app.tsx | 21 +- frontend/pages/data/calorimetric/index.tsx | 61 +- frontend/pages/data/geometric/index.tsx | 13 +- frontend/pages/data/hygrothermal/index.tsx | 37 +- frontend/pages/data/optical/index.tsx | 97 +- frontend/pages/data/photovoltaic/index.tsx | 13 +- frontend/pages/user-info.tsx | 5 +- frontend/paths.ts | 80 +- frontend/queries/currentUser.generated.ts | 18 + frontend/queries/data.generated.ts | 177 + frontend/queries/databases.generated.ts | 23 + frontend/styles/global.css | 8 + frontend/test/index.test.tsx | 70 +- frontend/tsconfig.json | 21 +- frontend/yarn.lock | 7778 +++++++++-------- 36 files changed, 4803 insertions(+), 4203 deletions(-) create mode 100644 frontend/.vscode/settings.json create mode 100644 frontend/codegen.ts delete mode 100644 frontend/codegen.yml create mode 100644 frontend/eslint.config.mjs delete mode 100644 frontend/next.config.js create mode 100644 frontend/next.config.ts create mode 100644 frontend/queries/currentUser.generated.ts create mode 100644 frontend/queries/data.generated.ts create mode 100644 frontend/queries/databases.generated.ts create mode 100644 frontend/styles/global.css diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json new file mode 100644 index 00000000..96f44007 --- /dev/null +++ b/frontend/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": null, + "typescript.tsserver.experimental.enableProjectDiagnostics": true +} \ No newline at end of file diff --git a/frontend/codegen.ts b/frontend/codegen.ts new file mode 100644 index 00000000..cef6e6a0 --- /dev/null +++ b/frontend/codegen.ts @@ -0,0 +1,76 @@ +import type { CodegenConfig } from '@graphql-codegen/cli'; + +const config: CodegenConfig = { + overwrite: true, + schema: "./type-defs.graphqls", + documents: "./queries/*.graphql", + // Don't exit with non-zero status when there are no documents + ignoreNoDocuments: true, + generates: { + // Inspired by https://www.apollographql.com/docs/react/development-testing/graphql-codegen#generating-precompiled-graphql-documents-with-their-type-definitions + "./__generated__/graphql.ts": { + plugins: ["typescript"], + }, + "./queries/": { + preset: "near-operation-file", + presetConfig: { + // This should be the file generated by the "typescript" plugin above, + // relative to the directory specified for this configuration + baseTypesPath: "../__generated__/graphql.ts", + }, + plugins: ["typescript-operations", "typed-document-node"], + config: { + avoidOptionals: { + // Use `null` for nullable fields instead of optionals + field: true, + // Allow nullable input fields to remain unspecified + inputValue: false, + }, + // Use `unknown` instead of `any` for unconfigured scalars + defaultScalarType: "unknown", + // Apollo Client always includes `__typename` fields + nonOptionalTypename: true, + // Apollo Client doesn't add the `__typename` field to root types so + // don't generate a type for the `__typename` for root operation types. + skipTypeNameForRoot: true, + // ... + // strictScalars: true, + scalars: { + ID: { + input: 'string', + output: 'string | number' + }, + Any: 'unknown', + Byte: 'number', + DateTime: 'string', + Locale: 'string', + NonNegativeInt: 'number', + Url: 'string', + Uuid: 'string', + }, + useTypeImports: true, + // immutableTypes: true, + // nullability: true, + }, + }, + "./__generated__/fragmentMatcherTypes.ts": { + plugins: ["fragment-matcher"], + config: { + module: "es2015", + apolloClientVersion: 3, + useExplicitTyping: false, + deterministic: true, + }, + }, + // './queries/': { + // preset: 'near-operation-file', + // presetConfig: { + // extension: '.graphql.ts', + // baseTypesPath: '../__generated__/graphql.ts', + // }, + // plugins: ['typescript-operations', 'typed-document-node'], + // }, + }, +}; + +export default config; diff --git a/frontend/codegen.yml b/frontend/codegen.yml deleted file mode 100644 index d1ff6d03..00000000 --- a/frontend/codegen.yml +++ /dev/null @@ -1,11 +0,0 @@ -schema: - - type-defs.graphqls -generates: - # https://www.graphql-code-generator.com/docs/plugins/fragment-matcher - __generated__/fragmentMatcherTypes.ts: - plugins: - - "fragment-matcher" - config: - module: "es2015" - apolloClientVersion: 3 - useExplicitTyping: false diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx index e5cd22bf..5544d81c 100644 --- a/frontend/components/Footer.tsx +++ b/frontend/components/Footer.tsx @@ -1,5 +1,4 @@ import { Typography } from "antd"; -import * as React from "react"; import paths from "../paths"; export type FooterProps = {}; diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index f00930a8..1750e6e1 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -1,14 +1,15 @@ -import * as React from "react"; +import { useQuery } from '@apollo/client/react'; import Link from "next/link"; import { useRouter } from "next/router"; import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; -import { useCurrentUserQuery } from "../queries/currentUser.graphql"; +import { CurrentUserDocument } from "../queries/currentUser.generated"; import { getXsrfToken } from "../lib/apollo"; +import { Route } from 'next'; type NavItemProps = { - path: string; + path: Route; label: string; }; @@ -33,7 +34,7 @@ const moderatorItems = [ export default function NavBar({ items }: NavBarProps) { const router = useRouter(); - const currentUser = useCurrentUserQuery()?.data?.currentUser; + const currentUser = useQuery(CurrentUserDocument)?.data?.currentUser; return ( diff --git a/frontend/components/data/DataPageHeader.tsx b/frontend/components/data/DataPageHeader.tsx index 6a05ebee..cea6d64a 100644 --- a/frontend/components/data/DataPageHeader.tsx +++ b/frontend/components/data/DataPageHeader.tsx @@ -2,7 +2,7 @@ import { Descriptions, Typography } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import Link from "next/link"; import paths from "../../paths"; -import { DataPartialFragment } from "../../queries/data.graphql"; +import { DataPartialFragment } from "../../queries/data.generated"; import { ReactNode } from "react"; export type DataPageHeaderProps = { @@ -51,13 +51,15 @@ export default function DataPageHeader({ data, extra, children }: DataPageHeader {data.resourceTree.root.value.uuid} - - - {data.userId} - - + {data.userId != null && + + + {data.userId} + + + } {children} diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index e126e8af..d5ed4bc3 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,16 +1,17 @@ -import { Scalars } from "../../../__generated__/__types__"; -import { useCalorimetricDataQuery } from "../../../queries/data.graphql"; +import { Scalars } from "../../../__generated__/graphql"; +import { CalorimetricDataDocument } from "../../../queries/data.generated"; import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; import { messageApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; export type CalorimetricDataProps = { - calorimetricDataId: Scalars["Uuid"]; + calorimetricDataId: Scalars["Uuid"]["input"]; }; export default function CalorimetricData({ calorimetricDataId }: CalorimetricDataProps) { - const { loading, error, data } = useCalorimetricDataQuery({ + const { loading, error, data } = useQuery(CalorimetricDataDocument, { variables: { uuid: calorimetricDataId, }, diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 72d9ca86..36cdefc4 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -1,16 +1,17 @@ -import { Scalars } from "../../../__generated__/__types__"; -import { useGeometricDataQuery } from "../../../queries/data.graphql"; +import { Scalars } from "../../../__generated__/graphql"; +import { GeometricDataDocument } from "../../../queries/data.generated"; import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; import { messageApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; export type GeometricDataProps = { - geometricDataId: Scalars["Uuid"]; + geometricDataId: Scalars["Uuid"]["input"]; }; export default function GeometricData({ geometricDataId }: GeometricDataProps) { - const { loading, error, data } = useGeometricDataQuery({ + const { loading, error, data } = useQuery(GeometricDataDocument, { variables: { uuid: geometricDataId, }, diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index 71b1269f..da726f97 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -1,16 +1,17 @@ -import { Scalars } from "../../../__generated__/__types__"; -import { useHygrothermalDataQuery } from "../../../queries/data.graphql"; +import { Scalars } from "../../../__generated__/graphql"; +import { HygrothermalDataDocument } from "../../../queries/data.generated"; import { Skeleton, Result } from "antd"; import { useEffect } from "react"; import { messageApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; export type HygrothermalDataProps = { - hygrothermalDataId: Scalars["Uuid"]; + hygrothermalDataId: Scalars["Uuid"]["input"]; }; export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDataProps) { - const { loading, error, data } = useHygrothermalDataQuery({ + const { loading, error, data } = useQuery(HygrothermalDataDocument, { variables: { uuid: hygrothermalDataId, }, diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 6a0e6f31..e744ed38 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,16 +1,17 @@ -import { Scalars } from "../../../__generated__/__types__"; -import { useOpticalDataQuery } from "../../../queries/data.graphql"; +import { Scalars } from "../../../__generated__/graphql"; +import { OpticalDataDocument } from "../../../queries/data.generated"; import { Skeleton, Result, Descriptions } from "antd"; import { useEffect } from "react"; import { messageApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; export type OpticalDataProps = { - opticalDataId: Scalars["Uuid"]; + opticalDataId: Scalars["Uuid"]["input"]; }; export default function OpticalData({ opticalDataId }: OpticalDataProps) { - const { loading, error, data } = useOpticalDataQuery({ + const { loading, error, data } = useQuery(OpticalDataDocument, { variables: { uuid: opticalDataId, }, diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 488dc2f5..d00aa55a 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -1,16 +1,17 @@ -import { Scalars } from "../../../__generated__/__types__"; -import { usePhotovoltaicDataQuery } from "../../../queries/data.graphql"; +import { Scalars } from "../../../__generated__/graphql"; +import { PhotovoltaicDataDocument } from "../../../queries/data.generated"; import { Skeleton, Result } from "antd"; import { useEffect } from "react"; import { messageApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; export type PhotovoltaicDataProps = { - photovoltaicDataId: Scalars["Uuid"]; + photovoltaicDataId: Scalars["Uuid"]["input"]; }; export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDataProps) { - const { loading, error, data } = usePhotovoltaicDataQuery({ + const { loading, error, data } = useQuery(PhotovoltaicDataDocument, { variables: { uuid: photovoltaicDataId, }, diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index 803c9eb4..d22eb36b 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -1,15 +1,16 @@ -import { useDatabaseQuery } from "../../queries/databases.graphql"; +import { useQuery } from '@apollo/client/react'; +import { DatabaseDocument } from "../../queries/databases.generated"; import { Skeleton, Result, Descriptions, Typography, Tag } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { ReactNode, useEffect } from "react"; import { messageApolloError } from "../../lib/apollo"; import UpdateDatabase from "./UpdateDatabase"; -import { DatabaseVerificationState } from "../../__generated__/__types__"; +import { DatabaseVerificationState } from "../../__generated__/graphql"; export type DatabaseProps = {}; export default function Database({}: DatabaseProps) { - const { loading, error, data } = useDatabaseQuery(); + const { loading, error, data } = useQuery(DatabaseDocument); const database = data?.database; useEffect(() => { diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx index 045539b4..328a40e7 100644 --- a/frontend/components/databases/UpdateDatabase.tsx +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -1,10 +1,10 @@ -import * as React from "react"; +import { useMutation } from '@apollo/client/react'; import { Alert, Form, Input, Button, Modal } from "antd"; import { - useUpdateDatabaseMutation, + UpdateDatabaseDocument, DatabaseDocument, -} from "../../queries/databases.graphql"; -import { Scalars } from "../../__generated__/__types__"; +} from "../../queries/databases.generated"; +import { Scalars } from "../../__generated__/graphql"; import { useState } from "react"; import { handleFormErrors } from "../../lib/form"; @@ -19,14 +19,14 @@ const tailLayout = { type FormValues = { newName: string; newDescription: string; - newLocator: Scalars["Url"]; + newLocator: Scalars["Url"]["input"]; }; export type UpdateDatabaseProps = { - databaseId: Scalars["Uuid"]; + databaseId: Scalars["Uuid"]["input"]; name: string; description: string; - locator: Scalars["Url"]; + locator: Scalars["Url"]["input"]; }; export default function UpdateDatabase({ @@ -36,7 +36,7 @@ export default function UpdateDatabase({ locator, }: UpdateDatabaseProps) { const [open, setOpen] = useState(false); - const [updateDatabaseMutation] = useUpdateDatabaseMutation({ + const [updateDatabaseMutation] = useMutation(UpdateDatabaseDocument, { // TODO Update the cache more efficiently as explained on https://www.apollographql.com/docs/react/caching/cache-interaction/ and https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates // See https://www.apollographql.com/docs/react/data/mutations/#options refetchQueries: [ @@ -59,7 +59,7 @@ export default function UpdateDatabase({ try { setUpdating(true); // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const { errors, data } = await updateDatabaseMutation({ + const { error, data } = await updateDatabaseMutation({ variables: { databaseId: databaseId, name: newName, @@ -68,14 +68,14 @@ export default function UpdateDatabase({ }, }); handleFormErrors( - errors, + error, data?.updateDatabase?.errors?.map((x) => { return { code: x.code, message: x.message, path: x.path }; }), setGlobalErrorMessages, form ); - if (!errors && !data?.updateDatabase?.errors) + if (!error && !data?.updateDatabase?.errors) data?.updateDatabase?.database; { setOpen(false); diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs new file mode 100644 index 00000000..52cd499d --- /dev/null +++ b/frontend/eslint.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from "eslint/config"; +import next from "eslint-config-next"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +export default defineConfig([{ + extends: [...next], + + rules: { + "react/display-name": "off", + }, +}]); \ No newline at end of file diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 8d9f0619..289dba8b 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -1,19 +1,16 @@ import { IncomingMessage, ServerResponse } from "http"; import { useMemo } from "react"; -import { - ApolloClient, - ApolloError, - createHttpLink, - InMemoryCache, - NormalizedCacheObject, -} from "@apollo/client"; -import { createPersistedQueryLink } from "@apollo/client/link/persisted-queries"; +import { ApolloClient, InMemoryCache, HttpLink, ErrorLike } from "@apollo/client"; +import { Defer20220824Handler } from "@apollo/client/incremental"; +import { LocalState } from "@apollo/client/local-state"; +import { CombinedGraphQLErrors } from "@apollo/client/errors"; +import { PersistedQueryLink } from "@apollo/client/link/persisted-queries"; import merge from "deepmerge"; import { message } from "antd"; import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin -let apolloClient: ApolloClient | undefined; +let apolloClient: ApolloClient | undefined; export type ResolverContext = { req?: IncomingMessage; @@ -49,11 +46,11 @@ function createIsomorphLink(context: ResolverContext = {}) { var antiforgeryToken = getXsrfToken(); if (antiforgeryToken) headers["X-XSRF-TOKEN"] = antiforgeryToken; } - return createPersistedQueryLink({ + return new PersistedQueryLink({ sha256, useGETForHashedQueries: false, }).concat( - createHttpLink({ + new HttpLink({ uri: // In the case `typeof window === "undefined"`, that is on the server // side, because we require HTTPS for the JWT bearer token, we cannot use @@ -80,6 +77,8 @@ function createApolloClient(context?: ResolverContext) { cache: new InMemoryCache({ possibleTypes: fragmentMatcherTypes.possibleTypes, }), + localState: new LocalState({}), + incrementalHandler: new Defer20220824Handler() }); } @@ -97,7 +96,7 @@ export function initializeApollo( // Get existing cache, loaded during client side data fetching const existingCache = _apolloClient.extract(); // Merge the existing cache into data passed from getStaticProps/getServerSideProps - const data = merge(initialState, existingCache); + const data = merge(initialState, existingCache as any); // Restore the cache with the merged data _apolloClient.cache.restore(data); } @@ -109,19 +108,52 @@ export function initializeApollo( return _apolloClient; } -export function messageApolloError(error: ApolloError) { - message.error( - `Name(${error.name}); Message(${ - error.message - }); GraphQL Errors(${error.graphQLErrors - .map((e) => `[Message(${e.message}); Path(${e.path?.join(".")})]`) - .join(", ")}); Network Error(Name(${error.networkError?.name}); Message(${ - error.networkError?.message - }))` - ); +// See https://www.apollographql.com/docs/react/data/error-handling +export function messageApolloError(error: ErrorLike) { + if (CombinedGraphQLErrors.is(error)) { + message.error( + `Name(${error.name + }); Message(${error.message + }); Extensions(${error.extensions + }); Stack(${error.stack + }); GraphQL Errors(${error.errors + .map((e) => `[Message(${e.message + }); Path(${e.path?.join(".") + }); Locations(${e.locations?.join(", ") + }); Extensions(${e.extensions + })]`) + .join(", ") + })` + ); + // } else if (CombinedProtocolErrors.is(error)) { + // Handle multipart subscription protocol errors + // } else if (LocalStateError.is(error)) { + // Handle errors thrown by the `LocalState` class + // } else if (ServerError.is(error)) { + // Handle server HTTP errors + // } else if (ServerParseError.is(error)) { + // Handle JSON parse errors + // } else if (UnconventionalError.is(error)) { + // Handle errors thrown by irregular types + } else { + message.error( + `Name(${error.name}); Message(${error.message}); Stack(${error.stack})` + ); + } } export function useApollo(initialState: any) { const store = useMemo(() => initializeApollo(initialState), [initialState]); return store; } + +/* +Copy the contents of this block into a `.d.ts` file in your project to enable correct response types in your custom links. +If you do not use the `@defer` directive in your application, you can safely remove this block. + +import "@apollo/client"; +import { Defer20220824Handler } from "@apollo/client/incremental"; +declare module "@apollo/client" { + export interface TypeOverrides extends Defer20220824Handler.TypeOverrides {} +} +*/ diff --git a/frontend/lib/form.ts b/frontend/lib/form.ts index 3ea26554..81ce720d 100644 --- a/frontend/lib/form.ts +++ b/frontend/lib/form.ts @@ -1,17 +1,22 @@ -import { GraphQLFormattedError } from "graphql"; import { FormInstance } from "antd"; import { Dispatch, SetStateAction } from "react"; +import { CombinedGraphQLErrors, ErrorLike } from "@apollo/client"; export function handleFormErrors( - graphQlErrors: readonly GraphQLFormattedError[] | undefined, + apolloError: ErrorLike | undefined, userErrors: { code: string; message: string; path: string[] }[] | undefined, setGlobalErrorMessages: Dispatch>, form: FormInstance ) { const globalErrorMessages = new Array(); - if (graphQlErrors) { + if (apolloError) { // TODO Is this how we want to handle GraphQl errors? - globalErrorMessages.push(...graphQlErrors.map((e) => e.message)); + globalErrorMessages.push(...`${apolloError.name}: ${apolloError.message}`); + if (CombinedGraphQLErrors.is(apolloError)) { + globalErrorMessages.push(...apolloError.errors.map((e) => + `${e.message} at path ${e.path?.join(", ")} and locations ${e.locations?.join(", ")}` + )); + } } if (userErrors) { const errorPathToMessage = userErrors.reduce((a, x) => { diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index 66542f55..9aa89ac2 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -6,12 +6,14 @@ import { } from "./freeTextFilter"; import Link from "next/link"; import { - AppliedMethod, - GetHttpsResourceTree, Scalars, -} from "../__generated__/__types__"; + Publication, + Standard, +} from "../__generated__/graphql"; import { Highlight } from "../components/Highlight"; import paths from "../paths"; +import { JSX } from "react"; +import { Route } from "next"; const sortDirections: SortOrder[] = ["ascend", "descend"]; @@ -62,7 +64,7 @@ export function getColumnProps( export function getDateTimeColumnProps( title: string, key: keyof RecordType, - getValue: (record: RecordType) => Scalars["DateTime"] | null | undefined + getValue: (record: RecordType) => Scalars["DateTime"]["output"] | null | undefined ) { return { ...getColumnProps(title, key), @@ -77,7 +79,7 @@ export function getDateTimeColumnProps( } export function getTimestampColumnProps< - RecordType extends { timestamp: string } + RecordType extends { timestamp: Scalars["DateTime"]["output"] } >() { return { ...getDateTimeColumnProps( @@ -135,7 +137,7 @@ export function getInternallyLinkedFilterableStringColumnProps( key: keyof RecordType ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (record: RecordType) => string + getPath: (record: RecordType) => Route ) { return getFilterableStringColumnProps( title, @@ -206,12 +208,12 @@ export function getExternallyLinkedFilterableLocatorColumnProps( ); } -export function getUuidColumnProps( +export function getUuidColumnProps( onFilterTextChange: ( key: keyof RecordType ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (uuid: string) => string + getPath: (uuid: Scalars["Uuid"]["output"]) => Route ) { return getInternallyLinkedFilterableStringColumnProps( "UUID", @@ -353,10 +355,10 @@ export function getDescriptionListColumnProps( key: keyof RecordType, getEntries: (record: RecordType) => | { - key: string; - title: string; - value: string | null | undefined; - }[] + key: string; + title: string; + value: string | null | undefined; + }[] | null | undefined ) { @@ -399,15 +401,15 @@ export function getFilterableDescriptionListColumnProps( key: keyof RecordType, getEntries: (record: RecordType) => | { - key: string; - title: string; - value: string | null | undefined; - render?: ( - record: RecordType, - highlightedValue: JSX.Element, - value: string | null | undefined - ) => JSX.Element; - }[] + key: string; + title: string; + value: string | null | undefined; + render?: ( + record: RecordType, + highlightedValue: JSX.Element, + value: string | null | undefined + ) => JSX.Element; + }[] | null | undefined, onFilterTextChange: ( @@ -470,8 +472,105 @@ export function getFilterableDescriptionListColumnProps( }; } +export function getReferenceColumnProps< + RecordType extends { reference?: Publication | Standard | null } +>( + onFilterTextChange: ( + key: keyof RecordType + ) => (newFilterText: string) => void, + getFilterText: (key: keyof RecordType) => string | undefined +) { + return getFilterableDescriptionListColumnProps( + "Reference", + "reference", + (record) => + record.reference && [ + { + key: "title", + title: "Title", + value: record.reference.title, + }, + { + key: "abstract", + title: "Abstract", + value: record.reference.abstract, + }, + { + key: "section", + title: "Section", + value: record.reference.section, + }, + ...(record.reference.__typename !== "Standard" + ? [] + : [ + { + key: "numeration", + title: "Numeration", + value: `${record.reference.numeration.prefix} ${record.reference.numeration.mainNumber} ${record.reference.numeration.suffix}`, + }, + { + key: "year", + title: "Year", + value: record.reference.year, + }, + { + key: "locator", + title: "Locator", + value: record.reference.locator, + render: ( + _record: RecordType, + hightlightedValue: JSX.Element, + value: string | null | undefined + ) => ( + // TODO Actually, `value` is neither `null` nor `undefined` but the type system does not know about it. How can we make it know about it so we don't need `|| ""` here? + ( + {hightlightedValue} + ) + ), + }, + { + key: "Standardizers", + title: "Standardizers", + value: record.reference.standardizers.join(", "), + }, + ]), + ...(record.reference.__typename !== "Publication" + ? [] + : [ + { + key: "arXiv", + title: "arXiv", + value: record.reference.arXiv, + }, + { + key: "doi", + title: "DOI", + value: record.reference.doi, + }, + { + key: "urn", + title: "URN", + value: record.reference.urn, + }, + { + key: "webAddress", + title: "Website", + value: record.reference.webAddress, + }, + { + key: "authors", + title: "Authors", + value: record.reference.authors?.join(", "), + }, + ]), + ], + onFilterTextChange, + getFilterText + ); +} + export function getComponentUuidColumnProps< - RecordType extends { componentId: Scalars["Uuid"] } + RecordType extends { componentId: Scalars["Uuid"]["output"] } >( onFilterTextChange: ( key: keyof RecordType @@ -489,7 +588,7 @@ export function getComponentUuidColumnProps< } export function getAppliedMethodColumnProps< - RecordType extends { appliedMethod: AppliedMethod } + RecordType extends { appliedMethod: { methodId: Scalars["Uuid"]["output"] } } >( onFilterTextChange: ( key: keyof RecordType @@ -529,7 +628,7 @@ export function getAppliedMethodColumnProps< } export function getResourceTreeColumnProps< - RecordType extends { resourceTree: GetHttpsResourceTree } + RecordType extends { resourceTree: { root: { value: { description?: string | null | undefined, hashValue: string, locator: Scalars["Url"]["output"], dataFormatId: Scalars["Uuid"]["output"] } } } } >( onFilterTextChange: ( key: keyof RecordType @@ -561,7 +660,7 @@ export function getResourceTreeColumnProps< ), }, { - key: "formatId", + key: "dataFormatId", title: "Data Format UUID", value: x.resourceTree.root.value.dataFormatId, render: (_record, hightlightedValue, _value) => ( diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 4f11a03d..19709046 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +import "./.next/types/routes.d.ts"; // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/next.config.js b/frontend/next.config.js deleted file mode 100644 index e4ad2f97..00000000 --- a/frontend/next.config.js +++ /dev/null @@ -1,37 +0,0 @@ -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, - swcMinify: true, - webpack: (config, options) => { - config.module.rules.push({ - test: /\.graphql$/, - exclude: /node_modules/, - use: [options.defaultLoaders.babel, { loader: "graphql-let/loader" }], - }); - - config.module.rules.push({ - test: /\.graphqls$/, - exclude: /node_modules/, - use: ["graphql-let/schema/loader"], - }); - - config.module.rules.push({ - test: /\.ya?ml$/, - type: "json", - use: "yaml-loader", - }); - - // Enable polling based on env variable being set - // Inspired by https://medium.com/mikkotikkanen/solving-next-js-fast-refresh-on-docker-windows-71dfdb3ee785 - if (process.env.NEXT_WEBPACK_USEPOLLING == "true" || false) { - config.watchOptions = { - poll: 500, - aggregateTimeout: 300 - } - } - - return config; - }, -} - -module.exports = nextConfig; diff --git a/frontend/next.config.ts b/frontend/next.config.ts new file mode 100644 index 00000000..c5ebc4ca --- /dev/null +++ b/frontend/next.config.ts @@ -0,0 +1,33 @@ +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + reactStrictMode: true, + typedRoutes: true, + allowedDevOrigins: + process.env.NEXT_PUBLIC_DATABASE_URL == null + ? [] + : [new URL(process.env.NEXT_PUBLIC_DATABASE_URL).hostname], + turbopack: { + rules: { + '/\.(yml|yaml$)/': [ + 'yaml-loader', + ], + }, + }, + experimental: { + typedEnv: true, + }, + // experimental: { + // swcPlugins: [ + // [ + // '@swc-contrib/plugin-graphql-codegen-client-preset', + // { + // artifactDirectory: './__generated__/', + // gqlTagName: 'graphql' + // } + // ] + // ] + // }, +} + +export default nextConfig diff --git a/frontend/package.json b/frontend/package.json index 7ba7f58d..de68f731 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,59 +1,55 @@ { "name": "frontend", - "version": "v1.0.0", + "version": "v1.1.0", "private": true, "author": "Simon Wacker", "license": "MIT", "scripts": { - "codegen": "graphql-let && graphql-codegen --config codegen.yml", - "dev": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' next dev --port 8080", + "codegen": "graphql-codegen --config ./codegen.ts", + "codegen:watch": "graphql-codegen --config ./codegen.ts --watch", + "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_OPTIONS=\"--use-openssl-ca\" next dev --port 8080'", "build": "yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", "start": "next start --port 8080" }, "dependencies": { "@ant-design/pro-layout": "^7.10.3", - "@apollo/client": "^3.7.8", + "@apollo/client": "^4.0.9", + "@parcel/watcher": "^2.5.1", "antd": "^5.0", - "crypto-hash": "^3.0.0", + "crypto-hash": "^4.0.0", "dayjs": "^1.0", - "graphql": "^16.0.1", - "next": "^13.0.0", - "react": "^18.3.1", - "react-cookie": "^7.1.4", - "react-dom": "^18.3.1", - "react-highlight-words": "^0.20.0", - "sharp": "^0.33.4" + "graphql": "^16.12.0", + "next": "16.0.3", + "react": "19.2.0", + "react-cookie": "^8.0.1", + "react-dom": "19.2.0", + "react-highlight-words": "^0.21.0", + "rxjs": "^7.8.2", + "sharp": "^0.34.5" }, "devDependencies": { "@babel/core": "^7.0", - "@graphql-codegen/cli": "^3.0.0", - "@graphql-codegen/fragment-matcher": "^4.0.0", - "@graphql-codegen/import-types-preset": "^2.2.6", - "@graphql-codegen/plugin-helpers": "^4.0.0", - "@graphql-codegen/typescript": "^3.0.0", - "@graphql-codegen/typescript-operations": "^3.0.0", - "@graphql-codegen/typescript-react-apollo": "^3.3.7", - "@graphql-codegen/typescript-resolvers": "^3.0.0", - "@graphql-tools/load-files": "^6.6.1", - "@graphql-tools/merge": "^8.3.18", - "@graphql-tools/schema": "^9.0.16", + "@graphql-codegen/cli": "^6.0.2", + "@graphql-codegen/fragment-matcher": "^6.0.0", + "@graphql-codegen/near-operation-file-preset": "^3.1.0", + "@graphql-codegen/typescript": "^5.0.4", + "@graphql-codegen/typescript-operations": "^5.0.4", "@types/node": "^24.10.1", - "@types/qrcode.react": "^1.0", - "@types/react": "^18.0", - "@types/react-dom": "^18.0", + "@types/react": "19.2.5", + "@types/react-dom": "19.2.3", "@types/react-highlight-words": "^0.20.0", - "@types/react-test-renderer": "^18.0", - "babel-jest": "^29.0", + "@types/react-test-renderer": "^19.1.0", + "babel-jest": "^30.2.0", + "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", - "eslint-config-next": "^13.0.0", - "graphql-let": "^0.18", + "eslint-config-next": "16.0.3", "graphql-tag": "^2.0", - "jest": "^29.0", + "jest": "^30.2.0", "license-checker": "^25.0.1", "prettier": "^3.3.2", - "react-test-renderer": "^18.2.0", + "react-test-renderer": "^19.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4", "yaml-loader": "^0.8" @@ -69,5 +65,9 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "resolutions": { + "@types/react": "19.2.5", + "@types/react-dom": "19.2.3" } } diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index fa2344e8..6df43cec 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,16 +1,25 @@ import { AppProps } from "next/app"; -import { ApolloProvider } from "@apollo/client"; import { useApollo } from "../lib/apollo"; +import { ApolloProvider } from "@apollo/client/react"; import { CookiesProvider } from "react-cookie"; +import { ConfigProvider, message } from "antd"; +import '../styles/global.css'; export default function App({ Component, pageProps }: AppProps) { const apolloClient = useApollo(pageProps.initialApolloState); + message.config({ + top: 100, + duration: 2, + }); + return ( - - - - - + + + + + + + ); } diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index c6f84448..75fccc82 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -8,12 +8,12 @@ import { Typography, Descriptions, } from "antd"; -import { useAllCalorimetricDataQuery } from "../../../queries/data.graphql"; +import { AllCalorimetricDataDocument } from "../../../queries/data.generated"; import { CalorimetricData, Scalars, CalorimetricDataPropositionInput, -} from "../../../__generated__/__types__"; +} from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { @@ -34,6 +34,7 @@ import { UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; const layout = { labelCol: { span: 8 }, @@ -97,7 +98,7 @@ function Page() { // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allCalorimetricDataQuery = useAllCalorimetricDataQuery({ + const allCalorimetricDataQuery = useQuery(AllCalorimetricDataDocument, { skip: true, errorPolicy: "all", }); @@ -112,33 +113,33 @@ function Page() { uValues, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; gValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; uValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -203,13 +204,13 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + } ); if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.graphQLErrors.map((error) => error.message)); + messageApi.error(error.message); } // TODO Casting to `CalorimetricData` is wrong and error prone! setData((data?.allCalorimetricData?.edges?.map((x) => x.node) || []) as CalorimetricData[]); diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index 1fb137d3..e78f9d4c 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -8,12 +8,12 @@ import { Typography, Descriptions, } from "antd"; -import { useAllGeometricDataQuery } from "../../../queries/data.graphql"; +import { AllGeometricDataDocument } from "../../../queries/data.generated"; import { GeometricData, Scalars, GeometricDataPropositionInput, -} from "../../../__generated__/__types__"; +} from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { @@ -34,6 +34,7 @@ import { UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; const layout = { labelCol: { span: 8 }, @@ -82,7 +83,7 @@ function Page() { const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); - const allGeometricDataQuery = useAllGeometricDataQuery({ + const allGeometricDataQuery = useQuery(AllGeometricDataDocument, { skip: true, errorPolicy: "all", }); @@ -99,14 +100,14 @@ function Page() { | { negator: Negator; comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; + value: Scalars["Uuid"]["input"] | undefined; }[] | undefined; dataFormatIds: | { negator: Negator; comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; + value: Scalars["Uuid"]["input"] | undefined; }[] | undefined; thicknesses: @@ -167,7 +168,7 @@ function Page() { ); if (error) { console.log(error); - messageApi.error(error.graphQLErrors.map((error) => error.message)); + messageApi.error(error.message); } setData((data?.allGeometricData?.edges?.map((x) => x.node) || []) as GeometricData[]); } catch (error) { diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 66eb6027..63cf93d3 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -1,11 +1,11 @@ import Layout from "../../../components/Layout"; import { Table, message, Form, Button, Alert, Typography } from "antd"; -import { useAllHygrothermalDataQuery } from "../../../queries/data.graphql"; +import { AllHygrothermalDataDocument } from "../../../queries/data.generated"; import { HygrothermalData, Scalars, HygrothermalDataPropositionInput, -} from "../../../__generated__/__types__"; +} from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { @@ -22,6 +22,7 @@ import { UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; const layout = { labelCol: { span: 8 }, @@ -85,7 +86,7 @@ function Page() { // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allHygrothermalDataQuery = useAllHygrothermalDataQuery({ + const allHygrothermalDataQuery = useQuery(AllHygrothermalDataDocument, { skip: true, errorPolicy: "all", }); @@ -98,19 +99,19 @@ function Page() { dataFormatIds, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -143,13 +144,13 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + } ); if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.graphQLErrors.map((error) => error.message)); + messageApi.error(error.message); } // TODO Casting to `HygrothermalData` is wrong and error prone! setData((data?.allHygrothermalData?.edges?.map((x) => x.node) || []) as HygrothermalData[]); diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index 38be023b..7eaa1b9d 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -8,12 +8,12 @@ import { Typography, Descriptions, } from "antd"; -import { useAllOpticalDataQuery } from "../../../queries/data.graphql"; +import { AllOpticalDataDocument } from "../../../queries/data.generated"; import { OpticalData, Scalars, OpticalDataPropositionInput, -} from "../../../__generated__/__types__"; +} from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { @@ -34,6 +34,7 @@ import { UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; const layout = { labelCol: { span: 8 }, @@ -97,7 +98,7 @@ function Page() { // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allOpticalDataQuery = useAllOpticalDataQuery({ + const allOpticalDataQuery = useQuery(AllOpticalDataDocument, { skip: true, errorPolicy: "all", }); @@ -115,54 +116,54 @@ function Page() { nearnormalHemisphericalVisibleTransmittances, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; infraredEmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalSolarReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalSolarTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalVisibleReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalVisibleTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -288,13 +289,13 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + } ); if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.graphQLErrors.map((error) => error.message)); + messageApi.error(error.message); } // TODO Casting to `OpticalData` is wrong and error prone! setData((data?.allOpticalData?.edges?.map((x) => x.node) || []) as OpticalData[]); diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index 3b116c4b..88258c3c 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -1,11 +1,11 @@ import Layout from "../../../components/Layout"; import { Table, message, Form, Button, Alert, Typography } from "antd"; -import { useAllPhotovoltaicDataQuery } from "../../../queries/data.graphql"; +import { AllPhotovoltaicDataDocument } from "../../../queries/data.generated"; import { PhotovoltaicData, Scalars, PhotovoltaicDataPropositionInput, -} from "../../../__generated__/__types__"; +} from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { @@ -22,6 +22,7 @@ import { UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; const layout = { labelCol: { span: 8 }, @@ -85,7 +86,7 @@ function Page() { // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allPhotovoltaicDataQuery = useAllPhotovoltaicDataQuery({ + const allPhotovoltaicDataQuery = useQuery(AllPhotovoltaicDataDocument, { skip: true, errorPolicy: "all", }); @@ -101,14 +102,14 @@ function Page() { | { negator: Negator; comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; + value: Scalars["Uuid"]["input"] | undefined; }[] | undefined; dataFormatIds: | { negator: Negator; comparator: UuidPropositionComparator; - value: Scalars["Uuid"] | undefined; + value: Scalars["Uuid"]["input"] | undefined; }[] | undefined; }) => { @@ -149,7 +150,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.graphQLErrors.map((error) => error.message)); + messageApi.error(error.message); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! setData((data?.allPhotovoltaicData?.edges?.map((x) => x.node) || []) as PhotovoltaicData[]); diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 50ba0030..c4f7e6f8 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,12 +1,13 @@ +import { useQuery } from '@apollo/client/react'; import Layout from "../components/Layout"; -import { useCurrentUserInfoQuery } from "../queries/currentUser.graphql"; +import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; import { Skeleton, Result, Descriptions, Typography } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { useEffect } from "react"; import { messageApolloError } from "../lib/apollo"; function Page() { - const { loading, error, data } = useCurrentUserInfoQuery(); + const { loading, error, data } = useQuery(CurrentUserInfoDocument); const currentUserInfo = data?.currentUserInfo; useEffect(() => { diff --git a/frontend/paths.ts b/frontend/paths.ts index 24f8617e..ac7576da 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -1,66 +1,68 @@ +import { Route } from "next"; + export default { - home: "/", - legalNotice: "/legal-notice", - dataProtectionInformation: "/data-protection-information", - antiforgeryToken: "/antiforgery/token", - userInfo: "/user-info", - database: "/database", - calorimetricData: "/data/calorimetric", + home: "/" as Route, + legalNotice: "/legal-notice" as Route, + dataProtectionInformation: "/data-protection-information" as Route, + antiforgeryToken: "/antiforgery/token" as Route, + userInfo: "/user-info" as Route, + database: "/database" as Route, + calorimetricData: "/data/calorimetric" as Route, calorimetricDatum(uuid: string) { - return `/data/calorimetric/${encodeURIComponent(uuid)}`; + return `/data/calorimetric/${encodeURIComponent(uuid)}` as Route; }, - hygrothermalData: "/data/hygrothermal", + hygrothermalData: "/data/hygrothermal" as Route, hygrothermalDatum(uuid: string) { - return `/data/hygrothermal/${encodeURIComponent(uuid)}`; + return `/data/hygrothermal/${encodeURIComponent(uuid)}` as Route; }, - opticalData: "/data/optical", + opticalData: "/data/optical" as Route, opticalDatum(uuid: string) { - return `/data/optical/${encodeURIComponent(uuid)}`; + return `/data/optical/${encodeURIComponent(uuid)}` as Route; }, - photovoltaicData: "/data/photovoltaic", + photovoltaicData: "/data/photovoltaic" as Route, photovoltaicDatum(uuid: string) { - return `/data/photovoltaic/${encodeURIComponent(uuid)}`; + return `/data/photovoltaic/${encodeURIComponent(uuid)}` as Route; }, - geometricData: "/data/geometric", + geometricData: "/data/geometric" as Route, geometricDatum(uuid: string) { - return `/data/geometric/${encodeURIComponent(uuid)}`; + return `/data/geometric/${encodeURIComponent(uuid)}` as Route; }, - createData: "/data/create", - uploadFile: "/upload-file", - login: "/connect/login", - logout: "/connect/logout", - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href, + createData: "/data/create" as Route, + uploadFile: "/upload-file" as Route, + login: "/connect/login" as Route, + logout: "/connect/logout" as Route, + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href as Route, metabase: { home: process.env.NEXT_PUBLIC_METABASE_URL, - components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL).href, + components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, component(uuid: string) { - return new URL(`/components/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/components/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL).href, + databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, database(uuid: string) { - return new URL(`/databases/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/databases/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL).href, + dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, dataFormat(uuid: string) { - return new URL(`/data-formats/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/data-formats/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL).href, + institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, institution(uuid: string) { - return new URL(`/institutions/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/institutions/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL).href, + methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, method(uuid: string) { - return new URL(`/methods/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/methods/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL).href, + users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, user(uuid: string) { - return new URL(`/users/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href + return new URL(`/users/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route }, - allCalorimetricData: new URL(`/data/calorimetric`, process.env.NEXT_PUBLIC_METABASE_URL).href, - allGeometricData: new URL(`/data/geometric`, process.env.NEXT_PUBLIC_METABASE_URL).href, - allHygrothermalData: new URL(`/data/hygrothermal`, process.env.NEXT_PUBLIC_METABASE_URL).href, - allOpticalData: new URL(`/data/optical`, process.env.NEXT_PUBLIC_METABASE_URL).href, - allPhotovoltaicData: new URL(`/data/photovoltaic`, process.env.NEXT_PUBLIC_METABASE_URL).href, - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL).href, + allCalorimetricData: new URL(`/data/calorimetric`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + allGeometricData: new URL(`/data/geometric`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + allHygrothermalData: new URL(`/data/hygrothermal`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + allOpticalData: new URL(`/data/optical`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + allPhotovoltaicData: new URL(`/data/photovoltaic`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, }, }; diff --git a/frontend/queries/currentUser.generated.ts b/frontend/queries/currentUser.generated.ts new file mode 100644 index 00000000..08712ae9 --- /dev/null +++ b/frontend/queries/currentUser.generated.ts @@ -0,0 +1,18 @@ +import type * as Types from '../__generated__/graphql'; + +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type CurrentUserPartialFragment = { __typename: 'User', name: string }; + +export type CurrentUserQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type CurrentUserQuery = { currentUser: { __typename: 'User', name: string } | null }; + +export type CurrentUserInfoQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type CurrentUserInfoQuery = { currentUserInfo: { __typename: 'UserInfo', email: string, emailVerified: boolean, name: string, phoneNumber: string | null, phoneNumberVerified: boolean, roles: Array | null, sub: string, website: string | null, address: { __typename: 'Address', formatted: string } | null } }; + +export const CurrentUserPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CurrentUserPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode; +export const CurrentUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CurrentUserPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CurrentUserPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode; +export const CurrentUserInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentUserInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentUserInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"address"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"formatted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"emailVerified"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phoneNumber"}},{"kind":"Field","name":{"kind":"Name","value":"phoneNumberVerified"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"sub"}},{"kind":"Field","name":{"kind":"Name","value":"website"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/queries/data.generated.ts b/frontend/queries/data.generated.ts new file mode 100644 index 00000000..367a0ffd --- /dev/null +++ b/frontend/queries/data.generated.ts @@ -0,0 +1,177 @@ +import type * as Types from '../__generated__/graphql'; + +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type DataPartial_CalorimetricData_Fragment = { __typename: 'CalorimetricData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type DataPartial_GeometricData_Fragment = { __typename: 'GeometricData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type DataPartial_HygrothermalData_Fragment = { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type DataPartial_OpticalData_Fragment = { __typename: 'OpticalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type DataPartial_PhotovoltaicData_Fragment = { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type DataPartialFragment = + | DataPartial_CalorimetricData_Fragment + | DataPartial_GeometricData_Fragment + | DataPartial_HygrothermalData_Fragment + | DataPartial_OpticalData_Fragment + | DataPartial_PhotovoltaicData_Fragment +; + +export type CalorimetricDataPartialFragment = { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type CalorimetricDataQueryVariables = Types.Exact<{ + uuid: Types.Scalars['Uuid']['input']; +}>; + + +export type CalorimetricDataQuery = { calorimetricData: { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; + +export type AllCalorimetricDataQueryVariables = Types.Exact<{ + where?: Types.InputMaybe; +}>; + + +export type AllCalorimetricDataQuery = { allCalorimetricData: { __typename: 'CalorimetricDataConnection', edges: Array<{ __typename: 'CalorimetricDataEdge', node: { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; + +export type HygrothermalDataPartialFragment = { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type HygrothermalDataQueryVariables = Types.Exact<{ + uuid: Types.Scalars['Uuid']['input']; +}>; + + +export type HygrothermalDataQuery = { hygrothermalData: { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; + +export type AllHygrothermalDataQueryVariables = Types.Exact<{ + where?: Types.InputMaybe; +}>; + + +export type AllHygrothermalDataQuery = { allHygrothermalData: { __typename: 'HygrothermalDataConnection', edges: Array<{ __typename: 'HygrothermalDataEdge', node: { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; + +export type OpticalDataPartialFragment = { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type OpticalDataQueryVariables = Types.Exact<{ + uuid: Types.Scalars['Uuid']['input']; +}>; + + +export type OpticalDataQuery = { opticalData: { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; + +export type AllOpticalDataQueryVariables = Types.Exact<{ + where?: Types.InputMaybe; +}>; + + +export type AllOpticalDataQuery = { allOpticalData: { __typename: 'OpticalDataConnection', edges: Array<{ __typename: 'OpticalDataEdge', node: { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; + +export type PhotovoltaicDataPartialFragment = { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type PhotovoltaicDataQueryVariables = Types.Exact<{ + uuid: Types.Scalars['Uuid']['input']; +}>; + + +export type PhotovoltaicDataQuery = { photovoltaicData: { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; + +export type AllPhotovoltaicDataQueryVariables = Types.Exact<{ + where?: Types.InputMaybe; +}>; + + +export type AllPhotovoltaicDataQuery = { allPhotovoltaicData: { __typename: 'PhotovoltaicDataConnection', edges: Array<{ __typename: 'PhotovoltaicDataEdge', node: { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; + +export type GeometricDataPartialFragment = { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; + +export type GeometricDataQueryVariables = Types.Exact<{ + uuid: Types.Scalars['Uuid']['input']; +}>; + + +export type GeometricDataQuery = { geometricData: { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; + +export type AllGeometricDataQueryVariables = Types.Exact<{ + where?: Types.InputMaybe; +}>; + + +export type AllGeometricDataQuery = { allGeometricData: { __typename: 'GeometricDataConnection', edges: Array<{ __typename: 'GeometricDataEdge', node: { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: + | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } + | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } + | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; + +export const DataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const CalorimetricDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const HygrothermalDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const OpticalDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const PhotovoltaicDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const GeometricDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; +export const CalorimetricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CalorimetricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calorimetricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalorimetricDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}}]} as unknown as DocumentNode; +export const AllCalorimetricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllCalorimetricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allCalorimetricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalorimetricDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}}]} as unknown as DocumentNode; +export const HygrothermalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"HygrothermalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hygrothermalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"HygrothermalDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; +export const AllHygrothermalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllHygrothermalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allHygrothermalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"HygrothermalDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; +export const OpticalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"OpticalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"opticalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OpticalDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}}]} as unknown as DocumentNode; +export const AllOpticalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllOpticalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allOpticalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OpticalDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}}]} as unknown as DocumentNode; +export const PhotovoltaicDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PhotovoltaicData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"photovoltaicData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PhotovoltaicDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; +export const AllPhotovoltaicDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPhotovoltaicData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allPhotovoltaicData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PhotovoltaicDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; +export const GeometricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GeometricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"geometricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"GeometricDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}}]} as unknown as DocumentNode; +export const AllGeometricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllGeometricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allGeometricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"GeometricDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/queries/databases.generated.ts b/frontend/queries/databases.generated.ts new file mode 100644 index 00000000..05fa972c --- /dev/null +++ b/frontend/queries/databases.generated.ts @@ -0,0 +1,23 @@ +import type * as Types from '../__generated__/graphql'; + +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type DatabasePartialFragment = { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean }; + +export type DatabaseQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type DatabaseQuery = { database: { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean } }; + +export type UpdateDatabaseMutationVariables = Types.Exact<{ + databaseId: Types.Scalars['Uuid']['input']; + name: Types.Scalars['String']['input']; + description: Types.Scalars['String']['input']; + locator: Types.Scalars['Url']['input']; +}>; + + +export type UpdateDatabaseMutation = { updateDatabase: { __typename: 'UpdateDatabasePayload', database: { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean } | null, errors: Array<{ __typename: 'UpdateDatabaseError', code: Types.UpdateDatabaseErrorCode, message: string, path: Array }> | null } }; + +export const DatabasePartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; +export const DatabaseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DatabasePartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; +export const UpdateDatabaseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateDatabase"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"databaseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"description"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locator"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Url"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateDatabase"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"databaseId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"databaseId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"description"},"value":{"kind":"Variable","name":{"kind":"Name","value":"description"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"locator"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locator"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DatabasePartial"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/styles/global.css b/frontend/styles/global.css new file mode 100644 index 00000000..7c476937 --- /dev/null +++ b/frontend/styles/global.css @@ -0,0 +1,8 @@ +.message { + margin-top: 10hv; +} + +.error-message { + margin-top: 5px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/frontend/test/index.test.tsx b/frontend/test/index.test.tsx index f200c774..c74db378 100644 --- a/frontend/test/index.test.tsx +++ b/frontend/test/index.test.tsx @@ -1,37 +1,37 @@ -import { InMemoryCache, gql } from "@apollo/client"; -import React from "react"; -import Index from "../pages"; -import renderer from "react-test-renderer"; -import { MockedProvider } from "@apollo/client/testing"; +// import { InMemoryCache, gql } from "@apollo/client"; +// import React from "react"; +// import Index from "../pages"; +// import renderer from "react-test-renderer"; +// import { MockedProvider } from "@apollo/client/testing"; -const cache = new InMemoryCache(); -cache.writeQuery({ - query: gql` - query Viewer { - viewer { - id - name - status - } - } - `, - data: { - viewer: { - __typename: "User", - id: "Baa", - name: "Baa", - status: "Healthy", - }, - }, -}); +// const cache = new InMemoryCache(); +// cache.writeQuery({ +// query: gql` +// query Viewer { +// viewer { +// id +// name +// status +// } +// } +// `, +// data: { +// viewer: { +// __typename: "User", +// id: "Baa", +// name: "Baa", +// status: "Healthy", +// }, +// }, +// }); -describe("Index", () => { - it("renders the html we want", async () => { - const component = renderer.create( - - - - ); - expect(component.toJSON()).toMatchSnapshot(); - }); -}); +// describe("Index", () => { +// it("renders the html we want", async () => { +// const component = renderer.create( +// +// +// +// ); +// expect(component.toJSON()).toMatchSnapshot(); +// }); +// }); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 2128691b..52bbc88e 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -5,8 +5,11 @@ "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, - "jsx": "preserve", - "lib": ["dom", "es2017"], + "jsx": "react-jsx", + "lib": [ + "dom", + "es2017" + ], "module": "esnext", "moduleResolution": "node", "noEmit": true, @@ -18,7 +21,15 @@ "strict": true, "target": "esnext", "incremental": true + // "verbatimModuleSyntax": true }, - "exclude": ["node_modules"], - "include": ["**/*.ts", "**/*.tsx"] -} + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 3f98f8ae..569d1cca 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,34 +2,26 @@ # yarn lockfile v1 -"@ampproject/remapping@^2.2.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" - integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== - dependencies: - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.24" - -"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.1.0.tgz#60eadfa2e21871d8948dac5d50b9f056062f8af3" - integrity sha512-MMoDGWn1y9LdQJQSHiCC20x3uZ3CwQnv9QMz6pCmJOrqdgM9YxsoVVY0wtrdXbmfSgnV0KNk6zi09NAhMR2jvg== +"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.2.1": + version "7.2.1" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.2.1.tgz#3bbc1c6c18550020d1622a0067ff03492318df98" + integrity sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== dependencies: - "@ctrl/tinycolor" "^3.6.1" + "@ant-design/fast-color" "^2.0.6" -"@ant-design/cssinjs-utils@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.1.tgz#57abb43671023f937348bd33442862c60ac8e8b2" - integrity sha512-2HAiyGGGnM0es40SxdszeQAU5iWp41wBIInq+ONTCKjlSKOrzQfnw4JDtB8IBmqE6tQaEKwmzTP2LGdt5DSwYQ== +"@ant-design/cssinjs-utils@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" + integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== dependencies: "@ant-design/cssinjs" "^1.21.0" "@babel/runtime" "^7.23.2" rc-util "^5.38.0" -"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1": - version "1.21.1" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.21.1.tgz#7320813c5f747e0cde52c388eff5198d78d57230" - integrity sha512-tyWnlK+XH7Bumd0byfbCiZNK43HEubMoCcu9VxwsAwiHdHTgWa+tMN0/yvxa+e8EzuFP1WdUNNPclRpVtD33lg== +"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1", "@ant-design/cssinjs@^1.23.0": + version "1.24.0" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz#7db091f03f189abc77a13cbd27a2293802cd7285" + integrity sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg== dependencies: "@babel/runtime" "^7.11.1" "@emotion/hash" "^0.8.0" @@ -37,7 +29,7 @@ classnames "^2.3.1" csstype "^3.1.3" rc-util "^5.35.0" - stylis "^4.3.3" + stylis "^4.3.4" "@ant-design/fast-color@^2.0.6": version "2.0.6" @@ -51,10 +43,10 @@ resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== -"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.5.1.tgz#4ff57b2a0d3bafae3d990c2781fd857ead36c935" - integrity sha512-0UrM02MA2iDIgvLatWrj6YTCYe0F/cwXvVE0E2SqGrL7PZireQwgEKTKBisWpZyal5eXZLvuM98kju6YtYne8w== +"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.6.1.tgz#7290fcdc3d96ff3fca793ed399053cd29ad5dbd3" + integrity sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== dependencies: "@ant-design/colors" "^7.0.0" "@ant-design/icons-svg" "^4.4.0" @@ -63,31 +55,30 @@ rc-util "^5.31.1" "@ant-design/pro-layout@^7.10.3": - version "7.21.1" - resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.21.1.tgz#1e686e14c6ee03f4d8b2bfa06b0954c032cc745a" - integrity sha512-9B+MBPTYZbhe5xdUxgfikoUZtB/77R3hAve8iI4fVNBoc4KAm/wpejasvKgWZn/8gRht8KOXfmfe2ngjz/BWOA== + version "7.22.7" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz#7002b1838e1cdf879bbec813677b478c26fbbb77" + integrity sha512-fvmtNA1r9SaasVIQIQt611VSlNxtVxDbQ3e+1GhYQza3tVJi/3gCZuDyfMfTnbLmf3PaW/YvLkn7MqDbzAzoLA== dependencies: "@ant-design/cssinjs" "^1.21.1" "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.15.1" - "@ant-design/pro-utils" "2.16.1" + "@ant-design/pro-provider" "2.16.2" + "@ant-design/pro-utils" "2.18.0" "@babel/runtime" "^7.18.0" "@umijs/route-utils" "^4.0.0" "@umijs/use-params" "^1.0.9" classnames "^2.3.2" lodash "^4.17.21" lodash-es "^4.17.21" - omit.js "^2.0.2" - path-to-regexp "8.0.0" + path-to-regexp "8.2.0" rc-resize-observer "^1.1.0" rc-util "^5.0.6" swr "^2.0.0" warning "^4.0.3" -"@ant-design/pro-provider@2.15.1": - version "2.15.1" - resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.15.1.tgz#5937a6a91d490792de372bdfa838889b3177537c" - integrity sha512-UiNhQ3Yl+h8liC24LLWpqbcdhC9ggiXvHeCYrqBHjOx2WNFWpWO5JP0327k3RQjShprt+jZQ/cdNHx6s7BToXA== +"@ant-design/pro-provider@2.16.2": + version "2.16.2" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.16.2.tgz#3ccec06ea9a69a4d48adc593a268413c813dab2f" + integrity sha512-0KmCH1EaOND787Jz6VRMYtLNZmqfT0JPjdUfxhyOxFfnBRfrjyfZgIa6CQoAJLEUMWv57PccWS8wRHVUUk2Yiw== dependencies: "@ant-design/cssinjs" "^1.21.1" "@babel/runtime" "^7.18.0" @@ -96,13 +87,13 @@ rc-util "^5.0.1" swr "^2.0.0" -"@ant-design/pro-utils@2.16.1": - version "2.16.1" - resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.16.1.tgz#810b30feb2cde58abe9d0b07929b89724dde73c2" - integrity sha512-GaBEgP638fvddO0YqevuIT3O8pQIrqfHhlQ4UDnf1JkhVxuVxP3XdwDituQWnyRoxLPSjNBZBOBghBBMb8nWsg== +"@ant-design/pro-utils@2.18.0": + version "2.18.0" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.18.0.tgz#50a6bcc95742b71a7a17252f7db6c2ccd2ec60f4" + integrity sha512-8+ikyrN8L8a8Ph4oeHTOJEiranTj18+9+WHCHjKNdEfukI7Rjn8xpYdLJWb2AUJkb9d4eoAqjd5+k+7w81Df0w== dependencies: "@ant-design/icons" "^5.0.0" - "@ant-design/pro-provider" "2.15.1" + "@ant-design/pro-provider" "2.16.2" "@babel/runtime" "^7.18.0" classnames "^2.3.2" dayjs "^1.11.10" @@ -123,32 +114,18 @@ resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" -"@apollo/client@^3.7.8": - version "3.11.8" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.11.8.tgz#f6bacdc7e1b243807c1387113e1d445a53471a9c" - integrity sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA== +"@apollo/client@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.0.9.tgz#fdc0388f73a1640f3da30a8791e448b96ad0b49a" + integrity sha512-Lh2drMzFE9x5jVS8RKmlGL5SORkvpyUJIT+wTErxDUR2HpWePiBfhhcHHRSlZFiCR866ewCv4euTc4IDF0GWxw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" "@wry/equality" "^0.5.6" "@wry/trie" "^0.5.0" graphql-tag "^2.12.6" - hoist-non-react-statics "^3.3.2" optimism "^0.18.0" - prop-types "^15.7.2" - rehackt "^0.1.0" - response-iterator "^0.2.6" - symbol-observable "^4.0.0" - ts-invariant "^0.10.3" tslib "^2.3.0" - zen-observable-ts "^1.2.5" - -"@ardatan/aggregate-error@0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" - integrity sha512-vyrkEHG1jrukmzTPtyWB4NLPauUw5bQeg4uhn8f+1SSynmrOcyvlb1GKQjjgoBzElLdfXCRYX8UnBlhklOHYRQ== - dependencies: - tslib "~2.0.1" "@ardatan/relay-compiler@12.0.0": version "12.0.0" @@ -173,234 +150,187 @@ signedsource "^1.0.0" yargs "^15.3.1" -"@ardatan/sync-fetch@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" - integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== +"@ardatan/relay-compiler@^12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz#a60824672da7f7cef2a3879ed833b120d292fa08" + integrity sha512-mBDFOGvAoVlWaWqs3hm1AciGHSQE1rqFc/liZTyYz/Oek9yZdT5H26pH2zAFuEiTiBVPPyMuqf5VjOFPI2DGsQ== dependencies: - node-fetch "^2.6.1" + "@babel/generator" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/runtime" "^7.26.10" + chalk "^4.0.0" + fb-watchman "^2.0.0" + immutable "~3.7.6" + invariant "^2.2.4" + nullthrows "^1.1.1" + relay-runtime "12.0.0" + signedsource "^1.0.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: - "@babel/helper-validator-identifier" "^7.25.9" + "@babel/helper-validator-identifier" "^7.27.1" js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.25.9": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e" - integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== - -"@babel/core@7.15.5": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.15.5.tgz#f8ed9ace730722544609f90c9bb49162dc3bf5b9" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/core@^7.0", "@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.14.0", "@babel/core@^7.23.9": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" + picocolors "^1.1.1" + +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" + integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== + +"@babel/core@^7.0", "@babel/core@^7.14.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" + integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.5" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-module-transforms" "^7.28.3" + "@babel/helpers" "^7.28.4" + "@babel/parser" "^7.28.5" + "@babel/template" "^7.27.2" + "@babel/traverse" "^7.28.5" + "@babel/types" "^7.28.5" + "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.15.4", "@babel/generator@^7.18.13", "@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.2.tgz#87b75813bec87916210e5e01939a4c823d6bb74f" - integrity sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw== +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.26.10", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" + integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== dependencies: - "@babel/parser" "^7.26.2" - "@babel/types" "^7.26.0" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" + "@babel/parser" "^7.28.5" + "@babel/types" "^7.28.5" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" - integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== +"@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": + version "7.27.3" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" + integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== dependencies: - "@babel/types" "^7.25.9" + "@babel/types" "^7.27.3" -"@babel/helper-compilation-targets@^7.15.4", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" - integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: - "@babel/compat-data" "^7.25.9" - "@babel/helper-validator-option" "^7.25.9" + "@babel/compat-data" "^7.27.2" + "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" "@babel/helper-create-class-features-plugin@^7.18.6": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" - integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/traverse" "^7.25.9" + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" + integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-member-expression-to-functions" "^7.28.5" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" + "@babel/traverse" "^7.28.5" semver "^6.3.1" -"@babel/helper-function-name@^7.15.4": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz#75f1e1725742f39ac6584ee0b16d94513da38dd2" - integrity sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA== - dependencies: - "@babel/template" "^7.24.7" - "@babel/types" "^7.24.7" +"@babel/helper-globals@^7.28.0": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-hoist-variables@^7.15.4": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz#b4ede1cde2fd89436397f30dc9376ee06b0f25ee" - integrity sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ== +"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== dependencies: - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.28.5" + "@babel/types" "^7.28.5" -"@babel/helper-member-expression-to-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" - integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" -"@babel/helper-module-imports@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" - integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== +"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": + version "7.28.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@babel/traverse" "^7.28.3" -"@babel/helper-module-transforms@^7.15.4", "@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" - integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== +"@babel/helper-optimise-call-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/types" "^7.27.1" -"@babel/helper-optimise-call-expression@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" - integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== - dependencies: - "@babel/types" "^7.25.9" +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== -"@babel/helper-plugin-utils@7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" - integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ== - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" - integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== - -"@babel/helper-replace-supers@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" - integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== +"@babel/helper-replace-supers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== dependencies: - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.27.1" + "@babel/helper-optimise-call-expression" "^7.27.1" + "@babel/traverse" "^7.27.1" -"@babel/helper-simple-access@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" - integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== +"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" -"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" - integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-split-export-declaration@^7.15.4": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" - integrity sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA== - dependencies: - "@babel/types" "^7.24.7" +"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== -"@babel/helper-string-parser@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" - integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== +"@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== - -"@babel/helper-validator-option@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" - integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== - -"@babel/helpers@^7.15.4", "@babel/helpers@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" - integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== +"@babel/helpers@^7.28.4": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== dependencies: - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.0" - -"@babel/parser@7.15.5": - version "7.15.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.5.tgz#d33a58ca69facc05b26adfe4abebfed56c1c2dac" - integrity sha512-2hQstc6I7T6tQsWzlboMh3SgMRPaS4H6H7cPQsJkdzTzEGqQrpLDsE2BGASU5sBPoEQyHzeqU6C8uKbFeEk6sg== + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.4" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.15.4", "@babel/parser@^7.15.5", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.26.2": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.2.tgz#fd7b6f487cfea09889557ef5d4eeb9ff9a5abd11" - integrity sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: - "@babel/types" "^7.26.0" + "@babel/types" "^7.28.5" "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" @@ -449,26 +379,26 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.25.9": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz#96507595c21b45fccfc2bc758d5c45452e6164fa" - integrity sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg== +"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz#6c83cf0d7d635b716827284b7ecd5aead9237662" + integrity sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" - integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== +"@babel/plugin-syntax-import-assertions@^7.26.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-attributes@^7.24.7": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" - integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" @@ -484,12 +414,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.25.9", "@babel/plugin-syntax-jsx@^7.7.2": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" - integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== +"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" @@ -547,222 +477,205 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" - integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== +"@babel/plugin-syntax-typescript@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" - integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" - integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-block-scoping@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" - integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" + integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-classes@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" - integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" + integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/traverse" "^7.25.9" - globals "^11.1.0" + "@babel/helper-annotate-as-pure" "^7.27.3" + "@babel/helper-compilation-targets" "^7.27.2" + "@babel/helper-globals" "^7.28.0" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" + "@babel/traverse" "^7.28.4" "@babel/plugin-transform-computed-properties@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" - integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/template" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/template" "^7.27.1" "@babel/plugin-transform-destructuring@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" - integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" + integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.28.5" "@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.9.tgz#85879b42a8f5948fd6317069978e98f23ef8aec1" - integrity sha512-/VVukELzPDdci7UUsWQaSkhgnjIWXnIyRpM02ldxaVoFK96c41So8JcKT3m0gYjyv7j5FNPGS5vfELrWalkbDA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz#5def3e1e7730f008d683144fb79b724f92c5cdf9" + integrity sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-syntax-flow" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-syntax-flow" "^7.27.1" "@babel/plugin-transform-for-of@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" - integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-function-name@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" - integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/helper-compilation-targets" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/traverse" "^7.27.1" "@babel/plugin-transform-literals@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" - integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" - integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686" - integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-simple-access" "^7.25.9" + "@babel/helper-module-transforms" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-object-super@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" - integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-replace-supers" "^7.27.1" "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" - integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== + version "7.27.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz#1fd2febb7c74e7d21cf3b05f7aebc907940af53a" + integrity sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-property-literals@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" - integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-display-name@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.9.tgz#4b79746b59efa1f38c8695065a92a9f5afb24f7d" - integrity sha512-KJfMlYIUxQB1CJfO3e0+h0ZHWOTLCPP115Awhaz8U0Zpq36Gl/cXlpoyMRnUWlhNUBAzldnCiAZNvCDj7CrKxQ== + version "7.28.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz#6f20a7295fea7df42eb42fed8f896813f5b934de" + integrity sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-jsx@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz#06367940d8325b36edff5e2b9cbe782947ca4166" - integrity sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz#1023bc94b78b0a2d68c82b5e96aed573bcfb9db0" + integrity sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw== dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-syntax-jsx" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/helper-annotate-as-pure" "^7.27.1" + "@babel/helper-module-imports" "^7.27.1" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/plugin-syntax-jsx" "^7.27.1" + "@babel/types" "^7.27.1" "@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" - integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-spread@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" - integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" "@babel/plugin-transform-template-literals@^7.0.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" - integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.6": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1" - integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.15.4", "@babel/template@^7.18.10", "@babel/template@^7.24.7", "@babel/template@^7.25.9", "@babel/template@^7.3.3": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== - dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/traverse@7.15.4": - version "7.15.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.14.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.16.8", "@babel/traverse@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" - integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== - dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/generator" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/template" "^7.25.9" - "@babel/types" "^7.25.9" + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== + dependencies: + "@babel/helper-plugin-utils" "^7.27.1" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10": + version "7.28.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== + +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.1", "@babel/template@^7.27.2": + version "7.27.2" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.2" + "@babel/types" "^7.27.1" + +"@babel/traverse@^7.14.0", "@babel/traverse@^7.26.10", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" + integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/generator" "^7.28.5" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.28.5" + "@babel/template" "^7.27.2" + "@babel/types" "^7.28.5" debug "^4.3.1" - globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.3.3": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" - integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== +"@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5": + version "7.28.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" "@bcoe/v8-coverage@^0.2.3": version "0.2.3" @@ -776,15 +689,30 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@ctrl/tinycolor@^3.4.0", "@ctrl/tinycolor@^3.6.1": +"@ctrl/tinycolor@^3.4.0": version "3.6.1" resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== -"@emnapi/runtime@^1.2.0": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.3.1.tgz#0fcaa575afc31f455fd33534c19381cfce6c6f60" - integrity sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw== +"@emnapi/core@^1.4.3": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4" + integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg== + dependencies: + "@emnapi/wasi-threads" "1.1.0" + tslib "^2.4.0" + +"@emnapi/runtime@^1.4.3", "@emnapi/runtime@^1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791" + integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" + integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== dependencies: tslib "^2.4.0" @@ -798,36 +726,71 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint-community/eslint-utils@^4.2.0": - version "4.4.1" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" - integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== +"@envelop/core@^5.2.3", "@envelop/core@^5.3.0", "@envelop/core@^5.4.0": + version "5.4.0" + resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.4.0.tgz#e79b8da5ab4f05eccd9758435df3dc50b11e607d" + integrity sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ== + dependencies: + "@envelop/instrumentation" "^1.0.0" + "@envelop/types" "^5.2.1" + "@whatwg-node/promise-helpers" "^1.2.4" + tslib "^2.5.0" + +"@envelop/instrumentation@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@envelop/instrumentation/-/instrumentation-1.0.0.tgz#43268392e065d8ba975cacbdf4fc297dfe3e11e5" + integrity sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw== + dependencies: + "@whatwg-node/promise-helpers" "^1.2.1" + tslib "^2.5.0" + +"@envelop/types@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@envelop/types/-/types-5.2.1.tgz#6bc9713f2aea56d7de3ea39e8bb70035c0475b36" + integrity sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg== + dependencies: + "@whatwg-node/promise-helpers" "^1.0.0" + tslib "^2.5.0" + +"@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": + version "4.12.2" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== -"@eslint/config-array@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.18.0.tgz#37d8fe656e0d5e3dbaea7758ea56540867fd074d" - integrity sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw== +"@eslint/config-array@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: - "@eslint/object-schema" "^2.1.4" + "@eslint/object-schema" "^2.1.7" debug "^4.3.1" minimatch "^3.1.2" -"@eslint/core@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.7.0.tgz#a1bb4b6a4e742a5ff1894b7ee76fbf884ec72bd3" - integrity sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw== +"@eslint/config-helpers@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== + dependencies: + "@eslint/core" "^0.17.0" -"@eslint/eslintrc@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.1.0.tgz#dbd3482bfd91efa663cbe7aa1f506839868207b6" - integrity sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ== +"@eslint/core@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -839,23 +802,29 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@9.14.0": - version "9.14.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.14.0.tgz#2347a871042ebd11a00fd8c2d3d56a265ee6857e" - integrity sha512-pFoEtFWCPyDOl+C6Ift+wC7Ro89otjigCf5vcuWqWgqNSQbRrpjSvdeE6ofLz4dHmyxD5f7gIdGT4+p36L6Twg== +"@eslint/js@9.39.1": + version "9.39.1" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" + integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== -"@eslint/object-schema@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.4.tgz#9e69f8bb4031e11df79e03db09f9dbbae1740843" - integrity sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ== +"@eslint/object-schema@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== -"@eslint/plugin-kit@^0.2.0": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz#5eff371953bc13e3f4d88150e2c53959f64f74f6" - integrity sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw== +"@eslint/plugin-kit@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: + "@eslint/core" "^0.17.0" levn "^0.4.1" +"@fastify/busboy@^3.1.1": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" + integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== + "@graphql-codegen/add@^3.2.1": version "3.2.3" resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.2.3.tgz#f1ecee085987e7c21841edc4b1fd48877c663e1a" @@ -864,99 +833,115 @@ "@graphql-codegen/plugin-helpers" "^3.1.1" tslib "~2.4.0" -"@graphql-codegen/cli@^3.0.0": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-3.3.1.tgz#103e7a2263126fdde168a1ce623fc2bdc05352f0" - integrity sha512-4Es8Y9zFeT0Zx2qRL7L3qXDbbqvXK6aID+8v8lP6gaYD+uWx3Jd4Hsq5vxwVBR+6flm0BW/C85Qm0cvmT7O6LA== +"@graphql-codegen/add@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.0.tgz#d195051d7c4bd6d3955a92361cc9eec7784bb541" + integrity sha512-biFdaURX0KTwEJPQ1wkT6BRgNasqgQ5KbCI1a3zwtLtO7XTo7/vKITPylmiU27K5DSOWYnY/1jfSqUAEBuhZrQ== + dependencies: + "@graphql-codegen/plugin-helpers" "^6.0.0" + tslib "~2.6.0" + +"@graphql-codegen/cli@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.0.2.tgz#516330f5828630a0f83db3cbf810c164d00230ef" + integrity sha512-W+0ime0xMrCyG77q+5xiPkkqPLuXJcTx0Zr9TTOxF4zIqWKVsuImS3qVxtpeTx+GRbb8VWv9IedWMtt91JGzQg== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/core" "^3.1.0" - "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-tools/apollo-engine-loader" "^7.3.6" - "@graphql-tools/code-file-loader" "^7.3.17" - "@graphql-tools/git-loader" "^7.2.13" - "@graphql-tools/github-loader" "^7.3.20" - "@graphql-tools/graphql-file-loader" "^7.5.0" - "@graphql-tools/json-file-loader" "^7.4.1" - "@graphql-tools/load" "^7.8.0" - "@graphql-tools/prisma-loader" "^7.2.49" - "@graphql-tools/url-loader" "^7.13.2" - "@graphql-tools/utils" "^9.0.0" - "@parcel/watcher" "^2.1.0" - "@whatwg-node/fetch" "^0.8.0" + "@graphql-codegen/client-preset" "^5.1.2" + "@graphql-codegen/core" "^5.0.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-tools/apollo-engine-loader" "^8.0.0" + "@graphql-tools/code-file-loader" "^8.0.0" + "@graphql-tools/git-loader" "^8.0.0" + "@graphql-tools/github-loader" "^9.0.0" + "@graphql-tools/graphql-file-loader" "^8.0.0" + "@graphql-tools/json-file-loader" "^8.0.0" + "@graphql-tools/load" "^8.1.0" + "@graphql-tools/url-loader" "^9.0.0" + "@graphql-tools/utils" "^10.0.0" + "@inquirer/prompts" "^7.8.2" + "@whatwg-node/fetch" "^0.10.0" chalk "^4.1.0" - cosmiconfig "^7.0.0" - debounce "^1.2.0" + cosmiconfig "^9.0.0" + debounce "^2.0.0" detect-indent "^6.0.0" - graphql-config "^4.5.0" - inquirer "^8.0.0" + graphql-config "^5.1.1" is-glob "^4.0.1" - jiti "^1.17.1" + jiti "^2.3.0" json-to-pretty-yaml "^1.2.2" - listr2 "^4.0.5" + listr2 "^9.0.0" log-symbols "^4.0.0" micromatch "^4.0.5" shell-quote "^1.7.3" string-env-interpolation "^1.0.1" ts-log "^2.2.3" tslib "^2.4.0" - yaml "^1.10.0" + yaml "^2.3.1" yargs "^17.0.0" -"@graphql-codegen/core@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-3.1.0.tgz#ad859d52d509a4eb2ebe5aabba6543a628fb181b" - integrity sha512-DH1/yaR7oJE6/B+c6ZF2Tbdh7LixF1K8L+8BoSubjNyQ8pNwR4a70mvc1sv6H7qgp6y1bPQ9tKE+aazRRshysw== +"@graphql-codegen/client-preset@^5.1.2": + version "5.1.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.1.3.tgz#cfc7eedb2906c0e1318841bd7423cd94e1b980eb" + integrity sha512-8nlKt8/gO/BovWahLb96taMssHKPibBfslry1ed9DIJtbOrceFYF3yNbFZuTHmI644C7ZvoYK93JkE3VzDlCyg== dependencies: - "@graphql-codegen/plugin-helpers" "^4.1.0" - "@graphql-tools/schema" "^9.0.0" - "@graphql-tools/utils" "^9.1.1" - tslib "~2.5.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/template" "^7.20.7" + "@graphql-codegen/add" "^6.0.0" + "@graphql-codegen/gql-tag-operations" "5.0.5" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/typed-document-node" "^6.1.2" + "@graphql-codegen/typescript" "^5.0.4" + "@graphql-codegen/typescript-operations" "^5.0.4" + "@graphql-codegen/visitor-plugin-common" "^6.1.2" + "@graphql-tools/documents" "^1.0.0" + "@graphql-tools/utils" "^10.0.0" + "@graphql-typed-document-node/core" "3.2.0" + tslib "~2.6.0" -"@graphql-codegen/fragment-matcher@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-4.0.1.tgz#56f53759f1c1428e60eca18bdbda071fd8b858f5" - integrity sha512-m1jjCBppB+QXupYu8eVM1BZM36APPArHEVB8R0v29Vj2Kz/35+pe682PyQaf7iyxTkemW759h2KIvWFtXdLCJQ== +"@graphql-codegen/core@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.0.tgz#c3fd07226a3cd2f75c80896c127adc191f42e179" + integrity sha512-vLTEW0m8LbE4xgRwbFwCdYxVkJ1dBlVJbQyLb9Q7bHnVFgHAP982Xo8Uv7FuPBmON+2IbTjkCqhFLHVZbqpvjQ== dependencies: - "@graphql-codegen/plugin-helpers" "^4.1.0" - tslib "~2.5.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-tools/schema" "^10.0.0" + "@graphql-tools/utils" "^10.0.0" + tslib "~2.6.0" -"@graphql-codegen/import-types-preset@^2.2.6": - version "2.2.6" - resolved "https://registry.yarnpkg.com/@graphql-codegen/import-types-preset/-/import-types-preset-2.2.6.tgz#aab292d93f79bffc09bc12a51843c3272b526475" - integrity sha512-Lo2ITOln3UVdyyEPiijj8bVhVg0Ghp/JzHXA2LXxrJVCRbXizQhVC2vjiaWTjMskPt9Zub0yIoce4+RrbsXKcg== +"@graphql-codegen/fragment-matcher@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz#4beb848c0fdb38bbdc7b99250de84ec4456e7a5d" + integrity sha512-lXdFWrUSEbOL2KhmYxdeNPLllcBGZnNunwgV72C12+21v9lyKaOqFnYueyt0pT110MGCwUhKTnXzUtV1D5i9Jw== dependencies: - "@graphql-codegen/add" "^3.2.1" - "@graphql-codegen/plugin-helpers" "^2.7.2" - "@graphql-codegen/visitor-plugin-common" "2.13.1" - tslib "~2.4.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + tslib "~2.6.0" -"@graphql-codegen/plugin-helpers@^1.18.5", "@graphql-codegen/plugin-helpers@^1.18.8": - version "1.18.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-1.18.8.tgz#39aac745b9e22e28c781cc07cf74836896a3a905" - integrity sha512-mb4I9j9lMGqvGggYuZ0CV+Hme08nar68xkpPbAVotg/ZBmlhZIok/HqW2BcMQi7Rj+Il5HQMeQ1wQ1M7sv/TlQ== +"@graphql-codegen/gql-tag-operations@5.0.5": + version "5.0.5" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.0.5.tgz#691f53a4a9e09608fd4345622fec01364294fabf" + integrity sha512-DutUBwA3UMOB2AI6O1FDidYw7N0Br4d/ogGrYg6XOeeVuRYigc6i9wX4qiv4ofD34Ujfcfze0U2PI3ZOR33NKw== dependencies: - "@graphql-tools/utils" "^7.9.1" - common-tags "1.8.0" - import-from "4.0.0" - lodash "~4.17.0" - tslib "~2.3.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/visitor-plugin-common" "6.1.2" + "@graphql-tools/utils" "^10.0.0" + auto-bind "~4.0.0" + tslib "~2.6.0" -"@graphql-codegen/plugin-helpers@^2.7.2": - version "2.7.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-2.7.2.tgz#6544f739d725441c826a8af6a49519f588ff9bed" - integrity sha512-kln2AZ12uii6U59OQXdjLk5nOlh1pHis1R98cDZGFnfaiAbX9V3fxcZ1MMJkB7qFUymTALzyjZoXXdyVmPMfRg== +"@graphql-codegen/near-operation-file-preset@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-3.1.0.tgz#5c60a9ec137bb78d83eeab39a97595a09ad6bd9a" + integrity sha512-sXIIi0BPP3IcARdzSztpE51oJTcGB67hi7ddBYfLinks/R/5aFG1Ry/J61707Kt+6Q5WhTnf5XAQUAqi6200yA== dependencies: - "@graphql-tools/utils" "^8.8.0" - change-case-all "1.0.14" - common-tags "1.8.2" - import-from "4.0.0" - lodash "~4.17.0" - tslib "~2.4.0" + "@graphql-codegen/add" "^3.2.1" + "@graphql-codegen/plugin-helpers" "^3.0.0" + "@graphql-codegen/visitor-plugin-common" "2.13.8" + "@graphql-tools/utils" "^10.0.0" + parse-filepath "^1.0.2" + tslib "^2.8.1" -"@graphql-codegen/plugin-helpers@^3.1.1": +"@graphql-codegen/plugin-helpers@^3.0.0", "@graphql-codegen/plugin-helpers@^3.1.1", "@graphql-codegen/plugin-helpers@^3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz#69a2e91178f478ea6849846ade0a59a844d34389" integrity sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg== @@ -968,369 +953,385 @@ lodash "~4.17.0" tslib "~2.4.0" -"@graphql-codegen/plugin-helpers@^4.0.0", "@graphql-codegen/plugin-helpers@^4.1.0", "@graphql-codegen/plugin-helpers@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-4.2.0.tgz#8324914d0f99162a223cfa01796cdd6be972d2ae" - integrity sha512-THFTCfg+46PXlXobYJ/OoCX6pzjI+9woQqCjdyKtgoI0tn3Xq2HUUCiidndxUpEYVrXb5pRiRXb7b/ZbMQqD0A== +"@graphql-codegen/plugin-helpers@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.0.0.tgz#8a913c82a95b9ff36b2180f7c56611cc56268625" + integrity sha512-Z7P89vViJvQakRyMbq/JF2iPLruRFOwOB6IXsuSvV/BptuuEd7fsGPuEf8bdjjDxUY0pJZnFN8oC7jIQ8p9GKA== dependencies: - "@graphql-tools/utils" "^9.0.0" + "@graphql-tools/utils" "^10.0.0" change-case-all "1.0.15" common-tags "1.8.2" import-from "4.0.0" lodash "~4.17.0" - tslib "~2.5.0" - -"@graphql-codegen/schema-ast@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-3.0.1.tgz#37b458bb57b95715a9eb4259341c856dae2a461d" - integrity sha512-rTKTi4XiW4QFZnrEqetpiYEWVsOFNoiR/v3rY9mFSttXFbIwNXPme32EspTiGWmEEdHY8UuTDtZN3vEcs/31zw== - dependencies: - "@graphql-codegen/plugin-helpers" "^4.1.0" - "@graphql-tools/utils" "^9.0.0" - tslib "~2.5.0" + tslib "~2.6.0" -"@graphql-codegen/typescript-operations@^3.0.0": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-3.0.4.tgz#60163c07f0ef73655779ece450d02c1172c44027" - integrity sha512-6yE2OL2+WJ1vd5MwFEGXpaxsFGzjAGUytPVHDML3Bi3TwP1F3lnQlIko4untwvHW0JhZEGQ7Ck30H9HjcxpdKA== - dependencies: - "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-codegen/typescript" "^3.0.4" - "@graphql-codegen/visitor-plugin-common" "3.1.1" - auto-bind "~4.0.0" - tslib "~2.5.0" - -"@graphql-codegen/typescript-react-apollo@^3.3.7": - version "3.3.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-3.3.7.tgz#e856caa22c5f7bc9a546c44f54e5f3bd5801ab67" - integrity sha512-9DUiGE8rcwwEkf/S1kpBT/Py/UUs9Qak14bOnTT1JHWs1MWhiDA7vml+A8opU7YFI1EVbSSaE5jjRv11WHoikQ== - dependencies: - "@graphql-codegen/plugin-helpers" "^2.7.2" - "@graphql-codegen/visitor-plugin-common" "2.13.1" - auto-bind "~4.0.0" - change-case-all "1.0.14" - tslib "~2.4.0" - -"@graphql-codegen/typescript-resolvers@1.19.1": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-1.19.1.tgz#56677ec56c1ca7174d22a2f236e3fb7f6503e708" - integrity sha512-KdCVfg2u2RMbHu7eV9SOh5rmfnEQaMsQ0k8741bMbBmCESLnrWltujF2RT1OPN7WCn7xJejBtrFg/3UgT0fNug== +"@graphql-codegen/schema-ast@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz#9708d7484a01bb3a502685126708d7b139fd0721" + integrity sha512-jn7Q3PKQc0FxXjbpo9trxzlz/GSFQWxL042l0iC8iSbM/Ar+M7uyBwMtXPsev/3Razk+osQyreghIz0d2+6F7Q== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.5" - "@graphql-codegen/typescript" "^1.22.0" - "@graphql-codegen/visitor-plugin-common" "^1.20.0" - "@graphql-tools/utils" "^7.0.0" - auto-bind "~4.0.0" - tslib "~2.2.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-tools/utils" "^10.0.0" + tslib "~2.6.0" -"@graphql-codegen/typescript-resolvers@^3.0.0": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-resolvers/-/typescript-resolvers-3.2.1.tgz#d0e6aca10d2417b67267f53f3e0304c7be4cbd84" - integrity sha512-2ZIHk5J6HTuylse5ZIxw+aega54prHxvj7vM8hiKJ6vejZ94kvVPAq4aWmSFOkZ5lqU3YnM/ZyWfnhT5CUDj1g== +"@graphql-codegen/typed-document-node@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.2.tgz#81b6a4cc0ba6f632153190caf4cd9e10b4d04436" + integrity sha512-DelLv7BY8Sx0toyCiEsc46W3FtqipiiqhprUnGnSalfKnKVB8KUodXKaf70migy6hWyDl5d1OJOp5wrttuIy2Q== dependencies: - "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-codegen/typescript" "^3.0.4" - "@graphql-codegen/visitor-plugin-common" "3.1.1" - "@graphql-tools/utils" "^9.0.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/visitor-plugin-common" "6.1.2" auto-bind "~4.0.0" - tslib "~2.5.0" + change-case-all "1.0.15" + tslib "~2.6.0" -"@graphql-codegen/typescript@^1.22.0": - version "1.23.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-1.23.0.tgz#48a5372bcbe81a442c71c1bb032c312c6586a59a" - integrity sha512-ZfFgk5mGfuOy4kEpy+dcuvJMphigMfJ4AkiP1qWmWFufDW3Sg2yayTSNmzeFdcXMrWGgfNW2dKtuuTmbmQhS5g== +"@graphql-codegen/typescript-operations@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.4.tgz#052680d41f4bab3bb85af8e906c43bde29239e7b" + integrity sha512-5Bu/BTmyNjdSfSLLBKjC0+4XWcY01uotVcnVIWIxxRdIHoRxnTW6PUkT5CoPHP5r/Uoo3OvIJxh+0LYSH5suwA== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.8" - "@graphql-codegen/visitor-plugin-common" "1.22.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/typescript" "^5.0.4" + "@graphql-codegen/visitor-plugin-common" "6.1.2" auto-bind "~4.0.0" - tslib "~2.3.0" + tslib "~2.6.0" -"@graphql-codegen/typescript@^3.0.0", "@graphql-codegen/typescript@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-3.0.4.tgz#e12dc106a2722ebc7d18556980ccf47fa9d0805f" - integrity sha512-x4O47447DZrWNtE/l5CU9QzzW4m1RbmCEdijlA3s2flG/y1Ckqdemob4CWfilSm5/tZ3w1junVDY616RDTSvZw== +"@graphql-codegen/typescript@^5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.4.tgz#a7f51505445568fd737b01ce7fad515c2b89897f" + integrity sha512-q6S8hX+aR4BzeGgolac4gp22rBnXbLhedmOwT1UBT9e3lGNmNpYC7WJUEzAPjWf6z1lRSNmojLlwEjTnffhKNA== dependencies: - "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-codegen/schema-ast" "^3.0.1" - "@graphql-codegen/visitor-plugin-common" "3.1.1" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/schema-ast" "^5.0.0" + "@graphql-codegen/visitor-plugin-common" "6.1.2" auto-bind "~4.0.0" - tslib "~2.5.0" + tslib "~2.6.0" -"@graphql-codegen/visitor-plugin-common@1.22.0", "@graphql-codegen/visitor-plugin-common@^1.20.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-1.22.0.tgz#75fc8b580143bccbec411eb92d5fef715ed22e42" - integrity sha512-2afJGb6d8iuZl9KizYsexPwraEKO1lAvt5eVHNM5Xew4vwz/AUHeqDR2uOeQgVV+27EzjjzSDd47IEdH0dLC2w== +"@graphql-codegen/visitor-plugin-common@2.13.8": + version "2.13.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz#09bc6317b227e5a278f394f4cef0d6c2d1910597" + integrity sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ== dependencies: - "@graphql-codegen/plugin-helpers" "^1.18.8" - "@graphql-tools/optimize" "^1.0.1" - "@graphql-tools/relay-operation-optimizer" "^6.3.0" - array.prototype.flatmap "^1.2.4" - auto-bind "~4.0.0" - change-case-all "1.0.14" - dependency-graph "^0.11.0" - graphql-tag "^2.11.0" - parse-filepath "^1.0.2" - tslib "~2.3.0" - -"@graphql-codegen/visitor-plugin-common@2.13.1": - version "2.13.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.1.tgz#2228660f6692bcdb96b1f6d91a0661624266b76b" - integrity sha512-mD9ufZhDGhyrSaWQGrU1Q1c5f01TeWtSWy/cDwXYjJcHIj1Y/DG2x0tOflEfCvh5WcnmHNIw4lzDsg1W7iFJEg== - dependencies: - "@graphql-codegen/plugin-helpers" "^2.7.2" + "@graphql-codegen/plugin-helpers" "^3.1.2" "@graphql-tools/optimize" "^1.3.0" "@graphql-tools/relay-operation-optimizer" "^6.5.0" - "@graphql-tools/utils" "^8.8.0" + "@graphql-tools/utils" "^9.0.0" auto-bind "~4.0.0" - change-case-all "1.0.14" + change-case-all "1.0.15" dependency-graph "^0.11.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-codegen/visitor-plugin-common@3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-3.1.1.tgz#50c2aa3c537a805ce68d2f115d0a9811b151428c" - integrity sha512-uAfp+zu/009R3HUAuTK2AamR1bxIltM6rrYYI6EXSmkM3rFtFsLTuJhjUDj98HcUCszJZrADppz8KKLGRUVlNg== +"@graphql-codegen/visitor-plugin-common@6.1.2", "@graphql-codegen/visitor-plugin-common@^6.1.2": + version "6.1.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.1.2.tgz#e614d6606402a152686d6f90e6363150a7a55bde" + integrity sha512-zYdrhJKgk8kqE1Xz5/m/Ua42zk+rIvYB/FHh3dE1AhZ6b1IDqgKjF3LnkT+K2qenf9EfT4yNjXd5CEKMeXfHyg== dependencies: - "@graphql-codegen/plugin-helpers" "^4.2.0" - "@graphql-tools/optimize" "^1.3.0" - "@graphql-tools/relay-operation-optimizer" "^6.5.0" - "@graphql-tools/utils" "^9.0.0" + "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-tools/optimize" "^2.0.0" + "@graphql-tools/relay-operation-optimizer" "^7.0.0" + "@graphql-tools/utils" "^10.0.0" auto-bind "~4.0.0" change-case-all "1.0.15" - dependency-graph "^0.11.0" + dependency-graph "^1.0.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" - tslib "~2.5.0" + tslib "~2.6.0" -"@graphql-tools/apollo-engine-loader@^7.3.6": - version "7.3.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-7.3.26.tgz#91e54460d5579933e42a2010b8688c3459c245d8" - integrity sha512-h1vfhdJFjnCYn9b5EY1Z91JTF0KB3hHVJNQIsiUV2mpQXZdeOXQoaWeYEKaiI5R6kwBw5PP9B0fv3jfUIG8LyQ== +"@graphql-hive/signal@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-1.0.0.tgz#6e2193660a47c925abadbe72293dfc9430e24f8f" + integrity sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag== + +"@graphql-hive/signal@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-2.0.0.tgz#205805328b118e1ae556417ed364257234800464" + integrity sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ== + +"@graphql-tools/apollo-engine-loader@^8.0.0": + version "8.0.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.26.tgz#38bd348c999f70b714421da22c0b4e4e1ac9a695" + integrity sha512-vNeWX/hGpHMVaDJNBbf7yhmy95ueZ3f4rUjl6v819Emsfo0ItnyTM2FmOPUwpWIm7DpCKCSt134ERy/fgo14rg== dependencies: - "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/utils" "^9.2.1" - "@whatwg-node/fetch" "^0.8.0" + "@graphql-tools/utils" "^10.10.3" + "@whatwg-node/fetch" "^0.10.13" + sync-fetch "0.6.0-2" tslib "^2.4.0" -"@graphql-tools/batch-execute@^8.5.22": - version "8.5.22" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.22.tgz#a742aa9d138fe794e786d8fb6429665dc7df5455" - integrity sha512-hcV1JaY6NJQFQEwCKrYhpfLK8frSXDbtNMoTur98u10Cmecy1zrqNKSqhEyGetpgHxaJRqszGzKeI3RuroDN6A== +"@graphql-tools/batch-execute@^10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.3.tgz#57d7fa56d507d32990efbd96e09e80fa22a80363" + integrity sha512-QH9tKbBQcLeSToXufGKRKWZUpVFJ+gRzJ8wSjz0amQXxpHiF8sEAIJKy9CmgRfnhvt31BjOMTr1sUS4xbhj7lw== dependencies: - "@graphql-tools/utils" "^9.2.1" - dataloader "^2.2.2" - tslib "^2.4.0" - value-or-promise "^1.0.12" + "@graphql-tools/utils" "^10.10.1" + "@whatwg-node/promise-helpers" "^1.3.2" + dataloader "^2.2.3" + tslib "^2.8.1" -"@graphql-tools/code-file-loader@^7.3.17": - version "7.3.23" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.23.tgz#33793f9a1f8e74981f8ae6ec4ab7061f9713db15" - integrity sha512-8Wt1rTtyTEs0p47uzsPJ1vAtfAx0jmxPifiNdmo9EOCuUPyQGEbMaik/YkqZ7QUFIEYEQu+Vgfo8tElwOPtx5Q== +"@graphql-tools/batch-execute@^9.0.19": + version "9.0.19" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz#b724f3542e2cee8a3a71262a711469afa39cae88" + integrity sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.2" - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/utils" "^10.9.1" + "@whatwg-node/promise-helpers" "^1.3.0" + dataloader "^2.2.3" + tslib "^2.8.1" + +"@graphql-tools/code-file-loader@^8.0.0": + version "8.1.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.26.tgz#d1013bf6e780972cc9ec7df6d07b2f1a5039f501" + integrity sha512-VamhpBEbrABCjtJqEFBUrHBBVX4Iw7q4Ga8H3W0P7mO+sE1HuTfpWirSdBLlhc6nGcSyTb6FA1mEgGjjUASIHA== + dependencies: + "@graphql-tools/graphql-tag-pluck" "8.3.25" + "@graphql-tools/utils" "^10.10.3" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^9.0.31": - version "9.0.35" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.35.tgz#94683f4bcec63520b4a6c8b2abf2e2e9324ea4f1" - integrity sha512-jwPu8NJbzRRMqi4Vp/5QX1vIUeUPpWmlQpOkXQD2r1X45YsVceyUUBnktCrlJlDB4jPRVy7JQGwmYo3KFiOBMA== +"@graphql-tools/delegate@^10.2.23": + version "10.2.23" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-10.2.23.tgz#fcd42e7aa70149269ad644e03098c8d28810bd87" + integrity sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w== + dependencies: + "@graphql-tools/batch-execute" "^9.0.19" + "@graphql-tools/executor" "^1.4.9" + "@graphql-tools/schema" "^10.0.25" + "@graphql-tools/utils" "^10.9.1" + "@repeaterjs/repeater" "^3.0.6" + "@whatwg-node/promise-helpers" "^1.3.0" + dataloader "^2.2.3" + dset "^3.1.2" + tslib "^2.8.1" + +"@graphql-tools/delegate@^11.1.2": + version "11.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-11.1.2.tgz#e075bf6b7a4b7c31eb792785242e3c7bc051ca95" + integrity sha512-C6W4DM+z3nN7OkV08CAuBUmCW+etmfiKzlpx09oEGBWzw61eYQdKlqp1qqeKtYebELam9LkHTa1n8/rilv5PXw== + dependencies: + "@graphql-tools/batch-execute" "^10.0.3" + "@graphql-tools/executor" "^1.4.11" + "@graphql-tools/schema" "^10.0.27" + "@graphql-tools/utils" "^10.10.1" + "@repeaterjs/repeater" "^3.0.6" + "@whatwg-node/promise-helpers" "^1.3.2" + dataloader "^2.2.3" + tslib "^2.8.1" + +"@graphql-tools/documents@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/documents/-/documents-1.0.1.tgz#ae19cd5667d22c23b331d3a1429443ed7130faee" + integrity sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA== dependencies: - "@graphql-tools/batch-execute" "^8.5.22" - "@graphql-tools/executor" "^0.0.20" - "@graphql-tools/schema" "^9.0.19" - "@graphql-tools/utils" "^9.2.1" - dataloader "^2.2.2" - tslib "^2.5.0" - value-or-promise "^1.0.12" + lodash.sortby "^4.7.0" + tslib "^2.4.0" -"@graphql-tools/executor-graphql-ws@^0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.14.tgz#e0f53fc4cfc8a06cc461b2bc1edb4bb9a8e837ed" - integrity sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w== +"@graphql-tools/executor-common@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz#763603a6d7a22bb09d67ce682e84a0d730ff2bf9" + integrity sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q== dependencies: - "@graphql-tools/utils" "^9.2.1" - "@repeaterjs/repeater" "3.0.4" - "@types/ws" "^8.0.0" - graphql-ws "5.12.1" - isomorphic-ws "5.0.0" - tslib "^2.4.0" - ws "8.13.0" + "@envelop/core" "^5.2.3" + "@graphql-tools/utils" "^10.8.1" -"@graphql-tools/executor-http@^0.1.7", "@graphql-tools/executor-http@^0.1.9": - version "0.1.10" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.10.tgz#faf48e18e62a925796c9653c2f50cf2095bc8e6f" - integrity sha512-hnAfbKv0/lb9s31LhWzawQ5hghBfHS+gYWtqxME6Rl0Aufq9GltiiLBcl7OVVOnkLF0KhwgbYP1mB5VKmgTGpg== +"@graphql-tools/executor-common@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz#f681de443d7e90e1768c44bd64a4cd418dc356e3" + integrity sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow== dependencies: - "@graphql-tools/utils" "^9.2.1" + "@envelop/core" "^5.3.0" + "@graphql-tools/utils" "^10.9.1" + +"@graphql-tools/executor-common@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-1.0.4.tgz#805d7b55a5f47c84ca4b12b27ea6786028d8623e" + integrity sha512-EKkVjo1Fbx+qK5ZeI+KneeoGzk6HU6aL4E0bX4J3lfnjyaZVESbODCDS/9bBcrV1X2dEby9MfWqA6fkFodP+hw== + dependencies: + "@envelop/core" "^5.4.0" + "@graphql-tools/utils" "^10.10.1" + +"@graphql-tools/executor-graphql-ws@^2.0.1": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz#e8d7689d266b3ccea962b708d6f5d31a1a246c66" + integrity sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q== + dependencies: + "@graphql-tools/executor-common" "^0.0.6" + "@graphql-tools/utils" "^10.9.1" + "@whatwg-node/disposablestack" "^0.0.6" + graphql-ws "^6.0.6" + isomorphic-ws "^5.0.0" + tslib "^2.8.1" + ws "^8.18.3" + +"@graphql-tools/executor-graphql-ws@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.2.tgz#92a662fc4277b8c6fcf9f5e6f8a3fc5813930547" + integrity sha512-nlBXYrWkZ+VFIVU2QrRNnWImBwZglY/uuyK0/dkFMFy9FEjZmErLpGv62PZqQUSEjPakNgUil+T94CUHGebR9w== + dependencies: + "@graphql-tools/executor-common" "^1.0.4" + "@graphql-tools/utils" "^10.10.1" + "@whatwg-node/disposablestack" "^0.0.6" + graphql-ws "^6.0.6" + isows "^1.0.7" + tslib "^2.8.1" + ws "^8.18.3" + +"@graphql-tools/executor-http@^1.1.9": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz#057de79ebc90edbd242259cccb1268d37ee6c579" + integrity sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A== + dependencies: + "@graphql-hive/signal" "^1.0.0" + "@graphql-tools/executor-common" "^0.0.4" + "@graphql-tools/utils" "^10.8.1" "@repeaterjs/repeater" "^3.0.4" - "@whatwg-node/fetch" "^0.8.1" - dset "^3.1.2" - extract-files "^11.0.0" + "@whatwg-node/disposablestack" "^0.0.6" + "@whatwg-node/fetch" "^0.10.4" + "@whatwg-node/promise-helpers" "^1.3.0" meros "^1.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" + tslib "^2.8.1" -"@graphql-tools/executor-legacy-ws@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.11.tgz#a1e12be8279e92a363a23d4105461a34cd9e389e" - integrity sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw== +"@graphql-tools/executor-http@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.0.6.tgz#3381b5170a9a3bfb5c9a2605be7109de2fe86189" + integrity sha512-gTcSdaJWbqYKg/aVQ/T+sIC6Isa4jjx+QhVMyawgivmdbLTg0UgsbR6g97eNVoxnRzFq/CuPSy71IHQeq8Q6iA== dependencies: - "@graphql-tools/utils" "^9.2.1" + "@graphql-hive/signal" "^2.0.0" + "@graphql-tools/executor-common" "^1.0.4" + "@graphql-tools/utils" "^10.10.1" + "@repeaterjs/repeater" "^3.0.4" + "@whatwg-node/disposablestack" "^0.0.6" + "@whatwg-node/fetch" "^0.10.12" + "@whatwg-node/promise-helpers" "^1.3.2" + meros "^1.3.2" + tslib "^2.8.1" + +"@graphql-tools/executor-legacy-ws@^1.1.19", "@graphql-tools/executor-legacy-ws@^1.1.23": + version "1.1.23" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.23.tgz#f9891d2fd591ebb9c5b77b73b55c93d0acd87fa2" + integrity sha512-wwS6ZlJDaC+zxE1DcfYrPJk1ynQ0xcbOWS/x8dy4hO6ZCjRawkogoqN3Muab0E9RzuwF29LRu+aOH6isO5mQKg== + dependencies: + "@graphql-tools/utils" "^10.10.3" "@types/ws" "^8.0.0" - isomorphic-ws "5.0.0" + isomorphic-ws "^5.0.0" tslib "^2.4.0" - ws "8.13.0" + ws "^8.17.1" -"@graphql-tools/executor@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.20.tgz#d51d159696e839522dd49d936636af251670e425" - integrity sha512-GdvNc4vszmfeGvUqlcaH1FjBoguvMYzxAfT6tDd4/LgwymepHhinqLNA5otqwVLW+JETcDaK7xGENzFomuE6TA== +"@graphql-tools/executor@^1.4.11", "@graphql-tools/executor@^1.4.9": + version "1.4.13" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.4.13.tgz#ebc35e6060ba9dd88e4a664dac02b3f4b0ff0582" + integrity sha512-2hTSRfH2kb4ua0ANOV/K6xUoCZsHAE6igE1bimtWUK7v0bowPIxGRKRPpF8JLbImpsJuTCC4HGOCMy7otg3FIQ== dependencies: - "@graphql-tools/utils" "^9.2.1" - "@graphql-typed-document-node/core" "3.2.0" + "@graphql-tools/utils" "^10.10.3" + "@graphql-typed-document-node/core" "^3.2.0" "@repeaterjs/repeater" "^3.0.4" + "@whatwg-node/disposablestack" "^0.0.6" + "@whatwg-node/promise-helpers" "^1.0.0" tslib "^2.4.0" - value-or-promise "^1.0.12" -"@graphql-tools/git-loader@^7.2.13": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-7.3.0.tgz#ca10c17d4f58c4592432d9d2ac1c2b393aebad16" - integrity sha512-gcGAK+u16eHkwsMYqqghZbmDquh8QaO24Scsxq+cVR+vx1ekRlsEiXvu+yXVDbZdcJ6PBIbeLcQbEu+xhDLmvQ== +"@graphql-tools/git-loader@^8.0.0": + version "8.0.30" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.30.tgz#5d2955f965bd2b057c819034f163c989e1db8bf5" + integrity sha512-Rx3rphMFBKrILkCrjB6k5JF2z+3XYEnc5wUOw2rMxFAHBHeZjBfA6TDLUtd8EKnaf6h5y0pKpb+uZ72EYJaH2A== dependencies: - "@graphql-tools/graphql-tag-pluck" "7.5.2" - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/graphql-tag-pluck" "8.3.25" + "@graphql-tools/utils" "^10.10.3" is-glob "4.0.3" - micromatch "^4.0.4" + micromatch "^4.0.8" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/github-loader@^7.3.20": - version "7.3.28" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-7.3.28.tgz#a7166b136e8442bd8b3ab943ad3b66c84bcabfcf" - integrity sha512-OK92Lf9pmxPQvjUNv05b3tnVhw0JRfPqOf15jZjyQ8BfdEUrJoP32b4dRQQem/wyRL24KY4wOfArJNqzpsbwCA== - dependencies: - "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/executor-http" "^0.1.9" - "@graphql-tools/graphql-tag-pluck" "^7.4.6" - "@graphql-tools/utils" "^9.2.1" - "@whatwg-node/fetch" "^0.8.0" +"@graphql-tools/github-loader@^9.0.0": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.4.tgz#c706df905398480258fd5836a34c4d6a0621cbc4" + integrity sha512-w8RRhOTS1Qn2/MUWO2ogtLok6/0p15IdAoYfkCW7duCTPWWG/blCtiYNDyrOZahjCuL4WrxL9+JZfj+6nXSpIQ== + dependencies: + "@graphql-tools/executor-http" "^3.0.6" + "@graphql-tools/graphql-tag-pluck" "^8.3.25" + "@graphql-tools/utils" "^10.10.3" + "@whatwg-node/fetch" "^0.10.13" + "@whatwg-node/promise-helpers" "^1.0.0" + sync-fetch "0.6.0-2" tslib "^2.4.0" - value-or-promise "^1.0.12" -"@graphql-tools/graphql-file-loader@^7.3.7", "@graphql-tools/graphql-file-loader@^7.5.0": - version "7.5.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz#7c281617ea3ab4db4d42a2bdb49850f2b937f0f9" - integrity sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw== +"@graphql-tools/graphql-file-loader@^8.0.0": + version "8.1.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.6.tgz#87aca1f87616a637436d995e1c8800f6e92ee6f9" + integrity sha512-BwK9kUrRbv9w8WMFUedSkPogGdQivopRSRs1aqT9FUD1J3iVLltEHMUx9KTnpPHO7OcULSflMqQs5ZI/3cW+7Q== dependencies: - "@graphql-tools/import" "6.7.18" - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/import" "7.1.6" + "@graphql-tools/utils" "^10.10.3" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@7.5.2", "@graphql-tools/graphql-tag-pluck@^7.4.6": - version "7.5.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.5.2.tgz#502f1e066e19d832ebdeba5f571d7636dc27572d" - integrity sha512-RW+H8FqOOLQw0BPXaahYepVSRjuOHw+7IL8Opaa5G5uYGOBxoXR7DceyQ7BcpMgktAOOmpDNQ2WtcboChOJSRA== - dependencies: - "@babel/parser" "^7.16.8" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/traverse" "^7.16.8" - "@babel/types" "^7.16.8" - "@graphql-tools/utils" "^9.2.1" +"@graphql-tools/graphql-tag-pluck@8.3.25", "@graphql-tools/graphql-tag-pluck@^8.3.25": + version "8.3.25" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.25.tgz#5735cf30f6673b520ab43779ce42ed621c1fc4de" + integrity sha512-b8oTBe0mDQDh3zPcKCkaTPmjLv1TJslBUKXPNLfu5CWS2+gL8Z/z0UuAhCe5gTveuKDJYjkEO7xcct9JfcDi4g== + dependencies: + "@babel/core" "^7.26.10" + "@babel/parser" "^7.26.10" + "@babel/plugin-syntax-import-assertions" "^7.26.0" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" + "@graphql-tools/utils" "^10.10.3" tslib "^2.4.0" -"@graphql-tools/import@6.4.0": - version "6.4.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.4.0.tgz#eb2178d6df8d964e7b9d6b1ed75f80d12f9060a7" - integrity sha512-jfE01oPcmc4vzAcYLs6xT7XC4jJWrM1HNtIwc7HyyHTxrC3nf36XrF3txEZ2l20GT53+OWnMgYx1HhauLGdJmA== +"@graphql-tools/import@7.1.6": + version "7.1.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.6.tgz#8c53941c13cb11f2364617a46b756fcaefd6dae0" + integrity sha512-aROsZq/ApWiAWE9k/xfYiKFUQiw81958e3DvL+HqGtbFQq+F11hIi0PgwOEiEereJZHIuVYZmIfbdQd4gFq4+w== dependencies: - resolve-from "5.0.0" - tslib "~2.3.0" - -"@graphql-tools/import@6.7.18": - version "6.7.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.18.tgz#ad092d8a4546bb6ffc3e871e499eec7ac368680b" - integrity sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ== - dependencies: - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/utils" "^10.10.3" + "@theguild/federation-composition" "^0.20.2" resolve-from "5.0.0" tslib "^2.4.0" -"@graphql-tools/json-file-loader@^7.3.7", "@graphql-tools/json-file-loader@^7.4.1": - version "7.4.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.18.tgz#d78ae40979bde51cfc59717757354afc9e35fba2" - integrity sha512-AJ1b6Y1wiVgkwsxT5dELXhIVUPs/u3VZ8/0/oOtpcoyO/vAeM5rOvvWegzicOOnQw8G45fgBRMkkRfeuwVt6+w== +"@graphql-tools/json-file-loader@^8.0.0": + version "8.0.24" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.24.tgz#feda38f8beaf5758f3153a42f53d4a54796c7be8" + integrity sha512-S2XKpUVAzY84hniaMpv6V0of2joPfLPaeY4XXUQZfi4w0uiPSzHWA/EnXohgojJsQLgN+FP4dXFfp2yA5GG6VA== dependencies: - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/utils" "^10.10.3" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/load-files@^6.6.1": - version "6.6.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/load-files/-/load-files-6.6.1.tgz#91ce18d910baf8678459486d8cccd474767bec0a" - integrity sha512-nd4GOjdD68bdJkHfRepILb0gGwF63mJI7uD4oJuuf2Kzeq8LorKa6WfyxUhdMuLmZhnx10zdAlWPfwv1NOAL4Q== - dependencies: - globby "11.1.0" - tslib "^2.4.0" - unixify "1.0.0" - -"@graphql-tools/load@^7.5.5", "@graphql-tools/load@^7.8.0": - version "7.8.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.14.tgz#f2356f9a5f658a42e33934ae036e4b2cadf2d1e9" - integrity sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg== +"@graphql-tools/load@^8.1.0": + version "8.1.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.6.tgz#e07b06cda5459f58722e09f5308fba6b331b3ea1" + integrity sha512-/bUYqGdB2Y6BflW42IjmauBDzxjec3LQmVAuImVGeiOqw1Rca/DDP7KRQe3vEv8yf/xMVj/PkIl+YRjoo12YxA== dependencies: - "@graphql-tools/schema" "^9.0.18" - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/schema" "^10.0.29" + "@graphql-tools/utils" "^10.10.3" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.3.18", "@graphql-tools/merge@^8.4.1": - version "8.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" - integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== +"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.1.5": + version "9.1.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.5.tgz#db61fc3ee8f7cff4636fddded796037acad5e0c6" + integrity sha512-eVcir6nCcOC/Wzv7ZAng3xec3dj6FehE8+h9TvgvUyrDEKVMdFfrO6etRFZ2hucWVcY8S6drx7zQx04N4lPM8Q== dependencies: - "@graphql-tools/utils" "^9.2.1" + "@graphql-tools/utils" "^10.10.3" tslib "^2.4.0" -"@graphql-tools/optimize@^1.0.1", "@graphql-tools/optimize@^1.3.0": +"@graphql-tools/optimize@^1.3.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.4.0.tgz#20d6a9efa185ef8fc4af4fd409963e0907c6e112" integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== dependencies: tslib "^2.4.0" -"@graphql-tools/prisma-loader@^7.2.49": - version "7.2.72" - resolved "https://registry.yarnpkg.com/@graphql-tools/prisma-loader/-/prisma-loader-7.2.72.tgz#6304fc23600458396f3ede713d8e2371df7850e3" - integrity sha512-0a7uV7Fky6yDqd0tI9+XMuvgIo6GAqiVzzzFV4OSLry4AwiQlI3igYseBV7ZVOGhedOTqj/URxjpiv07hRcwag== +"@graphql-tools/optimize@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-2.0.0.tgz#7a9779d180824511248a50c5a241eff6e7a2d906" + integrity sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg== dependencies: - "@graphql-tools/url-loader" "^7.17.18" - "@graphql-tools/utils" "^9.2.1" - "@types/js-yaml" "^4.0.0" - "@types/json-stable-stringify" "^1.0.32" - "@whatwg-node/fetch" "^0.8.2" - chalk "^4.1.0" - debug "^4.3.1" - dotenv "^16.0.0" - graphql-request "^6.0.0" - http-proxy-agent "^6.0.0" - https-proxy-agent "^6.0.0" - jose "^4.11.4" - js-yaml "^4.0.0" - json-stable-stringify "^1.0.1" - lodash "^4.17.20" - scuid "^1.1.0" tslib "^2.4.0" - yaml-ast-parser "^0.0.43" -"@graphql-tools/relay-operation-optimizer@^6.3.0", "@graphql-tools/relay-operation-optimizer@^6.5.0": +"@graphql-tools/relay-operation-optimizer@^6.5.0": version "6.5.18" resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz#a1b74a8e0a5d0c795b8a4d19629b654cf66aa5ab" integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== @@ -1339,52 +1340,71 @@ "@graphql-tools/utils" "^9.2.1" tslib "^2.4.0" -"@graphql-tools/schema@^9.0.0", "@graphql-tools/schema@^9.0.16", "@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": - version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" - integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== +"@graphql-tools/relay-operation-optimizer@^7.0.0": + version "7.0.25" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.25.tgz#7667f435ac17a0c08e44cad7543dba1972f7fe7f" + integrity sha512-1S7qq9eyO6ygPNWX2lZd+oxbpl63OhnTTw8+t5OWprM2Tzws9HEosLUpsMR85z1gbezeKtUDt9a2bsSyu4MMFg== dependencies: - "@graphql-tools/merge" "^8.4.1" - "@graphql-tools/utils" "^9.2.1" + "@ardatan/relay-compiler" "^12.0.3" + "@graphql-tools/utils" "^10.10.3" tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/url-loader@^7.13.2", "@graphql-tools/url-loader@^7.17.18", "@graphql-tools/url-loader@^7.9.7": - version "7.17.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.18.tgz#3e253594d23483e4c0dd3a4c3dd2ad5cd0141192" - integrity sha512-ear0CiyTj04jCVAxi7TvgbnGDIN2HgqzXzwsfcqiVg9cvjT40NcMlZ2P1lZDgqMkZ9oyLTV8Bw6j+SyG6A+xPw== - dependencies: - "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/delegate" "^9.0.31" - "@graphql-tools/executor-graphql-ws" "^0.0.14" - "@graphql-tools/executor-http" "^0.1.7" - "@graphql-tools/executor-legacy-ws" "^0.0.11" - "@graphql-tools/utils" "^9.2.1" - "@graphql-tools/wrap" "^9.4.2" - "@types/ws" "^8.0.0" - "@whatwg-node/fetch" "^0.8.0" - isomorphic-ws "^5.0.0" + +"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.25", "@graphql-tools/schema@^10.0.27", "@graphql-tools/schema@^10.0.29": + version "10.0.29" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.29.tgz#cbb0d0cdeb2c55559ace51e2ffadee39990d01c0" + integrity sha512-+Htiupnq6U/AWOEAJerIOGT1pAf4u43Q3n2JmFpqFfYJchz6sKWZ7L9Lpe/NusaaUQty/IOF+eQlNFypEaWxhg== + dependencies: + "@graphql-tools/merge" "^9.1.5" + "@graphql-tools/utils" "^10.10.3" tslib "^2.4.0" - value-or-promise "^1.0.11" - ws "^8.12.0" -"@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.9.1": - version "7.10.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-7.10.0.tgz#07a4cb5d1bec1ff1dc1d47a935919ee6abd38699" - integrity sha512-d334r6bo9mxdSqZW6zWboEnnOOFRrAPVQJ7LkU8/6grglrbcu6WhwCLzHb90E94JI3TD3ricC3YGbUqIi9Xg0w== +"@graphql-tools/url-loader@^8.0.0": + version "8.0.33" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz#1e5ebfa6a840e3bdc25336c231df2889028f7e0c" + integrity sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw== dependencies: - "@ardatan/aggregate-error" "0.0.6" - camel-case "4.1.2" - tslib "~2.2.0" + "@graphql-tools/executor-graphql-ws" "^2.0.1" + "@graphql-tools/executor-http" "^1.1.9" + "@graphql-tools/executor-legacy-ws" "^1.1.19" + "@graphql-tools/utils" "^10.9.1" + "@graphql-tools/wrap" "^10.0.16" + "@types/ws" "^8.0.0" + "@whatwg-node/fetch" "^0.10.0" + "@whatwg-node/promise-helpers" "^1.0.0" + isomorphic-ws "^5.0.0" + sync-fetch "0.6.0-2" + tslib "^2.4.0" + ws "^8.17.1" + +"@graphql-tools/url-loader@^9.0.0": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.4.tgz#0fd3893cb10616fb8db9a6e6da7482dca8244e5d" + integrity sha512-UNLU9g3IcZjldb08hsBLp6G8YPzZwC38bZ8PFFs/QyXXPFXzoXECT8rHLzvt32w3YFGSbTpj8FiS/KMdTQHZCg== + dependencies: + "@graphql-tools/executor-graphql-ws" "^3.1.2" + "@graphql-tools/executor-http" "^3.0.6" + "@graphql-tools/executor-legacy-ws" "^1.1.23" + "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/wrap" "^11.0.0" + "@types/ws" "^8.0.0" + "@whatwg-node/fetch" "^0.10.13" + "@whatwg-node/promise-helpers" "^1.0.0" + isomorphic-ws "^5.0.0" + sync-fetch "0.6.0-2" + tslib "^2.4.0" + ws "^8.17.1" -"@graphql-tools/utils@^8.8.0": - version "8.13.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.1.tgz#b247607e400365c2cd87ff54654d4ad25a7ac491" - integrity sha512-qIh9yYpdUFmctVqovwMdheVNJqFh+DQNWIhX87FJStfXYnmweBUDATok9fWPleKeFwxnW8IapKmY8m8toJEkAw== +"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.10.1", "@graphql-tools/utils@^10.10.3", "@graphql-tools/utils@^10.8.1", "@graphql-tools/utils@^10.9.1": + version "10.10.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.10.3.tgz#1c6ad8d824ad3fd05571cd2bfdce863c497136d4" + integrity sha512-2EdYiefeLLxsoeZTukSNZJ0E/Z5NnWBUGK2VJa0DQj1scDhVd93HeT1eW9TszJOYmIh3eWAKLv58ri/1XUmdsQ== dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + "@whatwg-node/promise-helpers" "^1.0.0" + cross-inspect "1.0.1" tslib "^2.4.0" -"@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.1.1", "@graphql-tools/utils@^9.2.1": +"@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.2.1": version "9.2.1" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== @@ -1392,16 +1412,27 @@ "@graphql-typed-document-node/core" "^3.1.1" tslib "^2.4.0" -"@graphql-tools/wrap@^9.4.2": - version "9.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.2.tgz#30835587c4c73be1780908a7cb077d8013aa2703" - integrity sha512-DFcd9r51lmcEKn0JW43CWkkI2D6T9XI1juW/Yo86i04v43O9w2/k4/nx2XTJv4Yv+iXwUw7Ok81PGltwGJSDSA== +"@graphql-tools/wrap@^10.0.16": + version "10.1.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-10.1.4.tgz#46f94b00113693d4d4eccddcf262494164234ba6" + integrity sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg== dependencies: - "@graphql-tools/delegate" "^9.0.31" - "@graphql-tools/schema" "^9.0.18" - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" + "@graphql-tools/delegate" "^10.2.23" + "@graphql-tools/schema" "^10.0.25" + "@graphql-tools/utils" "^10.9.1" + "@whatwg-node/promise-helpers" "^1.3.0" + tslib "^2.8.1" + +"@graphql-tools/wrap@^11.0.0": + version "11.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.0.4.tgz#87390a99647d6019d3835e652902e2b2e275d0ac" + integrity sha512-bX1X2QC1oFiH+1nTLhWVx4fp2xoXbDvL+eEiozuWCv7PeZoBs7EgklKZFtpWbcSdNA3L3EuA8JM6mf2jOG6/Sg== + dependencies: + "@graphql-tools/delegate" "^11.1.2" + "@graphql-tools/schema" "^10.0.27" + "@graphql-tools/utils" "^10.10.1" + "@whatwg-node/promise-helpers" "^1.3.2" + tslib "^2.8.1" "@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" @@ -1414,140 +1445,326 @@ integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": - version "0.16.6" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" - integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + version "0.16.7" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: "@humanfs/core" "^0.19.1" - "@humanwhocodes/retry" "^0.3.0" + "@humanwhocodes/retry" "^0.4.0" "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/retry@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" - integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== +"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== -"@humanwhocodes/retry@^0.4.0": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b" - integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA== +"@img/colour@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" + integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== -"@img/sharp-darwin-arm64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz#ef5b5a07862805f1e8145a377c8ba6e98813ca08" - integrity sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ== +"@img/sharp-darwin-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz#6e0732dcade126b6670af7aa17060b926835ea86" + integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w== optionalDependencies: - "@img/sharp-libvips-darwin-arm64" "1.0.4" + "@img/sharp-libvips-darwin-arm64" "1.2.4" -"@img/sharp-darwin-x64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz#e03d3451cd9e664faa72948cc70a403ea4063d61" - integrity sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q== +"@img/sharp-darwin-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz#19bc1dd6eba6d5a96283498b9c9f401180ee9c7b" + integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw== optionalDependencies: - "@img/sharp-libvips-darwin-x64" "1.0.4" + "@img/sharp-libvips-darwin-x64" "1.2.4" -"@img/sharp-libvips-darwin-arm64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz#447c5026700c01a993c7804eb8af5f6e9868c07f" - integrity sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg== +"@img/sharp-libvips-darwin-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz#2894c0cb87d42276c3889942e8e2db517a492c43" + integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g== -"@img/sharp-libvips-darwin-x64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz#e0456f8f7c623f9dbfbdc77383caa72281d86062" - integrity sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ== +"@img/sharp-libvips-darwin-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz#e63681f4539a94af9cd17246ed8881734386f8cc" + integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg== -"@img/sharp-libvips-linux-arm64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz#979b1c66c9a91f7ff2893556ef267f90ebe51704" - integrity sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA== +"@img/sharp-libvips-linux-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz#b1b288b36864b3bce545ad91fa6dadcf1a4ad318" + integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw== -"@img/sharp-libvips-linux-arm@1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz#99f922d4e15216ec205dcb6891b721bfd2884197" - integrity sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g== +"@img/sharp-libvips-linux-arm@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz#b9260dd1ebe6f9e3bdbcbdcac9d2ac125f35852d" + integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A== -"@img/sharp-libvips-linux-s390x@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz#f8a5eb1f374a082f72b3f45e2fb25b8118a8a5ce" - integrity sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA== +"@img/sharp-libvips-linux-ppc64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz#4b83ecf2a829057222b38848c7b022e7b4d07aa7" + integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA== -"@img/sharp-libvips-linux-x64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz#d4c4619cdd157774906e15770ee119931c7ef5e0" - integrity sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw== +"@img/sharp-libvips-linux-riscv64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz#880b4678009e5a2080af192332b00b0aaf8a48de" + integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA== -"@img/sharp-libvips-linuxmusl-arm64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz#166778da0f48dd2bded1fa3033cee6b588f0d5d5" - integrity sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA== +"@img/sharp-libvips-linux-s390x@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz#74f343c8e10fad821b38f75ced30488939dc59ec" + integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ== -"@img/sharp-libvips-linuxmusl-x64@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz#93794e4d7720b077fcad3e02982f2f1c246751ff" - integrity sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw== +"@img/sharp-libvips-linux-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce" + integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw== + +"@img/sharp-libvips-linuxmusl-arm64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz#c8d6b48211df67137541007ee8d1b7b1f8ca8e06" + integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw== + +"@img/sharp-libvips-linuxmusl-x64@1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz#be11c75bee5b080cbee31a153a8779448f919f75" + integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg== + +"@img/sharp-linux-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz#7aa7764ef9c001f15e610546d42fce56911790cc" + integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg== + optionalDependencies: + "@img/sharp-libvips-linux-arm64" "1.2.4" + +"@img/sharp-linux-arm@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz#5fb0c3695dd12522d39c3ff7a6bc816461780a0d" + integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw== + optionalDependencies: + "@img/sharp-libvips-linux-arm" "1.2.4" -"@img/sharp-linux-arm64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz#edb0697e7a8279c9fc829a60fc35644c4839bb22" - integrity sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA== +"@img/sharp-linux-ppc64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz#9c213a81520a20caf66978f3d4c07456ff2e0813" + integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA== optionalDependencies: - "@img/sharp-libvips-linux-arm64" "1.0.4" + "@img/sharp-libvips-linux-ppc64" "1.2.4" -"@img/sharp-linux-arm@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz#422c1a352e7b5832842577dc51602bcd5b6f5eff" - integrity sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ== +"@img/sharp-linux-riscv64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz#cdd28182774eadbe04f62675a16aabbccb833f60" + integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw== optionalDependencies: - "@img/sharp-libvips-linux-arm" "1.0.5" + "@img/sharp-libvips-linux-riscv64" "1.2.4" -"@img/sharp-linux-s390x@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz#f5c077926b48e97e4a04d004dfaf175972059667" - integrity sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q== +"@img/sharp-linux-s390x@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz#93eac601b9f329bb27917e0e19098c722d630df7" + integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg== optionalDependencies: - "@img/sharp-libvips-linux-s390x" "1.0.4" + "@img/sharp-libvips-linux-s390x" "1.2.4" -"@img/sharp-linux-x64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz#d806e0afd71ae6775cc87f0da8f2d03a7c2209cb" - integrity sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA== +"@img/sharp-linux-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8" + integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ== optionalDependencies: - "@img/sharp-libvips-linux-x64" "1.0.4" + "@img/sharp-libvips-linux-x64" "1.2.4" -"@img/sharp-linuxmusl-arm64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz#252975b915894fb315af5deea174651e208d3d6b" - integrity sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g== +"@img/sharp-linuxmusl-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz#d6515ee971bb62f73001a4829b9d865a11b77086" + integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg== optionalDependencies: - "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" -"@img/sharp-linuxmusl-x64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz#3f4609ac5d8ef8ec7dadee80b560961a60fd4f48" - integrity sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw== +"@img/sharp-linuxmusl-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz#d97978aec7c5212f999714f2f5b736457e12ee9f" + integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q== optionalDependencies: - "@img/sharp-libvips-linuxmusl-x64" "1.0.4" + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" -"@img/sharp-wasm32@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz#6f44f3283069d935bb5ca5813153572f3e6f61a1" - integrity sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg== +"@img/sharp-wasm32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz#2f15803aa626f8c59dd7c9d0bbc766f1ab52cfa0" + integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw== dependencies: - "@emnapi/runtime" "^1.2.0" + "@emnapi/runtime" "^1.7.0" + +"@img/sharp-win32-arm64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz#3706e9e3ac35fddfc1c87f94e849f1b75307ce0a" + integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g== + +"@img/sharp-win32-ia32@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz#0b71166599b049e032f085fb9263e02f4e4788de" + integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg== -"@img/sharp-win32-ia32@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz#1a0c839a40c5351e9885628c85f2e5dfd02b52a9" - integrity sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ== +"@img/sharp-win32-x64@0.34.5": + version "0.34.5" + resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8" + integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw== + +"@inquirer/ansi@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.2.tgz#674a4c4d81ad460695cb2a1fc69d78cd187f337e" + integrity sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ== -"@img/sharp-win32-x64@0.33.5": - version "0.33.5" - resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" - integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== +"@inquirer/checkbox@^4.3.2": + version "4.3.2" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz#e1483e6519d6ffef97281a54d2a5baa0d81b3f3b" + integrity sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA== + dependencies: + "@inquirer/ansi" "^1.0.2" + "@inquirer/core" "^10.3.2" + "@inquirer/figures" "^1.0.15" + "@inquirer/type" "^3.0.10" + yoctocolors-cjs "^2.1.3" + +"@inquirer/confirm@^5.1.21": + version "5.1.21" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.21.tgz#610c4acd7797d94890a6e2dde2c98eb1e891dd12" + integrity sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + +"@inquirer/core@^10.3.2": + version "10.3.2" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.2.tgz#535979ff3ff4fe1e7cc4f83e2320504c743b7e20" + integrity sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A== + dependencies: + "@inquirer/ansi" "^1.0.2" + "@inquirer/figures" "^1.0.15" + "@inquirer/type" "^3.0.10" + cli-width "^4.1.0" + mute-stream "^2.0.0" + signal-exit "^4.1.0" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.3" + +"@inquirer/editor@^4.2.23": + version "4.2.23" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-4.2.23.tgz#fe046a3bfdae931262de98c1052437d794322e0b" + integrity sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/external-editor" "^1.0.3" + "@inquirer/type" "^3.0.10" + +"@inquirer/expand@^4.0.23": + version "4.0.23" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-4.0.23.tgz#a38b5f32226d75717c370bdfed792313b92bdc05" + integrity sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + yoctocolors-cjs "^2.1.3" + +"@inquirer/external-editor@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz#c23988291ee676290fdab3fd306e64010a6d13b8" + integrity sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA== + dependencies: + chardet "^2.1.1" + iconv-lite "^0.7.0" + +"@inquirer/figures@^1.0.15": + version "1.0.15" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a" + integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g== + +"@inquirer/input@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-4.3.1.tgz#778683b4c4c4d95d05d4b05c4a854964b73565b4" + integrity sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + +"@inquirer/number@^3.0.23": + version "3.0.23" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-3.0.23.tgz#3fdec2540d642093fd7526818fd8d4bdc7335094" + integrity sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + +"@inquirer/password@^4.0.23": + version "4.0.23" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-4.0.23.tgz#b9f5187c8c92fd7aa9eceb9d8f2ead0d7e7b000d" + integrity sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA== + dependencies: + "@inquirer/ansi" "^1.0.2" + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + +"@inquirer/prompts@^7.8.2": + version "7.10.1" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-7.10.1.tgz#e1436c0484cf04c22548c74e2cd239e989d5f847" + integrity sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg== + dependencies: + "@inquirer/checkbox" "^4.3.2" + "@inquirer/confirm" "^5.1.21" + "@inquirer/editor" "^4.2.23" + "@inquirer/expand" "^4.0.23" + "@inquirer/input" "^4.3.1" + "@inquirer/number" "^3.0.23" + "@inquirer/password" "^4.0.23" + "@inquirer/rawlist" "^4.1.11" + "@inquirer/search" "^3.2.2" + "@inquirer/select" "^4.4.2" + +"@inquirer/rawlist@^4.1.11": + version "4.1.11" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz#313c8c3ffccb7d41e990c606465726b4a898a033" + integrity sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/type" "^3.0.10" + yoctocolors-cjs "^2.1.3" + +"@inquirer/search@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-3.2.2.tgz#4cc6fd574dcd434e4399badc37c742c3fd534ac8" + integrity sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA== + dependencies: + "@inquirer/core" "^10.3.2" + "@inquirer/figures" "^1.0.15" + "@inquirer/type" "^3.0.10" + yoctocolors-cjs "^2.1.3" + +"@inquirer/select@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-4.4.2.tgz#2ac8fca960913f18f1d1b35323ed8fcd27d89323" + integrity sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w== + dependencies: + "@inquirer/ansi" "^1.0.2" + "@inquirer/core" "^10.3.2" + "@inquirer/figures" "^1.0.15" + "@inquirer/type" "^3.0.10" + yoctocolors-cjs "^2.1.3" + +"@inquirer/type@^3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.10.tgz#11ed564ec78432a200ea2601a212d24af8150d50" + integrity sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -1565,237 +1782,240 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" - integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== +"@jest/console@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.2.0.tgz#c52fcd5b58fdd2e8eb66b2fd8ae56f2f64d05b28" + integrity sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ== dependencies: - "@jest/types" "^29.6.3" + "@jest/types" "30.2.0" "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" + chalk "^4.1.2" + jest-message-util "30.2.0" + jest-util "30.2.0" slash "^3.0.0" -"@jest/core@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" - integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== - dependencies: - "@jest/console" "^29.7.0" - "@jest/reporters" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" +"@jest/core@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.2.0.tgz#813d59faa5abd5510964a8b3a7b17cc77b775275" + integrity sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ== + dependencies: + "@jest/console" "30.2.0" + "@jest/pattern" "30.0.1" + "@jest/reporters" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.7.0" - jest-config "^29.7.0" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-resolve-dependencies "^29.7.0" - jest-runner "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - jest-watcher "^29.7.0" - micromatch "^4.0.4" - pretty-format "^29.7.0" + ansi-escapes "^4.3.2" + chalk "^4.1.2" + ci-info "^4.2.0" + exit-x "^0.2.2" + graceful-fs "^4.2.11" + jest-changed-files "30.2.0" + jest-config "30.2.0" + jest-haste-map "30.2.0" + jest-message-util "30.2.0" + jest-regex-util "30.0.1" + jest-resolve "30.2.0" + jest-resolve-dependencies "30.2.0" + jest-runner "30.2.0" + jest-runtime "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" + jest-watcher "30.2.0" + micromatch "^4.0.8" + pretty-format "30.2.0" slash "^3.0.0" - strip-ansi "^6.0.0" -"@jest/environment@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" - integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== +"@jest/diff-sequences@30.0.1": + version "30.0.1" + resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" + integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== + +"@jest/environment@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.2.0.tgz#1e673cdb8b93ded707cf6631b8353011460831fa" + integrity sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g== dependencies: - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" + "@jest/fake-timers" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - jest-mock "^29.7.0" + jest-mock "30.2.0" -"@jest/expect-utils@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" - integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== +"@jest/expect-utils@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" + integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== dependencies: - jest-get-type "^29.6.3" + "@jest/get-type" "30.1.0" -"@jest/expect@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" - integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== +"@jest/expect@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.2.0.tgz#9a5968499bb8add2bbb09136f69f7df5ddbf3185" + integrity sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA== dependencies: - expect "^29.7.0" - jest-snapshot "^29.7.0" + expect "30.2.0" + jest-snapshot "30.2.0" -"@jest/fake-timers@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" - integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== +"@jest/fake-timers@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz#0941ddc28a339b9819542495b5408622dc9e94ec" + integrity sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw== dependencies: - "@jest/types" "^29.6.3" - "@sinonjs/fake-timers" "^10.0.2" + "@jest/types" "30.2.0" + "@sinonjs/fake-timers" "^13.0.0" "@types/node" "*" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -"@jest/globals@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" - integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== + jest-message-util "30.2.0" + jest-mock "30.2.0" + jest-util "30.2.0" + +"@jest/get-type@30.1.0": + version "30.1.0" + resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" + integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== + +"@jest/globals@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.2.0.tgz#2f4b696d5862664b89c4ee2e49ae24d2bb7e0988" + integrity sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw== + dependencies: + "@jest/environment" "30.2.0" + "@jest/expect" "30.2.0" + "@jest/types" "30.2.0" + jest-mock "30.2.0" + +"@jest/pattern@30.0.1": + version "30.0.1" + resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" + integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/types" "^29.6.3" - jest-mock "^29.7.0" + "@types/node" "*" + jest-regex-util "30.0.1" -"@jest/reporters@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" - integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== +"@jest/reporters@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.2.0.tgz#a36b28fcbaf0c4595250b108e6f20e363348fd91" + integrity sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" + "@jest/console" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" + "@jridgewell/trace-mapping" "^0.3.25" "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" + chalk "^4.1.2" + collect-v8-coverage "^1.0.2" + exit-x "^0.2.2" + glob "^10.3.10" + graceful-fs "^4.2.11" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" + istanbul-lib-source-maps "^5.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - jest-worker "^29.7.0" + jest-message-util "30.2.0" + jest-util "30.2.0" + jest-worker "30.2.0" slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" + string-length "^4.0.2" v8-to-istanbul "^9.0.1" -"@jest/schemas@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" - integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== +"@jest/schemas@30.0.5": + version "30.0.5" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" + integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== dependencies: - "@sinclair/typebox" "^0.27.8" + "@sinclair/typebox" "^0.34.0" -"@jest/source-map@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" - integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== +"@jest/snapshot-utils@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz#387858eb90c2f98f67bff327435a532ac5309fbe" + integrity sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug== dependencies: - "@jridgewell/trace-mapping" "^0.3.18" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" - integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== - dependencies: - "@jest/console" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" - integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== - dependencies: - "@jest/test-result" "^29.7.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - slash "^3.0.0" + "@jest/types" "30.2.0" + chalk "^4.1.2" + graceful-fs "^4.2.11" + natural-compare "^1.4.0" -"@jest/transform@^27.1.0": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/source-map@30.0.1": + version "30.0.1" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-30.0.1.tgz#305ebec50468f13e658b3d5c26f85107a5620aaa" + integrity sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" - micromatch "^4.0.4" - pirates "^4.0.4" + "@jridgewell/trace-mapping" "^0.3.25" + callsites "^3.1.0" + graceful-fs "^4.2.11" + +"@jest/test-result@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.2.0.tgz#9c0124377fb7996cdffb86eda3dbc56eacab363d" + integrity sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg== + dependencies: + "@jest/console" "30.2.0" + "@jest/types" "30.2.0" + "@types/istanbul-lib-coverage" "^2.0.6" + collect-v8-coverage "^1.0.2" + +"@jest/test-sequencer@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz#bf0066bc72e176d58f5dfa7f212b6e7eee44f221" + integrity sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q== + dependencies: + "@jest/test-result" "30.2.0" + graceful-fs "^4.2.11" + jest-haste-map "30.2.0" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" - -"@jest/transform@^29.7.0": - version "29.7.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" - integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.6.3" - "@jridgewell/trace-mapping" "^0.3.18" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" + +"@jest/transform@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" + integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== + dependencies: + "@babel/core" "^7.27.4" + "@jest/types" "30.2.0" + "@jridgewell/trace-mapping" "^0.3.25" + babel-plugin-istanbul "^7.0.1" + chalk "^4.1.2" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - micromatch "^4.0.4" - pirates "^4.0.4" + graceful-fs "^4.2.11" + jest-haste-map "30.2.0" + jest-regex-util "30.0.1" + jest-util "30.2.0" + micromatch "^4.0.8" + pirates "^4.0.7" slash "^3.0.0" - write-file-atomic "^4.0.2" + write-file-atomic "^5.0.1" -"@jest/types@^27.1.0", "@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/types@30.2.0": + version "30.2.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" + integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" + "@jest/pattern" "30.0.1" + "@jest/schemas" "30.0.5" + "@types/istanbul-lib-coverage" "^2.0.6" + "@types/istanbul-reports" "^3.0.4" "@types/node" "*" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" + "@types/yargs" "^17.0.33" + chalk "^4.1.2" -"@jest/types@^29.6.3": - version "29.6.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" - integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== +"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: - "@jest/schemas" "^29.6.3" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" - integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": @@ -1803,15 +2023,10 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" - integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" @@ -1821,70 +2036,74 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@next/env@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.5.7.tgz#5006f4460a7fa598a03e1c2aa4e59e45c71082d3" - integrity sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA== - -"@next/eslint-plugin-next@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.7.tgz#9a8cd86a7a27b8f370ec3b130e598688c869bdc6" - integrity sha512-c4vuEOOXeib4js5gDq+zFqAAdRGXX6T0d+zFETiQkRwy7vyj5lBov1dW0Z09nDst2lvxo7VEcKrQMUBH5Vgx7Q== - dependencies: - glob "7.1.7" - -"@next/swc-darwin-arm64@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.7.tgz#b99b91c04a884ba1272a3bd5db2b6f47a5bb10c2" - integrity sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g== - -"@next/swc-darwin-x64@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.7.tgz#0a30d1c40430ed09ef4384bd90148e68badbf5e5" - integrity sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g== - -"@next/swc-linux-arm64-gnu@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.7.tgz#f6464e423186494d44ae9544c76b50e661682bc0" - integrity sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg== - -"@next/swc-linux-arm64-musl@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.7.tgz#88b62e006d00fc31359723f96cbd601132812873" - integrity sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg== - -"@next/swc-linux-x64-gnu@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.7.tgz#68d31a7c75f1b0dbc408f9fe49ac878c6723446c" - integrity sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw== - -"@next/swc-linux-x64-musl@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.7.tgz#09c9c3c667abd55f2af0b4e464342350baeabdb3" - integrity sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA== - -"@next/swc-win32-arm64-msvc@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.7.tgz#3314966f960a22ee95adb8285e0927c9e4ba7205" - integrity sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA== - -"@next/swc-win32-ia32-msvc@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.7.tgz#fd118f3bd5a87453b252eb3c5fae54ddce035026" - integrity sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA== - -"@next/swc-win32-x64-msvc@13.5.7": - version "13.5.7" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.7.tgz#3eff03a5a80281449c58fece85a780c1e6e3594b" - integrity sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g== +"@napi-rs/wasm-runtime@^0.2.11": + version "0.2.12" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2" + integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== + dependencies: + "@emnapi/core" "^1.4.3" + "@emnapi/runtime" "^1.4.3" + "@tybys/wasm-util" "^0.10.0" + +"@next/env@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.0.3.tgz#bda60d79929d4b8f554928854eadd7467e77ca4d" + integrity sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ== + +"@next/eslint-plugin-next@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.3.tgz#003142fb52928cf2e81ec1b159fab993fe58825c" + integrity sha512-6sPWmZetzFWMsz7Dhuxsdmbu3fK+/AxKRtj7OB0/3OZAI2MHB/v2FeYh271LZ9abvnM1WIwWc/5umYjx0jo5sQ== + dependencies: + fast-glob "3.3.1" + +"@next/swc-darwin-arm64@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.3.tgz#262b2d091d7bef43f1fbdb636d23e23628a76727" + integrity sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg== + +"@next/swc-darwin-x64@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.3.tgz#3fe4fce190c99c0e11cf6306ed2951466abcbd7d" + integrity sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg== + +"@next/swc-linux-arm64-gnu@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.3.tgz#d58d8db100ae4210ac88a431d85926f00523a490" + integrity sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A== + +"@next/swc-linux-arm64-musl@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.3.tgz#9654ee5da32f0d29f1f9f9ad56924f30d3225449" + integrity sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg== + +"@next/swc-linux-x64-gnu@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz#3cea2178b1c5d859701628b595622c117807f201" + integrity sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ== + +"@next/swc-linux-x64-musl@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.3.tgz#0e688deb07e25f2bf85ad4e712c2829f32ea1556" + integrity sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA== + +"@next/swc-win32-arm64-msvc@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.3.tgz#805e4f7dff4169e756c585c98924e7d73a325818" + integrity sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g== + +"@next/swc-win32-x64-msvc@16.0.3": + version "16.0.3" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.3.tgz#e77575eef8e243b31ae12303086aeab16abccbc0" + integrity sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1912,121 +2131,104 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@parcel/watcher-android-arm64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a" - integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ== +"@parcel/watcher-android-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" + integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== -"@parcel/watcher-darwin-arm64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f" - integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== +"@parcel/watcher-darwin-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67" + integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== -"@parcel/watcher-darwin-x64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb" - integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA== +"@parcel/watcher-darwin-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" + integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== -"@parcel/watcher-freebsd-x64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82" - integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw== +"@parcel/watcher-freebsd-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" + integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== -"@parcel/watcher-linux-arm-glibc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42" - integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA== +"@parcel/watcher-linux-arm-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" + integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== -"@parcel/watcher-linux-arm-musl@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4" - integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA== +"@parcel/watcher-linux-arm-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" + integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== -"@parcel/watcher-linux-arm64-glibc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03" - integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA== +"@parcel/watcher-linux-arm64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" + integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== -"@parcel/watcher-linux-arm64-musl@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732" - integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q== +"@parcel/watcher-linux-arm64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" + integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== -"@parcel/watcher-linux-x64-glibc@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d" - integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== +"@parcel/watcher-linux-x64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" + integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== -"@parcel/watcher-linux-x64-musl@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef" - integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== +"@parcel/watcher-linux-x64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" + integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== -"@parcel/watcher-win32-arm64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154" - integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig== +"@parcel/watcher-win32-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" + integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== -"@parcel/watcher-win32-ia32@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220" - integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA== +"@parcel/watcher-win32-ia32@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" + integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== -"@parcel/watcher-win32-x64@2.5.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7" - integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw== +"@parcel/watcher-win32-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" + integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== -"@parcel/watcher@^2.1.0": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10" - integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ== +"@parcel/watcher@^2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" + integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== dependencies: detect-libc "^1.0.3" is-glob "^4.0.3" micromatch "^4.0.5" node-addon-api "^7.0.0" optionalDependencies: - "@parcel/watcher-android-arm64" "2.5.0" - "@parcel/watcher-darwin-arm64" "2.5.0" - "@parcel/watcher-darwin-x64" "2.5.0" - "@parcel/watcher-freebsd-x64" "2.5.0" - "@parcel/watcher-linux-arm-glibc" "2.5.0" - "@parcel/watcher-linux-arm-musl" "2.5.0" - "@parcel/watcher-linux-arm64-glibc" "2.5.0" - "@parcel/watcher-linux-arm64-musl" "2.5.0" - "@parcel/watcher-linux-x64-glibc" "2.5.0" - "@parcel/watcher-linux-x64-musl" "2.5.0" - "@parcel/watcher-win32-arm64" "2.5.0" - "@parcel/watcher-win32-ia32" "2.5.0" - "@parcel/watcher-win32-x64" "2.5.0" - -"@peculiar/asn1-schema@^2.3.13", "@peculiar/asn1-schema@^2.3.8": - version "2.3.13" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.13.tgz#ec8509cdcbc0da3abe73fd7e690556b57a61b8f4" - integrity sha512-3Xq3a01WkHRZL8X04Zsfg//mGaA21xlL4tlVn4v2xGT0JStiztATRkMwa5b+f/HXmY2smsiLXYK46Gwgzvfg3g== - dependencies: - asn1js "^3.0.5" - pvtsutils "^1.3.5" - tslib "^2.6.2" - -"@peculiar/json-schema@^1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" - integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== - dependencies: - tslib "^2.0.0" + "@parcel/watcher-android-arm64" "2.5.1" + "@parcel/watcher-darwin-arm64" "2.5.1" + "@parcel/watcher-darwin-x64" "2.5.1" + "@parcel/watcher-freebsd-x64" "2.5.1" + "@parcel/watcher-linux-arm-glibc" "2.5.1" + "@parcel/watcher-linux-arm-musl" "2.5.1" + "@parcel/watcher-linux-arm64-glibc" "2.5.1" + "@parcel/watcher-linux-arm64-musl" "2.5.1" + "@parcel/watcher-linux-x64-glibc" "2.5.1" + "@parcel/watcher-linux-x64-musl" "2.5.1" + "@parcel/watcher-win32-arm64" "2.5.1" + "@parcel/watcher-win32-ia32" "2.5.1" + "@parcel/watcher-win32-x64" "2.5.1" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@peculiar/webcrypto@^1.4.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.5.0.tgz#9e57174c02c1291051c553600347e12b81469e10" - integrity sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg== - dependencies: - "@peculiar/asn1-schema" "^2.3.8" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.5" - tslib "^2.6.2" - webcrypto-core "^1.8.0" +"@pkgr/core@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@rc-component/async-validator@^5.0.3": version "5.0.4" @@ -2078,14 +2280,13 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/qrcode@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.0.0.tgz#48a8de5eb11d0e65926f1377c4b1ef4c888997f5" - integrity sha512-L+rZ4HXP2sJ1gHMGHjsg9jlYBX/SLN2D6OxP9Zn3qgtpMWtO2vUfxVFwiogHpAIqs54FnALxraUy/BCO1yRIgg== +"@rc-component/qrcode@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.1.0.tgz#4e38f1d7c2c8aae7f62d60ab110a842c1395db3e" + integrity sha512-ABA80Yer0c6I2+moqNY0kF3Y1NxIT6wDP/EINIqbiRbfZKP1HtHpKMh8WuTXLgVGYsoWG2g9/n0PgM8KdnJb4Q== dependencies: "@babel/runtime" "^7.24.7" classnames "^2.3.2" - rc-util "^5.38.0" "@rc-component/tour@~1.15.1": version "1.15.1" @@ -2098,24 +2299,19 @@ classnames "^2.3.2" rc-util "^5.24.4" -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.2.3": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.2.3.tgz#b47e945115e2d0a7f7e067dbb9ed76c91c1b4385" - integrity sha512-X1oFIpKoXAMXNDYCviOmTfuNuYxE4h5laBsyCqVAVMjNHxoF3/uiyA7XdegK1XbCvBbCZ6P6byWrEoDRpKL8+A== +"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.3.0.tgz#9499ada078daca9dd99d01f0f0743ee1ab9e398b" + integrity sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg== dependencies: "@babel/runtime" "^7.23.2" "@rc-component/portal" "^1.1.0" classnames "^2.3.2" rc-motion "^2.0.0" rc-resize-observer "^1.3.1" - rc-util "^5.38.0" - -"@repeaterjs/repeater@3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" - integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== + rc-util "^5.44.0" -"@repeaterjs/repeater@^3.0.4": +"@repeaterjs/repeater@^3.0.4", "@repeaterjs/repeater@^3.0.6": version "3.0.6" resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== @@ -2125,41 +2321,46 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@rushstack/eslint-patch@^1.3.3": - version "1.10.4" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz#427d5549943a9c6fce808e39ea64dbe60d4047f1" - integrity sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA== - -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@sinclair/typebox@^0.34.0": + version "0.34.41" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c" + integrity sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g== -"@sinonjs/commons@^3.0.0": +"@sinonjs/commons@^3.0.1": version "3.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^10.0.2": - version "10.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66" - integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA== +"@sinonjs/fake-timers@^13.0.0": + version "13.0.5" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz#36b9dbc21ad5546486ea9173d6bea063eb1717d5" + integrity sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw== dependencies: - "@sinonjs/commons" "^3.0.0" + "@sinonjs/commons" "^3.0.1" -"@swc/helpers@0.5.2": - version "0.5.2" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.2.tgz#85ea0c76450b61ad7d10a37050289eded783c27d" - integrity sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw== +"@swc/helpers@0.5.15": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== dependencies: - tslib "^2.4.0" + tslib "^2.8.0" + +"@theguild/federation-composition@^0.20.2": + version "0.20.2" + resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.20.2.tgz#d6b449c6cf4d3f09426fcdd9bb20f2cb83742a6b" + integrity sha512-QI4iSdrc4JvCWnMb1QbiHnEpdD33KGdiU66qfWOcM8ENebRGHkGjXDnUrVJ8F9g1dmCRMTNfn2NFGqTcDpeYXw== + dependencies: + constant-case "^3.0.4" + debug "4.4.3" + json5 "^2.2.3" + lodash.sortby "^4.7.0" "@tsconfig/node10@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" - integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + version "1.0.12" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== "@tsconfig/node12@^1.0.7": version "1.0.11" @@ -2176,7 +2377,14 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@tybys/wasm-util@^0.10.0": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" + integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== + dependencies: + tslib "^2.4.0" + +"@types/babel__core@^7.20.5": version "7.20.5" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -2188,9 +2396,9 @@ "@types/babel__traverse" "*" "@types/babel__generator@*": - version "7.6.8" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== + version "7.27.0" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== dependencies: "@babel/types" "^7.0.0" @@ -2202,39 +2410,26 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.6.tgz#8dc9f0ae0f202c08d8d4dab648912c8d6038e3f7" - integrity sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg== +"@types/babel__traverse@*": + version "7.28.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== dependencies: - "@babel/types" "^7.20.7" - -"@types/cookie@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" - integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== + "@babel/types" "^7.28.2" "@types/estree@^1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" - integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== - -"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" - integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ== - dependencies: - "@types/node" "*" + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/hoist-non-react-statics@^3.3.5": - version "3.3.5" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494" - integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg== +"@types/hoist-non-react-statics@^3.3.6": + version "3.3.7" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c" + integrity sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g== dependencies: - "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== @@ -2246,70 +2441,34 @@ dependencies: "@types/istanbul-lib-coverage" "*" -"@types/istanbul-reports@^3.0.0": +"@types/istanbul-reports@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" -"@types/js-yaml@^4.0.0": - version "4.0.9" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" - integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== - -"@types/json-schema@^7.0.15", "@types/json-schema@^7.0.6": +"@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/json-stable-stringify@^1.0.32": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.1.0.tgz#41393e6b7a9a67221607346af4a79783aeb28aea" - integrity sha512-ESTsHWB72QQq+pjUFIbEz9uSCZppD31YrVkbt2rnUciTYEvcwN6uZIhX5JZeBHqRlFJ41x/7MewCs7E2Qux6Cg== - "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*": - version "22.9.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" - integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== - dependencies: - undici-types "~6.19.8" - -"@types/node@^20.14.9": - version "20.17.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.6.tgz#6e4073230c180d3579e8c60141f99efdf5df0081" - integrity sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ== - dependencies: - undici-types "~6.19.2" - -"@types/parse-json@^4.0.0": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" - integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== - -"@types/prop-types@*": - version "15.7.13" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" - integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== - -"@types/qrcode.react@^1.0": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/qrcode.react/-/qrcode.react-1.0.5.tgz#d4ddcacee8f34d22a663029a230c5f0ab908cfb7" - integrity sha512-BghPtnlwvrvq8QkGa1H25YnN+5OIgCKFuQruncGWLGJYOzeSKiix/4+B9BtfKF2wf5ja8yfyWYA3OXju995G8w== +"@types/node@*", "@types/node@^24.10.1": + version "24.10.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" + integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== dependencies: - "@types/react" "*" + undici-types "~7.16.0" -"@types/react-dom@^18.0": - version "18.3.1" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.1.tgz#1e4654c08a9cdcfb6594c780ac59b55aad42fe07" - integrity sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ== - dependencies: - "@types/react" "*" +"@types/react-dom@19.2.3": + version "19.2.3" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" + integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== "@types/react-highlight-words@^0.20.0": version "0.20.0" @@ -2318,30 +2477,29 @@ dependencies: "@types/react" "*" -"@types/react-test-renderer@^18.0": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz#839502eae70058a4ae161f63385a8e7929cef4c0" - integrity sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw== +"@types/react-test-renderer@^19.1.0": + version "19.1.0" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" + integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0": - version "18.3.12" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.12.tgz#99419f182ccd69151813b7ee24b792fe08774f60" - integrity sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw== +"@types/react@*", "@types/react@19.2.5": + version "19.2.5" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.5.tgz#bb75da5c52a956ba7d2623dffbba0fdadc3e4145" + integrity sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw== dependencies: - "@types/prop-types" "*" csstype "^3.0.2" -"@types/stack-utils@^2.0.0": +"@types/stack-utils@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/ws@^8.0.0": - version "8.5.13" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.13.tgz#6414c280875e2691d0d1e080b05addbf5cb91e20" - integrity sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA== + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" @@ -2350,102 +2508,255 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== -"@types/yargs@^16.0.0": - version "16.0.9" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e" - integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA== +"@types/yargs@^17.0.33": + version "17.0.35" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" + integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== dependencies: "@types/yargs-parser" "*" -"@types/yargs@^17.0.8": - version "17.0.33" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.33.tgz#8c32303da83eec050a84b3c7ae7b9f922d13e32d" - integrity sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA== +"@typescript-eslint/eslint-plugin@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz#c53edeec13a79483f4ca79c298d5231b02e9dc17" + integrity sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/type-utils" "8.47.0" + "@typescript-eslint/utils" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" + graphemer "^1.4.0" + ignore "^7.0.0" + natural-compare "^1.4.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/parser@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.47.0.tgz#51b14ab2be2057ec0f57073b9ff3a9c078b0a964" + integrity sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ== dependencies: - "@types/yargs-parser" "*" + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" + debug "^4.3.4" -"@typescript-eslint/parser@^5.4.2 || ^6.0.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b" - integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ== +"@typescript-eslint/project-service@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.47.0.tgz#b8afc65e0527568018af911b702dcfbfdca16471" + integrity sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA== dependencies: - "@typescript-eslint/scope-manager" "6.21.0" - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/typescript-estree" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/tsconfig-utils" "^8.47.0" + "@typescript-eslint/types" "^8.47.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1" - integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg== +"@typescript-eslint/scope-manager@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz#d1c36a973a5499fed3a99e2e6a66aec5c9b1e542" + integrity sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" -"@typescript-eslint/types@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d" - integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg== +"@typescript-eslint/tsconfig-utils@8.47.0", "@typescript-eslint/tsconfig-utils@^8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz#4f178b62813538759e0989dd081c5474fad39b84" + integrity sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g== -"@typescript-eslint/typescript-estree@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46" - integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ== +"@typescript-eslint/type-utils@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz#b9b0141d99bd5bece3811d7eee68a002597ffa55" + integrity sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A== dependencies: - "@typescript-eslint/types" "6.21.0" - "@typescript-eslint/visitor-keys" "6.21.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/utils" "8.47.0" debug "^4.3.4" - globby "^11.1.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" + integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== + +"@typescript-eslint/typescript-estree@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" + integrity sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg== + dependencies: + "@typescript-eslint/project-service" "8.47.0" + "@typescript-eslint/tsconfig-utils" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/visitor-keys" "8.47.0" + debug "^4.3.4" + fast-glob "^3.3.2" is-glob "^4.0.3" - minimatch "9.0.3" - semver "^7.5.4" - ts-api-utils "^1.0.1" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^2.1.0" + +"@typescript-eslint/utils@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" + integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== + dependencies: + "@eslint-community/eslint-utils" "^4.7.0" + "@typescript-eslint/scope-manager" "8.47.0" + "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" -"@typescript-eslint/visitor-keys@6.21.0": - version "6.21.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47" - integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A== +"@typescript-eslint/visitor-keys@8.47.0": + version "8.47.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz#35f36ed60a170dfc9d4d738e78387e217f24c29f" + integrity sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ== dependencies: - "@typescript-eslint/types" "6.21.0" - eslint-visitor-keys "^3.4.1" + "@typescript-eslint/types" "8.47.0" + eslint-visitor-keys "^4.2.1" "@umijs/route-utils@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-4.0.1.tgz#156df5b3f2328059722d3ee7dd8f65e18c3cde8b" - integrity sha512-+1ixf1BTOLuH+ORb4x8vYMPeIt38n9q0fJDwhv9nSxrV46mxbLF0nmELIo9CKQB2gHfuC4+hww6xejJ6VYnBHQ== + version "4.0.3" + resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-4.0.3.tgz#70779ee069ac048509786bcf316368d3516ca5fd" + integrity sha512-zPEcYhl1cSfkSRDzzGgoD1mDvGjxoOTJFvkn55srfgdQ3NZe2ZMCScCU6DEnOxuKP1XDVf8pqyqCDVd2+RCQIw== "@umijs/use-params@^1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== -"@whatwg-node/events@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" - integrity sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA== - -"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1", "@whatwg-node/fetch@^0.8.2": - version "0.8.8" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.8.tgz#48c6ad0c6b7951a73e812f09dd22d75e9fa18cae" - integrity sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg== - dependencies: - "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.6" - busboy "^1.6.0" - urlpattern-polyfill "^8.0.0" - web-streams-polyfill "^3.2.1" - -"@whatwg-node/node-fetch@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz#e28816955f359916e2d830b68a64493124faa6d0" - integrity sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA== - dependencies: - "@whatwg-node/events" "^0.0.3" - busboy "^1.6.0" - fast-querystring "^1.1.1" - fast-url-parser "^1.1.3" - tslib "^2.3.1" +"@ungap/structured-clone@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== + +"@unrs/resolver-binding-android-arm-eabi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz#9f5b04503088e6a354295e8ea8fe3cb99e43af81" + integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== + +"@unrs/resolver-binding-android-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz#7414885431bd7178b989aedc4d25cccb3865bc9f" + integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== + +"@unrs/resolver-binding-darwin-arm64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz#b4a8556f42171fb9c9f7bac8235045e82aa0cbdf" + integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== + +"@unrs/resolver-binding-darwin-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz#fd4d81257b13f4d1a083890a6a17c00de571f0dc" + integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== + +"@unrs/resolver-binding-freebsd-x64@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz#d2513084d0f37c407757e22f32bd924a78cfd99b" + integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== + +"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz#844d2605d057488d77fab09705f2866b86164e0a" + integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== + +"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz#204892995cefb6bd1d017d52d097193bc61ddad3" + integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== + +"@unrs/resolver-binding-linux-arm64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz#023eb0c3aac46066a10be7a3f362e7b34f3bdf9d" + integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== + +"@unrs/resolver-binding-linux-arm64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz#9e6f9abb06424e3140a60ac996139786f5d99be0" + integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== + +"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz#b111417f17c9d1b02efbec8e08398f0c5527bb44" + integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== + +"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz#92ffbf02748af3e99873945c9a8a5ead01d508a9" + integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== + +"@unrs/resolver-binding-linux-riscv64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz#0bec6f1258fc390e6b305e9ff44256cb207de165" + integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== + +"@unrs/resolver-binding-linux-s390x-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz#577843a084c5952f5906770633ccfb89dac9bc94" + integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== + +"@unrs/resolver-binding-linux-x64-gnu@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" + integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== + +"@unrs/resolver-binding-linux-x64-musl@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz#bfb9af75f783f98f6a22c4244214efe4df1853d6" + integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== + +"@unrs/resolver-binding-wasm32-wasi@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz#752c359dd875684b27429500d88226d7cc72f71d" + integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== + dependencies: + "@napi-rs/wasm-runtime" "^0.2.11" + +"@unrs/resolver-binding-win32-arm64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz#ce5735e600e4c2fbb409cd051b3b7da4a399af35" + integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== + +"@unrs/resolver-binding-win32-ia32-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz#72fc57bc7c64ec5c3de0d64ee0d1810317bc60a6" + integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== + +"@unrs/resolver-binding-win32-x64-msvc@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" + integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== + +"@whatwg-node/disposablestack@^0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz#2064a1425ea66194def6df0c7a1851b6939c82bb" + integrity sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw== + dependencies: + "@whatwg-node/promise-helpers" "^1.0.0" + tslib "^2.6.3" + +"@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.12", "@whatwg-node/fetch@^0.10.13", "@whatwg-node/fetch@^0.10.4": + version "0.10.13" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.10.13.tgz#2d47190c620f134dda31c7f827976a909b2cca7a" + integrity sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q== + dependencies: + "@whatwg-node/node-fetch" "^0.8.3" + urlpattern-polyfill "^10.0.0" + +"@whatwg-node/node-fetch@^0.8.3": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz#72df6b3ea9d295bcedbc8f05cf2cfeabe654669f" + integrity sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA== + dependencies: + "@fastify/busboy" "^3.1.1" + "@whatwg-node/disposablestack" "^0.0.6" + "@whatwg-node/promise-helpers" "^1.3.2" + tslib "^2.6.3" + +"@whatwg-node/promise-helpers@^1.0.0", "@whatwg-node/promise-helpers@^1.2.1", "@whatwg-node/promise-helpers@^1.2.4", "@whatwg-node/promise-helpers@^1.3.0", "@whatwg-node/promise-helpers@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" + integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== + dependencies: + tslib "^2.6.3" "@wry/caches@^1.0.0": version "1.0.1" @@ -2468,13 +2779,6 @@ dependencies: tslib "^2.3.0" -"@wry/trie@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" - integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w== - dependencies: - tslib "^2.3.0" - "@wry/trie@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" @@ -2499,32 +2803,12 @@ acorn-walk@^8.1.1: dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1: - version "8.14.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" - integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== - -agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" - integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== - dependencies: - debug "^4.3.4" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== -ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2534,18 +2818,30 @@ ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" +ansi-escapes@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.2.0.tgz#31b25afa3edd3efc09d98c2fee831d460ff06b49" + integrity sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw== + dependencies: + environment "^1.0.0" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2560,67 +2856,72 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" -ansi-styles@^5.0.0: +ansi-styles@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0, ansi-styles@^6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + antd@^5.0: - version "5.21.6" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.21.6.tgz#60d8eb442f7f8019bab198e30ea5a8d6d224a6ce" - integrity sha512-EviOde/VEu+OsIKH5t6YXTMmmNeg9R85m0W5zXAo+Np8Latg9q10691JvAqOTMpnrRmbdeKUQL1Krp69Bzbe/g== + version "5.29.1" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.29.1.tgz#e124b1aaa709a534816c42d558da02a917b995cc" + integrity sha512-TTFVbpKbyL6cPfEoKq6Ya3BIjTUr7uDW9+7Z+1oysRv1gpcN7kQ4luH8r/+rXXwz4n6BIz1iBJ1ezKCdsdNW0w== dependencies: - "@ant-design/colors" "^7.1.0" - "@ant-design/cssinjs" "^1.21.1" - "@ant-design/cssinjs-utils" "^1.1.1" - "@ant-design/icons" "^5.5.1" + "@ant-design/colors" "^7.2.1" + "@ant-design/cssinjs" "^1.23.0" + "@ant-design/cssinjs-utils" "^1.1.3" + "@ant-design/fast-color" "^2.0.6" + "@ant-design/icons" "^5.6.1" "@ant-design/react-slick" "~1.1.2" - "@babel/runtime" "^7.25.6" - "@ctrl/tinycolor" "^3.6.1" + "@babel/runtime" "^7.26.0" "@rc-component/color-picker" "~2.0.1" "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/qrcode" "~1.0.0" + "@rc-component/qrcode" "~1.1.0" "@rc-component/tour" "~1.15.1" - "@rc-component/trigger" "^2.2.3" + "@rc-component/trigger" "^2.3.0" classnames "^2.5.1" copy-to-clipboard "^3.3.3" dayjs "^1.11.11" - rc-cascader "~3.28.2" - rc-checkbox "~3.3.0" - rc-collapse "~3.8.0" + rc-cascader "~3.34.0" + rc-checkbox "~3.5.0" + rc-collapse "~3.9.0" rc-dialog "~9.6.0" - rc-drawer "~7.2.0" - rc-dropdown "~4.2.0" - rc-field-form "~2.4.0" - rc-image "~7.11.0" - rc-input "~1.6.3" - rc-input-number "~9.2.0" - rc-mentions "~2.16.1" - rc-menu "~9.15.1" - rc-motion "^2.9.3" - rc-notification "~5.6.2" - rc-pagination "~4.3.0" - rc-picker "~4.6.15" + rc-drawer "~7.3.0" + rc-dropdown "~4.2.1" + rc-field-form "~2.7.1" + rc-image "~7.12.0" + rc-input "~1.8.0" + rc-input-number "~9.5.0" + rc-mentions "~2.20.0" + rc-menu "~9.16.1" + rc-motion "^2.9.5" + rc-notification "~5.6.4" + rc-pagination "~5.1.0" + rc-picker "~4.11.3" rc-progress "~4.0.0" - rc-rate "~2.13.0" - rc-resize-observer "^1.4.0" - rc-segmented "~2.5.0" - rc-select "~14.15.2" - rc-slider "~11.1.7" + rc-rate "~2.13.1" + rc-resize-observer "^1.4.3" + rc-segmented "~2.7.0" + rc-select "~14.16.8" + rc-slider "~11.1.9" rc-steps "~6.0.1" rc-switch "~4.1.0" - rc-table "~7.47.5" - rc-tabs "~15.3.0" - rc-textarea "~1.8.2" - rc-tooltip "~6.2.1" - rc-tree "~5.9.0" - rc-tree-select "~5.23.0" - rc-upload "~4.8.1" - rc-util "^5.43.0" + rc-table "~7.54.0" + rc-tabs "~15.7.0" + rc-textarea "~1.10.2" + rc-tooltip "~6.4.0" + rc-tree "~5.13.1" + rc-tree-select "~5.27.0" + rc-upload "~4.11.0" + rc-util "^5.44.4" scroll-into-view-if-needed "^3.1.0" throttle-debounce "^5.0.2" -anymatch@^3.0.3: +anymatch@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== @@ -2650,35 +2951,32 @@ aria-query@^5.3.2: resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== -array-buffer-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" - integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== +array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== dependencies: - call-bind "^1.0.5" - is-array-buffer "^3.0.4" + call-bound "^1.0.3" + is-array-buffer "^3.0.5" array-find-index@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== -array-includes@^3.1.6, array-includes@^3.1.8: - version "3.1.8" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.8.tgz#5e370cbe172fdd5dd6530c1d4aadda25281ba97d" - integrity sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ== +array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - is-string "^1.0.7" - -array-tree-filter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190" - integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw== + es-abstract "^1.24.0" + es-object-atoms "^1.1.1" + get-intrinsic "^1.3.0" + is-string "^1.1.1" + math-intrinsics "^1.1.0" array-union@^2.1.0: version "2.1.0" @@ -2697,37 +2995,38 @@ array.prototype.findlast@^1.2.5: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.findlastindex@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz#8c35a755c72908719453f87145ca011e39334d0d" - integrity sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ== +array.prototype.findlastindex@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.2" + es-abstract "^1.23.9" es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-shim-unscopables "^1.0.2" + es-object-atoms "^1.1.1" + es-shim-unscopables "^1.1.0" -array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" - integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" -array.prototype.flatmap@^1.2.4, array.prototype.flatmap@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" - integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== +array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" array.prototype.tosorted@^1.1.4: version "1.1.4" @@ -2740,43 +3039,33 @@ array.prototype.tosorted@^1.1.4: es-errors "^1.3.0" es-shim-unscopables "^1.0.2" -arraybuffer.prototype.slice@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" - integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== dependencies: array-buffer-byte-length "^1.0.1" - call-bind "^1.0.5" + call-bind "^1.0.8" define-properties "^1.2.1" - es-abstract "^1.22.3" - es-errors "^1.2.1" - get-intrinsic "^1.2.3" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" - is-shared-array-buffer "^1.0.2" asap@^2.0.0, asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -asn1js@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" - integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== - dependencies: - pvtsutils "^1.3.2" - pvutils "^1.1.3" - tslib "^2.4.0" - ast-types-flow@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-function@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== auto-bind@~4.0.0: version "4.0.0" @@ -2791,82 +3080,55 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: - version "4.10.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.2.tgz#85228e3e1d8b8532a27659b332e39b7fa0e022df" - integrity sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w== + version "4.11.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6" + integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ== axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== -babel-jest@27.1.0: - version "27.1.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.1.0.tgz#e96ca04554fd32274439869e2b6d24de9d91bc4e" - integrity sha512-6NrdqzaYemALGCuR97QkC/FkFIEBWP5pw5TMJoUHZTVXyOgocujp6A0JE2V6gE0HtqAAv6VKU/nI+OCR1Z4gHA== - dependencies: - "@jest/transform" "^27.1.0" - "@jest/types" "^27.1.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.0.6" - chalk "^4.0.0" - graceful-fs "^4.2.4" - slash "^3.0.0" - -babel-jest@^29.0, babel-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" - integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== - dependencies: - "@jest/transform" "^29.7.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.6.3" - chalk "^4.0.0" - graceful-fs "^4.2.9" +babel-jest@30.2.0, babel-jest@^30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac" + integrity sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw== + dependencies: + "@jest/transform" "30.2.0" + "@types/babel__core" "^7.20.5" + babel-plugin-istanbul "^7.0.1" + babel-preset-jest "30.2.0" + chalk "^4.1.2" + graceful-fs "^4.2.11" slash "^3.0.0" -babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== +babel-plugin-istanbul@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" + integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-instrument "^6.0.2" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" - "@types/babel__traverse" "^7.0.6" - -babel-plugin-jest-hoist@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" - integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== +babel-plugin-jest-hoist@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz#94c250d36b43f95900f3a219241e0f4648191ce2" + integrity sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA== dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" + "@types/babel__core" "^7.20.5" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== -babel-preset-current-node-syntax@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz#9a929eafece419612ef4ae4f60b1862ebad8ef30" - integrity sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw== +babel-preset-current-node-syntax@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" + integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" @@ -2917,58 +3179,41 @@ babel-preset-fbjs@^3.4.0: "@babel/plugin-transform-template-literals" "^7.0.0" babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" -babel-preset-jest@^27.0.6: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== - dependencies: - babel-plugin-jest-hoist "^27.5.1" - babel-preset-current-node-syntax "^1.0.0" - -babel-preset-jest@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" - integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== +babel-preset-jest@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz#04717843e561347781d6d7f69c81e6bcc3ed11ce" + integrity sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ== dependencies: - babel-plugin-jest-hoist "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" + babel-plugin-jest-hoist "30.2.0" + babel-preset-current-node-syntax "^1.2.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.3.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +baseline-browser-mapping@^2.8.25: + version "2.8.29" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz#d8800b71399c783cb1bf2068c2bcc3b6cfd7892c" + integrity sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA== big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bl@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + version "1.1.12" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" @@ -2980,14 +3225,15 @@ braces@^3.0.3: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.24.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" - integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg== + version "4.28.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" + integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== dependencies: - caniuse-lite "^1.0.30001669" - electron-to-chromium "^1.5.41" - node-releases "^2.0.18" - update-browserslist-db "^1.1.1" + baseline-browser-mapping "^2.8.25" + caniuse-lite "^1.0.30001754" + electron-to-chromium "^1.5.249" + node-releases "^2.0.27" + update-browserslist-db "^1.1.4" bser@2.1.1: version "2.1.1" @@ -3001,43 +3247,38 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -busboy@1.6.0, busboy@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: - streamsearch "^1.1.0" + es-errors "^1.3.0" + function-bind "^1.1.2" -call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== +call-bind@^1.0.7, call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: + call-bind-apply-helpers "^1.0.0" es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" get-intrinsic "^1.2.4" - set-function-length "^1.2.1" + set-function-length "^1.2.2" -caller@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/caller/-/caller-1.0.1.tgz#b851860f70e195db3d277395aa1a7e23ea30ecf5" - integrity sha512-tfj+ErW/0SbOyfoXriRtdjCMTwrZAvLXj2jHqlh8YCcgoZVzUI22E/JJLxbiuZqDs0Ke7BuRrHTVuxm3EwYbJQ== +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" -callsites@^3.0.0: +callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@4.1.2, camel-case@^4.1.2: +camel-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== @@ -3050,15 +3291,15 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.0: +camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001669: - version "1.0.30001677" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001677.tgz#27c2e2c637e007cfa864a16f7dfe7cde66b38b5f" - integrity sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog== +caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001754: + version "1.0.30001756" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz#fe80104631102f88e58cad8aa203a2c3e5ec9ebd" + integrity sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A== capital-case@^1.0.4: version "1.0.4" @@ -3069,16 +3310,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@^2.4.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: +chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3086,21 +3318,14 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" -change-case-all@1.0.14: - version "1.0.14" - resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.14.tgz#bac04da08ad143278d0ac3dda7eccd39280bfba1" - integrity sha512-CWVm2uT7dmSHdO/z1CXT/n47mWonyypzBbuCy5tN7uMg22BsfkhwT6oHmFCAk+gL1LOOxhdbB9SZz3J1KTY3gA== +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: - change-case "^4.1.2" - is-lower-case "^2.0.2" - is-upper-case "^2.0.2" - lower-case "^2.0.2" - lower-case-first "^2.0.2" - sponge-case "^1.0.1" - swap-case "^2.0.2" - title-case "^3.0.3" - upper-case "^2.0.2" - upper-case-first "^2.0.2" + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" change-case-all@1.0.15: version "1.0.15" @@ -3141,57 +3366,47 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +chardet@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-2.1.1.tgz#5c75593704a642f71ee53717df234031e65373c8" + integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== -ci-info@^3.2.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.2.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" + integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== -cjs-module-lexer@^1.0.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" - integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== +cjs-module-lexer@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz#bff23b0609cc9afa428bd35f1918f7d03b448562" + integrity sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ== classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== +cli-cursor@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.5.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + restore-cursor "^5.0.0" -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== +cli-truncate@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.1.1.tgz#455476face9904d94b7d11e98d9adbca15292ea5" + integrity sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A== dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" + slice-ansi "^7.1.0" + string-width "^8.0.0" -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== -client-only@0.0.1, client-only@^0.0.1: +client-only@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== @@ -3214,20 +3429,15 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== -collect-v8-coverage@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9" - integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== +collect-v8-coverage@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" + integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== color-convert@^1.9.0: version "1.9.3" @@ -3248,52 +3458,43 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" - integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== - dependencies: - color-convert "^2.0.1" - color-string "^1.9.0" +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.16: +colorette@^2.0.20: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== -common-tags@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" - integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== - common-tags@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== compute-scroll-into-view@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz#753f11d972596558d8fe7c6bcbc8497690ab4c87" - integrity sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg== + version "3.1.1" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz#02c3386ec531fb6a9881967388e53e8564f3e9aa" + integrity sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw== concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +concurrently@^9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.1.tgz#248ea21b95754947be2dad9c3e4b60f18ca4e44f" + integrity sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng== + dependencies: + chalk "4.1.2" + rxjs "7.8.2" + shell-quote "1.8.3" + supports-color "8.1.1" + tree-kill "1.2.2" + yargs "17.7.2" + constant-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" @@ -3303,20 +3504,15 @@ constant-case@^3.0.4: tslib "^2.0.3" upper-case "^2.0.2" -convert-source-map@^1.4.0, convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" - integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== +cookie@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" + integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== copy-to-clipboard@^3.3.3: version "3.3.3" @@ -3325,39 +3521,25 @@ copy-to-clipboard@^3.3.3: dependencies: toggle-selection "^1.0.6" -cosmiconfig@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" - integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== +cosmiconfig@^8.1.0: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== dependencies: - import-fresh "^3.2.1" + import-fresh "^3.3.0" js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - -cosmiconfig@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6" - integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.2.1" - parse-json "^5.0.0" + parse-json "^5.2.0" path-type "^4.0.0" - yaml "^1.10.0" -create-jest@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" - integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-config "^29.7.0" - jest-util "^29.7.0" - prompts "^2.0.1" + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" create-require@^1.1.0: version "1.1.1" @@ -3365,82 +3547,94 @@ create-require@^1.1.0: integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.5: - version "3.1.8" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" - integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" + integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== + dependencies: + node-fetch "^2.7.0" + +cross-inspect@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" + integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A== dependencies: - node-fetch "^2.6.12" + tslib "^2.4.0" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== +cross-spawn@^7.0.3, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" -crypto-hash@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-3.1.0.tgz#8be4497e1feee8e75dfc545aa5348fb11ae167c5" - integrity sha512-HR8JlZ+Dn54Lc5gGWZJxJitWbOCUzWb9/AlyONGecBnYZ+n/ONvt0gQnEzDNpJMYeRrYO7KogQ4qwyTPKnWKEw== +crypto-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-4.0.0.tgz#c58a8737560980714fa086113de3b8f577b72178" + integrity sha512-KkkFriWj2teXIJOmftIRavBL4q8A7EZV24Nn9sWhIR4hdeS8u+q2A2p0tWZ1m4xVoP1zs3VJjVwwAUnztBb8uw== csstype@^3.0.2, csstype@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -data-view-buffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" - integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== +data-uri-to-buffer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== + +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== dependencies: - call-bind "^1.0.6" + call-bound "^1.0.3" es-errors "^1.3.0" - is-data-view "^1.0.1" + is-data-view "^1.0.2" -data-view-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" - integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== dependencies: - call-bind "^1.0.7" + call-bound "^1.0.3" es-errors "^1.3.0" - is-data-view "^1.0.1" + is-data-view "^1.0.2" -data-view-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" - integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== dependencies: - call-bind "^1.0.6" + call-bound "^1.0.2" es-errors "^1.3.0" is-data-view "^1.0.1" -dataloader@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" - integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== +dataloader@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" + integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: - version "1.11.13" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" - integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + version "1.11.19" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" + integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== -debounce@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" - integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== +debounce@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5" + integrity sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw== -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: - version "4.3.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" - integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== +debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" @@ -3461,28 +3655,21 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== -dedent@^1.0.0: - version "1.5.3" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" - integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== +dedent@^1.6.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" + integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.0, deepmerge@^4.2.2: +deepmerge@^4.0, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -3492,7 +3679,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -3506,6 +3693,16 @@ dependency-graph@^0.11.0: resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== +dependency-graph@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-1.0.0.tgz#bb5e85aec1310bc13b22dbd76e3196c4ee4c10d2" + integrity sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg== + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + detect-indent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" @@ -3516,12 +3713,12 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== -detect-libc@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" - integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== +detect-libc@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== -detect-newline@^3.0.0: +detect-newline@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== @@ -3534,11 +3731,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" - integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== - diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -3566,26 +3758,40 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dotenv@^16.0.0: - version "16.4.5" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" - integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== - dset@^3.1.2: version "3.1.4" resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== -electron-to-chromium@^1.5.41: - version "1.5.52" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.52.tgz#2bed832c95a56a195504f918150e548474687da8" - integrity sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ== +dunder-proto@^1.0.0, dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +electron-to-chromium@^1.5.249: + version "1.5.256" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz#f07f78de0893ab060f60ac897195c029a3b50d1a" + integrity sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ== emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emoji-regex@^10.3.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -3601,137 +3807,147 @@ emojis-list@^3.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== -enhanced-resolve@^5.15.0: - version "5.17.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" - integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +environment@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + version "1.3.4" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: - version "1.23.3" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" - integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== +es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== dependencies: - array-buffer-byte-length "^1.0.1" - arraybuffer.prototype.slice "^1.0.3" + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - data-view-buffer "^1.0.1" - data-view-byte-length "^1.0.1" - data-view-byte-offset "^1.0.0" - es-define-property "^1.0.0" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" es-errors "^1.3.0" - es-object-atoms "^1.0.0" - es-set-tostringtag "^2.0.3" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.6" - get-intrinsic "^1.2.4" - get-symbol-description "^1.0.2" - globalthis "^1.0.3" - gopd "^1.0.1" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" + has-proto "^1.2.0" + has-symbols "^1.1.0" hasown "^2.0.2" - internal-slot "^1.0.7" - is-array-buffer "^3.0.4" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" is-callable "^1.2.7" - is-data-view "^1.0.1" + is-data-view "^1.0.2" is-negative-zero "^2.0.3" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.3" - is-string "^1.0.7" - is-typed-array "^1.1.13" - is-weakref "^1.0.2" - object-inspect "^1.13.1" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" object-keys "^1.1.1" - object.assign "^4.1.5" - regexp.prototype.flags "^1.5.2" - safe-array-concat "^1.1.2" - safe-regex-test "^1.0.3" - string.prototype.trim "^1.2.9" - string.prototype.trimend "^1.0.8" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" string.prototype.trimstart "^1.0.8" - typed-array-buffer "^1.0.2" - typed-array-byte-length "^1.0.1" - typed-array-byte-offset "^1.0.2" - typed-array-length "^1.0.6" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.15" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + +es-define-property@^1.0.0, es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== -es-errors@^1.2.1, es-errors@^1.3.0: +es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-iterator-helpers@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz#2f1a3ab998b30cb2d10b195b587c6d9ebdebf152" - integrity sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q== +es-iterator-helpers@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" + integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" - es-abstract "^1.23.3" + es-abstract "^1.23.6" es-errors "^1.3.0" es-set-tostringtag "^2.0.3" function-bind "^1.1.2" - get-intrinsic "^1.2.4" + get-intrinsic "^1.2.6" globalthis "^1.0.4" - gopd "^1.0.1" + gopd "^1.2.0" has-property-descriptors "^1.0.2" - has-proto "^1.0.3" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - iterator.prototype "^1.1.3" - safe-array-concat "^1.1.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + iterator.prototype "^1.1.4" + safe-array-concat "^1.1.3" -es-object-atoms@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" - integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" - integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== +es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: - get-intrinsic "^1.2.4" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" has-tostringtag "^1.0.2" - hasown "^2.0.1" + hasown "^2.0.2" -es-shim-unscopables@^1.0.0, es-shim-unscopables@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" - integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== +es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== dependencies: - hasown "^2.0.0" + hasown "^2.0.2" -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" @@ -3753,20 +3969,20 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@^13.0.0: - version "13.5.7" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.5.7.tgz#fc5d86b22364c93d9279acab2a6f4848c4dbccaf" - integrity sha512-pdeUuL9KZ8qFzzKqCbxk6FXwG9dNEnot/3+qSFJqxdSGgkFUH8cgZus/meyCi2S0cTAsDbBEE030E6zvL9pUYQ== +eslint-config-next@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.0.3.tgz#986cf220a9a879f4648f2ead3f4b45a4201c5a92" + integrity sha512-5F6qDjcZldf0Y0ZbqvWvap9xzYUxyDf7/of37aeyhvkrQokj/4bT1JYWZdlWUr283aeVa+s52mPq9ogmGg+5dw== dependencies: - "@next/eslint-plugin-next" "13.5.7" - "@rushstack/eslint-patch" "^1.3.3" - "@typescript-eslint/parser" "^5.4.2 || ^6.0.0" + "@next/eslint-plugin-next" "16.0.3" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" - eslint-plugin-import "^2.28.1" - eslint-plugin-jsx-a11y "^6.7.1" - eslint-plugin-react "^7.33.2" - eslint-plugin-react-hooks "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + eslint-plugin-import "^2.32.0" + eslint-plugin-jsx-a11y "^6.10.0" + eslint-plugin-react "^7.37.0" + eslint-plugin-react-hooks "^7.0.0" + globals "16.4.0" + typescript-eslint "^8.46.0" eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: version "0.3.9" @@ -3778,52 +3994,51 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: resolve "^1.22.4" eslint-import-resolver-typescript@^3.5.2: - version "3.6.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz#bb8e388f6afc0f940ce5d2c5fd4a3d147f038d9e" - integrity sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA== + version "3.10.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2" + integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ== dependencies: "@nolyfill/is-core-module" "1.0.39" - debug "^4.3.5" - enhanced-resolve "^5.15.0" - eslint-module-utils "^2.8.1" - fast-glob "^3.3.2" - get-tsconfig "^4.7.5" - is-bun-module "^1.0.2" - is-glob "^4.0.3" - -eslint-module-utils@^2.12.0, eslint-module-utils@^2.8.1: - version "2.12.0" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" - integrity sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg== + debug "^4.4.0" + get-tsconfig "^4.10.0" + is-bun-module "^2.0.0" + stable-hash "^0.0.5" + tinyglobby "^0.2.13" + unrs-resolver "^1.6.2" + +eslint-module-utils@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" + integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== dependencies: debug "^3.2.7" -eslint-plugin-import@^2.28.1: - version "2.31.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz#310ce7e720ca1d9c0bb3f69adfd1c6bdd7d9e0e7" - integrity sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A== +eslint-plugin-import@^2.32.0: + version "2.32.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" + integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== dependencies: "@rtsao/scc" "^1.1.0" - array-includes "^3.1.8" - array.prototype.findlastindex "^1.2.5" - array.prototype.flat "^1.3.2" - array.prototype.flatmap "^1.3.2" + array-includes "^3.1.9" + array.prototype.findlastindex "^1.2.6" + array.prototype.flat "^1.3.3" + array.prototype.flatmap "^1.3.3" debug "^3.2.7" doctrine "^2.1.0" eslint-import-resolver-node "^0.3.9" - eslint-module-utils "^2.12.0" + eslint-module-utils "^2.12.1" hasown "^2.0.2" - is-core-module "^2.15.1" + is-core-module "^2.16.1" is-glob "^4.0.3" minimatch "^3.1.2" object.fromentries "^2.0.8" object.groupby "^1.0.3" - object.values "^1.2.0" + object.values "^1.2.1" semver "^6.3.1" - string.prototype.trimend "^1.0.8" + string.prototype.trimend "^1.0.9" tsconfig-paths "^3.15.0" -eslint-plugin-jsx-a11y@^6.7.1: +eslint-plugin-jsx-a11y@^6.10.0: version "6.10.2" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== @@ -3844,78 +4059,84 @@ eslint-plugin-jsx-a11y@^6.7.1: safe-regex-test "^1.0.3" string.prototype.includes "^2.0.1" -"eslint-plugin-react-hooks@^4.5.0 || 5.0.0-canary-7118f5dd7-20230705": - version "5.0.0-canary-7118f5dd7-20230705" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz#4d55c50e186f1a2b0636433d2b0b2f592ddbccfd" - integrity sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw== +eslint-plugin-react-hooks@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" + integrity sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA== + dependencies: + "@babel/core" "^7.24.4" + "@babel/parser" "^7.24.4" + hermes-parser "^0.25.1" + zod "^3.25.0 || ^4.0.0" + zod-validation-error "^3.5.0 || ^4.0.0" -eslint-plugin-react@^7.33.2: - version "7.37.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz#cd0935987876ba2900df2f58339f6d92305acc7a" - integrity sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w== +eslint-plugin-react@^7.37.0: + version "7.37.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065" + integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" - array.prototype.flatmap "^1.3.2" + array.prototype.flatmap "^1.3.3" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" - es-iterator-helpers "^1.1.0" + es-iterator-helpers "^1.2.1" estraverse "^5.3.0" hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" - object.entries "^1.1.8" + object.entries "^1.1.9" object.fromentries "^2.0.8" - object.values "^1.2.0" + object.values "^1.2.1" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" - string.prototype.matchall "^4.0.11" + string.prototype.matchall "^4.0.12" string.prototype.repeat "^1.0.0" -eslint-scope@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" - integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== +eslint-scope@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" - integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== +eslint-visitor-keys@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.5.0: - version "9.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.14.0.tgz#534180a97c00af08bcf2b60b0ebf0c4d6c1b2c95" - integrity sha512-c2FHsVBr87lnUtjP4Yhvk4yEhKrQavGafRA/Se1ouse8PfbfC/Qh9Mxa00yWsZRlqeUB9raXip0aiiUZkgnr9g== + version "9.39.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" + integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== dependencies: - "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.18.0" - "@eslint/core" "^0.7.0" - "@eslint/eslintrc" "^3.1.0" - "@eslint/js" "9.14.0" - "@eslint/plugin-kit" "^0.2.0" + "@eslint/config-array" "^0.21.1" + "@eslint/config-helpers" "^0.4.2" + "@eslint/core" "^0.17.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.39.1" + "@eslint/plugin-kit" "^0.4.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.0" + "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - "@types/json-schema" "^7.0.15" ajv "^6.12.4" chalk "^4.0.0" - cross-spawn "^7.0.2" + cross-spawn "^7.0.6" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.2.0" - eslint-visitor-keys "^4.2.0" - espree "^10.3.0" + eslint-scope "^8.4.0" + eslint-visitor-keys "^4.2.1" + espree "^10.4.0" esquery "^1.5.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3930,16 +4151,15 @@ eslint@^9.5.0: minimatch "^3.1.2" natural-compare "^1.4.0" optionator "^0.9.3" - text-table "^0.2.0" -espree@^10.0.1, espree@^10.3.0: - version "10.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" - integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== +espree@^10.0.1, espree@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== dependencies: - acorn "^8.14.0" + acorn "^8.15.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.0" + eslint-visitor-keys "^4.2.1" esprima@^4.0.0: version "4.0.1" @@ -3970,7 +4190,12 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -execa@^5.0.0: +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== @@ -3985,50 +4210,32 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" - integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== - dependencies: - "@jest/expect-utils" "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" +exit-x@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" + integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== +expect@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" + integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extract-files@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" - integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== - -fast-decode-uri-component@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" - integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + "@jest/expect-utils" "30.2.0" + "@jest/get-type" "30.1.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-mock "30.2.0" + jest-util "30.2.0" fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.1.1, fast-glob@^3.2.9, fast-glob@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== +fast-glob@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -4036,6 +4243,17 @@ fast-glob@^3.1.1, fast-glob@^3.2.9, fast-glob@^3.3.2: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.2.9, fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.8" + fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -4046,28 +4264,14 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-querystring@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" - integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== - dependencies: - fast-decode-uri-component "^1.0.1" - -fast-url-parser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== - dependencies: - punycode "^1.3.2" - fastq@^1.6.0: - version "1.17.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" - integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + version "1.19.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== dependencies: reusify "^1.0.4" -fb-watchman@^2.0.0: +fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== @@ -4092,12 +4296,18 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^1.0.35" -figures@^3.0.0: +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: - escape-string-regexp "^1.0.5" + node-domexception "^1.0.0" + web-streams-polyfill "^3.0.3" file-entry-cache@^8.0.0: version "8.0.0" @@ -4138,23 +4348,38 @@ flat-cache@^4.0.0: keyv "^4.5.4" flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + +for-each@^0.3.3, for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + +foreground-child@^3.1.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" - integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== +formdata-polyfill@^4.0.10: + version "4.0.10" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: - is-callable "^1.1.3" + fetch-blob "^3.1.2" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2: +fsevents@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -4164,22 +4389,29 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" - integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== +function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gensync@1.0.0-beta.2, gensync@^1.0.0-beta.2: +generator-function@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== + +gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -4189,40 +4421,58 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== +get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.0, get-east-asian-width@^1.3.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" + integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== + +get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" es-errors "^1.3.0" + es-object-atoms "^1.1.1" function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.0, get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stream@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" - integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== dependencies: - call-bind "^1.0.5" + call-bound "^1.0.3" es-errors "^1.3.0" - get-intrinsic "^1.2.4" + get-intrinsic "^1.2.6" -get-tsconfig@^4.7.5: - version "4.8.1" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.1.tgz#8995eb391ae6e1638d251118c7b56de7eb425471" - integrity sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg== +get-tsconfig@^4.10.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" + integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== dependencies: resolve-pkg-maps "^1.0.0" @@ -4240,24 +4490,19 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== +glob@^10.3.10: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" -glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.1, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -4269,17 +4514,17 @@ glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" + integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== globals@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== -globalthis@^1.0.3, globalthis@^1.0.4: +globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== @@ -4287,19 +4532,7 @@ globalthis@^1.0.3, globalthis@^1.0.4: define-properties "^1.2.1" gopd "^1.0.1" -globby@11.0.4: - version "11.0.4" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" - integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - -globby@11.1.0, globby@^11.0.3, globby@^11.1.0: +globby@^11.0.3: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -4311,68 +4544,38 @@ globby@11.1.0, globby@^11.0.3, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" +gopd@^1.0.1, gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.2.11: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphql-config@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.5.0.tgz#257c2338950b8dce295a27f75c5f6c39f8f777b2" - integrity sha512-x6D0/cftpLUJ0Ch1e5sj1TZn6Wcxx4oMfmhaG9shM0DKajA9iR+j1z86GSTQ19fShbGvrSSvbIQsHku6aQ6BBw== - dependencies: - "@graphql-tools/graphql-file-loader" "^7.3.7" - "@graphql-tools/json-file-loader" "^7.3.7" - "@graphql-tools/load" "^7.5.5" - "@graphql-tools/merge" "^8.2.6" - "@graphql-tools/url-loader" "^7.9.7" - "@graphql-tools/utils" "^9.0.0" - cosmiconfig "8.0.0" - jiti "1.17.1" - minimatch "4.2.3" - string-env-interpolation "1.0.1" +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +graphql-config@^5.1.1: + version "5.1.5" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-5.1.5.tgz#34e0bfba88e74b6eefd889716a9378086f595f7f" + integrity sha512-mG2LL1HccpU8qg5ajLROgdsBzx/o2M6kgI3uAmoaXiSH9PCUbtIyLomLqUtCFaAeG2YCFsl0M5cfQ9rKmDoMVA== + dependencies: + "@graphql-tools/graphql-file-loader" "^8.0.0" + "@graphql-tools/json-file-loader" "^8.0.0" + "@graphql-tools/load" "^8.1.0" + "@graphql-tools/merge" "^9.0.0" + "@graphql-tools/url-loader" "^8.0.0" + "@graphql-tools/utils" "^10.0.0" + cosmiconfig "^8.1.0" + jiti "^2.0.0" + minimatch "^9.0.5" + string-env-interpolation "^1.0.1" tslib "^2.4.0" -graphql-let@^0.18: - version "0.18.6" - resolved "https://registry.yarnpkg.com/graphql-let/-/graphql-let-0.18.6.tgz#52424d3be32d6e42eaca9f2b6c12a76b858660cb" - integrity sha512-BMKDBpGEJm5Ybgaz9Mb4clEXtysDu7YUv1zSlgHY5DuqMoW3FVFjb2+kq6BikvzRQAiauNV+Yme4mWM0uvfpNw== - dependencies: - "@babel/core" "7.15.5" - "@babel/helper-plugin-utils" "7.14.5" - "@babel/parser" "7.15.5" - "@babel/traverse" "7.15.4" - "@graphql-tools/import" "6.4.0" - caller "1.0.1" - gensync "1.0.0-beta.2" - globby "11.0.4" - loader-utils "2.0.0" - log-update "4.0.0" - make-dir "3.1.0" - minimist "1.2.5" - schema-utils "3.0.0" - slash "3.0.0" - string-env-interpolation "1.0.1" - yaml "1.10.2" - optionalDependencies: - "@graphql-codegen/typescript-resolvers" "1.19.1" - babel-jest "27.1.0" - -graphql-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.1.0.tgz#f4eb2107967af3c7a5907eb3131c671eac89be4f" - integrity sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw== - dependencies: - "@graphql-typed-document-node/core" "^3.2.0" - cross-fetch "^3.1.5" - graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -4380,20 +4583,20 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: dependencies: tslib "^2.1.0" -graphql-ws@5.12.1: - version "5.12.1" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.12.1.tgz#c62d5ac54dbd409cc6520b0b39de374b3d59d0dd" - integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== +graphql-ws@^6.0.6: + version "6.0.6" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.6.tgz#e9c9ff85f4ddb5bbe6faa2721c0d27e6c11746d2" + integrity sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw== -graphql@^16.0.1: - version "16.9.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" - integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== +graphql@^16.12.0: + version "16.12.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7" + integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ== -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +has-bigints@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== has-flag@^3.0.0: version "3.0.0" @@ -4412,24 +4615,26 @@ has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: dependencies: es-define-property "^1.0.0" -has-proto@^1.0.1, has-proto@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== -has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: +has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" -hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: +hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== @@ -4444,6 +4649,18 @@ header-case@^2.0.4: capital-case "^1.0.4" tslib "^2.0.3" +hermes-estree@0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== + +hermes-parser@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== + dependencies: + hermes-estree "0.25.1" + highlight-words-core@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" @@ -4466,53 +4683,37 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-proxy-agent@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-6.1.1.tgz#dc04f1a84e09511740cfbd984a56f86cc42e4277" - integrity sha512-JRCz+4Whs6yrrIoIlrH+ZTmhrRwtMnmOHsHn8GFEn9O2sVfSE+DAZ3oyyGIKF8tjJEeSJmP89j7aTjVsSqsU0g== - dependencies: - agent-base "^7.1.0" - debug "^4.3.4" - -https-proxy-agent@^6.0.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-6.2.1.tgz#0965ab47371b3e531cf6794d1eb148710a992ba7" - integrity sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA== - dependencies: - agent-base "^7.0.2" - debug "4" - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== +iconv-lite@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" + integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== dependencies: - safer-buffer ">= 2.1.2 < 3" + safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -ignore@^5.1.4, ignore@^5.2.0: +ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== +ignore@^7.0.0: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== + immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== +import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -4522,7 +4723,7 @@ import-from@4.0.0: resolved "https://registry.yarnpkg.com/import-from/-/import-from-4.0.0.tgz#2710b8d66817d232e16f4166e319248d3d5492e2" integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== -import-local@^3.0.2: +import-local@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== @@ -4535,11 +4736,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4548,40 +4744,19 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inquirer@^8.0.0: - version "8.2.6" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" - integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - wrap-ansi "^6.0.1" - -internal-slot@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" - integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== dependencies: es-errors "^1.3.0" - hasown "^2.0.0" - side-channel "^1.0.4" + hasown "^2.0.2" + side-channel "^1.1.0" invariant@^2.2.4: version "2.2.4" @@ -4598,107 +4773,121 @@ is-absolute@^1.0.0: is-relative "^1.0.0" is-windows "^1.0.1" -is-array-buffer@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" - integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== +is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-async-function@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" - integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== dependencies: - has-tostringtag "^1.0.0" + async-function "^1.0.0" + call-bound "^1.0.3" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: - has-bigints "^1.0.1" + has-bigints "^1.0.2" -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + call-bound "^1.0.3" + has-tostringtag "^1.0.2" -is-bun-module@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-1.2.1.tgz#495e706f42e29f086fd5fe1ac3c51f106062b9fc" - integrity sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q== +is-bun-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" + integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ== dependencies: - semver "^7.6.3" + semver "^7.7.1" -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" - integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== +is-core-module@^2.13.0, is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" -is-data-view@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" - integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== +is-data-view@^1.0.1, is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" is-typed-array "^1.1.13" -is-date-object@^1.0.1, is-date-object@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== +is-date-object@^1.0.5, is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== dependencies: - has-tostringtag "^1.0.0" + call-bound "^1.0.2" + has-tostringtag "^1.0.2" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-finalizationregistry@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" - integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== dependencies: - call-bind "^1.0.2" + call-bound "^1.0.3" is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-generator-fn@^2.0.0: +is-fullwidth-code-point@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" + integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== + dependencies: + get-east-asian-width "^1.3.1" + +is-generator-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-generator-function@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: - has-tostringtag "^1.0.0" + call-bound "^1.0.4" + generator-function "^2.0.0" + get-proto "^1.0.1" + has-tostringtag "^1.0.2" + safe-regex-test "^1.1.0" is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" @@ -4707,11 +4896,6 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - is-lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" @@ -4729,25 +4913,28 @@ is-negative-zero@^2.0.3: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== dependencies: - has-tostringtag "^1.0.0" + call-bound "^1.0.3" + has-tostringtag "^1.0.2" is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" is-relative@^1.0.0: version "1.0.0" @@ -4761,43 +4948,41 @@ is-set@^2.0.3: resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== -is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" - integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== dependencies: - call-bind "^1.0.7" + call-bound "^1.0.3" is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== dependencies: - has-tostringtag "^1.0.0" + call-bound "^1.0.3" + has-tostringtag "^1.0.2" -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== +is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== dependencies: - has-symbols "^1.0.2" + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" -is-typed-array@^1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== +is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: - which-typed-array "^1.1.14" - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + which-typed-array "^1.1.16" is-unc-path@^1.0.0: version "1.0.0" @@ -4823,20 +5008,20 @@ is-weakmap@^2.0.2: resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== +is-weakref@^1.0.2, is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== dependencies: - call-bind "^1.0.2" + call-bound "^1.0.3" is-weakset@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.3.tgz#e801519df8c0c43e12ff2834eead84ec9e624007" - integrity sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ== + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" is-windows@^1.0.1: version "1.0.2" @@ -4853,28 +5038,22 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: +isomorphic-ws@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== +isows@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" + integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== + istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== -istanbul-lib-instrument@^5.0.4: - version "5.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" - integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-instrument@^6.0.0: +istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== @@ -4894,465 +5073,409 @@ istanbul-lib-report@^3.0.0: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== +istanbul-lib-source-maps@^5.0.0: + version "5.0.6" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== dependencies: + "@jridgewell/trace-mapping" "^0.3.23" debug "^4.1.1" istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" istanbul-reports@^3.1.3: - version "3.1.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" - integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterator.prototype@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.3.tgz#016c2abe0be3bbdb8319852884f60908ac62bf9c" - integrity sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ== +iterator.prototype@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" + integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== dependencies: - define-properties "^1.2.1" - get-intrinsic "^1.2.1" - has-symbols "^1.0.3" - reflect.getprototypeof "^1.0.4" - set-function-name "^2.0.1" + define-data-property "^1.1.4" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.6" + get-proto "^1.0.0" + has-symbols "^1.1.0" + set-function-name "^2.0.2" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" javascript-stringify@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" - integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== +jest-changed-files@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz#602266e478ed554e1e1469944faa7efd37cee61c" + integrity sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ== dependencies: - execa "^5.0.0" - jest-util "^29.7.0" + execa "^5.1.1" + jest-util "30.2.0" p-limit "^3.1.0" -jest-circus@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" - integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== +jest-circus@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.2.0.tgz#98b8198b958748a2f322354311023d1d02e7603f" + integrity sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg== dependencies: - "@jest/environment" "^29.7.0" - "@jest/expect" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" + "@jest/environment" "30.2.0" + "@jest/expect" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - chalk "^4.0.0" + chalk "^4.1.2" co "^4.6.0" - dedent "^1.0.0" - is-generator-fn "^2.0.0" - jest-each "^29.7.0" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-runtime "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" + dedent "^1.6.0" + is-generator-fn "^2.1.0" + jest-each "30.2.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-runtime "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" p-limit "^3.1.0" - pretty-format "^29.7.0" - pure-rand "^6.0.0" + pretty-format "30.2.0" + pure-rand "^7.0.0" slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" - integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== - dependencies: - "@jest/core" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" - chalk "^4.0.0" - create-jest "^29.7.0" - exit "^0.1.2" - import-local "^3.0.2" - jest-config "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - yargs "^17.3.1" - -jest-config@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" - integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.7.0" - "@jest/types" "^29.6.3" - babel-jest "^29.7.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.7.0" - jest-environment-node "^29.7.0" - jest-get-type "^29.6.3" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-runner "^29.7.0" - jest-util "^29.7.0" - jest-validate "^29.7.0" - micromatch "^4.0.4" + stack-utils "^2.0.6" + +jest-cli@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.2.0.tgz#1780f8e9d66bf84a10b369aea60aeda7697dcc67" + integrity sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA== + dependencies: + "@jest/core" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" + chalk "^4.1.2" + exit-x "^0.2.2" + import-local "^3.2.0" + jest-config "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" + yargs "^17.7.2" + +jest-config@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.2.0.tgz#29df8c50e2ad801cc59c406b50176c18c362a90b" + integrity sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA== + dependencies: + "@babel/core" "^7.27.4" + "@jest/get-type" "30.1.0" + "@jest/pattern" "30.0.1" + "@jest/test-sequencer" "30.2.0" + "@jest/types" "30.2.0" + babel-jest "30.2.0" + chalk "^4.1.2" + ci-info "^4.2.0" + deepmerge "^4.3.1" + glob "^10.3.10" + graceful-fs "^4.2.11" + jest-circus "30.2.0" + jest-docblock "30.2.0" + jest-environment-node "30.2.0" + jest-regex-util "30.0.1" + jest-resolve "30.2.0" + jest-runner "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" + micromatch "^4.0.8" parse-json "^5.2.0" - pretty-format "^29.7.0" + pretty-format "30.2.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" - integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-docblock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" - integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" - integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== - dependencies: - "@jest/types" "^29.6.3" - chalk "^4.0.0" - jest-get-type "^29.6.3" - jest-util "^29.7.0" - pretty-format "^29.7.0" - -jest-environment-node@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" - integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/types" "^29.6.3" - "@types/node" "*" - jest-mock "^29.7.0" - jest-util "^29.7.0" - -jest-get-type@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" - integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== - -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== - dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" +jest-diff@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" + integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== + dependencies: + "@jest/diff-sequences" "30.0.1" + "@jest/get-type" "30.1.0" + chalk "^4.1.2" + pretty-format "30.2.0" + +jest-docblock@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" + integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== + dependencies: + detect-newline "^3.1.0" + +jest-each@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.2.0.tgz#39e623ae71641c2ac3ee69b3ba3d258fce8e768d" + integrity sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ== + dependencies: + "@jest/get-type" "30.1.0" + "@jest/types" "30.2.0" + chalk "^4.1.2" + jest-util "30.2.0" + pretty-format "30.2.0" + +jest-environment-node@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz#3def7980ebd2fd86e74efd4d2e681f55ab38da0f" + integrity sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA== + dependencies: + "@jest/environment" "30.2.0" + "@jest/fake-timers" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" + jest-mock "30.2.0" + jest-util "30.2.0" + jest-validate "30.2.0" -jest-haste-map@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" - integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== +jest-haste-map@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" + integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== dependencies: - "@jest/types" "^29.6.3" - "@types/graceful-fs" "^4.1.3" + "@jest/types" "30.2.0" "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.6.3" - jest-util "^29.7.0" - jest-worker "^29.7.0" - micromatch "^4.0.4" + anymatch "^3.1.3" + fb-watchman "^2.0.2" + graceful-fs "^4.2.11" + jest-regex-util "30.0.1" + jest-util "30.2.0" + jest-worker "30.2.0" + micromatch "^4.0.8" walker "^1.0.8" optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" - integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== - dependencies: - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-matcher-utils@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" - integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== - dependencies: - chalk "^4.0.0" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - pretty-format "^29.7.0" - -jest-message-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" - integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.6.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.7.0" + fsevents "^2.3.3" + +jest-leak-detector@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz#292fdca7b7c9cf594e1e570ace140b01d8beb736" + integrity sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ== + dependencies: + "@jest/get-type" "30.1.0" + pretty-format "30.2.0" + +jest-matcher-utils@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" + integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== + dependencies: + "@jest/get-type" "30.1.0" + chalk "^4.1.2" + jest-diff "30.2.0" + pretty-format "30.2.0" + +jest-message-util@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" + integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== + dependencies: + "@babel/code-frame" "^7.27.1" + "@jest/types" "30.2.0" + "@types/stack-utils" "^2.0.3" + chalk "^4.1.2" + graceful-fs "^4.2.11" + micromatch "^4.0.8" + pretty-format "30.2.0" slash "^3.0.0" - stack-utils "^2.0.3" + stack-utils "^2.0.6" -jest-mock@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" - integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== +jest-mock@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" + integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== dependencies: - "@jest/types" "^29.6.3" + "@jest/types" "30.2.0" "@types/node" "*" - jest-util "^29.7.0" + jest-util "30.2.0" -jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== -jest-regex-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== - -jest-regex-util@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" - integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== - -jest-resolve-dependencies@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" - integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== - dependencies: - jest-regex-util "^29.6.3" - jest-snapshot "^29.7.0" - -jest-resolve@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" - integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.7.0" - jest-validate "^29.7.0" - resolve "^1.20.0" - resolve.exports "^2.0.0" +jest-regex-util@30.0.1: + version "30.0.1" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" + integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== + +jest-resolve-dependencies@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz#3370e2c0b49cc560f6a7e8ec3a59dd99525e1a55" + integrity sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w== + dependencies: + jest-regex-util "30.0.1" + jest-snapshot "30.2.0" + +jest-resolve@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.2.0.tgz#2e2009cbd61e8f1f003355d5ec87225412cebcd7" + integrity sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A== + dependencies: + chalk "^4.1.2" + graceful-fs "^4.2.11" + jest-haste-map "30.2.0" + jest-pnp-resolver "^1.2.3" + jest-util "30.2.0" + jest-validate "30.2.0" slash "^3.0.0" - -jest-runner@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" - integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== - dependencies: - "@jest/console" "^29.7.0" - "@jest/environment" "^29.7.0" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" + unrs-resolver "^1.7.11" + +jest-runner@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.2.0.tgz#c62b4c3130afa661789705e13a07bdbcec26a114" + integrity sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ== + dependencies: + "@jest/console" "30.2.0" + "@jest/environment" "30.2.0" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - chalk "^4.0.0" + chalk "^4.1.2" emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.7.0" - jest-environment-node "^29.7.0" - jest-haste-map "^29.7.0" - jest-leak-detector "^29.7.0" - jest-message-util "^29.7.0" - jest-resolve "^29.7.0" - jest-runtime "^29.7.0" - jest-util "^29.7.0" - jest-watcher "^29.7.0" - jest-worker "^29.7.0" + exit-x "^0.2.2" + graceful-fs "^4.2.11" + jest-docblock "30.2.0" + jest-environment-node "30.2.0" + jest-haste-map "30.2.0" + jest-leak-detector "30.2.0" + jest-message-util "30.2.0" + jest-resolve "30.2.0" + jest-runtime "30.2.0" + jest-util "30.2.0" + jest-watcher "30.2.0" + jest-worker "30.2.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" - integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== - dependencies: - "@jest/environment" "^29.7.0" - "@jest/fake-timers" "^29.7.0" - "@jest/globals" "^29.7.0" - "@jest/source-map" "^29.6.3" - "@jest/test-result" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" +jest-runtime@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.2.0.tgz#395ea792cde048db1b0cd1a92dc9cb9f1921bf8a" + integrity sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg== + dependencies: + "@jest/environment" "30.2.0" + "@jest/fake-timers" "30.2.0" + "@jest/globals" "30.2.0" + "@jest/source-map" "30.0.1" + "@jest/test-result" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.7.0" - jest-message-util "^29.7.0" - jest-mock "^29.7.0" - jest-regex-util "^29.6.3" - jest-resolve "^29.7.0" - jest-snapshot "^29.7.0" - jest-util "^29.7.0" + chalk "^4.1.2" + cjs-module-lexer "^2.1.0" + collect-v8-coverage "^1.0.2" + glob "^10.3.10" + graceful-fs "^4.2.11" + jest-haste-map "30.2.0" + jest-message-util "30.2.0" + jest-mock "30.2.0" + jest-regex-util "30.0.1" + jest-resolve "30.2.0" + jest-snapshot "30.2.0" + jest-util "30.2.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.9" - -jest-snapshot@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" - integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.7.0" - "@jest/transform" "^29.7.0" - "@jest/types" "^29.6.3" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.7.0" - graceful-fs "^4.2.9" - jest-diff "^29.7.0" - jest-get-type "^29.6.3" - jest-matcher-utils "^29.7.0" - jest-message-util "^29.7.0" - jest-util "^29.7.0" - natural-compare "^1.4.0" - pretty-format "^29.7.0" - semver "^7.5.3" - -jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" - integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== - dependencies: - "@jest/types" "^29.6.3" +jest-snapshot@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz#266fbbb4b95fc4665ce6f32f1f38eeb39f4e26d0" + integrity sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA== + dependencies: + "@babel/core" "^7.27.4" + "@babel/generator" "^7.27.5" + "@babel/plugin-syntax-jsx" "^7.27.1" + "@babel/plugin-syntax-typescript" "^7.27.1" + "@babel/types" "^7.27.3" + "@jest/expect-utils" "30.2.0" + "@jest/get-type" "30.1.0" + "@jest/snapshot-utils" "30.2.0" + "@jest/transform" "30.2.0" + "@jest/types" "30.2.0" + babel-preset-current-node-syntax "^1.2.0" + chalk "^4.1.2" + expect "30.2.0" + graceful-fs "^4.2.11" + jest-diff "30.2.0" + jest-matcher-utils "30.2.0" + jest-message-util "30.2.0" + jest-util "30.2.0" + pretty-format "30.2.0" + semver "^7.7.2" + synckit "^0.11.8" + +jest-util@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" + integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== + dependencies: + "@jest/types" "30.2.0" "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" - integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== - dependencies: - "@jest/types" "^29.6.3" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.6.3" + chalk "^4.1.2" + ci-info "^4.2.0" + graceful-fs "^4.2.11" + picomatch "^4.0.2" + +jest-validate@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.2.0.tgz#273eaaed4c0963b934b5b31e96289edda6e0a2ef" + integrity sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw== + dependencies: + "@jest/get-type" "30.1.0" + "@jest/types" "30.2.0" + camelcase "^6.3.0" + chalk "^4.1.2" leven "^3.1.0" - pretty-format "^29.7.0" + pretty-format "30.2.0" -jest-watcher@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" - integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== +jest-watcher@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.2.0.tgz#f9c055de48e18c979e7756a3917e596e2d69b07b" + integrity sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg== dependencies: - "@jest/test-result" "^29.7.0" - "@jest/types" "^29.6.3" + "@jest/test-result" "30.2.0" + "@jest/types" "30.2.0" "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" + ansi-escapes "^4.3.2" + chalk "^4.1.2" emittery "^0.13.1" - jest-util "^29.7.0" - string-length "^4.0.1" + jest-util "30.2.0" + string-length "^4.0.2" -jest-worker@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== +jest-worker@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" + integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== dependencies: "@types/node" "*" + "@ungap/structured-clone" "^1.3.0" + jest-util "30.2.0" merge-stream "^2.0.0" - supports-color "^8.0.0" + supports-color "^8.1.1" -jest-worker@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" - integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== +jest@^30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-30.2.0.tgz#9f0a71e734af968f26952b5ae4b724af82681630" + integrity sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A== dependencies: - "@types/node" "*" - jest-util "^29.7.0" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@^29.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" - integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== - dependencies: - "@jest/core" "^29.7.0" - "@jest/types" "^29.6.3" - import-local "^3.0.2" - jest-cli "^29.7.0" + "@jest/core" "30.2.0" + "@jest/types" "30.2.0" + import-local "^3.2.0" + jest-cli "30.2.0" -jiti@1.17.1: - version "1.17.1" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.17.1.tgz#264daa43ee89a03e8be28c3d712ccc4eb9f1e8ed" - integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== - -jiti@^1.17.1: - version "1.21.6" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" - integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== - -jose@^4.11.4: - version "4.15.9" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.9.tgz#9b68eda29e9a0614c042fa29387196c7dd800100" - integrity sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA== +jiti@^2.0.0, jiti@^2.3.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -5360,24 +5483,24 @@ jose@^4.11.4: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + version "3.14.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== +js-yaml@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" jsesc@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-buffer@3.0.1: version "3.0.1" @@ -5399,16 +5522,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stable-stringify@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz#52d4361b47d49168bcc4e564189a42e5a7439454" - integrity sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg== - dependencies: - call-bind "^1.0.5" - isarray "^2.0.5" - jsonify "^0.0.1" - object-keys "^1.1.1" - json-to-pretty-yaml@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" @@ -5436,11 +5549,6 @@ json5@^2.1.2, json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jsonify@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" - integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== - "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" @@ -5458,11 +5566,6 @@ keyv@^4.5.4: dependencies: json-buffer "3.0.1" -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" @@ -5509,28 +5612,17 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -listr2@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.5.tgz#9dcc50221583e8b4c71c43f9c7dfd0ef546b75d5" - integrity sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.16" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.5.5" - through "^2.3.8" - wrap-ansi "^7.0.0" - -loader-utils@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== +listr2@^9.0.0: + version "9.0.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" + integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" + cli-truncate "^5.0.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^6.1.0" + rfdc "^1.4.1" + wrap-ansi "^9.0.0" loader-utils@^2.0.0: version "2.0.4" @@ -5565,12 +5657,17 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + +lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.0.0, log-symbols@^4.1.0: +log-symbols@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -5578,17 +5675,18 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -log-update@4.0.0, log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== +log-update@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" + ansi-escapes "^7.0.0" + cli-cursor "^5.0.0" + slice-ansi "^7.1.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -5609,6 +5707,11 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5616,13 +5719,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -make-dir@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-dir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -5647,6 +5743,11 @@ map-cache@^0.2.0: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + memoize-one@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" @@ -5662,12 +5763,12 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meros@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" - integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== +meros@^1.2.1, meros@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.2.tgz#4cb0d7f3d22074c6e7ae1d66f72c080bca2e414b" + integrity sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A== -micromatch@^4.0.4, micromatch@^4.0.5: +micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== @@ -5680,19 +5781,10 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimatch@4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" - integrity sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng== - dependencies: - brace-expansion "^1.1.7" - -minimatch@9.0.3: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" +mimic-function@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" @@ -5701,16 +5793,23 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -5723,43 +5822,46 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" + integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== nanoid@^3.3.6: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +napi-postinstall@^0.3.0: + version "0.3.4" + resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" + integrity sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@^13.0.0: - version "13.5.7" - resolved "https://registry.yarnpkg.com/next/-/next-13.5.7.tgz#deddbb6644b235f0f6be2bbb6facce9ce004fd8e" - integrity sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg== +next@16.0.3: + version "16.0.3" + resolved "https://registry.yarnpkg.com/next/-/next-16.0.3.tgz#f05d651805524320b9d6a644de87230779fa56bb" + integrity sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w== dependencies: - "@next/env" "13.5.7" - "@swc/helpers" "0.5.2" - busboy "1.6.0" - caniuse-lite "^1.0.30001406" + "@next/env" "16.0.3" + "@swc/helpers" "0.5.15" + caniuse-lite "^1.0.30001579" postcss "8.4.31" - styled-jsx "5.1.1" - watchpack "2.4.0" + styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "13.5.7" - "@next/swc-darwin-x64" "13.5.7" - "@next/swc-linux-arm64-gnu" "13.5.7" - "@next/swc-linux-arm64-musl" "13.5.7" - "@next/swc-linux-x64-gnu" "13.5.7" - "@next/swc-linux-x64-musl" "13.5.7" - "@next/swc-win32-arm64-msvc" "13.5.7" - "@next/swc-win32-ia32-msvc" "13.5.7" - "@next/swc-win32-x64-msvc" "13.5.7" + "@next/swc-darwin-arm64" "16.0.3" + "@next/swc-darwin-x64" "16.0.3" + "@next/swc-linux-arm64-gnu" "16.0.3" + "@next/swc-linux-arm64-musl" "16.0.3" + "@next/swc-linux-x64-gnu" "16.0.3" + "@next/swc-linux-x64-musl" "16.0.3" + "@next/swc-win32-arm64-msvc" "16.0.3" + "@next/swc-win32-x64-msvc" "16.0.3" + sharp "^0.34.4" no-case@^3.0.4: version "3.0.4" @@ -5774,22 +5876,36 @@ node-addon-api@^7.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== -node-fetch@^2.6.1, node-fetch@^2.6.12: +node-domexception@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" +node-fetch@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== + dependencies: + data-uri-to-buffer "^4.0.0" + fetch-blob "^3.1.4" + formdata-polyfill "^4.0.10" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.18: - version "2.0.18" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" - integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== nopt@^4.0.1: version "4.0.3" @@ -5843,34 +5959,37 @@ object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.4, object.assign@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" - integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== +object.assign@^4.1.4, object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== dependencies: - call-bind "^1.0.5" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" - has-symbols "^1.0.3" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" object-keys "^1.1.1" -object.entries@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.8.tgz#bffe6f282e01f4d17807204a24f8edd823599c41" - integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== +object.entries@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" + integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" define-properties "^1.2.1" - es-object-atoms "^1.0.0" + es-object-atoms "^1.1.1" object.fromentries@^2.0.8: version "2.0.8" @@ -5891,20 +6010,16 @@ object.groupby@^1.0.3: define-properties "^1.2.1" es-abstract "^1.23.2" -object.values@^1.1.6, object.values@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" - integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== +object.values@^1.1.6, object.values@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" es-object-atoms "^1.0.0" -omit.js@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f" - integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg== - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -5912,21 +6027,28 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.0, onetime@^5.1.2: +onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" +onetime@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== + dependencies: + mimic-function "^5.0.0" + optimism@^0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63" - integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ== + version "0.18.1" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" + integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== dependencies: "@wry/caches" "^1.0.0" "@wry/context" "^0.7.0" - "@wry/trie" "^0.4.3" + "@wry/trie" "^0.5.0" tslib "^2.3.0" optionator@^0.9.3: @@ -5941,27 +6063,12 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" -ora@^5.4.1: - version "5.4.1" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== @@ -5974,6 +6081,15 @@ osenv@^0.1.4: os-homedir "^1.0.0" os-tmpdir "^1.0.0" +own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -6002,18 +6118,16 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -6038,7 +6152,7 @@ parse-filepath@^1.0.2: map-cache "^0.2.0" path-root "^0.1.1" -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6096,30 +6210,43 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-to-regexp@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.0.0.tgz#92076ec6b2eaf08be7c3233484142c05e8866cf5" - integrity sha512-GAWaqWlTjYK/7SVpIUA6CTxmcg65SP30sbjdCvyYReosRkk7Z/LyHWwkK3Vu0FcIi0FNTADUs4eh1AsU5s10cg== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-to-regexp@8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" + integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -picocolors@^1.0.0, picocolors@^1.1.0: +picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pirates@^4.0.4: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== +picomatch@^4.0.2, picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +pirates@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== pkg-dir@^4.2.0: version "4.2.0" @@ -6129,9 +6256,9 @@ pkg-dir@^4.2.0: find-up "^4.0.0" possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + version "1.1.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== postcss@8.4.31: version "8.4.31" @@ -6148,18 +6275,18 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" - integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== + version "3.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== -pretty-format@^29.7.0: - version "29.7.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" - integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== +pretty-format@30.2.0: + version "30.2.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" + integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" + "@jest/schemas" "30.0.5" + ansi-styles "^5.2.0" + react-is "^18.3.1" promise@^7.1.1: version "7.3.1" @@ -6168,15 +6295,7 @@ promise@^7.1.1: dependencies: asap "~2.0.3" -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -6185,63 +6304,45 @@ prop-types@^15.5.8, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -pure-rand@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" - integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== - -pvtsutils@^1.3.2, pvtsutils@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" - integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== - dependencies: - tslib "^2.6.1" - -pvutils@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" - integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== +pure-rand@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-7.0.1.tgz#6f53a5a9e3e4a47445822af96821ca509ed37566" + integrity sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -rc-cascader@~3.28.2: - version "3.28.2" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.28.2.tgz#91720d3498261a7bff9fffc953501a8830f601fb" - integrity sha512-8f+JgM83iLTvjgdkgU7GfI4qY8icXOBP0cGZjOdx2iJAkEe8ucobxDQAVE69UD/c3ehCxZlcgEHeD5hFmypbUw== +rc-cascader@~3.34.0: + version "3.34.0" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.34.0.tgz#56f936ab6b1229bab7d558701ce9b9e96536582c" + integrity sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag== dependencies: - "@babel/runtime" "^7.12.5" - array-tree-filter "^2.1.0" + "@babel/runtime" "^7.25.7" classnames "^2.3.1" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.37.0" + rc-select "~14.16.2" + rc-tree "~5.13.0" + rc-util "^5.43.0" -rc-checkbox@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.3.0.tgz#0ffcb65ab78c7d2fcd1a0d6554af36786516bd02" - integrity sha512-Ih3ZaAcoAiFKJjifzwsGiT/f/quIkxJoklW4yKGho14Olulwn8gN7hOBve0/WGDg5o/l/5mL0w7ff7/YGvefVw== +rc-checkbox@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz#3ae2441e3a321774d390f76539e706864fcf5ff0" + integrity sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.3.2" rc-util "^5.25.2" -rc-collapse@~3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.8.0.tgz#02bcf81e1601aa185cd3b9fab0ceefd8dc11aefb" - integrity sha512-YVBkssrKPBG09TGfcWWGj8zJBYD9G3XuTy89t5iUmSXrIXEAnO1M+qjUxRW6b4Qi0+wNWG6MHJF/+US+nmIlzA== +rc-collapse@~3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.9.0.tgz#972404ce7724e1c9d1d2476543e1175404a36806" + integrity sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6259,10 +6360,10 @@ rc-dialog@~9.6.0: rc-motion "^2.3.0" rc-util "^5.21.0" -rc-drawer@~7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.2.0.tgz#8d7de2f1fd52f3ac5a25f54afbb8ac14c62e5663" - integrity sha512-9lOQ7kBekEJRdEpScHvtmEtXnAsy+NGDXiRWc2ZVC7QXAazNVbeT4EraQKYwCME8BJLa8Bxqxvs5swwyOepRwg== +rc-drawer@~7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.3.0.tgz#1bb5fe5f9da38b6a2b2a7dffc9fcb647252a328f" + integrity sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg== dependencies: "@babel/runtime" "^7.23.9" "@rc-component/portal" "^1.1.1" @@ -6270,29 +6371,29 @@ rc-drawer@~7.2.0: rc-motion "^2.6.1" rc-util "^5.38.1" -rc-dropdown@~4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.0.tgz#c6052fcfe9c701487b141e411cdc277dc7c6f061" - integrity sha512-odM8Ove+gSh0zU27DUj5cG1gNKg7mLWBYzB5E4nNLrLwBmYEgYP43vHKDGOVZcJSVElQBI0+jTQgjnq0NfLjng== +rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz#44729eb2a4272e0353d31ac060da21e606accb1c" + integrity sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== dependencies: "@babel/runtime" "^7.18.3" "@rc-component/trigger" "^2.0.0" classnames "^2.2.6" - rc-util "^5.17.0" + rc-util "^5.44.1" -rc-field-form@~2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.4.0.tgz#26997160d12ae43a94c356c1290bfc011c69b3ca" - integrity sha512-XZ/lF9iqf9HXApIHQHqzJK5v2w4mkUMsVqAzOyWVzoiwwXEavY6Tpuw7HavgzIoD+huVff4JghSGcgEfX6eycg== +rc-field-form@~2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.7.1.tgz#8bb1d7c6bf40e2b99b494816972252c327d9b5a0" + integrity sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/async-validator" "^5.0.3" rc-util "^5.32.2" -rc-image@~7.11.0: - version "7.11.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.11.0.tgz#18c77ea557a6fdbe26856c688a9aace1505c0e77" - integrity sha512-aZkTEZXqeqfPZtnSdNUnKQA0N/3MbgR7nUnZ+/4MfSFWPFHZau4p5r5ShaI0KPEMnNjv4kijSCFq/9wtJpwykw== +rc-image@~7.12.0: + version "7.12.0" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe" + integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/portal" "^1.0.2" @@ -6301,43 +6402,43 @@ rc-image@~7.11.0: rc-motion "^2.6.2" rc-util "^5.34.1" -rc-input-number@~9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.2.0.tgz#7e9344ff054421d2bfff0eebd7c1b8ef22d12220" - integrity sha512-5XZFhBCV5f9UQ62AZ2hFbEY8iZT/dm23Q1kAg0H8EvOgD3UDbYYJAayoVIkM3lQaCqYAW5gV0yV3vjw1XtzWHg== +rc-input-number@~9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38" + integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/mini-decimal" "^1.0.1" classnames "^2.2.5" - rc-input "~1.6.0" + rc-input "~1.8.0" rc-util "^5.40.1" -rc-input@~1.6.0, rc-input@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.6.3.tgz#f1708fc3d5e68f95cb20faeb3eed1df8543cd444" - integrity sha512-wI4NzuqBS8vvKr8cljsvnTUqItMfG1QbJoxovCgL+DX4eVUcHIjVwharwevIxyy7H/jbLryh+K7ysnJr23aWIA== +rc-input@~1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974" + integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-util "^5.18.1" -rc-mentions@~2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.16.1.tgz#5e54ebe3ce6cd79838846ff1c8cfaf2e7aa15cec" - integrity sha512-GnhSTGP9Mtv6pqFFGQze44LlrtWOjHNrUUAcsdo9DnNAhN4pwVPEWy4z+2jpjkiGlJ3VoXdvMHcNDQdfI9fEaw== +rc-mentions@~2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7" + integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ== dependencies: "@babel/runtime" "^7.22.5" "@rc-component/trigger" "^2.0.0" classnames "^2.2.6" - rc-input "~1.6.0" - rc-menu "~9.15.1" - rc-textarea "~1.8.0" + rc-input "~1.8.0" + rc-menu "~9.16.0" + rc-textarea "~1.10.0" rc-util "^5.34.1" -rc-menu@~9.15.1: - version "9.15.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.15.1.tgz#d8b38ea534a7f596a8da063881519e7eaafca698" - integrity sha512-UKporqU6LPfHnpPmtP6hdEK4iO5Q+b7BRv/uRpxdIyDGplZy9jwUjsnpev5bs3PQKB0H0n34WAPDfjAfn3kAPA== +rc-menu@~9.16.0, rc-menu@~9.16.1: + version "9.16.1" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.16.1.tgz#9df1168e41d87dc7164c582173e1a1d32011899f" + integrity sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/trigger" "^2.0.0" @@ -6346,19 +6447,19 @@ rc-menu@~9.15.1: rc-overflow "^1.3.1" rc-util "^5.27.0" -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.3: - version "2.9.3" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.3.tgz#b1bdaf816f1ccb3e4b3b0c531c3037a59286379e" - integrity sha512-rkW47ABVkic7WEB0EKJqzySpvDqwl60/tdkY7hWP7dYnh5pm0SzJpo54oW3TDUGXV5wfxXFmMkxrzRRbotQ0+w== +rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.5: + version "2.9.5" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" - rc-util "^5.43.0" + rc-util "^5.44.0" -rc-notification@~5.6.2: - version "5.6.2" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.2.tgz#8525b32d49dd96ec974acae61d1d1eabde61463a" - integrity sha512-Id4IYMoii3zzrG0lB0gD6dPgJx4Iu95Xu0BQrhHIbp7ZnAZbLqdqQ73aIWH0d0UFcElxwaKjnzNovTjo7kXz7g== +rc-notification@~5.6.4: + version "5.6.4" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544" + integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6366,28 +6467,28 @@ rc-notification@~5.6.2: rc-util "^5.20.1" rc-overflow@^1.3.1, rc-overflow@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.3.2.tgz#72ee49e85a1308d8d4e3bd53285dc1f3e0bcce2c" - integrity sha512-nsUm78jkYAoPygDAcGZeC2VwIg/IBGSodtOY3pMof4W3M9qRJgqaDYm03ZayHlde3I6ipliAxbN0RUcGf5KOzw== + version "1.5.0" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.5.0.tgz#02e58a15199e392adfcc87e0d6e9e7c8e57f2771" + integrity sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-resize-observer "^1.0.0" rc-util "^5.37.0" -rc-pagination@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-4.3.0.tgz#c6022f820aa3a45fd734ae33a2915d39597dce1d" - integrity sha512-UubEWA0ShnroQ1tDa291Fzw6kj0iOeF26IsUObxYTpimgj4/qPCWVFl18RLZE+0Up1IZg0IK4pMn6nB3mjvB7g== +rc-pagination@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-5.1.0.tgz#a6e63a2c5db29e62f991282eb18a2d3ee725ba8b" + integrity sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.3.2" rc-util "^5.38.0" -rc-picker@~4.6.15: - version "4.6.15" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.6.15.tgz#1531c9c382a295e2d1f1f38440d6678b09cd0468" - integrity sha512-OWZ1yrMie+KN2uEUfYCfS4b2Vu6RC1FWwNI0s+qypsc3wRt7g+peuZKVIzXCTaJwyyZruo80+akPg2+GmyiJjw== +rc-picker@~4.11.3: + version "4.11.3" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.11.3.tgz#7e7e3ad83aa461c284b8391c697492d1c34d2cb8" + integrity sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== dependencies: "@babel/runtime" "^7.24.7" "@rc-component/trigger" "^2.0.0" @@ -6405,39 +6506,39 @@ rc-progress@~4.0.0: classnames "^2.2.6" rc-util "^5.16.1" -rc-rate@~2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.0.tgz#642f591ccf55c3a5d84d8d212caf1f7951d203a8" - integrity sha512-oxvx1Q5k5wD30sjN5tqAyWTvJfLNNJn7Oq3IeS4HxWfAiC4BOXMITNAsw7u/fzdtO4MS8Ki8uRLOzcnEuoQiAw== +rc-rate@~2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.1.tgz#29af7a3d4768362e9d4388f955a8b6389526b7fd" + integrity sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" rc-util "^5.0.1" -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.0.tgz#7bba61e6b3c604834980647cce6451914750d0cc" - integrity sha512-PnMVyRid9JLxFavTjeDXEXo65HCRqbmLBw9xX9gfC4BZiSzbLXKzW3jPz+J0P71pLbD5tBMTT+mkstV5gD0c9Q== +rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" + integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== dependencies: "@babel/runtime" "^7.20.7" classnames "^2.2.1" - rc-util "^5.38.0" + rc-util "^5.44.1" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.5.0.tgz#3b5423adf57459345c77c39c7581fde786a16c11" - integrity sha512-B28Fe3J9iUFOhFJET3RoXAPFJ2u47QvLSYcZWC4tFYNGPEjug5LAxEasZlA/PpAxhdOPqGWsGbSj7ftneukJnw== +rc-segmented@~2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.7.0.tgz#f56c2044abf8f03958b3a9a9d32987f10dcc4fc4" + integrity sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA== dependencies: "@babel/runtime" "^7.11.1" classnames "^2.2.1" rc-motion "^2.4.4" rc-util "^5.17.0" -rc-select@~14.15.0, rc-select@~14.15.2: - version "14.15.2" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.15.2.tgz#d85fcf3a708bdf837b003feeed653347b8980ad0" - integrity sha512-oNoXlaFmpqXYcQDzcPVLrEqS2J9c+/+oJuGrlXeVVX/gVgrbHa5YcyiRUXRydFjyuA7GP3elRuLF7Y3Tfwltlw== +rc-select@~14.16.2, rc-select@~14.16.8: + version "14.16.8" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97" + integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/trigger" "^2.1.1" @@ -6447,10 +6548,10 @@ rc-select@~14.15.0, rc-select@~14.15.2: rc-util "^5.16.1" rc-virtual-list "^3.5.2" -rc-slider@~11.1.7: - version "11.1.7" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.7.tgz#3de333b1ec84d53a7bda2f816bb4779423628f09" - integrity sha512-ytYbZei81TX7otdC0QvoYD72XSlxvTihNth5OeZ6PMXyEDq/vHdWFulQmfDGyXK1NwKwSlKgpvINOa88uT5g2A== +rc-slider@~11.1.9: + version "11.1.9" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.9.tgz#d872130fbf4ec51f28543d62e90451091d6f5208" + integrity sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.5" @@ -6474,66 +6575,67 @@ rc-switch@~4.1.0: classnames "^2.2.1" rc-util "^5.30.0" -rc-table@~7.47.5: - version "7.47.5" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.47.5.tgz#3c530200baa82346c7e72fe9b1dbd47d4aa15838" - integrity sha512-fzq+V9j/atbPIcvs3emuclaEoXulwQpIiJA6/7ey52j8+9cJ4P8DGmp4YzfUVDrb3qhgedcVeD6eRgUrokwVEQ== +rc-table@~7.54.0: + version "7.54.0" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.54.0.tgz#dedd4ea18d1189f2acdf90a80f04d8ca0111e16a" + integrity sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw== dependencies: "@babel/runtime" "^7.10.1" "@rc-component/context" "^1.4.0" classnames "^2.2.5" rc-resize-observer "^1.1.0" - rc-util "^5.41.0" + rc-util "^5.44.3" rc-virtual-list "^3.14.2" -rc-tabs@~15.3.0: - version "15.3.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.3.0.tgz#3fcc332fbb9307d5eb147e0404daca871fb92a89" - integrity sha512-lzE18r+zppT/jZWOAWS6ntdkDUKHOLJzqMi5UAij1LeKwOaQaupupAoI9Srn73GRzVpmGznkECMRrzkRusC40A== +rc-tabs@~15.7.0: + version "15.7.0" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.7.0.tgz#14ca2ee6213d00491a8b67ae26e2d35c256bf19a" + integrity sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA== dependencies: "@babel/runtime" "^7.11.2" classnames "2.x" rc-dropdown "~4.2.0" - rc-menu "~9.15.1" + rc-menu "~9.16.0" rc-motion "^2.6.2" rc-resize-observer "^1.0.0" rc-util "^5.34.1" -rc-textarea@~1.8.0, rc-textarea@~1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.8.2.tgz#57a6847304551c1883fc3fb0c5076d587f70bf7f" - integrity sha512-UFAezAqltyR00a8Lf0IPAyTd29Jj9ee8wt8DqXyDMal7r/Cg/nDt3e1OOv3Th4W6mKaZijjgwuPXhAfVNTN8sw== +rc-textarea@~1.10.0, rc-textarea@~1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.2.tgz#459e3574a95c32939c6793045a1e4db04cb514cc" + integrity sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ== dependencies: "@babel/runtime" "^7.10.1" classnames "^2.2.1" - rc-input "~1.6.0" + rc-input "~1.8.0" rc-resize-observer "^1.0.0" rc-util "^5.27.0" -rc-tooltip@~6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.2.1.tgz#9a8f0335c86443a0c20c2557933205f645a381b7" - integrity sha512-rws0duD/3sHHsD905Nex7FvoUGy2UBQRhTkKxeEvr2FB+r21HsOxcDJI0TzyO8NHhnAA8ILr8pfbSBg5Jj5KBg== +rc-tooltip@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz#e832ed0392872025e59928cfc1ad9045656467fd" + integrity sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g== dependencies: "@babel/runtime" "^7.11.2" "@rc-component/trigger" "^2.0.0" classnames "^2.3.1" + rc-util "^5.44.3" -rc-tree-select@~5.23.0: - version "5.23.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.23.0.tgz#e56da0923c7c11dea98d4e14bb76969283c94468" - integrity sha512-aQGi2tFSRw1WbXv0UVXPzHm09E0cSvUVZMLxQtMv3rnZZpNmdRXWrnd9QkLNlVH31F+X5rgghmdSFF3yZW0N9A== +rc-tree-select@~5.27.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz#3daa62972ae80846dac96bf4776d1a9dc9c7c4c6" + integrity sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww== dependencies: - "@babel/runtime" "^7.10.1" + "@babel/runtime" "^7.25.7" classnames "2.x" - rc-select "~14.15.0" - rc-tree "~5.9.0" - rc-util "^5.16.1" + rc-select "~14.16.2" + rc-tree "~5.13.0" + rc-util "^5.43.0" -rc-tree@~5.9.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.9.0.tgz#1835b2bef36cfeb4ec15d62e0319fc503aa485f1" - integrity sha512-CPrgOvm9d/9E+izTONKSngNzQdIEjMox2PBufWjS1wf7vxtvmCWzK1SlpHbRY6IaBfJIeZ+88RkcIevf729cRg== +rc-tree@~5.13.0, rc-tree@~5.13.1: + version "5.13.1" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.13.1.tgz#f36a33a94a1282f4b09685216c01487089748910" + integrity sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== dependencies: "@babel/runtime" "^7.10.1" classnames "2.x" @@ -6541,92 +6643,84 @@ rc-tree@~5.9.0: rc-util "^5.16.1" rc-virtual-list "^3.5.1" -rc-upload@~4.8.1: - version "4.8.1" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.8.1.tgz#ac55f2bc101b95b52a6e47f3c18f0f55b54e16d2" - integrity sha512-toEAhwl4hjLAI1u8/CgKWt30BR06ulPa4iGQSMvSXoHzO88gPCslxqV/mnn4gJU7PDoltGIC9Eh+wkeudqgHyw== +rc-upload@~4.11.0: + version "4.11.0" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.11.0.tgz#c2ced905a8b38b3e5c48493d388ca0e8373de18b" + integrity sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA== dependencies: "@babel/runtime" "^7.18.3" classnames "^2.2.5" rc-util "^5.2.0" -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.41.0, rc-util@^5.43.0: - version "5.43.0" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.43.0.tgz#bba91fbef2c3e30ea2c236893746f3e9b05ecc4c" - integrity sha512-AzC7KKOXFqAdIBqdGWepL9Xn7cm3vnAmjlHqUnoQaTMZYhM4VlXGLkkHHxj/BZ7Td0+SOPKB4RGPboBVKT9htw== +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3, rc-util@^5.44.4: + version "5.44.4" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" + integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== dependencies: "@babel/runtime" "^7.18.3" react-is "^18.2.0" rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.15.0" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.15.0.tgz#45c5b1ef1363287214e0a3c17af29ee0c3764764" - integrity sha512-dF2YQztqrU3ijAeWOqscTshCEr7vpimzSqAVjO1AyAmaqcHulaXpnGR0ptK5PXfxTUy48VkJOiglMIxlkYGs0w== + version "3.19.2" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz#1dd2d782c9a3ccbe537bb873447d73f83af8de0f" + integrity sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA== dependencies: "@babel/runtime" "^7.20.0" classnames "^2.2.6" rc-resize-observer "^1.0.0" rc-util "^5.36.0" -react-cookie@^7.1.4: - version "7.2.2" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-7.2.2.tgz#a7559e552ea9cca39a4b3686723a5acf504b8f84" - integrity sha512-e+hi6axHcw9VODoeVu8WyMWyoosa1pzpyjfvrLdF7CexfU+WSGZdDuRfHa4RJgTpfv3ZjdIpHE14HpYBieHFhg== +react-cookie@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.0.1.tgz#0d7f5112c9003b61c70d3d53f1f0a5b741e9f2e9" + integrity sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA== dependencies: - "@types/hoist-non-react-statics" "^3.3.5" + "@types/hoist-non-react-statics" "^3.3.6" hoist-non-react-statics "^3.3.2" - universal-cookie "^7.0.0" + universal-cookie "^8.0.0" -react-dom@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== +react-dom@19.2.0: + version "19.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8" + integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" + scheduler "^0.27.0" -react-highlight-words@^0.20.0: - version "0.20.0" - resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c60bfff5d14678c8f0e8fbe4bdcf083e6c70d507" - integrity sha512-asCxy+jCehDVhusNmCBoxDf2mm1AJ//D+EzDx1m5K7EqsMBIHdZ5G4LdwbSEXqZq1Ros0G0UySWmAtntSph7XA== +react-highlight-words@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.21.0.tgz#a109acdf7dc6fac3ed7db82e9cba94e8d65c281c" + integrity sha512-SdWEeU9fIINArEPO1rO5OxPyuhdEKZQhHzZZP1ie6UeXQf+CjycT1kWaB+9bwGcVbR0NowuHK3RqgqNg6bgBDQ== dependencies: highlight-words-core "^1.2.0" memoize-one "^4.0.0" - prop-types "^15.5.8" - -"react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.0, react-is@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" - integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-shallow-renderer@^16.15.0: - version "16.15.0" - resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" - integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== - dependencies: - object-assign "^4.1.1" - react-is "^16.12.0 || ^17.0.0 || ^18.0.0" - -react-test-renderer@^18.2.0: +react-is@^18.2.0, react-is@^18.3.1: version "18.3.1" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.3.1.tgz#e693608a1f96283400d4a3afead6893f958b80b4" - integrity sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA== - dependencies: - react-is "^18.3.1" - react-shallow-renderer "^16.15.0" - scheduler "^0.23.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react@^18.3.1: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== +react-is@^19.2.0: + version "19.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.0.tgz#ddc3b4a4e0f3336c3847f18b806506388d7b9973" + integrity sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA== + +react-test-renderer@^19.2.0: + version "19.2.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.0.tgz#5c9782b4a4ba0630a77d7ce092779fdf9ccde209" + integrity sha512-zLCFMHFE9vy/w3AxO0zNxy6aAupnCuLSVOJYDe/Tp+ayGI1f2PLQsFVPANSD42gdSbmYx5oN+1VWDhcXtq7hAQ== dependencies: - loose-envify "^1.1.0" + react-is "^19.2.0" + scheduler "^0.27.0" + +react@19.2.0: + version "19.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5" + integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== read-installed@~4.0.3: version "4.0.3" @@ -6649,17 +6743,8 @@ read-package-json@^2.0.0: dependencies: glob "^7.1.1" json-parse-even-better-errors "^2.3.0" - normalize-package-data "^2.0.0" - npm-normalize-package-bin "^1.0.0" - -readable-stream@^3.4.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + normalize-package-data "^2.0.0" + npm-normalize-package-bin "^1.0.0" readdir-scoped-modules@^1.0.0: version "1.1.0" @@ -6671,39 +6756,32 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -reflect.getprototypeof@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" - integrity sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" define-properties "^1.2.1" - es-abstract "^1.23.1" + es-abstract "^1.23.9" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - globalthis "^1.0.3" - which-builtin-type "^1.1.3" - -regenerator-runtime@^0.14.0: - version "0.14.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" - integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" -regexp.prototype.flags@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz#b3ae40b1d2499b8350ab2c3fe6ef3845d3a96f42" - integrity sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ== +regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" define-properties "^1.2.1" es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" set-function-name "^2.0.2" -rehackt@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b" - integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw== - relay-runtime@12.0.0: version "12.0.0" resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" @@ -6724,9 +6802,9 @@ remove-trailing-separator@^1.0.1: integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-trailing-spaces@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" - integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== + version "1.0.9" + resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz#39c270a309ea16fda84253ffbdeb1b5afa0aa271" + integrity sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA== require-directory@^2.1.1: version "2.1.1" @@ -6765,17 +6843,12 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve.exports@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" - integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== - -resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.4: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== +resolve@^1.10.0, resolve@^1.22.4: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: - is-core-module "^2.13.0" + is-core-module "^2.16.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6788,34 +6861,24 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -response-iterator@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" - integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw== - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== +restore-cursor@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" + onetime "^7.0.0" + signal-exit "^4.1.0" reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + version "1.1.0" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== -rfdc@^1.3.0: +rfdc@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -6823,62 +6886,55 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.5.5: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== +rxjs@7.8.2, rxjs@^7.8.2: + version "7.8.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== dependencies: tslib "^2.1.0" -safe-array-concat@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" - integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== dependencies: - call-bind "^1.0.7" - get-intrinsic "^1.2.4" - has-symbols "^1.0.3" + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" -safe-regex-test@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" - integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== +safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== dependencies: - call-bind "^1.0.6" + call-bound "^1.0.2" es-errors "^1.3.0" - is-regex "^1.1.4" + is-regex "^1.2.1" safe-stable-stringify@^2.4.3: version "2.5.0" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== -"safer-buffer@>= 2.1.2 < 3": +"safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - -schema-utils@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== scroll-into-view-if-needed@^3.1.0: version "3.1.0" @@ -6887,25 +6943,20 @@ scroll-into-view-if-needed@^3.1.0: dependencies: compute-scroll-into-view "^3.0.2" -scuid@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" - integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== - "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: +semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== sentence-case@^3.0.4: version "3.0.4" @@ -6921,7 +6972,7 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.2.1: +set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -6933,7 +6984,7 @@ set-function-length@^1.2.1: gopd "^1.0.1" has-property-descriptors "^1.0.2" -set-function-name@^2.0.1, set-function-name@^2.0.2: +set-function-name@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== @@ -6943,39 +6994,53 @@ set-function-name@^2.0.1, set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + setimmediate@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -sharp@^0.33.4: - version "0.33.5" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.33.5.tgz#13e0e4130cc309d6a9497596715240b2ec0c594e" - integrity sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw== +sharp@^0.34.4, sharp@^0.34.5: + version "0.34.5" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" + integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== dependencies: - color "^4.2.3" - detect-libc "^2.0.3" - semver "^7.6.3" + "@img/colour" "^1.0.0" + detect-libc "^2.1.2" + semver "^7.7.3" optionalDependencies: - "@img/sharp-darwin-arm64" "0.33.5" - "@img/sharp-darwin-x64" "0.33.5" - "@img/sharp-libvips-darwin-arm64" "1.0.4" - "@img/sharp-libvips-darwin-x64" "1.0.4" - "@img/sharp-libvips-linux-arm" "1.0.5" - "@img/sharp-libvips-linux-arm64" "1.0.4" - "@img/sharp-libvips-linux-s390x" "1.0.4" - "@img/sharp-libvips-linux-x64" "1.0.4" - "@img/sharp-libvips-linuxmusl-arm64" "1.0.4" - "@img/sharp-libvips-linuxmusl-x64" "1.0.4" - "@img/sharp-linux-arm" "0.33.5" - "@img/sharp-linux-arm64" "0.33.5" - "@img/sharp-linux-s390x" "0.33.5" - "@img/sharp-linux-x64" "0.33.5" - "@img/sharp-linuxmusl-arm64" "0.33.5" - "@img/sharp-linuxmusl-x64" "0.33.5" - "@img/sharp-wasm32" "0.33.5" - "@img/sharp-win32-ia32" "0.33.5" - "@img/sharp-win32-x64" "0.33.5" + "@img/sharp-darwin-arm64" "0.34.5" + "@img/sharp-darwin-x64" "0.34.5" + "@img/sharp-libvips-darwin-arm64" "1.2.4" + "@img/sharp-libvips-darwin-x64" "1.2.4" + "@img/sharp-libvips-linux-arm" "1.2.4" + "@img/sharp-libvips-linux-arm64" "1.2.4" + "@img/sharp-libvips-linux-ppc64" "1.2.4" + "@img/sharp-libvips-linux-riscv64" "1.2.4" + "@img/sharp-libvips-linux-s390x" "1.2.4" + "@img/sharp-libvips-linux-x64" "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64" "1.2.4" + "@img/sharp-libvips-linuxmusl-x64" "1.2.4" + "@img/sharp-linux-arm" "0.34.5" + "@img/sharp-linux-arm64" "0.34.5" + "@img/sharp-linux-ppc64" "0.34.5" + "@img/sharp-linux-riscv64" "0.34.5" + "@img/sharp-linux-s390x" "0.34.5" + "@img/sharp-linux-x64" "0.34.5" + "@img/sharp-linuxmusl-arm64" "0.34.5" + "@img/sharp-linuxmusl-x64" "0.34.5" + "@img/sharp-wasm32" "0.34.5" + "@img/sharp-win32-arm64" "0.34.5" + "@img/sharp-win32-ia32" "0.34.5" + "@img/sharp-win32-x64" "0.34.5" shebang-command@^2.0.0: version "2.0.0" @@ -6989,65 +7054,78 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== +shell-quote@1.8.3, shell-quote@^1.7.3: + version "1.8.3" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== -side-channel@^1.0.4, side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: - call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" -signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: +signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1, signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + signedsource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@3.0.0, slash@^3.0.0: +slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== +slice-ansi@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" + integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" + ansi-styles "^6.2.1" + is-fullwidth-code-point "^5.0.0" slide@~1.1.3: version "1.1.6" @@ -7075,12 +7153,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -source-map@^0.6.0, source-map@^0.6.1: +source-map@^0.6.0: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -7116,9 +7189,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.20" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz#e44ed19ed318dd1e5888f93325cee800f0f51b89" - integrity sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw== + version "3.0.22" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== spdx-ranges@^2.0.0: version "2.1.1" @@ -7146,29 +7219,37 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== -stack-utils@^2.0.3: +stable-hash@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" + integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== + +stack-utils@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" -streamsearch@^1.1.0: +stop-iteration-iterator@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" string-convert@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== -string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: +string-env-interpolation@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== -string-length@^4.0.1: +string-length@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== @@ -7176,6 +7257,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -7185,6 +7275,32 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string-width@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== + dependencies: + emoji-regex "^10.3.0" + get-east-asian-width "^1.0.0" + strip-ansi "^7.1.0" + +string-width@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" + integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== + dependencies: + get-east-asian-width "^1.3.0" + strip-ansi "^7.1.0" + string.prototype.includes@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" @@ -7194,23 +7310,24 @@ string.prototype.includes@^2.0.1: define-properties "^1.2.1" es-abstract "^1.23.3" -string.prototype.matchall@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz#1092a72c59268d2abaad76582dccc687c0297e0a" - integrity sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg== +string.prototype.matchall@^4.0.12: + version "4.0.12" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" + integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.3" define-properties "^1.2.1" - es-abstract "^1.23.2" + es-abstract "^1.23.6" es-errors "^1.3.0" es-object-atoms "^1.0.0" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.7" - regexp.prototype.flags "^1.5.2" + get-intrinsic "^1.2.6" + gopd "^1.2.0" + has-symbols "^1.1.0" + internal-slot "^1.1.0" + regexp.prototype.flags "^1.5.3" set-function-name "^2.0.2" - side-channel "^1.0.6" + side-channel "^1.1.0" string.prototype.repeat@^1.0.0: version "1.0.0" @@ -7220,22 +7337,26 @@ string.prototype.repeat@^1.0.0: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trim@^1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" - integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" define-properties "^1.2.1" - es-abstract "^1.23.0" + es-abstract "^1.23.5" es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" -string.prototype.trimend@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" - integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.2" define-properties "^1.2.1" es-object-atoms "^1.0.0" @@ -7248,12 +7369,12 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - safe-buffer "~5.2.0" + ansi-regex "^5.0.1" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" @@ -7262,6 +7383,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1, strip-ansi@^7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -7282,17 +7410,24 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -styled-jsx@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" - integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== +styled-jsx@5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" + integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== dependencies: client-only "0.0.1" -stylis@^4.3.3: - version "4.3.4" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.4.tgz#ca5c6c4a35c4784e4e93a2a24dc4e9fa075250a4" - integrity sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now== +stylis@^4.3.4: + version "4.3.6" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + +supports-color@8.1.1, supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" supports-color@^5.3.0: version "5.5.0" @@ -7308,13 +7443,6 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -7328,22 +7456,28 @@ swap-case@^2.0.2: tslib "^2.0.3" swr@^2.0.0: - version "2.2.5" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.2.5.tgz#063eea0e9939f947227d5ca760cc53696f46446b" - integrity sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg== + version "2.3.6" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.6.tgz#5fee0ee8a0762a16871ee371075cb09422b64f50" + integrity sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw== dependencies: - client-only "^0.0.1" - use-sync-external-store "^1.2.0" + dequal "^2.0.3" + use-sync-external-store "^1.4.0" -symbol-observable@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205" - integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ== +sync-fetch@0.6.0-2: + version "0.6.0-2" + resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.6.0-2.tgz#d82d6dc8efaf2d103a9015e7bd7ba0bfc8e078f2" + integrity sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A== + dependencies: + node-fetch "^3.3.2" + timeout-signal "^2.0.0" + whatwg-mimetype "^4.0.0" -tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== +synckit@^0.11.8: + version "0.11.11" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" + integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== + dependencies: + "@pkgr/core" "^0.2.9" test-exclude@^6.0.0: version "6.0.0" @@ -7354,20 +7488,23 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== -through@^2.3.6, through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +timeout-signal@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/timeout-signal/-/timeout-signal-2.0.0.tgz#23207ea448d50258bb0defe3beea4a467643abba" + integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== + +tinyglobby@^0.2.13: + version "0.2.15" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" title-case@^3.0.3: version "3.0.3" @@ -7376,13 +7513,6 @@ title-case@^3.0.3: dependencies: tslib "^2.0.3" -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -7405,22 +7535,20 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +tree-kill@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + treeify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== -ts-api-utils@^1.0.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.4.0.tgz#709c6f2076e511a81557f3d07a0cbd566ae8195c" - integrity sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ== - -ts-invariant@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" - integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ== - dependencies: - tslib "^2.1.0" +ts-api-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== ts-log@^2.2.3: version "2.2.7" @@ -7456,35 +7584,20 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1, tslib@^2.6.2, tslib@^2.7.0: +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tslib@~2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== - -tslib@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" - integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== - -tslib@~2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - tslib@~2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== -tslib@~2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tslib@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -7503,109 +7616,139 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -typed-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" - integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: - call-bind "^1.0.7" + call-bound "^1.0.3" es-errors "^1.3.0" - is-typed-array "^1.1.13" + is-typed-array "^1.1.14" -typed-array-byte-length@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" - integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== dependencies: - call-bind "^1.0.7" + call-bind "^1.0.8" for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" -typed-array-byte-offset@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" - integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== dependencies: available-typed-arrays "^1.0.7" - call-bind "^1.0.7" + call-bind "^1.0.8" for-each "^0.3.3" - gopd "^1.0.1" - has-proto "^1.0.3" - is-typed-array "^1.1.13" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" -typed-array-length@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" - integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== dependencies: call-bind "^1.0.7" for-each "^0.3.3" gopd "^1.0.1" - has-proto "^1.0.3" is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== +typescript-eslint@^8.46.0: + version "8.47.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.47.0.tgz#bb8fcf4f2c69ffcd5d088f7f30cd52936ff05cbc" + integrity sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q== dependencies: - is-typedarray "^1.0.0" + "@typescript-eslint/eslint-plugin" "8.47.0" + "@typescript-eslint/parser" "8.47.0" + "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/utils" "8.47.0" typescript@^5.0.4: - version "5.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" - integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + version "5.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ua-parser-js@^1.0.35: - version "1.0.39" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.39.tgz#bfc07f361549bf249bd8f4589a4cccec18fd2018" - integrity sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw== + version "1.0.41" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.41.tgz#bd04dc9ec830fcf9e4fad35cf22dcedd2e3b4e9c" + integrity sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug== -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== dependencies: - call-bind "^1.0.2" + call-bound "^1.0.3" has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== -undici-types@~6.19.2, undici-types@~6.19.8: - version "6.19.8" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" - integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== -universal-cookie@^7.0.0: - version "7.2.2" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-7.2.2.tgz#93ae9ec55baab89b24300473543170bb8112773c" - integrity sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ== +universal-cookie@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.0.1.tgz#19263fcda6889978a081c610a90fc395eeaeec64" + integrity sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg== dependencies: - "@types/cookie" "^0.6.0" - cookie "^0.7.2" + cookie "^1.0.2" -unixify@1.0.0, unixify@^1.0.0: +unixify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" -update-browserslist-db@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5" - integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A== +unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: + version "1.11.1" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" + integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== + dependencies: + napi-postinstall "^0.3.0" + optionalDependencies: + "@unrs/resolver-binding-android-arm-eabi" "1.11.1" + "@unrs/resolver-binding-android-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-arm64" "1.11.1" + "@unrs/resolver-binding-darwin-x64" "1.11.1" + "@unrs/resolver-binding-freebsd-x64" "1.11.1" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.11.1" + "@unrs/resolver-binding-linux-arm64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-arm64-musl" "1.11.1" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-riscv64-musl" "1.11.1" + "@unrs/resolver-binding-linux-s390x-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-gnu" "1.11.1" + "@unrs/resolver-binding-linux-x64-musl" "1.11.1" + "@unrs/resolver-binding-wasm32-wasi" "1.11.1" + "@unrs/resolver-binding-win32-arm64-msvc" "1.11.1" + "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" + "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" + +update-browserslist-db@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== dependencies: escalade "^3.2.0" - picocolors "^1.1.0" + picocolors "^1.1.1" upper-case-first@^2.0.2: version "2.0.2" @@ -7628,20 +7771,15 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urlpattern-polyfill@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" - integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== - -use-sync-external-store@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" - integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== +urlpattern-polyfill@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz#1b2517e614136c73ba32948d5e7a3a063cba8e74" + integrity sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw== -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +use-sync-external-store@^1.4.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== util-extend@^1.0.1: version "1.0.3" @@ -7670,12 +7808,7 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -value-or-promise@^1.0.11, value-or-promise@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" - integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== - -walker@^1.0.7, walker@^1.0.8: +walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== @@ -7689,42 +7822,21 @@ warning@^4.0.3: dependencies: loose-envify "^1.0.0" -watchpack@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - -web-streams-polyfill@^3.2.1: +web-streams-polyfill@^3.0.3: version "3.3.3" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== -webcrypto-core@^1.8.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.8.1.tgz#09d5bd8a9c48e9fbcaf412e06b1ff1a57514ce86" - integrity sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A== - dependencies: - "@peculiar/asn1-schema" "^2.3.13" - "@peculiar/json-schema" "^1.1.12" - asn1js "^3.0.5" - pvtsutils "^1.3.5" - tslib "^2.7.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -7733,34 +7845,35 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" -which-builtin-type@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" - integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== dependencies: + call-bound "^1.0.2" function.prototype.name "^1.1.6" has-tostringtag "^1.0.2" is-async-function "^2.0.0" - is-date-object "^1.0.5" - is-finalizationregistry "^1.0.2" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" is-generator-function "^1.0.10" - is-regex "^1.1.4" + is-regex "^1.2.1" is-weakref "^1.0.2" isarray "^2.0.5" - which-boxed-primitive "^1.0.2" + which-boxed-primitive "^1.1.0" which-collection "^1.0.2" - which-typed-array "^1.1.15" + which-typed-array "^1.1.16" which-collection@^1.0.2: version "1.0.2" @@ -7777,15 +7890,17 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.14, which-typed-array@^1.1.15: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.19" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== dependencies: available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" has-tostringtag "^1.0.2" which@^2.0.1: @@ -7800,7 +7915,16 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== @@ -7818,38 +7942,41 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrap-ansi@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== + dependencies: + ansi-styles "^6.2.1" + string-width "^7.0.0" + strip-ansi "^7.1.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -write-file-atomic@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" - integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== +write-file-atomic@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" - signal-exit "^3.0.7" + signal-exit "^4.0.1" -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== - -ws@^8.12.0: - version "8.18.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" - integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== +ws@^8.17.1, ws@^8.18.3: + version "8.18.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== y18n@^4.0.0: version "4.0.3" @@ -7866,11 +7993,6 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml-ast-parser@^0.0.43: - version "0.0.43" - resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb" - integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A== - yaml-loader@^0.8: version "0.8.1" resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.1.tgz#034f901147073cfc307cdcce8bd44c1547e60ba1" @@ -7880,15 +8002,10 @@ yaml-loader@^0.8: loader-utils "^2.0.0" yaml "^2.0.0" -yaml@1.10.2, yaml@^1.10.0: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yaml@^2.0.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.6.0.tgz#14059ad9d0b1680d0f04d3a60fe00f3a857303c3" - integrity sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ== +yaml@^2.0.0, yaml@^2.3.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== yargs-parser@^18.1.2: version "18.1.3" @@ -7903,6 +8020,19 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yargs@^15.3.1: version "15.4.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" @@ -7920,19 +8050,6 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.0.0, yargs@^17.3.1: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -7943,14 +8060,17 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zen-observable-ts@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" - integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg== - dependencies: - zen-observable "0.8.15" +yoctocolors-cjs@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== + +"zod-validation-error@^3.5.0 || ^4.0.0": + version "4.0.2" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz#bc605eba49ce0fcd598c127fee1c236be3f22918" + integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== -zen-observable@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" - integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== +"zod@^3.25.0 || ^4.0.0": + version "4.1.12" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" + integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== From 3fea4d594daf6c3f40638917cfce4e037dc10664 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 20 Nov 2025 16:52:26 +0100 Subject: [PATCH 566/878] I can delete the nonPrime surface and the integral values without changing the results --- .../Methods/SpectralToIntegralMethodTests.cs | 24 + ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 12210 ++++++++++++++++ 2 files changed, 12234 insertions(+) create mode 100644 backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 4953fbe9..a2286887 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -82,6 +82,30 @@ public void Calculate_Few() } } + [Test] + [SuppressMessage("Naming", "CA1707")] + public async Task Calculate_Some() + { + using var fileStream = File.OpenRead( + ConstructFilePath("./Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") + ); + using var jsonDocument = await JsonDocument.ParseAsync( + fileStream, + JsonDocumentSettings.Lax + ); + var spectralDataPoints = jsonDocument.RootElement.Deserialize( + MethodBase.JsonSerializerOptions + ) ?? throw new InvalidOperationException(); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); + } + } + [Test] [SuppressMessage("Naming", "CA1707")] public async Task Calculate_Many() diff --git a/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json new file mode 100644 index 00000000..ed9fb252 --- /dev/null +++ b/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json @@ -0,0 +1,12210 @@ +{ + "data": [ + { + "componentCharacteristics": { + "surface": "prime", + "symmetries": "azimuthalAngleInvariance" + }, + "dataPoints": [ + { + "incidence": { + "wavelengths": { + "wavelength": 300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0476000010967255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0303000006824732, + "reflectance": 0.0472000017762184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0931999981403351, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.191599994897842, + "reflectance": 0.0485999993979931 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.318300008773804, + "reflectance": 0.0516999997198582 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.453700006008148, + "reflectance": 0.0568000003695488 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.576499998569489, + "reflectance": 0.0628999993205071 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.676800012588501, + "reflectance": 0.0689999982714653 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.751299977302551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.804400026798248, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861100018024445, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873399972915649, + "reflectance": 0.0829000025987625 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879499971866608, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888599991798401, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896899998188019, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896650016307831, + "reflectance": 0.0832000002264977 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896399974822998, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895399987697601, + "reflectance": 0.0827500000596046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894400000572205, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0823500007390976 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894200026988983, + "reflectance": 0.0821999981999397 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894649982452393, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0820000022649765 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896250009536743, + "reflectance": 0.0819500014185906 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89740002155304, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898800015449524, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900200009346008, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900900006294251, + "reflectance": 0.0818499997258186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0817999988794327 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901899993419647, + "reflectance": 0.0817499980330467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902199983596802, + "reflectance": 0.0816999971866608 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903299987316132, + "reflectance": 0.0816499963402748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904399991035461, + "reflectance": 0.0816000029444695 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904799997806549, + "reflectance": 0.0815500020980835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905200004577637, + "reflectance": 0.0815000012516975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905700027942658, + "reflectance": 0.0814500004053116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.906199991703033, + "reflectance": 0.0813999995589256 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905849993228912, + "reflectance": 0.0812999978661537 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905499994754791, + "reflectance": 0.0812000036239624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905399978160858, + "reflectance": 0.0811000019311905 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90530002117157, + "reflectance": 0.0810000002384186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90514999628067, + "reflectance": 0.0808999985456467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807999968528748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807500034570694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807000026106834 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904600024223328, + "reflectance": 0.0806000009179115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90420001745224, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903699994087219, + "reflectance": 0.0803999975323677 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903199970722199, + "reflectance": 0.0803000032901764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902400016784668, + "reflectance": 0.0801500007510185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0799999982118607 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901000022888184, + "reflectance": 0.0798999965190888 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90039998292923, + "reflectance": 0.0798000022768974 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.899649977684021, + "reflectance": 0.0797000005841255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898899972438812, + "reflectance": 0.0795999988913536 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898249983787537, + "reflectance": 0.0794999971985817 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.897599995136261, + "reflectance": 0.0794000029563904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896849989891052, + "reflectance": 0.0793000012636185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896099984645844, + "reflectance": 0.0791999995708466 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0790499970316887 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89410001039505, + "reflectance": 0.0789000019431114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.893450021743774, + "reflectance": 0.0788000002503395 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.892799973487854, + "reflectance": 0.0786999985575676 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89190000295639, + "reflectance": 0.0785999968647957 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.890999972820282, + "reflectance": 0.0785000026226044 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89045000076294, + "reflectance": 0.0784000009298325 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889900028705597, + "reflectance": 0.0782999992370606 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889150023460388, + "reflectance": 0.0781999975442886 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888400018215179, + "reflectance": 0.0781000033020973 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887750029563904, + "reflectance": 0.0780500024557114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887099981307983, + "reflectance": 0.0780000016093254 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.886399984359741, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.885699987411499, + "reflectance": 0.0777999982237816 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.884800016880035, + "reflectance": 0.0776999965310097 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.883899986743927, + "reflectance": 0.0776000022888184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.882799983024597, + "reflectance": 0.0774499997496605 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.881699979305267, + "reflectance": 0.0772999972105026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.88085001707077, + "reflectance": 0.0772000029683113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879999995231628, + "reflectance": 0.0771000012755394 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878650009632111, + "reflectance": 0.0769499987363815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877300024032593, + "reflectance": 0.0768000036478043 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875999987125397, + "reflectance": 0.0767000019550324 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0766000002622604 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873849987983704, + "reflectance": 0.0764499977231026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873000025749207, + "reflectance": 0.0763000026345253 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872150003910065, + "reflectance": 0.0762000009417534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871299982070923, + "reflectance": 0.0760999992489815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869650006294251, + "reflectance": 0.0759499967098236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867999970912933, + "reflectance": 0.0758000016212463 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867299973964691, + "reflectance": 0.0756999999284744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866599977016449, + "reflectance": 0.0755999982357025 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865100026130676, + "reflectance": 0.0754500031471252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863600015640259, + "reflectance": 0.0753000006079674 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86269998550415, + "reflectance": 0.0751999989151955 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861800014972687, + "reflectance": 0.0750999972224236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861150026321411, + "reflectance": 0.0750000029802322 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860499978065491, + "reflectance": 0.0749000012874603 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859849989414215, + "reflectance": 0.0748500004410744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85920000076294, + "reflectance": 0.0747999995946884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858449995517731, + "reflectance": 0.0746999979019165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857699990272522, + "reflectance": 0.0746000036597252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856649994850159, + "reflectance": 0.0745000019669533 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855599999427795, + "reflectance": 0.0744000002741814 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854300022125244, + "reflectance": 0.0742999985814095 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852999985218048, + "reflectance": 0.0741999968886375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852150022983551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851300001144409, + "reflectance": 0.0740000009536743 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850499987602234, + "reflectance": 0.0739500001072884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849699974060059, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848900020122528, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848100006580353, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847350001335144, + "reflectance": 0.0736500024795532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846599996089935, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845899999141693, + "reflectance": 0.0734999999403954 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845200002193451, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844600021839142, + "reflectance": 0.0733499974012375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843999981880188, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843500018119812, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842999994754791, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842599987983704, + "reflectance": 0.0730499997735024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842199981212616, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841849982738495, + "reflectance": 0.0729499980807304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841499984264374, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841199994087219, + "reflectance": 0.0728499963879585 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840900003910065, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84060001373291, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840099990367889, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839900016784668, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839699983596802, + "reflectance": 0.0726500004529953 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83950001001358, + "reflectance": 0.0725999996066093 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839299976825714, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839100003242493, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838949978351593, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838660001754761, + "reflectance": 0.0724700018763542 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838519990444183, + "reflectance": 0.0724399983882904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838379979133606, + "reflectance": 0.0724100023508072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838100016117096, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837960004806519, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837819993495941, + "reflectance": 0.0722900032997131 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837679982185364, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837539970874786, + "reflectance": 0.0722299963235855 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837400019168854, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837430000305176, + "reflectance": 0.0721900016069412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837459981441498, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837490022182465, + "reflectance": 0.0721699967980385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837520003318787, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837549984455109, + "reflectance": 0.0721499994397163 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837580025196075, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837610006332398, + "reflectance": 0.0721300020813942 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83763998746872, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837670028209686, + "reflectance": 0.0721099972724915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837700009346008, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837790012359619, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83788001537323, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837970018386841, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838060021400452, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838150024414063, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838329970836639, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83841997385025, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838509976863861, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838599979877472, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838769972324371, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838940024375916, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839110016822815, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839280009269714, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839450001716614, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839619994163513, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839789986610413, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840129971504211, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840650022029877, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841000020503998, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841350018978119, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84170001745224, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842050015926361, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842400014400482, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842750012874603, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843100011348724, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843450009822846, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843800008296967, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844190001487732, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844579994678497, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844969987869263, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845359981060028, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845749974250794, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846140027046204, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846530020236969, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8473100066185, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847699999809265, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848190009593964, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848680019378662, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849170029163361, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849659979343414, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850149989128113, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850639998912811, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85113000869751, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851620018482208, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852110028266907, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85259997844696, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852859973907471, + "reflectance": 0.0727099999785423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853120028972626, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853380024433136, + "reflectance": 0.0727299973368645 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853640019893646, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853900015354157, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854160010814667, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854420006275177, + "reflectance": 0.0727699995040894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854680001735687, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854939997196198, + "reflectance": 0.0727899968624115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855199992656708, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855979979038239, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856760025024414, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857540011405945, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858319997787476, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859099984169006, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859879970550537, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860660016536713, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861440002918243, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862219989299774, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862999975681305, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863579988479614, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864160001277924, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864740014076233, + "reflectance": 0.0733200013637543 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865320026874542, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865899980068207, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866479992866516, + "reflectance": 0.0734400004148483 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867060005664825, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867640018463135, + "reflectance": 0.0735199972987175 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868219971656799, + "reflectance": 0.0735599994659424 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869319975376129, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869840025901794, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870880007743835, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871399998664856, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871919989585877, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872439980506897, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872959971427918, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873480021953583, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874000012874603, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874270021915436, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874539971351624, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874809980392456, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875079989433289, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875349998474121, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875620007514954, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875890016555786, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876160025596619, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876429975032806, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876699984073639, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876860022544861, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877020001411438, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878260016441345, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878220021724701, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878180027008057, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878099977970123, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878059983253479, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878019988536835, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877939999103546, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877900004386902, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877740025520325, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877579987049103, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877420008182526, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877260029315949, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877099990844727, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876919984817505, + "reflectance": 0.0735699981451035 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876739978790283, + "reflectance": 0.0735400021076202 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876559972763062, + "reflectance": 0.0735099986195564 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876380026340485, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876200020313263, + "reflectance": 0.0734499990940094 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876020014286041, + "reflectance": 0.0734200030565262 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87584000825882, + "reflectance": 0.0733899995684624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875660002231598, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875479996204376, + "reflectance": 0.0733300000429153 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875299990177155, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875159978866577, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875020027160645, + "reflectance": 0.0732600018382072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874880015850067, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87474000453949, + "reflectance": 0.0732199996709824 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874599993228912, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874459981918335, + "reflectance": 0.0731799975037575 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874319970607758, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874180018901825, + "reflectance": 0.0731400027871132 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874040007591248, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87389999628067, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87379002571106, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873679995536804, + "reflectance": 0.0730599984526634 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873570024967194, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873459994792938, + "reflectance": 0.0730199962854385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873350024223328, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873239994049072, + "reflectance": 0.0729800015687943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873130023479462, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873019993305206, + "reflectance": 0.0729399994015694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872910022735596, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87279999256134, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872600018978119, + "reflectance": 0.0728600025177002 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872500002384186, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872399985790253, + "reflectance": 0.0728200003504753 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872300028800964, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872200012207031, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872099995613098, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871999979019165, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871900022029877, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871800005435944, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871619999408722, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8714399933815, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871259987354279, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871079981327057, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870899975299835, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870720028877258, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870540022850037, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870180010795593, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870000004768372, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869989991188049, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869979977607727, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86997002363205, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869960010051727, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869949996471405, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869939982891083, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869930028915405, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869920015335083, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869910001754761, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869899988174439, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869790017604828, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869679987430573, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869570016860962, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869459986686707, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869350016117096, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869239985942841, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86913001537323, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869019985198975, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868910014629364, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868470013141632, + "reflectance": 0.0720599964261055 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868139982223511, + "reflectance": 0.0720200017094612 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867810010910034, + "reflectance": 0.0719799995422363 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867479979991913, + "reflectance": 0.0719399973750114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867150008678436, + "reflectance": 0.0719000026583672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866819977760315, + "reflectance": 0.0718600004911423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866490006446838, + "reflectance": 0.0718199983239174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866159975528717, + "reflectance": 0.0717800036072731 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865830004215241, + "reflectance": 0.0717400014400482 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865499973297119, + "reflectance": 0.0716999992728233 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864019989967346, + "reflectance": 0.0715800002217293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862540006637573, + "reflectance": 0.0714600011706352 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8610600233078, + "reflectance": 0.0713400021195412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859579980373383, + "reflectance": 0.0712200030684471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85809999704361, + "reflectance": 0.0710999965667725 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856620013713837, + "reflectance": 0.0709799975156784 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855139970779419, + "reflectance": 0.0708599984645844 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853659987449646, + "reflectance": 0.0707399994134903 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852180004119873, + "reflectance": 0.0706200003623962 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8507000207901, + "reflectance": 0.0705000013113022 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850210011005402, + "reflectance": 0.0704500004649162 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849229991436005, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848739981651306, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848249971866608, + "reflectance": 0.0702499970793724 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847760021686554, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847270011901856, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846780002117157, + "reflectance": 0.0701000019907951 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846289992332459, + "reflectance": 0.0700500011444092 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84579998254776, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84636002779007, + "reflectance": 0.0700299963355064 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0700599998235703 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847479999065399, + "reflectance": 0.0700900033116341 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848039984703064, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848599970340729, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849160015583038, + "reflectance": 0.0701799988746643 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0702100023627281 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850279986858368, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850839972496033, + "reflectance": 0.0702700018882752 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851400017738342, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851589977741242, + "reflectance": 0.0703099966049194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851779997348785, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851970016956329, + "reflectance": 0.0703300014138222 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852159976959229, + "reflectance": 0.0703400000929833 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852349996566773, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852540016174316, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852729976177216, + "reflectance": 0.0703700035810471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85291999578476, + "reflectance": 0.0703800022602081 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853110015392304, + "reflectance": 0.0703900009393692 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853299975395203, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852970004081726, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852639973163605, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852310001850128, + "reflectance": 0.0702800005674362 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851979970932007, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85164999961853, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851320028305054, + "reflectance": 0.0701600015163422 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850989997386932, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850660026073456, + "reflectance": 0.0700799971818924 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850329995155335, + "reflectance": 0.0700400024652481 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850000023841858, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84918999671936, + "reflectance": 0.0699300020933151 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848380029201508, + "reflectance": 0.0698599964380264 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84757000207901, + "reflectance": 0.0697899982333183 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846759974956513, + "reflectance": 0.0697200000286102 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84595000743866, + "reflectance": 0.0696500018239021 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845139980316162, + "reflectance": 0.069580003619194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844330012798309, + "reflectance": 0.0695099979639053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843519985675812, + "reflectance": 0.0694399997591972 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842710018157959, + "reflectance": 0.0693700015544891 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841899991035461, + "reflectance": 0.069300003349781 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840929985046387, + "reflectance": 0.0692199990153313 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0691400021314621 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838989973068237, + "reflectance": 0.0690599977970123 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838020026683807, + "reflectance": 0.0689800009131432 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837050020694733, + "reflectance": 0.0688999965786934 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.836080014705658, + "reflectance": 0.0688199996948242 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.835110008716583, + "reflectance": 0.0687400028109551 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.834140002727509, + "reflectance": 0.0686599984765053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.833169996738434, + "reflectance": 0.0685800015926361 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.832199990749359, + "reflectance": 0.0684999972581863 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0317602977156639 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0311778578907251 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0305749978870153 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0298982560634613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0293271541595459 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0286844298243523 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0279881600290537 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0273720752447844 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.026628976687789 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 5900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0257604569196701 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0249478276818991 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.024136908352375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0232145637273788 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0223778914660215 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0212853774428368 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0201160944998264 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0192228704690933 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0179411377757788 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0166530515998602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 6900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0153178321197629 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0139876734465361 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0125085180625319 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0109969433397055 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00932515412569046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00777597306296229 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00600432511419058 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0043658409267664 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00291289901360869 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00186265294905752 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 7900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00189377903006971 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.00460418686270714 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0139699345454574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0337979234755039 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0602677464485168 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.085798554122448 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.11000569909811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.132390573620796 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.15159609913826 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.169293016195297 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 8900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.186990514397621 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.205486640334129 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.229181557893753 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.253237813711166 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.273080766201019 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.286618322134018 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.289090216159821 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.283574998378754 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.273699045181274 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.263387382030487 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 9900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.252852469682694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.241850733757019 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.233270466327667 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.224181711673737 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.215439319610596 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.207660540938377 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.200180113315582 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.192028716206551 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.18305516242981 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.172752574086189 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 10900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.162584468722343 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.152016565203667 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.139815807342529 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.129515245556831 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.11989776045084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.112267836928368 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.103999778628349 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.097388967871666 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0901678577065468 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0837536007165909 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 11900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0777500867843628 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0726242884993553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0690992400050163 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0673081576824188 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.067036047577858 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0679356977343559 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.07016621530056 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.072583980858326 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0750323981046677 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0774050876498222 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 12900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0791785567998886 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0803491547703743 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.080874614417553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0806821063160896 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0794599503278732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0780422985553742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0761934518814087 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.074484221637249 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0724922269582748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0708932727575302 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 13900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.069573238492012 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.068089209496975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0665859431028366 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0652998611330986 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0639152824878693 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0625139251351357 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0612633489072323 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0600595921278 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0591550245881081 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0582340992987156 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 14900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0575152933597565 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0559111572802067 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0549225434660912 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.053684089332819 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0525968782603741 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0516011491417885 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0506701804697514 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0497782118618488 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.048476554453373 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0472403652966023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 15900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0462779141962528 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0454610884189606 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0447233505547047 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0436200089752674 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0425228625535965 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0416529029607773 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0408408083021641 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0390464328229427 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0372622981667519 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0363757237792015 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 16900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0353994518518448 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0345350205898285 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0332791395485401 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0323126092553139 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0312565825879574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0299149621278048 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.029098879545927 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.028380474075675 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0276288501918316 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0272911917418242 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 17900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0271986965090036 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0274871438741684 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0282482616603374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0295610837638378 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.031615424901247 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0346785746514797 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0387909337878227 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0438205040991306 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0499514639377594 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0578287653625011 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 18900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0641179233789444 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0747176632285118 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0862487331032753 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0983018204569817 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.106630176305771 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.11955600976944 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.132110372185707 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.140063136816025 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.151939645409584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.15967133641243 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 19900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.170385435223579 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.180735871195793 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.187573298811913 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.194054409861565 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.203141912817955 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.20876456797123 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.215954780578613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.220133006572723 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.224167600274086 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.229536697268486 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 20900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.23248416185379 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.235197708010674 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.238405734300613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.239470198750496 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.240031272172928 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.24043296277523 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.240457206964493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.239868059754372 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.239642664790154 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.239145949482918 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 21900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.237540140748024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.235139980912209 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.233003750443459 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.231239601969719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 22900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 23900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 24900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 25000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.230256736278534 + } + } + ] + } + ] +} \ No newline at end of file From bfd6ebb74895fc17cd7117af7a6671cb35edcf1e Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Thu, 20 Nov 2025 16:56:21 +0100 Subject: [PATCH 567/878] When I reduce the spectral data to 300-2500nm, I get the same (wrong) results --- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 3819 ----------------- 1 file changed, 3819 deletions(-) diff --git a/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json index ed9fb252..da0633d6 100644 --- a/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json +++ b/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json @@ -8384,3825 +8384,6 @@ "transmittance": 0.832199990749359, "reflectance": 0.0684999972581863 } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0317602977156639 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0311778578907251 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0305749978870153 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0298982560634613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0293271541595459 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0286844298243523 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0279881600290537 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0273720752447844 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.026628976687789 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 5900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0257604569196701 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0249478276818991 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.024136908352375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0232145637273788 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0223778914660215 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0212853774428368 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0201160944998264 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0192228704690933 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0179411377757788 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0166530515998602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 6900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0153178321197629 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0139876734465361 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0125085180625319 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0109969433397055 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00932515412569046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00777597306296229 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00600432511419058 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0043658409267664 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00291289901360869 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00186265294905752 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 7900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00189377903006971 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.00460418686270714 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0139699345454574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0337979234755039 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0602677464485168 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.085798554122448 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.11000569909811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.132390573620796 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.15159609913826 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.169293016195297 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 8900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.186990514397621 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.205486640334129 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.229181557893753 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.253237813711166 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.273080766201019 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.286618322134018 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.289090216159821 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.283574998378754 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.273699045181274 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.263387382030487 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 9900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.252852469682694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.241850733757019 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.233270466327667 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.224181711673737 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.215439319610596 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.207660540938377 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.200180113315582 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.192028716206551 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.18305516242981 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.172752574086189 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 10900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.162584468722343 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.152016565203667 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.139815807342529 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.129515245556831 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.11989776045084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.112267836928368 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.103999778628349 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.097388967871666 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0901678577065468 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0837536007165909 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 11900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0777500867843628 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0726242884993553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0690992400050163 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0673081576824188 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.067036047577858 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0679356977343559 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.07016621530056 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.072583980858326 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0750323981046677 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0774050876498222 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 12900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0791785567998886 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0803491547703743 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.080874614417553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0806821063160896 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0794599503278732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0780422985553742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0761934518814087 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.074484221637249 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0724922269582748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0708932727575302 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 13900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.069573238492012 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.068089209496975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0665859431028366 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0652998611330986 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0639152824878693 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0625139251351357 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0612633489072323 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0600595921278 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0591550245881081 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0582340992987156 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 14900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0575152933597565 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0559111572802067 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0549225434660912 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.053684089332819 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0525968782603741 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0516011491417885 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0506701804697514 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0497782118618488 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.048476554453373 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0472403652966023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 15900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0462779141962528 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0454610884189606 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0447233505547047 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0436200089752674 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0425228625535965 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0416529029607773 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0408408083021641 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0390464328229427 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0372622981667519 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0363757237792015 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 16900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0353994518518448 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0345350205898285 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0332791395485401 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0323126092553139 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0312565825879574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0299149621278048 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.029098879545927 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.028380474075675 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0276288501918316 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0272911917418242 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 17900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0271986965090036 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0274871438741684 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0282482616603374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0295610837638378 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.031615424901247 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0346785746514797 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0387909337878227 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0438205040991306 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0499514639377594 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0578287653625011 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 18900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0641179233789444 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0747176632285118 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0862487331032753 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0983018204569817 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.106630176305771 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.11955600976944 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.132110372185707 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.140063136816025 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.151939645409584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.15967133641243 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 19900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.170385435223579 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.180735871195793 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.187573298811913 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.194054409861565 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.203141912817955 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.20876456797123 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.215954780578613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.220133006572723 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.224167600274086 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.229536697268486 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 20900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.23248416185379 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.235197708010674 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.238405734300613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.239470198750496 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.240031272172928 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.24043296277523 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.240457206964493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.239868059754372 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.239642664790154 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.239145949482918 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 21900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.237540140748024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.235139980912209 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.233003750443459 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.231239601969719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 22900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 23900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 24900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 25000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.230256736278534 - } } ] } From 40be767ed5883289e82cf835d58f12edeb9e4f6e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 16:40:18 +0100 Subject: [PATCH 568/878] Bind logs an anonymous volume and clean-up generated files (to avoid stale files) --- Makefile | 2 +- backend/Dockerfile | 2 ++ docker-compose.yml | 3 ++- frontend/Dockerfile | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 859f61a4..483229bf 100644 --- a/Makefile +++ b/Makefile @@ -125,7 +125,7 @@ up : build ## (Re)create, and start containers (after building images if necessa down : ## Stop containers and remove containers and networks created by `up` and clear backend logs ${docker_compose} down \ --remove-orphans - -rm ./backend/src/logs/* + -rm ./frontend/queries/*.generated.ts .PHONY : down restart : ## Restart all stopped and running containers diff --git a/backend/Dockerfile b/backend/Dockerfile index 919c600d..f63ca055 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -370,6 +370,8 @@ EOT RUN \ mkdir --parents \ ./src/files && \ + mkdir --parents \ + ./src/logs && \ mkdir --parents \ ./src/bin \ ./src/obj && \ diff --git a/docker-compose.yml b/docker-compose.yml index 37a0fde1..1cc6d7d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,7 +92,7 @@ services: # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. # - /app/node_modules - # - /app/src/logs + - /app/src/logs - /app/src/bin - /app/src/obj - /app/test/bin @@ -162,6 +162,7 @@ services: # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. - /app/.next + - /app/__generated__ - /app/node_modules extra_hosts: - ${HOST}:host-gateway diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 8c6467be..1069bec2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -74,6 +74,7 @@ RUN \ RUN \ mkdir --parents \ ./.next \ + ./__generated__ \ ./node_modules ENTRYPOINT ["/usr/bin/tini", "--"] From 8edae26ecbec4d8a1a3c9a2a843bbe3dcfb03e15 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 17:19:34 +0100 Subject: [PATCH 569/878] Use `useMessage` as recommended by ant design --- see https://ant.design/components/message --- .../data/calorimetric/CalorimetricData.tsx | 10 ++++--- .../data/geometric/GeometricData.tsx | 10 ++++--- .../data/hygrothermal/HygrothermalData.tsx | 10 ++++--- .../components/data/optical/OpticalData.tsx | 10 ++++--- .../data/photovoltaic/PhotovoltaicData.tsx | 10 ++++--- frontend/components/databases/Database.tsx | 26 ++++++++++--------- frontend/lib/apollo.ts | 24 +++++++---------- frontend/pages/user-info.tsx | 8 +++--- 8 files changed, 63 insertions(+), 45 deletions(-) diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index d5ed4bc3..3a161210 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,8 +1,8 @@ import { Scalars } from "../../../__generated__/graphql"; import { CalorimetricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions } from "antd"; +import { Skeleton, Result, Descriptions, message } from "antd"; import { useEffect } from "react"; -import { messageApolloError } from "../../../lib/apollo"; +import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; @@ -17,10 +17,11 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat }, }); const calorimetricData = data?.calorimetricData; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -39,6 +40,8 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat } return ( + <> + {contextHolder} x.toLocaleString("en")).join(", ")} + ); } diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 36cdefc4..2f2fe9bb 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -1,8 +1,8 @@ import { Scalars } from "../../../__generated__/graphql"; import { GeometricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions } from "antd"; +import { Skeleton, Result, Descriptions, message } from "antd"; import { useEffect } from "react"; -import { messageApolloError } from "../../../lib/apollo"; +import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; @@ -19,10 +19,11 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { const geometricData = data?.geometricData; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -41,6 +42,8 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { } return ( + <> + {contextHolder} x.toLocaleString("en")).join(", ")} + ); } \ No newline at end of file diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index da726f97..f12becb2 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -1,8 +1,8 @@ import { Scalars } from "../../../__generated__/graphql"; import { HygrothermalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result } from "antd"; +import { Skeleton, Result, message } from "antd"; import { useEffect } from "react"; -import { messageApolloError } from "../../../lib/apollo"; +import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; @@ -17,10 +17,11 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat }, }); const hygrothermalData = data?.hygrothermalData; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -39,6 +40,8 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat } return ( + <> + {contextHolder} + ); } diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index e744ed38..d6d6f605 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,8 +1,8 @@ import { Scalars } from "../../../__generated__/graphql"; import { OpticalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions } from "antd"; +import { Skeleton, Result, Descriptions, message } from "antd"; import { useEffect } from "react"; -import { messageApolloError } from "../../../lib/apollo"; +import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; @@ -17,10 +17,11 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { }, }); const opticalData = data?.opticalData; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -39,6 +40,8 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { } return ( + <> + {contextHolder} x.toLocaleString("en")).join(", ")} + ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index d00aa55a..f2c42e76 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -1,8 +1,8 @@ import { Scalars } from "../../../__generated__/graphql"; import { PhotovoltaicDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result } from "antd"; +import { Skeleton, Result, message } from "antd"; import { useEffect } from "react"; -import { messageApolloError } from "../../../lib/apollo"; +import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; @@ -17,10 +17,11 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat }, }); const photovoltaicData = data?.photovoltaicData; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -39,6 +40,8 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat } return ( + <> + {contextHolder} + ); } diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index d22eb36b..1bc24df1 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -1,21 +1,22 @@ import { useQuery } from '@apollo/client/react'; import { DatabaseDocument } from "../../queries/databases.generated"; -import { Skeleton, Result, Descriptions, Typography, Tag } from "antd"; +import { Skeleton, Result, Descriptions, Typography, Tag, message } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { ReactNode, useEffect } from "react"; -import { messageApolloError } from "../../lib/apollo"; +import { stringifyApolloError } from "../../lib/apollo"; import UpdateDatabase from "./UpdateDatabase"; import { DatabaseVerificationState } from "../../__generated__/graphql"; export type DatabaseProps = {}; -export default function Database({}: DatabaseProps) { +export default function Database({ }: DatabaseProps) { const { loading, error, data } = useQuery(DatabaseDocument); const database = data?.database; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -35,20 +36,21 @@ export default function Database({}: DatabaseProps) { return ( <> + {contextHolder} , - ] + , + ] : [] )} tags={[ diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 289dba8b..e3c491a9 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -6,7 +6,6 @@ import { LocalState } from "@apollo/client/local-state"; import { CombinedGraphQLErrors } from "@apollo/client/errors"; import { PersistedQueryLink } from "@apollo/client/link/persisted-queries"; import merge from "deepmerge"; -import { message } from "antd"; import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin @@ -109,10 +108,9 @@ export function initializeApollo( } // See https://www.apollographql.com/docs/react/data/error-handling -export function messageApolloError(error: ErrorLike) { +export function stringifyApolloError(error: ErrorLike) { if (CombinedGraphQLErrors.is(error)) { - message.error( - `Name(${error.name + return `Name(${error.name }); Message(${error.message }); Extensions(${error.extensions }); Stack(${error.stack @@ -123,23 +121,19 @@ export function messageApolloError(error: ErrorLike) { }); Extensions(${e.extensions })]`) .join(", ") - })` - ); - // } else if (CombinedProtocolErrors.is(error)) { + })`; + // } if (CombinedProtocolErrors.is(error)) { // Handle multipart subscription protocol errors - // } else if (LocalStateError.is(error)) { + // } if (LocalStateError.is(error)) { // Handle errors thrown by the `LocalState` class - // } else if (ServerError.is(error)) { + // } if (ServerError.is(error)) { // Handle server HTTP errors - // } else if (ServerParseError.is(error)) { + // } if (ServerParseError.is(error)) { // Handle JSON parse errors - // } else if (UnconventionalError.is(error)) { + // } if (UnconventionalError.is(error)) { // Handle errors thrown by irregular types - } else { - message.error( - `Name(${error.name}); Message(${error.message}); Stack(${error.stack})` - ); } + return `Name(${error.name}); Message(${error.message}); Stack(${error.stack})`; } export function useApollo(initialState: any) { diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index c4f7e6f8..5cf29e2c 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,18 +1,19 @@ import { useQuery } from '@apollo/client/react'; import Layout from "../components/Layout"; import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { Skeleton, Result, Descriptions, Typography, message } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { useEffect } from "react"; -import { messageApolloError } from "../lib/apollo"; +import { stringifyApolloError } from "../lib/apollo"; function Page() { const { loading, error, data } = useQuery(CurrentUserInfoDocument); const currentUserInfo = data?.currentUserInfo; + const [messageApi, contextHolder] = message.useMessage(); useEffect(() => { if (error) { - messageApolloError(error); + messageApi.error(stringifyApolloError(error)); } }, [error]); @@ -32,6 +33,7 @@ function Page() { return ( + {contextHolder} Date: Thu, 20 Nov 2025 17:23:11 +0100 Subject: [PATCH 570/878] Format whole codebase with `make format` --- frontend/.vscode/settings.json | 8 +- frontend/codegen.ts | 20 +- frontend/components/Highlight.tsx | 2 +- frontend/components/NavBar.tsx | 14 +- frontend/components/data/DataPageHeader.tsx | 119 ++--- .../data/calorimetric/CalorimetricData.tsx | 42 +- .../data/geometric/GeometricData.tsx | 74 +-- .../data/hygrothermal/HygrothermalData.tsx | 23 +- .../components/data/optical/OpticalData.tsx | 83 ++-- .../data/photovoltaic/PhotovoltaicData.tsx | 23 +- frontend/components/databases/Database.tsx | 22 +- .../components/databases/UpdateDatabase.tsx | 6 +- frontend/eslint.config.mjs | 8 +- frontend/lib/apollo.ts | 37 +- frontend/lib/form.ts | 11 +- frontend/lib/freeTextFilter.tsx | 10 +- frontend/lib/table.tsx | 301 +++++------ frontend/next.config.ts | 10 +- frontend/pages/_app.tsx | 4 +- frontend/pages/data/calorimetric/index.tsx | 83 ++-- frontend/pages/data/create.tsx | 4 +- frontend/pages/data/geometric/[uuid].tsx | 22 +- frontend/pages/data/geometric/index.tsx | 451 ++++++++--------- frontend/pages/data/hygrothermal/index.tsx | 55 ++- frontend/pages/data/optical/index.tsx | 125 ++--- frontend/pages/data/photovoltaic/index.tsx | 29 +- frontend/pages/index.tsx | 47 +- frontend/pages/upload-file.tsx | 2 +- frontend/pages/user-info.tsx | 2 +- frontend/paths.ts | 79 ++- frontend/styles/global.css | 8 +- frontend/tsconfig.json | 18 +- frontend/type-defs.graphqls | 466 +++++++++++++++--- 33 files changed, 1325 insertions(+), 883 deletions(-) diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json index 96f44007..38b6a2fd 100644 --- a/frontend/.vscode/settings.json +++ b/frontend/.vscode/settings.json @@ -1,5 +1,5 @@ { - "editor.formatOnSave": true, - "editor.defaultFormatter": null, - "typescript.tsserver.experimental.enableProjectDiagnostics": true -} \ No newline at end of file + "editor.formatOnSave": true, + "editor.defaultFormatter": null, + "typescript.tsserver.experimental.enableProjectDiagnostics": true +} diff --git a/frontend/codegen.ts b/frontend/codegen.ts index cef6e6a0..33844950 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -1,4 +1,4 @@ -import type { CodegenConfig } from '@graphql-codegen/cli'; +import type { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { overwrite: true, @@ -37,16 +37,16 @@ const config: CodegenConfig = { // strictScalars: true, scalars: { ID: { - input: 'string', - output: 'string | number' + input: "string", + output: "string | number", }, - Any: 'unknown', - Byte: 'number', - DateTime: 'string', - Locale: 'string', - NonNegativeInt: 'number', - Url: 'string', - Uuid: 'string', + Any: "unknown", + Byte: "number", + DateTime: "string", + Locale: "string", + NonNegativeInt: "number", + Url: "string", + Uuid: "string", }, useTypeImports: true, // immutableTypes: true, diff --git a/frontend/components/Highlight.tsx b/frontend/components/Highlight.tsx index d431bcc3..d75e078e 100644 --- a/frontend/components/Highlight.tsx +++ b/frontend/components/Highlight.tsx @@ -15,5 +15,5 @@ export const Highlight = forwardRef( autoEscape textToHighlight={text || ""} /> - ) + ), ); diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 1750e6e1..7651eb27 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -1,4 +1,4 @@ -import { useQuery } from '@apollo/client/react'; +import { useQuery } from "@apollo/client/react"; import Link from "next/link"; import { useRouter } from "next/router"; import { Button, Menu } from "antd"; @@ -6,7 +6,7 @@ import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; import { CurrentUserDocument } from "../queries/currentUser.generated"; import { getXsrfToken } from "../lib/apollo"; -import { Route } from 'next'; +import { Route } from "next"; type NavItemProps = { path: Route; @@ -40,7 +40,9 @@ export default function NavBar({ items }: NavBarProps) { {items.map(({ path, label }) => ( - {label} + + {label} + ))} {currentUser ? ( @@ -51,7 +53,9 @@ export default function NavBar({ items }: NavBarProps) { > {moderatorItems.map(({ path, label }) => ( - {label} + + {label} + ))} @@ -61,7 +65,7 @@ export default function NavBar({ items }: NavBarProps) { type="hidden" value={ typeof window !== "undefined" - ? getXsrfToken() ?? undefined + ? (getXsrfToken() ?? undefined) : "" } /> diff --git a/frontend/components/data/DataPageHeader.tsx b/frontend/components/data/DataPageHeader.tsx index cea6d64a..8d5db192 100644 --- a/frontend/components/data/DataPageHeader.tsx +++ b/frontend/components/data/DataPageHeader.tsx @@ -6,62 +6,67 @@ import { DataPartialFragment } from "../../queries/data.generated"; import { ReactNode } from "react"; export type DataPageHeaderProps = { - data: DataPartialFragment, - extra?: ReactNode - children?: ReactNode + data: DataPartialFragment; + extra?: ReactNode; + children?: ReactNode; }; -export default function DataPageHeader({ data, extra, children }: DataPageHeaderProps) { - return ( - - - {data.uuid} - {data.timestamp} - {data.locale} - - - {data.componentId} - - - {data.createdAt} - - - {data.creatorId} - - - {data.warnings} - - - {data.appliedMethod.methodId} - - - - - {data.resourceTree.root.value.uuid} - - - {data.userId != null && - - - {data.userId} - - - } - {children} - - - ) -}; \ No newline at end of file +export default function DataPageHeader({ + data, + extra, + children, +}: DataPageHeaderProps) { + return ( + + + {data.uuid} + + {data.timestamp} + + {data.locale} + + + {data.componentId} + + + + {data.createdAt} + + + + {data.creatorId} + + + {data.warnings} + + + {data.appliedMethod.methodId} + + + + + {data.resourceTree.root.value.uuid} + + + {data.userId != null && ( + + + {data.userId} + + + )} + {children} + + + ); +} diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 3a161210..2a266f40 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -10,7 +10,9 @@ export type CalorimetricDataProps = { calorimetricDataId: Scalars["Uuid"]["input"]; }; -export default function CalorimetricData({ calorimetricDataId }: CalorimetricDataProps) { +export default function CalorimetricData({ + calorimetricDataId, +}: CalorimetricDataProps) { const { loading, error, data } = useQuery(CalorimetricDataDocument, { variables: { uuid: calorimetricDataId, @@ -42,22 +44,26 @@ export default function CalorimetricData({ calorimetricDataId }: CalorimetricDat return ( <> {contextHolder} - , - // ]} - > - - {calorimetricData.gValues.map((x) => x.toLocaleString("en")).join(", ")} - - - {calorimetricData.uValues.map((x) => x.toLocaleString("en")).join(", ")} - - - + , + // ]} + > + + {calorimetricData.gValues + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {calorimetricData.uValues + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + ); } diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 2f2fe9bb..658cbbf8 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -7,56 +7,58 @@ import DataPageHeader from "../DataPageHeader"; import { useQuery } from "@apollo/client/react"; export type GeometricDataProps = { - geometricDataId: Scalars["Uuid"]["input"]; + geometricDataId: Scalars["Uuid"]["input"]; }; export default function GeometricData({ geometricDataId }: GeometricDataProps) { - const { loading, error, data } = useQuery(GeometricDataDocument, { - variables: { - uuid: geometricDataId, - }, - }); + const { loading, error, data } = useQuery(GeometricDataDocument, { + variables: { + uuid: geometricDataId, + }, + }); - const geometricData = data?.geometricData; + const geometricData = data?.geometricData; const [messageApi, contextHolder] = message.useMessage(); - useEffect(() => { - if (error) { - messageApi.error(stringifyApolloError(error)); - } - }, [error]); - - if (loading) { - return ; + useEffect(() => { + if (error) { + messageApi.error(stringifyApolloError(error)); } + }, [error]); - if (!geometricData) { - return ( - - ); - } + if (loading) { + return ; + } + if (!geometricData) { return ( - <> - {contextHolder} - + ); + } + + return ( + <> + {contextHolder} + , // ]} - > - - {geometricData.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} - - - - ); -} \ No newline at end of file + > + + {geometricData.thicknesses + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + + ); +} diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index f12becb2..7b2463fb 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -10,7 +10,9 @@ export type HygrothermalDataProps = { hygrothermalDataId: Scalars["Uuid"]["input"]; }; -export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDataProps) { +export default function HygrothermalData({ + hygrothermalDataId, +}: HygrothermalDataProps) { const { loading, error, data } = useQuery(HygrothermalDataDocument, { variables: { uuid: hygrothermalDataId, @@ -42,16 +44,15 @@ export default function HygrothermalData({ hygrothermalDataId }: HygrothermalDat return ( <> {contextHolder} - , - // ]} - > - + , + // ]} + > ); } diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index d6d6f605..8bc13ae3 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -42,34 +42,61 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { return ( <> {contextHolder} - , - // ]} - > - - {opticalData.nearnormalHemisphericalVisibleTransmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalVisibleReflectances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalSolarTransmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.nearnormalHemisphericalSolarReflectances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.infraredEmittances.map((x) => x.toLocaleString("en")).join(", ")} - - - {opticalData.colorRenderingIndices.map((x) => x.toLocaleString("en")).join(", ")} - - + , + // ]} + > + + {opticalData.nearnormalHemisphericalVisibleTransmittances + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {opticalData.nearnormalHemisphericalVisibleReflectances + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {opticalData.nearnormalHemisphericalSolarTransmittances + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {opticalData.nearnormalHemisphericalSolarReflectances + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {opticalData.infraredEmittances + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + {opticalData.colorRenderingIndices + .map((x) => x.toLocaleString("en")) + .join(", ")} + + ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index f2c42e76..cf5075b5 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -10,7 +10,9 @@ export type PhotovoltaicDataProps = { photovoltaicDataId: Scalars["Uuid"]["input"]; }; -export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDataProps) { +export default function PhotovoltaicData({ + photovoltaicDataId, +}: PhotovoltaicDataProps) { const { loading, error, data } = useQuery(PhotovoltaicDataDocument, { variables: { uuid: photovoltaicDataId, @@ -42,16 +44,15 @@ export default function PhotovoltaicData({ photovoltaicDataId }: PhotovoltaicDat return ( <> {contextHolder} - , - // ]} - > - + , + // ]} + > ); } diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index 1bc24df1..62a8d9d2 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -1,4 +1,4 @@ -import { useQuery } from '@apollo/client/react'; +import { useQuery } from "@apollo/client/react"; import { DatabaseDocument } from "../../queries/databases.generated"; import { Skeleton, Result, Descriptions, Typography, Tag, message } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; @@ -9,7 +9,7 @@ import { DatabaseVerificationState } from "../../__generated__/graphql"; export type DatabaseProps = {}; -export default function Database({ }: DatabaseProps) { +export default function Database({}: DatabaseProps) { const { loading, error, data } = useQuery(DatabaseDocument); const database = data?.database; const [messageApi, contextHolder] = message.useMessage(); @@ -43,15 +43,15 @@ export default function Database({ }: DatabaseProps) { extra={([] as ReactNode[]).concat( database.isAuthorizedToUpdateNode ? [ - , - ] - : [] + , + ] + : [], )} tags={[ diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx index 328a40e7..f70a0738 100644 --- a/frontend/components/databases/UpdateDatabase.tsx +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -1,4 +1,4 @@ -import { useMutation } from '@apollo/client/react'; +import { useMutation } from "@apollo/client/react"; import { Alert, Form, Input, Button, Modal } from "antd"; import { UpdateDatabaseDocument, @@ -49,7 +49,7 @@ export default function UpdateDatabase({ ], }); const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() + new Array(), ); const [form] = Form.useForm(); const [updating, setUpdating] = useState(false); @@ -73,7 +73,7 @@ export default function UpdateDatabase({ return { code: x.code, message: x.message, path: x.path }; }), setGlobalErrorMessages, - form + form, ); if (!error && !data?.updateDatabase?.errors) data?.updateDatabase?.database; diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index 52cd499d..ac68d8f6 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -6,10 +6,12 @@ import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -export default defineConfig([{ +export default defineConfig([ + { extends: [...next], rules: { - "react/display-name": "off", + "react/display-name": "off", }, -}]); \ No newline at end of file + }, +]); diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index e3c491a9..91cfe3e3 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -1,6 +1,11 @@ import { IncomingMessage, ServerResponse } from "http"; import { useMemo } from "react"; -import { ApolloClient, InMemoryCache, HttpLink, ErrorLike } from "@apollo/client"; +import { + ApolloClient, + InMemoryCache, + HttpLink, + ErrorLike, +} from "@apollo/client"; import { Defer20220824Handler } from "@apollo/client/incremental"; import { LocalState } from "@apollo/client/local-state"; import { CombinedGraphQLErrors } from "@apollo/client/errors"; @@ -65,7 +70,7 @@ function createIsomorphLink(context: ResolverContext = {}) { useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", headers: headers, - }) + }), ); } @@ -77,7 +82,7 @@ function createApolloClient(context?: ResolverContext) { possibleTypes: fragmentMatcherTypes.possibleTypes, }), localState: new LocalState({}), - incrementalHandler: new Defer20220824Handler() + incrementalHandler: new Defer20220824Handler(), }); } @@ -85,7 +90,7 @@ export function initializeApollo( initialState: any = null, // Pages with Next.js data fetching methods, like `getStaticProps`, can send // a custom context which will be used by `SchemaLink` to server render pages - context?: ResolverContext + context?: ResolverContext, ) { const _apolloClient = apolloClient ?? createApolloClient(context); @@ -110,18 +115,18 @@ export function initializeApollo( // See https://www.apollographql.com/docs/react/data/error-handling export function stringifyApolloError(error: ErrorLike) { if (CombinedGraphQLErrors.is(error)) { - return `Name(${error.name - }); Message(${error.message - }); Extensions(${error.extensions - }); Stack(${error.stack - }); GraphQL Errors(${error.errors - .map((e) => `[Message(${e.message - }); Path(${e.path?.join(".") - }); Locations(${e.locations?.join(", ") - }); Extensions(${e.extensions - })]`) - .join(", ") - })`; + return `Name(${error.name}); Message(${error.message}); Extensions(${ + error.extensions + }); Stack(${error.stack}); GraphQL Errors(${error.errors + .map( + (e) => + `[Message(${e.message}); Path(${e.path?.join( + ".", + )}); Locations(${e.locations?.join(", ")}); Extensions(${ + e.extensions + })]`, + ) + .join(", ")})`; // } if (CombinedProtocolErrors.is(error)) { // Handle multipart subscription protocol errors // } if (LocalStateError.is(error)) { diff --git a/frontend/lib/form.ts b/frontend/lib/form.ts index 81ce720d..09ae2a4f 100644 --- a/frontend/lib/form.ts +++ b/frontend/lib/form.ts @@ -6,16 +6,19 @@ export function handleFormErrors( apolloError: ErrorLike | undefined, userErrors: { code: string; message: string; path: string[] }[] | undefined, setGlobalErrorMessages: Dispatch>, - form: FormInstance + form: FormInstance, ) { const globalErrorMessages = new Array(); if (apolloError) { // TODO Is this how we want to handle GraphQl errors? globalErrorMessages.push(...`${apolloError.name}: ${apolloError.message}`); if (CombinedGraphQLErrors.is(apolloError)) { - globalErrorMessages.push(...apolloError.errors.map((e) => - `${e.message} at path ${e.path?.join(", ")} and locations ${e.locations?.join(", ")}` - )); + globalErrorMessages.push( + ...apolloError.errors.map( + (e) => + `${e.message} at path ${e.path?.join(", ")} and locations ${e.locations?.join(", ")}`, + ), + ); } } if (userErrors) { diff --git a/frontend/lib/freeTextFilter.tsx b/frontend/lib/freeTextFilter.tsx index 061df3dc..d5d405ae 100644 --- a/frontend/lib/freeTextFilter.tsx +++ b/frontend/lib/freeTextFilter.tsx @@ -8,7 +8,7 @@ import { Dispatch, Key, SetStateAction } from "react"; export function setMapValue( map: Map, - setMap: Dispatch>> + setMap: Dispatch>>, ) { return (key: string) => { return (newValue: string) => { @@ -23,22 +23,22 @@ export function setMapValue( export function doesFieldIncludeFilterValue( field: string, - value: React.Key | boolean + value: React.Key | boolean, ) { return field.toLowerCase().includes(value.toString().toLowerCase()); } export function getFreeTextFilterProps( getField: (record: RecordType) => string | null | undefined, - onFilterTextChange: (newFilterText: string) => void + onFilterTextChange: (newFilterText: string) => void, ) { const filter = ( selectedKeys: Key[], - confirm: (param?: FilterConfirmProps | undefined) => void + confirm: (param?: FilterConfirmProps | undefined) => void, ) => { confirm(); onFilterTextChange( - selectedKeys.length === 0 ? "" : selectedKeys[0].toString() + selectedKeys.length === 0 ? "" : selectedKeys[0].toString(), ); }; diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index 9aa89ac2..d53d218a 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -5,11 +5,7 @@ import { getFreeTextFilterProps, } from "./freeTextFilter"; import Link from "next/link"; -import { - Scalars, - Publication, - Standard, -} from "../__generated__/graphql"; +import { Scalars, Publication, Standard } from "../__generated__/graphql"; import { Highlight } from "../components/Highlight"; import paths from "../paths"; import { JSX } from "react"; @@ -20,7 +16,7 @@ const sortDirections: SortOrder[] = ["ascend", "descend"]; function compare( x: ValueType | null | undefined, y: ValueType | null | undefined, - nonNullCompare: (a: ValueType, b: ValueType) => number + nonNullCompare: (a: ValueType, b: ValueType) => number, ) { if (x != null && y != null) { return nonNullCompare(x, y); @@ -37,7 +33,7 @@ function compare( function compareLexicographically( xs: (ValueType | null | undefined)[], ys: (ValueType | null | undefined)[], - elementCompare: (x: ValueType, y: ValueType) => number + elementCompare: (x: ValueType, y: ValueType) => number, ) { let i = 0; while (i < xs.length && i < ys.length) { @@ -52,7 +48,7 @@ function compareLexicographically( export function getColumnProps( title: string, - key: keyof RecordType + key: keyof RecordType, ) { return { title: title, @@ -64,7 +60,9 @@ export function getColumnProps( export function getDateTimeColumnProps( title: string, key: keyof RecordType, - getValue: (record: RecordType) => Scalars["DateTime"]["output"] | null | undefined + getValue: ( + record: RecordType, + ) => Scalars["DateTime"]["output"] | null | undefined, ) { return { ...getColumnProps(title, key), @@ -72,20 +70,20 @@ export function getDateTimeColumnProps( compare( getValue(a), getValue(b), - (x, y) => Date.parse(y) - Date.parse(x) + (x, y) => Date.parse(y) - Date.parse(x), ), sortDirections: sortDirections, }; } export function getTimestampColumnProps< - RecordType extends { timestamp: Scalars["DateTime"]["output"] } + RecordType extends { timestamp: Scalars["DateTime"]["output"] }, >() { return { ...getDateTimeColumnProps( "Timestamp", "timestamp", - (record) => record.timestamp + (record) => record.timestamp, ), }; } @@ -93,7 +91,7 @@ export function getTimestampColumnProps< export function getStringColumnProps( title: string, key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined + getValue: (record: RecordType) => string | null | undefined, ) { return { ...getColumnProps(title, key), @@ -108,14 +106,14 @@ export function getFilterableStringColumnProps( key: keyof RecordType, getValue: (record: RecordType) => string | null | undefined, onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, doRender: ( record: RecordType, highlightedValue: JSX.Element, - value: string | null | undefined - ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue + value: string | null | undefined, + ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue, ) { return { ...getStringColumnProps(title, key, getValue), @@ -124,7 +122,7 @@ export function getFilterableStringColumnProps( doRender( record, , - getValue(record) + getValue(record), ), }; } @@ -134,10 +132,10 @@ export function getInternallyLinkedFilterableStringColumnProps( key: keyof RecordType, getValue: (record: RecordType) => string | null | undefined, onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (record: RecordType) => Route + getPath: (record: RecordType) => Route, ) { return getFilterableStringColumnProps( title, @@ -148,10 +146,12 @@ export function getInternallyLinkedFilterableStringColumnProps( (record, _highlightedValue, value) => value ? ( // TODO Why does this not work with `_highlightedValue`? An error is raised saying "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?": https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-function-component or https://reactjs.org/docs/forwarding-refs.html or https://deepscan.io/docs/rules/react-func-component-invalid-ref-prop or https://www.carlrippon.com/react-forwardref-typescript/ - ({value}) + + {value} + ) : ( <> - ) + ), ); } @@ -160,10 +160,10 @@ export function getExternallyLinkedFilterableStringColumnProps( key: keyof RecordType, getValue: (record: RecordType) => string | null | undefined, onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (record: RecordType) => string + getPath: (record: RecordType) => string, ) { return getFilterableStringColumnProps( title, @@ -178,7 +178,7 @@ export function getExternallyLinkedFilterableStringColumnProps( ) : ( <> ); - } + }, ); } @@ -187,9 +187,9 @@ export function getExternallyLinkedFilterableLocatorColumnProps( key: keyof RecordType, getValue: (record: RecordType) => string | null | undefined, onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableStringColumnProps( title, @@ -204,16 +204,18 @@ export function getExternallyLinkedFilterableLocatorColumnProps( ) : ( <> ); - } + }, ); } -export function getUuidColumnProps( +export function getUuidColumnProps< + RecordType extends { uuid: Scalars["Uuid"]["output"] }, +>( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (uuid: Scalars["Uuid"]["output"]) => Route + getPath: (uuid: Scalars["Uuid"]["output"]) => Route, ) { return getInternallyLinkedFilterableStringColumnProps( "UUID", @@ -221,56 +223,56 @@ export function getUuidColumnProps record.uuid, onFilterTextChange, getFilterText, - (record) => getPath(record.uuid) + (record) => getPath(record.uuid), ); } export function getNameColumnProps( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableStringColumnProps( "Name", "name", (record) => record.name, onFilterTextChange, - getFilterText + getFilterText, ); } export function getDescriptionColumnProps< - RecordType extends { description?: string | null } + RecordType extends { description?: string | null }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableStringColumnProps( "Description", "description", (record) => record.description, onFilterTextChange, - getFilterText + getFilterText, ); } export function getAbbreviationColumnProps< - RecordType extends { abbreviation?: string | null } + RecordType extends { abbreviation?: string | null }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableStringColumnProps( "Abbreviation", "abbreviation", (record) => record.abbreviation, onFilterTextChange, - getFilterText + getFilterText, ); } @@ -278,13 +280,13 @@ export function getAbbreviationColumnProps< export function getEnumListColumnProps( title: string, key: keyof RecordType, - getValues: (record: RecordType) => EnumType[] | null | undefined + getValues: (record: RecordType) => EnumType[] | null | undefined, ) { return { ...getColumnProps(title, key), sorter: (a: RecordType, b: RecordType) => compare(getValues(a), getValues(b), (xs, ys) => - compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")) + compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")), ), sortDirections: sortDirections, }; @@ -292,7 +294,7 @@ export function getEnumListColumnProps( export function getFilterableEnumListColumnProps< RecordType, - EnumType extends string + EnumType extends string, >( title: string, key: keyof RecordType, @@ -300,14 +302,14 @@ export function getFilterableEnumListColumnProps< getValues: (record: RecordType) => EnumType[] | null | undefined, // TODO Call `onFilterTextChange` when the filter text changes (that is not done right now and that is the reason why matches are not highlighted). Note though that it cannot be called inside `onFilter` because that function is being called on render and we may not change state on render! _onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, getFilterText: (key: keyof RecordType) => string | undefined, doRender: ( record: RecordType, highlightedValue: JSX.Element, - value: EnumType - ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue + value: EnumType, + ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue, ) { return { ...getEnumListColumnProps(title, key, getValues), @@ -339,7 +341,7 @@ export function getFilterableEnumListColumnProps< {doRender( record, , - value + value, )} ))} @@ -355,12 +357,12 @@ export function getDescriptionListColumnProps( key: keyof RecordType, getEntries: (record: RecordType) => | { - key: string; - title: string; - value: string | null | undefined; - }[] + key: string; + title: string; + value: string | null | undefined; + }[] | null - | undefined + | undefined, ) { return { ...getColumnProps(title, key), @@ -368,7 +370,7 @@ export function getDescriptionListColumnProps( const aValues = getEntries(a)?.map((x) => x.value); const bValues = getEntries(b)?.map((x) => x.value); return compare(aValues, bValues, (xs, ys) => - compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")) + compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")), ); }, sortDirections: sortDirections, @@ -401,21 +403,21 @@ export function getFilterableDescriptionListColumnProps( key: keyof RecordType, getEntries: (record: RecordType) => | { - key: string; - title: string; - value: string | null | undefined; - render?: ( - record: RecordType, - highlightedValue: JSX.Element, - value: string | null | undefined - ) => JSX.Element; - }[] + key: string; + title: string; + value: string | null | undefined; + render?: ( + record: RecordType, + highlightedValue: JSX.Element, + value: string | null | undefined, + ) => JSX.Element; + }[] | null | undefined, onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return { ...getDescriptionListColumnProps(title, key, getEntries), @@ -424,10 +426,10 @@ export function getFilterableDescriptionListColumnProps( getEntries(record) ?.map( ({ title, value }) => - `${title}${titleValueDescriptionListSeparator}${value}` + `${title}${titleValueDescriptionListSeparator}${value}`, ) ?.join("\n"), - onFilterTextChange(key) + onFilterTextChange(key), ), render: (_value: string, record: RecordType, _index: number) => { const titlesAndValues = getEntries(record); @@ -459,11 +461,11 @@ export function getFilterableDescriptionListColumnProps( ) : ( ), - value + value, )} ); - } + }, )} ); @@ -473,12 +475,12 @@ export function getFilterableDescriptionListColumnProps( } export function getReferenceColumnProps< - RecordType extends { reference?: Publication | Standard | null } + RecordType extends { reference?: Publication | Standard | null }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableDescriptionListColumnProps( "Reference", @@ -503,79 +505,79 @@ export function getReferenceColumnProps< ...(record.reference.__typename !== "Standard" ? [] : [ - { - key: "numeration", - title: "Numeration", - value: `${record.reference.numeration.prefix} ${record.reference.numeration.mainNumber} ${record.reference.numeration.suffix}`, - }, - { - key: "year", - title: "Year", - value: record.reference.year, - }, - { - key: "locator", - title: "Locator", - value: record.reference.locator, - render: ( - _record: RecordType, - hightlightedValue: JSX.Element, - value: string | null | undefined - ) => ( - // TODO Actually, `value` is neither `null` nor `undefined` but the type system does not know about it. How can we make it know about it so we don't need `|| ""` here? - ( - {hightlightedValue} - ) - ), - }, - { - key: "Standardizers", - title: "Standardizers", - value: record.reference.standardizers.join(", "), - }, - ]), + { + key: "numeration", + title: "Numeration", + value: `${record.reference.numeration.prefix} ${record.reference.numeration.mainNumber} ${record.reference.numeration.suffix}`, + }, + { + key: "year", + title: "Year", + value: record.reference.year, + }, + { + key: "locator", + title: "Locator", + value: record.reference.locator, + render: ( + _record: RecordType, + hightlightedValue: JSX.Element, + value: string | null | undefined, + ) => ( + // TODO Actually, `value` is neither `null` nor `undefined` but the type system does not know about it. How can we make it know about it so we don't need `|| ""` here? + + {hightlightedValue} + + ), + }, + { + key: "Standardizers", + title: "Standardizers", + value: record.reference.standardizers.join(", "), + }, + ]), ...(record.reference.__typename !== "Publication" ? [] : [ - { - key: "arXiv", - title: "arXiv", - value: record.reference.arXiv, - }, - { - key: "doi", - title: "DOI", - value: record.reference.doi, - }, - { - key: "urn", - title: "URN", - value: record.reference.urn, - }, - { - key: "webAddress", - title: "Website", - value: record.reference.webAddress, - }, - { - key: "authors", - title: "Authors", - value: record.reference.authors?.join(", "), - }, - ]), + { + key: "arXiv", + title: "arXiv", + value: record.reference.arXiv, + }, + { + key: "doi", + title: "DOI", + value: record.reference.doi, + }, + { + key: "urn", + title: "URN", + value: record.reference.urn, + }, + { + key: "webAddress", + title: "Website", + value: record.reference.webAddress, + }, + { + key: "authors", + title: "Authors", + value: record.reference.authors?.join(", "), + }, + ]), ], onFilterTextChange, - getFilterText + getFilterText, ); } export function getComponentUuidColumnProps< - RecordType extends { componentId: Scalars["Uuid"]["output"] } + RecordType extends { componentId: Scalars["Uuid"]["output"] }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getExternallyLinkedFilterableStringColumnProps( "Component UUID", @@ -583,17 +585,17 @@ export function getComponentUuidColumnProps< (x) => x.componentId, onFilterTextChange, getFilterText, - (x) => paths.metabase.component(x.componentId) + (x) => paths.metabase.component(x.componentId), ); } export function getAppliedMethodColumnProps< - RecordType extends { appliedMethod: { methodId: Scalars["Uuid"]["output"] } } + RecordType extends { appliedMethod: { methodId: Scalars["Uuid"]["output"] } }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableDescriptionListColumnProps( "Applied Method", @@ -623,17 +625,28 @@ export function getAppliedMethodColumnProps< // }, ], onFilterTextChange, - getFilterText + getFilterText, ); } export function getResourceTreeColumnProps< - RecordType extends { resourceTree: { root: { value: { description?: string | null | undefined, hashValue: string, locator: Scalars["Url"]["output"], dataFormatId: Scalars["Uuid"]["output"] } } } } + RecordType extends { + resourceTree: { + root: { + value: { + description?: string | null | undefined; + hashValue: string; + locator: Scalars["Url"]["output"]; + dataFormatId: Scalars["Uuid"]["output"]; + }; + }; + }; + }, >( onFilterTextChange: ( - key: keyof RecordType + key: keyof RecordType, ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined + getFilterText: (key: keyof RecordType) => string | undefined, ) { return getFilterableDescriptionListColumnProps( "Resource Tree Root", @@ -666,7 +679,7 @@ export function getResourceTreeColumnProps< render: (_record, hightlightedValue, _value) => ( {hightlightedValue} @@ -675,6 +688,6 @@ export function getResourceTreeColumnProps< }, ], onFilterTextChange, - getFilterText + getFilterText, ); } diff --git a/frontend/next.config.ts b/frontend/next.config.ts index c5ebc4ca..11f31308 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,4 +1,4 @@ -import type { NextConfig } from 'next' +import type { NextConfig } from "next"; const nextConfig: NextConfig = { reactStrictMode: true, @@ -9,9 +9,7 @@ const nextConfig: NextConfig = { : [new URL(process.env.NEXT_PUBLIC_DATABASE_URL).hostname], turbopack: { rules: { - '/\.(yml|yaml$)/': [ - 'yaml-loader', - ], + "/\.(yml|yaml$)/": ["yaml-loader"], }, }, experimental: { @@ -28,6 +26,6 @@ const nextConfig: NextConfig = { // ] // ] // }, -} +}; -export default nextConfig +export default nextConfig; diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 6df43cec..603f33be 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -3,7 +3,7 @@ import { useApollo } from "../lib/apollo"; import { ApolloProvider } from "@apollo/client/react"; import { CookiesProvider } from "react-cookie"; import { ConfigProvider, message } from "antd"; -import '../styles/global.css'; +import "../styles/global.css"; export default function App({ Component, pageProps }: AppProps) { const apolloClient = useApollo(pageProps.initialApolloState); @@ -14,7 +14,7 @@ export default function App({ Component, pageProps }: AppProps) { }); return ( - + diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index 75fccc82..1d3ba3a3 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -51,7 +51,7 @@ enum Negator { const negateIfNecessary = ( negator: Negator, - proposition: CalorimetricDataPropositionInput + proposition: CalorimetricDataPropositionInput, ): CalorimetricDataPropositionInput => { switch (negator) { case Negator.Is: @@ -63,7 +63,7 @@ const negateIfNecessary = ( }; const conjunct = ( - propositions: CalorimetricDataPropositionInput[] + propositions: CalorimetricDataPropositionInput[], ): CalorimetricDataPropositionInput => { if (propositions.length == 0) { return {}; @@ -90,7 +90,7 @@ function Page() { const [form] = Form.useForm(); const [filtering, setFiltering] = useState(false); const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() + new Array(), ); const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); @@ -113,33 +113,33 @@ function Page() { uValues, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; gValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; uValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -151,7 +151,7 @@ function Page() { propositions.push( negateIfNecessary(negator, { componentId: { [comparator]: value }, - }) + }), ); } } @@ -164,7 +164,7 @@ function Page() { dataFormatId: { [comparator]: value }, }, }, - }) + }), ); } } @@ -180,7 +180,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -195,7 +195,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -204,8 +204,8 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + }, ); if (error) { // TODO Handle properly. @@ -213,7 +213,10 @@ function Page() { messageApi.error(error.message); } // TODO Casting to `CalorimetricData` is wrong and error prone! - setData((data?.allCalorimetricData?.edges?.map((x) => x.node) || []) as CalorimetricData[]); + setData( + (data?.allCalorimetricData?.edges?.map((x) => x.node) || + []) as CalorimetricData[], + ); } catch (error) { // TODO Handle properly. console.log("Failed:", error); @@ -261,18 +264,18 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - paths.calorimetricDatum + paths.calorimetricDatum, ), }, { ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) + filterText.get(x), ), }, { ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { @@ -281,7 +284,7 @@ function Page() { { ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, // { @@ -297,13 +300,13 @@ function Page() { { ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { diff --git a/frontend/pages/data/create.tsx b/frontend/pages/data/create.tsx index dd7d16d4..8c7ec9d9 100644 --- a/frontend/pages/data/create.tsx +++ b/frontend/pages/data/create.tsx @@ -10,9 +10,7 @@ function Page() { Create data by sending GraphQL mutations to the{" "} - + GraphQL endpoint . diff --git a/frontend/pages/data/geometric/[uuid].tsx b/frontend/pages/data/geometric/[uuid].tsx index cd86778f..8a01c9b8 100644 --- a/frontend/pages/data/geometric/[uuid].tsx +++ b/frontend/pages/data/geometric/[uuid].tsx @@ -3,18 +3,18 @@ import GeometricData from "../../../components/data/geometric/GeometricData"; import Layout from "../../../components/Layout"; function Page() { - const router = useRouter(); + const router = useRouter(); - if (!router.isReady) { - return null; - } + if (!router.isReady) { + return null; + } - const { uuid } = router.query; + const { uuid } = router.query; - return ( - - - - ); + return ( + + + + ); } -export default Page; \ No newline at end of file +export default Page; diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index e78f9d4c..bf953f5c 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -1,270 +1,275 @@ import Layout from "../../../components/Layout"; import { - Table, - message, - Form, - Button, - Alert, - Typography, - Descriptions, + Table, + message, + Form, + Button, + Alert, + Typography, + Descriptions, } from "antd"; import { AllGeometricDataDocument } from "../../../queries/data.generated"; import { - GeometricData, - Scalars, - GeometricDataPropositionInput, + GeometricData, + Scalars, + GeometricDataPropositionInput, } from "../../../__generated__/graphql"; import { useState } from "react"; import { setMapValue } from "../../../lib/freeTextFilter"; import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, + getAppliedMethodColumnProps, + getComponentUuidColumnProps, + getDescriptionColumnProps, + getNameColumnProps, + getResourceTreeColumnProps, + getTimestampColumnProps, + getUuidColumnProps, } from "../../../lib/table"; import { - FloatPropositionComparator, - FloatPropositionFormList, + FloatPropositionComparator, + FloatPropositionFormList, } from "../../../components/FloatPropositionFormList"; import { - UuidPropositionComparator, - UuidPropositionFormList, + UuidPropositionComparator, + UuidPropositionFormList, } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; import { useQuery } from "@apollo/client/react"; const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, }; const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, + wrapperCol: { offset: 8, span: 16 }, }; enum Negator { - Is = "is", - IsNot = "isNot", + Is = "is", + IsNot = "isNot", } const negateIfNecessary = ( - negator: Negator, - proposition: GeometricDataPropositionInput + negator: Negator, + proposition: GeometricDataPropositionInput, ): GeometricDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - return proposition; - } + switch (negator) { + case Negator.Is: + return proposition; + case Negator.IsNot: + return proposition; + } }; const conjunct = ( - propositions: GeometricDataPropositionInput[] + propositions: GeometricDataPropositionInput[], ): GeometricDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; + if (propositions.length == 0) { + return {}; + } + if (propositions.length == 1) { + return propositions[0]; + } + return { and: propositions }; }; function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() - ); - const [messageApi, contextHolder] = message.useMessage(); - const [data, setData] = useState([]); + const [form] = Form.useForm(); + const [filtering, setFiltering] = useState(false); + const [globalErrorMessages, setGlobalErrorMessages] = useState( + new Array(), + ); + const [messageApi, contextHolder] = message.useMessage(); + const [data, setData] = useState([]); - const allGeometricDataQuery = useQuery(AllGeometricDataDocument, { - skip: true, - errorPolicy: "all", - }); + const allGeometricDataQuery = useQuery(AllGeometricDataDocument, { + skip: true, + errorPolicy: "all", + }); - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); + const [filterText, setFilterText] = useState(() => new Map()); + const onFilterTextChange = setMapValue(filterText, setFilterText); - const onFinish = ({ - componentIds, - dataFormatIds, - thicknesses, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; + const onFinish = ({ + componentIds, + dataFormatIds, + thicknesses, + }: { + componentIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; + | undefined; + dataFormatIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; }[] - | undefined; - thicknesses: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; + | undefined; + thicknesses: + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - const propositions: GeometricDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }) - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }) - ); - } - } - if (thicknesses) { - for (let { negator, comparator, value } of thicknesses) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - thicknesses: { - some: { - [comparator]: value, - }, - }, - }) - ); - } - } - } - const { error, data } = await allGeometricDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - } - ); - if (error) { - console.log(error); - messageApi.error(error.message); - } - setData((data?.allGeometricData?.edges?.map((x) => x.node) || []) as GeometricData[]); - } catch (error) { - console.log("Failed:", error); - } finally { - setFiltering(false); + | undefined; + }) => { + const filter = async () => { + try { + setFiltering(true); + const propositions: GeometricDataPropositionInput[] = []; + if (componentIds) { + for (let { negator, comparator, value } of componentIds) { + propositions.push( + negateIfNecessary(negator, { + componentId: { [comparator]: value }, + }), + ); + } + } + if (dataFormatIds) { + for (let { negator, comparator, value } of dataFormatIds) { + propositions.push( + negateIfNecessary(negator, { + resources: { + some: { + dataFormatId: { [comparator]: value }, + }, + }, + }), + ); + } + } + if (thicknesses) { + for (let { negator, comparator, value } of thicknesses) { + if (value !== undefined && value !== null) { + propositions.push( + negateIfNecessary(negator, { + thicknesses: { + some: { + [comparator]: value, + }, + }, + }), + ); } - }; - filter(); - }; - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); + } + } + const { error, data } = await allGeometricDataQuery.refetch( + propositions.length == 0 + ? {} + : { + where: conjunct(propositions), + }, + ); + if (error) { + console.log(error); + messageApi.error(error.message); + } + setData( + (data?.allGeometricData?.edges?.map((x) => x.node) || + []) as GeometricData[], + ); + } catch (error) { + console.log("Failed:", error); + } finally { + setFiltering(false); + } }; + filter(); + }; + const onFinishFailed = () => { + setGlobalErrorMessages(["Fix the errors below."]); + }; - return ( - - {contextHolder} - Geometric Data - {globalErrorMessages.length > 0 && ( - - )} + return ( + + {contextHolder} + Geometric Data + {globalErrorMessages.length > 0 && ( + + )} -
- - - + + + + - - - - -
( - onFilterTextChange, - (x) => filterText.get(x), - paths.geometricDatum - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x) - ), - }, - { - title: "Thicknesses", - key: "thicknesses", - render: (_text, record, _index) => ( - - - {record.thicknesses.map((x) => x.toLocaleString("en")).join(", ")} - - - ), - }, - ]} - dataSource={data} - /> - - ); + + + + +
( + onFilterTextChange, + (x) => filterText.get(x), + paths.geometricDatum, + ), + }, + { + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => + filterText.get(x), + ), + }, + { + ...getDescriptionColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getTimestampColumnProps<(typeof data)[0]>(), + }, + { + ...getComponentUuidColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getAppliedMethodColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getResourceTreeColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + title: "Thicknesses", + key: "thicknesses", + render: (_text, record, _index) => ( + + + {record.thicknesses + .map((x) => x.toLocaleString("en")) + .join(", ")} + + + ), + }, + ]} + dataSource={data} + /> + + ); } -export default Page; \ No newline at end of file +export default Page; diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 63cf93d3..238d722d 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -39,7 +39,7 @@ enum Negator { const negateIfNecessary = ( negator: Negator, - proposition: HygrothermalDataPropositionInput + proposition: HygrothermalDataPropositionInput, ): HygrothermalDataPropositionInput => { switch (negator) { case Negator.Is: @@ -51,7 +51,7 @@ const negateIfNecessary = ( }; const conjunct = ( - propositions: HygrothermalDataPropositionInput[] + propositions: HygrothermalDataPropositionInput[], ): HygrothermalDataPropositionInput => { if (propositions.length == 0) { return {}; @@ -78,7 +78,7 @@ function Page() { const [form] = Form.useForm(); const [filtering, setFiltering] = useState(false); const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() + new Array(), ); const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); @@ -99,19 +99,19 @@ function Page() { dataFormatIds, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -123,7 +123,7 @@ function Page() { propositions.push( negateIfNecessary(negator, { componentId: { [comparator]: value }, - }) + }), ); } } @@ -136,7 +136,7 @@ function Page() { dataFormatId: { [comparator]: value }, }, }, - }) + }), ); } } @@ -144,8 +144,8 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + }, ); if (error) { // TODO Handle properly. @@ -153,7 +153,10 @@ function Page() { messageApi.error(error.message); } // TODO Casting to `HygrothermalData` is wrong and error prone! - setData((data?.allHygrothermalData?.edges?.map((x) => x.node) || []) as HygrothermalData[]); + setData( + (data?.allHygrothermalData?.edges?.map((x) => x.node) || + []) as HygrothermalData[], + ); } catch (error) { // TODO Handle properly. console.log("Failed:", error); @@ -199,18 +202,18 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - paths.hygrothermalDatum + paths.hygrothermalDatum, ), }, { ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) + filterText.get(x), ), }, { ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { @@ -219,7 +222,7 @@ function Page() { { ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, // { @@ -235,13 +238,13 @@ function Page() { { ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, ]} diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index 7eaa1b9d..a7cd664c 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -51,7 +51,7 @@ enum Negator { const negateIfNecessary = ( negator: Negator, - proposition: OpticalDataPropositionInput + proposition: OpticalDataPropositionInput, ): OpticalDataPropositionInput => { switch (negator) { case Negator.Is: @@ -63,7 +63,7 @@ const negateIfNecessary = ( }; const conjunct = ( - propositions: OpticalDataPropositionInput[] + propositions: OpticalDataPropositionInput[], ): OpticalDataPropositionInput => { if (propositions.length == 0) { return {}; @@ -90,7 +90,7 @@ function Page() { const [form] = Form.useForm(); const [filtering, setFiltering] = useState(false); const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() + new Array(), ); const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); @@ -116,54 +116,54 @@ function Page() { nearnormalHemisphericalVisibleTransmittances, }: { componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; infraredEmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalSolarReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalSolarTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalVisibleReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; nearnormalHemisphericalVisibleTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; + | { + negator: Negator; + comparator: FloatPropositionComparator; + value: number | undefined; + }[] + | undefined; }) => { const filter = async () => { try { @@ -175,7 +175,7 @@ function Page() { propositions.push( negateIfNecessary(negator, { componentId: { [comparator]: value }, - }) + }), ); } } @@ -188,7 +188,7 @@ function Page() { dataFormatId: { [comparator]: value }, }, }, - }) + }), ); } } @@ -204,7 +204,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -223,7 +223,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -242,7 +242,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -261,7 +261,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -280,7 +280,7 @@ function Page() { [comparator]: value, }, }, - }) + }), ); } } @@ -289,8 +289,8 @@ function Page() { propositions.length == 0 ? {} : { - where: conjunct(propositions), - } + where: conjunct(propositions), + }, ); if (error) { // TODO Handle properly. @@ -298,7 +298,10 @@ function Page() { messageApi.error(error.message); } // TODO Casting to `OpticalData` is wrong and error prone! - setData((data?.allOpticalData?.edges?.map((x) => x.node) || []) as OpticalData[]); + setData( + (data?.allOpticalData?.edges?.map((x) => x.node) || + []) as OpticalData[], + ); } catch (error) { // TODO Handle properly. console.log("Failed:", error); @@ -364,18 +367,18 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - paths.opticalDatum + paths.opticalDatum, ), }, { ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) + filterText.get(x), ), }, { ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { @@ -384,7 +387,7 @@ function Page() { { ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, // { @@ -400,13 +403,13 @@ function Page() { { ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index 88258c3c..929fde56 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -39,7 +39,7 @@ enum Negator { const negateIfNecessary = ( negator: Negator, - proposition: PhotovoltaicDataPropositionInput + proposition: PhotovoltaicDataPropositionInput, ): PhotovoltaicDataPropositionInput => { switch (negator) { case Negator.Is: @@ -51,7 +51,7 @@ const negateIfNecessary = ( }; const conjunct = ( - propositions: PhotovoltaicDataPropositionInput[] + propositions: PhotovoltaicDataPropositionInput[], ): PhotovoltaicDataPropositionInput => { if (propositions.length == 0) { return {}; @@ -78,7 +78,7 @@ function Page() { const [form] = Form.useForm(); const [filtering, setFiltering] = useState(false); const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array() + new Array(), ); const [messageApi, contextHolder] = message.useMessage(); const [data, setData] = useState([]); @@ -123,7 +123,7 @@ function Page() { propositions.push( negateIfNecessary(negator, { componentId: { [comparator]: value }, - }) + }), ); } } @@ -136,7 +136,7 @@ function Page() { dataFormatId: { [comparator]: value }, }, }, - }) + }), ); } } @@ -145,7 +145,7 @@ function Page() { ? {} : { where: conjunct(propositions), - } + }, ); if (error) { // TODO Handle properly. @@ -153,7 +153,10 @@ function Page() { messageApi.error(error.message); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! - setData((data?.allPhotovoltaicData?.edges?.map((x) => x.node) || []) as PhotovoltaicData[]); + setData( + (data?.allPhotovoltaicData?.edges?.map((x) => x.node) || + []) as PhotovoltaicData[], + ); } catch (error) { // TODO Handle properly. console.log("Failed:", error); @@ -199,18 +202,18 @@ function Page() { ...getUuidColumnProps<(typeof data)[0]>( onFilterTextChange, (x) => filterText.get(x), - paths.photovoltaicDatum + paths.photovoltaicDatum, ), }, { ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x) + filterText.get(x), ), }, { ...getDescriptionColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { @@ -219,7 +222,7 @@ function Page() { { ...getComponentUuidColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, // { @@ -235,13 +238,13 @@ function Page() { { ...getAppliedMethodColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, { ...getResourceTreeColumnProps<(typeof data)[0]>( onFilterTextChange, - (x) => filterText.get(x) + (x) => filterText.get(x), ), }, ]} diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 4f3d741c..2be04d8a 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -54,40 +54,28 @@ function Page() { buildingenvelopedata.org , you can search there for example for{" "} - + optical data{" "} . You will find there also an overview about all building envelope{" "} - + {" "} components ,{" "} - + institutions ,{" "} - + data formats ,{" "} - + methods {" "} and{" "} - + databases {" "} of the Product Data Network{" "} @@ -98,16 +86,12 @@ function Page() { This website is completely based on the - + {" "} GraphQL endpoint . The{" "} - + GraphQL endpoint {" "} is the Application Programming Interface (API) to the backend of the @@ -119,9 +103,7 @@ function Page() { {" "} which is especially helpful if you develop software to plan buildings. The{" "} - + GraphQL endpoint of the metabase {" "} is then a convenient way to query for data, components, institutions, @@ -130,7 +112,9 @@ function Page() { The data format{" "} BED-JSON {" "} @@ -140,9 +124,7 @@ function Page() { JSON Schemas of the API specification . Other{" "} - + data formats {" "} are available, too. @@ -153,7 +135,8 @@ function Page() { style={{ maxWidth: "100%", height: "auto", - }} /> + }} + /> ); diff --git a/frontend/pages/upload-file.tsx b/frontend/pages/upload-file.tsx index 9928a0a3..9656e255 100644 --- a/frontend/pages/upload-file.tsx +++ b/frontend/pages/upload-file.tsx @@ -20,7 +20,7 @@ function Page() { const constructFileUploadAction = (_file: RcFile) => `/api/upload-file?getHttpsResourceUuid=${encodeURIComponent( - form.getFieldValue("getHttpsResourceUuid") + form.getFieldValue("getHttpsResourceUuid"), )}`; return ( diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 5cf29e2c..e7bc193f 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,4 +1,4 @@ -import { useQuery } from '@apollo/client/react'; +import { useQuery } from "@apollo/client/react"; import Layout from "../components/Layout"; import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; import { Skeleton, Result, Descriptions, Typography, message } from "antd"; diff --git a/frontend/paths.ts b/frontend/paths.ts index ac7576da..fa296d69 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -31,38 +31,79 @@ export default { uploadFile: "/upload-file" as Route, login: "/connect/login" as Route, logout: "/connect/logout" as Route, - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href as Route, + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL) + .href as Route, metabase: { home: process.env.NEXT_PUBLIC_METABASE_URL, - components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, component(uuid: string) { - return new URL(`/components/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/components/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, database(uuid: string) { - return new URL(`/databases/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/databases/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, dataFormat(uuid: string) { - return new URL(`/data-formats/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/data-formats/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, institution(uuid: string) { - return new URL(`/institutions/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/institutions/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, method(uuid: string) { - return new URL(`/methods/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/methods/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, user(uuid: string) { - return new URL(`/users/${encodeURIComponent(uuid)}`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route + return new URL( + `/users/${encodeURIComponent(uuid)}`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route; }, - allCalorimetricData: new URL(`/data/calorimetric`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, - allGeometricData: new URL(`/data/geometric`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, - allHygrothermalData: new URL(`/data/hygrothermal`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, - allOpticalData: new URL(`/data/optical`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, - allPhotovoltaicData: new URL(`/data/photovoltaic`, process.env.NEXT_PUBLIC_METABASE_URL).href as Route, - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL).href as Route, + allCalorimetricData: new URL( + `/data/calorimetric`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route, + allGeometricData: new URL( + `/data/geometric`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route, + allHygrothermalData: new URL( + `/data/hygrothermal`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route, + allOpticalData: new URL( + `/data/optical`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route, + allPhotovoltaicData: new URL( + `/data/photovoltaic`, + process.env.NEXT_PUBLIC_METABASE_URL, + ).href as Route, + graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL) + .href as Route, }, }; diff --git a/frontend/styles/global.css b/frontend/styles/global.css index 7c476937..f61b4468 100644 --- a/frontend/styles/global.css +++ b/frontend/styles/global.css @@ -1,8 +1,8 @@ .message { - margin-top: 10hv; + margin-top: 10hv; } .error-message { - margin-top: 5px; - margin-bottom: 5px; -} \ No newline at end of file + margin-top: 5px; + margin-bottom: 5px; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 52bbc88e..79f39b3d 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -6,10 +6,7 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "jsx": "react-jsx", - "lib": [ - "dom", - "es2017" - ], + "lib": ["dom", "es2017"], "module": "esnext", "moduleResolution": "node", "noEmit": true, @@ -23,13 +20,6 @@ "incremental": true // "verbatimModuleSyntax": true }, - "include": [ - "next-env.d.ts", - ".next/types/**/*.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] -} \ No newline at end of file + "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index f7f6611b..121859a2 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -25,7 +25,10 @@ interface Data { description: String isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -110,10 +113,16 @@ type CalorimetricData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -328,10 +337,16 @@ type GeometricData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! @@ -394,7 +409,10 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices( + order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") + where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + ): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -423,10 +441,16 @@ type HygrothermalData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -475,26 +499,55 @@ type KeyValuePairOfGuidAndNullableOfUInt32 { } type Mutation { - addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") - createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") - createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") - createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") - createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! + @cost(weight: "10") + addInstitutionAccessRights( + input: AddInstitutionAccessRightsInput! + ): AddInstitutionAccessRightsPayload! @cost(weight: "10") + createCalorimetricData( + input: CreateCalorimetricDataInput! + ): CreateCalorimetricDataPayload! @cost(weight: "10") + createGeometricData( + input: CreateGeometricDataInput! + ): CreateGeometricDataPayload! @cost(weight: "10") + createGetHttpsResource( + input: CreateGetHttpsResourceInput! + ): CreateGetHttpsResourcePayload! @cost(weight: "10") + createHygrothermalData( + input: CreateHygrothermalDataInput! + ): CreateHygrothermalDataPayload! @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! + @cost(weight: "10") + createPhotovoltaicData( + input: CreatePhotovoltaicDataInput! + ): CreatePhotovoltaicDataPayload! @cost(weight: "10") + createResponseApprovals( + where: CreateResponseApprovalsFilterInput @cost(weight: "10") + ): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") + deleteGetHttpsResource( + input: DeleteGetHttpsResourceInput! + ): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues( + where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10") + ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + removeDataApproval( + input: RemoveDataApprovalInput! + ): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") - updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateDataAccessRights( + input: UpdateDataAccessRightsInput! + ): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! + @cost(weight: "10") + updateInstitutionAccessRights( + input: UpdateInstitutionAccessRightsInput! + ): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") + updateResponseApprovals( + where: UpdateResponseApprovalsFilterInput @cost(weight: "10") + ): UpdateResponseApprovalsPayload! @cost(weight: "10") } type NamedMethodArgument { @@ -535,14 +588,20 @@ type OpticalData implements Node & Data { infraredEmittances: [Float!]! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! @@ -595,10 +654,16 @@ type PhotovoltaicData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser( + alreadyAccesedCount: NonNegativeInt! + uuid: Uuid! + ): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -653,39 +718,301 @@ type PublishDataPayload { } type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") + allCalorimetricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [CalorimetricDataSortInput!] @cost(weight: "10") + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): CalorimetricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allGeometricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [GeometricDataSortInput!] @cost(weight: "10") + where: GeometricDataPropositionInput @cost(weight: "10") + ): GeometricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allHygrothermalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [HygrothermalDataSortInput!] @cost(weight: "10") + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): HygrothermalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allOpticalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [OpticalDataSortInput!] @cost(weight: "10") + where: OpticalDataPropositionInput @cost(weight: "10") + ): OpticalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingCalorimetricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [CalorimetricDataSortInput!] @cost(weight: "10") + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): CalorimetricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingGeometricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [GeometricDataSortInput!] @cost(weight: "10") + where: GeometricDataPropositionInput @cost(weight: "10") + ): GeometricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingHygrothermalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [HygrothermalDataSortInput!] @cost(weight: "10") + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): HygrothermalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingOpticalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [OpticalDataSortInput!] @cost(weight: "10") + where: OpticalDataPropositionInput @cost(weight: "10") + ): OpticalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingPhotovoltaicData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [PhotovoltaicDataSortInput!] @cost(weight: "10") + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): PhotovoltaicDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPhotovoltaicData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [PhotovoltaicDataSortInput!] @cost(weight: "10") + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): PhotovoltaicDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + calculateMethod( + dataReference: CrossDatabaseDataReferenceInput! + methodId: Uuid! + ): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload( + data: Upload! + methodId: Uuid! + ): CalculateMethodPayload! @cost(weight: "10") + calorimetricData(id: Uuid!, locale: Locale): CalorimetricData + @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") - data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") + data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") - geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") + geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + getHttpsResources( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): GetHttpsResourceConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + hasCalorimetricData( + locale: Locale + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasGeometricData( + locale: Locale + where: GeometricDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasHygrothermalData( + locale: Locale + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasOpticalData( + locale: Locale + where: OpticalDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasPhotovoltaicData( + locale: Locale + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData + @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") - pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") + opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): GetHttpsResourceConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData + @cost(weight: "10") verificationCode: String! } @@ -1192,7 +1519,8 @@ input GetHttpsResourceSortInput { description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput + @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } @@ -1201,7 +1529,8 @@ input GetHttpsResourceTreeNonRootVertSortInput { description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput + @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } @@ -2116,18 +2445,35 @@ enum UriHostNameType { } "The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION +directive @cost( + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! +) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION +directive @listSize( + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean! = true + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!] + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] +) on FIELD_DEFINITION "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR +directive @specifiedBy( + "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." + url: String! +) on SCALAR scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") +scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") "BCP 47 compliant Language Tag string" scalar Locale @@ -2143,4 +2489,4 @@ scalar Upload scalar Url -scalar Uuid \ No newline at end of file +scalar Uuid From 3b4b95124d30781aec88fd05f649e3436cc75a23 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 18:50:30 +0100 Subject: [PATCH 571/878] Use the HotChocolate execution engine of the project itself directly to construct response approvals (instead of making an unnecessary HTTPS round trip) --- .../src/ApiRequests/GraphQlQueryHelpers.cs | 35 ++++++++ .../ApiRequests/IsGnuPgFingerprintValid.cs | 2 +- backend/src/ApiRequests/QueryAllMetaData.cs | 70 ---------------- .../src/ApiRequests/QueryByIdDataLoader.cs | 2 +- .../QueryCurrentUserOrApplication.cs | 2 +- backend/src/ApiRequests/QueryDatabase.cs | 2 +- backend/src/ApiRequests/UpdateDatabase.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 5 ++ backend/src/Services/ApiRequestService.cs | 26 ------ .../src/Services/ResponseApprovalService.cs | 81 ++++++++++++++++--- 10 files changed, 113 insertions(+), 114 deletions(-) create mode 100644 backend/src/ApiRequests/GraphQlQueryHelpers.cs delete mode 100644 backend/src/ApiRequests/QueryAllMetaData.cs diff --git a/backend/src/ApiRequests/GraphQlQueryHelpers.cs b/backend/src/ApiRequests/GraphQlQueryHelpers.cs new file mode 100644 index 00000000..9925c960 --- /dev/null +++ b/backend/src/ApiRequests/GraphQlQueryHelpers.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; + +namespace Database.ApiRequests; + +public static class GraphQlQueryHelpers +{ + public static Task Construct( + string fileName + ) + { + return Construct([fileName]); + } + + /// + /// Construct query from passed files. + /// + /// Name of files containung queries. + /// Query from all files. + public static async Task Construct( + string[] fileNames + ) + { + return string.Join( + Environment.NewLine, + await Task.WhenAll( + fileNames.Select(fileName => + File.ReadAllTextAsync($"./ApiRequests/Queries/{fileName}") + ) + ) + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index e3011be9..8e29c4a5 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -33,7 +33,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(QueryFileName), + await GraphQlQueryHelpers.Construct(QueryFileName), new { fingerprint, diff --git a/backend/src/ApiRequests/QueryAllMetaData.cs b/backend/src/ApiRequests/QueryAllMetaData.cs deleted file mode 100644 index bb697b22..00000000 --- a/backend/src/ApiRequests/QueryAllMetaData.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; -using Database.Services; -using GraphQL; - -namespace Database.ApiRequests; - -public sealed class QueryAllMetaData -{ - public static Uri GetGraphQlEndpoint(AppSettings appSettings) => - appSettings.DatabaseGraphQlEndpoint; - - public static readonly string[] CalorimetricDataFileNames = - [ - "DataFields.graphql", - "CalorimetricDataFields.graphql", - "CalorimetricData.graphql" - ]; - - public static readonly string[] GeometricDataFileNames = - [ - "DataFields.graphql", - "GeometricDataFields.graphql", - "GeometricData.graphql" - ]; - - public static readonly string[] HygrothermalDataFileNames = - [ - "DataFields.graphql", - "HygrothermalDataFields.graphql", - "HygrothermalData.graphql" - ]; - - public static readonly string[] PhotovoltaicDataFileNames = - [ - "DataFields.graphql", - "PhotovoltaicDataFields.graphql", - "PhotovoltaicData.graphql" - ]; - - public static readonly string[] OpticalDataFileNames = - [ - "DataFields.graphql", - "OpticalDataFields.graphql", - "OpticalData.graphql" - ]; - - public static async Task<(string Query, JsonElement Variables, string Response)> Do( - Guid dataId, - string[] fileNames, - AppSettings appSettings, - ApiRequestService apiRequestService, - CancellationToken cancellationToken - ) - { - var variables = new { id = dataId }; - var query = await apiRequestService.ConstructGraphQlQuery(fileNames); - var response = await apiRequestService.QueryGraphQlAsString( - GetGraphQlEndpoint(appSettings), - new GraphQLRequest( - query, - variables - ), - cancellationToken - ); - return (query, JsonSerializer.SerializeToElement(variables), response); - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryByIdDataLoader.cs b/backend/src/ApiRequests/QueryByIdDataLoader.cs index 057e54f7..c2f26a4a 100644 --- a/backend/src/ApiRequests/QueryByIdDataLoader.cs +++ b/backend/src/ApiRequests/QueryByIdDataLoader.cs @@ -45,7 +45,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(queryFileNames), + await GraphQlQueryHelpers.Construct(queryFileNames), new { ids diff --git a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs index 6cbede94..257a4567 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -115,7 +115,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(QueryFileName), + await GraphQlQueryHelpers.Construct(QueryFileName), new { databaseId = appSettings.DatabaseId diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index e5b79177..a0dc3f45 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -51,7 +51,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery(QueryFileName), + await GraphQlQueryHelpers.Construct(QueryFileName), new { id = databaseId diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 6d048140..32fdb52d 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -53,7 +53,7 @@ CancellationToken cancellationToken return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( - await apiRequestService.ConstructGraphQlQuery( + await GraphQlQueryHelpers.Construct( UpdateDatabaseFileName ), new diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 523da221..b6d95bdb 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -31,6 +31,11 @@ IWebHostEnvironment environment // GraphQL Server services .AddGraphQLServer() + // TODO add warmup task once we upgrade to version 16: https://chillicream.com/docs/hotchocolate/v16/server/warmup + // .AddWarmupTask(async (executor, cancellationToken) => + // { + // await executor.ExecuteAsync("{ __typename }", cancellationToken); + // }) .DisableIntrospection(false) // if the introspection result becomes too big we need to disable it in production .BindRuntimeType() // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index c8e54e03..d19b6b60 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -24,32 +24,6 @@ IHttpContextAccessor httpContextAccessor { public const string CustomHttpClient = "Custom"; - public Task ConstructGraphQlQuery( - string fileName - ) - { - return ConstructGraphQlQuery([fileName]); - } - - /// - /// Construct query from passed files. - /// - /// Name of files containung queries. - /// Query from all files. - public async Task ConstructGraphQlQuery( - string[] fileNames - ) - { - return string.Join( - Environment.NewLine, - await Task.WhenAll( - fileNames.Select(fileName => - File.ReadAllTextAsync($"./ApiRequests/Queries/{fileName}") - ) - ) - ); - } - public Task QueryGraphQlAsJson( Uri url, GraphQLRequest request, diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 03e93e39..fefad906 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -8,6 +8,9 @@ using Database.Json; using HotChocolate; using System.Linq; +using HotChocolate.Execution; +using Database.ApiRequests; +using System.Collections.Generic; namespace Database.Services; @@ -16,21 +19,56 @@ public static partial class ResponseApprovalServiceLogging [LoggerMessage( Level = LogLevel.Debug, Message = "Querying all meta data for {DataType} with ID {Id}")] - public static partial void QueryAllMetaData(this ILogger logger, Type dataType, Guid id); + public static partial void Query(this ILogger logger, Type dataType, Guid id); [LoggerMessage( Level = LogLevel.Debug, Message = "Query, variables, and response: {Query}, {Variables}, and {Response}")] - public static partial void QueryAndVariablesAndResponce(this ILogger logger, string query, JsonElement variables, string response); + public static partial void QueryAndVariablesAndResponse(this ILogger logger, string query, JsonElement variables, string response); } public sealed class ResponseApprovalService( AppSettings appSettings, SigningService signingService, - ApiRequestService apiRequestService, + IRequestExecutorResolver requestExecutorResolver, ILogger logger ) { + private static readonly string[] s_calorimetricDataQueryFileNames = + [ + "DataFields.graphql", + "CalorimetricDataFields.graphql", + "CalorimetricData.graphql" + ]; + + private static readonly string[] s_geometricDataQueryFileNames = + [ + "DataFields.graphql", + "GeometricDataFields.graphql", + "GeometricData.graphql" + ]; + + private static readonly string[] s_hygrothermalDataQueryFileNames = + [ + "DataFields.graphql", + "HygrothermalDataFields.graphql", + "HygrothermalData.graphql" + ]; + + private static readonly string[] s_photovoltaicDataQueryFileNames = + [ + "DataFields.graphql", + "PhotovoltaicDataFields.graphql", + "PhotovoltaicData.graphql" + ]; + + private static readonly string[] s_opticalDataQueryFileNames = + [ + "DataFields.graphql", + "OpticalDataFields.graphql", + "OpticalData.graphql" + ]; + /// /// Create response approval by calling graphql Api and signing responce. /// @@ -41,10 +79,8 @@ ILogger logger public async Task CreateResponseApproval(T dataObject, CancellationToken cancellationToken) where T : IData { - // Get dataset - logger.QueryAllMetaData(dataObject.GetType(), dataObject.Id); - var (query, variables, response) = await QueryAllMetaData(dataObject, cancellationToken); - logger.QueryAndVariablesAndResponce(query, variables, response); + var (query, variables, response) = await Query(dataObject, cancellationToken); + logger.QueryAndVariablesAndResponse(query, variables, response); var typedResponse = JsonSerializer.Deserialize>(response, JsonSerializerSettings.GraphQl) ?? throw new JsonException($"Failed to deserialize the GraphQL response: {response}"); if (typedResponse.Errors is not null && typedResponse.Errors.Length >= 1) @@ -63,17 +99,36 @@ public async Task CreateResponseApproval(T dataObject, Canc ); } - private async Task<(string Query, JsonElement Variables, string Response)> QueryAllMetaData(T dataObject, CancellationToken cancellationToken) + private async Task<(string Query, JsonElement Variables, string Response)> Query(T dataObject, CancellationToken cancellationToken) where T : IData { + logger.Query(dataObject.GetType(), dataObject.Id); return dataObject switch { - CalorimetricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.CalorimetricDataFileNames, appSettings, apiRequestService, cancellationToken), - GeometricData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.GeometricDataFileNames, appSettings, apiRequestService, cancellationToken), - HygrothermalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.HygrothermalDataFileNames, appSettings, apiRequestService, cancellationToken), - OpticalData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.OpticalDataFileNames, appSettings, apiRequestService, cancellationToken), - PhotovoltaicData data => await ApiRequests.QueryAllMetaData.Do(data.Id, ApiRequests.QueryAllMetaData.PhotovoltaicDataFileNames, appSettings, apiRequestService, cancellationToken), + CalorimetricData data => await Query(data.Id, s_calorimetricDataQueryFileNames, cancellationToken), + GeometricData data => await Query(data.Id, s_geometricDataQueryFileNames, cancellationToken), + HygrothermalData data => await Query(data.Id, s_hygrothermalDataQueryFileNames, cancellationToken), + OpticalData data => await Query(data.Id, s_opticalDataQueryFileNames, cancellationToken), + PhotovoltaicData data => await Query(data.Id, s_photovoltaicDataQueryFileNames, cancellationToken), _ => throw new ArgumentOutOfRangeException($"Unsupported data type {typeof(T)}"), }; } + + private async Task<(string Query, JsonElement Variables, string Response)> Query( + Guid dataId, + string[] queryFileNames, + CancellationToken cancellationToken + ) + { + var query = await GraphQlQueryHelpers.Construct(queryFileNames); + var variables = new Dictionary { ["id"] = dataId }; + var operationRequest = OperationRequestBuilder.New() + .SetDocument(query) + .SetVariableValues(variables) + .Build(); + var requestExecutor = await requestExecutorResolver.GetRequestExecutorAsync(cancellationToken: cancellationToken); + var executionResult = await requestExecutor.ExecuteAsync(operationRequest, cancellationToken); + var response = executionResult.ToJson(withIndentations: false); + return (query, JsonSerializer.SerializeToElement(variables), response); + } } \ No newline at end of file From 81dbfce81efd3c766d36916563283546b6cc4750 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 18:56:08 +0100 Subject: [PATCH 572/878] Use development `next-env.d.ts` file --- frontend/next-env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 19709046..7996d352 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/dev/types/routes.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. From 80f79c20938320b5c4497f326236a4c4132b5d27 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 19:00:12 +0100 Subject: [PATCH 573/878] Ignore generated files `*.generated.ts` and `next-env.d.ts --- frontend/.dockerignore | 7 +- frontend/.gitignore | 6 +- frontend/next-env.d.ts | 6 - frontend/queries/currentUser.generated.ts | 18 --- frontend/queries/data.generated.ts | 177 ---------------------- frontend/queries/databases.generated.ts | 23 --- 6 files changed, 6 insertions(+), 231 deletions(-) delete mode 100644 frontend/next-env.d.ts delete mode 100644 frontend/queries/currentUser.generated.ts delete mode 100644 frontend/queries/data.generated.ts delete mode 100644 frontend/queries/databases.generated.ts diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 14287850..92be1395 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,7 +1,6 @@ # https://docs.docker.com/engine/reference/builder/#dockerignore-file **/.* -!.graphql-let.yml !.env.local **/*.md **/*.bak @@ -19,6 +18,7 @@ coverage # next.js .next/ +next-env.d.ts out/ # production @@ -40,7 +40,6 @@ yarn-error.log* # vercel .vercel -# graphql-let +# graphql-codegen __generated__ -*.graphql.d.ts -*.graphqls.d.ts +*.generated.ts diff --git a/frontend/.gitignore b/frontend/.gitignore index a9737e41..c66fbf5f 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -13,6 +13,7 @@ # next.js /.next/ +/next-env.d.ts /out/ # production @@ -36,7 +37,6 @@ yarn-error.log* # vercel .vercel -# graphql-let +# graphql-codegen __generated__ -*.graphql.d.ts -*.graphqls.d.ts +*.generated.ts diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts deleted file mode 100644 index 7996d352..00000000 --- a/frontend/next-env.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/// -/// -import "./.next/dev/types/routes.d.ts"; - -// NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/frontend/queries/currentUser.generated.ts b/frontend/queries/currentUser.generated.ts deleted file mode 100644 index 08712ae9..00000000 --- a/frontend/queries/currentUser.generated.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type * as Types from '../__generated__/graphql'; - -import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -export type CurrentUserPartialFragment = { __typename: 'User', name: string }; - -export type CurrentUserQueryVariables = Types.Exact<{ [key: string]: never; }>; - - -export type CurrentUserQuery = { currentUser: { __typename: 'User', name: string } | null }; - -export type CurrentUserInfoQueryVariables = Types.Exact<{ [key: string]: never; }>; - - -export type CurrentUserInfoQuery = { currentUserInfo: { __typename: 'UserInfo', email: string, emailVerified: boolean, name: string, phoneNumber: string | null, phoneNumberVerified: boolean, roles: Array | null, sub: string, website: string | null, address: { __typename: 'Address', formatted: string } | null } }; - -export const CurrentUserPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CurrentUserPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode; -export const CurrentUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CurrentUserPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CurrentUserPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]} as unknown as DocumentNode; -export const CurrentUserInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CurrentUserInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentUserInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"address"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"formatted"}}]}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"emailVerified"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phoneNumber"}},{"kind":"Field","name":{"kind":"Name","value":"phoneNumberVerified"}},{"kind":"Field","name":{"kind":"Name","value":"roles"}},{"kind":"Field","name":{"kind":"Name","value":"sub"}},{"kind":"Field","name":{"kind":"Name","value":"website"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/queries/data.generated.ts b/frontend/queries/data.generated.ts deleted file mode 100644 index 367a0ffd..00000000 --- a/frontend/queries/data.generated.ts +++ /dev/null @@ -1,177 +0,0 @@ -import type * as Types from '../__generated__/graphql'; - -import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -export type DataPartial_CalorimetricData_Fragment = { __typename: 'CalorimetricData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type DataPartial_GeometricData_Fragment = { __typename: 'GeometricData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type DataPartial_HygrothermalData_Fragment = { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type DataPartial_OpticalData_Fragment = { __typename: 'OpticalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type DataPartial_PhotovoltaicData_Fragment = { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type DataPartialFragment = - | DataPartial_CalorimetricData_Fragment - | DataPartial_GeometricData_Fragment - | DataPartial_HygrothermalData_Fragment - | DataPartial_OpticalData_Fragment - | DataPartial_PhotovoltaicData_Fragment -; - -export type CalorimetricDataPartialFragment = { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type CalorimetricDataQueryVariables = Types.Exact<{ - uuid: Types.Scalars['Uuid']['input']; -}>; - - -export type CalorimetricDataQuery = { calorimetricData: { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; - -export type AllCalorimetricDataQueryVariables = Types.Exact<{ - where?: Types.InputMaybe; -}>; - - -export type AllCalorimetricDataQuery = { allCalorimetricData: { __typename: 'CalorimetricDataConnection', edges: Array<{ __typename: 'CalorimetricDataEdge', node: { __typename: 'CalorimetricData', gValues: Array, uValues: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; - -export type HygrothermalDataPartialFragment = { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type HygrothermalDataQueryVariables = Types.Exact<{ - uuid: Types.Scalars['Uuid']['input']; -}>; - - -export type HygrothermalDataQuery = { hygrothermalData: { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; - -export type AllHygrothermalDataQueryVariables = Types.Exact<{ - where?: Types.InputMaybe; -}>; - - -export type AllHygrothermalDataQuery = { allHygrothermalData: { __typename: 'HygrothermalDataConnection', edges: Array<{ __typename: 'HygrothermalDataEdge', node: { __typename: 'HygrothermalData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; - -export type OpticalDataPartialFragment = { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type OpticalDataQueryVariables = Types.Exact<{ - uuid: Types.Scalars['Uuid']['input']; -}>; - - -export type OpticalDataQuery = { opticalData: { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; - -export type AllOpticalDataQueryVariables = Types.Exact<{ - where?: Types.InputMaybe; -}>; - - -export type AllOpticalDataQuery = { allOpticalData: { __typename: 'OpticalDataConnection', edges: Array<{ __typename: 'OpticalDataEdge', node: { __typename: 'OpticalData', infraredEmittances: Array, nearnormalHemisphericalSolarReflectances: Array, nearnormalHemisphericalSolarTransmittances: Array, nearnormalHemisphericalVisibleReflectances: Array, nearnormalHemisphericalVisibleTransmittances: Array, colorRenderingIndices: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; - -export type PhotovoltaicDataPartialFragment = { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type PhotovoltaicDataQueryVariables = Types.Exact<{ - uuid: Types.Scalars['Uuid']['input']; -}>; - - -export type PhotovoltaicDataQuery = { photovoltaicData: { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; - -export type AllPhotovoltaicDataQueryVariables = Types.Exact<{ - where?: Types.InputMaybe; -}>; - - -export type AllPhotovoltaicDataQuery = { allPhotovoltaicData: { __typename: 'PhotovoltaicDataConnection', edges: Array<{ __typename: 'PhotovoltaicDataEdge', node: { __typename: 'PhotovoltaicData', uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; - -export type GeometricDataPartialFragment = { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> }; - -export type GeometricDataQueryVariables = Types.Exact<{ - uuid: Types.Scalars['Uuid']['input']; -}>; - - -export type GeometricDataQuery = { geometricData: { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } | null }; - -export type AllGeometricDataQueryVariables = Types.Exact<{ - where?: Types.InputMaybe; -}>; - - -export type AllGeometricDataQuery = { allGeometricData: { __typename: 'GeometricDataConnection', edges: Array<{ __typename: 'GeometricDataEdge', node: { __typename: 'GeometricData', thicknesses: Array, uuid: string, timestamp: string, componentId: string, name: string | null, description: string | null, createdAt: string, creatorId: string, locale: string, warnings: Array, userId: string | null, appliedMethod: { __typename: 'AppliedMethod', methodId: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }>, sources: Array<{ __typename: 'NamedMethodSource', name: string, value: { __typename: 'CrossDatabaseDataReference', dataId: string, dataKind: Types.DataKind, dataTimestamp: string, databaseId: string } }> }, resourceTree: { __typename: 'GetHttpsResourceTree', root: { __typename: 'GetHttpsResourceTreeRoot', value: { __typename: 'GetHttpsResource', uuid: string, description: string | null, hashValue: string, locator: string, dataFormatId: string } }, nonRootVertices: Array<{ __typename: 'GetHttpsResourceTreeNonRootVertex', parentId: string | number, vertexId: string | number, appliedConversionMethod: { __typename: 'ToTreeVertexAppliedConversionMethod', methodId: string, sourceName: string, arguments: Array<{ __typename: 'NamedMethodArgument', name: string, value: unknown }> }, value: { __typename: 'GetHttpsResource', description: string | null, dataFormatId: string, hashValue: string, id: string | number, locator: string, uuid: string, archivedFilesMetaInformation: Array<{ __typename: 'FileMetaInformation', dataFormatId: string, path: Array }> } }> }, approvals: Array<{ __typename: 'DataApproval', timestamp: string, signature: string, keyFingerprint: string, query: string, variables: unknown, message: string, approverId: string, statement: - | { __typename: 'Publication', abstract: string | null, arXiv: string | null, authors: Array | null, doi: string | null, section: string | null, title: string | null, urn: string | null, webAddress: string | null } - | { __typename: 'Standard', abstract: string | null, locator: string | null, section: string | null, standardizers: Array, title: string | null, year: number | null, numeration: { __typename: 'Numeration', mainNumber: string, prefix: string | null, suffix: string | null } } - | null }>, resources: Array<{ __typename: 'GetHttpsResource', description: string | null, hashValue: string, locator: string, dataFormatId: string }> } }> | null } | null }; - -export const DataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const CalorimetricDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const HygrothermalDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const OpticalDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const PhotovoltaicDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const GeometricDataPartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}}]} as unknown as DocumentNode; -export const CalorimetricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"CalorimetricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"calorimetricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalorimetricDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}}]} as unknown as DocumentNode; -export const AllCalorimetricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllCalorimetricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allCalorimetricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CalorimetricDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CalorimetricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"CalorimetricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"gValues"}},{"kind":"Field","name":{"kind":"Name","value":"uValues"}}]}}]} as unknown as DocumentNode; -export const HygrothermalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"HygrothermalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hygrothermalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"HygrothermalDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; -export const AllHygrothermalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllHygrothermalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allHygrothermalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"HygrothermalDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"HygrothermalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"HygrothermalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; -export const OpticalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"OpticalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"opticalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OpticalDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}}]} as unknown as DocumentNode; -export const AllOpticalDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllOpticalData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allOpticalData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OpticalDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OpticalDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"OpticalData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"infraredEmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalSolarTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleReflectances"}},{"kind":"Field","name":{"kind":"Name","value":"nearnormalHemisphericalVisibleTransmittances"}},{"kind":"Field","name":{"kind":"Name","value":"colorRenderingIndices"}}]}}]} as unknown as DocumentNode; -export const PhotovoltaicDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"PhotovoltaicData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"photovoltaicData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PhotovoltaicDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; -export const AllPhotovoltaicDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllPhotovoltaicData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allPhotovoltaicData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PhotovoltaicDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PhotovoltaicDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PhotovoltaicData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}}]}}]} as unknown as DocumentNode; -export const GeometricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GeometricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"geometricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"uuid"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"GeometricDataPartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}}]} as unknown as DocumentNode; -export const AllGeometricDataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AllGeometricData"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"where"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricDataPropositionInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"allGeometricData"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"where"},"value":{"kind":"Variable","name":{"kind":"Name","value":"where"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"GeometricDataPartial"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Data"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"componentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"appliedMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataId"}},{"kind":"Field","name":{"kind":"Name","value":"dataKind"}},{"kind":"Field","name":{"kind":"Name","value":"dataTimestamp"}},{"kind":"Field","name":{"kind":"Name","value":"databaseId"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"resourceTree"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"root"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"nonRootVertices"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appliedConversionMethod"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"arguments"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"value"}}]}},{"kind":"Field","name":{"kind":"Name","value":"methodId"}},{"kind":"Field","name":{"kind":"Name","value":"sourceName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"parentId"}},{"kind":"Field","name":{"kind":"Name","value":"value"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"archivedFilesMetaInformation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"uuid"}}]}},{"kind":"Field","name":{"kind":"Name","value":"vertexId"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approvals"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"timestamp"}},{"kind":"Field","name":{"kind":"Name","value":"signature"}},{"kind":"Field","name":{"kind":"Name","value":"keyFingerprint"}},{"kind":"Field","name":{"kind":"Name","value":"query"}},{"kind":"Field","name":{"kind":"Name","value":"variables"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"statement"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Publication"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"arXiv"}},{"kind":"Field","name":{"kind":"Name","value":"authors"}},{"kind":"Field","name":{"kind":"Name","value":"doi"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"urn"}},{"kind":"Field","name":{"kind":"Name","value":"webAddress"}}]}},{"kind":"InlineFragment","typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Standard"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"abstract"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"section"}},{"kind":"Field","name":{"kind":"Name","value":"standardizers"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"year"}},{"kind":"Field","name":{"kind":"Name","value":"numeration"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mainNumber"}},{"kind":"Field","name":{"kind":"Name","value":"prefix"}},{"kind":"Field","name":{"kind":"Name","value":"suffix"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"approverId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"creatorId"}},{"kind":"Field","name":{"kind":"Name","value":"locale"}},{"kind":"Field","name":{"kind":"Name","value":"resources"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"hashValue"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"dataFormatId"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warnings"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"GeometricDataPartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"GeometricData"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DataPartial"}},{"kind":"Field","name":{"kind":"Name","value":"thicknesses"}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/queries/databases.generated.ts b/frontend/queries/databases.generated.ts deleted file mode 100644 index 05fa972c..00000000 --- a/frontend/queries/databases.generated.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type * as Types from '../__generated__/graphql'; - -import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; -export type DatabasePartialFragment = { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean }; - -export type DatabaseQueryVariables = Types.Exact<{ [key: string]: never; }>; - - -export type DatabaseQuery = { database: { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean } }; - -export type UpdateDatabaseMutationVariables = Types.Exact<{ - databaseId: Types.Scalars['Uuid']['input']; - name: Types.Scalars['String']['input']; - description: Types.Scalars['String']['input']; - locator: Types.Scalars['Url']['input']; -}>; - - -export type UpdateDatabaseMutation = { updateDatabase: { __typename: 'UpdateDatabasePayload', database: { __typename: 'Database', uuid: string, name: string, description: string, locator: string, verificationState: Types.DatabaseVerificationState, verificationCode: string, isAuthorizedToUpdateNode: boolean, isAuthorizedToVerifyNode: boolean } | null, errors: Array<{ __typename: 'UpdateDatabaseError', code: Types.UpdateDatabaseErrorCode, message: string, path: Array }> | null } }; - -export const DatabasePartialFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; -export const DatabaseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DatabasePartial"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; -export const UpdateDatabaseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateDatabase"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"databaseId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Uuid"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"description"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"locator"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Url"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateDatabase"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"databaseId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"databaseId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"description"},"value":{"kind":"Variable","name":{"kind":"Name","value":"description"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"locator"},"value":{"kind":"Variable","name":{"kind":"Name","value":"locator"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"database"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DatabasePartial"}}]}},{"kind":"Field","name":{"kind":"Name","value":"errors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"message"}},{"kind":"Field","name":{"kind":"Name","value":"path"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DatabasePartial"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Database"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"uuid"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"locator"}},{"kind":"Field","name":{"kind":"Name","value":"verificationState"}},{"kind":"Field","name":{"kind":"Name","value":"verificationCode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToUpdateNode"}},{"kind":"Field","name":{"kind":"Name","value":"isAuthorizedToVerifyNode"}}]}}]} as unknown as DocumentNode; \ No newline at end of file From d3a002c0c6faa3342f41ca6419a6d93e55a3fe64 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 20:19:19 +0100 Subject: [PATCH 574/878] Recreate response approval after file upload (because the hash value and mirrored values may have changed) --- .../src/Controllers/FileUploadController.cs | 23 +++++++++++++++++-- .../src/Services/ResponseApprovalService.cs | 8 +++---- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 0035b1da..17d35e96 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -32,6 +32,16 @@ public static partial void SavedUploadedFile( string? fileNameForDisplay, string filePath ); + + [LoggerMessage( + EventId = 1, + Level = LogLevel.Error, + Message = "Failed to create response approval for data with ID {DataId}.")] + public static partial void FailedToCreateResponseApproval( + this ILogger logger, + Guid dataId, + Exception exception + ); } // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming @@ -46,7 +56,6 @@ ILogger logger // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions s_defaultFormOptions = new(); - private readonly ILogger _logger = logger; private readonly string[] _permittedExtensions = [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; @@ -90,6 +99,7 @@ protected override void Dispose(bool disposing) public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, [FromServices] ApplicationDbContext context, + [FromServices] ResponseApprovalService responseApprovalService, [FromServices] CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -165,7 +175,7 @@ CancellationToken cancellationToken await targetStream.WriteAsync(streamedFileContent, cancellationToken); // Don't trust the file name sent by the client. To display the // file name, HTML-encode the value. - _logger.SavedUploadedFile( + logger.SavedUploadedFile( WebUtility.HtmlEncode(contentDisposition.FileName.Value), getHttpsResource.FilePath ); @@ -184,6 +194,15 @@ await getHttpsResource.OpticalData.ExtractAndSetMirroredValuesFromFile( } await getHttpsResource.RecomputeHashValue(cancellationToken); await context.SaveChangesAsync(cancellationToken); + try + { + getHttpsResource.Data.Approval = await responseApprovalService.CreateResponseApproval(getHttpsResource.Data, cancellationToken); + await context.SaveChangesAsync(cancellationToken); + } + catch (Exception exception) + { + logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, exception); + } return Created(nameof(FileUploadController), null); } diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index fefad906..16cc22cd 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -76,8 +76,7 @@ ILogger logger /// /// Thows exception, when singing of data failed. /// - public async Task CreateResponseApproval(T dataObject, CancellationToken cancellationToken) - where T : IData + public async Task CreateResponseApproval(IData dataObject, CancellationToken cancellationToken) { var (query, variables, response) = await Query(dataObject, cancellationToken); logger.QueryAndVariablesAndResponse(query, variables, response); @@ -99,8 +98,7 @@ public async Task CreateResponseApproval(T dataObject, Canc ); } - private async Task<(string Query, JsonElement Variables, string Response)> Query(T dataObject, CancellationToken cancellationToken) - where T : IData + private async Task<(string Query, JsonElement Variables, string Response)> Query(IData dataObject, CancellationToken cancellationToken) { logger.Query(dataObject.GetType(), dataObject.Id); return dataObject switch @@ -110,7 +108,7 @@ public async Task CreateResponseApproval(T dataObject, Canc HygrothermalData data => await Query(data.Id, s_hygrothermalDataQueryFileNames, cancellationToken), OpticalData data => await Query(data.Id, s_opticalDataQueryFileNames, cancellationToken), PhotovoltaicData data => await Query(data.Id, s_photovoltaicDataQueryFileNames, cancellationToken), - _ => throw new ArgumentOutOfRangeException($"Unsupported data type {typeof(T)}"), + _ => throw new ArgumentOutOfRangeException($"Unsupported data type {dataObject.GetType()}"), }; } From 29ff595cb4808479331405358d67c174014a74a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 20:19:59 +0100 Subject: [PATCH 575/878] Adjust rate limiting to work properly with latest version of Next.JS (bursts needed a significant increase) --- see https://blog.nginx.org/blog/rate-limiting-nginx --- .../development/default.conf.template | 30 ++++++++++++------- .../production/default.conf.template | 28 ++++++++++------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index 4e77c310..d84d2cad 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -21,10 +21,19 @@ proxy_busy_buffers_size 256k; add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking -limit_req_zone $binary_remote_addr zone=one:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=graphql:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=connect:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=email:10m rate=30r/s; + server_tokens off; sendfile on; +# sendfile_max_chunk 1m; keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case. client_body_timeout 10; client_header_timeout 10; @@ -87,27 +96,27 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_cache_bypass $http_upgrade; - limit_req zone=one burst=30 nodelay; + limit_req zone=frontend burst=100 nodelay; } location /graphql { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=graphql burst=12 delay=6; } location /connect { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=connect burst=6 nodelay; } location /antiforgery { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=antiforgery burst=2 nodelay; } location /api { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=api burst=6 nodelay; client_max_body_size 10240m; } @@ -123,24 +132,25 @@ server { location /openapi { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=openapi burst=10 nodelay; } location /health { proxy_pass http://backend; - limit_req zone=one burst=20 nodelay; + limit_req zone=health burst=2 nodelay; } location /email { proxy_pass http://email; - limit_req zone=one burst=20 nodelay; + limit_req zone=email burst=20 nodelay; } location /email/hubs { proxy_pass http://email; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - limit_req zone=one burst=20 nodelay; + proxy_cache_bypass $http_upgrade; + limit_req zone=email burst=20 nodelay; } error_page 503 @maintenance; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index 6394d611..c6b8b98e 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -21,7 +21,15 @@ proxy_busy_buffers_size 256k; add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking -limit_req_zone $binary_remote_addr zone=one:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=graphql:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=connect:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; +limit_req_zone $binary_remote_addr zone=email:10m rate=30r/s; + server_tokens off; sendfile on; @@ -71,27 +79,27 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_cache_bypass $http_upgrade; - limit_req zone=one burst=30 nodelay; + limit_req zone=frontend burst=100 nodelay; } location /graphql { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=graphql burst=12 delay=6; } location /connect { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=connect burst=6 nodelay; } location /antiforgery { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=antiforgery burst=2 nodelay; } location /api { proxy_pass http://backend; - limit_req zone=one burst=10 nodelay; + limit_req zone=api burst=6 nodelay; client_max_body_size 10240m; } @@ -107,17 +115,17 @@ server { location /openapi { proxy_pass http://backend; - limit_req zone=one burst=20 nodelay; + limit_req zone=openapi burst=10 nodelay; } location /health { proxy_pass http://backend; - limit_req zone=one burst=20 nodelay; + limit_req zone=health burst=2 nodelay; } location /email { proxy_pass http://email; - limit_req zone=one burst=20 nodelay; + limit_req zone=email burst=20 nodelay; } location /email/hubs { @@ -125,7 +133,7 @@ server { proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_cache_bypass $http_upgrade; - limit_req zone=one burst=20 nodelay; + limit_req zone=email burst=20 nodelay; } error_page 503 @maintenance; From 4fba8d57dd210025574ef77f3a0424335ec356b1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 20:48:50 +0100 Subject: [PATCH 576/878] Build backend and frontend sequentially in production to avoid resource exhaustion --- alternatively we could configure max-parallelism as explained in https://docs.docker.com/build/buildkit/configure/#max-parallelism --- Makefile.production | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile.production b/Makefile.production index ba18eb0e..abc40899 100644 --- a/Makefile.production +++ b/Makefile.production @@ -60,7 +60,15 @@ build : check pull ## Build images COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ - --pull + --pull \ + backend + # --no-cache + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull \ + frontend # --no-cache .PHONY : build From f246d0fc0e4071cfb98c6cf76cb33a3620702113 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 20:50:11 +0100 Subject: [PATCH 577/878] Add a nice-to-have todo --- backend/src/Controllers/FileUploadController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 17d35e96..c09e680d 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -201,6 +201,7 @@ await getHttpsResource.OpticalData.ExtractAndSetMirroredValuesFromFile( } catch (Exception exception) { + // TODO How can this be reported to the user? logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, exception); } return Created(nameof(FileUploadController), null); From 1dec4e555b25401c27f2770d054de46fae5bb4cb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 21:06:05 +0100 Subject: [PATCH 578/878] Correct file name of next.js config --- README.md | 2 +- frontend/Dockerfile-production | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04a9abdf..64d29670 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ that the log files grew huge. You can delete them by running When the `frontend` Docker image does not build in production because of an unused import in an automatically generated file, for example, one in the directory `./frontend/__generated__`, then **temporarily** ignore TypeScript -build errors by adding the following lines to `./frontend/next.config.js`, for +build errors by adding the following lines to `./frontend/next.config.ts`, for example with `vi` or `nano` in a shell on the deployment machine: ``` diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index c9869d93..952231d4 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -36,7 +36,7 @@ WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ COPY --from=build /app/.next ./.next -COPY --from=build /app/next.config.js ./ +COPY --from=build /app/next.config.ts ./ COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/package.json ./ COPY --from=build /app/public ./public From 858114776d76d8957fb4847d3a390ac1a308aaa7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 20 Nov 2025 21:23:59 +0100 Subject: [PATCH 579/878] Do not include GPG keys in Docker context --- backend/.dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/.dockerignore b/backend/.dockerignore index f12b3ac4..b8c6308a 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -3,6 +3,7 @@ **/.* **/*.md **/*.bak +**/*.gpg Dockerfile* **/core From f6f9f72bd67556478c1a9467bf30d94988fb0b37 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 21 Nov 2025 18:35:18 +0100 Subject: [PATCH 580/878] Validate cross-database data references --- backend/src/ApiRequests/DataDataLoader.cs | 77 +++ backend/src/ApiRequests/Queries/Data.graphql | 11 + .../CreateCalorimetricDataMutation.cs | 6 + .../GraphQl/DataX/CreateDataMutationBase.cs | 53 +- .../CreateGeometricDataMutation.cs | 6 + .../CreateGetHttpsResourceMutation.cs | 12 + .../CreateHygrothermalDataMutation.cs | 6 + .../OpticalDataX/CreateOpticalDataMutation.cs | 6 + .../CreatePhotovoltaicDataMutation.cs | 6 + .../GraphQlSchemaTests.IsUnchanged.snap | 58 +- frontend/type-defs.graphqls | 524 ++++-------------- 11 files changed, 337 insertions(+), 428 deletions(-) create mode 100644 backend/src/ApiRequests/DataDataLoader.cs create mode 100644 backend/src/ApiRequests/Queries/Data.graphql diff --git a/backend/src/ApiRequests/DataDataLoader.cs b/backend/src/ApiRequests/DataDataLoader.cs new file mode 100644 index 00000000..48875699 --- /dev/null +++ b/backend/src/ApiRequests/DataDataLoader.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Enumerations; +using Database.Services; +using GraphQL; +using GreenDonut; + +namespace Database.ApiRequests; + +public static class DataDataLoader +{ + private const string QueryFileName = "Data.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + appSettings.MetabaseGraphQlEndpoint; + + public sealed record Data( + Guid Uuid, + DataKind Kind + ); + + public sealed record Database( + Guid Uuid, + Data? Data + ); + + private sealed record DataData( + Database? Database + ); + + [DataLoader] + public static async Task> GetDataByDatabaseAndIdAndKindAsync( + IReadOnlyList<(Guid DatabaseId, Guid DataId, DataKind DataKind)> databaseIdsAndDataIdsAndDataKinds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + var keysAndDatabases = await Task.WhenAll( + databaseIdsAndDataIdsAndDataKinds + .Select(async databaseIdAndDataIdAndDataKind => + ( + Key: databaseIdAndDataIdAndDataKind, + Database: (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint(appSettings), + new GraphQLRequest( + await GraphQlQueryHelpers.Construct(QueryFileName), + new + { + databaseIdAndDataIdAndDataKind.DatabaseId, + databaseIdAndDataIdAndDataKind.DataId, + databaseIdAndDataIdAndDataKind.DataKind + }, + null + ), + cancellationToken + )) + .Data + ?.Database + ) + ) + ); + return keysAndDatabases + .Where(_ => _.Database is not null) + .ToDictionary( + _ => ( + _.Key.DatabaseId, + _.Key.DataId, + _.Key.DataKind + ), + _ => _.Database! + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Data.graphql b/backend/src/ApiRequests/Queries/Data.graphql new file mode 100644 index 00000000..404bdabb --- /dev/null +++ b/backend/src/ApiRequests/Queries/Data.graphql @@ -0,0 +1,11 @@ +query Data($databaseId: Uuid!, $dataId: Uuid!, $dataKind: DataKind!) { + database(id: $databaseId) + { + uuid + data(id: $dataId, kind: $dataKind) + { + uuid + kind + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index b8b89f8f..b8402198 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -64,6 +64,8 @@ public enum CreateCalorimetricDataErrorCode ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -103,6 +105,7 @@ public async Task CreateCalorimetricDataAsync( IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken @@ -129,6 +132,9 @@ CancellationToken cancellationToken CreateCalorimetricDataErrorCode.UNKNOWN_CREATOR, methodByIdDataLoader, CreateCalorimetricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateCalorimetricDataErrorCode.UNKNOWN_DATABASE, + CreateCalorimetricDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateCalorimetricDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 0f90be2f..c7b6f71d 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -6,6 +6,8 @@ using GraphQL.Client.Abstractions.Utilities; using Database.Extensions; using static Database.ApiRequests.DataFormatDataLoader; +using System.Linq; +using System.Globalization; namespace Database.GraphQl.DataX; @@ -33,6 +35,9 @@ protected async Task> ValidateAsync( TErrorCode unknownCreatorErrorCode, IMethodByIdDataLoader methodByIdDataLoader, TErrorCode unknownAppliedMethodErrorCode, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + TErrorCode unknownDatabase, + TErrorCode unknownCrossDatabaseData, IDataFormatByIdDataLoader dataFormatByIdDataLoader, TErrorCode unknownDataFormatErrorCode, CancellationToken cancellationToken @@ -74,12 +79,56 @@ CancellationToken cancellationToken errors.Add( NewError( unknownAppliedMethodErrorCode, - "The applied method does not exist", + "The applied method does not exist.", [nameof(input), nameof(input.AppliedMethod).ToLowerFirst(), nameof(input.AppliedMethod.MethodId).ToLowerFirst()] ) ); } - // TODO check applied method sources + // TODO check applied method argument and source names + var databases = await Task.WhenAll( + input.AppliedMethod.Sources.Select(source => + dataByDatabaseAndIdAndKindDataLoader.LoadAsync( + (source.Value.DatabaseId, source.Value.DataId, source.Value.DataKind), + cancellationToken + ) + ) + ); + foreach (var (database, index) in databases.WithIndex()) + { + string[] valuePath = [ + nameof(input), + nameof(input.AppliedMethod).ToLowerFirst(), + nameof(input.AppliedMethod.Sources).ToLowerFirst(), + index.ToString(CultureInfo.InvariantCulture), + nameof(NamedMethodSourceInput.Value) + ]; + if (database is null) + { + errors.Add( + NewError( + unknownDatabase, + "The database does not exist.", + [ + ..valuePath, + nameof(NamedMethodSourceInput.Value.DatabaseId), + ] + ) + ); + } + else if (database.Data is null) + { + errors.Add( + NewError( + unknownCrossDatabaseData, + "The data does not exist with the given kind.", + [ + ..valuePath, + nameof(NamedMethodSourceInput.Value.DataId), + ] + ) + ); + } + } var validateRootResourceResult = await ValidateGetHttpsResourceAsync( input.RootResource, [nameof(input), nameof(input.RootResource)], diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 2f7cdd25..f3c30df3 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -61,6 +61,8 @@ public enum CreateGeometricDataErrorCode ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -101,6 +103,7 @@ public async Task CreateGeometricDataAsync( IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken @@ -127,6 +130,9 @@ CancellationToken cancellationToken CreateGeometricDataErrorCode.UNKNOWN_CREATOR, methodByIdDataLoader, CreateGeometricDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateGeometricDataErrorCode.UNKNOWN_DATABASE, + CreateGeometricDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateGeometricDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index fd8178f4..12489766 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -58,6 +58,7 @@ public enum CreateGetHttpsResourceErrorCode UNKNOWN_PARENT, ILLEGAL_PARENT, UNKNOWN_DATA_FORMAT, + UNKNOWN_METHOD, CREATING_RESPONSE_APPROVAL_FAILED } @@ -102,6 +103,7 @@ public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, IDataFormatByIdDataLoader dataFormatByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, ResponseApprovalService responseApprovalService, CommonAuthorization authorization, CancellationToken cancellationToken @@ -169,6 +171,16 @@ await context.GetHttpsResources { errors.AddRange(validateResourceErrors); } + if (await methodByIdDataLoader.LoadAsync(input.AppliedConversionMethod.MethodId, cancellationToken) is null) + { + errors.Add( + NewError( + CreateGetHttpsResourceErrorCode.UNKNOWN_METHOD, + "The applied conversion method does not exist", + [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst(), nameof(input.AppliedConversionMethod.MethodId).ToLowerFirst()] + ) + ); + } // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. if (errors.Count >= 1 || dataFormat is null) { diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index f2fa8942..4668cc71 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -58,6 +58,8 @@ public enum CreateHygrothermalDataErrorCode ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -97,6 +99,7 @@ public async Task CreateHygrothermalDataAsync( IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken @@ -123,6 +126,9 @@ CancellationToken cancellationToken CreateHygrothermalDataErrorCode.UNKNOWN_CREATOR, methodByIdDataLoader, CreateHygrothermalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateHygrothermalDataErrorCode.UNKNOWN_DATABASE, + CreateHygrothermalDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateHygrothermalDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index b4b46ee8..26ace80f 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -80,6 +80,8 @@ public enum CreateOpticalDataErrorCode ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -119,6 +121,7 @@ public async Task CreateOpticalDataAsync( IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken @@ -145,6 +148,9 @@ CancellationToken cancellationToken CreateOpticalDataErrorCode.UNKNOWN_CREATOR, methodByIdDataLoader, CreateOpticalDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateOpticalDataErrorCode.UNKNOWN_DATABASE, + CreateOpticalDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateOpticalDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 78716be2..d9a7476e 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -60,6 +60,8 @@ public enum CreatePhotovoltaicDataErrorCode ILLEGAL_CREATED_AT, UNKNOWN_CREATOR, UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, UNKNOWN_DATA_FORMAT, CREATING_RESPONSE_APPROVAL_FAILED } @@ -99,6 +101,7 @@ public async Task CreatePhotovoltaicDataAsync( IComponentByIdDataLoader componentByIdDataLoader, IInstitutionByIdDataLoader institutionByIdDataLoader, IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, CancellationToken cancellationToken @@ -125,6 +128,9 @@ CancellationToken cancellationToken CreatePhotovoltaicDataErrorCode.UNKNOWN_CREATOR, methodByIdDataLoader, CreatePhotovoltaicDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATABASE, + CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA_FORMAT, cancellationToken diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index f7f6611b..11e404c9 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -881,6 +881,8 @@ input BooleanPropositionInput { input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -891,12 +893,12 @@ input CalorimetricDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput gValues: FloatsPropositionInput uValues: FloatsPropositionInput } input CalorimetricDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -904,7 +906,6 @@ input CalorimetricDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input CielabColorInput { @@ -1011,6 +1012,8 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1055,6 +1058,8 @@ input DataApprovalPropositionInput { input DataFilterInput { and: [DataFilterInput!] or: [DataFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1075,6 +1080,7 @@ input DataKindPropositionInput { } input DataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1145,6 +1151,8 @@ input FloatsPropositionInput { input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] or: [GeometricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1155,11 +1163,11 @@ input GeometricDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput thicknesses: FloatsPropositionInput } input GeometricDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1167,7 +1175,6 @@ input GeometricDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input GetHttpsResourcePropositionInput { @@ -1220,6 +1227,8 @@ input GetHttpsResourceTreeNonRootVertexFilterInput { input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1230,10 +1239,10 @@ input HygrothermalDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput } input HygrothermalDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1241,7 +1250,6 @@ input HygrothermalDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input Int32PropositionInput { @@ -1403,6 +1411,21 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } +input NullableOfUuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + input NumerationInput { mainNumber: String! prefix: String @@ -1426,6 +1449,8 @@ input NumerationSortInput { input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1436,7 +1461,6 @@ input OpticalDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput type: NullableOfOpticalComponentTypePropositionInput subtype: NullableOfOpticalComponentSubtypePropositionInput coatedSide: NullableOfCoatedSidePropositionInput @@ -1450,6 +1474,7 @@ input OpticalDataPropositionInput { } input OpticalDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1457,12 +1482,13 @@ input OpticalDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] or: [PhotovoltaicDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1473,10 +1499,10 @@ input PhotovoltaicDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput } input PhotovoltaicDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1484,7 +1510,6 @@ input PhotovoltaicDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input PublicationInput { @@ -1709,6 +1734,8 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1857,6 +1884,8 @@ enum CreateCalorimetricDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1869,6 +1898,8 @@ enum CreateGeometricDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1881,6 +1912,7 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_PARENT ILLEGAL_PARENT UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD CREATING_RESPONSE_APPROVAL_FAILED } @@ -1892,6 +1924,8 @@ enum CreateHygrothermalDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1904,6 +1938,8 @@ enum CreateOpticalDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -1916,6 +1952,8 @@ enum CreatePhotovoltaicDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 121859a2..11e404c9 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -25,10 +25,7 @@ interface Data { description: String isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -113,16 +110,10 @@ type CalorimetricData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -337,16 +328,10 @@ type GeometricData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! @@ -409,10 +394,7 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices( - order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") - where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - ): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -441,16 +423,10 @@ type HygrothermalData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -499,55 +475,26 @@ type KeyValuePairOfGuidAndNullableOfUInt32 { } type Mutation { - addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! - @cost(weight: "10") - addInstitutionAccessRights( - input: AddInstitutionAccessRightsInput! - ): AddInstitutionAccessRightsPayload! @cost(weight: "10") - createCalorimetricData( - input: CreateCalorimetricDataInput! - ): CreateCalorimetricDataPayload! @cost(weight: "10") - createGeometricData( - input: CreateGeometricDataInput! - ): CreateGeometricDataPayload! @cost(weight: "10") - createGetHttpsResource( - input: CreateGetHttpsResourceInput! - ): CreateGetHttpsResourcePayload! @cost(weight: "10") - createHygrothermalData( - input: CreateHygrothermalDataInput! - ): CreateHygrothermalDataPayload! @cost(weight: "10") - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! - @cost(weight: "10") - createPhotovoltaicData( - input: CreatePhotovoltaicDataInput! - ): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals( - where: CreateResponseApprovalsFilterInput @cost(weight: "10") - ): CreateResponseApprovalsPayload! @cost(weight: "10") + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteGetHttpsResource( - input: DeleteGetHttpsResourceInput! - ): DeleteGetHttpsResourcePayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues( - where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10") - ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval( - input: RemoveDataApprovalInput! - ): RemoveDataApprovalPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights( - input: UpdateDataAccessRightsInput! - ): UpdateDataAccessRightsPayload! @cost(weight: "10") - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! - @cost(weight: "10") - updateInstitutionAccessRights( - input: UpdateInstitutionAccessRightsInput! - ): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals( - where: UpdateResponseApprovalsFilterInput @cost(weight: "10") - ): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") } type NamedMethodArgument { @@ -588,20 +535,14 @@ type OpticalData implements Node & Data { infraredEmittances: [Float!]! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! @@ -654,16 +595,10 @@ type PhotovoltaicData implements Node & Data { id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! locale: Locale! name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid @@ -718,301 +653,39 @@ type PublishDataPayload { } type Query { - allCalorimetricData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [CalorimetricDataSortInput!] @cost(weight: "10") - where: CalorimetricDataPropositionInput @cost(weight: "10") - ): CalorimetricDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allGeometricData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [GeometricDataSortInput!] @cost(weight: "10") - where: GeometricDataPropositionInput @cost(weight: "10") - ): GeometricDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allHygrothermalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [HygrothermalDataSortInput!] @cost(weight: "10") - where: HygrothermalDataPropositionInput @cost(weight: "10") - ): HygrothermalDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allOpticalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [OpticalDataSortInput!] @cost(weight: "10") - where: OpticalDataPropositionInput @cost(weight: "10") - ): OpticalDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPendingCalorimetricData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [CalorimetricDataSortInput!] @cost(weight: "10") - where: CalorimetricDataPropositionInput @cost(weight: "10") - ): CalorimetricDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPendingGeometricData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [GeometricDataSortInput!] @cost(weight: "10") - where: GeometricDataPropositionInput @cost(weight: "10") - ): GeometricDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPendingHygrothermalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [HygrothermalDataSortInput!] @cost(weight: "10") - where: HygrothermalDataPropositionInput @cost(weight: "10") - ): HygrothermalDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPendingOpticalData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [OpticalDataSortInput!] @cost(weight: "10") - where: OpticalDataPropositionInput @cost(weight: "10") - ): OpticalDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPendingPhotovoltaicData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [PhotovoltaicDataSortInput!] @cost(weight: "10") - where: PhotovoltaicDataPropositionInput @cost(weight: "10") - ): PhotovoltaicDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - allPhotovoltaicData( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - locale: Locale - order: [PhotovoltaicDataSortInput!] @cost(weight: "10") - where: PhotovoltaicDataPropositionInput @cost(weight: "10") - ): PhotovoltaicDataConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - calculateMethod( - dataReference: CrossDatabaseDataReferenceInput! - methodId: Uuid! - ): CalculateMethodPayload! @cost(weight: "10") - calculateMethodWithDataUpload( - data: Upload! - methodId: Uuid! - ): CalculateMethodPayload! @cost(weight: "10") - calorimetricData(id: Uuid!, locale: Locale): CalorimetricData - @cost(weight: "10") + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") - data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") + data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") - geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") + geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): GetHttpsResourceConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - hasCalorimetricData( - locale: Locale - where: CalorimetricDataPropositionInput @cost(weight: "10") - ): Boolean! @cost(weight: "10") - hasGeometricData( - locale: Locale - where: GeometricDataPropositionInput @cost(weight: "10") - ): Boolean! @cost(weight: "10") - hasHygrothermalData( - locale: Locale - where: HygrothermalDataPropositionInput @cost(weight: "10") - ): Boolean! @cost(weight: "10") - hasOpticalData( - locale: Locale - where: OpticalDataPropositionInput @cost(weight: "10") - ): Boolean! @cost(weight: "10") - hasPhotovoltaicData( - locale: Locale - where: PhotovoltaicDataPropositionInput @cost(weight: "10") - ): Boolean! @cost(weight: "10") - hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData - @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") - pendingGetHttpsResources( - "Returns the elements in the list that come after the specified cursor." - after: String - "Returns the elements in the list that come before the specified cursor." - before: String - "Returns the first _n_ elements from the list." - first: Int - "Returns the last _n_ elements from the list." - last: Int - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): GetHttpsResourceConnection - @listSize( - assumedSize: 100 - slicingArguments: ["first", "last"] - slicingArgumentDefaultValue: 100 - sizedFields: ["edges", "nodes"] - requireOneSlicingArgument: false - ) - @cost(weight: "10") - photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData - @cost(weight: "10") + opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") verificationCode: String! } @@ -1208,6 +881,8 @@ input BooleanPropositionInput { input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1218,12 +893,12 @@ input CalorimetricDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput gValues: FloatsPropositionInput uValues: FloatsPropositionInput } input CalorimetricDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1231,7 +906,6 @@ input CalorimetricDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input CielabColorInput { @@ -1338,6 +1012,8 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsFilterInput { and: [CreateResponseApprovalsFilterInput!] or: [CreateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1382,6 +1058,8 @@ input DataApprovalPropositionInput { input DataFilterInput { and: [DataFilterInput!] or: [DataFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1402,6 +1080,7 @@ input DataKindPropositionInput { } input DataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1472,6 +1151,8 @@ input FloatsPropositionInput { input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] or: [GeometricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1482,11 +1163,11 @@ input GeometricDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput thicknesses: FloatsPropositionInput } input GeometricDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1494,7 +1175,6 @@ input GeometricDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input GetHttpsResourcePropositionInput { @@ -1519,8 +1199,7 @@ input GetHttpsResourceSortInput { description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput - @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } @@ -1529,8 +1208,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput - @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } @@ -1549,6 +1227,8 @@ input GetHttpsResourceTreeNonRootVertexFilterInput { input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1559,10 +1239,10 @@ input HygrothermalDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput } input HygrothermalDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1570,7 +1250,6 @@ input HygrothermalDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input Int32PropositionInput { @@ -1732,6 +1411,21 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } +input NullableOfUuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + input NumerationInput { mainNumber: String! prefix: String @@ -1755,6 +1449,8 @@ input NumerationSortInput { input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1765,7 +1461,6 @@ input OpticalDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput type: NullableOfOpticalComponentTypePropositionInput subtype: NullableOfOpticalComponentSubtypePropositionInput coatedSide: NullableOfCoatedSidePropositionInput @@ -1779,6 +1474,7 @@ input OpticalDataPropositionInput { } input OpticalDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1786,12 +1482,13 @@ input OpticalDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] or: [PhotovoltaicDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1802,10 +1499,10 @@ input PhotovoltaicDataPropositionInput { approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput warnings: StringsPropositionInput - id: UuidPropositionInput } input PhotovoltaicDataSortInput { + id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") @@ -1813,7 +1510,6 @@ input PhotovoltaicDataSortInput { creatorId: SortEnumType @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") appliedMethod: AppliedMethodSortInput @cost(weight: "10") - id: SortEnumType @cost(weight: "10") } input PublicationInput { @@ -2038,6 +1734,8 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsFilterInput { and: [UpdateResponseApprovalsFilterInput!] or: [UpdateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -2186,6 +1884,8 @@ enum CreateCalorimetricDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -2198,6 +1898,8 @@ enum CreateGeometricDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -2210,6 +1912,7 @@ enum CreateGetHttpsResourceErrorCode { UNKNOWN_PARENT ILLEGAL_PARENT UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD CREATING_RESPONSE_APPROVAL_FAILED } @@ -2221,6 +1924,8 @@ enum CreateHygrothermalDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -2233,6 +1938,8 @@ enum CreateOpticalDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -2245,6 +1952,8 @@ enum CreatePhotovoltaicDataErrorCode { ILLEGAL_CREATED_AT UNKNOWN_CREATOR UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA UNKNOWN_DATA_FORMAT CREATING_RESPONSE_APPROVAL_FAILED } @@ -2445,35 +2154,18 @@ enum UriHostNameType { } "The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost( - "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." - weight: String! -) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION "The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize( - "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." - assumedSize: Int - "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." - requireOneSlicingArgument: Boolean! = true - "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." - sizedFields: [String!] - "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." - slicingArgumentDefaultValue: Int - "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." - slicingArguments: [String!] -) on FIELD_DEFINITION +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION "The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy( - "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." - url: String! -) on SCALAR +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") "BCP 47 compliant Language Tag string" scalar Locale @@ -2489,4 +2181,4 @@ scalar Upload scalar Url -scalar Uuid +scalar Uuid \ No newline at end of file From 74425adab8b6c77b322dcf37a0d09a5763ee7bb7 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 24 Nov 2025 12:44:23 +0100 Subject: [PATCH 581/878] EN410solar works if the wavelengths of the spectralDataPoints exceed the range of the wavelengthsWeights --- .../Methods/SpectralToIntegralMethodTests.cs | 61 +- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 8429 +++++++++++++++++ 2 files changed, 8488 insertions(+), 2 deletions(-) create mode 100644 backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index a2286887..bc4d0ba6 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -43,7 +43,7 @@ public void Calculate_None() [Test] [SuppressMessage("Naming", "CA1707")] - public void Calculate_One() + public void Calculate_One_200() { var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ @@ -60,6 +60,63 @@ public void Calculate_One() } } + [Test] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_One_300() + { + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(300)), new Results(1)), + ]) + ]); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); + } + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_One_400() + { + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(400)), new Results(1)), + ]) + ]); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); + } + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public void Calculate_One_2500() + { + var spectralDataPoints = new SpectralToIntegralInput([ + new SpectralToIntegralData([ + new DataPoint(new Incidence(new Wavelengths(2500)), new Results(1)), + ]) + ]); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); + } + } + [Test] [SuppressMessage("Naming", "CA1707")] public void Calculate_Few() @@ -87,7 +144,7 @@ public void Calculate_Few() public async Task Calculate_Some() { using var fileStream = File.OpenRead( - ConstructFilePath("./Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") + ConstructFilePath("./Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") ); using var jsonDocument = await JsonDocument.ParseAsync( fileStream, diff --git a/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json new file mode 100644 index 00000000..c37e5f80 --- /dev/null +++ b/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json @@ -0,0 +1,8429 @@ +{ + "data": [ + { + "componentCharacteristics": { + "surface": "prime", + "symmetries": "azimuthalAngleInvariance" + }, + "dataPoints": [ + { + "incidence": { + "wavelengths": { + "wavelength": 200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0476000010967255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0476000010967255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0303000006824732, + "reflectance": 0.0472000017762184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0931999981403351, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.191599994897842, + "reflectance": 0.0485999993979931 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.318300008773804, + "reflectance": 0.0516999997198582 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.453700006008148, + "reflectance": 0.0568000003695488 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.576499998569489, + "reflectance": 0.0628999993205071 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.676800012588501, + "reflectance": 0.0689999982714653 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.751299977302551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.804400026798248, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861100018024445, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873399972915649, + "reflectance": 0.0829000025987625 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879499971866608, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888599991798401, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896899998188019, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896650016307831, + "reflectance": 0.0832000002264977 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896399974822998, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895399987697601, + "reflectance": 0.0827500000596046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894400000572205, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0823500007390976 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894200026988983, + "reflectance": 0.0821999981999397 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894649982452393, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0820000022649765 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896250009536743, + "reflectance": 0.0819500014185906 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89740002155304, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898800015449524, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900200009346008, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900900006294251, + "reflectance": 0.0818499997258186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0817999988794327 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901899993419647, + "reflectance": 0.0817499980330467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902199983596802, + "reflectance": 0.0816999971866608 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903299987316132, + "reflectance": 0.0816499963402748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904399991035461, + "reflectance": 0.0816000029444695 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904799997806549, + "reflectance": 0.0815500020980835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905200004577637, + "reflectance": 0.0815000012516975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905700027942658, + "reflectance": 0.0814500004053116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.906199991703033, + "reflectance": 0.0813999995589256 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905849993228912, + "reflectance": 0.0812999978661537 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905499994754791, + "reflectance": 0.0812000036239624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905399978160858, + "reflectance": 0.0811000019311905 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90530002117157, + "reflectance": 0.0810000002384186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90514999628067, + "reflectance": 0.0808999985456467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807999968528748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807500034570694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807000026106834 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904600024223328, + "reflectance": 0.0806000009179115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90420001745224, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903699994087219, + "reflectance": 0.0803999975323677 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903199970722199, + "reflectance": 0.0803000032901764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902400016784668, + "reflectance": 0.0801500007510185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0799999982118607 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901000022888184, + "reflectance": 0.0798999965190888 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90039998292923, + "reflectance": 0.0798000022768974 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.899649977684021, + "reflectance": 0.0797000005841255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898899972438812, + "reflectance": 0.0795999988913536 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898249983787537, + "reflectance": 0.0794999971985817 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.897599995136261, + "reflectance": 0.0794000029563904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896849989891052, + "reflectance": 0.0793000012636185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896099984645844, + "reflectance": 0.0791999995708466 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0790499970316887 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89410001039505, + "reflectance": 0.0789000019431114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.893450021743774, + "reflectance": 0.0788000002503395 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.892799973487854, + "reflectance": 0.0786999985575676 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89190000295639, + "reflectance": 0.0785999968647957 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.890999972820282, + "reflectance": 0.0785000026226044 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89045000076294, + "reflectance": 0.0784000009298325 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889900028705597, + "reflectance": 0.0782999992370606 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889150023460388, + "reflectance": 0.0781999975442886 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888400018215179, + "reflectance": 0.0781000033020973 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887750029563904, + "reflectance": 0.0780500024557114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887099981307983, + "reflectance": 0.0780000016093254 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.886399984359741, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.885699987411499, + "reflectance": 0.0777999982237816 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.884800016880035, + "reflectance": 0.0776999965310097 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.883899986743927, + "reflectance": 0.0776000022888184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.882799983024597, + "reflectance": 0.0774499997496605 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.881699979305267, + "reflectance": 0.0772999972105026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.88085001707077, + "reflectance": 0.0772000029683113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879999995231628, + "reflectance": 0.0771000012755394 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878650009632111, + "reflectance": 0.0769499987363815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877300024032593, + "reflectance": 0.0768000036478043 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875999987125397, + "reflectance": 0.0767000019550324 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0766000002622604 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873849987983704, + "reflectance": 0.0764499977231026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873000025749207, + "reflectance": 0.0763000026345253 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872150003910065, + "reflectance": 0.0762000009417534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871299982070923, + "reflectance": 0.0760999992489815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869650006294251, + "reflectance": 0.0759499967098236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867999970912933, + "reflectance": 0.0758000016212463 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867299973964691, + "reflectance": 0.0756999999284744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866599977016449, + "reflectance": 0.0755999982357025 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865100026130676, + "reflectance": 0.0754500031471252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863600015640259, + "reflectance": 0.0753000006079674 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86269998550415, + "reflectance": 0.0751999989151955 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861800014972687, + "reflectance": 0.0750999972224236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861150026321411, + "reflectance": 0.0750000029802322 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860499978065491, + "reflectance": 0.0749000012874603 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859849989414215, + "reflectance": 0.0748500004410744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85920000076294, + "reflectance": 0.0747999995946884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858449995517731, + "reflectance": 0.0746999979019165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857699990272522, + "reflectance": 0.0746000036597252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856649994850159, + "reflectance": 0.0745000019669533 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855599999427795, + "reflectance": 0.0744000002741814 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854300022125244, + "reflectance": 0.0742999985814095 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852999985218048, + "reflectance": 0.0741999968886375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852150022983551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851300001144409, + "reflectance": 0.0740000009536743 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850499987602234, + "reflectance": 0.0739500001072884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849699974060059, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848900020122528, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848100006580353, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847350001335144, + "reflectance": 0.0736500024795532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846599996089935, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845899999141693, + "reflectance": 0.0734999999403954 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845200002193451, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844600021839142, + "reflectance": 0.0733499974012375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843999981880188, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843500018119812, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842999994754791, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842599987983704, + "reflectance": 0.0730499997735024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842199981212616, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841849982738495, + "reflectance": 0.0729499980807304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841499984264374, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841199994087219, + "reflectance": 0.0728499963879585 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840900003910065, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84060001373291, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840099990367889, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839900016784668, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839699983596802, + "reflectance": 0.0726500004529953 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83950001001358, + "reflectance": 0.0725999996066093 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839299976825714, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839100003242493, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838949978351593, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838660001754761, + "reflectance": 0.0724700018763542 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838519990444183, + "reflectance": 0.0724399983882904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838379979133606, + "reflectance": 0.0724100023508072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838100016117096, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837960004806519, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837819993495941, + "reflectance": 0.0722900032997131 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837679982185364, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837539970874786, + "reflectance": 0.0722299963235855 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837400019168854, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837430000305176, + "reflectance": 0.0721900016069412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837459981441498, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837490022182465, + "reflectance": 0.0721699967980385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837520003318787, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837549984455109, + "reflectance": 0.0721499994397163 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837580025196075, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837610006332398, + "reflectance": 0.0721300020813942 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83763998746872, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837670028209686, + "reflectance": 0.0721099972724915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837700009346008, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837790012359619, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83788001537323, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837970018386841, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838060021400452, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838150024414063, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838329970836639, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83841997385025, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838509976863861, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838599979877472, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838769972324371, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838940024375916, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839110016822815, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839280009269714, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839450001716614, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839619994163513, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839789986610413, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840129971504211, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840650022029877, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841000020503998, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841350018978119, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84170001745224, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842050015926361, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842400014400482, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842750012874603, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843100011348724, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843450009822846, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843800008296967, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844190001487732, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844579994678497, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844969987869263, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845359981060028, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845749974250794, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846140027046204, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846530020236969, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8473100066185, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847699999809265, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848190009593964, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848680019378662, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849170029163361, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849659979343414, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850149989128113, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850639998912811, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85113000869751, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851620018482208, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852110028266907, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85259997844696, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852859973907471, + "reflectance": 0.0727099999785423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853120028972626, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853380024433136, + "reflectance": 0.0727299973368645 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853640019893646, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853900015354157, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854160010814667, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854420006275177, + "reflectance": 0.0727699995040894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854680001735687, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854939997196198, + "reflectance": 0.0727899968624115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855199992656708, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855979979038239, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856760025024414, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857540011405945, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858319997787476, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859099984169006, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859879970550537, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860660016536713, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861440002918243, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862219989299774, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862999975681305, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863579988479614, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864160001277924, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864740014076233, + "reflectance": 0.0733200013637543 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865320026874542, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865899980068207, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866479992866516, + "reflectance": 0.0734400004148483 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867060005664825, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867640018463135, + "reflectance": 0.0735199972987175 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868219971656799, + "reflectance": 0.0735599994659424 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869319975376129, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869840025901794, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870880007743835, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871399998664856, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871919989585877, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872439980506897, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872959971427918, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873480021953583, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874000012874603, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874270021915436, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874539971351624, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874809980392456, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875079989433289, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875349998474121, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875620007514954, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875890016555786, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876160025596619, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876429975032806, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876699984073639, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876860022544861, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877020001411438, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878260016441345, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878220021724701, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878180027008057, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878099977970123, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878059983253479, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878019988536835, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877939999103546, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877900004386902, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877740025520325, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877579987049103, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877420008182526, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877260029315949, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877099990844727, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876919984817505, + "reflectance": 0.0735699981451035 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876739978790283, + "reflectance": 0.0735400021076202 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876559972763062, + "reflectance": 0.0735099986195564 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876380026340485, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876200020313263, + "reflectance": 0.0734499990940094 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876020014286041, + "reflectance": 0.0734200030565262 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87584000825882, + "reflectance": 0.0733899995684624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875660002231598, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875479996204376, + "reflectance": 0.0733300000429153 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875299990177155, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875159978866577, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875020027160645, + "reflectance": 0.0732600018382072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874880015850067, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87474000453949, + "reflectance": 0.0732199996709824 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874599993228912, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874459981918335, + "reflectance": 0.0731799975037575 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874319970607758, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874180018901825, + "reflectance": 0.0731400027871132 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874040007591248, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87389999628067, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87379002571106, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873679995536804, + "reflectance": 0.0730599984526634 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873570024967194, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873459994792938, + "reflectance": 0.0730199962854385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873350024223328, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873239994049072, + "reflectance": 0.0729800015687943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873130023479462, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873019993305206, + "reflectance": 0.0729399994015694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872910022735596, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87279999256134, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872600018978119, + "reflectance": 0.0728600025177002 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872500002384186, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872399985790253, + "reflectance": 0.0728200003504753 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872300028800964, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872200012207031, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872099995613098, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871999979019165, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871900022029877, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871800005435944, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871619999408722, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8714399933815, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871259987354279, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871079981327057, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870899975299835, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870720028877258, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870540022850037, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870180010795593, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870000004768372, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869989991188049, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869979977607727, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86997002363205, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869960010051727, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869949996471405, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869939982891083, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869930028915405, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869920015335083, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869910001754761, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869899988174439, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869790017604828, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869679987430573, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869570016860962, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869459986686707, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869350016117096, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869239985942841, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86913001537323, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869019985198975, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868910014629364, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868470013141632, + "reflectance": 0.0720599964261055 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868139982223511, + "reflectance": 0.0720200017094612 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867810010910034, + "reflectance": 0.0719799995422363 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867479979991913, + "reflectance": 0.0719399973750114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867150008678436, + "reflectance": 0.0719000026583672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866819977760315, + "reflectance": 0.0718600004911423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866490006446838, + "reflectance": 0.0718199983239174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866159975528717, + "reflectance": 0.0717800036072731 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865830004215241, + "reflectance": 0.0717400014400482 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865499973297119, + "reflectance": 0.0716999992728233 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864019989967346, + "reflectance": 0.0715800002217293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862540006637573, + "reflectance": 0.0714600011706352 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8610600233078, + "reflectance": 0.0713400021195412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859579980373383, + "reflectance": 0.0712200030684471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85809999704361, + "reflectance": 0.0710999965667725 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856620013713837, + "reflectance": 0.0709799975156784 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855139970779419, + "reflectance": 0.0708599984645844 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853659987449646, + "reflectance": 0.0707399994134903 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852180004119873, + "reflectance": 0.0706200003623962 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8507000207901, + "reflectance": 0.0705000013113022 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850210011005402, + "reflectance": 0.0704500004649162 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849229991436005, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848739981651306, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848249971866608, + "reflectance": 0.0702499970793724 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847760021686554, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847270011901856, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846780002117157, + "reflectance": 0.0701000019907951 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846289992332459, + "reflectance": 0.0700500011444092 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84579998254776, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84636002779007, + "reflectance": 0.0700299963355064 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0700599998235703 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847479999065399, + "reflectance": 0.0700900033116341 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848039984703064, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848599970340729, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849160015583038, + "reflectance": 0.0701799988746643 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0702100023627281 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850279986858368, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850839972496033, + "reflectance": 0.0702700018882752 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851400017738342, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851589977741242, + "reflectance": 0.0703099966049194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851779997348785, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851970016956329, + "reflectance": 0.0703300014138222 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852159976959229, + "reflectance": 0.0703400000929833 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852349996566773, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852540016174316, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852729976177216, + "reflectance": 0.0703700035810471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85291999578476, + "reflectance": 0.0703800022602081 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853110015392304, + "reflectance": 0.0703900009393692 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853299975395203, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852970004081726, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852639973163605, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852310001850128, + "reflectance": 0.0702800005674362 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851979970932007, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85164999961853, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851320028305054, + "reflectance": 0.0701600015163422 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850989997386932, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850660026073456, + "reflectance": 0.0700799971818924 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850329995155335, + "reflectance": 0.0700400024652481 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850000023841858, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84918999671936, + "reflectance": 0.0699300020933151 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848380029201508, + "reflectance": 0.0698599964380264 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84757000207901, + "reflectance": 0.0697899982333183 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846759974956513, + "reflectance": 0.0697200000286102 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84595000743866, + "reflectance": 0.0696500018239021 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845139980316162, + "reflectance": 0.069580003619194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844330012798309, + "reflectance": 0.0695099979639053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843519985675812, + "reflectance": 0.0694399997591972 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842710018157959, + "reflectance": 0.0693700015544891 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841899991035461, + "reflectance": 0.069300003349781 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840929985046387, + "reflectance": 0.0692199990153313 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0691400021314621 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838989973068237, + "reflectance": 0.0690599977970123 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838020026683807, + "reflectance": 0.0689800009131432 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837050020694733, + "reflectance": 0.0688999965786934 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.836080014705658, + "reflectance": 0.0688199996948242 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.835110008716583, + "reflectance": 0.0687400028109551 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.834140002727509, + "reflectance": 0.0686599984765053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.833169996738434, + "reflectance": 0.0685800015926361 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.832199990749359, + "reflectance": 0.0684999972581863 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.832199990749359, + "reflectance": 0.0684999972581863 + } + } + ] + } + ] +} \ No newline at end of file From 37de5ce8dc7d63e6e4a846bdf5a9d8128e277c02 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 24 Nov 2025 14:11:12 +0100 Subject: [PATCH 582/878] The problem is the spectralDAtaPointWavelengthAbove at 2500 nm --- .../SpectralToIntegralMethod.cs | 42 +- .../Methods/SpectralToIntegralMethodTests.cs | 27 +- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 8391 +++++++++++++++++ 3 files changed, 8458 insertions(+), 2 deletions(-) create mode 100644 backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index d655e9d7..abc859f2 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -221,6 +221,46 @@ public override SpectralToIntegralOutput Calculate(SpectralToIntegralInput input ); } + // public double Calculate( + // IReadOnlyList spectralDataPoints, + // ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights + // ) + // { + // var sortedSpectralDataPoints = + // spectralDataPoints + // .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) + // .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) + // .ToList() + // .AsReadOnly(); + // var numerator = 0.0D; + // var denominator = 0.0D; + // foreach (var wavelengthsWeight in wavelengthsWeights) + // { + // foreach (var spectralDataPoint in sortedSpectralDataPoints) + // { + // if (spectralDataPoint.Incidence.Wavelengths.Wavelength == wavelengthsWeight.wavelength) + // { + // transmittance = spectralDataPoint.Results.Transmittance; + // } + // if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength + // && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength + // ) + // { + // spectralDataPointWavelengthAbove = spectralDataPoint; + // } + // } + // // Trapezoidal rule to calculate an integral + // var averageValue = ( + // spectralDataPointWavelengthBelow.Results.Transmittance + // + spectralDataPointWavelengthAbove.Results.Transmittance + // ) / 2; + // numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + // denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + // } + // return numerator / denominator; + // } + + // public double CalculateInterpolationBetweenSpectralDataPoints( public double Calculate( IReadOnlyList spectralDataPoints, ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights @@ -274,7 +314,7 @@ public double Calculate( // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { - return + return dataPoint.Incidence.Wavelengths.Wavelength <= 0 || dataPoint.Incidence.Wavelengths.Wavelength >= 3000; } diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index bc4d0ba6..c1fcb8c3 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -141,7 +141,7 @@ public void Calculate_Few() [Test] [SuppressMessage("Naming", "CA1707")] - public async Task Calculate_Some() + public async Task Calculate_modification02() { using var fileStream = File.OpenRead( ConstructFilePath("./Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") @@ -163,6 +163,31 @@ public async Task Calculate_Some() } } + [Test] + [SuppressMessage("Naming", "CA1707")] + public async Task Calculate_modification03() + { + using var fileStream = File.OpenRead( + ConstructFilePath("./Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") + ); + using var jsonDocument = await JsonDocument.ParseAsync( + fileStream, + JsonDocumentSettings.Lax + ); + var spectralDataPoints = jsonDocument.RootElement.Deserialize( + MethodBase.JsonSerializerOptions + ) ?? throw new InvalidOperationException(); + var method = new SpectralToIntegralMethod(); + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); + } + } + + [Test] [SuppressMessage("Naming", "CA1707")] public async Task Calculate_Many() diff --git a/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json new file mode 100644 index 00000000..da0633d6 --- /dev/null +++ b/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json @@ -0,0 +1,8391 @@ +{ + "data": [ + { + "componentCharacteristics": { + "surface": "prime", + "symmetries": "azimuthalAngleInvariance" + }, + "dataPoints": [ + { + "incidence": { + "wavelengths": { + "wavelength": 300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0476000010967255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0303000006824732, + "reflectance": 0.0472000017762184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.0931999981403351, + "reflectance": 0.0474000014364719 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.191599994897842, + "reflectance": 0.0485999993979931 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.318300008773804, + "reflectance": 0.0516999997198582 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.453700006008148, + "reflectance": 0.0568000003695488 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.576499998569489, + "reflectance": 0.0628999993205071 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.676800012588501, + "reflectance": 0.0689999982714653 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.751299977302551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.804400026798248, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861100018024445, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873399972915649, + "reflectance": 0.0829000025987625 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879499971866608, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888599991798401, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896899998188019, + "reflectance": 0.0834000036120415 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896650016307831, + "reflectance": 0.0832000002264977 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896399974822998, + "reflectance": 0.0829999968409538 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895399987697601, + "reflectance": 0.0827500000596046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894400000572205, + "reflectance": 0.0825000032782555 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894299983978272, + "reflectance": 0.0823500007390976 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894200026988983, + "reflectance": 0.0821999981999397 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.894649982452393, + "reflectance": 0.0820999965071678 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0820000022649765 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896250009536743, + "reflectance": 0.0819500014185906 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89740002155304, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898800015449524, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900200009346008, + "reflectance": 0.0819000005722046 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.900900006294251, + "reflectance": 0.0818499997258186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0817999988794327 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901899993419647, + "reflectance": 0.0817499980330467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902199983596802, + "reflectance": 0.0816999971866608 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903299987316132, + "reflectance": 0.0816499963402748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904399991035461, + "reflectance": 0.0816000029444695 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904799997806549, + "reflectance": 0.0815500020980835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905200004577637, + "reflectance": 0.0815000012516975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905700027942658, + "reflectance": 0.0814500004053116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.906199991703033, + "reflectance": 0.0813999995589256 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905849993228912, + "reflectance": 0.0812999978661537 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905499994754791, + "reflectance": 0.0812000036239624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.905399978160858, + "reflectance": 0.0811000019311905 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90530002117157, + "reflectance": 0.0810000002384186 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90514999628067, + "reflectance": 0.0808999985456467 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807999968528748 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807500034570694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904999971389771, + "reflectance": 0.0807000026106834 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.904600024223328, + "reflectance": 0.0806000009179115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90420001745224, + "reflectance": 0.0804999992251396 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903699994087219, + "reflectance": 0.0803999975323677 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.903199970722199, + "reflectance": 0.0803000032901764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.902400016784668, + "reflectance": 0.0801500007510185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901600003242493, + "reflectance": 0.0799999982118607 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.901000022888184, + "reflectance": 0.0798999965190888 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.90039998292923, + "reflectance": 0.0798000022768974 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.899649977684021, + "reflectance": 0.0797000005841255 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898899972438812, + "reflectance": 0.0795999988913536 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.898249983787537, + "reflectance": 0.0794999971985817 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.897599995136261, + "reflectance": 0.0794000029563904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896849989891052, + "reflectance": 0.0793000012636185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.896099984645844, + "reflectance": 0.0791999995708466 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.895099997520447, + "reflectance": 0.0790499970316887 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89410001039505, + "reflectance": 0.0789000019431114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.893450021743774, + "reflectance": 0.0788000002503395 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.892799973487854, + "reflectance": 0.0786999985575676 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89190000295639, + "reflectance": 0.0785999968647957 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.890999972820282, + "reflectance": 0.0785000026226044 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.89045000076294, + "reflectance": 0.0784000009298325 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889900028705597, + "reflectance": 0.0782999992370606 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.889150023460388, + "reflectance": 0.0781999975442886 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.888400018215179, + "reflectance": 0.0781000033020973 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887750029563904, + "reflectance": 0.0780500024557114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.887099981307983, + "reflectance": 0.0780000016093254 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.886399984359741, + "reflectance": 0.0778999999165535 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.885699987411499, + "reflectance": 0.0777999982237816 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.884800016880035, + "reflectance": 0.0776999965310097 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.883899986743927, + "reflectance": 0.0776000022888184 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.882799983024597, + "reflectance": 0.0774499997496605 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.881699979305267, + "reflectance": 0.0772999972105026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.88085001707077, + "reflectance": 0.0772000029683113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.879999995231628, + "reflectance": 0.0771000012755394 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878650009632111, + "reflectance": 0.0769499987363815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877300024032593, + "reflectance": 0.0768000036478043 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875999987125397, + "reflectance": 0.0767000019550324 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874700009822846, + "reflectance": 0.0766000002622604 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873849987983704, + "reflectance": 0.0764499977231026 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873000025749207, + "reflectance": 0.0763000026345253 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872150003910065, + "reflectance": 0.0762000009417534 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871299982070923, + "reflectance": 0.0760999992489815 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869650006294251, + "reflectance": 0.0759499967098236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867999970912933, + "reflectance": 0.0758000016212463 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867299973964691, + "reflectance": 0.0756999999284744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866599977016449, + "reflectance": 0.0755999982357025 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865100026130676, + "reflectance": 0.0754500031471252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863600015640259, + "reflectance": 0.0753000006079674 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86269998550415, + "reflectance": 0.0751999989151955 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861800014972687, + "reflectance": 0.0750999972224236 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861150026321411, + "reflectance": 0.0750000029802322 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860499978065491, + "reflectance": 0.0749000012874603 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859849989414215, + "reflectance": 0.0748500004410744 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85920000076294, + "reflectance": 0.0747999995946884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858449995517731, + "reflectance": 0.0746999979019165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857699990272522, + "reflectance": 0.0746000036597252 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856649994850159, + "reflectance": 0.0745000019669533 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855599999427795, + "reflectance": 0.0744000002741814 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854300022125244, + "reflectance": 0.0742999985814095 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852999985218048, + "reflectance": 0.0741999968886375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852150022983551, + "reflectance": 0.0741000026464462 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851300001144409, + "reflectance": 0.0740000009536743 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850499987602234, + "reflectance": 0.0739500001072884 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849699974060059, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848900020122528, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848100006580353, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847350001335144, + "reflectance": 0.0736500024795532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846599996089935, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845899999141693, + "reflectance": 0.0734999999403954 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845200002193451, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844600021839142, + "reflectance": 0.0733499974012375 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843999981880188, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843500018119812, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842999994754791, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842599987983704, + "reflectance": 0.0730499997735024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842199981212616, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841849982738495, + "reflectance": 0.0729499980807304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841499984264374, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841199994087219, + "reflectance": 0.0728499963879585 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840900003910065, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84060001373291, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840099990367889, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839900016784668, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839699983596802, + "reflectance": 0.0726500004529953 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83950001001358, + "reflectance": 0.0725999996066093 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839299976825714, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839100003242493, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838949978351593, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838800013065338, + "reflectance": 0.0724999979138374 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838660001754761, + "reflectance": 0.0724700018763542 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838519990444183, + "reflectance": 0.0724399983882904 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838379979133606, + "reflectance": 0.0724100023508072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838100016117096, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837960004806519, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837819993495941, + "reflectance": 0.0722900032997131 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837679982185364, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837539970874786, + "reflectance": 0.0722299963235855 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837400019168854, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837430000305176, + "reflectance": 0.0721900016069412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837459981441498, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837490022182465, + "reflectance": 0.0721699967980385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837520003318787, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837549984455109, + "reflectance": 0.0721499994397163 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837580025196075, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837610006332398, + "reflectance": 0.0721300020813942 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83763998746872, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837670028209686, + "reflectance": 0.0721099972724915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837700009346008, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837790012359619, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83788001537323, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837970018386841, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838060021400452, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838150024414063, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838240027427673, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838329970836639, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.83841997385025, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838509976863861, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838599979877472, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838769972324371, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838940024375916, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839110016822815, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839280009269714, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839450001716614, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839619994163513, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839789986610413, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840129971504211, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840300023555756, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840650022029877, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841000020503998, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841350018978119, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84170001745224, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842050015926361, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842400014400482, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842750012874603, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843100011348724, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843450009822846, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843800008296967, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844190001487732, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844579994678497, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844969987869263, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845359981060028, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845749974250794, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846140027046204, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846530020236969, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8473100066185, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847699999809265, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848190009593964, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848680019378662, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849170029163361, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849659979343414, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850149989128113, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850639998912811, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85113000869751, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851620018482208, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852110028266907, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85259997844696, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852859973907471, + "reflectance": 0.0727099999785423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853120028972626, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853380024433136, + "reflectance": 0.0727299973368645 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853640019893646, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853900015354157, + "reflectance": 0.0727500021457672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854160010814667, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854420006275177, + "reflectance": 0.0727699995040894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854680001735687, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.854939997196198, + "reflectance": 0.0727899968624115 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855199992656708, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855979979038239, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856760025024414, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.857540011405945, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.858319997787476, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859099984169006, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859879970550537, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.860660016536713, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.861440002918243, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862219989299774, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862999975681305, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.863579988479614, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864160001277924, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864740014076233, + "reflectance": 0.0733200013637543 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865320026874542, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865899980068207, + "reflectance": 0.0733999982476234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866479992866516, + "reflectance": 0.0734400004148483 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867060005664825, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867640018463135, + "reflectance": 0.0735199972987175 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868219971656799, + "reflectance": 0.0735599994659424 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1505 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869319975376129, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1510 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869840025901794, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1515 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1520 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870880007743835, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1525 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871399998664856, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1530 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871919989585877, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1535 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872439980506897, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1540 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872959971427918, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1545 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873480021953583, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1550 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874000012874603, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1555 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874270021915436, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1560 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874539971351624, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1565 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874809980392456, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1570 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875079989433289, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1575 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875349998474121, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1580 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875620007514954, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1585 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875890016555786, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1590 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876160025596619, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1595 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876429975032806, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1600 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876699984073639, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1605 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876860022544861, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1610 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877020001411438, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1615 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1620 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1625 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1630 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1635 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1640 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1645 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1650 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87830001115799, + "reflectance": 0.0738999992609024 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1655 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878260016441345, + "reflectance": 0.0738900005817413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1660 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878220021724701, + "reflectance": 0.0738800019025803 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1665 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878180027008057, + "reflectance": 0.0738700032234192 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1670 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878139972686768, + "reflectance": 0.0738599970936775 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1675 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878099977970123, + "reflectance": 0.0738499984145165 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1680 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878059983253479, + "reflectance": 0.0738399997353554 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1685 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.878019988536835, + "reflectance": 0.0738300010561943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1690 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87797999382019, + "reflectance": 0.0738200023770332 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1695 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877939999103546, + "reflectance": 0.0738100036978722 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1700 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877900004386902, + "reflectance": 0.0737999975681305 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1705 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877820014953613, + "reflectance": 0.0737800002098084 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1710 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877740025520325, + "reflectance": 0.0737600028514862 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1715 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877659976482391, + "reflectance": 0.0737399980425835 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1720 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877579987049103, + "reflectance": 0.0737200006842613 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1725 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877499997615814, + "reflectance": 0.0737000033259392 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1730 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877420008182526, + "reflectance": 0.0736799985170364 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1735 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877340018749237, + "reflectance": 0.0736600011587143 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1740 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877260029315949, + "reflectance": 0.0736399963498116 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1745 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877179980278015, + "reflectance": 0.0736199989914894 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1750 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.877099990844727, + "reflectance": 0.0736000016331673 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1755 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876919984817505, + "reflectance": 0.0735699981451035 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1760 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876739978790283, + "reflectance": 0.0735400021076202 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1765 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876559972763062, + "reflectance": 0.0735099986195564 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1770 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876380026340485, + "reflectance": 0.0734800025820732 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1775 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876200020313263, + "reflectance": 0.0734499990940094 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1780 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.876020014286041, + "reflectance": 0.0734200030565262 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1785 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87584000825882, + "reflectance": 0.0733899995684624 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1790 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875660002231598, + "reflectance": 0.0733600035309792 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1795 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875479996204376, + "reflectance": 0.0733300000429153 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1800 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875299990177155, + "reflectance": 0.0732999965548515 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1805 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875159978866577, + "reflectance": 0.0732799991965294 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1810 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.875020027160645, + "reflectance": 0.0732600018382072 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1815 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874880015850067, + "reflectance": 0.0732399970293045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1820 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87474000453949, + "reflectance": 0.0732199996709824 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1825 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874599993228912, + "reflectance": 0.0732000023126602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1830 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874459981918335, + "reflectance": 0.0731799975037575 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1835 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874319970607758, + "reflectance": 0.0731600001454353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1840 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874180018901825, + "reflectance": 0.0731400027871132 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1845 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.874040007591248, + "reflectance": 0.0731199979782105 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1850 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87389999628067, + "reflectance": 0.0731000006198883 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1855 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87379002571106, + "reflectance": 0.0730800032615662 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1860 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873679995536804, + "reflectance": 0.0730599984526634 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1865 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873570024967194, + "reflectance": 0.0730400010943413 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1870 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873459994792938, + "reflectance": 0.0730199962854385 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1875 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873350024223328, + "reflectance": 0.0729999989271164 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1880 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873239994049072, + "reflectance": 0.0729800015687943 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1885 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873130023479462, + "reflectance": 0.0729599967598915 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1890 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.873019993305206, + "reflectance": 0.0729399994015694 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1895 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872910022735596, + "reflectance": 0.0729200020432472 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1900 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.87279999256134, + "reflectance": 0.0728999972343445 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1905 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872699975967407, + "reflectance": 0.0728799998760223 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1910 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872600018978119, + "reflectance": 0.0728600025177002 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1915 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872500002384186, + "reflectance": 0.0728399977087975 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1920 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872399985790253, + "reflectance": 0.0728200003504753 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1925 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872300028800964, + "reflectance": 0.0728000029921532 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1930 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872200012207031, + "reflectance": 0.0727799981832504 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1935 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.872099995613098, + "reflectance": 0.0727600008249283 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1940 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871999979019165, + "reflectance": 0.0727400034666061 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1945 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871900022029877, + "reflectance": 0.0727199986577034 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1950 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871800005435944, + "reflectance": 0.0727000012993813 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1955 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871619999408722, + "reflectance": 0.0726699978113174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1960 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8714399933815, + "reflectance": 0.0726400017738342 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1965 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871259987354279, + "reflectance": 0.0726099982857704 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1970 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.871079981327057, + "reflectance": 0.0725800022482872 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1975 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870899975299835, + "reflectance": 0.0725499987602234 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1980 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870720028877258, + "reflectance": 0.0725200027227402 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1985 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870540022850037, + "reflectance": 0.0724899992346764 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1990 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870360016822815, + "reflectance": 0.0724600031971932 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 1995 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870180010795593, + "reflectance": 0.0724299997091293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2000 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.870000004768372, + "reflectance": 0.0724000036716461 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2005 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869989991188049, + "reflectance": 0.0723899975419045 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2010 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869979977607727, + "reflectance": 0.0723799988627434 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2015 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86997002363205, + "reflectance": 0.0723700001835823 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2020 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869960010051727, + "reflectance": 0.0723600015044212 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2025 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869949996471405, + "reflectance": 0.0723500028252602 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2030 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869939982891083, + "reflectance": 0.0723399966955185 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2035 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869930028915405, + "reflectance": 0.0723299980163574 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2040 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869920015335083, + "reflectance": 0.0723199993371964 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2045 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869910001754761, + "reflectance": 0.0723100006580353 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2050 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869899988174439, + "reflectance": 0.0723000019788742 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2055 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869790017604828, + "reflectance": 0.0722799971699715 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2060 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869679987430573, + "reflectance": 0.0722599998116493 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2065 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869570016860962, + "reflectance": 0.0722400024533272 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2070 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869459986686707, + "reflectance": 0.0722199976444244 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2075 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869350016117096, + "reflectance": 0.0722000002861023 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2080 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869239985942841, + "reflectance": 0.0721800029277802 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2085 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.86913001537323, + "reflectance": 0.0721599981188774 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2090 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.869019985198975, + "reflectance": 0.0721400007605553 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2095 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868910014629364, + "reflectance": 0.0721200034022331 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2100 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868799984455109, + "reflectance": 0.0720999985933304 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2105 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868470013141632, + "reflectance": 0.0720599964261055 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2110 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.868139982223511, + "reflectance": 0.0720200017094612 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2115 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867810010910034, + "reflectance": 0.0719799995422363 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2120 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867479979991913, + "reflectance": 0.0719399973750114 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2125 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.867150008678436, + "reflectance": 0.0719000026583672 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2130 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866819977760315, + "reflectance": 0.0718600004911423 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2135 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866490006446838, + "reflectance": 0.0718199983239174 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2140 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.866159975528717, + "reflectance": 0.0717800036072731 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2145 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865830004215241, + "reflectance": 0.0717400014400482 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2150 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.865499973297119, + "reflectance": 0.0716999992728233 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2155 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.864019989967346, + "reflectance": 0.0715800002217293 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2160 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.862540006637573, + "reflectance": 0.0714600011706352 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2165 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8610600233078, + "reflectance": 0.0713400021195412 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2170 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.859579980373383, + "reflectance": 0.0712200030684471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2175 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85809999704361, + "reflectance": 0.0710999965667725 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2180 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.856620013713837, + "reflectance": 0.0709799975156784 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2185 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.855139970779419, + "reflectance": 0.0708599984645844 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2190 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853659987449646, + "reflectance": 0.0707399994134903 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2195 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852180004119873, + "reflectance": 0.0706200003623962 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2200 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.8507000207901, + "reflectance": 0.0705000013113022 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2205 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850210011005402, + "reflectance": 0.0704500004649162 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2210 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2215 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849229991436005, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2220 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848739981651306, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2225 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848249971866608, + "reflectance": 0.0702499970793724 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2230 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847760021686554, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2235 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847270011901856, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2240 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846780002117157, + "reflectance": 0.0701000019907951 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2245 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846289992332459, + "reflectance": 0.0700500011444092 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2250 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84579998254776, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2255 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84636002779007, + "reflectance": 0.0700299963355064 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2260 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846920013427734, + "reflectance": 0.0700599998235703 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2265 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.847479999065399, + "reflectance": 0.0700900033116341 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2270 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848039984703064, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2275 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848599970340729, + "reflectance": 0.0701500028371811 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2280 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849160015583038, + "reflectance": 0.0701799988746643 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2285 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.849720001220703, + "reflectance": 0.0702100023627281 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2290 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850279986858368, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2295 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850839972496033, + "reflectance": 0.0702700018882752 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2300 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851400017738342, + "reflectance": 0.0702999979257584 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2305 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851589977741242, + "reflectance": 0.0703099966049194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2310 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851779997348785, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2315 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851970016956329, + "reflectance": 0.0703300014138222 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2320 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852159976959229, + "reflectance": 0.0703400000929833 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2325 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852349996566773, + "reflectance": 0.0703499987721443 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2330 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852540016174316, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2335 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852729976177216, + "reflectance": 0.0703700035810471 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2340 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85291999578476, + "reflectance": 0.0703800022602081 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2345 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853110015392304, + "reflectance": 0.0703900009393692 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2350 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.853299975395203, + "reflectance": 0.0703999996185303 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2355 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852970004081726, + "reflectance": 0.0703599974513054 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2360 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852639973163605, + "reflectance": 0.0703200027346611 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2365 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.852310001850128, + "reflectance": 0.0702800005674362 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2370 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851979970932007, + "reflectance": 0.0702399984002113 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2375 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.85164999961853, + "reflectance": 0.0702000036835671 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2380 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.851320028305054, + "reflectance": 0.0701600015163422 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2385 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850989997386932, + "reflectance": 0.0701199993491173 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2390 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850660026073456, + "reflectance": 0.0700799971818924 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2395 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850329995155335, + "reflectance": 0.0700400024652481 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2400 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.850000023841858, + "reflectance": 0.0700000002980232 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2405 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84918999671936, + "reflectance": 0.0699300020933151 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2410 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.848380029201508, + "reflectance": 0.0698599964380264 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2415 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84757000207901, + "reflectance": 0.0697899982333183 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2420 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.846759974956513, + "reflectance": 0.0697200000286102 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2425 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.84595000743866, + "reflectance": 0.0696500018239021 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2430 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.845139980316162, + "reflectance": 0.069580003619194 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2435 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.844330012798309, + "reflectance": 0.0695099979639053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2440 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.843519985675812, + "reflectance": 0.0694399997591972 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2445 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.842710018157959, + "reflectance": 0.0693700015544891 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2450 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.841899991035461, + "reflectance": 0.069300003349781 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2455 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.840929985046387, + "reflectance": 0.0692199990153313 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2460 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.839959979057312, + "reflectance": 0.0691400021314621 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2465 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838989973068237, + "reflectance": 0.0690599977970123 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2470 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.838020026683807, + "reflectance": 0.0689800009131432 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2475 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.837050020694733, + "reflectance": 0.0688999965786934 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2480 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.836080014705658, + "reflectance": 0.0688199996948242 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2485 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.835110008716583, + "reflectance": 0.0687400028109551 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2490 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.834140002727509, + "reflectance": 0.0686599984765053 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2495 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.833169996738434, + "reflectance": 0.0685800015926361 + } + }, + { + "incidence": { + "wavelengths": { + "wavelength": 2500 + }, + "direction": { + "polar": 0 + } + }, + "emergence": { + "direction": { + "polar": 0 + } + }, + "results": { + "transmittance": 0.832199990749359, + "reflectance": 0.0684999972581863 + } + } + ] + } + ] +} \ No newline at end of file From bebf53cee6d24211d0278b9806ea5520419cb267 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 24 Nov 2025 15:48:51 +0100 Subject: [PATCH 583/878] The separate treatment of the last wavelengthsWeight leads to good EN410solar --- .../SpectralToIntegralMethod.cs | 112 ++++++++------- .../Methods/SpectralToIntegralMethodTests.cs | 130 ++---------------- 2 files changed, 76 insertions(+), 166 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index abc859f2..96977d38 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -221,51 +221,15 @@ public override SpectralToIntegralOutput Calculate(SpectralToIntegralInput input ); } - // public double Calculate( - // IReadOnlyList spectralDataPoints, - // ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights - // ) - // { - // var sortedSpectralDataPoints = - // spectralDataPoints - // .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) - // .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) - // .ToList() - // .AsReadOnly(); - // var numerator = 0.0D; - // var denominator = 0.0D; - // foreach (var wavelengthsWeight in wavelengthsWeights) - // { - // foreach (var spectralDataPoint in sortedSpectralDataPoints) - // { - // if (spectralDataPoint.Incidence.Wavelengths.Wavelength == wavelengthsWeight.wavelength) - // { - // transmittance = spectralDataPoint.Results.Transmittance; - // } - // if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength - // && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - // ) - // { - // spectralDataPointWavelengthAbove = spectralDataPoint; - // } - // } - // // Trapezoidal rule to calculate an integral - // var averageValue = ( - // spectralDataPointWavelengthBelow.Results.Transmittance - // + spectralDataPointWavelengthAbove.Results.Transmittance - // ) / 2; - // numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; - // denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; - // } - // return numerator / denominator; - // } - - // public double CalculateInterpolationBetweenSpectralDataPoints( public double Calculate( IReadOnlyList spectralDataPoints, ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights ) { + if (spectralDataPoints.Count == 0) + { + throw new ArgumentException("The list spectralDataPoints is empty."); + } var sortedSpectralDataPoints = spectralDataPoints .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) @@ -274,12 +238,9 @@ public double Calculate( .AsReadOnly(); var numerator = 0.0D; var denominator = 0.0D; - foreach (var wavelengthsWeight in wavelengthsWeights) + for (int i = 0; i < (wavelengthsWeights.Length - 1); i++) { - var spectralDataPointWavelengthBelow = new DataPoint( - new Incidence(new Wavelengths(0)), - new Results(99) - ); + var spectralDataPointWavelengthBelow = new DataPoint(new Incidence(new Wavelengths(0)), new Results(99)); var spectralDataPointWavelengthAbove = new DataPoint( new Incidence(new Wavelengths(1000000)), new Results(99) @@ -287,13 +248,13 @@ public double Calculate( foreach (var spectralDataPoint in sortedSpectralDataPoints) { // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeight.wavelength + if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeights[i].wavelength && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength ) { spectralDataPointWavelengthBelow = spectralDataPoint; } - if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength + if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeights[i].wavelength && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength ) { @@ -305,12 +266,65 @@ public double Calculate( spectralDataPointWavelengthBelow.Results.Transmittance + spectralDataPointWavelengthAbove.Results.Transmittance ) / 2; - numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; - denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + numerator += averageValue * wavelengthsWeights[i].deltaWavelength * wavelengthsWeights[i].weight; + denominator += wavelengthsWeights[i].deltaWavelength * wavelengthsWeights[i].weight; } + // Treat the last wavelengthWeight separately, because it has no spectralDataPointWavelengthAbove + numerator += spectralDataPoints[spectralDataPoints.Count() - 1].Results.Transmittance * wavelengthsWeights[wavelengthsWeights.Length - 1].deltaWavelength * wavelengthsWeights[wavelengthsWeights.Length - 1].weight; + denominator += wavelengthsWeights[wavelengthsWeights.Length - 1].deltaWavelength * wavelengthsWeights[wavelengthsWeights.Length - 1].weight; return numerator / denominator; } + // public double Calculate( + // IReadOnlyList spectralDataPoints, + // ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights + // ) + // { + // var sortedSpectralDataPoints = + // spectralDataPoints + // .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) + // .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) + // .ToList() + // .AsReadOnly(); + // var numerator = 0.0D; + // var denominator = 0.0D; + // foreach (var wavelengthsWeight in wavelengthsWeights) + // { + // var spectralDataPointWavelengthBelow = new DataPoint( + // new Incidence(new Wavelengths(0)), + // new Results(99) + // ); + // var spectralDataPointWavelengthAbove = new DataPoint( + // new Incidence(new Wavelengths(1000000)), + // new Results(99) + // ); + // foreach (var spectralDataPoint in sortedSpectralDataPoints) + // { + // // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint + // if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeight.wavelength + // && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength + // ) + // { + // spectralDataPointWavelengthBelow = spectralDataPoint; + // } + // if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength + // && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength + // ) + // { + // spectralDataPointWavelengthAbove = spectralDataPoint; + // } + // } + // // Trapezoidal rule to calculate an integral + // var averageValue = ( + // spectralDataPointWavelengthBelow.Results.Transmittance + // + spectralDataPointWavelengthAbove.Results.Transmittance + // ) / 2; + // numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + // denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; + // } + // return numerator / denominator; + // } + // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index c1fcb8c3..3cba828d 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -28,83 +28,28 @@ private static string ConstructFilePath(string relativePath) [SuppressMessage("Naming", "CA1707")] public void Calculate_None() { + // Arrange var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([]) ]); var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(99.0, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(98.99999999999999, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(98.99999999999999, 0.00000001F); - } - } - [Test] - [SuppressMessage("Naming", "CA1707")] - public void Calculate_One_200() - { - var spectralDataPoints = new SpectralToIntegralInput([ - new SpectralToIntegralData([ - new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), - ]) - ]); - var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); - } - } + // Act + Action act = () => method.Calculate(spectralDataPoints); - [Test] - [SuppressMessage("Naming", "CA1707")] - public void Calculate_One_300() - { - var spectralDataPoints = new SpectralToIntegralInput([ - new SpectralToIntegralData([ - new DataPoint(new Incidence(new Wavelengths(300)), new Results(1)), - ]) - ]); - var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); - } - } - - [Test] - [SuppressMessage("Naming", "CA1707")] - public void Calculate_One_400() - { - var spectralDataPoints = new SpectralToIntegralInput([ - new SpectralToIntegralData([ - new DataPoint(new Incidence(new Wavelengths(400)), new Results(1)), - ]) - ]); - var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); - } + // Assert + act.Should() + .Throw() + .WithMessage("The list spectralDataPoints is empty."); } [Test] [SuppressMessage("Naming", "CA1707")] - public void Calculate_One_2500() + public void Calculate_One() { var spectralDataPoints = new SpectralToIntegralInput([ new SpectralToIntegralData([ - new DataPoint(new Incidence(new Wavelengths(2500)), new Results(1)), + new DataPoint(new Incidence(new Wavelengths(200)), new Results(1)), ]) ]); var method = new SpectralToIntegralMethod(); @@ -112,8 +57,8 @@ public void Calculate_One_2500() using (new AssertionScope()) { results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(50.0, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(50.0, 0.00000001F); + results.En410Solar.Should().BeApproximately(49.95456161229, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(49.887486078556094, 0.00000001F); } } @@ -139,55 +84,6 @@ public void Calculate_Few() } } - [Test] - [SuppressMessage("Naming", "CA1707")] - public async Task Calculate_modification02() - { - using var fileStream = File.OpenRead( - ConstructFilePath("./Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") - ); - using var jsonDocument = await JsonDocument.ParseAsync( - fileStream, - JsonDocumentSettings.Lax - ); - var spectralDataPoints = jsonDocument.RootElement.Deserialize( - MethodBase.JsonSerializerOptions - ) ?? throw new InvalidOperationException(); - var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); - } - } - - [Test] - [SuppressMessage("Naming", "CA1707")] - public async Task Calculate_modification03() - { - using var fileStream = File.OpenRead( - ConstructFilePath("./Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") - ); - using var jsonDocument = await JsonDocument.ParseAsync( - fileStream, - JsonDocumentSettings.Lax - ); - var spectralDataPoints = jsonDocument.RootElement.Deserialize( - MethodBase.JsonSerializerOptions - ) ?? throw new InvalidOperationException(); - var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); - } - } - - [Test] [SuppressMessage("Naming", "CA1707")] public async Task Calculate_Many() @@ -207,8 +103,8 @@ public async Task Calculate_Many() using (new AssertionScope()) { results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.9080345756086065, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.8593226930274324, 0.00000001F); + results.En410Solar.Should().BeApproximately(0.8617466755155275, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8573906338464905, 0.00000001F); } } } \ No newline at end of file From c0ef9f48e5eeddc42a8a75018100f1a0e468e968 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 24 Nov 2025 16:26:12 +0100 Subject: [PATCH 584/878] `Calculate_Many()` throws an exception! --- .../SpectralToIntegralMethod.cs | 6 ++++-- .../Methods/SpectralToIntegralMethodTests.cs | 17 +++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index 96977d38..e271c22e 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -226,9 +226,9 @@ public double Calculate( ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights ) { - if (spectralDataPoints.Count == 0) + if (spectralDataPoints == null || spectralDataPoints.Count == 0) { - throw new ArgumentException("The list spectralDataPoints is empty."); + throw new ArgumentException("The list `spectralDataPoints` is empty."); } var sortedSpectralDataPoints = spectralDataPoints @@ -260,6 +260,8 @@ public double Calculate( { spectralDataPointWavelengthAbove = spectralDataPoint; } + if (spectralDataPointWavelengthBelow.Results.Transmittance == 99) { throw new ArgumentException("`spectralDataPoints` has no data point for the smallest wavelength of `wavelengthsWeights`."); } + if (spectralDataPointWavelengthAbove.Results.Transmittance == 99) { throw new ArgumentException("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); } } // Trapezoidal rule to calculate an integral var averageValue = ( diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 3cba828d..30f48475 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -40,7 +40,7 @@ public void Calculate_None() // Assert act.Should() .Throw() - .WithMessage("The list spectralDataPoints is empty."); + .WithMessage("The list `spectralDataPoints` is empty."); } [Test] @@ -53,13 +53,14 @@ public void Calculate_One() ]) ]); var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(50.00000000000002, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(49.95456161229, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(49.887486078556094, 0.00000001F); - } + + // Act + Action act = () => method.Calculate(spectralDataPoints); + + // Assert + act.Should() + .Throw() + .WithMessage("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); } [Test] From 0e12c527c242dae5a2a59da02f0bdc7c1eb99b98 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 24 Nov 2025 21:05:23 +0100 Subject: [PATCH 585/878] Use eager execution with data loaders --- backend/src/GraphQl/DataMutationBase.cs | 75 ++++++++----------- .../GraphQl/DataX/CreateDataMutationBase.cs | 2 + 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index 0a6a42b8..ba5e0fa3 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -11,6 +11,7 @@ using Database.Extensions; using System.Linq; using System.Globalization; +using HotChocolate; namespace Database.GraphQl; @@ -86,56 +87,46 @@ protected async Task>> ValidateGetHttps CancellationToken cancellationToken ) { - var validateDataFormats = new Task>[1 + input.ArchivedFilesMetaInformation.Count]; - validateDataFormats[0] = Task.Run>(async () => - { - var dataFormat = await dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); - if (dataFormat is null) - { - return new Result.Error( - NewError( - unknownDataFormatErrorCode, - "The data format does not exist", - [.. getHttpsResourcePath, nameof(input.DataFormatId).ToLowerFirst()] - ) - ); - } - return new Result.Data(dataFormat); - }, - cancellationToken - ); + var validateDataFormats = new Task[1 + input.ArchivedFilesMetaInformation.Count]; + // First eagerly execute all `LoadAsync`s on data loaders to make it know all keys. + validateDataFormats[0] = dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); foreach (var (archivedFileMetaInformation, index) in input.ArchivedFilesMetaInformation.WithIndex()) { - validateDataFormats[index + 1] = Task.Run>(async () => - { - var dataFormat = await dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); - if (dataFormat is null) - { - // TODO Is the index in the path formated correctly or should it be enclosed in square brackets? - return new Result.Error( - NewError( - unknownDataFormatErrorCode, - "The data format does not exist", - [.. getHttpsResourcePath, nameof(input.ArchivedFilesMetaInformation).ToLowerFirst(), index.ToString(CultureInfo.InvariantCulture), nameof(archivedFileMetaInformation.DataFormatId).ToLowerFirst()] - ) - ); - } - return new Result.Data(dataFormat); - }, - cancellationToken + validateDataFormats[index + 1] = dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); + } + // Then await all "loads". + var dataFormats = await Task.WhenAll(validateDataFormats); + // Finally collect errors. + var errors = new List(); + var dataFormat = dataFormats[1]; + if (dataFormat is null) + { + errors.Add( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [.. getHttpsResourcePath, nameof(input.DataFormatId).ToLowerFirst()] + ) ); } - var maybeDataFormats = await Task.WhenAll(validateDataFormats); - var errors = maybeDataFormats - .Select(_ => _.Failed(out var error) ? error : null) - .NotNull() - .ToList() - .AsReadOnly(); + foreach (var (archivedFileDataFormat, index) in dataFormats[1..].WithIndex()) + { + if (archivedFileDataFormat is null) + { + // TODO Is the index in the path formated correctly or should it be enclosed in square brackets? + errors.Add( + NewError( + unknownDataFormatErrorCode, + "The data format does not exist", + [.. getHttpsResourcePath, nameof(input.ArchivedFilesMetaInformation).ToLowerFirst(), index.ToString(CultureInfo.InvariantCulture), nameof(FileMetaInformationInput.DataFormatId).ToLowerFirst()] + ) + ); + } + } if (errors.Count >= 1) { return new Result>.Error(errors); } - maybeDataFormats[0].Succeeded(out var dataFormat); return new Result>.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index c7b6f71d..9288fa87 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -8,6 +8,7 @@ using static Database.ApiRequests.DataFormatDataLoader; using System.Linq; using System.Globalization; +using System.Collections.Immutable; namespace Database.GraphQl.DataX; @@ -92,6 +93,7 @@ CancellationToken cancellationToken cancellationToken ) ) + .ToImmutableArray() // Execute eagerly to have the data loader collect all keys first. ); foreach (var (database, index) in databases.WithIndex()) { From 9c16a6a1339bb4a7c53a41e1a6f52333cff14371 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 25 Nov 2025 16:04:21 +0100 Subject: [PATCH 586/878] Build the frontend first as right now it requires more resources --- Makefile.production | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile.production b/Makefile.production index abc40899..a7bccd42 100644 --- a/Makefile.production +++ b/Makefile.production @@ -61,14 +61,14 @@ build : check pull ## Build images DOCKER_BUILDKIT=1 \ ${docker_compose} build \ --pull \ - backend + frontend # --no-cache COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ --pull \ - frontend + backend # --no-cache .PHONY : build From 1871db97f2db58cba9ca468f8e34d963ae42987e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 25 Nov 2025 19:13:20 +0100 Subject: [PATCH 587/878] Extract g-values and u-values for calorimetric data and widths, heights, and thicknesses for geometric data --- .../src/Controllers/FileUploadController.cs | 4 +- backend/src/Data/CalorimetricData.cs | 18 + backend/src/Data/DataX.cs | 8 +- backend/src/Data/GeometricData.cs | 31 + backend/src/Data/HygrothermalData.cs | 12 + backend/src/Data/IData.cs | 8 + backend/src/Data/OpticalData.cs | 16 +- backend/src/Data/PhotovoltaicData.cs | 12 + .../DataPointsEnumerationsExtensions.cs | 2 +- ...bleResultsCalorimetricDataJsonExtractor.cs | 36 + ... DoubleResultsOpticalDataJsonExtractor.cs} | 5 +- ...ledDimensionsGeometricDataJsonExtractor.cs | 38 + .../CreateGeometricDataMutation.cs | 4 + ...rWidthAndHeightOfGeometricData.Designer.cs | 2706 +++++++++++++++++ ...102_MirrorWidthAndHeightOfGeometricData.cs | 44 + .../ApplicationDbContextModelSnapshot.cs | 8 + backend/src/Migrations/migrate.sql | 25 + ...02_MirrorWidthAndHeightOfGeometricData.sql | 25 + ...113172901_CheckResourcesForConsistency.sql | 22 + 19 files changed, 3008 insertions(+), 16 deletions(-) create mode 100644 backend/src/Extractors/DoubleResultsCalorimetricDataJsonExtractor.cs rename backend/src/Extractors/{DoubleResultsJsonExtractor.cs => DoubleResultsOpticalDataJsonExtractor.cs} (84%) create mode 100644 backend/src/Extractors/InstalledDimensionsGeometricDataJsonExtractor.cs create mode 100644 backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs create mode 100644 backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs create mode 100644 backend/src/Migrations/migrate_from_20251113172901_CheckResourcesForConsistency_to_20251125181102_MirrorWidthAndHeightOfGeometricData.sql create mode 100644 backend/src/Migrations/rollback_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20251113172901_CheckResourcesForConsistency.sql diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index c09e680d..3644781c 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -185,9 +185,9 @@ CancellationToken cancellationToken // read the headers for the next section. section = await reader.ReadNextSectionAsync(cancellationToken); } - if (getHttpsResource.IsRoot() && getHttpsResource.OpticalData is not null) + if (getHttpsResource.IsRoot()) { - await getHttpsResource.OpticalData.ExtractAndSetMirroredValuesFromFile( + await getHttpsResource.Data.ExtractAndSetValuesFromFile( getHttpsResource.FilePath, getHttpsResource.DataFormatId ); diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index c372934d..d8701689 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; +using Database.Extractors; namespace Database.Data; @@ -67,4 +69,20 @@ double[] uValues public double[] GValues { get; private set; } public double[] UValues { get; private set; } + + public override async Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + if (dataFormatId == IData.BedJsonDataFormatId) + { + GValues = await new DoubleResultsCalorimetricDataJsonExtractor( + CalorimetricResult.G_VALUE + ).Extract(filePath); + UValues = await new DoubleResultsCalorimetricDataJsonExtractor( + CalorimetricResult.U_VALUE + ).Extract(filePath); + } + } } \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 67145a5e..18203160 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Database.Enumerations; namespace Database.Data; @@ -85,12 +86,17 @@ public void Retract() // TODO Exactly one resource must not have a parent and each other resource must have one from // this list and the graph must be connected. In other words, the resources must form a tree. - public virtual ICollection Resources { get; } = []; + public abstract ICollection Resources { get; } public DataAccessRights DataAccessRights { get; private set; } = new DataAccessRights(); public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; + public abstract Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ); + /// public bool IsRestrictedByApplication(string applicationId) { diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 750d0608..71f3c054 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; +using Database.Extractors; namespace Database.Data; @@ -17,6 +19,8 @@ public GeometricData( Guid creatorId, DateTime createdAt, AppliedMethod appliedMethod, + double[] widths, + double[] heights, double[] thicknesses ) : base( userId, @@ -30,6 +34,8 @@ double[] thicknesses appliedMethod ) { + Widths = widths; + Heights = heights; Thicknesses = thicknesses; } @@ -42,6 +48,8 @@ public GeometricData( string[] warnings, Guid creatorId, DateTime createdAt, + double[] widths, + double[] heights, double[] thicknesses ) : base( userId, @@ -54,11 +62,34 @@ double[] thicknesses createdAt ) { + Widths = widths; + Heights = heights; Thicknesses = thicknesses; } [InverseProperty(nameof(GetHttpsResource.GeometricData))] public override ICollection Resources { get; } = []; + public double[] Widths { get; private set; } + public double[] Heights { get; private set; } public double[] Thicknesses { get; private set; } + + public override async Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + if (dataFormatId == IData.BedJsonDataFormatId) + { + Widths = await new InstalledDimensionsGeometricDataJsonExtractor( + Dimension.WIDTH + ).Extract(filePath); + Heights = await new InstalledDimensionsGeometricDataJsonExtractor( + Dimension.HEIGHT + ).Extract(filePath); + Thicknesses = await new InstalledDimensionsGeometricDataJsonExtractor( + Dimension.THICKNESS + ).Extract(filePath); + } + } } \ No newline at end of file diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index c30ae539..ea1fc6ab 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; namespace Database.Data; @@ -56,4 +57,15 @@ DateTime createdAt [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] public override ICollection Resources { get; } = []; + + public override Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; + } } \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 802105d4..dcac8a89 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using System.Threading.Tasks; using Database.Enumerations; using Database.GraphQl; @@ -14,6 +15,8 @@ namespace Database.Data; [JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] public interface IData : IEntity { + public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); + Guid? UserId { get; } Guid ComponentId { get; } string? Name { get; } @@ -64,4 +67,9 @@ Guid creatorId void Publish(); void Retract(); + + Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ); } \ No newline at end of file diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 11762b9e..e5e491b4 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -11,8 +11,6 @@ namespace Database.Data; public sealed class OpticalData : DataX { - public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); - public OpticalData( Guid? userId, string locale, @@ -112,43 +110,43 @@ double[] colorRenderingIndices public double[] ColorRenderingIndices { get; private set; } public ICollection CielabColors { get; private set; } = []; - public async Task ExtractAndSetMirroredValuesFromFile( + public override async Task ExtractAndSetValuesFromFile( string filePath, Guid dataFormatId ) { - if (dataFormatId == BedJsonDataFormatId) + if (dataFormatId == IData.BedJsonDataFormatId) { NearnormalHemisphericalVisibleTransmittances = - await new DoubleResultsJsonExtractor( + await new DoubleResultsOpticalDataJsonExtractor( IncidenceDirection.NEARNORMAL, WavelengthsIntegral.VISIBLE, EmergenceDirection.HEMISPHERICAL, DataPointResult.TRANSMITTANCE ).Extract(filePath); NearnormalHemisphericalVisibleReflectances = - await new DoubleResultsJsonExtractor( + await new DoubleResultsOpticalDataJsonExtractor( IncidenceDirection.NEARNORMAL, WavelengthsIntegral.VISIBLE, EmergenceDirection.HEMISPHERICAL, DataPointResult.REFLECTANCE ).Extract(filePath); NearnormalHemisphericalSolarTransmittances = - await new DoubleResultsJsonExtractor( + await new DoubleResultsOpticalDataJsonExtractor( IncidenceDirection.NEARNORMAL, WavelengthsIntegral.SOLAR, EmergenceDirection.HEMISPHERICAL, DataPointResult.TRANSMITTANCE ).Extract(filePath); NearnormalHemisphericalSolarReflectances = - await new DoubleResultsJsonExtractor( + await new DoubleResultsOpticalDataJsonExtractor( IncidenceDirection.NEARNORMAL, WavelengthsIntegral.SOLAR, EmergenceDirection.HEMISPHERICAL, DataPointResult.REFLECTANCE ).Extract(filePath); InfraredEmittances = - await new DoubleResultsJsonExtractor( + await new DoubleResultsOpticalDataJsonExtractor( IncidenceDirection.HEMISPHERICAL, WavelengthsIntegral.INFRARED, EmergenceDirection.HEMISPHERICAL, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 0d159caa..69f5f6f7 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; namespace Database.Data; @@ -56,4 +57,15 @@ DateTime createdAt [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] public override ICollection Resources { get; } = []; + + public override Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; + } } \ No newline at end of file diff --git a/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs index 8114d85b..a4645cf3 100644 --- a/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs +++ b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs @@ -37,7 +37,7 @@ public static string ToJsonEnum(this EmergenceDirection emergenceDirection) }; } - public static string ToJsonEnum(this DataPointResult result) + public static string ToJsonProperty(this DataPointResult result) { return result switch { diff --git a/backend/src/Extractors/DoubleResultsCalorimetricDataJsonExtractor.cs b/backend/src/Extractors/DoubleResultsCalorimetricDataJsonExtractor.cs new file mode 100644 index 00000000..c72a325e --- /dev/null +++ b/backend/src/Extractors/DoubleResultsCalorimetricDataJsonExtractor.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Json.Path; + +namespace Database.Extractors; + +[SuppressMessage("Naming", "CA1707")] +public enum CalorimetricResult +{ + G_VALUE, // Solar Gain + U_VALUE // CalorimetricResult +} + +public static class CalorimetricResultExtensions +{ + public static string ToJsonProperty(this CalorimetricResult result) + { + return result switch + { + CalorimetricResult.U_VALUE => "uValue", + CalorimetricResult.G_VALUE => "gValue", + _ => throw new ArgumentOutOfRangeException(nameof(result), $"Unsupported result {result}."), + }; + } +} + +public sealed class DoubleResultsCalorimetricDataJsonExtractor( + CalorimetricResult result +) : StructJsonPathExtractor( + JsonPath.Parse( + $"$.results[*].{result.ToJsonProperty()}" + ), + ExtractNumberWithUncertainty +) +{ +} \ No newline at end of file diff --git a/backend/src/Extractors/DoubleResultsJsonExtractor.cs b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs similarity index 84% rename from backend/src/Extractors/DoubleResultsJsonExtractor.cs rename to backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs index 8ab1a2f7..ace3d211 100644 --- a/backend/src/Extractors/DoubleResultsJsonExtractor.cs +++ b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs @@ -1,10 +1,9 @@ using Json.Path; using Database.Enumerations.DataPoints; -using System.Text.Json; namespace Database.Extractors; -public sealed class DoubleResultsJsonExtractor( +public sealed class DoubleResultsOpticalDataJsonExtractor( IncidenceDirection incidenceDirection, WavelengthsIntegral incidenceWavelengthsIntegral, EmergenceDirection emergenceDirection, @@ -15,7 +14,7 @@ DataPointResult result $"@.incidence.direction{incidenceDirection.ToJsonPathQuery()} && " + $"@.incidence.wavelengths.integral=='{incidenceWavelengthsIntegral.ToJsonEnum()}' && " + $"@.emergence.direction=='{emergenceDirection.ToJsonEnum()}'" + - $")].results.{result.ToJsonEnum()}" + $")].results.{result.ToJsonProperty()}" ), ExtractNumberWithUncertainty ) diff --git a/backend/src/Extractors/InstalledDimensionsGeometricDataJsonExtractor.cs b/backend/src/Extractors/InstalledDimensionsGeometricDataJsonExtractor.cs new file mode 100644 index 00000000..4ed890c5 --- /dev/null +++ b/backend/src/Extractors/InstalledDimensionsGeometricDataJsonExtractor.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Json.Path; + +namespace Database.Extractors; + +[SuppressMessage("Naming", "CA1707")] +public enum Dimension +{ + WIDTH, + HEIGHT, + THICKNESS +} + +public static class DimensionExtensions +{ + public static string ToJsonProperty(this Dimension dimension) + { + return dimension switch + { + Dimension.WIDTH => "width", + Dimension.HEIGHT => "height", + Dimension.THICKNESS => "thickness", + _ => throw new ArgumentOutOfRangeException(nameof(dimension), $"Unsupported dimension {dimension}."), + }; + } +} + +public sealed class InstalledDimensionsGeometricDataJsonExtractor( + Dimension dimension +) : StructJsonPathExtractor( + JsonPath.Parse( + $"$.dimensions.installed[*].{dimension.ToJsonProperty()}" + ), + ExtractNumberWithUncertainty +) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index f3c30df3..8d1205a1 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -26,6 +26,8 @@ public sealed record CreateGeometricDataInput( Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, + double[] Widths, + double[] Heights, double[] Thicknesses ) : IValidateCreateInput { @@ -44,6 +46,8 @@ public GeometricData ToDomainModel( CreatorId, CreatedAt, AppliedMethod.ToDomainModel(), + Widths, + Heights, Thicknesses ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs new file mode 100644 index 00000000..f7528f17 --- /dev/null +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs @@ -0,0 +1,2706 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20251125181102_MirrorWidthAndHeightOfGeometricData")] + partial class MirrorWidthAndHeightOfGeometricData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs new file mode 100644 index 00000000..73f90eb7 --- /dev/null +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MirrorWidthAndHeightOfGeometricData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Heights", + schema: "database", + table: "geometric_data", + type: "double precision[]", + nullable: false, + defaultValue: new double[0]); + + migrationBuilder.AddColumn( + name: "Widths", + schema: "database", + table: "geometric_data", + type: "double precision[]", + nullable: false, + defaultValue: new double[0]); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Heights", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "Widths", + schema: "database", + table: "geometric_data"); + } + } +} diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 2d8ecc07..372da4b6 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -109,6 +109,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Description") .HasColumnType("text"); + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + b.Property("Locale") .IsRequired() .HasColumnType("text"); @@ -136,6 +140,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + b.HasKey("Id"); b.ToTable("geometric_data", "database"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 1bf231b9..64a79e61 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1802,3 +1802,28 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data ADD "Heights" double precision[] NOT NULL DEFAULT ARRAY[]::double precision[]; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data ADD "Widths" double precision[] NOT NULL DEFAULT ARRAY[]::double precision[]; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251125181102_MirrorWidthAndHeightOfGeometricData', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251113172901_CheckResourcesForConsistency_to_20251125181102_MirrorWidthAndHeightOfGeometricData.sql b/backend/src/Migrations/migrate_from_20251113172901_CheckResourcesForConsistency_to_20251125181102_MirrorWidthAndHeightOfGeometricData.sql new file mode 100644 index 00000000..97df916b --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251113172901_CheckResourcesForConsistency_to_20251125181102_MirrorWidthAndHeightOfGeometricData.sql @@ -0,0 +1,25 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data ADD "Heights" double precision[] NOT NULL DEFAULT ARRAY[]::double precision[]; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data ADD "Widths" double precision[] NOT NULL DEFAULT ARRAY[]::double precision[]; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20251125181102_MirrorWidthAndHeightOfGeometricData', '9.0.10'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20251113172901_CheckResourcesForConsistency.sql b/backend/src/Migrations/rollback_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20251113172901_CheckResourcesForConsistency.sql new file mode 100644 index 00000000..e134c19a --- /dev/null +++ b/backend/src/Migrations/rollback_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20251113172901_CheckResourcesForConsistency.sql @@ -0,0 +1,22 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data DROP COLUMN "Heights"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + ALTER TABLE database.geometric_data DROP COLUMN "Widths"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20251125181102_MirrorWidthAndHeightOfGeometricData'; + END IF; +END $EF$; +COMMIT; + From 548d037f8eab7fcffbdd52124972c769fe3fce72 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 26 Nov 2025 11:53:25 +0100 Subject: [PATCH 588/878] When I throw and catch an exception in `Calculate()`, `Calculate_Many` passes --- .../SpectralToIntegralMethod.cs | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index e271c22e..52657f98 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -248,20 +248,41 @@ public double Calculate( foreach (var spectralDataPoint in sortedSpectralDataPoints) { // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeights[i].wavelength - && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength - ) + try { - spectralDataPointWavelengthBelow = spectralDataPoint; + if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeights[i].wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength + ) + { + spectralDataPointWavelengthBelow = spectralDataPoint; + } + if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeights[i].wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength + ) + { + spectralDataPointWavelengthAbove = spectralDataPoint; + } + if (spectralDataPointWavelengthBelow.Results.Transmittance == 99) + { + + throw new ArgumentException("`spectralDataPoints` has no data point for the smallest wavelength of `wavelengthsWeights`."); + } + if (spectralDataPointWavelengthAbove.Results.Transmittance == 99) + { + + throw new ArgumentException("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); + } } - if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeights[i].wavelength - && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - ) + catch (ArgumentException ex) { - spectralDataPointWavelengthAbove = spectralDataPoint; + Console.WriteLine($"spectralDataPointWavelengthBelow.Results.Transmittance {spectralDataPointWavelengthBelow.Results.Transmittance}"); + Console.WriteLine($"wavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}"); + Console.WriteLine($"spectralDataPoint.Incidence.Wavelengths.Wavelength {spectralDataPoint.Incidence.Wavelengths.Wavelength}"); + + Console.WriteLine($"spectralDataPointWavelengthAbove.Results.Transmittance {spectralDataPointWavelengthAbove.Results.Transmittance}"); + Console.WriteLine($"wavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}"); + Console.WriteLine($"spectralDataPoint.Incidence.Wavelengths.Wavelength {spectralDataPoint.Incidence.Wavelengths.Wavelength}"); } - if (spectralDataPointWavelengthBelow.Results.Transmittance == 99) { throw new ArgumentException("`spectralDataPoints` has no data point for the smallest wavelength of `wavelengthsWeights`."); } - if (spectralDataPointWavelengthAbove.Results.Transmittance == 99) { throw new ArgumentException("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); } } // Trapezoidal rule to calculate an integral var averageValue = ( From d156aaa6b0c0dc283ed08f5227381b697c83315d Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 26 Nov 2025 12:24:44 +0100 Subject: [PATCH 589/878] Throw exception after iteration of spectralDataPoints --- .../SpectralToIntegralMethod.cs | 98 +++---------------- .../Methods/SpectralToIntegralMethodTests.cs | 17 +++- 2 files changed, 28 insertions(+), 87 deletions(-) diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index 52657f98..e58b468f 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -247,43 +247,27 @@ public double Calculate( ); foreach (var spectralDataPoint in sortedSpectralDataPoints) { - // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - try + if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeights[i].wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength + ) { - if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeights[i].wavelength - && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength - ) - { - spectralDataPointWavelengthBelow = spectralDataPoint; - } - if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeights[i].wavelength - && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - ) - { - spectralDataPointWavelengthAbove = spectralDataPoint; - } - if (spectralDataPointWavelengthBelow.Results.Transmittance == 99) - { - - throw new ArgumentException("`spectralDataPoints` has no data point for the smallest wavelength of `wavelengthsWeights`."); - } - if (spectralDataPointWavelengthAbove.Results.Transmittance == 99) - { - - throw new ArgumentException("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); - } + spectralDataPointWavelengthBelow = spectralDataPoint; } - catch (ArgumentException ex) + if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeights[i].wavelength + && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength + ) { - Console.WriteLine($"spectralDataPointWavelengthBelow.Results.Transmittance {spectralDataPointWavelengthBelow.Results.Transmittance}"); - Console.WriteLine($"wavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}"); - Console.WriteLine($"spectralDataPoint.Incidence.Wavelengths.Wavelength {spectralDataPoint.Incidence.Wavelengths.Wavelength}"); - - Console.WriteLine($"spectralDataPointWavelengthAbove.Results.Transmittance {spectralDataPointWavelengthAbove.Results.Transmittance}"); - Console.WriteLine($"wavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}"); - Console.WriteLine($"spectralDataPoint.Incidence.Wavelengths.Wavelength {spectralDataPoint.Incidence.Wavelengths.Wavelength}"); + spectralDataPointWavelengthAbove = spectralDataPoint; } } + if (spectralDataPointWavelengthBelow.Results.Transmittance == 99) + { + throw new ArgumentException($"`spectralDataPoints` has no data point for the smallest wavelength of `wavelengthsWeights`.\nwavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}\nspectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength {spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength}\nspectralDataPointWavelengthBelow.Results.Transmittance {spectralDataPointWavelengthBelow.Results.Transmittance}\nspectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength {spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength}\nspectralDataPointWavelengthAbove.Results.Transmittance {spectralDataPointWavelengthAbove.Results.Transmittance}\n"); + } + if (spectralDataPointWavelengthAbove.Results.Transmittance == 99) + { + throw new ArgumentException($"`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`.\nwavelengthsWeights[i].wavelength {wavelengthsWeights[i].wavelength}\nspectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength {spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength}\nspectralDataPointWavelengthBelow.Results.Transmittance {spectralDataPointWavelengthBelow.Results.Transmittance}\nspectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength {spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength}\nspectralDataPointWavelengthAbove.Results.Transmittance {spectralDataPointWavelengthAbove.Results.Transmittance}\n"); + } // Trapezoidal rule to calculate an integral var averageValue = ( spectralDataPointWavelengthBelow.Results.Transmittance @@ -298,56 +282,6 @@ public double Calculate( return numerator / denominator; } - // public double Calculate( - // IReadOnlyList spectralDataPoints, - // ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights - // ) - // { - // var sortedSpectralDataPoints = - // spectralDataPoints - // .Where(dataPoint => !WavelengthOutOfBounds(dataPoint)) - // .OrderBy(dataPoint => dataPoint.Incidence.Wavelengths.Wavelength) - // .ToList() - // .AsReadOnly(); - // var numerator = 0.0D; - // var denominator = 0.0D; - // foreach (var wavelengthsWeight in wavelengthsWeights) - // { - // var spectralDataPointWavelengthBelow = new DataPoint( - // new Incidence(new Wavelengths(0)), - // new Results(99) - // ); - // var spectralDataPointWavelengthAbove = new DataPoint( - // new Incidence(new Wavelengths(1000000)), - // new Results(99) - // ); - // foreach (var spectralDataPoint in sortedSpectralDataPoints) - // { - // // Find the spectralDataPoints with the wavelengths which are the closest to the wavelength of the weightingDataPoint - // if (spectralDataPoint.Incidence.Wavelengths.Wavelength <= wavelengthsWeight.wavelength - // && spectralDataPoint.Incidence.Wavelengths.Wavelength > spectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength - // ) - // { - // spectralDataPointWavelengthBelow = spectralDataPoint; - // } - // if (spectralDataPoint.Incidence.Wavelengths.Wavelength > wavelengthsWeight.wavelength - // && spectralDataPoint.Incidence.Wavelengths.Wavelength < spectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength - // ) - // { - // spectralDataPointWavelengthAbove = spectralDataPoint; - // } - // } - // // Trapezoidal rule to calculate an integral - // var averageValue = ( - // spectralDataPointWavelengthBelow.Results.Transmittance - // + spectralDataPointWavelengthAbove.Results.Transmittance - // ) / 2; - // numerator += averageValue * wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; - // denominator += wavelengthsWeight.deltaWavelength * wavelengthsWeight.weight; - // } - // return numerator / denominator; - // } - // Search predicate returns true if the wavelength is smaller than 0 nm or larger than 3000 nm. private static bool WavelengthOutOfBounds(DataPoint dataPoint) { diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 30f48475..6e0f8067 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -100,12 +100,19 @@ public async Task Calculate_Many() MethodBase.JsonSerializerOptions ) ?? throw new InvalidOperationException(); var method = new SpectralToIntegralMethod(); - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) + try + { + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) + { + results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.8617466755155275, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8573906338464905, 0.00000001F); + } + } + catch (ArgumentException ex) { - results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.8617466755155275, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.8573906338464905, 0.00000001F); + Console.WriteLine(ex.Message); } } } \ No newline at end of file From 42153d41298efae00de47f617e6e7b63189e9f0f Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 26 Nov 2025 13:14:15 +0100 Subject: [PATCH 590/878] Fix test Calculate_One() --- backend/test/Methods/SpectralToIntegralMethodTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 6e0f8067..a33cea64 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -60,7 +60,7 @@ public void Calculate_One() // Assert act.Should() .Throw() - .WithMessage("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`."); + .WithMessage("`spectralDataPoints` has no data point for the largest wavelength of `wavelengthsWeights`.\nwavelengthsWeights[i].wavelength 380\nspectralDataPointWavelengthBelow.Incidence.Wavelengths.Wavelength 200\nspectralDataPointWavelengthBelow.Results.Transmittance 1\nspectralDataPointWavelengthAbove.Incidence.Wavelengths.Wavelength 1000000\nspectralDataPointWavelengthAbove.Results.Transmittance 99\n"); } [Test] From ee0c5b0080139620755b251447e3b4cc18121278 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 26 Nov 2025 13:16:11 +0100 Subject: [PATCH 591/878] Delete unnecessary modifications of the spectrally-resolved data --- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 8391 ---------------- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 8429 ----------------- ..._2d40b285-79d4-4ec6-8d46-767bd9b0f249.json | 8391 ---------------- 3 files changed, 25211 deletions(-) delete mode 100644 backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json delete mode 100644 backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json delete mode 100644 backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json diff --git a/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json deleted file mode 100644 index da0633d6..00000000 --- a/backend/test/Methods/modification01_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json +++ /dev/null @@ -1,8391 +0,0 @@ -{ - "data": [ - { - "componentCharacteristics": { - "surface": "prime", - "symmetries": "azimuthalAngleInvariance" - }, - "dataPoints": [ - { - "incidence": { - "wavelengths": { - "wavelength": 300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0476000010967255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0303000006824732, - "reflectance": 0.0472000017762184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0931999981403351, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.191599994897842, - "reflectance": 0.0485999993979931 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.318300008773804, - "reflectance": 0.0516999997198582 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.453700006008148, - "reflectance": 0.0568000003695488 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.576499998569489, - "reflectance": 0.0628999993205071 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.676800012588501, - "reflectance": 0.0689999982714653 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.751299977302551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.804400026798248, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861100018024445, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873399972915649, - "reflectance": 0.0829000025987625 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879499971866608, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888599991798401, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896899998188019, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896650016307831, - "reflectance": 0.0832000002264977 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896399974822998, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895399987697601, - "reflectance": 0.0827500000596046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894400000572205, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0823500007390976 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894200026988983, - "reflectance": 0.0821999981999397 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894649982452393, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0820000022649765 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896250009536743, - "reflectance": 0.0819500014185906 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89740002155304, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898800015449524, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900200009346008, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900900006294251, - "reflectance": 0.0818499997258186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0817999988794327 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901899993419647, - "reflectance": 0.0817499980330467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902199983596802, - "reflectance": 0.0816999971866608 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903299987316132, - "reflectance": 0.0816499963402748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904399991035461, - "reflectance": 0.0816000029444695 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904799997806549, - "reflectance": 0.0815500020980835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905200004577637, - "reflectance": 0.0815000012516975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905700027942658, - "reflectance": 0.0814500004053116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.906199991703033, - "reflectance": 0.0813999995589256 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905849993228912, - "reflectance": 0.0812999978661537 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905499994754791, - "reflectance": 0.0812000036239624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905399978160858, - "reflectance": 0.0811000019311905 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90530002117157, - "reflectance": 0.0810000002384186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90514999628067, - "reflectance": 0.0808999985456467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807999968528748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807500034570694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807000026106834 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904600024223328, - "reflectance": 0.0806000009179115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90420001745224, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903699994087219, - "reflectance": 0.0803999975323677 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903199970722199, - "reflectance": 0.0803000032901764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902400016784668, - "reflectance": 0.0801500007510185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0799999982118607 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901000022888184, - "reflectance": 0.0798999965190888 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90039998292923, - "reflectance": 0.0798000022768974 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.899649977684021, - "reflectance": 0.0797000005841255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898899972438812, - "reflectance": 0.0795999988913536 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898249983787537, - "reflectance": 0.0794999971985817 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.897599995136261, - "reflectance": 0.0794000029563904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896849989891052, - "reflectance": 0.0793000012636185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896099984645844, - "reflectance": 0.0791999995708466 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0790499970316887 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89410001039505, - "reflectance": 0.0789000019431114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.893450021743774, - "reflectance": 0.0788000002503395 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.892799973487854, - "reflectance": 0.0786999985575676 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89190000295639, - "reflectance": 0.0785999968647957 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.890999972820282, - "reflectance": 0.0785000026226044 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89045000076294, - "reflectance": 0.0784000009298325 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889900028705597, - "reflectance": 0.0782999992370606 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889150023460388, - "reflectance": 0.0781999975442886 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888400018215179, - "reflectance": 0.0781000033020973 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887750029563904, - "reflectance": 0.0780500024557114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887099981307983, - "reflectance": 0.0780000016093254 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.886399984359741, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.885699987411499, - "reflectance": 0.0777999982237816 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.884800016880035, - "reflectance": 0.0776999965310097 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.883899986743927, - "reflectance": 0.0776000022888184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.882799983024597, - "reflectance": 0.0774499997496605 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.881699979305267, - "reflectance": 0.0772999972105026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.88085001707077, - "reflectance": 0.0772000029683113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879999995231628, - "reflectance": 0.0771000012755394 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878650009632111, - "reflectance": 0.0769499987363815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877300024032593, - "reflectance": 0.0768000036478043 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875999987125397, - "reflectance": 0.0767000019550324 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0766000002622604 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873849987983704, - "reflectance": 0.0764499977231026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873000025749207, - "reflectance": 0.0763000026345253 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872150003910065, - "reflectance": 0.0762000009417534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871299982070923, - "reflectance": 0.0760999992489815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869650006294251, - "reflectance": 0.0759499967098236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867999970912933, - "reflectance": 0.0758000016212463 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867299973964691, - "reflectance": 0.0756999999284744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866599977016449, - "reflectance": 0.0755999982357025 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865100026130676, - "reflectance": 0.0754500031471252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863600015640259, - "reflectance": 0.0753000006079674 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86269998550415, - "reflectance": 0.0751999989151955 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861800014972687, - "reflectance": 0.0750999972224236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861150026321411, - "reflectance": 0.0750000029802322 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860499978065491, - "reflectance": 0.0749000012874603 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859849989414215, - "reflectance": 0.0748500004410744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85920000076294, - "reflectance": 0.0747999995946884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858449995517731, - "reflectance": 0.0746999979019165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857699990272522, - "reflectance": 0.0746000036597252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856649994850159, - "reflectance": 0.0745000019669533 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855599999427795, - "reflectance": 0.0744000002741814 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854300022125244, - "reflectance": 0.0742999985814095 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852999985218048, - "reflectance": 0.0741999968886375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852150022983551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851300001144409, - "reflectance": 0.0740000009536743 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850499987602234, - "reflectance": 0.0739500001072884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849699974060059, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848900020122528, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848100006580353, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847350001335144, - "reflectance": 0.0736500024795532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846599996089935, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845899999141693, - "reflectance": 0.0734999999403954 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845200002193451, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844600021839142, - "reflectance": 0.0733499974012375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843999981880188, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843500018119812, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842999994754791, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842599987983704, - "reflectance": 0.0730499997735024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842199981212616, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841849982738495, - "reflectance": 0.0729499980807304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841499984264374, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841199994087219, - "reflectance": 0.0728499963879585 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840900003910065, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84060001373291, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840099990367889, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839900016784668, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839699983596802, - "reflectance": 0.0726500004529953 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83950001001358, - "reflectance": 0.0725999996066093 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839299976825714, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839100003242493, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838949978351593, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838660001754761, - "reflectance": 0.0724700018763542 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838519990444183, - "reflectance": 0.0724399983882904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838379979133606, - "reflectance": 0.0724100023508072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838100016117096, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837960004806519, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837819993495941, - "reflectance": 0.0722900032997131 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837679982185364, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837539970874786, - "reflectance": 0.0722299963235855 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837400019168854, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837430000305176, - "reflectance": 0.0721900016069412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837459981441498, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837490022182465, - "reflectance": 0.0721699967980385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837520003318787, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837549984455109, - "reflectance": 0.0721499994397163 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837580025196075, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837610006332398, - "reflectance": 0.0721300020813942 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83763998746872, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837670028209686, - "reflectance": 0.0721099972724915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837700009346008, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837790012359619, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83788001537323, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837970018386841, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838060021400452, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838150024414063, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838329970836639, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83841997385025, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838509976863861, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838599979877472, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838769972324371, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838940024375916, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839110016822815, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839280009269714, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839450001716614, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839619994163513, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839789986610413, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840129971504211, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840650022029877, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841000020503998, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841350018978119, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84170001745224, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842050015926361, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842400014400482, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842750012874603, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843100011348724, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843450009822846, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843800008296967, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844190001487732, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844579994678497, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844969987869263, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845359981060028, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845749974250794, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846140027046204, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846530020236969, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8473100066185, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847699999809265, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848190009593964, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848680019378662, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849170029163361, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849659979343414, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850149989128113, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850639998912811, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85113000869751, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851620018482208, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852110028266907, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85259997844696, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852859973907471, - "reflectance": 0.0727099999785423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853120028972626, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853380024433136, - "reflectance": 0.0727299973368645 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853640019893646, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853900015354157, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854160010814667, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854420006275177, - "reflectance": 0.0727699995040894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854680001735687, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854939997196198, - "reflectance": 0.0727899968624115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855199992656708, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855979979038239, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856760025024414, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857540011405945, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858319997787476, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859099984169006, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859879970550537, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860660016536713, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861440002918243, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862219989299774, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862999975681305, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863579988479614, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864160001277924, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864740014076233, - "reflectance": 0.0733200013637543 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865320026874542, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865899980068207, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866479992866516, - "reflectance": 0.0734400004148483 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867060005664825, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867640018463135, - "reflectance": 0.0735199972987175 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868219971656799, - "reflectance": 0.0735599994659424 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869319975376129, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869840025901794, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870880007743835, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871399998664856, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871919989585877, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872439980506897, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872959971427918, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873480021953583, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874000012874603, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874270021915436, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874539971351624, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874809980392456, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875079989433289, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875349998474121, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875620007514954, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875890016555786, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876160025596619, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876429975032806, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876699984073639, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876860022544861, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877020001411438, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878260016441345, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878220021724701, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878180027008057, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878099977970123, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878059983253479, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878019988536835, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877939999103546, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877900004386902, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877740025520325, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877579987049103, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877420008182526, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877260029315949, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877099990844727, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876919984817505, - "reflectance": 0.0735699981451035 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876739978790283, - "reflectance": 0.0735400021076202 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876559972763062, - "reflectance": 0.0735099986195564 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876380026340485, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876200020313263, - "reflectance": 0.0734499990940094 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876020014286041, - "reflectance": 0.0734200030565262 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87584000825882, - "reflectance": 0.0733899995684624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875660002231598, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875479996204376, - "reflectance": 0.0733300000429153 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875299990177155, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875159978866577, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875020027160645, - "reflectance": 0.0732600018382072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874880015850067, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87474000453949, - "reflectance": 0.0732199996709824 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874599993228912, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874459981918335, - "reflectance": 0.0731799975037575 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874319970607758, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874180018901825, - "reflectance": 0.0731400027871132 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874040007591248, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87389999628067, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87379002571106, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873679995536804, - "reflectance": 0.0730599984526634 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873570024967194, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873459994792938, - "reflectance": 0.0730199962854385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873350024223328, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873239994049072, - "reflectance": 0.0729800015687943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873130023479462, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873019993305206, - "reflectance": 0.0729399994015694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872910022735596, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87279999256134, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872600018978119, - "reflectance": 0.0728600025177002 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872500002384186, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872399985790253, - "reflectance": 0.0728200003504753 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872300028800964, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872200012207031, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872099995613098, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871999979019165, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871900022029877, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871800005435944, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871619999408722, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8714399933815, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871259987354279, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871079981327057, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870899975299835, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870720028877258, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870540022850037, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870180010795593, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870000004768372, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869989991188049, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869979977607727, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86997002363205, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869960010051727, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869949996471405, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869939982891083, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869930028915405, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869920015335083, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869910001754761, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869899988174439, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869790017604828, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869679987430573, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869570016860962, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869459986686707, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869350016117096, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869239985942841, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86913001537323, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869019985198975, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868910014629364, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868470013141632, - "reflectance": 0.0720599964261055 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868139982223511, - "reflectance": 0.0720200017094612 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867810010910034, - "reflectance": 0.0719799995422363 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867479979991913, - "reflectance": 0.0719399973750114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867150008678436, - "reflectance": 0.0719000026583672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866819977760315, - "reflectance": 0.0718600004911423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866490006446838, - "reflectance": 0.0718199983239174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866159975528717, - "reflectance": 0.0717800036072731 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865830004215241, - "reflectance": 0.0717400014400482 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865499973297119, - "reflectance": 0.0716999992728233 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864019989967346, - "reflectance": 0.0715800002217293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862540006637573, - "reflectance": 0.0714600011706352 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8610600233078, - "reflectance": 0.0713400021195412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859579980373383, - "reflectance": 0.0712200030684471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85809999704361, - "reflectance": 0.0710999965667725 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856620013713837, - "reflectance": 0.0709799975156784 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855139970779419, - "reflectance": 0.0708599984645844 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853659987449646, - "reflectance": 0.0707399994134903 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852180004119873, - "reflectance": 0.0706200003623962 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8507000207901, - "reflectance": 0.0705000013113022 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850210011005402, - "reflectance": 0.0704500004649162 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849229991436005, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848739981651306, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848249971866608, - "reflectance": 0.0702499970793724 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847760021686554, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847270011901856, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846780002117157, - "reflectance": 0.0701000019907951 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846289992332459, - "reflectance": 0.0700500011444092 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84579998254776, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84636002779007, - "reflectance": 0.0700299963355064 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0700599998235703 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847479999065399, - "reflectance": 0.0700900033116341 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848039984703064, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848599970340729, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849160015583038, - "reflectance": 0.0701799988746643 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0702100023627281 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850279986858368, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850839972496033, - "reflectance": 0.0702700018882752 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851400017738342, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851589977741242, - "reflectance": 0.0703099966049194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851779997348785, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851970016956329, - "reflectance": 0.0703300014138222 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852159976959229, - "reflectance": 0.0703400000929833 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852349996566773, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852540016174316, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852729976177216, - "reflectance": 0.0703700035810471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85291999578476, - "reflectance": 0.0703800022602081 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853110015392304, - "reflectance": 0.0703900009393692 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853299975395203, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852970004081726, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852639973163605, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852310001850128, - "reflectance": 0.0702800005674362 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851979970932007, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85164999961853, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851320028305054, - "reflectance": 0.0701600015163422 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850989997386932, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850660026073456, - "reflectance": 0.0700799971818924 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850329995155335, - "reflectance": 0.0700400024652481 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850000023841858, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84918999671936, - "reflectance": 0.0699300020933151 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848380029201508, - "reflectance": 0.0698599964380264 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84757000207901, - "reflectance": 0.0697899982333183 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846759974956513, - "reflectance": 0.0697200000286102 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84595000743866, - "reflectance": 0.0696500018239021 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845139980316162, - "reflectance": 0.069580003619194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844330012798309, - "reflectance": 0.0695099979639053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843519985675812, - "reflectance": 0.0694399997591972 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842710018157959, - "reflectance": 0.0693700015544891 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841899991035461, - "reflectance": 0.069300003349781 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840929985046387, - "reflectance": 0.0692199990153313 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0691400021314621 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838989973068237, - "reflectance": 0.0690599977970123 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838020026683807, - "reflectance": 0.0689800009131432 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837050020694733, - "reflectance": 0.0688999965786934 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.836080014705658, - "reflectance": 0.0688199996948242 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.835110008716583, - "reflectance": 0.0687400028109551 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.834140002727509, - "reflectance": 0.0686599984765053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.833169996738434, - "reflectance": 0.0685800015926361 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.832199990749359, - "reflectance": 0.0684999972581863 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json deleted file mode 100644 index c37e5f80..00000000 --- a/backend/test/Methods/modification02_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json +++ /dev/null @@ -1,8429 +0,0 @@ -{ - "data": [ - { - "componentCharacteristics": { - "surface": "prime", - "symmetries": "azimuthalAngleInvariance" - }, - "dataPoints": [ - { - "incidence": { - "wavelengths": { - "wavelength": 200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0476000010967255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0476000010967255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0303000006824732, - "reflectance": 0.0472000017762184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0931999981403351, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.191599994897842, - "reflectance": 0.0485999993979931 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.318300008773804, - "reflectance": 0.0516999997198582 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.453700006008148, - "reflectance": 0.0568000003695488 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.576499998569489, - "reflectance": 0.0628999993205071 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.676800012588501, - "reflectance": 0.0689999982714653 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.751299977302551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.804400026798248, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861100018024445, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873399972915649, - "reflectance": 0.0829000025987625 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879499971866608, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888599991798401, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896899998188019, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896650016307831, - "reflectance": 0.0832000002264977 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896399974822998, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895399987697601, - "reflectance": 0.0827500000596046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894400000572205, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0823500007390976 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894200026988983, - "reflectance": 0.0821999981999397 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894649982452393, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0820000022649765 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896250009536743, - "reflectance": 0.0819500014185906 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89740002155304, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898800015449524, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900200009346008, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900900006294251, - "reflectance": 0.0818499997258186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0817999988794327 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901899993419647, - "reflectance": 0.0817499980330467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902199983596802, - "reflectance": 0.0816999971866608 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903299987316132, - "reflectance": 0.0816499963402748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904399991035461, - "reflectance": 0.0816000029444695 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904799997806549, - "reflectance": 0.0815500020980835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905200004577637, - "reflectance": 0.0815000012516975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905700027942658, - "reflectance": 0.0814500004053116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.906199991703033, - "reflectance": 0.0813999995589256 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905849993228912, - "reflectance": 0.0812999978661537 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905499994754791, - "reflectance": 0.0812000036239624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905399978160858, - "reflectance": 0.0811000019311905 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90530002117157, - "reflectance": 0.0810000002384186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90514999628067, - "reflectance": 0.0808999985456467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807999968528748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807500034570694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807000026106834 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904600024223328, - "reflectance": 0.0806000009179115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90420001745224, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903699994087219, - "reflectance": 0.0803999975323677 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903199970722199, - "reflectance": 0.0803000032901764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902400016784668, - "reflectance": 0.0801500007510185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0799999982118607 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901000022888184, - "reflectance": 0.0798999965190888 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90039998292923, - "reflectance": 0.0798000022768974 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.899649977684021, - "reflectance": 0.0797000005841255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898899972438812, - "reflectance": 0.0795999988913536 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898249983787537, - "reflectance": 0.0794999971985817 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.897599995136261, - "reflectance": 0.0794000029563904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896849989891052, - "reflectance": 0.0793000012636185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896099984645844, - "reflectance": 0.0791999995708466 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0790499970316887 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89410001039505, - "reflectance": 0.0789000019431114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.893450021743774, - "reflectance": 0.0788000002503395 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.892799973487854, - "reflectance": 0.0786999985575676 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89190000295639, - "reflectance": 0.0785999968647957 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.890999972820282, - "reflectance": 0.0785000026226044 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89045000076294, - "reflectance": 0.0784000009298325 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889900028705597, - "reflectance": 0.0782999992370606 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889150023460388, - "reflectance": 0.0781999975442886 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888400018215179, - "reflectance": 0.0781000033020973 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887750029563904, - "reflectance": 0.0780500024557114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887099981307983, - "reflectance": 0.0780000016093254 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.886399984359741, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.885699987411499, - "reflectance": 0.0777999982237816 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.884800016880035, - "reflectance": 0.0776999965310097 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.883899986743927, - "reflectance": 0.0776000022888184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.882799983024597, - "reflectance": 0.0774499997496605 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.881699979305267, - "reflectance": 0.0772999972105026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.88085001707077, - "reflectance": 0.0772000029683113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879999995231628, - "reflectance": 0.0771000012755394 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878650009632111, - "reflectance": 0.0769499987363815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877300024032593, - "reflectance": 0.0768000036478043 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875999987125397, - "reflectance": 0.0767000019550324 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0766000002622604 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873849987983704, - "reflectance": 0.0764499977231026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873000025749207, - "reflectance": 0.0763000026345253 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872150003910065, - "reflectance": 0.0762000009417534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871299982070923, - "reflectance": 0.0760999992489815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869650006294251, - "reflectance": 0.0759499967098236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867999970912933, - "reflectance": 0.0758000016212463 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867299973964691, - "reflectance": 0.0756999999284744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866599977016449, - "reflectance": 0.0755999982357025 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865100026130676, - "reflectance": 0.0754500031471252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863600015640259, - "reflectance": 0.0753000006079674 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86269998550415, - "reflectance": 0.0751999989151955 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861800014972687, - "reflectance": 0.0750999972224236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861150026321411, - "reflectance": 0.0750000029802322 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860499978065491, - "reflectance": 0.0749000012874603 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859849989414215, - "reflectance": 0.0748500004410744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85920000076294, - "reflectance": 0.0747999995946884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858449995517731, - "reflectance": 0.0746999979019165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857699990272522, - "reflectance": 0.0746000036597252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856649994850159, - "reflectance": 0.0745000019669533 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855599999427795, - "reflectance": 0.0744000002741814 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854300022125244, - "reflectance": 0.0742999985814095 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852999985218048, - "reflectance": 0.0741999968886375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852150022983551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851300001144409, - "reflectance": 0.0740000009536743 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850499987602234, - "reflectance": 0.0739500001072884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849699974060059, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848900020122528, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848100006580353, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847350001335144, - "reflectance": 0.0736500024795532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846599996089935, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845899999141693, - "reflectance": 0.0734999999403954 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845200002193451, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844600021839142, - "reflectance": 0.0733499974012375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843999981880188, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843500018119812, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842999994754791, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842599987983704, - "reflectance": 0.0730499997735024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842199981212616, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841849982738495, - "reflectance": 0.0729499980807304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841499984264374, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841199994087219, - "reflectance": 0.0728499963879585 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840900003910065, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84060001373291, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840099990367889, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839900016784668, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839699983596802, - "reflectance": 0.0726500004529953 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83950001001358, - "reflectance": 0.0725999996066093 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839299976825714, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839100003242493, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838949978351593, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838660001754761, - "reflectance": 0.0724700018763542 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838519990444183, - "reflectance": 0.0724399983882904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838379979133606, - "reflectance": 0.0724100023508072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838100016117096, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837960004806519, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837819993495941, - "reflectance": 0.0722900032997131 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837679982185364, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837539970874786, - "reflectance": 0.0722299963235855 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837400019168854, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837430000305176, - "reflectance": 0.0721900016069412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837459981441498, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837490022182465, - "reflectance": 0.0721699967980385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837520003318787, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837549984455109, - "reflectance": 0.0721499994397163 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837580025196075, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837610006332398, - "reflectance": 0.0721300020813942 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83763998746872, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837670028209686, - "reflectance": 0.0721099972724915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837700009346008, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837790012359619, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83788001537323, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837970018386841, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838060021400452, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838150024414063, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838329970836639, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83841997385025, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838509976863861, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838599979877472, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838769972324371, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838940024375916, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839110016822815, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839280009269714, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839450001716614, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839619994163513, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839789986610413, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840129971504211, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840650022029877, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841000020503998, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841350018978119, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84170001745224, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842050015926361, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842400014400482, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842750012874603, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843100011348724, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843450009822846, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843800008296967, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844190001487732, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844579994678497, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844969987869263, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845359981060028, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845749974250794, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846140027046204, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846530020236969, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8473100066185, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847699999809265, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848190009593964, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848680019378662, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849170029163361, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849659979343414, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850149989128113, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850639998912811, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85113000869751, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851620018482208, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852110028266907, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85259997844696, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852859973907471, - "reflectance": 0.0727099999785423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853120028972626, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853380024433136, - "reflectance": 0.0727299973368645 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853640019893646, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853900015354157, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854160010814667, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854420006275177, - "reflectance": 0.0727699995040894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854680001735687, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854939997196198, - "reflectance": 0.0727899968624115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855199992656708, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855979979038239, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856760025024414, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857540011405945, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858319997787476, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859099984169006, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859879970550537, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860660016536713, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861440002918243, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862219989299774, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862999975681305, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863579988479614, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864160001277924, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864740014076233, - "reflectance": 0.0733200013637543 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865320026874542, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865899980068207, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866479992866516, - "reflectance": 0.0734400004148483 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867060005664825, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867640018463135, - "reflectance": 0.0735199972987175 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868219971656799, - "reflectance": 0.0735599994659424 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869319975376129, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869840025901794, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870880007743835, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871399998664856, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871919989585877, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872439980506897, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872959971427918, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873480021953583, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874000012874603, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874270021915436, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874539971351624, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874809980392456, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875079989433289, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875349998474121, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875620007514954, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875890016555786, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876160025596619, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876429975032806, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876699984073639, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876860022544861, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877020001411438, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878260016441345, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878220021724701, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878180027008057, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878099977970123, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878059983253479, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878019988536835, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877939999103546, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877900004386902, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877740025520325, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877579987049103, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877420008182526, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877260029315949, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877099990844727, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876919984817505, - "reflectance": 0.0735699981451035 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876739978790283, - "reflectance": 0.0735400021076202 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876559972763062, - "reflectance": 0.0735099986195564 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876380026340485, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876200020313263, - "reflectance": 0.0734499990940094 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876020014286041, - "reflectance": 0.0734200030565262 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87584000825882, - "reflectance": 0.0733899995684624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875660002231598, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875479996204376, - "reflectance": 0.0733300000429153 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875299990177155, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875159978866577, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875020027160645, - "reflectance": 0.0732600018382072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874880015850067, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87474000453949, - "reflectance": 0.0732199996709824 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874599993228912, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874459981918335, - "reflectance": 0.0731799975037575 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874319970607758, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874180018901825, - "reflectance": 0.0731400027871132 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874040007591248, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87389999628067, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87379002571106, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873679995536804, - "reflectance": 0.0730599984526634 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873570024967194, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873459994792938, - "reflectance": 0.0730199962854385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873350024223328, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873239994049072, - "reflectance": 0.0729800015687943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873130023479462, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873019993305206, - "reflectance": 0.0729399994015694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872910022735596, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87279999256134, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872600018978119, - "reflectance": 0.0728600025177002 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872500002384186, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872399985790253, - "reflectance": 0.0728200003504753 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872300028800964, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872200012207031, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872099995613098, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871999979019165, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871900022029877, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871800005435944, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871619999408722, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8714399933815, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871259987354279, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871079981327057, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870899975299835, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870720028877258, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870540022850037, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870180010795593, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870000004768372, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869989991188049, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869979977607727, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86997002363205, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869960010051727, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869949996471405, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869939982891083, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869930028915405, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869920015335083, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869910001754761, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869899988174439, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869790017604828, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869679987430573, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869570016860962, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869459986686707, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869350016117096, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869239985942841, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86913001537323, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869019985198975, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868910014629364, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868470013141632, - "reflectance": 0.0720599964261055 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868139982223511, - "reflectance": 0.0720200017094612 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867810010910034, - "reflectance": 0.0719799995422363 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867479979991913, - "reflectance": 0.0719399973750114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867150008678436, - "reflectance": 0.0719000026583672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866819977760315, - "reflectance": 0.0718600004911423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866490006446838, - "reflectance": 0.0718199983239174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866159975528717, - "reflectance": 0.0717800036072731 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865830004215241, - "reflectance": 0.0717400014400482 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865499973297119, - "reflectance": 0.0716999992728233 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864019989967346, - "reflectance": 0.0715800002217293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862540006637573, - "reflectance": 0.0714600011706352 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8610600233078, - "reflectance": 0.0713400021195412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859579980373383, - "reflectance": 0.0712200030684471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85809999704361, - "reflectance": 0.0710999965667725 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856620013713837, - "reflectance": 0.0709799975156784 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855139970779419, - "reflectance": 0.0708599984645844 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853659987449646, - "reflectance": 0.0707399994134903 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852180004119873, - "reflectance": 0.0706200003623962 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8507000207901, - "reflectance": 0.0705000013113022 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850210011005402, - "reflectance": 0.0704500004649162 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849229991436005, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848739981651306, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848249971866608, - "reflectance": 0.0702499970793724 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847760021686554, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847270011901856, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846780002117157, - "reflectance": 0.0701000019907951 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846289992332459, - "reflectance": 0.0700500011444092 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84579998254776, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84636002779007, - "reflectance": 0.0700299963355064 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0700599998235703 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847479999065399, - "reflectance": 0.0700900033116341 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848039984703064, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848599970340729, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849160015583038, - "reflectance": 0.0701799988746643 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0702100023627281 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850279986858368, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850839972496033, - "reflectance": 0.0702700018882752 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851400017738342, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851589977741242, - "reflectance": 0.0703099966049194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851779997348785, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851970016956329, - "reflectance": 0.0703300014138222 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852159976959229, - "reflectance": 0.0703400000929833 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852349996566773, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852540016174316, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852729976177216, - "reflectance": 0.0703700035810471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85291999578476, - "reflectance": 0.0703800022602081 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853110015392304, - "reflectance": 0.0703900009393692 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853299975395203, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852970004081726, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852639973163605, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852310001850128, - "reflectance": 0.0702800005674362 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851979970932007, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85164999961853, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851320028305054, - "reflectance": 0.0701600015163422 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850989997386932, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850660026073456, - "reflectance": 0.0700799971818924 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850329995155335, - "reflectance": 0.0700400024652481 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850000023841858, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84918999671936, - "reflectance": 0.0699300020933151 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848380029201508, - "reflectance": 0.0698599964380264 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84757000207901, - "reflectance": 0.0697899982333183 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846759974956513, - "reflectance": 0.0697200000286102 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84595000743866, - "reflectance": 0.0696500018239021 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845139980316162, - "reflectance": 0.069580003619194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844330012798309, - "reflectance": 0.0695099979639053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843519985675812, - "reflectance": 0.0694399997591972 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842710018157959, - "reflectance": 0.0693700015544891 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841899991035461, - "reflectance": 0.069300003349781 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840929985046387, - "reflectance": 0.0692199990153313 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0691400021314621 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838989973068237, - "reflectance": 0.0690599977970123 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838020026683807, - "reflectance": 0.0689800009131432 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837050020694733, - "reflectance": 0.0688999965786934 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.836080014705658, - "reflectance": 0.0688199996948242 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.835110008716583, - "reflectance": 0.0687400028109551 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.834140002727509, - "reflectance": 0.0686599984765053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.833169996738434, - "reflectance": 0.0685800015926361 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.832199990749359, - "reflectance": 0.0684999972581863 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.832199990749359, - "reflectance": 0.0684999972581863 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json deleted file mode 100644 index da0633d6..00000000 --- a/backend/test/Methods/modification03_2d40b285-79d4-4ec6-8d46-767bd9b0f249.json +++ /dev/null @@ -1,8391 +0,0 @@ -{ - "data": [ - { - "componentCharacteristics": { - "surface": "prime", - "symmetries": "azimuthalAngleInvariance" - }, - "dataPoints": [ - { - "incidence": { - "wavelengths": { - "wavelength": 300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0476000010967255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0303000006824732, - "reflectance": 0.0472000017762184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.0931999981403351, - "reflectance": 0.0474000014364719 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.191599994897842, - "reflectance": 0.0485999993979931 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.318300008773804, - "reflectance": 0.0516999997198582 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.453700006008148, - "reflectance": 0.0568000003695488 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.576499998569489, - "reflectance": 0.0628999993205071 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.676800012588501, - "reflectance": 0.0689999982714653 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.751299977302551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.804400026798248, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861100018024445, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873399972915649, - "reflectance": 0.0829000025987625 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879499971866608, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888599991798401, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896899998188019, - "reflectance": 0.0834000036120415 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896650016307831, - "reflectance": 0.0832000002264977 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896399974822998, - "reflectance": 0.0829999968409538 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895399987697601, - "reflectance": 0.0827500000596046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894400000572205, - "reflectance": 0.0825000032782555 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894299983978272, - "reflectance": 0.0823500007390976 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894200026988983, - "reflectance": 0.0821999981999397 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.894649982452393, - "reflectance": 0.0820999965071678 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0820000022649765 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896250009536743, - "reflectance": 0.0819500014185906 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89740002155304, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898800015449524, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900200009346008, - "reflectance": 0.0819000005722046 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.900900006294251, - "reflectance": 0.0818499997258186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0817999988794327 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901899993419647, - "reflectance": 0.0817499980330467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902199983596802, - "reflectance": 0.0816999971866608 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903299987316132, - "reflectance": 0.0816499963402748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904399991035461, - "reflectance": 0.0816000029444695 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904799997806549, - "reflectance": 0.0815500020980835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905200004577637, - "reflectance": 0.0815000012516975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905700027942658, - "reflectance": 0.0814500004053116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.906199991703033, - "reflectance": 0.0813999995589256 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905849993228912, - "reflectance": 0.0812999978661537 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905499994754791, - "reflectance": 0.0812000036239624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.905399978160858, - "reflectance": 0.0811000019311905 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90530002117157, - "reflectance": 0.0810000002384186 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90514999628067, - "reflectance": 0.0808999985456467 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807999968528748 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807500034570694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904999971389771, - "reflectance": 0.0807000026106834 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.904600024223328, - "reflectance": 0.0806000009179115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90420001745224, - "reflectance": 0.0804999992251396 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903699994087219, - "reflectance": 0.0803999975323677 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.903199970722199, - "reflectance": 0.0803000032901764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.902400016784668, - "reflectance": 0.0801500007510185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901600003242493, - "reflectance": 0.0799999982118607 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.901000022888184, - "reflectance": 0.0798999965190888 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.90039998292923, - "reflectance": 0.0798000022768974 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.899649977684021, - "reflectance": 0.0797000005841255 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898899972438812, - "reflectance": 0.0795999988913536 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.898249983787537, - "reflectance": 0.0794999971985817 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.897599995136261, - "reflectance": 0.0794000029563904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896849989891052, - "reflectance": 0.0793000012636185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.896099984645844, - "reflectance": 0.0791999995708466 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.895099997520447, - "reflectance": 0.0790499970316887 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89410001039505, - "reflectance": 0.0789000019431114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.893450021743774, - "reflectance": 0.0788000002503395 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.892799973487854, - "reflectance": 0.0786999985575676 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89190000295639, - "reflectance": 0.0785999968647957 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.890999972820282, - "reflectance": 0.0785000026226044 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.89045000076294, - "reflectance": 0.0784000009298325 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889900028705597, - "reflectance": 0.0782999992370606 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.889150023460388, - "reflectance": 0.0781999975442886 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.888400018215179, - "reflectance": 0.0781000033020973 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887750029563904, - "reflectance": 0.0780500024557114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.887099981307983, - "reflectance": 0.0780000016093254 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.886399984359741, - "reflectance": 0.0778999999165535 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.885699987411499, - "reflectance": 0.0777999982237816 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.884800016880035, - "reflectance": 0.0776999965310097 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.883899986743927, - "reflectance": 0.0776000022888184 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.882799983024597, - "reflectance": 0.0774499997496605 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.881699979305267, - "reflectance": 0.0772999972105026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.88085001707077, - "reflectance": 0.0772000029683113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.879999995231628, - "reflectance": 0.0771000012755394 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878650009632111, - "reflectance": 0.0769499987363815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877300024032593, - "reflectance": 0.0768000036478043 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875999987125397, - "reflectance": 0.0767000019550324 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874700009822846, - "reflectance": 0.0766000002622604 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873849987983704, - "reflectance": 0.0764499977231026 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873000025749207, - "reflectance": 0.0763000026345253 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872150003910065, - "reflectance": 0.0762000009417534 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871299982070923, - "reflectance": 0.0760999992489815 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869650006294251, - "reflectance": 0.0759499967098236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867999970912933, - "reflectance": 0.0758000016212463 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867299973964691, - "reflectance": 0.0756999999284744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866599977016449, - "reflectance": 0.0755999982357025 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865100026130676, - "reflectance": 0.0754500031471252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863600015640259, - "reflectance": 0.0753000006079674 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86269998550415, - "reflectance": 0.0751999989151955 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861800014972687, - "reflectance": 0.0750999972224236 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861150026321411, - "reflectance": 0.0750000029802322 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860499978065491, - "reflectance": 0.0749000012874603 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859849989414215, - "reflectance": 0.0748500004410744 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85920000076294, - "reflectance": 0.0747999995946884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858449995517731, - "reflectance": 0.0746999979019165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857699990272522, - "reflectance": 0.0746000036597252 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856649994850159, - "reflectance": 0.0745000019669533 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855599999427795, - "reflectance": 0.0744000002741814 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854300022125244, - "reflectance": 0.0742999985814095 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852999985218048, - "reflectance": 0.0741999968886375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852150022983551, - "reflectance": 0.0741000026464462 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851300001144409, - "reflectance": 0.0740000009536743 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850499987602234, - "reflectance": 0.0739500001072884 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849699974060059, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848900020122528, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848100006580353, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847350001335144, - "reflectance": 0.0736500024795532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846599996089935, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845899999141693, - "reflectance": 0.0734999999403954 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845200002193451, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844600021839142, - "reflectance": 0.0733499974012375 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843999981880188, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843500018119812, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842999994754791, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842599987983704, - "reflectance": 0.0730499997735024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842199981212616, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841849982738495, - "reflectance": 0.0729499980807304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841499984264374, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841199994087219, - "reflectance": 0.0728499963879585 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840900003910065, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84060001373291, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840099990367889, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839900016784668, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839699983596802, - "reflectance": 0.0726500004529953 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83950001001358, - "reflectance": 0.0725999996066093 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839299976825714, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839100003242493, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838949978351593, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838800013065338, - "reflectance": 0.0724999979138374 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838660001754761, - "reflectance": 0.0724700018763542 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838519990444183, - "reflectance": 0.0724399983882904 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838379979133606, - "reflectance": 0.0724100023508072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838100016117096, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837960004806519, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837819993495941, - "reflectance": 0.0722900032997131 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837679982185364, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837539970874786, - "reflectance": 0.0722299963235855 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837400019168854, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837430000305176, - "reflectance": 0.0721900016069412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837459981441498, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837490022182465, - "reflectance": 0.0721699967980385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837520003318787, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837549984455109, - "reflectance": 0.0721499994397163 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837580025196075, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837610006332398, - "reflectance": 0.0721300020813942 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83763998746872, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837670028209686, - "reflectance": 0.0721099972724915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837700009346008, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837790012359619, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83788001537323, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837970018386841, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838060021400452, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838150024414063, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838240027427673, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838329970836639, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.83841997385025, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838509976863861, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838599979877472, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838769972324371, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838940024375916, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839110016822815, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839280009269714, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839450001716614, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839619994163513, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839789986610413, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840129971504211, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840300023555756, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840650022029877, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841000020503998, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841350018978119, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84170001745224, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842050015926361, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842400014400482, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842750012874603, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843100011348724, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843450009822846, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843800008296967, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844190001487732, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844579994678497, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844969987869263, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845359981060028, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845749974250794, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846140027046204, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846530020236969, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8473100066185, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847699999809265, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848190009593964, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848680019378662, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849170029163361, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849659979343414, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850149989128113, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850639998912811, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85113000869751, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851620018482208, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852110028266907, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85259997844696, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852859973907471, - "reflectance": 0.0727099999785423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853120028972626, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853380024433136, - "reflectance": 0.0727299973368645 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853640019893646, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853900015354157, - "reflectance": 0.0727500021457672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854160010814667, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854420006275177, - "reflectance": 0.0727699995040894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854680001735687, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.854939997196198, - "reflectance": 0.0727899968624115 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855199992656708, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855979979038239, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856760025024414, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.857540011405945, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.858319997787476, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859099984169006, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859879970550537, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.860660016536713, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.861440002918243, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862219989299774, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862999975681305, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.863579988479614, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864160001277924, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864740014076233, - "reflectance": 0.0733200013637543 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865320026874542, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865899980068207, - "reflectance": 0.0733999982476234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866479992866516, - "reflectance": 0.0734400004148483 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867060005664825, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867640018463135, - "reflectance": 0.0735199972987175 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868219971656799, - "reflectance": 0.0735599994659424 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1505 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869319975376129, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1510 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869840025901794, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1515 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1520 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870880007743835, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1525 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871399998664856, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1530 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871919989585877, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1535 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872439980506897, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1540 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872959971427918, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1545 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873480021953583, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1550 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874000012874603, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1555 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874270021915436, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1560 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874539971351624, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1565 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874809980392456, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1570 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875079989433289, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1575 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875349998474121, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1580 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875620007514954, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1585 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875890016555786, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1590 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876160025596619, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1595 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876429975032806, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1600 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876699984073639, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1605 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876860022544861, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1610 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877020001411438, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1615 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1620 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1625 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1630 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1635 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1640 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1645 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1650 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87830001115799, - "reflectance": 0.0738999992609024 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1655 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878260016441345, - "reflectance": 0.0738900005817413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1660 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878220021724701, - "reflectance": 0.0738800019025803 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1665 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878180027008057, - "reflectance": 0.0738700032234192 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1670 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878139972686768, - "reflectance": 0.0738599970936775 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1675 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878099977970123, - "reflectance": 0.0738499984145165 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1680 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878059983253479, - "reflectance": 0.0738399997353554 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1685 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.878019988536835, - "reflectance": 0.0738300010561943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1690 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87797999382019, - "reflectance": 0.0738200023770332 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1695 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877939999103546, - "reflectance": 0.0738100036978722 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1700 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877900004386902, - "reflectance": 0.0737999975681305 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1705 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877820014953613, - "reflectance": 0.0737800002098084 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1710 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877740025520325, - "reflectance": 0.0737600028514862 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1715 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877659976482391, - "reflectance": 0.0737399980425835 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1720 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877579987049103, - "reflectance": 0.0737200006842613 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1725 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877499997615814, - "reflectance": 0.0737000033259392 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1730 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877420008182526, - "reflectance": 0.0736799985170364 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1735 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877340018749237, - "reflectance": 0.0736600011587143 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1740 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877260029315949, - "reflectance": 0.0736399963498116 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1745 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877179980278015, - "reflectance": 0.0736199989914894 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1750 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.877099990844727, - "reflectance": 0.0736000016331673 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1755 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876919984817505, - "reflectance": 0.0735699981451035 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1760 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876739978790283, - "reflectance": 0.0735400021076202 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1765 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876559972763062, - "reflectance": 0.0735099986195564 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1770 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876380026340485, - "reflectance": 0.0734800025820732 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1775 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876200020313263, - "reflectance": 0.0734499990940094 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1780 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.876020014286041, - "reflectance": 0.0734200030565262 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1785 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87584000825882, - "reflectance": 0.0733899995684624 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1790 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875660002231598, - "reflectance": 0.0733600035309792 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1795 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875479996204376, - "reflectance": 0.0733300000429153 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1800 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875299990177155, - "reflectance": 0.0732999965548515 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1805 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875159978866577, - "reflectance": 0.0732799991965294 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1810 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.875020027160645, - "reflectance": 0.0732600018382072 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1815 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874880015850067, - "reflectance": 0.0732399970293045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1820 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87474000453949, - "reflectance": 0.0732199996709824 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1825 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874599993228912, - "reflectance": 0.0732000023126602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1830 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874459981918335, - "reflectance": 0.0731799975037575 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1835 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874319970607758, - "reflectance": 0.0731600001454353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1840 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874180018901825, - "reflectance": 0.0731400027871132 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1845 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.874040007591248, - "reflectance": 0.0731199979782105 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1850 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87389999628067, - "reflectance": 0.0731000006198883 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1855 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87379002571106, - "reflectance": 0.0730800032615662 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1860 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873679995536804, - "reflectance": 0.0730599984526634 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1865 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873570024967194, - "reflectance": 0.0730400010943413 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1870 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873459994792938, - "reflectance": 0.0730199962854385 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1875 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873350024223328, - "reflectance": 0.0729999989271164 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1880 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873239994049072, - "reflectance": 0.0729800015687943 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1885 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873130023479462, - "reflectance": 0.0729599967598915 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1890 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.873019993305206, - "reflectance": 0.0729399994015694 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1895 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872910022735596, - "reflectance": 0.0729200020432472 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1900 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.87279999256134, - "reflectance": 0.0728999972343445 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1905 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872699975967407, - "reflectance": 0.0728799998760223 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1910 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872600018978119, - "reflectance": 0.0728600025177002 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1915 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872500002384186, - "reflectance": 0.0728399977087975 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1920 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872399985790253, - "reflectance": 0.0728200003504753 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1925 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872300028800964, - "reflectance": 0.0728000029921532 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1930 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872200012207031, - "reflectance": 0.0727799981832504 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1935 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.872099995613098, - "reflectance": 0.0727600008249283 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1940 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871999979019165, - "reflectance": 0.0727400034666061 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1945 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871900022029877, - "reflectance": 0.0727199986577034 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1950 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871800005435944, - "reflectance": 0.0727000012993813 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1955 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871619999408722, - "reflectance": 0.0726699978113174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1960 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8714399933815, - "reflectance": 0.0726400017738342 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1965 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871259987354279, - "reflectance": 0.0726099982857704 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1970 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.871079981327057, - "reflectance": 0.0725800022482872 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1975 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870899975299835, - "reflectance": 0.0725499987602234 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1980 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870720028877258, - "reflectance": 0.0725200027227402 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1985 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870540022850037, - "reflectance": 0.0724899992346764 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1990 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870360016822815, - "reflectance": 0.0724600031971932 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 1995 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870180010795593, - "reflectance": 0.0724299997091293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2000 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.870000004768372, - "reflectance": 0.0724000036716461 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2005 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869989991188049, - "reflectance": 0.0723899975419045 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2010 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869979977607727, - "reflectance": 0.0723799988627434 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2015 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86997002363205, - "reflectance": 0.0723700001835823 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2020 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869960010051727, - "reflectance": 0.0723600015044212 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2025 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869949996471405, - "reflectance": 0.0723500028252602 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2030 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869939982891083, - "reflectance": 0.0723399966955185 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2035 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869930028915405, - "reflectance": 0.0723299980163574 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2040 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869920015335083, - "reflectance": 0.0723199993371964 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2045 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869910001754761, - "reflectance": 0.0723100006580353 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2050 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869899988174439, - "reflectance": 0.0723000019788742 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2055 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869790017604828, - "reflectance": 0.0722799971699715 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2060 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869679987430573, - "reflectance": 0.0722599998116493 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2065 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869570016860962, - "reflectance": 0.0722400024533272 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2070 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869459986686707, - "reflectance": 0.0722199976444244 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2075 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869350016117096, - "reflectance": 0.0722000002861023 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2080 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869239985942841, - "reflectance": 0.0721800029277802 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2085 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.86913001537323, - "reflectance": 0.0721599981188774 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2090 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.869019985198975, - "reflectance": 0.0721400007605553 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2095 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868910014629364, - "reflectance": 0.0721200034022331 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2100 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868799984455109, - "reflectance": 0.0720999985933304 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2105 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868470013141632, - "reflectance": 0.0720599964261055 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2110 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.868139982223511, - "reflectance": 0.0720200017094612 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2115 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867810010910034, - "reflectance": 0.0719799995422363 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2120 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867479979991913, - "reflectance": 0.0719399973750114 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2125 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.867150008678436, - "reflectance": 0.0719000026583672 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2130 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866819977760315, - "reflectance": 0.0718600004911423 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2135 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866490006446838, - "reflectance": 0.0718199983239174 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2140 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.866159975528717, - "reflectance": 0.0717800036072731 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2145 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865830004215241, - "reflectance": 0.0717400014400482 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2150 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.865499973297119, - "reflectance": 0.0716999992728233 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2155 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.864019989967346, - "reflectance": 0.0715800002217293 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2160 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.862540006637573, - "reflectance": 0.0714600011706352 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2165 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8610600233078, - "reflectance": 0.0713400021195412 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2170 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.859579980373383, - "reflectance": 0.0712200030684471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2175 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85809999704361, - "reflectance": 0.0710999965667725 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2180 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.856620013713837, - "reflectance": 0.0709799975156784 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2185 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.855139970779419, - "reflectance": 0.0708599984645844 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2190 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853659987449646, - "reflectance": 0.0707399994134903 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2195 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852180004119873, - "reflectance": 0.0706200003623962 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2200 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.8507000207901, - "reflectance": 0.0705000013113022 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2205 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850210011005402, - "reflectance": 0.0704500004649162 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2210 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2215 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849229991436005, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2220 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848739981651306, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2225 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848249971866608, - "reflectance": 0.0702499970793724 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2230 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847760021686554, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2235 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847270011901856, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2240 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846780002117157, - "reflectance": 0.0701000019907951 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2245 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846289992332459, - "reflectance": 0.0700500011444092 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2250 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84579998254776, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2255 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84636002779007, - "reflectance": 0.0700299963355064 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2260 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846920013427734, - "reflectance": 0.0700599998235703 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2265 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.847479999065399, - "reflectance": 0.0700900033116341 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2270 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848039984703064, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2275 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848599970340729, - "reflectance": 0.0701500028371811 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2280 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849160015583038, - "reflectance": 0.0701799988746643 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2285 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.849720001220703, - "reflectance": 0.0702100023627281 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2290 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850279986858368, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2295 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850839972496033, - "reflectance": 0.0702700018882752 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2300 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851400017738342, - "reflectance": 0.0702999979257584 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2305 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851589977741242, - "reflectance": 0.0703099966049194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2310 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851779997348785, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2315 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851970016956329, - "reflectance": 0.0703300014138222 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2320 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852159976959229, - "reflectance": 0.0703400000929833 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2325 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852349996566773, - "reflectance": 0.0703499987721443 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2330 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852540016174316, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2335 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852729976177216, - "reflectance": 0.0703700035810471 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2340 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85291999578476, - "reflectance": 0.0703800022602081 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2345 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853110015392304, - "reflectance": 0.0703900009393692 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2350 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.853299975395203, - "reflectance": 0.0703999996185303 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2355 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852970004081726, - "reflectance": 0.0703599974513054 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2360 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852639973163605, - "reflectance": 0.0703200027346611 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2365 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.852310001850128, - "reflectance": 0.0702800005674362 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2370 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851979970932007, - "reflectance": 0.0702399984002113 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2375 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.85164999961853, - "reflectance": 0.0702000036835671 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2380 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.851320028305054, - "reflectance": 0.0701600015163422 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2385 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850989997386932, - "reflectance": 0.0701199993491173 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2390 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850660026073456, - "reflectance": 0.0700799971818924 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2395 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850329995155335, - "reflectance": 0.0700400024652481 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2400 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.850000023841858, - "reflectance": 0.0700000002980232 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2405 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84918999671936, - "reflectance": 0.0699300020933151 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2410 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.848380029201508, - "reflectance": 0.0698599964380264 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2415 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84757000207901, - "reflectance": 0.0697899982333183 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2420 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.846759974956513, - "reflectance": 0.0697200000286102 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2425 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.84595000743866, - "reflectance": 0.0696500018239021 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2430 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.845139980316162, - "reflectance": 0.069580003619194 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2435 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.844330012798309, - "reflectance": 0.0695099979639053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2440 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.843519985675812, - "reflectance": 0.0694399997591972 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2445 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.842710018157959, - "reflectance": 0.0693700015544891 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2450 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.841899991035461, - "reflectance": 0.069300003349781 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2455 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.840929985046387, - "reflectance": 0.0692199990153313 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2460 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.839959979057312, - "reflectance": 0.0691400021314621 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2465 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838989973068237, - "reflectance": 0.0690599977970123 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2470 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.838020026683807, - "reflectance": 0.0689800009131432 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2475 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.837050020694733, - "reflectance": 0.0688999965786934 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2480 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.836080014705658, - "reflectance": 0.0688199996948242 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2485 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.835110008716583, - "reflectance": 0.0687400028109551 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2490 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.834140002727509, - "reflectance": 0.0686599984765053 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2495 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.833169996738434, - "reflectance": 0.0685800015926361 - } - }, - { - "incidence": { - "wavelengths": { - "wavelength": 2500 - }, - "direction": { - "polar": 0 - } - }, - "emergence": { - "direction": { - "polar": 0 - } - }, - "results": { - "transmittance": 0.832199990749359, - "reflectance": 0.0684999972581863 - } - } - ] - } - ] -} \ No newline at end of file From e8abe992a358e9ca3baeff17e50c3e583387c815 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Wed, 26 Nov 2025 13:25:56 +0100 Subject: [PATCH 592/878] No need to catch. NUnit fails when there is an exception --- .../Methods/SpectralToIntegralMethodTests.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index a33cea64..9331335e 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -100,19 +100,13 @@ public async Task Calculate_Many() MethodBase.JsonSerializerOptions ) ?? throw new InvalidOperationException(); var method = new SpectralToIntegralMethod(); - try - { - var results = method.Calculate(spectralDataPoints); - using (new AssertionScope()) - { - results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); - results.En410Solar.Should().BeApproximately(0.8617466755155275, 0.00000001F); - results.Iso9050Solar.Should().BeApproximately(0.8573906338464905, 0.00000001F); - } - } - catch (ArgumentException ex) + var results = method.Calculate(spectralDataPoints); + using (new AssertionScope()) { - Console.WriteLine(ex.Message); + results.En410Visible.Should().BeApproximately(0.9023789669000116, 0.000000000000001F); + results.En410Solar.Should().BeApproximately(0.8617466755155275, 0.00000001F); + results.Iso9050Solar.Should().BeApproximately(0.8573906338464905, 0.00000001F); } + } } \ No newline at end of file From 52bd86a4821788569cbfca255ff25a7e928aabc3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 1 Dec 2025 16:47:05 +0100 Subject: [PATCH 593/878] Add mutations to update GET HTTPS resources --- backend/src/Data/GetHttpsResource.cs | 58 +++++- .../CreateGetHttpsResourceMutation.cs | 14 +- .../SetGetHttpsResourceParentMutation.cs | 168 ++++++++++++++++++ .../UpdateChildGetHttpsResourceMutation.cs | 157 ++++++++++++++++ .../UpdateRootGetHttpsResourceMutation.cs | 144 +++++++++++++++ .../GraphQlSchemaTests.IsUnchanged.snap | 94 ++++++++++ frontend/type-defs.graphqls | 94 ++++++++++ 7 files changed, 716 insertions(+), 13 deletions(-) create mode 100644 backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 2ad860cf..cec3f128 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Database.Enumerations; using Database.Extensions; +using Database.GraphQl; using Database.Utilities; using EntityFrameworkCore.Projectables; @@ -207,7 +208,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(Parent))] public ICollection Children { get; } = []; - public void UpdateFileExtension(string fileExtension) + public void UpdateFileExtension(string? fileExtension) { var oldFilePath = FilePath; FileExtension = fileExtension; @@ -243,4 +244,59 @@ public static string ConstructVertexId(Guid id) { return id.ToString("D").Base64Encode(); } + + internal void UpdateRoot( + string description, + Guid dataFormatId, + string? fileExtension, + FileMetaInformation[] archivedFilesMetaInformation + ) + { + if (ParentId is not null) + { + throw new InvalidOperationException($"This resource with ID {Id} is not a root but the child of {ParentId}."); + } + Description = description; + DataFormatId = dataFormatId; + if (fileExtension != FileExtension) + { + UpdateFileExtension(fileExtension); + } + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + } + + internal void UpdateChild( + string description, + Guid dataFormatId, + string? fileExtension, + FileMetaInformation[] archivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethod appliedConversionMethod + ) + { + if (ParentId is null) + { + throw new InvalidOperationException($"This resource with ID {Id} is not a child."); + } + Description = description; + DataFormatId = dataFormatId; + if (fileExtension != FileExtension) + { + UpdateFileExtension(fileExtension); + } + ArchivedFilesMetaInformation = archivedFilesMetaInformation; + AppliedConversionMethod = appliedConversionMethod; + } + + internal void SetParent( + Guid parentId, + ToTreeVertexAppliedConversionMethod appliedConversionMethod + ) + { + if (ParentId is null) + { + throw new InvalidOperationException($"This resource with ID {Id} is not a child."); + } + ParentId = parentId; + AppliedConversionMethod = appliedConversionMethod; + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 12489766..d3ce497c 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -7,7 +7,6 @@ using Database.Enumerations; using HotChocolate.Types; using Database.Services; -using Microsoft.Extensions.Logging; using Database.Utilities; using GreenDonut.Data; using System.Diagnostics.CodeAnalysis; @@ -20,10 +19,10 @@ namespace Database.GraphQl.GetHttpsResources; public sealed record CreateGetHttpsResourceInput( - string Description, - Guid DataFormatId, Guid DataId, DataKind DataKind, + string Description, + Guid DataFormatId, Guid ParentId, IReadOnlyList ArchivedFilesMetaInformation, ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod @@ -74,15 +73,6 @@ public sealed record CreateGetHttpsResourcePayload( IReadOnlyCollection? Errors ) : Payload; -public static partial class CreateGetHttpsResourceMutationLogging -{ - [LoggerMessage( - Level = LogLevel.Warning, - Message = "Recomputing the hash value of the GET HTTPS resource with ID {Id} failed." - )] - public static partial void FailedRecomputingHashValue(this ILogger logger, Guid id, Exception exception); -} - [ExtendObjectType(nameof(Mutation))] public sealed class CreateGetHttpsResourceMutation : DataMutationBase diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs new file mode 100644 index 00000000..bbfc7aec --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -0,0 +1,168 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Authorization; +using HotChocolate.Types; +using Database.Services; +using GreenDonut.Data; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using Microsoft.EntityFrameworkCore; +using Database.ApiRequests; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed record SetGetHttpsResourceParentInput( + Guid GetHttpsResourceId, + Guid ParentId, + ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetGetHttpsResourceParentErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_RESOURCE, + UNKNOWN_PARENT, + ILLEGAL_PARENT, + UNKNOWN_METHOD, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record SetGetHttpsResourceParentError( + SetGetHttpsResourceParentErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetGetHttpsResourceParentPayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetGetHttpsResourceParentMutation +: DataMutationBase +{ + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override SetGetHttpsResourceParentPayload NewPayload( + GetHttpsResource? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetGetHttpsResourceParentError NewError( + SetGetHttpsResourceParentErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetGetHttpsResourceParentAsync( + SetGetHttpsResourceParentInput input, + ApplicationDbContext context, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + ResponseApprovalService responseApprovalService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetGetHttpsResourceParentErrorCode.UNAUTHENTICATED, + SetGetHttpsResourceParentErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var resource = await context.GetHttpsResourcesWithData + .Where(_ => _.Id == input.GetHttpsResourceId) + .Where(_ => _.ParentId != null) + .SingleOrDefaultAsync(cancellationToken); + if (resource is null) + { + return NewPayload(null, + [NewError( + SetGetHttpsResourceParentErrorCode.UNKNOWN_RESOURCE, + "Unknown child GET HTTPS resource, the ID may be wrong or the resource may be a root resource.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + )] + ); + } + + var errors = new List(); + var parent = + await context.GetHttpsResources + .SingleOrDefaultAsync(_ => + _.Id == input.ParentId, + cancellationToken + ); + if (parent is null) + { + errors.Add( + NewError( + SetGetHttpsResourceParentErrorCode.UNKNOWN_PARENT, + "There is not GET HTTPS resource with the parent ID.", + [nameof(input), nameof(input.ParentId).ToLowerFirst()] + ) + ); + } + if (parent is not null && parent.DataId != resource.DataId) + { + errors.Add( + NewError( + SetGetHttpsResourceParentErrorCode.ILLEGAL_PARENT, + $"The parent belongs to the data set with ID {parent.DataId} which is not the data set of the resource, namely the one with the ID {resource.DataId}.", + [nameof(input), nameof(input.ParentId).ToLowerFirst()] + ) + ); + } + if (await methodByIdDataLoader.LoadAsync(input.AppliedConversionMethod.MethodId, cancellationToken) is null) + { + errors.Add( + NewError( + SetGetHttpsResourceParentErrorCode.UNKNOWN_METHOD, + "The applied conversion method does not exist", + [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst(), nameof(input.AppliedConversionMethod.MethodId).ToLowerFirst()] + ) + ); + } + // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. + if (errors.Count >= 1) + { + return NewPayload(null, errors); + } + + resource.SetParent( + input.ParentId, + input.AppliedConversionMethod.ToDomainModel() + ); + await context.SaveChangesAsync(cancellationToken); + + if (resource.Data is not null && (await CreateResponseApprovalAsync( + resource.Data, + SetGetHttpsResourceParentErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + // TODO rollback? + return createResponseApprovalErrorPayload; + } + + return NewPayload(resource, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs new file mode 100644 index 00000000..ba74b9f9 --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -0,0 +1,157 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Authorization; +using HotChocolate.Types; +using Database.Services; +using GreenDonut.Data; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using Microsoft.EntityFrameworkCore; +using Database.ApiRequests; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed record UpdateChildGetHttpsResourceInput( + Guid GetHttpsResourceId, + string Description, + Guid DataFormatId, + IReadOnlyList ArchivedFilesMetaInformation, + ToTreeVertexAppliedConversionMethodInput AppliedConversionMethod +) : IValidateGetHttpsResourceInput; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateChildGetHttpsResourceErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_RESOURCE, + UNKNOWN_DATA_FORMAT, + UNKNOWN_METHOD, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record UpdateChildGetHttpsResourceError( + UpdateChildGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record UpdateChildGetHttpsResourcePayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateChildGetHttpsResourceMutation +: DataMutationBase +{ + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override UpdateChildGetHttpsResourcePayload NewPayload( + GetHttpsResource? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateChildGetHttpsResourceError NewError( + UpdateChildGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UpdateChildGetHttpsResourceAsync( + UpdateChildGetHttpsResourceInput input, + ApplicationDbContext context, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + ResponseApprovalService responseApprovalService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UpdateChildGetHttpsResourceErrorCode.UNAUTHENTICATED, + UpdateChildGetHttpsResourceErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var resource = await context.GetHttpsResourcesWithData + .Where(_ => _.Id == input.GetHttpsResourceId) + .Where(_ => _.ParentId != null) + .SingleOrDefaultAsync(cancellationToken); + if (resource is null) + { + return NewPayload(null, + [NewError( + UpdateChildGetHttpsResourceErrorCode.UNKNOWN_RESOURCE, + "Unknown child GET HTTPS resource, the ID may be wrong or the resource may be a root resource.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + )] + ); + } + + var errors = new List(); + var validateResourceResult = await ValidateGetHttpsResourceAsync( + input, + [nameof(input)], + dataFormatByIdDataLoader, + UpdateChildGetHttpsResourceErrorCode.UNKNOWN_DATA_FORMAT, + cancellationToken + ); + if (validateResourceResult.Failed(out var dataFormat, out var validateResourceErrors)) + { + errors.AddRange(validateResourceErrors); + } + if (await methodByIdDataLoader.LoadAsync(input.AppliedConversionMethod.MethodId, cancellationToken) is null) + { + errors.Add( + NewError( + UpdateChildGetHttpsResourceErrorCode.UNKNOWN_METHOD, + "The applied conversion method does not exist", + [nameof(input), nameof(input.AppliedConversionMethod).ToLowerFirst(), nameof(input.AppliedConversionMethod.MethodId).ToLowerFirst()] + ) + ); + } + // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. + if (errors.Count >= 1 || dataFormat is null) + { + return NewPayload(null, errors); + } + + resource.UpdateChild( + input.Description, + input.DataFormatId, + dataFormat.Extension, + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToArray(), + input.AppliedConversionMethod.ToDomainModel() + ); + await context.SaveChangesAsync(cancellationToken); + + if (resource.Data is not null && (await CreateResponseApprovalAsync( + resource.Data, + UpdateChildGetHttpsResourceErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + // TODO rollback? + return createResponseApprovalErrorPayload; + } + + return NewPayload(resource, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs new file mode 100644 index 00000000..9d4771ce --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -0,0 +1,144 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Authorization; +using HotChocolate.Types; +using Database.Services; +using GreenDonut.Data; +using System.Diagnostics.CodeAnalysis; +using System.Collections.Generic; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using Microsoft.EntityFrameworkCore; +using Database.ApiRequests; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed record UpdateRootGetHttpsResourceInput( + Guid GetHttpsResourceId, + string Description, + Guid DataFormatId, + IReadOnlyList ArchivedFilesMetaInformation +) : IValidateGetHttpsResourceInput; + +[SuppressMessage("Naming", "CA1707")] +public enum UpdateRootGetHttpsResourceErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_RESOURCE, + UNKNOWN_DATA_FORMAT, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record UpdateRootGetHttpsResourceError( + UpdateRootGetHttpsResourceErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record UpdateRootGetHttpsResourcePayload( + GetHttpsResource? GetHttpsResource, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class UpdateRootGetHttpsResourceMutation +: DataMutationBase +{ + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override UpdateRootGetHttpsResourcePayload NewPayload( + GetHttpsResource? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UpdateRootGetHttpsResourceError NewError( + UpdateRootGetHttpsResourceErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UpdateRootGetHttpsResourceAsync( + UpdateRootGetHttpsResourceInput input, + ApplicationDbContext context, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + ResponseApprovalService responseApprovalService, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UpdateRootGetHttpsResourceErrorCode.UNAUTHENTICATED, + UpdateRootGetHttpsResourceErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var resource = await context.GetHttpsResourcesWithData + .Where(_ => _.Id == input.GetHttpsResourceId) + .Where(_ => _.ParentId == null) + .SingleOrDefaultAsync(cancellationToken); + if (resource is null) + { + return NewPayload(null, + [NewError( + UpdateRootGetHttpsResourceErrorCode.UNKNOWN_RESOURCE, + "Unknown root GET HTTPS resource, the ID may be wrong or the resource may be a child of another resource.", + [nameof(input), nameof(input.GetHttpsResourceId).ToLowerFirst()] + )] + ); + } + + var errors = new List(); + var validateResourceResult = await ValidateGetHttpsResourceAsync( + input, + [nameof(input)], + dataFormatByIdDataLoader, + UpdateRootGetHttpsResourceErrorCode.UNKNOWN_DATA_FORMAT, + cancellationToken + ); + if (validateResourceResult.Failed(out var dataFormat, out var validateResourceErrors)) + { + errors.AddRange(validateResourceErrors); + } + // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. + if (errors.Count >= 1 || dataFormat is null) + { + return NewPayload(null, errors); + } + + resource.UpdateRoot( + input.Description, + input.DataFormatId, + dataFormat.Extension, + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToArray() + ); + await context.SaveChangesAsync(cancellationToken); + + if (resource.Data is not null && (await CreateResponseApprovalAsync( + resource.Data, + UpdateRootGetHttpsResourceErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + // TODO rollback? + return createResponseApprovalErrorPayload; + } + + return NewPayload(resource, null); + } +} \ No newline at end of file diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 11e404c9..46e145cf 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -325,6 +325,7 @@ type GeometricData implements Node & Data { dataAccessRights: DataAccessRights! databaseId: Uuid! description: String + heights: [Float!]! id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! @@ -338,6 +339,7 @@ type GeometricData implements Node & Data { userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! + widths: [Float!]! } "A connection to a list of items." @@ -490,11 +492,14 @@ type Mutation { recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") + updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") } type NamedMethodArgument { @@ -735,6 +740,18 @@ type RetractDataPayload { query: Query! } +type SetGetHttpsResourceParentError implements UserError { + code: SetGetHttpsResourceParentErrorCode! + message: String! + path: [String!]! +} + +type SetGetHttpsResourceParentPayload { + errors: [SetGetHttpsResourceParentError!] + getHttpsResource: GetHttpsResource + query: Query! +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -754,6 +771,18 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UpdateChildGetHttpsResourceError implements UserError { + code: UpdateChildGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateChildGetHttpsResourcePayload { + errors: [UpdateChildGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! @@ -814,6 +843,18 @@ type UpdateResponseApprovalsPayload { query: Query! } +type UpdateRootGetHttpsResourceError implements UserError { + code: UpdateRootGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateRootGetHttpsResourcePayload { + errors: [UpdateRootGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type User implements Node { id: ID! name: String! @@ -946,11 +987,13 @@ input CreateGeometricDataInput { createdAt: DateTime! creatorId: Uuid! description: String + heights: [Float!]! locale: String! name: String rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! + widths: [Float!]! } input CreateGetHttpsResourceInput { @@ -1612,6 +1655,12 @@ input RootGetHttpsResourceInput { description: String! } +input SetGetHttpsResourceParentInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + getHttpsResourceId: Uuid! + parentId: Uuid! +} + input StandardInput { abstract: String locator: Url @@ -1697,6 +1746,14 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateChildGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + input UpdateDataAccessRightsInput { allowedApplications: [String!] allowedInstitutions: [Uuid!] @@ -1748,6 +1805,13 @@ input UpdateResponseApprovalsFilterInput { warnings: StringsPropositionInput } +input UpdateRootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -2078,6 +2142,17 @@ enum RetractDataErrorCode { NOT_PUBLISHED } +enum SetGetHttpsResourceParentErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_PARENT + ILLEGAL_PARENT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + enum SortEnumType { ASC DESC @@ -2110,6 +2185,16 @@ enum Standardizer { WELL } +enum UpdateChildGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + enum UpdateDataAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED @@ -2145,6 +2230,15 @@ enum UpdateResponseApprovalsErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum UpdateRootGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + enum UriHostNameType { UNKNOWN BASIC diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 11e404c9..46e145cf 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -325,6 +325,7 @@ type GeometricData implements Node & Data { dataAccessRights: DataAccessRights! databaseId: Uuid! description: String + heights: [Float!]! id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! @@ -338,6 +339,7 @@ type GeometricData implements Node & Data { userId: Uuid uuid: Uuid! @cost(weight: "10") warnings: [String!]! + widths: [Float!]! } "A connection to a list of items." @@ -490,11 +492,14 @@ type Mutation { recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") + updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") } type NamedMethodArgument { @@ -735,6 +740,18 @@ type RetractDataPayload { query: Query! } +type SetGetHttpsResourceParentError implements UserError { + code: SetGetHttpsResourceParentErrorCode! + message: String! + path: [String!]! +} + +type SetGetHttpsResourceParentPayload { + errors: [SetGetHttpsResourceParentError!] + getHttpsResource: GetHttpsResource + query: Query! +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -754,6 +771,18 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UpdateChildGetHttpsResourceError implements UserError { + code: UpdateChildGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateChildGetHttpsResourcePayload { + errors: [UpdateChildGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type UpdateDataAccessRightsError implements UserError { code: UpdateDataAccessRightsErrorCode! message: String! @@ -814,6 +843,18 @@ type UpdateResponseApprovalsPayload { query: Query! } +type UpdateRootGetHttpsResourceError implements UserError { + code: UpdateRootGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateRootGetHttpsResourcePayload { + errors: [UpdateRootGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + type User implements Node { id: ID! name: String! @@ -946,11 +987,13 @@ input CreateGeometricDataInput { createdAt: DateTime! creatorId: Uuid! description: String + heights: [Float!]! locale: String! name: String rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! warnings: [String!]! + widths: [Float!]! } input CreateGetHttpsResourceInput { @@ -1612,6 +1655,12 @@ input RootGetHttpsResourceInput { description: String! } +input SetGetHttpsResourceParentInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + getHttpsResourceId: Uuid! + parentId: Uuid! +} + input StandardInput { abstract: String locator: Url @@ -1697,6 +1746,14 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UpdateChildGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + input UpdateDataAccessRightsInput { allowedApplications: [String!] allowedInstitutions: [Uuid!] @@ -1748,6 +1805,13 @@ input UpdateResponseApprovalsFilterInput { warnings: StringsPropositionInput } +input UpdateRootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + input UriHostNameTypePropositionInput { equalTo: UriHostNameType @cost(weight: "10") notEqualTo: UriHostNameType @cost(weight: "10") @@ -2078,6 +2142,17 @@ enum RetractDataErrorCode { NOT_PUBLISHED } +enum SetGetHttpsResourceParentErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_PARENT + ILLEGAL_PARENT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + enum SortEnumType { ASC DESC @@ -2110,6 +2185,16 @@ enum Standardizer { WELL } +enum UpdateChildGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + enum UpdateDataAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED @@ -2145,6 +2230,15 @@ enum UpdateResponseApprovalsErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum UpdateRootGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + enum UriHostNameType { UNKNOWN BASIC From fef6ffeaef8a8275219f2c5947f7e5bac070484b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Dec 2025 18:01:56 +0100 Subject: [PATCH 594/878] Upgrade to .NET 10 and C# 14 --- backend/Dockerfile | 21 ++++++++++++++---- backend/Dockerfile-bootstrap | 21 ++++++++++++++---- backend/Dockerfile-production | 12 ++++++----- backend/dotnet-certificate-tool | 2 +- backend/dotnet-tools.json | 16 +++++++------- backend/src/Database.csproj | 34 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 10 ++++----- 7 files changed, 72 insertions(+), 44 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index f63ca055..a93547d1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -8,7 +8,7 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags -FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 ARG GROUP_ID ARG USER_ID @@ -16,13 +16,26 @@ ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME ARG CERTIFICATE_AUTHORITY_PASSWORD # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) +# id --user --name ${USER_ID} 2>/dev/null RUN \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + adduser && \ existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_user_name}"; then deluser --system "${existing_user_name}"; fi && \ - if test -n "${existing_group_name}"; then delgroup --system "${existing_group_name}"; fi && \ + if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ + if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me + adduser --system --uid ${USER_ID} --ingroup us me && \ + apt-get remove \ + --assume-yes \ + adduser && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* ############# # As `root` # diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 0e33afce..39879665 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -5,19 +5,32 @@ # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk -FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 +FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 ARG GROUP_ID ARG USER_ID # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) +# id --user --name ${USER_ID} 2>/dev/null RUN \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + adduser && \ existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_user_name}"; then deluser --system "${existing_user_name}"; fi && \ - if test -n "${existing_group_name}"; then delgroup --system "${existing_group_name}"; fi && \ + if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ + if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me + adduser --system --uid ${USER_ID} --ingroup us me && \ + apt-get remove \ + --assume-yes \ + adduser && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. # For available versions see https://repos.gnupg.org/deb/gnupg/ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 8e4aca0d..c1927020 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -13,9 +13,10 @@ # BUILD # ######### -# https://hub.docker.com/_/microsoft-dotnet-sdk +# https://hub.docker.com/r/microsoft/dotnet-sdk # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags -FROM mcr.microsoft.com/dotnet/sdk:9.0-bookworm-slim-amd64 AS build +# https://github.com/dotnet/dotnet-docker/blob/main/README.sdk.md +FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 AS build # RUN \ # apt-get update && \ @@ -51,9 +52,10 @@ RUN \ # RUNTIME # ########### -# https://hub.docker.com/_/microsoft-dotnet-aspnet -# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags -FROM mcr.microsoft.com/dotnet/aspnet:9.0-bookworm-slim-amd64 +# https://hub.docker.com/r/microsoft/dotnet-aspnet +# https://mcr.microsoft.com/artifact/mar/dotnet/aspnet/tags +# https://github.com/dotnet/dotnet-docker/blob/main/README.aspnet.md +FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-amd64 RUN \ apt-get update && \ diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index d3d0454b..d5847eb8 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit d3d0454b971a7bd509b55bbdc882bcd166023c52 +Subproject commit d5847eb8110297c129a21723c4f8a522b3337638 diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 01ba6f35..cb3221e8 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -10,49 +10,49 @@ "rollForward": false }, "dotnet-counters": { - "version": "9.0.621003", + "version": "9.0.652701", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "9.0.621003", + "version": "9.0.652701", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "9.0.10", + "version": "10.0.0", "commands": [ "dotnet-ef" ], "rollForward": false }, "dotnet-script": { - "version": "1.6.0", + "version": "2.0.0", "commands": [ "dotnet-script" ], "rollForward": false }, "dotnet-trace": { - "version": "9.0.621003", + "version": "9.0.652701", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "9.0.621003", + "version": "9.0.652701", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "9.0.621003", + "version": "9.0.652701", "commands": [ "dotnet-sos" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.2.3", + "version": "2025.3.0.3", "commands": [ "jb" ], diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 3cfde1f8..5782c671 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,8 +1,8 @@ - net9.0 - 13.0 + net10.0 + 14.0 31666837686487542159 enable @@ -35,33 +35,33 @@ - + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - + + - - - - - - - + + + + + + + - + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index b02c66bc..e3c9fd25 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,9 +1,9 @@ - net9.0 + net10.0 false - 13.0 + 14.0 false enable @@ -16,11 +16,11 @@ - + - - + + From a8609e1434d0631de5b94ce56b29deae8caf1c49 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Dec 2025 18:31:32 +0100 Subject: [PATCH 595/878] Forbid duplicate properties when (de)serializing JSON --- backend/src/Json/JsonSerializerSettings.cs | 1 + backend/src/Methods/MethodBase.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/Json/JsonSerializerSettings.cs b/backend/src/Json/JsonSerializerSettings.cs index 3d86a7a4..509acc07 100644 --- a/backend/src/Json/JsonSerializerSettings.cs +++ b/backend/src/Json/JsonSerializerSettings.cs @@ -22,6 +22,7 @@ public static class JsonSerializerSettings ReadCommentHandling = JsonCommentHandling.Disallow, RespectNullableAnnotations = true, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, + AllowDuplicateProperties = false, }; public static readonly JsonSerializerOptions GraphQl = diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index faf5b038..3e38038c 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -20,6 +20,7 @@ public abstract class MethodBase ReadCommentHandling = JsonCommentHandling.Skip, RespectNullableAnnotations = true, UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, + AllowDuplicateProperties = false, }; public JsonElement Calculate(JsonElement input) From a3a61033a3e3ccf735d90b2d75ca89d8a7016855 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 Dec 2025 17:07:00 +0100 Subject: [PATCH 596/878] Make the compiler give diagnostics for the whole solution (instead of just the open files) --- backend/.vscode/settings.json | 3 ++- .../src/ApiRequests/IsGnuPgFingerprintValid.cs | 2 +- .../src/Configuration/GraphQlConfiguration.cs | 11 +++++++++++ backend/src/Data/CalorimetricData.cs | 4 ++-- backend/src/Data/CrossDatabaseDataReference.cs | 4 ++-- backend/src/Data/DataApproval.cs | 6 +++--- backend/src/Data/DataX.cs | 8 ++++---- backend/src/Data/GeometricData.cs | 4 ++-- backend/src/Data/HygrothermalData.cs | 4 ++-- backend/src/Data/IApproval.cs | 2 +- backend/src/Data/IData.cs | 4 ++-- backend/src/Data/OpticalData.cs | 4 ++-- backend/src/Data/PhotovoltaicData.cs | 4 ++-- backend/src/Data/ResponseApproval.cs | 4 ++-- backend/src/Database.csproj | 2 ++ backend/src/Extensions/NodaTimeExtensions.cs | 17 +++++++++++++++++ .../CreateCalorimetricDataMutation.cs | 2 +- .../Common/OpenEndedDateTimeRangeInput.cs | 4 ++-- .../Common/OpenEndedDateTimeRangeType.cs | 8 ++++---- .../GraphQl/CrossDatabaseDataReferenceInput.cs | 2 +- .../DataApprovals/AddDataApprovalMutation.cs | 2 +- .../src/GraphQl/DataX/CreateDataMutationBase.cs | 4 ++-- backend/src/GraphQl/DataX/DataResolvers.cs | 4 ++-- backend/src/GraphQl/DataX/UpdateDataMutation.cs | 2 +- .../CreateGeometricDataMutation.cs | 2 +- .../CreateHygrothermalDataMutation.cs | 2 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 2 +- .../CreatePhotovoltaicDataMutation.cs | 2 +- backend/src/Services/DataApprovalService.cs | 2 +- backend/src/Services/ResponseApprovalService.cs | 3 ++- backend/src/Startup.cs | 4 ++-- 31 files changed, 80 insertions(+), 48 deletions(-) create mode 100644 backend/src/Extensions/NodaTimeExtensions.cs diff --git a/backend/.vscode/settings.json b/backend/.vscode/settings.json index 388b01e1..5c1847af 100644 --- a/backend/.vscode/settings.json +++ b/backend/.vscode/settings.json @@ -4,5 +4,6 @@ "editor.defaultFormatter": "ms-dotnettools.csharp" }, "workbench.editor.enablePreview": false, - "workbench.editor.enablePreviewFromQuickOpen": false + "workbench.editor.enablePreviewFromQuickOpen": false, + "dotnet.backgroundAnalysis.compilerDiagnosticsScope": "fullSolution" } \ No newline at end of file diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index 8e29c4a5..c8ea4f11 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -24,7 +24,7 @@ private sealed record Data(Institution? Institution); public static async Task Do( string fingerprint, Guid institutionId, - DateTime createdAt, + OffsetDateTime createdAt, AppSettings appSettings, ApiRequestService apiRequestService, CancellationToken cancellationToken diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b6d95bdb..b1f50eec 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -14,6 +14,7 @@ using Database.Data; using Database.GraphQl; using Database.GraphQl.DataX; +using NodaTime; namespace Database.Configuration; @@ -119,6 +120,16 @@ IWebHostEnvironment environment .AddType(new UrlType("Url")) .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar .AddType(new LocaleType()) + // Register converters between NodaTime's `OffsetDateTime` and .NET's + // `DateTimeOffset` to reuse the existing `DateTimeType` + // https://chillicream.com/docs/hotchocolate/v15/defining-a-schema/scalars#custom-converters + .BindRuntimeType() + .AddTypeConverter( + _ => _.ToDateTimeOffset() + ) + .AddTypeConverter( + _ => OffsetDateTime.FromDateTimeOffset(_) + ) // Object Types .AddType() // Query, Mutation, Subscription, Object, and Input Types diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index d8701689..b646cd30 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -17,7 +17,7 @@ public CalorimetricData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, AppliedMethod appliedMethod, double[] gValues, double[] uValues @@ -46,7 +46,7 @@ public CalorimetricData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, double[] gValues, double[] uValues ) : base( diff --git a/backend/src/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index 35061bb4..e8e506f4 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -7,13 +7,13 @@ namespace Database.Data; [Owned] public sealed class CrossDatabaseDataReference( Guid dataId, - DateTime dataTimestamp, + OffsetDateTime dataTimestamp, DataKind dataKind, Guid databaseId ) { public Guid DataId { get; private set; } = dataId; - public DateTime DataTimestamp { get; private set; } = dataTimestamp; + public OffsetDateTime DataTimestamp { get; private set; } = dataTimestamp; public DataKind DataKind { get; private set; } = dataKind; public Guid DatabaseId { get; private set; } = databaseId; } \ No newline at end of file diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 127b24a9..787728db 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -6,7 +6,7 @@ namespace Database.Data; [Owned] public sealed class DataApproval( - DateTime timestamp, + OffsetDateTime timestamp, string signature, string keyFingerprint, string query, @@ -19,7 +19,7 @@ Reference statement { // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties private DataApproval( - DateTime timestamp, + OffsetDateTime timestamp, string signature, string keyFingerprint, string query, @@ -41,7 +41,7 @@ Guid approverId } public Guid ApproverId { get; private set; } = approverId; - public DateTime Timestamp { get; private set; } = timestamp; + public OffsetDateTime Timestamp { get; private set; } = timestamp; public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 18203160..1ca1270f 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -14,7 +14,7 @@ public abstract class DataX( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + OffsetDateTime createdAt ) : Entity, IData { protected DataX( @@ -25,7 +25,7 @@ protected DataX( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, AppliedMethod appliedMethod ) : this( @@ -48,7 +48,7 @@ public void Update( string? name, string? description, string[] warnings, - DateTime createdAt, + OffsetDateTime createdAt, Guid creatorId ) { @@ -78,7 +78,7 @@ public void Retract() public string? Description { get; private set; } = description; public string[] Warnings { get; private set; } = warnings; public Guid CreatorId { get; private set; } = creatorId; - public DateTime CreatedAt { get; private set; } = createdAt; + public OffsetDateTime CreatedAt { get; private set; } = createdAt; public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = []; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 71f3c054..f112cb7e 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -17,7 +17,7 @@ public GeometricData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, AppliedMethod appliedMethod, double[] widths, double[] heights, @@ -47,7 +47,7 @@ public GeometricData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, double[] widths, double[] heights, double[] thicknesses diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index ea1fc6ab..41f003af 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -16,7 +16,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, AppliedMethod appliedMethod ) : base( userId, @@ -41,7 +41,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + OffsetDateTime createdAt ) : base( userId, locale, diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 788f1c2d..62904fcc 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -12,7 +12,7 @@ namespace Database.Data; [JsonDerivedType(typeof(DataApproval), typeDiscriminator: nameof(DataApproval))] public interface IApproval { - DateTime Timestamp { get; } + OffsetDateTime Timestamp { get; } string Signature { get; } string KeyFingerprint { get; } string Query { get; } diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index dcac8a89..e1a376ab 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -23,7 +23,7 @@ public interface IData : IEntity string? Description { get; } string[] Warnings { get; } Guid CreatorId { get; } - DateTime CreatedAt { get; } + OffsetDateTime CreatedAt { get; } AppliedMethod AppliedMethod { get; } ICollection Approvals { get; } ICollection Resources { get; } @@ -60,7 +60,7 @@ void Update( string? name, string? description, string[] warnings, - DateTime createdAt, + OffsetDateTime createdAt, Guid creatorId ); diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index e5e491b4..ab69ef2e 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -19,7 +19,7 @@ public OpticalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, CoatedSide? coatedSide, @@ -64,7 +64,7 @@ public OpticalData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, CoatedSide? coatedSide, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 69f5f6f7..c7646cff 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -16,7 +16,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt, + OffsetDateTime createdAt, AppliedMethod appliedMethod ) : base( userId, @@ -41,7 +41,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - DateTime createdAt + OffsetDateTime createdAt ) : base( userId, locale, diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index a0ae247b..954b75e8 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -6,7 +6,7 @@ namespace Database.Data; [Owned] public sealed class ResponseApproval( - DateTime timestamp, + OffsetDateTime timestamp, string signature, string keyFingerprint, string query, @@ -16,7 +16,7 @@ Guid approverId ) : IApproval { - public DateTime Timestamp { get; private set; } = timestamp; + public OffsetDateTime Timestamp { get; private set; } = timestamp; public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; public string Query { get; private set; } = query; diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 5782c671..3ed6bf97 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -33,6 +33,7 @@ + @@ -46,6 +47,7 @@ + diff --git a/backend/src/Extensions/NodaTimeExtensions.cs b/backend/src/Extensions/NodaTimeExtensions.cs new file mode 100644 index 00000000..9f004d36 --- /dev/null +++ b/backend/src/Extensions/NodaTimeExtensions.cs @@ -0,0 +1,17 @@ +using NodaTime; + +namespace Database.Extensions; + +public static class NodaTimeExtensions +{ + extension(OffsetDateTime) + { + public static OffsetDateTime UtcNow() + { + return + SystemClock.Instance + .GetCurrentInstant() + .WithOffset(Offset.Zero); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index b8402198..f9337fe1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -23,7 +23,7 @@ public sealed record CreateCalorimetricDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs index f465106b..b21ce903 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs @@ -3,6 +3,6 @@ namespace Database.GraphQl.Common; public sealed record OpenEndedDateTimeRangeInput( - DateTime? From, - DateTime? Until + OffsetDateTime? From, + OffsetDateTime? Until ); \ No newline at end of file diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs index 9c904140..864a0c95 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -5,10 +5,10 @@ namespace Database.GraphQl.Common; public sealed class OpenEndedDateTimeRangeType - : ObjectType> + : ObjectType> { protected override void Configure( - IObjectTypeDescriptor> descriptor + IObjectTypeDescriptor> descriptor ) { descriptor.BindFieldsExplicitly(); @@ -21,7 +21,7 @@ IObjectTypeDescriptor> descriptor .Type() .Resolve(context => { - var range = context.Parent>(); + var range = context.Parent>(); return range.LowerBoundInfinite ? null : range.LowerBound; @@ -33,7 +33,7 @@ IObjectTypeDescriptor> descriptor .Type() .Resolve(context => { - var range = context.Parent>(); + var range = context.Parent>(); return range.UpperBoundInfinite ? null : range.UpperBound; diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index c02c45e7..973fb718 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -6,7 +6,7 @@ namespace Database.GraphQl; public sealed record CrossDatabaseDataReferenceInput( Guid DataId, - DateTime DataTimestamp, + OffsetDateTime DataTimestamp, DataKind DataKind, Guid DatabaseId ) diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 5ad1ed16..2ba7ce19 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -26,7 +26,7 @@ public sealed record AddDataApprovalInput ( Guid DataId, DataKind DataKind, - DateTime Timestamp, + OffsetDateTime Timestamp, string Signature, string KeyFingerprint, string Query, diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 9288fa87..6a7660ce 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -15,7 +15,7 @@ namespace Database.GraphQl.DataX; public interface IValidateCreateInput { Guid ComponentId { get; } - DateTime CreatedAt { get; } + OffsetDateTime CreatedAt { get; } Guid CreatorId { get; } AppliedMethodInput AppliedMethod { get; } RootGetHttpsResourceInput RootResource { get; } @@ -55,7 +55,7 @@ CancellationToken cancellationToken ) ); } - if (input.CreatedAt > DateTime.UtcNow) + if (input.CreatedAt > OffsetDateTime.UtcNow) { errors.Add( NewError( diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 6f7b6c59..442ba5b5 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -37,8 +37,8 @@ [Parent] IData data return new GetHttpsResourceTree(data); } - public DateTime GetTimestamp() + public OffsetDateTime GetTimestamp() { - return DateTime.UtcNow; + return OffsetDateTime.UtcNow; } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index 35391342..9bd657b9 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -21,7 +21,7 @@ public sealed record UpdateDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId ) : IIdentifyDataInput; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 8d1205a1..4343e773 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -22,7 +22,7 @@ public sealed record CreateGeometricDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 4668cc71..2322305d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -21,7 +21,7 @@ public sealed record CreateHygrothermalDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 26ace80f..4db829ba 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -23,7 +23,7 @@ public sealed record CreateOpticalDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId, OpticalComponentType? Type, OpticalComponentSubtype? Subtype, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index d9a7476e..a497bdc1 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -23,7 +23,7 @@ public sealed record CreatePhotovoltaicDataInput( string? Name, string? Description, string[] Warnings, - DateTime CreatedAt, + OffsetDateTime CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs index 8837ccb1..b93e20f7 100644 --- a/backend/src/Services/DataApprovalService.cs +++ b/backend/src/Services/DataApprovalService.cs @@ -12,7 +12,7 @@ ApiRequestService apiRequestService public Task IsGnuPgFingerprintValid( string fingerprint, Guid institutionId, - DateTime createdAt, + OffsetDateTime createdAt, CancellationToken cancellationToken ) { diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 16cc22cd..e506c85c 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -11,6 +11,7 @@ using HotChocolate.Execution; using Database.ApiRequests; using System.Collections.Generic; +using Database.Extensions; namespace Database.Services; @@ -88,7 +89,7 @@ public async Task CreateResponseApproval(IData dataObject, Can } var (signature, fingerprint) = await signingService.SignData(response); return new ResponseApproval( - DateTime.UtcNow, + OffsetDateTime.UtcNow(), signature, fingerprint, query, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 60f4f224..f8799503 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -83,7 +83,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO _.AllowedHosts = ... _.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forward-the-scheme-for-linux-and-non-iis-reverse-proxies - _.KnownNetworks.Clear(); + _.KnownIPNetworks.Clear(); _.KnownProxies.Clear(); } ); @@ -177,6 +177,7 @@ AppSettings appSettings _ => _ .SetPostgresVersion(13, 13) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally + .UseNodaTime() .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) @@ -185,7 +186,6 @@ AppSettings appSettings .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.PublishingStateTypeName, appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) - // .UseNodaTime(); ) .UseSchemaName(appSettings.Database.SchemaName) .UseOpenIddict() From 0b7e5ec26560a8494ff341e37e72fc549f328840 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 Dec 2025 21:05:13 +0100 Subject: [PATCH 597/878] Use NodaTime to represent date/time/duration/timespans in a PostgreSQL compatible way --- see https://www.npgsql.org/doc/types/nodatime.html?tabs=datasource and https://www.npgsql.org/efcore/mapping/nodatime.html?tabs=ef9-with-connection-string --- .../ApiRequests/IsGnuPgFingerprintValid.cs | 1 + .../src/Configuration/GraphQlConfiguration.cs | 16 +++---- backend/src/Data/CalorimetricData.cs | 1 + .../src/Data/CrossDatabaseDataReference.cs | 1 + backend/src/Data/DataApproval.cs | 1 + backend/src/Data/DataX.cs | 1 + backend/src/Data/GeometricData.cs | 1 + backend/src/Data/HygrothermalData.cs | 1 + backend/src/Data/IApproval.cs | 1 + backend/src/Data/IData.cs | 1 + backend/src/Data/InstitutionAccessRights.cs | 12 ++--- backend/src/Data/OpticalData.cs | 1 + backend/src/Data/PhotovoltaicData.cs | 1 + backend/src/Data/ResponseApproval.cs | 1 + backend/src/Database.csproj | 1 + backend/src/Extensions/NodaTimeExtensions.cs | 17 ++++--- .../AddInstitutionAccessRightsMutation.cs | 3 +- .../UpdateInstitutionAccessRightsMutation.cs | 3 +- .../CreateCalorimetricDataMutation.cs | 1 + .../Common/OpenEndedDateTimeRangeInput.cs | 2 +- .../Common/OpenEndedDateTimeRangeType.cs | 4 +- .../CrossDatabaseDataReferenceInput.cs | 1 + .../DataApprovals/AddDataApprovalMutation.cs | 1 + .../GraphQl/DataX/CreateDataMutationBase.cs | 1 + backend/src/GraphQl/DataX/DataResolvers.cs | 2 + .../src/GraphQl/DataX/UpdateDataMutation.cs | 1 + .../CreateGeometricDataMutation.cs | 1 + .../CreateHygrothermalDataMutation.cs | 1 + .../OpticalDataX/CreateOpticalDataMutation.cs | 1 + .../CreatePhotovoltaicDataMutation.cs | 1 + backend/src/Json/JsonSerializerSettings.cs | 7 ++- backend/src/Methods/MethodBase.cs | 34 +++++++------- ...102_MirrorWidthAndHeightOfGeometricData.cs | 4 +- .../ApplicationDbContextModelSnapshot.cs | 45 ++++++++++--------- backend/src/Services/CacheService.cs | 17 ++++--- backend/src/Services/DataApprovalService.cs | 1 + .../src/Services/ResponseApprovalService.cs | 3 +- .../Integration/GraphQl/GraphQlSchemaTests.cs | 8 +++- backend/test/Integration/IntegrationTests.cs | 12 ++--- 39 files changed, 133 insertions(+), 79 deletions(-) diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index c8ea4f11..4e477c60 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Net.Http; using System.Threading; diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b1f50eec..b34d14f7 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Hosting; @@ -14,7 +15,6 @@ using Database.Data; using Database.GraphQl; using Database.GraphQl.DataX; -using NodaTime; namespace Database.Configuration; @@ -123,13 +123,13 @@ IWebHostEnvironment environment // Register converters between NodaTime's `OffsetDateTime` and .NET's // `DateTimeOffset` to reuse the existing `DateTimeType` // https://chillicream.com/docs/hotchocolate/v15/defining-a-schema/scalars#custom-converters - .BindRuntimeType() - .AddTypeConverter( - _ => _.ToDateTimeOffset() - ) - .AddTypeConverter( - _ => OffsetDateTime.FromDateTimeOffset(_) - ) + // .BindRuntimeType() + // .AddTypeConverter( + // _ => _.ToDateTimeOffset() + // ) + // .AddTypeConverter( + // _ => OffsetDateTime.FromDateTimeOffset(_) + // ) // Object Types .AddType() // Query, Mutation, Subscription, Object, and Input Types diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index b646cd30..5c3af10f 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index e8e506f4..28243f7e 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using Database.Enumerations; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 787728db..e1e6da8d 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 1ca1270f..6289763c 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.Linq; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index f112cb7e..44803846 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 41f003af..12e5b97a 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index 62904fcc..cff9dc13 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Text.Json; using System.Text.Json.Serialization; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index e1a376ab..31b39f03 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.Text.Json.Serialization; diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 0fb532b6..95dc8c2a 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; +using Database.Extensions; using Database.Services; using EntityFrameworkCore.Projectables; using Microsoft.EntityFrameworkCore; +using NodaTime; namespace Database.Data; @@ -12,14 +14,14 @@ public sealed class InstitutionAccessRights( Guid institutionId, uint? allowedUserCount, uint? allowedDatasetsPerTime, - TimeSpan period - ) - : Entity + Duration period +) +: Entity { public Guid InstitutionId { get; set; } = institutionId; public uint? AllowedUserCount { get; set; } = allowedUserCount; public uint? AllowedDatasetsPerTime { get; set; } = allowedDatasetsPerTime; - public TimeSpan Period { get; set; } = period; + public Duration Period { get; set; } = period; public List UserAlreadyAccessed { get; private set; } = []; [NotMapped] @@ -44,7 +46,7 @@ internal bool IsDataRestrictedByTime(IData dataItem, CacheService cacheService, if (AllowedDatasetsPerTime is not null) { var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPeriod(InstitutionId); - if (accessesPerPeriod.StartTime.Add(Period) < DateTime.Now) + if (accessesPerPeriod.StartTime + Period < OffsetDateTime.UtcNow) { if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) { diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index ab69ef2e..55678f93 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index c7646cff..950df818 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 954b75e8..019bc2c7 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 3ed6bf97..a94db20f 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -46,6 +46,7 @@ all + diff --git a/backend/src/Extensions/NodaTimeExtensions.cs b/backend/src/Extensions/NodaTimeExtensions.cs index 9f004d36..9f5edc65 100644 --- a/backend/src/Extensions/NodaTimeExtensions.cs +++ b/backend/src/Extensions/NodaTimeExtensions.cs @@ -6,12 +6,19 @@ public static class NodaTimeExtensions { extension(OffsetDateTime) { - public static OffsetDateTime UtcNow() + public static OffsetDateTime UtcNow => + SystemClock.Instance + .GetCurrentInstant() + .WithOffset(Offset.Zero); + + public static bool operator >(OffsetDateTime x, OffsetDateTime y) + { + return OffsetDateTime.Comparer.Instant.Compare(x, y) > 0; + } + + public static bool operator <(OffsetDateTime x, OffsetDateTime y) { - return - SystemClock.Instance - .GetCurrentInstant() - .WithOffset(Offset.Zero); + return OffsetDateTime.Comparer.Instant.Compare(x, y) < 0; } } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs index 7246620d..18b8b90c 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using Database.Extensions; +using NodaTime; namespace Database.GraphQl.AccessRights; @@ -99,7 +100,7 @@ CancellationToken cancellationToken input.InstitutionId, input.AllowedUserCount, input.AllowedDatasetsPerTimeSpan, - TimeSpan.FromDays(input.PeriodInDays)); + Duration.FromDays(input.PeriodInDays)); context.InstitutionAccessRights.Add(accessRights); await context.SaveChangesAsync(cancellationToken); return NewPayload(accessRights, null); diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs index 5fc0f6b6..68a8a002 100644 --- a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using Database.Extensions; +using NodaTime; namespace Database.GraphQl.AccessRights; @@ -97,7 +98,7 @@ CancellationToken cancellationToken accessRights.AllowedUserCount = input.AllowedUserCount; accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = TimeSpan.FromDays(input.PeriodInDays); + accessRights.Period = Duration.FromDays(input.PeriodInDays); await context.SaveChangesAsync(cancellationToken); return NewPayload(accessRights, null); } diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index f9337fe1..02043afc 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Linq; using System.Threading; diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs index b21ce903..a7015e0d 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeInput.cs @@ -1,4 +1,4 @@ -using DateTime = System.DateTime; +using NodaTime; namespace Database.GraphQl.Common; diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs index 864a0c95..7febcbe5 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -1,6 +1,6 @@ +using NodaTime; using HotChocolate.Types; using NpgsqlTypes; -using DateTime = System.DateTime; namespace Database.GraphQl.Common; @@ -14,7 +14,7 @@ IObjectTypeDescriptor> descriptor descriptor.BindFieldsExplicitly(); var suffixedName = nameof(OpenEndedDateTimeRangeType); - descriptor.Name(suffixedName.Remove(suffixedName.Length - "Type".Length)); + descriptor.Name(suffixedName[..^"Type".Length]); descriptor .Field("from") diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 973fb718..64f58761 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using Database.Data; using Database.Enumerations; diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 2ba7ce19..0112e5e5 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.Linq; diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 6a7660ce..27987001 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 442ba5b5..0196e891 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Threading; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using Database.GraphQl.GetHttpsResources; using HotChocolate.Resolvers; using Database.GraphQl.Extensions; +using Database.Extensions; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index 9bd657b9..4d69330a 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 4343e773..12a533a7 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Linq; using System.Threading; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 2322305d..d3a75b9e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 4db829ba..a343cb28 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index a497bdc1..1158602f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Linq; using System.Threading; diff --git a/backend/src/Json/JsonSerializerSettings.cs b/backend/src/Json/JsonSerializerSettings.cs index 509acc07..f23269f5 100644 --- a/backend/src/Json/JsonSerializerSettings.cs +++ b/backend/src/Json/JsonSerializerSettings.cs @@ -1,6 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using GraphQL.Client.Serializer.SystemTextJson; +using NodaTime; +using NodaTime.Serialization.SystemTextJson; namespace Database.Json; @@ -10,7 +12,7 @@ namespace Database.Json; public static class JsonSerializerSettings { private static readonly JsonSerializerOptions s_common = - new() + new JsonSerializerOptions() { AllowOutOfOrderMetadataProperties = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, @@ -23,7 +25,8 @@ public static class JsonSerializerSettings RespectNullableAnnotations = true, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, AllowDuplicateProperties = false, - }; + } + .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); public static readonly JsonSerializerOptions GraphQl = new(s_common) diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index 3e38038c..f87ecf43 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -1,27 +1,31 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using NodaTime; +using NodaTime.Serialization.SystemTextJson; namespace Database.Methods; public abstract class MethodBase : IMethod { - public static readonly JsonSerializerOptions JsonSerializerOptions = new() - { - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = false, - IncludeFields = false, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Skip, - RespectNullableAnnotations = true, - UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, - AllowDuplicateProperties = false, - }; + public static readonly JsonSerializerOptions JsonSerializerOptions = + new JsonSerializerOptions() + { + AllowTrailingCommas = true, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IgnoreReadOnlyFields = true, + IgnoreReadOnlyProperties = false, + IncludeFields = false, + NumberHandling = JsonNumberHandling.Strict, + PropertyNameCaseInsensitive = false, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + ReadCommentHandling = JsonCommentHandling.Skip, + RespectNullableAnnotations = true, + UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, + AllowDuplicateProperties = false, + } + .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); public JsonElement Calculate(JsonElement input) { diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs index 73f90eb7..38f1bbff 100644 --- a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs @@ -16,7 +16,7 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "geometric_data", type: "double precision[]", nullable: false, - defaultValue: new double[0]); + defaultValue: System.Array.Empty()); migrationBuilder.AddColumn( name: "Widths", @@ -24,7 +24,7 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "geometric_data", type: "double precision[]", nullable: false, - defaultValue: new double[0]); + defaultValue: System.Array.Empty()); } /// diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 372da4b6..04b42a39 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -21,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "9.0.10") + .HasAnnotation("ProductVersion", "10.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); @@ -45,7 +46,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .HasColumnType("timestamp with time zone"); b.Property("CreatorId") @@ -100,7 +101,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .HasColumnType("timestamp with time zone"); b.Property("CreatorId") @@ -247,7 +248,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .HasColumnType("timestamp with time zone"); b.Property("CreatorId") @@ -300,7 +301,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("InstitutionId") .HasColumnType("uuid"); - b.Property("Period") + b.Property("Period") .HasColumnType("interval"); b.PrimitiveCollection>("UserAlreadyAccessed") @@ -338,7 +339,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .HasColumnType("timestamp with time zone"); b.Property("CreatorId") @@ -411,7 +412,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .HasColumnType("timestamp with time zone"); b.Property("CreatorId") @@ -780,7 +781,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("DataKind") .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") + b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") @@ -827,7 +828,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -873,7 +874,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -1134,7 +1135,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("DataKind") .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") + b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") @@ -1181,7 +1182,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -1227,7 +1228,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -1610,7 +1611,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("DataKind") .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") + b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") @@ -1657,7 +1658,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -1703,7 +1704,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -2001,7 +2002,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("DataKind") .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") + b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") @@ -2048,7 +2049,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -2094,7 +2095,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -2357,7 +2358,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b3.Property("DataKind") .HasColumnType("database.data_kind"); - b3.Property("DataTimestamp") + b3.Property("DataTimestamp") .HasColumnType("timestamp with time zone"); b3.Property("DatabaseId") @@ -2404,7 +2405,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") @@ -2450,7 +2451,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); - b1.Property("Timestamp") + b1.Property("Timestamp") .HasColumnType("timestamp with time zone"); b1.Property("Variables") diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index d58f1a0a..a1881725 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,6 +1,8 @@ using System; using System.Diagnostics.CodeAnalysis; +using Database.Extensions; using Microsoft.Extensions.Caching.Memory; +using NodaTime; using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Services; @@ -12,7 +14,8 @@ public sealed class CacheService( { public CurrentUserOrApplication? SetCurrentUserOrApplication(string token, CurrentUserOrApplication cachedUserOrApplication) { - var cacheEntryOptions = new MemoryCacheEntryOptions() + var cacheEntryOptions = + new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); return currentUserOrApplicationCache.Set(token, cachedUserOrApplication, cacheEntryOptions); } @@ -36,24 +39,24 @@ public uint SetAccessCountForUser(Guid userId, uint count) return accessCountCache.Set(userId, count); } - public (DateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId) + public (OffsetDateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId) { - if (!timePeriodCountCache.TryGetValue(institutionId, out (DateTime StartTime, uint Count) accessesPerPeriod)) + if (!timePeriodCountCache.TryGetValue(institutionId, out (OffsetDateTime StartTime, uint Count) accessesPerPeriod)) { - return timePeriodCountCache.Set(institutionId, (DateTime.Now, (uint)0)); + return timePeriodCountCache.Set(institutionId, (OffsetDateTime.UtcNow, (uint)0)); } return accessesPerPeriod; } - public (DateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId) + public (OffsetDateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId) { var accessesPerPeriod = GetOrCreateAccessCountForPeriod(institutionId); accessesPerPeriod.Count++; return timePeriodCountCache.Set(institutionId, accessesPerPeriod); } - public (DateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId) + public (OffsetDateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId) { - return timePeriodCountCache.Set(institutionId, (DateTime.Now, (uint)0)); + return timePeriodCountCache.Set(institutionId, (OffsetDateTime.UtcNow, (uint)0)); } } \ No newline at end of file diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs index b93e20f7..cff92371 100644 --- a/backend/src/Services/DataApprovalService.cs +++ b/backend/src/Services/DataApprovalService.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index e506c85c..06f255d4 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,3 +1,4 @@ +using NodaTime; using System; using System.Text.Json; using System.Threading; @@ -89,7 +90,7 @@ public async Task CreateResponseApproval(IData dataObject, Can } var (signature, fingerprint) = await signingService.SignData(response); return new ResponseApproval( - OffsetDateTime.UtcNow(), + OffsetDateTime.UtcNow, signature, fingerprint, query, diff --git a/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs index e2198aaf..d9bc142f 100644 --- a/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs +++ b/backend/test/Integration/GraphQl/GraphQlSchemaTests.cs @@ -18,8 +18,14 @@ public async Task IsUnchanged() // Act var response = await HttpClient.GetAsync("/graphql?sdl"); // Assert - response.StatusCode.Should().Be(HttpStatusCode.OK); var schema = await response.Content.ReadAsStringAsync(); + response.StatusCode.Should().Be( + HttpStatusCode.OK, + "because {0} is not {1}. The response is {2}", + response.StatusCode, + HttpStatusCode.OK, + schema + ); Snapshot.Match(schema); } } \ No newline at end of file diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs index d18af115..a5aa72ac 100644 --- a/backend/test/Integration/IntegrationTests.cs +++ b/backend/test/Integration/IntegrationTests.cs @@ -10,16 +10,15 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.Json.Serialization; -using System.Text.RegularExpressions; using System.Threading.Tasks; using FluentAssertions; -using IdentityModel.Client; using Json.Path; using NUnit.Framework; using Snapshooter; -using TokenResponse = IdentityModel.Client.TokenResponse; -using WebApplicationFactoryClientOptions = Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions; +using Microsoft.AspNetCore.Mvc.Testing; using Database.Data; +using NodaTime.Serialization.SystemTextJson; +using NodaTime; namespace Database.Tests.Integration; @@ -28,11 +27,12 @@ public abstract partial class IntegrationTests : IDisposable { private static readonly JsonSerializerOptions s_customJsonSerializerOptions = - new() + new JsonSerializerOptions() { Converters = { new JsonStringEnumConverter() }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; + } + .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); private bool _disposed; From ea9078b12b716b41dd058f64935e605c2e09e739 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Dec 2025 16:23:25 +0100 Subject: [PATCH 598/878] Target the property of the record with the `GraphQLType` data annotation --- .../CalorimetricDataX/CreateCalorimetricDataMutation.cs | 5 +---- .../GraphQl/GeometricDataX/CreateGeometricDataMutation.cs | 2 +- .../HygrothermalDataX/CreateHygrothermalDataMutation.cs | 3 +-- .../src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs | 3 +-- .../PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs | 3 +-- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 02043afc..be567d74 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -1,13 +1,11 @@ using NodaTime; using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Services; -using Database.Utilities; using HotChocolate; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -18,8 +16,7 @@ namespace Database.GraphQl.CalorimetricDataX; public sealed record CreateCalorimetricDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, + [property: GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 12a533a7..ce222d80 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -18,7 +18,7 @@ namespace Database.GraphQl.GeometricDataX; public sealed record CreateGeometricDataInput( - [GraphQLType>] string Locale, + [property: GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index d3a75b9e..43c9d748 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -16,8 +16,7 @@ namespace Database.GraphQl.HygrothermalDataX; public sealed record CreateHygrothermalDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, + [property: GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index a343cb28..da78cd07 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -18,8 +18,7 @@ namespace Database.GraphQl.OpticalDataX; public sealed record CreateOpticalDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, + [property: GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 1158602f..75f890a9 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -18,8 +18,7 @@ namespace Database.GraphQl.PhotovoltaicDataX; public sealed record CreatePhotovoltaicDataInput( - // TODO Why does specifying the type with an attribute not work here? - [GraphQLType>] string Locale, + [property: GraphQLType>] string Locale, Guid ComponentId, string? Name, string? Description, From d33054d83bbfde45ae2c74d7165575e2427ea707 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Dec 2025 16:24:27 +0100 Subject: [PATCH 599/878] Use `GraphQLType` with generics (instead of string parameter) --- backend/src/GraphQl/Methods/MethodQueries.cs | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 2278 ----------------- 2 files changed, 1 insertion(+), 2279 deletions(-) diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index d60dc617..ce21d262 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -63,7 +63,7 @@ Guid DataFormatId public async Task CalculateMethodWithDataUploadAsync( Guid methodId, - [GraphQLType(typeof(NonNullType))] IFile data, + [GraphQLType>] IFile data, MethodFactory methodFactory, CancellationToken cancellationToken ) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 46e145cf..e69de29b 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -1,2278 +0,0 @@ -schema { - query: Query - mutation: Mutation -} - -interface Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - timestamp: DateTime! - variables: Any! -} - -interface Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources: [GetHttpsResource!]! - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! - warnings: [String!]! -} - -interface GetHttpsResourceTreeVertex { - value: GetHttpsResource! - vertexId: ID -} - -"The node interface is implemented by entities that have a global unique identifier." -interface Node { - id: ID! -} - -interface UserError { - message: String! - path: [String!]! -} - -type AddDataApprovalError implements UserError { - code: AddDataApprovalErrorCode! - message: String! - path: [String!]! -} - -type AddDataApprovalPayload { - dataApproval: DataApproval - errors: [AddDataApprovalError!] - query: Query! -} - -type AddInstitutionAccessRightsError implements UserError { - code: AddInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type AddInstitutionAccessRightsPayload { - errors: [AddInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - -type Address { - formatted: String! -} - -type AppliedMethod { - arguments: [NamedMethodArgument!]! - methodId: Uuid! - sources: [NamedMethodSource!]! -} - -type CalculateMethodError implements UserError { - code: CalculateMethodErrorCode! - message: String! - path: [String!]! -} - -type CalculateMethodPayload { - errors: [CalculateMethodError!] - result: Any -} - -type CalorimetricData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - gValues: [Float!]! - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - uValues: [Float!]! - warnings: [String!]! -} - -"A connection to a list of items." -type CalorimetricDataConnection { - "A list of edges." - edges: [CalorimetricDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type CalorimetricDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: CalorimetricData! -} - -type CielabColor { - aStar: Float! - bStar: Float! - illuminant: Illuminant - lStar: Float! - observer: CalorimetricObserver -} - -type CreateCalorimetricDataError implements UserError { - code: CreateCalorimetricDataErrorCode! - message: String! - path: [String!]! -} - -type CreateCalorimetricDataPayload { - calorimetricData: CalorimetricData - errors: [CreateCalorimetricDataError!] - query: Query! -} - -type CreateGeometricDataError implements UserError { - code: CreateGeometricDataErrorCode! - message: String! - path: [String!]! -} - -type CreateGeometricDataPayload { - errors: [CreateGeometricDataError!] - geometricData: GeometricData - query: Query! -} - -type CreateGetHttpsResourceError implements UserError { - code: CreateGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type CreateGetHttpsResourcePayload { - errors: [CreateGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type CreateHygrothermalDataError implements UserError { - code: CreateHygrothermalDataErrorCode! - message: String! - path: [String!]! -} - -type CreateHygrothermalDataPayload { - errors: [CreateHygrothermalDataError!] - hygrothermalData: HygrothermalData - query: Query! -} - -type CreateOpticalDataError implements UserError { - code: CreateOpticalDataErrorCode! - message: String! - path: [String!]! -} - -type CreateOpticalDataPayload { - errors: [CreateOpticalDataError!] - opticalData: OpticalData - query: Query! -} - -type CreatePhotovoltaicDataError implements UserError { - code: CreatePhotovoltaicDataErrorCode! - message: String! - path: [String!]! -} - -type CreatePhotovoltaicDataPayload { - errors: [CreatePhotovoltaicDataError!] - photovoltaicData: PhotovoltaicData - query: Query! -} - -type CreateResponseApprovalsError implements UserError { - code: CreateResponseApprovalsErrorCode! - message: String! - path: [String!]! -} - -type CreateResponseApprovalsPayload { - data: [Data!] - errors: [CreateResponseApprovalsError!] - query: Query! -} - -type CrossDatabaseDataReference { - databaseId: Uuid! - dataId: Uuid! - dataKind: DataKind! - dataTimestamp: DateTime! -} - -type DataAccessRights { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] - hasRestrictions: Boolean! - hasRestrictionsByApplication: Boolean! - hasRestrictionsByInstitution: Boolean! - hasRestrictionsByUser: Boolean! -} - -type DataApproval implements Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - statement: Reference @cost(weight: "10") - timestamp: DateTime! - variables: Any! -} - -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - -type Database { - description: String! - isAuthorizedToUpdateNode: Boolean! - isAuthorizedToVerifyNode: Boolean! - locator: Url! - name: String! - operator: DatabaseOperatorEdge! - uuid: Uuid! - verificationCode: String! - verificationState: DatabaseVerificationState! -} - -type DatabaseOperatorEdge { - node: Institution! -} - -type DeleteDataError implements UserError { - code: DeleteDataErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataPayload { - errors: [DeleteDataError!] - query: Query! -} - -type DeleteGetHttpsResourceError implements UserError { - code: DeleteGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type DeleteGetHttpsResourcePayload { - errors: [DeleteGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type FileMetaInformation { - dataFormatId: Uuid! - path: [String!]! -} - -type GeometricData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - heights: [Float!]! - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - thicknesses: [Float!]! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! - widths: [Float!]! -} - -"A connection to a list of items." -type GeometricDataConnection { - "A list of edges." - edges: [GeometricDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type GeometricDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: GeometricData! -} - -type GetHttpsResource implements Node { - appliedConversionMethod: ToTreeVertexAppliedConversionMethod - archivedFilesMetaInformation: [FileMetaInformation!]! - children: [GetHttpsResource!]! @cost(weight: "10") - data: Data @cost(weight: "10") - dataFormatId: Uuid! - description: String - doesFileExist: Boolean! - hashValue: String! - id: ID! - isChild: Boolean! - isRoot: Boolean! - locator: Url! - parent: GetHttpsResource @cost(weight: "10") - uuid: Uuid! @cost(weight: "10") -} - -"A connection to a list of items." -type GetHttpsResourceConnection { - "A list of edges." - edges: [GetHttpsResourceEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type GetHttpsResourceEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: GetHttpsResource! -} - -type GetHttpsResourceTree { - nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") - root: GetHttpsResourceTreeRoot! @cost(weight: "10") -} - -type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { - appliedConversionMethod: ToTreeVertexAppliedConversionMethod! - parentId: ID! - value: GetHttpsResource! - vertexId: ID! -} - -type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { - value: GetHttpsResource! - vertexId: ID! -} - -type HygrothermalData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type HygrothermalDataConnection { - "A list of edges." - edges: [HygrothermalDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type HygrothermalDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: HygrothermalData! -} - -type Institution { - uuid: Uuid! -} - -type InstitutionAccessRights { - allowedDatasetsPerTime: NonNegativeInt - allowedUserCount: NonNegativeInt - hasRestrictions: Boolean! - hasRestrictionsByTime: Boolean! - hasRestrictionsByUser: Boolean! - id: Uuid! - institutionId: Uuid! - period: TimeSpan! - userAlreadyAccessed: [Uuid!]! - version: NonNegativeInt! -} - -type KeyValuePairOfGuidAndNullableOfUInt32 { - key: Uuid! - value: NonNegativeInt -} - -type Mutation { - addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") - createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") - createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") - createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") - createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") - deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") - publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") - retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") - setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") - updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") - updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") - updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") - updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") -} - -type NamedMethodArgument { - name: String! - value: Any! -} - -type NamedMethodSource { - name: String! - value: CrossDatabaseDataReference! -} - -type Numeration { - mainNumber: String! - prefix: String - suffix: String -} - -type OpenEndedDateTimeRange { - from: DateTime @cost(weight: "10") - until: DateTime @cost(weight: "10") -} - -type OpticalData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - cielabColors: [CielabColor!]! - coatedSide: CoatedSide - colorRenderingIndices: [Float!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - infraredEmittances: [Float!]! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - nearnormalHemisphericalSolarReflectances: [Float!]! - nearnormalHemisphericalSolarTransmittances: [Float!]! - nearnormalHemisphericalVisibleReflectances: [Float!]! - nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - subtype: OpticalComponentSubtype - timestamp: DateTime! - type: OpticalComponentType - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type OpticalDataConnection { - "A list of edges." - edges: [OpticalDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type OpticalDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: OpticalData! -} - -"Information about pagination in a connection." -type PageInfo { - "When paginating forwards, the cursor to continue." - endCursor: String - "Indicates whether more edges exist following the set defined by the clients arguments." - hasNextPage: Boolean! - "Indicates whether more edges exist prior the set defined by the clients arguments." - hasPreviousPage: Boolean! - "When paginating backwards, the cursor to continue." - startCursor: String -} - -type PhotovoltaicData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type PhotovoltaicDataConnection { - "A list of edges." - edges: [PhotovoltaicDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type PhotovoltaicDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: PhotovoltaicData! -} - -type Publication { - abstract: String - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: String - authors: [String!] - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: String - "Referenced section" - section: String - title: String - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: String - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: Url -} - -type PublishDataError implements UserError { - code: PublishDataErrorCode! - message: String! - path: [String!]! -} - -type PublishDataPayload { - data: Data - errors: [PublishDataError!] - query: Query! -} - -type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") - currentUser: User @cost(weight: "10") - currentUserInfo: UserInfo! @cost(weight: "10") - data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") - database: Database! @cost(weight: "10") - geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") - getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") - "Fetches an object given its ID." - node("ID of the object." id: ID!): Node @cost(weight: "10") - "Lookup nodes by a list of IDs." - nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") - pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") - verificationCode: String! -} - -type RecomputeGetHttpsResourceHashValuesError implements UserError { - code: RecomputeGetHttpsResourceHashValuesErrorCode! - message: String! - path: [String!]! -} - -type RecomputeGetHttpsResourceHashValuesPayload { - errors: [RecomputeGetHttpsResourceHashValuesError!] - getHttpsResources: [GetHttpsResource!] - query: Query! -} - -type RemoveDataApprovalError implements UserError { - code: RemoveDataApprovalErrorCode! - message: String! - path: [String!]! -} - -type RemoveDataApprovalPayload { - dataApproval: DataApproval - errors: [RemoveDataApprovalError!] - query: Query! -} - -type ResponseApproval implements Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - timestamp: DateTime! - variables: Any! -} - -type RetractDataError implements UserError { - code: RetractDataErrorCode! - message: String! - path: [String!]! -} - -type RetractDataPayload { - data: Data - errors: [RetractDataError!] - query: Query! -} - -type SetGetHttpsResourceParentError implements UserError { - code: SetGetHttpsResourceParentErrorCode! - message: String! - path: [String!]! -} - -type SetGetHttpsResourceParentPayload { - errors: [SetGetHttpsResourceParentError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." -type Standard { - abstract: String - locator: Url - numeration: Numeration! - "The section of the standard to which the reference refers to." - section: String - standardizers: [Standardizer!]! - title: String - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: Int -} - -type ToTreeVertexAppliedConversionMethod { - arguments: [NamedMethodArgument!]! - methodId: Uuid! - sourceName: String! -} - -type UpdateChildGetHttpsResourceError implements UserError { - code: UpdateChildGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type UpdateChildGetHttpsResourcePayload { - errors: [UpdateChildGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type UpdateDataAccessRightsError implements UserError { - code: UpdateDataAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateDataAccessRightsPayload { - dataAccessRights: DataAccessRights - errors: [UpdateDataAccessRightsError!] - query: Query! -} - -type UpdateDataError implements UserError { - code: UpdateDataErrorCode! - message: String! - path: [String!]! -} - -type UpdateDataPayload { - data: Data - errors: [UpdateDataError!] - query: Query! -} - -type UpdateDatabaseError { - code: UpdateDatabaseErrorCode! - message: String! - path: [String!]! -} - -type UpdateDatabasePayload { - database: Database - errors: [UpdateDatabaseError!] - query: Query! -} - -type UpdateInstitutionAccessRightsError implements UserError { - code: UpdateInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateInstitutionAccessRightsPayload { - errors: [UpdateInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - -type UpdateResponseApprovalsError implements UserError { - code: UpdateResponseApprovalsErrorCode! - message: String! - path: [String!]! -} - -type UpdateResponseApprovalsPayload { - data: [Data!] - errors: [UpdateResponseApprovalsError!] - query: Query! -} - -type UpdateRootGetHttpsResourceError implements UserError { - code: UpdateRootGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type UpdateRootGetHttpsResourcePayload { - errors: [UpdateRootGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type User implements Node { - id: ID! - name: String! - subject: String! - uuid: Uuid! @cost(weight: "10") -} - -type UserInfo { - address: Address - email: String! - emailVerified: Boolean! - name: String! - phoneNumber: String - phoneNumberVerified: Boolean! - roles: [String!] - sub: String! - website: String -} - -union Reference = Publication | Standard - -input AddDataApprovalInput { - approverId: Uuid! - dataId: Uuid! - dataKind: DataKind! - keyFingerprint: String! - message: String! - query: String! - signature: String! - statement: ReferenceInput! - timestamp: DateTime! - variables: Any! -} - -input AddInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - -input AppliedMethodInput { - arguments: [NamedMethodArgumentInput!]! - methodId: Uuid! - sources: [NamedMethodSourceInput!]! -} - -input AppliedMethodPropositionInput { - and: [AppliedMethodPropositionInput!] - or: [AppliedMethodPropositionInput!] - methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput - sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput -} - -input AppliedMethodSortInput { - methodId: SortEnumType @cost(weight: "10") -} - -input BooleanPropositionInput { - equalTo: Boolean @cost(weight: "10") - notEqualTo: Boolean @cost(weight: "10") -} - -input CalorimetricDataPropositionInput { - and: [CalorimetricDataPropositionInput!] - or: [CalorimetricDataPropositionInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput - gValues: FloatsPropositionInput - uValues: FloatsPropositionInput -} - -input CalorimetricDataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input CielabColorInput { - aStar: Float! - bStar: Float! - illuminant: Illuminant - lStar: Float! - observer: CalorimetricObserver -} - -input CielabColorPropositionInput { - and: [CielabColorPropositionInput!] - or: [CielabColorPropositionInput!] - lStar: FloatPropositionInput - aStar: FloatPropositionInput - bStar: FloatPropositionInput - observer: NullableOfCalorimetricObserverPropositionInput - illuminant: NullableOfIlluminantPropositionInput -} - -input CreateCalorimetricDataInput { - appliedMethod: AppliedMethodInput! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - description: String - gValues: [Float!]! - locale: String! - name: String - rootResource: RootGetHttpsResourceInput! - uValues: [Float!]! - warnings: [String!]! -} - -input CreateGeometricDataInput { - appliedMethod: AppliedMethodInput! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - description: String - heights: [Float!]! - locale: String! - name: String - rootResource: RootGetHttpsResourceInput! - thicknesses: [Float!]! - warnings: [String!]! - widths: [Float!]! -} - -input CreateGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - dataId: Uuid! - dataKind: DataKind! - description: String! - parentId: Uuid! -} - -input CreateHygrothermalDataInput { - appliedMethod: AppliedMethodInput! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - description: String - locale: String! - name: String - rootResource: RootGetHttpsResourceInput! - warnings: [String!]! -} - -input CreateOpticalDataInput { - appliedMethod: AppliedMethodInput! - cielabColors: [CielabColorInput!]! - coatedSide: CoatedSide - colorRenderingIndices: [Float!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - description: String - infraredEmittances: [Float!]! - locale: String! - name: String - nearnormalHemisphericalSolarReflectances: [Float!]! - nearnormalHemisphericalSolarTransmittances: [Float!]! - nearnormalHemisphericalVisibleReflectances: [Float!]! - nearnormalHemisphericalVisibleTransmittances: [Float!]! - rootResource: RootGetHttpsResourceInput! - subtype: OpticalComponentSubtype - type: OpticalComponentType - warnings: [String!]! -} - -input CreatePhotovoltaicDataInput { - appliedMethod: AppliedMethodInput! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - description: String - locale: String! - name: String - rootResource: RootGetHttpsResourceInput! - warnings: [String!]! -} - -input CreateResponseApprovalsFilterInput { - and: [CreateResponseApprovalsFilterInput!] - or: [CreateResponseApprovalsFilterInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput -} - -input CrossDatabaseDataReferenceInput { - databaseId: Uuid! - dataId: Uuid! - dataKind: DataKind! - dataTimestamp: DateTime! -} - -input CrossDatabaseDataReferencePropositionInput { - and: [CrossDatabaseDataReferencePropositionInput!] - or: [CrossDatabaseDataReferencePropositionInput!] - dataId: UuidPropositionInput - dataTimestamp: DateTimePropositionInput - dataKind: DataKindPropositionInput - databaseId: UuidPropositionInput -} - -input DataApprovalPropositionInput { - and: [DataApprovalPropositionInput!] - or: [DataApprovalPropositionInput!] - approverId: UuidPropositionInput - timestamp: DateTimePropositionInput - signature: StringPropositionInput - keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput - message: StringPropositionInput - statement: ReferencePropositionInput -} - -input DataFilterInput { - and: [DataFilterInput!] - or: [DataFilterInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput -} - -input DataKindPropositionInput { - equalTo: DataKind @cost(weight: "10") - notEqualTo: DataKind @cost(weight: "10") - in: [DataKind!] @cost(weight: "10") - notIn: [DataKind!] @cost(weight: "10") -} - -input DataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input DateTimePropositionInput { - equalTo: DateTime @cost(weight: "10") - notEqualTo: DateTime @cost(weight: "10") - in: [DateTime!] @cost(weight: "10") - notIn: [DateTime!] @cost(weight: "10") - greaterThan: DateTime @cost(weight: "10") - notGreaterThan: DateTime @cost(weight: "10") - greaterThanOrEqualTo: DateTime @cost(weight: "10") - notGreaterThanOrEqualTo: DateTime @cost(weight: "10") - lessThan: DateTime @cost(weight: "10") - notLessThan: DateTime @cost(weight: "10") - lessThanOrEqualTo: DateTime @cost(weight: "10") - notLessThanOrEqualTo: DateTime @cost(weight: "10") -} - -input DeleteDataInput { - dataId: Uuid! - dataKind: DataKind! -} - -input DeleteGetHttpsResourceInput { - getHttpsResourceId: Uuid! -} - -input FileMetaInformationInput { - dataFormatId: Uuid! - path: [String!]! -} - -input FileMetaInformationPropositionInput { - and: [FileMetaInformationPropositionInput!] - or: [FileMetaInformationPropositionInput!] - path: StringsPropositionInput - dataFormatId: UuidPropositionInput -} - -input FloatPropositionInput { - equalTo: Float @cost(weight: "10") - notEqualTo: Float @cost(weight: "10") - in: [Float!] @cost(weight: "10") - notIn: [Float!] @cost(weight: "10") - greaterThan: Float @cost(weight: "10") - notGreaterThan: Float @cost(weight: "10") - greaterThanOrEqualTo: Float @cost(weight: "10") - notGreaterThanOrEqualTo: Float @cost(weight: "10") - lessThan: Float @cost(weight: "10") - notLessThan: Float @cost(weight: "10") - lessThanOrEqualTo: Float @cost(weight: "10") - notLessThanOrEqualTo: Float @cost(weight: "10") -} - -input FloatsPropositionInput { - all: FloatPropositionInput @cost(weight: "10") - none: FloatPropositionInput @cost(weight: "10") - some: FloatPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input GeometricDataPropositionInput { - and: [GeometricDataPropositionInput!] - or: [GeometricDataPropositionInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput - thicknesses: FloatsPropositionInput -} - -input GeometricDataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input GetHttpsResourcePropositionInput { - and: [GetHttpsResourcePropositionInput!] - or: [GetHttpsResourcePropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput - appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput - calorimetricData: CalorimetricDataPropositionInput - geometricData: GeometricDataPropositionInput - hygrothermalData: HygrothermalDataPropositionInput - opticalData: OpticalDataPropositionInput - photovoltaicData: PhotovoltaicDataPropositionInput -} - -input GetHttpsResourceSortInput { - id: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - hashValue: SortEnumType @cost(weight: "10") - dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") -} - -input GetHttpsResourceTreeNonRootVertSortInput { - id: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - hashValue: SortEnumType @cost(weight: "10") - dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") -} - -input GetHttpsResourceTreeNonRootVertexFilterInput { - and: [GetHttpsResourceTreeNonRootVertexFilterInput!] - or: [GetHttpsResourceTreeNonRootVertexFilterInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput - appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput -} - -input HygrothermalDataPropositionInput { - and: [HygrothermalDataPropositionInput!] - or: [HygrothermalDataPropositionInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput -} - -input HygrothermalDataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input Int32PropositionInput { - equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int!] @cost(weight: "10") - notIn: [Int!] @cost(weight: "10") - greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") - greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") - lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") - lessThanOrEqualTo: Int @cost(weight: "10") - notLessThanOrEqualTo: Int @cost(weight: "10") -} - -input JsonElementPropositionInput { - and: [JsonElementPropositionInput!] - or: [JsonElementPropositionInput!] - valueKind: JsonValueKindPropositionInput -} - -input JsonValueKindPropositionInput { - equalTo: JsonValueKind @cost(weight: "10") - notEqualTo: JsonValueKind @cost(weight: "10") - in: [JsonValueKind!] @cost(weight: "10") - notIn: [JsonValueKind!] @cost(weight: "10") -} - -input KeyValuePairOfGuidAndNullableOfUInt32Input { - key: Uuid! - value: NonNegativeInt -} - -input ListFilterInputTypeOfCielabColorPropositionInput { - all: CielabColorPropositionInput @cost(weight: "10") - none: CielabColorPropositionInput @cost(weight: "10") - some: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfDataApprovalPropositionInput { - all: DataApprovalPropositionInput @cost(weight: "10") - none: DataApprovalPropositionInput @cost(weight: "10") - some: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfFileMetaInformationPropositionInput { - all: FileMetaInformationPropositionInput @cost(weight: "10") - none: FileMetaInformationPropositionInput @cost(weight: "10") - some: FileMetaInformationPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfGetHttpsResourcePropositionInput { - all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { - all: NamedMethodArgumentPropositionInput @cost(weight: "10") - none: NamedMethodArgumentPropositionInput @cost(weight: "10") - some: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListStandardizerPropositionInput { - all: StandardizerPropositionInput @cost(weight: "10") - none: StandardizerPropositionInput @cost(weight: "10") - some: StandardizerPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input NamedMethodArgumentInput { - name: String! - value: Any! -} - -input NamedMethodArgumentPropositionInput { - and: [NamedMethodArgumentPropositionInput!] - or: [NamedMethodArgumentPropositionInput!] - name: StringPropositionInput -} - -input NamedMethodArgumentSortInput { - name: SortEnumType @cost(weight: "10") -} - -input NamedMethodSourceInput { - name: String! - value: CrossDatabaseDataReferenceInput! -} - -input NamedMethodSourcePropositionInput { - and: [NamedMethodSourcePropositionInput!] - or: [NamedMethodSourcePropositionInput!] - name: StringPropositionInput - value: CrossDatabaseDataReferencePropositionInput -} - -input NullableOfCalorimetricObserverPropositionInput { - equalTo: CalorimetricObserver @cost(weight: "10") - notEqualTo: CalorimetricObserver @cost(weight: "10") - in: [CalorimetricObserver] @cost(weight: "10") - notIn: [CalorimetricObserver] @cost(weight: "10") -} - -input NullableOfCoatedSidePropositionInput { - equalTo: CoatedSide @cost(weight: "10") - notEqualTo: CoatedSide @cost(weight: "10") - in: [CoatedSide] @cost(weight: "10") - notIn: [CoatedSide] @cost(weight: "10") -} - -input NullableOfIlluminantPropositionInput { - equalTo: Illuminant @cost(weight: "10") - notEqualTo: Illuminant @cost(weight: "10") - in: [Illuminant] @cost(weight: "10") - notIn: [Illuminant] @cost(weight: "10") -} - -input NullableOfInt32PropositionInput { - equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - notIn: [Int] @cost(weight: "10") - greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") - greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") - lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") - lessThanOrEqualTo: Int @cost(weight: "10") - notLessThanOrEqualTo: Int @cost(weight: "10") -} - -input NullableOfOpticalComponentSubtypePropositionInput { - equalTo: OpticalComponentSubtype @cost(weight: "10") - notEqualTo: OpticalComponentSubtype @cost(weight: "10") - in: [OpticalComponentSubtype] @cost(weight: "10") - notIn: [OpticalComponentSubtype] @cost(weight: "10") -} - -input NullableOfOpticalComponentTypePropositionInput { - equalTo: OpticalComponentType @cost(weight: "10") - notEqualTo: OpticalComponentType @cost(weight: "10") - in: [OpticalComponentType] @cost(weight: "10") - notIn: [OpticalComponentType] @cost(weight: "10") -} - -input NullableOfUuidPropositionInput { - equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid] @cost(weight: "10") - notIn: [Uuid] @cost(weight: "10") - greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") - greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") - lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") - lessThanOrEqualTo: Uuid @cost(weight: "10") - notLessThanOrEqualTo: Uuid @cost(weight: "10") -} - -input NumerationInput { - mainNumber: String! - prefix: String - suffix: String -} - -input NumerationPropositionInput { - and: [NumerationPropositionInput!] - or: [NumerationPropositionInput!] - prefix: StringPropositionInput - mainNumber: StringPropositionInput - suffix: StringPropositionInput -} - -input NumerationSortInput { - prefix: SortEnumType @cost(weight: "10") - mainNumber: SortEnumType @cost(weight: "10") - suffix: SortEnumType @cost(weight: "10") -} - -input OpticalDataPropositionInput { - and: [OpticalDataPropositionInput!] - or: [OpticalDataPropositionInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput - type: NullableOfOpticalComponentTypePropositionInput - subtype: NullableOfOpticalComponentSubtypePropositionInput - coatedSide: NullableOfCoatedSidePropositionInput - nearnormalHemisphericalSolarReflectances: FloatsPropositionInput - nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput - nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput - nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput - infraredEmittances: FloatsPropositionInput - colorRenderingIndices: FloatsPropositionInput - cielabColors: ListFilterInputTypeOfCielabColorPropositionInput -} - -input OpticalDataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input PhotovoltaicDataPropositionInput { - and: [PhotovoltaicDataPropositionInput!] - or: [PhotovoltaicDataPropositionInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput -} - -input PhotovoltaicDataSortInput { - id: SortEnumType @cost(weight: "10") - locale: SortEnumType @cost(weight: "10") - name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") -} - -input PublicationInput { - abstract: String - arXiv: String - authors: [String!] - doi: String - section: String - title: String - urn: String - webAddress: Url -} - -input PublicationPropositionInput { - and: [PublicationPropositionInput!] - or: [PublicationPropositionInput!] - authors: StringsPropositionInput - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: StringPropositionInput - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: StringPropositionInput - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: StringPropositionInput - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: UriPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput - "Referenced section" - section: StringPropositionInput -} - -input PublicationSortInput { - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: SortEnumType @cost(weight: "10") - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: SortEnumType @cost(weight: "10") - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: SortEnumType @cost(weight: "10") - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") - abstract: SortEnumType @cost(weight: "10") - "Referenced section" - section: SortEnumType @cost(weight: "10") -} - -input PublishDataInput { - dataId: Uuid! - dataKind: DataKind! -} - -input RecomputeGetHttpsResourceHashValuesFilterInput { - and: [RecomputeGetHttpsResourceHashValuesFilterInput!] - or: [RecomputeGetHttpsResourceHashValuesFilterInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput - appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput - calorimetricData: CalorimetricDataPropositionInput - geometricData: GeometricDataPropositionInput - hygrothermalData: HygrothermalDataPropositionInput - opticalData: OpticalDataPropositionInput - photovoltaicData: PhotovoltaicDataPropositionInput -} - -input ReferenceInput { - publication: PublicationInput - standard: StandardInput -} - -input ReferencePropositionInput { - and: [ReferencePropositionInput!] - or: [ReferencePropositionInput!] - standard: StandardPropositionInput - publication: PublicationPropositionInput -} - -input ReferenceSortInput { - standard: StandardSortInput @cost(weight: "10") - publication: PublicationSortInput @cost(weight: "10") -} - -input RemoveDataApprovalInput { - dataId: Uuid! - dataKind: DataKind! - signature: String! -} - -input RetractDataInput { - dataId: Uuid! - dataKind: DataKind! -} - -input RootGetHttpsResourceInput { - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - description: String! -} - -input SetGetHttpsResourceParentInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! - getHttpsResourceId: Uuid! - parentId: Uuid! -} - -input StandardInput { - abstract: String - locator: Url - numeration: NumerationInput! - section: String - standardizers: [Standardizer!]! - title: String - year: Int -} - -"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." -input StandardPropositionInput { - and: [StandardPropositionInput!] - or: [StandardPropositionInput!] - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: NullableOfInt32PropositionInput - numeration: NumerationPropositionInput - standardizers: ListStandardizerPropositionInput - locator: UriPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput - "The section of the standard to which the reference refers to." - section: StringPropositionInput -} - -"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." -input StandardSortInput { - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: SortEnumType @cost(weight: "10") - numeration: NumerationSortInput @cost(weight: "10") - locator: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") - abstract: SortEnumType @cost(weight: "10") - "The section of the standard to which the reference refers to." - section: SortEnumType @cost(weight: "10") -} - -input StandardizerPropositionInput { - equalTo: Standardizer @cost(weight: "10") - notEqualTo: Standardizer @cost(weight: "10") - in: [Standardizer!] @cost(weight: "10") - notIn: [Standardizer!] @cost(weight: "10") -} - -input StringPropositionInput { - and: [StringPropositionInput!] - or: [StringPropositionInput!] - equalTo: String @cost(weight: "10") - notEqualTo: String @cost(weight: "10") - contains: String @cost(weight: "20") - doesNotContain: String @cost(weight: "20") - in: [String] @cost(weight: "10") - notIn: [String] @cost(weight: "10") - startsWith: String @cost(weight: "20") - doesNotStartWith: String @cost(weight: "20") - endsWith: String @cost(weight: "20") - doesNotEndWith: String @cost(weight: "20") -} - -input StringsPropositionInput { - all: StringPropositionInput @cost(weight: "10") - none: StringPropositionInput @cost(weight: "10") - some: StringPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ToTreeVertexAppliedConversionMethodInput { - arguments: [NamedMethodArgumentInput!]! - methodId: Uuid! - sourceName: String! -} - -input ToTreeVertexAppliedConversionMethodPropositionInput { - and: [ToTreeVertexAppliedConversionMethodPropositionInput!] - or: [ToTreeVertexAppliedConversionMethodPropositionInput!] - methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput - sourceName: StringPropositionInput -} - -input ToTreeVertexAppliedConversionMethodSortInput { - methodId: SortEnumType @cost(weight: "10") - sourceName: SortEnumType @cost(weight: "10") -} - -input UpdateChildGetHttpsResourceInput { - appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - description: String! - getHttpsResourceId: Uuid! -} - -input UpdateDataAccessRightsInput { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] - dataId: Uuid! - dataKind: DataKind! -} - -input UpdateDataInput { - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataId: Uuid! - dataKind: DataKind! - description: String - locale: String! - name: String - warnings: [String!]! -} - -input UpdateDatabaseInput { - databaseId: Uuid! - description: String! - locator: Url! - name: String! -} - -input UpdateInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - -input UpdateResponseApprovalsFilterInput { - and: [UpdateResponseApprovalsFilterInput!] - or: [UpdateResponseApprovalsFilterInput!] - id: UuidPropositionInput - userId: NullableOfUuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput - warnings: StringsPropositionInput -} - -input UpdateRootGetHttpsResourceInput { - archivedFilesMetaInformation: [FileMetaInformationInput!]! - dataFormatId: Uuid! - description: String! - getHttpsResourceId: Uuid! -} - -input UriHostNameTypePropositionInput { - equalTo: UriHostNameType @cost(weight: "10") - notEqualTo: UriHostNameType @cost(weight: "10") - in: [UriHostNameType!] @cost(weight: "10") - notIn: [UriHostNameType!] @cost(weight: "10") -} - -input UriPropositionInput { - and: [UriPropositionInput!] - or: [UriPropositionInput!] - absolutePath: StringPropositionInput - absoluteUri: StringPropositionInput - localPath: StringPropositionInput - authority: StringPropositionInput - hostNameType: UriHostNameTypePropositionInput - isDefaultPort: BooleanPropositionInput - isFile: BooleanPropositionInput - isLoopback: BooleanPropositionInput - pathAndQuery: StringPropositionInput - segments: StringsPropositionInput - isUnc: BooleanPropositionInput - host: StringPropositionInput - port: Int32PropositionInput - query: StringPropositionInput - fragment: StringPropositionInput - scheme: StringPropositionInput - originalString: StringPropositionInput - dnsSafeHost: StringPropositionInput - idnHost: StringPropositionInput - isAbsoluteUri: BooleanPropositionInput - userEscaped: BooleanPropositionInput - userInfo: StringPropositionInput -} - -input UriSortInput { - absolutePath: SortEnumType @cost(weight: "10") - absoluteUri: SortEnumType @cost(weight: "10") - localPath: SortEnumType @cost(weight: "10") - authority: SortEnumType @cost(weight: "10") - hostNameType: SortEnumType @cost(weight: "10") - isDefaultPort: SortEnumType @cost(weight: "10") - isFile: SortEnumType @cost(weight: "10") - isLoopback: SortEnumType @cost(weight: "10") - pathAndQuery: SortEnumType @cost(weight: "10") - isUnc: SortEnumType @cost(weight: "10") - host: SortEnumType @cost(weight: "10") - port: SortEnumType @cost(weight: "10") - query: SortEnumType @cost(weight: "10") - fragment: SortEnumType @cost(weight: "10") - scheme: SortEnumType @cost(weight: "10") - originalString: SortEnumType @cost(weight: "10") - dnsSafeHost: SortEnumType @cost(weight: "10") - idnHost: SortEnumType @cost(weight: "10") - isAbsoluteUri: SortEnumType @cost(weight: "10") - userEscaped: SortEnumType @cost(weight: "10") - userInfo: SortEnumType @cost(weight: "10") -} - -input UuidPropositionInput { - equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid!] @cost(weight: "10") - notIn: [Uuid!] @cost(weight: "10") - greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") - greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") - lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") - lessThanOrEqualTo: Uuid @cost(weight: "10") - notLessThanOrEqualTo: Uuid @cost(weight: "10") -} - -enum AddDataApprovalErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - CREATING_RESPONSE_APPROVAL_FAILED - AMBIGUOUS_STATEMENT - MISSING_STATEMENT - INVALID_KEY_FINGERPRINT - FAILED_RECEIVING_KEY - BAD_SIGNATURE - FAILED_VERIFYING_SIGNATURE - FAILED_VERIFYING_RESPONSE - ILLEGAL_MESSAGE - WRONG_RESPONSE - WRONG_STATEMENT -} - -enum AddInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - ALREADY_EXISTS -} - -"Defines when a policy shall be executed." -enum ApplyPolicy { - "Before the resolver was executed." - BEFORE_RESOLVER - "After the resolver was executed." - AFTER_RESOLVER - "The policy is applied in the validation step before the execution." - VALIDATION -} - -enum CalculateMethodErrorCode { - UNKNOWN_METHOD - UNKNOWN_DATABASE - DATA_QUERY_FAILED -} - -enum CalorimetricObserver { - TWO_DEGREES - TEN_DEGREES -} - -enum CoatedSide { - PRIME - NON_PRIME - BOTH - NEITHER - UNKNOWN - NOT_APPLICABLE -} - -enum CreateCalorimetricDataErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_COMPONENT - ILLEGAL_CREATED_AT - UNKNOWN_CREATOR - UNKNOWN_APPLIED_METHOD - UNKNOWN_DATABASE - UNKNOWN_DATA - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreateGeometricDataErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_COMPONENT - ILLEGAL_CREATED_AT - UNKNOWN_CREATOR - UNKNOWN_APPLIED_METHOD - UNKNOWN_DATABASE - UNKNOWN_DATA - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreateGetHttpsResourceErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_DATA - UNKNOWN_PARENT - ILLEGAL_PARENT - UNKNOWN_DATA_FORMAT - UNKNOWN_METHOD - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreateHygrothermalDataErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_COMPONENT - ILLEGAL_CREATED_AT - UNKNOWN_CREATOR - UNKNOWN_APPLIED_METHOD - UNKNOWN_DATABASE - UNKNOWN_DATA - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreateOpticalDataErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_COMPONENT - ILLEGAL_CREATED_AT - UNKNOWN_CREATOR - UNKNOWN_APPLIED_METHOD - UNKNOWN_DATABASE - UNKNOWN_DATA - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreatePhotovoltaicDataErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_COMPONENT - ILLEGAL_CREATED_AT - UNKNOWN_CREATOR - UNKNOWN_APPLIED_METHOD - UNKNOWN_DATABASE - UNKNOWN_DATA - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum CreateResponseApprovalsErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_DATABASE - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum DataKind { - CALORIMETRIC_DATA - GEOMETRIC_DATA - HYGROTHERMAL_DATA - OPTICAL_DATA - PHOTOVOLTAIC_DATA -} - -enum DatabaseVerificationState { - PENDING - VERIFIED -} - -enum DeleteDataErrorCode { - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - NOT_PENDING -} - -enum DeleteGetHttpsResourceErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_RESOURCE - ROOT_RESOURCE - NOT_PENDING_DATA - CREATING_RESPONSE_APPROVAL_FAILED - HAS_CHILDREN -} - -enum Illuminant { - A - D65 -} - -enum JsonValueKind { - UNDEFINED - OBJECT - ARRAY - STRING - NUMBER - TRUE - FALSE - NULL -} - -enum OpticalComponentSubtype { - MONOLITHIC - LAMINATE - INTERLAYER - EMBEDDED_COATING - COATED - COATING - APPLIED_FILM - FILM - VENETIAN_BLIND - DIFFUSING_SHADE - ROLLER_SHADE - WOVEN_SHADE - VERTICAL_LOUVER - PERFORATED_SCREEN - CELLULAR_SHADE - PLEATED_SHADE - ROMAN_SHADE - SHADE_MATERIAL - FRITTED_GLASS - ACID_ETCHED_GLASS - SANDBLASTED_GLASS - CHROMOGENIC -} - -enum OpticalComponentType { - GLAZING - SHADING -} - -enum PublishDataErrorCode { - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - CREATING_RESPONSE_APPROVAL_FAILED - MISSING_ROOT_RESOURCE - DUPLICATE_ROOT_RESOURCE - RESOURCES_WITHOUT_FILE -} - -enum RecomputeGetHttpsResourceHashValuesErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - FAILED - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum RemoveDataApprovalErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_APPROVAL - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum RetractDataErrorCode { - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - CREATING_RESPONSE_APPROVAL_FAILED - NOT_PUBLISHED -} - -enum SetGetHttpsResourceParentErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_RESOURCE - UNKNOWN_PARENT - ILLEGAL_PARENT - UNKNOWN_METHOD - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum SortEnumType { - ASC - DESC -} - -enum Standardizer { - AERC - AGI - ASHRAE - BREEAM - BS - BSI - CEN - CIE - DGNB - DIN - DVWG - IEC - IES - IFT - ISO - JIS - LEED - NFRC - RIBA - UL - UNECE - VDI - VFF - WELL -} - -enum UpdateChildGetHttpsResourceErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_RESOURCE - UNKNOWN_DATA_FORMAT - UNKNOWN_METHOD - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum UpdateDataAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA -} - -enum UpdateDataErrorCode { - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum UpdateDatabaseErrorCode { - UNKNOWN - UNAUTHORIZED - UNKNOWN_DATABASE -} - -enum UpdateInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_ACCESS_RIGHTS -} - -enum UpdateResponseApprovalsErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_DATABASE - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum UpdateRootGetHttpsResourceErrorCode { - UNKNOWN - UNAUTHORIZED - UNAUTHENTICATED - UNKNOWN_RESOURCE - UNKNOWN_DATA_FORMAT - CREATING_RESPONSE_APPROVAL_FAILED -} - -enum UriHostNameType { - UNKNOWN - BASIC - DNS - I_PV4 - I_PV6 -} - -"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION - -"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION - -"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR - -scalar Any - -"The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") - -"BCP 47 compliant Language Tag string" -scalar Locale - -"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." -scalar NonNegativeInt - -"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." -scalar TimeSpan - -"The `Upload` scalar type represents a file upload." -scalar Upload - -scalar Url - -scalar Uuid \ No newline at end of file From 0a7a7a8320945fbc05415016cabae2ffd7563279 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Dec 2025 18:17:52 +0100 Subject: [PATCH 600/878] Make HotChocolate use proper GraphQL types for NodaTime types --- .../src/Configuration/GraphQlConfiguration.cs | 26 +- backend/src/GraphQl/LocaleType.cs | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 2424 +++++++++++++++++ frontend/type-defs.graphqls | 214 +- 4 files changed, 2620 insertions(+), 46 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b34d14f7..ace1ccab 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,20 +1,21 @@ -using NodaTime; using System; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using HotChocolate.Configuration; using HotChocolate.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; +using HotChocolate.Types.NodaTime; +using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.Types; -using HotChocolate.Configuration; -using HotChocolate.Execution; using Database.Data; using Database.GraphQl; using Database.GraphQl.DataX; +using NodaTime; namespace Database.Configuration; @@ -119,17 +120,20 @@ IWebHostEnvironment environment .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type .AddType(new UrlType("Url")) .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar - .AddType(new LocaleType()) + .AddType() + .AddType() + .AddType() + // .AddType() // Register converters between NodaTime's `OffsetDateTime` and .NET's // `DateTimeOffset` to reuse the existing `DateTimeType` // https://chillicream.com/docs/hotchocolate/v15/defining-a-schema/scalars#custom-converters - // .BindRuntimeType() - // .AddTypeConverter( - // _ => _.ToDateTimeOffset() - // ) - // .AddTypeConverter( - // _ => OffsetDateTime.FromDateTimeOffset(_) - // ) + .BindRuntimeType() + .AddTypeConverter( + _ => _.ToDateTimeOffset() + ) + .AddTypeConverter( + _ => OffsetDateTime.FromDateTimeOffset(_) + ) // Object Types .AddType() // Query, Mutation, Subscription, Object, and Input Types diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/LocaleType.cs index 58564a0f..0c337de6 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/LocaleType.cs @@ -42,7 +42,7 @@ public sealed class LocaleType( /// public LocaleType() : this( - nameof(LocaleType).Remove(nameof(LocaleType).Length - "Type".Length), + nameof(LocaleType)[..^"Type".Length], "BCP 47 compliant Language Tag string" ) { diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index e69de29b..427cabc6 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -0,0 +1,2424 @@ +schema { + query: Query + mutation: Mutation +} + +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + +interface Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +interface GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +interface UserError { + message: String! + path: [String!]! +} + +type AddDataApprovalError implements UserError { + code: AddDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type AddDataApprovalPayload { + dataApproval: DataApproval + errors: [AddDataApprovalError!] + query: Query! +} + +type AddInstitutionAccessRightsError implements UserError { + code: AddInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type AddInstitutionAccessRightsPayload { + errors: [AddInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights + query: Query! +} + +type Address { + formatted: String! +} + +type AppliedMethod { + arguments: [NamedMethodArgument!]! + methodId: Uuid! + sources: [NamedMethodSource!]! +} + +type CalculateMethodError implements UserError { + code: CalculateMethodErrorCode! + message: String! + path: [String!]! +} + +type CalculateMethodPayload { + errors: [CalculateMethodError!] + result: Any +} + +type CalorimetricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + gValues: [Float!]! + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + uValues: [Float!]! + warnings: [String!]! +} + +"A connection to a list of items." +type CalorimetricDataConnection { + "A list of edges." + edges: [CalorimetricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type CalorimetricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: CalorimetricData! +} + +type CielabColor { + aStar: Float! + bStar: Float! + illuminant: Illuminant + lStar: Float! + observer: CalorimetricObserver +} + +type CreateCalorimetricDataError implements UserError { + code: CreateCalorimetricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateCalorimetricDataPayload { + calorimetricData: CalorimetricData + errors: [CreateCalorimetricDataError!] + query: Query! +} + +type CreateGeometricDataError implements UserError { + code: CreateGeometricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateGeometricDataPayload { + errors: [CreateGeometricDataError!] + geometricData: GeometricData + query: Query! +} + +type CreateGetHttpsResourceError implements UserError { + code: CreateGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type CreateGetHttpsResourcePayload { + errors: [CreateGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type CreateHygrothermalDataError implements UserError { + code: CreateHygrothermalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateHygrothermalDataPayload { + errors: [CreateHygrothermalDataError!] + hygrothermalData: HygrothermalData + query: Query! +} + +type CreateOpticalDataError implements UserError { + code: CreateOpticalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateOpticalDataPayload { + errors: [CreateOpticalDataError!] + opticalData: OpticalData + query: Query! +} + +type CreatePhotovoltaicDataError implements UserError { + code: CreatePhotovoltaicDataErrorCode! + message: String! + path: [String!]! +} + +type CreatePhotovoltaicDataPayload { + errors: [CreatePhotovoltaicDataError!] + photovoltaicData: PhotovoltaicData + query: Query! +} + +type CreateResponseApprovalsError implements UserError { + code: CreateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type CreateResponseApprovalsPayload { + data: [Data!] + errors: [CreateResponseApprovalsError!] + query: Query! +} + +type CrossDatabaseDataReference { + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +type DataAccessRights { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] + hasRestrictions: Boolean! + hasRestrictionsByApplication: Boolean! + hasRestrictionsByInstitution: Boolean! + hasRestrictionsByUser: Boolean! +} + +type DataApproval implements Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: Reference @cost(weight: "10") + timestamp: DateTime! + variables: Any! +} + +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + +type Database { + description: String! + isAuthorizedToUpdateNode: Boolean! + isAuthorizedToVerifyNode: Boolean! + locator: Url! + name: String! + operator: DatabaseOperatorEdge! + uuid: Uuid! + verificationCode: String! + verificationState: DatabaseVerificationState! +} + +type DatabaseOperatorEdge { + node: Institution! +} + +type DeleteDataError implements UserError { + code: DeleteDataErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataPayload { + errors: [DeleteDataError!] + query: Query! +} + +type DeleteGetHttpsResourceError implements UserError { + code: DeleteGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type DeleteGetHttpsResourcePayload { + errors: [DeleteGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type FileMetaInformation { + dataFormatId: Uuid! + path: [String!]! +} + +type GeometricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + heights: [Float!]! + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + thicknesses: [Float!]! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! + widths: [Float!]! +} + +"A connection to a list of items." +type GeometricDataConnection { + "A list of edges." + edges: [GeometricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GeometricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GeometricData! +} + +type GetHttpsResource implements Node { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod + archivedFilesMetaInformation: [FileMetaInformation!]! + children: [GetHttpsResource!]! @cost(weight: "10") + data: Data @cost(weight: "10") + dataFormatId: Uuid! + description: String + doesFileExist: Boolean! + hashValue: String! + id: ID! + isChild: Boolean! + isRoot: Boolean! + locator: Url! + parent: GetHttpsResource @cost(weight: "10") + uuid: Uuid! @cost(weight: "10") +} + +"A connection to a list of items." +type GetHttpsResourceConnection { + "A list of edges." + edges: [GetHttpsResourceEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GetHttpsResourceEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GetHttpsResource! +} + +type GetHttpsResourceTree { + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + root: GetHttpsResourceTreeRoot! @cost(weight: "10") +} + +type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod! + parentId: ID! + value: GetHttpsResource! + vertexId: ID! +} + +type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID! +} + +type HygrothermalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type HygrothermalDataConnection { + "A list of edges." + edges: [HygrothermalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type HygrothermalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: HygrothermalData! +} + +type Institution { + uuid: Uuid! +} + +type InstitutionAccessRights { + allowedDatasetsPerTime: NonNegativeInt + allowedUserCount: NonNegativeInt + hasRestrictions: Boolean! + hasRestrictionsByTime: Boolean! + hasRestrictionsByUser: Boolean! + id: Uuid! + institutionId: Uuid! + period: Duration! + userAlreadyAccessed: [Uuid!]! + version: NonNegativeInt! +} + +type KeyValuePairOfGuidAndNullableOfUInt32 { + key: Uuid! + value: NonNegativeInt +} + +type Mutation { + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") + deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") + publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") + updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") +} + +type NamedMethodArgument { + name: String! + value: Any! +} + +type NamedMethodSource { + name: String! + value: CrossDatabaseDataReference! +} + +type Numeration { + mainNumber: String! + prefix: String + suffix: String +} + +type OpenEndedDateTimeRange { + from: DateTime @cost(weight: "10") + until: DateTime @cost(weight: "10") +} + +type OpticalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + cielabColors: [CielabColor!]! + coatedSide: CoatedSide + colorRenderingIndices: [Float!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + infraredEmittances: [Float!]! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + subtype: OpticalComponentSubtype + timestamp: DateTime! + type: OpticalComponentType + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type OpticalDataConnection { + "A list of edges." + edges: [OpticalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type OpticalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: OpticalData! +} + +"Information about pagination in a connection." +type PageInfo { + "When paginating forwards, the cursor to continue." + endCursor: String + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String +} + +type PhotovoltaicData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type PhotovoltaicDataConnection { + "A list of edges." + edges: [PhotovoltaicDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type PhotovoltaicDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: PhotovoltaicData! +} + +type Publication { + abstract: String + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: String + authors: [String!] + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: String + "Referenced section" + section: String + title: String + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: String + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: Url +} + +type PublishDataError implements UserError { + code: PublishDataErrorCode! + message: String! + path: [String!]! +} + +type PublishDataPayload { + data: Data + errors: [PublishDataError!] + query: Query! +} + +type Query { + allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") + calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") + currentUser: User @cost(weight: "10") + currentUserInfo: UserInfo! @cost(weight: "10") + data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") + database: Database! @cost(weight: "10") + geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") + getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") + getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + "Fetches an object given its ID." + node("ID of the object." id: ID!): Node @cost(weight: "10") + "Lookup nodes by a list of IDs." + nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") + verificationCode: String! +} + +type RecomputeGetHttpsResourceHashValuesError implements UserError { + code: RecomputeGetHttpsResourceHashValuesErrorCode! + message: String! + path: [String!]! +} + +type RecomputeGetHttpsResourceHashValuesPayload { + errors: [RecomputeGetHttpsResourceHashValuesError!] + getHttpsResources: [GetHttpsResource!] + query: Query! +} + +type RemoveDataApprovalError implements UserError { + code: RemoveDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type RemoveDataApprovalPayload { + dataApproval: DataApproval + errors: [RemoveDataApprovalError!] + query: Query! +} + +type ResponseApproval implements Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + +type RetractDataError implements UserError { + code: RetractDataErrorCode! + message: String! + path: [String!]! +} + +type RetractDataPayload { + data: Data + errors: [RetractDataError!] + query: Query! +} + +type SetGetHttpsResourceParentError implements UserError { + code: SetGetHttpsResourceParentErrorCode! + message: String! + path: [String!]! +} + +type SetGetHttpsResourceParentPayload { + errors: [SetGetHttpsResourceParentError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +type Standard { + abstract: String + locator: Url + numeration: Numeration! + "The section of the standard to which the reference refers to." + section: String + standardizers: [Standardizer!]! + title: String + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: Int +} + +type ToTreeVertexAppliedConversionMethod { + arguments: [NamedMethodArgument!]! + methodId: Uuid! + sourceName: String! +} + +type UpdateChildGetHttpsResourceError implements UserError { + code: UpdateChildGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateChildGetHttpsResourcePayload { + errors: [UpdateChildGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type UpdateDataAccessRightsError implements UserError { + code: UpdateDataAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataAccessRightsPayload { + dataAccessRights: DataAccessRights + errors: [UpdateDataAccessRightsError!] + query: Query! +} + +type UpdateDataError implements UserError { + code: UpdateDataErrorCode! + message: String! + path: [String!]! +} + +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] + query: Query! +} + +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! + message: String! + path: [String!]! +} + +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] + query: Query! +} + +type UpdateInstitutionAccessRightsError implements UserError { + code: UpdateInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type UpdateInstitutionAccessRightsPayload { + errors: [UpdateInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights + query: Query! +} + +type UpdateResponseApprovalsError implements UserError { + code: UpdateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type UpdateResponseApprovalsPayload { + data: [Data!] + errors: [UpdateResponseApprovalsError!] + query: Query! +} + +type UpdateRootGetHttpsResourceError implements UserError { + code: UpdateRootGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type UpdateRootGetHttpsResourcePayload { + errors: [UpdateRootGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type User implements Node { + id: ID! + name: String! + subject: String! + uuid: Uuid! @cost(weight: "10") +} + +type UserInfo { + address: Address + email: String! + emailVerified: Boolean! + name: String! + phoneNumber: String + phoneNumberVerified: Boolean! + roles: [String!] + sub: String! + website: String +} + +union Reference = Publication | Standard + +input AddDataApprovalInput { + approverId: Uuid! + dataId: Uuid! + dataKind: DataKind! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: ReferenceInput! + timestamp: DateTime! + variables: Any! +} + +input AddInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + +input AppliedMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sources: [NamedMethodSourceInput!]! +} + +input AppliedMethodPropositionInput { + and: [AppliedMethodPropositionInput!] + or: [AppliedMethodPropositionInput!] + methodId: UuidPropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput + sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput +} + +input AppliedMethodSortInput { + methodId: SortEnumType @cost(weight: "10") +} + +input BooleanPropositionInput { + equalTo: Boolean @cost(weight: "10") + notEqualTo: Boolean @cost(weight: "10") +} + +input CalendarSystemPropositionInput { + and: [CalendarSystemPropositionInput!] + or: [CalendarSystemPropositionInput!] + id: StringPropositionInput + name: StringPropositionInput + minYear: Int32PropositionInput + maxYear: Int32PropositionInput + eras: ListFilterInputTypeOfEraPropositionInput +} + +input CalorimetricDataPropositionInput { + and: [CalorimetricDataPropositionInput!] + or: [CalorimetricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput + gValues: FloatsPropositionInput + uValues: FloatsPropositionInput +} + +input CalorimetricDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input CielabColorInput { + aStar: Float! + bStar: Float! + illuminant: Illuminant + lStar: Float! + observer: CalorimetricObserver +} + +input CielabColorPropositionInput { + and: [CielabColorPropositionInput!] + or: [CielabColorPropositionInput!] + lStar: FloatPropositionInput + aStar: FloatPropositionInput + bStar: FloatPropositionInput + observer: NullableOfCalorimetricObserverPropositionInput + illuminant: NullableOfIlluminantPropositionInput +} + +input CreateCalorimetricDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + gValues: [Float!]! + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + uValues: [Float!]! + warnings: [String!]! +} + +input CreateGeometricDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + heights: [Float!]! + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + thicknesses: [Float!]! + warnings: [String!]! + widths: [Float!]! +} + +input CreateGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String! + parentId: Uuid! +} + +input CreateHygrothermalDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + +input CreateOpticalDataInput { + appliedMethod: AppliedMethodInput! + cielabColors: [CielabColorInput!]! + coatedSide: CoatedSide + colorRenderingIndices: [Float!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + infraredEmittances: [Float!]! + locale: Locale! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + rootResource: RootGetHttpsResourceInput! + subtype: OpticalComponentSubtype + type: OpticalComponentType + warnings: [String!]! +} + +input CreatePhotovoltaicDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + +input CreateResponseApprovalsFilterInput { + and: [CreateResponseApprovalsFilterInput!] + or: [CreateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput +} + +input CrossDatabaseDataReferenceInput { + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +input CrossDatabaseDataReferencePropositionInput { + and: [CrossDatabaseDataReferencePropositionInput!] + or: [CrossDatabaseDataReferencePropositionInput!] + dataId: UuidPropositionInput + dataTimestamp: OffsetDateTimePropositionInput + dataKind: DataKindPropositionInput + databaseId: UuidPropositionInput +} + +input DataApprovalPropositionInput { + and: [DataApprovalPropositionInput!] + or: [DataApprovalPropositionInput!] + approverId: UuidPropositionInput + timestamp: OffsetDateTimePropositionInput + signature: StringPropositionInput + keyFingerprint: StringPropositionInput + query: StringPropositionInput + variables: JsonElementPropositionInput + message: StringPropositionInput + statement: ReferencePropositionInput +} + +input DataFilterInput { + and: [DataFilterInput!] + or: [DataFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput +} + +input DataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + in: [DataKind!] @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") +} + +input DataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input DeleteDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteGetHttpsResourceInput { + getHttpsResourceId: Uuid! +} + +input EraPropositionInput { + and: [EraPropositionInput!] + or: [EraPropositionInput!] + name: StringPropositionInput +} + +input FileMetaInformationInput { + dataFormatId: Uuid! + path: [String!]! +} + +input FileMetaInformationPropositionInput { + and: [FileMetaInformationPropositionInput!] + or: [FileMetaInformationPropositionInput!] + path: StringsPropositionInput + dataFormatId: UuidPropositionInput +} + +input FloatPropositionInput { + equalTo: Float @cost(weight: "10") + notEqualTo: Float @cost(weight: "10") + in: [Float!] @cost(weight: "10") + notIn: [Float!] @cost(weight: "10") + greaterThan: Float @cost(weight: "10") + notGreaterThan: Float @cost(weight: "10") + greaterThanOrEqualTo: Float @cost(weight: "10") + notGreaterThanOrEqualTo: Float @cost(weight: "10") + lessThan: Float @cost(weight: "10") + notLessThan: Float @cost(weight: "10") + lessThanOrEqualTo: Float @cost(weight: "10") + notLessThanOrEqualTo: Float @cost(weight: "10") +} + +input FloatsPropositionInput { + all: FloatPropositionInput @cost(weight: "10") + none: FloatPropositionInput @cost(weight: "10") + some: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input GeometricDataPropositionInput { + and: [GeometricDataPropositionInput!] + or: [GeometricDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput + thicknesses: FloatsPropositionInput +} + +input GeometricDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input GetHttpsResourcePropositionInput { + and: [GetHttpsResourcePropositionInput!] + or: [GetHttpsResourcePropositionInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput +} + +input GetHttpsResourceSortInput { + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") +} + +input GetHttpsResourceTreeNonRootVertSortInput { + id: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") +} + +input GetHttpsResourceTreeNonRootVertexFilterInput { + and: [GetHttpsResourceTreeNonRootVertexFilterInput!] + or: [GetHttpsResourceTreeNonRootVertexFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput +} + +input HygrothermalDataPropositionInput { + and: [HygrothermalDataPropositionInput!] + or: [HygrothermalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput +} + +input HygrothermalDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input Int32PropositionInput { + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int!] @cost(weight: "10") + notIn: [Int!] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") +} + +input Int64PropositionInput { + equalTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + in: [Long!] @cost(weight: "10") + notIn: [Long!] @cost(weight: "10") + greaterThan: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + greaterThanOrEqualTo: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + lessThan: Long @cost(weight: "10") + notLessThan: Long @cost(weight: "10") + lessThanOrEqualTo: Long @cost(weight: "10") + notLessThanOrEqualTo: Long @cost(weight: "10") +} + +input IsoDayOfWeekPropositionInput { + equalTo: IsoDayOfWeek @cost(weight: "10") + notEqualTo: IsoDayOfWeek @cost(weight: "10") + in: [IsoDayOfWeek!] @cost(weight: "10") + notIn: [IsoDayOfWeek!] @cost(weight: "10") +} + +input JsonElementPropositionInput { + and: [JsonElementPropositionInput!] + or: [JsonElementPropositionInput!] + valueKind: JsonValueKindPropositionInput +} + +input JsonValueKindPropositionInput { + equalTo: JsonValueKind @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") + in: [JsonValueKind!] @cost(weight: "10") + notIn: [JsonValueKind!] @cost(weight: "10") +} + +input KeyValuePairOfGuidAndNullableOfUInt32Input { + key: Uuid! + value: NonNegativeInt +} + +input ListFilterInputTypeOfCielabColorPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfDataApprovalPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfEraPropositionInput { + all: EraPropositionInput @cost(weight: "10") + none: EraPropositionInput @cost(weight: "10") + some: EraPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfFileMetaInformationPropositionInput { + all: FileMetaInformationPropositionInput @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfGetHttpsResourcePropositionInput { + all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListFilterInputTypeOfNamedMethodSourcePropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ListStandardizerPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input LocalDatePropositionInput { + and: [LocalDatePropositionInput!] + or: [LocalDatePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + month: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + dayOfYear: Int32PropositionInput +} + +input LocalDateTimePropositionInput { + and: [LocalDateTimePropositionInput!] + or: [LocalDateTimePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + month: Int32PropositionInput + dayOfYear: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput + timeOfDay: LocalTimePropositionInput + date: LocalDatePropositionInput +} + +input LocalTimePropositionInput { + and: [LocalTimePropositionInput!] + or: [LocalTimePropositionInput!] + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput +} + +input NamedMethodArgumentInput { + name: String! + value: Any! +} + +input NamedMethodArgumentPropositionInput { + and: [NamedMethodArgumentPropositionInput!] + or: [NamedMethodArgumentPropositionInput!] + name: StringPropositionInput +} + +input NamedMethodArgumentSortInput { + name: SortEnumType @cost(weight: "10") +} + +input NamedMethodSourceInput { + name: String! + value: CrossDatabaseDataReferenceInput! +} + +input NamedMethodSourcePropositionInput { + and: [NamedMethodSourcePropositionInput!] + or: [NamedMethodSourcePropositionInput!] + name: StringPropositionInput + value: CrossDatabaseDataReferencePropositionInput +} + +input NullableOfCalorimetricObserverPropositionInput { + equalTo: CalorimetricObserver @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") + in: [CalorimetricObserver] @cost(weight: "10") + notIn: [CalorimetricObserver] @cost(weight: "10") +} + +input NullableOfCoatedSidePropositionInput { + equalTo: CoatedSide @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") + in: [CoatedSide] @cost(weight: "10") + notIn: [CoatedSide] @cost(weight: "10") +} + +input NullableOfIlluminantPropositionInput { + equalTo: Illuminant @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") + in: [Illuminant] @cost(weight: "10") + notIn: [Illuminant] @cost(weight: "10") +} + +input NullableOfInt32PropositionInput { + equalTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") + notIn: [Int] @cost(weight: "10") + greaterThan: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + greaterThanOrEqualTo: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + lessThan: Int @cost(weight: "10") + notLessThan: Int @cost(weight: "10") + lessThanOrEqualTo: Int @cost(weight: "10") + notLessThanOrEqualTo: Int @cost(weight: "10") +} + +input NullableOfOpticalComponentSubtypePropositionInput { + equalTo: OpticalComponentSubtype @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") + in: [OpticalComponentSubtype] @cost(weight: "10") + notIn: [OpticalComponentSubtype] @cost(weight: "10") +} + +input NullableOfOpticalComponentTypePropositionInput { + equalTo: OpticalComponentType @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") + in: [OpticalComponentType] @cost(weight: "10") + notIn: [OpticalComponentType] @cost(weight: "10") +} + +input NullableOfUuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + +input NumerationInput { + mainNumber: String! + prefix: String + suffix: String +} + +input NumerationPropositionInput { + and: [NumerationPropositionInput!] + or: [NumerationPropositionInput!] + prefix: StringPropositionInput + mainNumber: StringPropositionInput + suffix: StringPropositionInput +} + +input NumerationSortInput { + prefix: SortEnumType @cost(weight: "10") + mainNumber: SortEnumType @cost(weight: "10") + suffix: SortEnumType @cost(weight: "10") +} + +input OffsetDateTimePropositionInput { + and: [OffsetDateTimePropositionInput!] + or: [OffsetDateTimePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + month: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + dayOfYear: Int32PropositionInput + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput + localDateTime: LocalDateTimePropositionInput + date: LocalDatePropositionInput + timeOfDay: LocalTimePropositionInput + offset: OffsetPropositionInput +} + +input OffsetPropositionInput { + and: [OffsetPropositionInput!] + or: [OffsetPropositionInput!] + seconds: Int32PropositionInput + milliseconds: Int32PropositionInput + ticks: Int64PropositionInput + nanoseconds: Int64PropositionInput +} + +input OpticalDataPropositionInput { + and: [OpticalDataPropositionInput!] + or: [OpticalDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput + type: NullableOfOpticalComponentTypePropositionInput + subtype: NullableOfOpticalComponentSubtypePropositionInput + coatedSide: NullableOfCoatedSidePropositionInput + nearnormalHemisphericalSolarReflectances: FloatsPropositionInput + nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput + nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput + nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput + infraredEmittances: FloatsPropositionInput + colorRenderingIndices: FloatsPropositionInput + cielabColors: ListFilterInputTypeOfCielabColorPropositionInput +} + +input OpticalDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input PhotovoltaicDataPropositionInput { + and: [PhotovoltaicDataPropositionInput!] + or: [PhotovoltaicDataPropositionInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput +} + +input PhotovoltaicDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + +input PublicationInput { + abstract: String + arXiv: String + authors: [String!] + doi: String + section: String + title: String + urn: String + webAddress: Url +} + +input PublicationPropositionInput { + and: [PublicationPropositionInput!] + or: [PublicationPropositionInput!] + authors: StringsPropositionInput + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: StringPropositionInput + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: StringPropositionInput + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: StringPropositionInput + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "Referenced section" + section: StringPropositionInput +} + +input PublicationSortInput { + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: SortEnumType @cost(weight: "10") + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: SortEnumType @cost(weight: "10") + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: SortEnumType @cost(weight: "10") + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "Referenced section" + section: SortEnumType @cost(weight: "10") +} + +input PublishDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input RecomputeGetHttpsResourceHashValuesFilterInput { + and: [RecomputeGetHttpsResourceHashValuesFilterInput!] + or: [RecomputeGetHttpsResourceHashValuesFilterInput!] + id: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + dataFormatId: UuidPropositionInput + appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput + archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput + parent: GetHttpsResourceTreeNonRootVertexFilterInput + calorimetricData: CalorimetricDataPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + opticalData: OpticalDataPropositionInput + photovoltaicData: PhotovoltaicDataPropositionInput +} + +input ReferenceInput { + publication: PublicationInput + standard: StandardInput +} + +input ReferencePropositionInput { + and: [ReferencePropositionInput!] + or: [ReferencePropositionInput!] + standard: StandardPropositionInput + publication: PublicationPropositionInput +} + +input ReferenceSortInput { + standard: StandardSortInput @cost(weight: "10") + publication: PublicationSortInput @cost(weight: "10") +} + +input RemoveDataApprovalInput { + dataId: Uuid! + dataKind: DataKind! + signature: String! +} + +input RetractDataInput { + dataId: Uuid! + dataKind: DataKind! +} + +input RootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! +} + +input SetGetHttpsResourceParentInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + getHttpsResourceId: Uuid! + parentId: Uuid! +} + +input StandardInput { + abstract: String + locator: Url + numeration: NumerationInput! + section: String + standardizers: [Standardizer!]! + title: String + year: Int +} + +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardPropositionInput { + and: [StandardPropositionInput!] + or: [StandardPropositionInput!] + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: NullableOfInt32PropositionInput + numeration: NumerationPropositionInput + standardizers: ListStandardizerPropositionInput + locator: UriPropositionInput + title: StringPropositionInput + abstract: StringPropositionInput + "The section of the standard to which the reference refers to." + section: StringPropositionInput +} + +"`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." +input StandardSortInput { + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: SortEnumType @cost(weight: "10") + numeration: NumerationSortInput @cost(weight: "10") + locator: UriSortInput @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") + "The section of the standard to which the reference refers to." + section: SortEnumType @cost(weight: "10") +} + +input StandardizerPropositionInput { + equalTo: Standardizer @cost(weight: "10") + notEqualTo: Standardizer @cost(weight: "10") + in: [Standardizer!] @cost(weight: "10") + notIn: [Standardizer!] @cost(weight: "10") +} + +input StringPropositionInput { + and: [StringPropositionInput!] + or: [StringPropositionInput!] + equalTo: String @cost(weight: "10") + notEqualTo: String @cost(weight: "10") + contains: String @cost(weight: "20") + doesNotContain: String @cost(weight: "20") + in: [String] @cost(weight: "10") + notIn: [String] @cost(weight: "10") + startsWith: String @cost(weight: "20") + doesNotStartWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + doesNotEndWith: String @cost(weight: "20") +} + +input StringsPropositionInput { + all: StringPropositionInput @cost(weight: "10") + none: StringPropositionInput @cost(weight: "10") + some: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input ToTreeVertexAppliedConversionMethodInput { + arguments: [NamedMethodArgumentInput!]! + methodId: Uuid! + sourceName: String! +} + +input ToTreeVertexAppliedConversionMethodPropositionInput { + and: [ToTreeVertexAppliedConversionMethodPropositionInput!] + or: [ToTreeVertexAppliedConversionMethodPropositionInput!] + methodId: UuidPropositionInput + arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput + sourceName: StringPropositionInput +} + +input ToTreeVertexAppliedConversionMethodSortInput { + methodId: SortEnumType @cost(weight: "10") + sourceName: SortEnumType @cost(weight: "10") +} + +input UpdateChildGetHttpsResourceInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + +input UpdateDataAccessRightsInput { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + dataId: Uuid! + dataKind: DataKind! +} + +input UpdateDataInput { + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataId: Uuid! + dataKind: DataKind! + description: String + locale: String! + name: String + warnings: [String!]! +} + +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + +input UpdateInstitutionAccessRightsInput { + allowedDatasetsPerTimeSpan: NonNegativeInt + allowedUserCount: NonNegativeInt + institutionId: Uuid! + periodInDays: Int! +} + +input UpdateResponseApprovalsFilterInput { + and: [UpdateResponseApprovalsFilterInput!] + or: [UpdateResponseApprovalsFilterInput!] + id: UuidPropositionInput + userId: NullableOfUuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: ListFilterInputTypeOfDataApprovalPropositionInput + resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + warnings: StringsPropositionInput +} + +input UpdateRootGetHttpsResourceInput { + archivedFilesMetaInformation: [FileMetaInformationInput!]! + dataFormatId: Uuid! + description: String! + getHttpsResourceId: Uuid! +} + +input UriHostNameTypePropositionInput { + equalTo: UriHostNameType @cost(weight: "10") + notEqualTo: UriHostNameType @cost(weight: "10") + in: [UriHostNameType!] @cost(weight: "10") + notIn: [UriHostNameType!] @cost(weight: "10") +} + +input UriPropositionInput { + and: [UriPropositionInput!] + or: [UriPropositionInput!] + absolutePath: StringPropositionInput + absoluteUri: StringPropositionInput + localPath: StringPropositionInput + authority: StringPropositionInput + hostNameType: UriHostNameTypePropositionInput + isDefaultPort: BooleanPropositionInput + isFile: BooleanPropositionInput + isLoopback: BooleanPropositionInput + pathAndQuery: StringPropositionInput + segments: StringsPropositionInput + isUnc: BooleanPropositionInput + host: StringPropositionInput + port: Int32PropositionInput + query: StringPropositionInput + fragment: StringPropositionInput + scheme: StringPropositionInput + originalString: StringPropositionInput + dnsSafeHost: StringPropositionInput + idnHost: StringPropositionInput + isAbsoluteUri: BooleanPropositionInput + userEscaped: BooleanPropositionInput + userInfo: StringPropositionInput +} + +input UriSortInput { + absolutePath: SortEnumType @cost(weight: "10") + absoluteUri: SortEnumType @cost(weight: "10") + localPath: SortEnumType @cost(weight: "10") + authority: SortEnumType @cost(weight: "10") + hostNameType: SortEnumType @cost(weight: "10") + isDefaultPort: SortEnumType @cost(weight: "10") + isFile: SortEnumType @cost(weight: "10") + isLoopback: SortEnumType @cost(weight: "10") + pathAndQuery: SortEnumType @cost(weight: "10") + isUnc: SortEnumType @cost(weight: "10") + host: SortEnumType @cost(weight: "10") + port: SortEnumType @cost(weight: "10") + query: SortEnumType @cost(weight: "10") + fragment: SortEnumType @cost(weight: "10") + scheme: SortEnumType @cost(weight: "10") + originalString: SortEnumType @cost(weight: "10") + dnsSafeHost: SortEnumType @cost(weight: "10") + idnHost: SortEnumType @cost(weight: "10") + isAbsoluteUri: SortEnumType @cost(weight: "10") + userEscaped: SortEnumType @cost(weight: "10") + userInfo: SortEnumType @cost(weight: "10") +} + +input UuidPropositionInput { + equalTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + in: [Uuid!] @cost(weight: "10") + notIn: [Uuid!] @cost(weight: "10") + greaterThan: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + greaterThanOrEqualTo: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + lessThan: Uuid @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") + lessThanOrEqualTo: Uuid @cost(weight: "10") + notLessThanOrEqualTo: Uuid @cost(weight: "10") +} + +enum AddDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED + AMBIGUOUS_STATEMENT + MISSING_STATEMENT + INVALID_KEY_FINGERPRINT + FAILED_RECEIVING_KEY + BAD_SIGNATURE + FAILED_VERIFYING_SIGNATURE + FAILED_VERIFYING_RESPONSE + ILLEGAL_MESSAGE + WRONG_RESPONSE + WRONG_STATEMENT +} + +enum AddInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + ALREADY_EXISTS +} + +"Defines when a policy shall be executed." +enum ApplyPolicy { + "Before the resolver was executed." + BEFORE_RESOLVER + "After the resolver was executed." + AFTER_RESOLVER + "The policy is applied in the validation step before the execution." + VALIDATION +} + +enum CalculateMethodErrorCode { + UNKNOWN_METHOD + UNKNOWN_DATABASE + DATA_QUERY_FAILED +} + +enum CalorimetricObserver { + TWO_DEGREES + TEN_DEGREES +} + +enum CoatedSide { + PRIME + NON_PRIME + BOTH + NEITHER + UNKNOWN + NOT_APPLICABLE +} + +enum CreateCalorimetricDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateGeometricDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATA + UNKNOWN_PARENT + ILLEGAL_PARENT + UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateHygrothermalDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateOpticalDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreatePhotovoltaicDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum CreateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum DataKind { + CALORIMETRIC_DATA + GEOMETRIC_DATA + HYGROTHERMAL_DATA + OPTICAL_DATA + PHOTOVOLTAIC_DATA +} + +enum DatabaseVerificationState { + PENDING + VERIFIED +} + +enum DeleteDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + NOT_PENDING +} + +enum DeleteGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + ROOT_RESOURCE + NOT_PENDING_DATA + CREATING_RESPONSE_APPROVAL_FAILED + HAS_CHILDREN +} + +enum Illuminant { + A + D65 +} + +enum IsoDayOfWeek { + NONE + MONDAY + TUESDAY + WEDNESDAY + THURSDAY + FRIDAY + SATURDAY + SUNDAY +} + +enum JsonValueKind { + UNDEFINED + OBJECT + ARRAY + STRING + NUMBER + TRUE + FALSE + NULL +} + +enum OpticalComponentSubtype { + MONOLITHIC + LAMINATE + INTERLAYER + EMBEDDED_COATING + COATED + COATING + APPLIED_FILM + FILM + VENETIAN_BLIND + DIFFUSING_SHADE + ROLLER_SHADE + WOVEN_SHADE + VERTICAL_LOUVER + PERFORATED_SCREEN + CELLULAR_SHADE + PLEATED_SHADE + ROMAN_SHADE + SHADE_MATERIAL + FRITTED_GLASS + ACID_ETCHED_GLASS + SANDBLASTED_GLASS + CHROMOGENIC +} + +enum OpticalComponentType { + GLAZING + SHADING +} + +enum PublishDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED + MISSING_ROOT_RESOURCE + DUPLICATE_ROOT_RESOURCE + RESOURCES_WITHOUT_FILE +} + +enum RecomputeGetHttpsResourceHashValuesErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + FAILED + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum RemoveDataApprovalErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPROVAL + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum RetractDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED + NOT_PUBLISHED +} + +enum SetGetHttpsResourceParentErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_PARENT + ILLEGAL_PARENT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum SortEnumType { + ASC + DESC +} + +enum Standardizer { + AERC + AGI + ASHRAE + BREEAM + BS + BSI + CEN + CIE + DGNB + DIN + DVWG + IEC + IES + IFT + ISO + JIS + LEED + NFRC + RIBA + UL + UNECE + VDI + VFF + WELL +} + +enum UpdateChildGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + UNKNOWN_METHOD + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum UpdateDataAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum UpdateDataErrorCode { + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE +} + +enum UpdateInstitutionAccessRightsErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_ACCESS_RIGHTS +} + +enum UpdateResponseApprovalsErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_DATABASE + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum UpdateRootGetHttpsResourceErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_RESOURCE + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + +enum UriHostNameType { + UNKNOWN + BASIC + DNS + I_PV4 + I_PV6 +} + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION + +"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." +directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR + +scalar Any + +"The `DateTime` scalar represents an ISO-8601 compliant date time type." +scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") + +""" +Represents a time zone - a mapping between UTC and local time. +A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. + +Example: `Europe/Zurich` +""" +scalar DateTimeZone + +""" +Represents a fixed (and calendar-independent) length of time. + +Allowed patterns: +- `-D:hh:mm:ss.sssssssss` + +Examples: +- `-1:20:00:00.999999999` +""" +scalar Duration + +"BCP 47 compliant Language Tag string" +scalar Locale + +"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +scalar Long + +"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." +scalar NonNegativeInt + +"The `Upload` scalar type represents a file upload." +scalar Upload + +scalar Url + +scalar Uuid \ No newline at end of file diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 46e145cf..427cabc6 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -466,7 +466,7 @@ type InstitutionAccessRights { hasRestrictionsByUser: Boolean! id: Uuid! institutionId: Uuid! - period: TimeSpan! + period: Duration! userAlreadyAccessed: [Uuid!]! version: NonNegativeInt! } @@ -919,6 +919,16 @@ input BooleanPropositionInput { notEqualTo: Boolean @cost(weight: "10") } +input CalendarSystemPropositionInput { + and: [CalendarSystemPropositionInput!] + or: [CalendarSystemPropositionInput!] + id: StringPropositionInput + name: StringPropositionInput + minYear: Int32PropositionInput + maxYear: Int32PropositionInput + eras: ListFilterInputTypeOfEraPropositionInput +} + input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] @@ -929,7 +939,7 @@ input CalorimetricDataPropositionInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -974,7 +984,7 @@ input CreateCalorimetricDataInput { creatorId: Uuid! description: String gValues: [Float!]! - locale: String! + locale: Locale! name: String rootResource: RootGetHttpsResourceInput! uValues: [Float!]! @@ -988,7 +998,7 @@ input CreateGeometricDataInput { creatorId: Uuid! description: String heights: [Float!]! - locale: String! + locale: Locale! name: String rootResource: RootGetHttpsResourceInput! thicknesses: [Float!]! @@ -1012,7 +1022,7 @@ input CreateHygrothermalDataInput { createdAt: DateTime! creatorId: Uuid! description: String - locale: String! + locale: Locale! name: String rootResource: RootGetHttpsResourceInput! warnings: [String!]! @@ -1028,7 +1038,7 @@ input CreateOpticalDataInput { creatorId: Uuid! description: String infraredEmittances: [Float!]! - locale: String! + locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! nearnormalHemisphericalSolarTransmittances: [Float!]! @@ -1046,7 +1056,7 @@ input CreatePhotovoltaicDataInput { createdAt: DateTime! creatorId: Uuid! description: String - locale: String! + locale: Locale! name: String rootResource: RootGetHttpsResourceInput! warnings: [String!]! @@ -1062,7 +1072,7 @@ input CreateResponseApprovalsFilterInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1080,7 +1090,7 @@ input CrossDatabaseDataReferencePropositionInput { and: [CrossDatabaseDataReferencePropositionInput!] or: [CrossDatabaseDataReferencePropositionInput!] dataId: UuidPropositionInput - dataTimestamp: DateTimePropositionInput + dataTimestamp: OffsetDateTimePropositionInput dataKind: DataKindPropositionInput databaseId: UuidPropositionInput } @@ -1089,7 +1099,7 @@ input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] or: [DataApprovalPropositionInput!] approverId: UuidPropositionInput - timestamp: DateTimePropositionInput + timestamp: OffsetDateTimePropositionInput signature: StringPropositionInput keyFingerprint: StringPropositionInput query: StringPropositionInput @@ -1108,7 +1118,7 @@ input DataFilterInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1133,21 +1143,6 @@ input DataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } -input DateTimePropositionInput { - equalTo: DateTime @cost(weight: "10") - notEqualTo: DateTime @cost(weight: "10") - in: [DateTime!] @cost(weight: "10") - notIn: [DateTime!] @cost(weight: "10") - greaterThan: DateTime @cost(weight: "10") - notGreaterThan: DateTime @cost(weight: "10") - greaterThanOrEqualTo: DateTime @cost(weight: "10") - notGreaterThanOrEqualTo: DateTime @cost(weight: "10") - lessThan: DateTime @cost(weight: "10") - notLessThan: DateTime @cost(weight: "10") - lessThanOrEqualTo: DateTime @cost(weight: "10") - notLessThanOrEqualTo: DateTime @cost(weight: "10") -} - input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1157,6 +1152,12 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } +input EraPropositionInput { + and: [EraPropositionInput!] + or: [EraPropositionInput!] + name: StringPropositionInput +} + input FileMetaInformationInput { dataFormatId: Uuid! path: [String!]! @@ -1201,7 +1202,7 @@ input GeometricDataPropositionInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1277,7 +1278,7 @@ input HygrothermalDataPropositionInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1310,6 +1311,28 @@ input Int32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } +input Int64PropositionInput { + equalTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + in: [Long!] @cost(weight: "10") + notIn: [Long!] @cost(weight: "10") + greaterThan: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + greaterThanOrEqualTo: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + lessThan: Long @cost(weight: "10") + notLessThan: Long @cost(weight: "10") + lessThanOrEqualTo: Long @cost(weight: "10") + notLessThanOrEqualTo: Long @cost(weight: "10") +} + +input IsoDayOfWeekPropositionInput { + equalTo: IsoDayOfWeek @cost(weight: "10") + notEqualTo: IsoDayOfWeek @cost(weight: "10") + in: [IsoDayOfWeek!] @cost(weight: "10") + notIn: [IsoDayOfWeek!] @cost(weight: "10") +} + input JsonElementPropositionInput { and: [JsonElementPropositionInput!] or: [JsonElementPropositionInput!] @@ -1342,6 +1365,13 @@ input ListFilterInputTypeOfDataApprovalPropositionInput { any: Boolean @cost(weight: "10") } +input ListFilterInputTypeOfEraPropositionInput { + all: EraPropositionInput @cost(weight: "10") + none: EraPropositionInput @cost(weight: "10") + some: EraPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input ListFilterInputTypeOfFileMetaInformationPropositionInput { all: FileMetaInformationPropositionInput @cost(weight: "10") none: FileMetaInformationPropositionInput @cost(weight: "10") @@ -1377,6 +1407,57 @@ input ListStandardizerPropositionInput { any: Boolean @cost(weight: "10") } +input LocalDatePropositionInput { + and: [LocalDatePropositionInput!] + or: [LocalDatePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + month: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + dayOfYear: Int32PropositionInput +} + +input LocalDateTimePropositionInput { + and: [LocalDateTimePropositionInput!] + or: [LocalDateTimePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + month: Int32PropositionInput + dayOfYear: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput + timeOfDay: LocalTimePropositionInput + date: LocalDatePropositionInput +} + +input LocalTimePropositionInput { + and: [LocalTimePropositionInput!] + or: [LocalTimePropositionInput!] + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput +} + input NamedMethodArgumentInput { name: String! value: Any! @@ -1489,6 +1570,41 @@ input NumerationSortInput { suffix: SortEnumType @cost(weight: "10") } +input OffsetDateTimePropositionInput { + and: [OffsetDateTimePropositionInput!] + or: [OffsetDateTimePropositionInput!] + calendar: CalendarSystemPropositionInput + year: Int32PropositionInput + month: Int32PropositionInput + day: Int32PropositionInput + dayOfWeek: IsoDayOfWeekPropositionInput + yearOfEra: Int32PropositionInput + era: EraPropositionInput + dayOfYear: Int32PropositionInput + hour: Int32PropositionInput + clockHourOfHalfDay: Int32PropositionInput + minute: Int32PropositionInput + second: Int32PropositionInput + millisecond: Int32PropositionInput + tickOfSecond: Int32PropositionInput + tickOfDay: Int64PropositionInput + nanosecondOfSecond: Int32PropositionInput + nanosecondOfDay: Int64PropositionInput + localDateTime: LocalDateTimePropositionInput + date: LocalDatePropositionInput + timeOfDay: LocalTimePropositionInput + offset: OffsetPropositionInput +} + +input OffsetPropositionInput { + and: [OffsetPropositionInput!] + or: [OffsetPropositionInput!] + seconds: Int32PropositionInput + milliseconds: Int32PropositionInput + ticks: Int64PropositionInput + nanoseconds: Int64PropositionInput +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] @@ -1499,7 +1615,7 @@ input OpticalDataPropositionInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1537,7 +1653,7 @@ input PhotovoltaicDataPropositionInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -1798,7 +1914,7 @@ input UpdateResponseApprovalsFilterInput { description: StringPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput - createdAt: DateTimePropositionInput + createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: ListFilterInputTypeOfDataApprovalPropositionInput resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput @@ -2066,6 +2182,17 @@ enum Illuminant { D65 } +enum IsoDayOfWeek { + NONE + MONDAY + TUESDAY + WEDNESDAY + THURSDAY + FRIDAY + SATURDAY + SUNDAY +} + enum JsonValueKind { UNDEFINED OBJECT @@ -2261,15 +2388,34 @@ scalar Any "The `DateTime` scalar represents an ISO-8601 compliant date time type." scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") +""" +Represents a time zone - a mapping between UTC and local time. +A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. + +Example: `Europe/Zurich` +""" +scalar DateTimeZone + +""" +Represents a fixed (and calendar-independent) length of time. + +Allowed patterns: +- `-D:hh:mm:ss.sssssssss` + +Examples: +- `-1:20:00:00.999999999` +""" +scalar Duration + "BCP 47 compliant Language Tag string" scalar Locale +"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +scalar Long + "The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." scalar NonNegativeInt -"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." -scalar TimeSpan - "The `Upload` scalar type represents a file upload." scalar Upload From 0cfc261a857310010e5ab159e7eaac3030fcabcf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 4 Dec 2025 18:19:17 +0100 Subject: [PATCH 601/878] Use hot reload because address binding fails with `--no-hot--reload` as of .NET 10 (even though hot reload does not work when the GraphQL schema changes, which then requires a `docker compose --file docker-compose.yml --project-name database restart backend`) --- backend/src/Database.csproj | 6 ++++++ docker-compose.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index a94db20f..26f5731d 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -83,4 +83,10 @@ PreserveNewest + + + + + + diff --git a/docker-compose.yml b/docker-compose.yml index 1cc6d7d8..52fc0972 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,7 +67,7 @@ services: command: [ "dotnet", "watch", "--non-interactive", - "--no-hot-reload", # as of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803 + # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port. Just using `--non-interactive` is suggested in https://github.com/dotnet/sdk/issues/50965#issuecomment-3523103468 "--project", "/app/src/Database.csproj", "--", "run", From 0930b1447739032643552d71a4074deb32d52189 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Dec 2025 21:24:15 +0100 Subject: [PATCH 602/878] Gather all JSON serializer options in one place and fine-tune them --- backend/src/Data/Reference.cs | 1 - backend/src/Json/JsonSerializerSettings.cs | 24 ++++++++++-- ...meConverterUsingDateTimeParseAsFallback.cs | 38 +++++++++++++++++++ backend/src/Json/ReferenceJsonConverter.cs | 2 +- backend/src/Methods/MethodBase.cs | 27 ++----------- backend/test/Integration/IntegrationTests.cs | 14 +------ .../Methods/SpectralToIntegralMethodTests.cs | 4 +- 7 files changed, 66 insertions(+), 44 deletions(-) create mode 100644 backend/src/Json/OffsetDateTimeConverterUsingDateTimeParseAsFallback.cs diff --git a/backend/src/Data/Reference.cs b/backend/src/Data/Reference.cs index 88a26483..a56ca3c5 100644 --- a/backend/src/Data/Reference.cs +++ b/backend/src/Data/Reference.cs @@ -1,4 +1,3 @@ - using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; diff --git a/backend/src/Json/JsonSerializerSettings.cs b/backend/src/Json/JsonSerializerSettings.cs index f23269f5..0d87945d 100644 --- a/backend/src/Json/JsonSerializerSettings.cs +++ b/backend/src/Json/JsonSerializerSettings.cs @@ -1,6 +1,5 @@ using System.Text.Json; using System.Text.Json.Serialization; -using GraphQL.Client.Serializer.SystemTextJson; using NodaTime; using NodaTime.Serialization.SystemTextJson; @@ -14,16 +13,25 @@ public static class JsonSerializerSettings private static readonly JsonSerializerOptions s_common = new JsonSerializerOptions() { + Converters = + { + new OffsetDateTimeConverterUsingDateTimeParseAsFallback() + }, + AllowTrailingCommas = true, AllowOutOfOrderMetadataProperties = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, IgnoreReadOnlyFields = true, IgnoreReadOnlyProperties = false, IncludeFields = false, NumberHandling = JsonNumberHandling.Strict, + PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace, PropertyNameCaseInsensitive = false, ReadCommentHandling = JsonCommentHandling.Disallow, + ReferenceHandler = ReferenceHandler.IgnoreCycles, RespectNullableAnnotations = true, + UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement, UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, + WriteIndented = false, AllowDuplicateProperties = false, } .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); @@ -31,9 +39,13 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions GraphQl = new(s_common) { - Converters = { new JsonStringEnumConverter(new ToConstantCaseJsonNamingPolicy(), allowIntegerValues: false) }, + // because `Converters` is read-only, the converters below are `Add`ed according to https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers-with-collection-read-only-property-initialization + Converters = + { + new JsonStringEnumConverter(new ToConstantCaseJsonNamingPolicy(), allowIntegerValues: false) + }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - }; + }; //.SetupImmutableConverter(); public static readonly JsonSerializerOptions Rest = new(s_common) @@ -44,7 +56,11 @@ public static class JsonSerializerSettings public static readonly JsonSerializerOptions BedJson = new(s_common) { - Converters = { new JsonStringEnumConverter(new ToCamelCaseJsonNamingPolicy(), allowIntegerValues: false) }, + // because `Converters` is read-only, the converters below are `Add`ed according to https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers-with-collection-read-only-property-initialization + Converters = { + new JsonStringEnumConverter(new ToCamelCaseJsonNamingPolicy(), allowIntegerValues: false) + }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; } \ No newline at end of file diff --git a/backend/src/Json/OffsetDateTimeConverterUsingDateTimeParseAsFallback.cs b/backend/src/Json/OffsetDateTimeConverterUsingDateTimeParseAsFallback.cs new file mode 100644 index 00000000..736c1090 --- /dev/null +++ b/backend/src/Json/OffsetDateTimeConverterUsingDateTimeParseAsFallback.cs @@ -0,0 +1,38 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using NodaTime; +using NodaTime.Serialization.SystemTextJson; + +namespace Database.Json; + +// Inspired by https://learn.microsoft.com/en-us/dotnet/standard/datetime/system-text-json-support#use-datetimeoffsetparse-as-a-fallback +public sealed class OffsetDateTimeConverterUsingDateTimeParseAsFallback : JsonConverter +{ + public override OffsetDateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var converter = NodaConverters.OffsetDateTimeConverter; + Debug.Assert(converter.CanConvert(typeToConvert)); + try + { + // OffsetDateTimePattern.ExtendedIso.Parse(reader.GetString()); + return converter.Read(ref reader, typeToConvert, options); + } + catch (JsonException) + { + return OffsetDateTime.FromDateTimeOffset( + DateTimeOffset.Parse(reader.GetString() ?? "", + CultureInfo.InvariantCulture) + ); + } + } + + public override void Write(Utf8JsonWriter writer, OffsetDateTime value, JsonSerializerOptions options) + { + // For information on the format specifier `o`, see + // https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#the-round-trip-o-o-format-specifier + writer.WriteStringValue(value.ToString("o", CultureInfo.InvariantCulture)); + } +} \ No newline at end of file diff --git a/backend/src/Json/ReferenceJsonConverter.cs b/backend/src/Json/ReferenceJsonConverter.cs index 517440d0..149529f5 100644 --- a/backend/src/Json/ReferenceJsonConverter.cs +++ b/backend/src/Json/ReferenceJsonConverter.cs @@ -7,7 +7,7 @@ namespace Database.Json; // Inspired by https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to public sealed class ReferenceJsonConverter - : JsonConverter +: JsonConverter { public ReferenceJsonConverter() { diff --git a/backend/src/Methods/MethodBase.cs b/backend/src/Methods/MethodBase.cs index f87ecf43..aaa2ea41 100644 --- a/backend/src/Methods/MethodBase.cs +++ b/backend/src/Methods/MethodBase.cs @@ -1,36 +1,15 @@ -using System; using System.Text.Json; -using System.Text.Json.Serialization; -using NodaTime; -using NodaTime.Serialization.SystemTextJson; +using Database.Json; namespace Database.Methods; public abstract class MethodBase : IMethod { - public static readonly JsonSerializerOptions JsonSerializerOptions = - new JsonSerializerOptions() - { - AllowTrailingCommas = true, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - IgnoreReadOnlyFields = true, - IgnoreReadOnlyProperties = false, - IncludeFields = false, - NumberHandling = JsonNumberHandling.Strict, - PropertyNameCaseInsensitive = false, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReadCommentHandling = JsonCommentHandling.Skip, - RespectNullableAnnotations = true, - UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, - AllowDuplicateProperties = false, - } - .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - public JsonElement Calculate(JsonElement input) { - var parsedInput = input.Deserialize(JsonSerializerOptions) - ?? throw new InvalidOperationException(); + var parsedInput = input.Deserialize(JsonSerializerSettings.BedJson) + ?? throw new JsonException($"Failed to deserialize the JSON input into a {typeof(TInput)} instance."); var output = Calculate(parsedInput); using var document = JsonDocument.Parse( JsonSerializer.SerializeToUtf8Bytes(output) diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs index a5aa72ac..162f75a0 100644 --- a/backend/test/Integration/IntegrationTests.cs +++ b/backend/test/Integration/IntegrationTests.cs @@ -9,7 +9,6 @@ using System.Text; using System.Text.Json; using System.Text.Json.Nodes; -using System.Text.Json.Serialization; using System.Threading.Tasks; using FluentAssertions; using Json.Path; @@ -17,8 +16,7 @@ using Snapshooter; using Microsoft.AspNetCore.Mvc.Testing; using Database.Data; -using NodaTime.Serialization.SystemTextJson; -using NodaTime; +using Database.Json; namespace Database.Tests.Integration; @@ -26,14 +24,6 @@ namespace Database.Tests.Integration; public abstract partial class IntegrationTests : IDisposable { - private static readonly JsonSerializerOptions s_customJsonSerializerOptions = - new JsonSerializerOptions() - { - Converters = { new JsonStringEnumConverter() }, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - } - .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - private bool _disposed; protected IntegrationTests() @@ -419,7 +409,7 @@ TContent content new ByteArrayContent( JsonSerializer.SerializeToUtf8Bytes( content, - s_customJsonSerializerOptions + JsonSerializerSettings.GraphQl ) ); result.Headers.ContentType = diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 9331335e..1ab29919 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -97,8 +97,8 @@ public async Task Calculate_Many() JsonDocumentSettings.Lax ); var spectralDataPoints = jsonDocument.RootElement.Deserialize( - MethodBase.JsonSerializerOptions - ) ?? throw new InvalidOperationException(); + JsonSerializerSettings.BedJson + ) ?? throw new JsonException($"Failed to deserialize the root elemnt into {typeof(SpectralToIntegralInput)}"); var method = new SpectralToIntegralMethod(); var results = method.Calculate(spectralDataPoints); using (new AssertionScope()) From 664b03f74c3882038ee7e6ec8384a76c37288dd2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Dec 2025 21:25:13 +0100 Subject: [PATCH 603/878] Do not use Enumerable methods on indexable collections. Instead use the collection directly --- .../src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index e58b468f..117db51e 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -277,7 +277,7 @@ public double Calculate( denominator += wavelengthsWeights[i].deltaWavelength * wavelengthsWeights[i].weight; } // Treat the last wavelengthWeight separately, because it has no spectralDataPointWavelengthAbove - numerator += spectralDataPoints[spectralDataPoints.Count() - 1].Results.Transmittance * wavelengthsWeights[wavelengthsWeights.Length - 1].deltaWavelength * wavelengthsWeights[wavelengthsWeights.Length - 1].weight; + numerator += spectralDataPoints[spectralDataPoints.Count - 1].Results.Transmittance * wavelengthsWeights[wavelengthsWeights.Length - 1].deltaWavelength * wavelengthsWeights[wavelengthsWeights.Length - 1].weight; denominator += wavelengthsWeights[wavelengthsWeights.Length - 1].deltaWavelength * wavelengthsWeights[wavelengthsWeights.Length - 1].weight; return numerator / denominator; } From 853acaa388ef073b971e10dbbdb1d41ac476772f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Dec 2025 21:31:13 +0100 Subject: [PATCH 604/878] Use strict scalars for proper type mappings --- frontend/codegen.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/codegen.ts b/frontend/codegen.ts index 33844950..5965ba2a 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -34,19 +34,23 @@ const config: CodegenConfig = { // don't generate a type for the `__typename` for root operation types. skipTypeNameForRoot: true, // ... - // strictScalars: true, + strictScalars: true, scalars: { ID: { input: "string", - output: "string | number", + output: "string", }, Any: "unknown", Byte: "number", DateTime: "string", + DateTimeZone: "string", + Duration: "string", Locale: "string", NonNegativeInt: "number", + Long: "number", Url: "string", Uuid: "string", + Upload: "unknown", }, useTypeImports: true, // immutableTypes: true, From 88ff192063a3c2c570579778d9d03150e61eab03 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Dec 2025 21:44:41 +0100 Subject: [PATCH 605/878] Refresh baseline browser mapping on build as suggested on https://www.npmjs.com/package/baseline-browser-mapping --- frontend/package.json | 6 +- frontend/yarn.lock | 1898 ++++++++++++++++++++--------------------- 2 files changed, 953 insertions(+), 951 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index de68f731..4c4daf8d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,9 +8,10 @@ "codegen": "graphql-codegen --config ./codegen.ts", "codegen:watch": "graphql-codegen --config ./codegen.ts --watch", "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_OPTIONS=\"--use-openssl-ca\" next dev --port 8080'", - "build": "yarn codegen && NODE_ENV=production next build", + "build": "yarn refresh-baseline-browser-mapping && yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", - "start": "next start --port 8080" + "start": "next start --port 8080", + "refresh-baseline-browser-mapping": "yarn install --dev baseline-browser-mapping@latest" }, "dependencies": { "@ant-design/pro-layout": "^7.10.3", @@ -41,6 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", + "baseline-browser-mapping": "^2.9.3", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 569d1cca..4e15e831 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4,14 +4,14 @@ "@ant-design/colors@^7.0.0", "@ant-design/colors@^7.2.1": version "7.2.1" - resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.2.1.tgz#3bbc1c6c18550020d1622a0067ff03492318df98" + resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz" integrity sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== dependencies: "@ant-design/fast-color" "^2.0.6" "@ant-design/cssinjs-utils@^1.1.3": version "1.1.3" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" + resolved "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz" integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== dependencies: "@ant-design/cssinjs" "^1.21.0" @@ -20,7 +20,7 @@ "@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1", "@ant-design/cssinjs@^1.23.0": version "1.24.0" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz#7db091f03f189abc77a13cbd27a2293802cd7285" + resolved "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz" integrity sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg== dependencies: "@babel/runtime" "^7.11.1" @@ -33,19 +33,19 @@ "@ant-design/fast-color@^2.0.6": version "2.0.6" - resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" + resolved "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz" integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== dependencies: "@babel/runtime" "^7.24.7" "@ant-design/icons-svg@^4.4.0": version "4.4.2" - resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" + resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== "@ant-design/icons@^5.0.0", "@ant-design/icons@^5.6.1": version "5.6.1" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.6.1.tgz#7290fcdc3d96ff3fca793ed399053cd29ad5dbd3" + resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz" integrity sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== dependencies: "@ant-design/colors" "^7.0.0" @@ -56,7 +56,7 @@ "@ant-design/pro-layout@^7.10.3": version "7.22.7" - resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz#7002b1838e1cdf879bbec813677b478c26fbbb77" + resolved "https://registry.npmjs.org/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz" integrity sha512-fvmtNA1r9SaasVIQIQt611VSlNxtVxDbQ3e+1GhYQza3tVJi/3gCZuDyfMfTnbLmf3PaW/YvLkn7MqDbzAzoLA== dependencies: "@ant-design/cssinjs" "^1.21.1" @@ -77,7 +77,7 @@ "@ant-design/pro-provider@2.16.2": version "2.16.2" - resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.16.2.tgz#3ccec06ea9a69a4d48adc593a268413c813dab2f" + resolved "https://registry.npmjs.org/@ant-design/pro-provider/-/pro-provider-2.16.2.tgz" integrity sha512-0KmCH1EaOND787Jz6VRMYtLNZmqfT0JPjdUfxhyOxFfnBRfrjyfZgIa6CQoAJLEUMWv57PccWS8wRHVUUk2Yiw== dependencies: "@ant-design/cssinjs" "^1.21.1" @@ -89,7 +89,7 @@ "@ant-design/pro-utils@2.18.0": version "2.18.0" - resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.18.0.tgz#50a6bcc95742b71a7a17252f7db6c2ccd2ec60f4" + resolved "https://registry.npmjs.org/@ant-design/pro-utils/-/pro-utils-2.18.0.tgz" integrity sha512-8+ikyrN8L8a8Ph4oeHTOJEiranTj18+9+WHCHjKNdEfukI7Rjn8xpYdLJWb2AUJkb9d4eoAqjd5+k+7w81Df0w== dependencies: "@ant-design/icons" "^5.0.0" @@ -105,7 +105,7 @@ "@ant-design/react-slick@~1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" + resolved "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz" integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== dependencies: "@babel/runtime" "^7.10.4" @@ -116,7 +116,7 @@ "@apollo/client@^4.0.9": version "4.0.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.0.9.tgz#fdc0388f73a1640f3da30a8791e448b96ad0b49a" + resolved "https://registry.npmjs.org/@apollo/client/-/client-4.0.9.tgz" integrity sha512-Lh2drMzFE9x5jVS8RKmlGL5SORkvpyUJIT+wTErxDUR2HpWePiBfhhcHHRSlZFiCR866ewCv4euTc4IDF0GWxw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -129,7 +129,7 @@ "@ardatan/relay-compiler@12.0.0": version "12.0.0" - resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz#2e4cca43088e807adc63450e8cab037020e91106" + resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz" integrity sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q== dependencies: "@babel/core" "^7.14.0" @@ -152,7 +152,7 @@ "@ardatan/relay-compiler@^12.0.3": version "12.0.3" - resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz#a60824672da7f7cef2a3879ed833b120d292fa08" + resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz" integrity sha512-mBDFOGvAoVlWaWqs3hm1AciGHSQE1rqFc/liZTyYz/Oek9yZdT5H26pH2zAFuEiTiBVPPyMuqf5VjOFPI2DGsQ== dependencies: "@babel/generator" "^7.26.10" @@ -168,7 +168,7 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: "@babel/helper-validator-identifier" "^7.27.1" @@ -177,12 +177,12 @@ "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" + resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== "@babel/core@^7.0", "@babel/core@^7.14.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" + resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== dependencies: "@babel/code-frame" "^7.27.1" @@ -203,7 +203,7 @@ "@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.26.10", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" + resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== dependencies: "@babel/parser" "^7.28.5" @@ -214,14 +214,14 @@ "@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": version "7.27.3" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz#f31fd86b915fc4daf1f3ac6976c59be7084ed9c5" + resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz" integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== dependencies: "@babel/types" "^7.27.3" "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" + resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz" integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: "@babel/compat-data" "^7.27.2" @@ -232,7 +232,7 @@ "@babel/helper-create-class-features-plugin@^7.18.6": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz#472d0c28028850968979ad89f173594a6995da46" + resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz" integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -245,12 +245,12 @@ "@babel/helper-globals@^7.28.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" + resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== "@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz#f3e07a10be37ed7a63461c63e6929575945a6150" + resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz" integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== dependencies: "@babel/traverse" "^7.28.5" @@ -258,7 +258,7 @@ "@babel/helper-module-imports@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: "@babel/traverse" "^7.27.1" @@ -266,7 +266,7 @@ "@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" + resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz" integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== dependencies: "@babel/helper-module-imports" "^7.27.1" @@ -275,19 +275,19 @@ "@babel/helper-optimise-call-expression@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz" integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== dependencies: "@babel/types" "^7.27.1" "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz" integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== "@babel/helper-replace-supers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz" integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== dependencies: "@babel/helper-member-expression-to-functions" "^7.27.1" @@ -296,7 +296,7 @@ "@babel/helper-skip-transparent-expression-wrappers@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz" integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== dependencies: "@babel/traverse" "^7.27.1" @@ -304,22 +304,22 @@ "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helpers@^7.28.4": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" + resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz" integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== dependencies: "@babel/template" "^7.27.2" @@ -327,14 +327,14 @@ "@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz" integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: "@babel/types" "^7.28.5" "@babel/plugin-proposal-class-properties@^7.0.0": version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== dependencies: "@babel/helper-create-class-features-plugin" "^7.18.6" @@ -342,7 +342,7 @@ "@babel/plugin-proposal-object-rest-spread@^7.0.0": version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== dependencies: "@babel/compat-data" "^7.20.5" @@ -353,161 +353,161 @@ "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz#6c83cf0d7d635b716827284b7ecd5aead9237662" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz" integrity sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-assertions@^7.26.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz" integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-attributes@^7.24.7": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz" integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz" integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.27.1": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz" integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-arrow-functions@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz" integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-block-scoped-functions@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz" integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-block-scoping@^7.0.0": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz#e0d3af63bd8c80de2e567e690a54e84d85eb16f6" + resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz" integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-classes@^7.0.0": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz#75d66175486788c56728a73424d67cbc7473495c" + resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz" integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== dependencies: "@babel/helper-annotate-as-pure" "^7.27.3" @@ -519,7 +519,7 @@ "@babel/plugin-transform-computed-properties@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz" integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -527,7 +527,7 @@ "@babel/plugin-transform-destructuring@^7.0.0": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz#b8402764df96179a2070bb7b501a1586cf8ad7a7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz" integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -535,7 +535,7 @@ "@babel/plugin-transform-flow-strip-types@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz#5def3e1e7730f008d683144fb79b724f92c5cdf9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz" integrity sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -543,7 +543,7 @@ "@babel/plugin-transform-for-of@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz" integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -551,7 +551,7 @@ "@babel/plugin-transform-function-name@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz" integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== dependencies: "@babel/helper-compilation-targets" "^7.27.1" @@ -560,21 +560,21 @@ "@babel/plugin-transform-literals@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz" integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-member-expression-literals@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz" integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-modules-commonjs@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz" integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== dependencies: "@babel/helper-module-transforms" "^7.27.1" @@ -582,7 +582,7 @@ "@babel/plugin-transform-object-super@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz" integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -590,28 +590,28 @@ "@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": version "7.27.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz#1fd2febb7c74e7d21cf3b05f7aebc907940af53a" + resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz" integrity sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-property-literals@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz" integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-display-name@^7.0.0": version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz#6f20a7295fea7df42eb42fed8f896813f5b934de" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz" integrity sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-jsx@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz#1023bc94b78b0a2d68c82b5e96aed573bcfb9db0" + resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz" integrity sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw== dependencies: "@babel/helper-annotate-as-pure" "^7.27.1" @@ -622,14 +622,14 @@ "@babel/plugin-transform-shorthand-properties@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz" integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-spread@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz" integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== dependencies: "@babel/helper-plugin-utils" "^7.27.1" @@ -637,19 +637,19 @@ "@babel/plugin-transform-template-literals@^7.0.0": version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz" integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10": version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== "@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.1", "@babel/template@^7.27.2": version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" + resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== dependencies: "@babel/code-frame" "^7.27.1" @@ -658,7 +658,7 @@ "@babel/traverse@^7.14.0", "@babel/traverse@^7.26.10", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" + resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz" integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== dependencies: "@babel/code-frame" "^7.27.1" @@ -671,7 +671,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5": version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz" integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: "@babel/helper-string-parser" "^7.27.1" @@ -679,19 +679,19 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" "@ctrl/tinycolor@^3.4.0": version "3.6.1" - resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" + resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== "@emnapi/core@^1.4.3": @@ -718,17 +718,17 @@ "@emotion/hash@^0.8.0": version "0.8.0" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== "@emotion/unitless@^0.7.5": version "0.7.5" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== "@envelop/core@^5.2.3", "@envelop/core@^5.3.0", "@envelop/core@^5.4.0": version "5.4.0" - resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.4.0.tgz#e79b8da5ab4f05eccd9758435df3dc50b11e607d" + resolved "https://registry.npmjs.org/@envelop/core/-/core-5.4.0.tgz" integrity sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ== dependencies: "@envelop/instrumentation" "^1.0.0" @@ -738,7 +738,7 @@ "@envelop/instrumentation@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@envelop/instrumentation/-/instrumentation-1.0.0.tgz#43268392e065d8ba975cacbdf4fc297dfe3e11e5" + resolved "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz" integrity sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw== dependencies: "@whatwg-node/promise-helpers" "^1.2.1" @@ -746,7 +746,7 @@ "@envelop/types@^5.2.1": version "5.2.1" - resolved "https://registry.yarnpkg.com/@envelop/types/-/types-5.2.1.tgz#6bc9713f2aea56d7de3ea39e8bb70035c0475b36" + resolved "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz" integrity sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg== dependencies: "@whatwg-node/promise-helpers" "^1.0.0" @@ -754,19 +754,19 @@ "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": version "4.9.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz" integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": version "4.12.2" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/config-array@^0.21.1": version "0.21.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" + resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz" integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: "@eslint/object-schema" "^2.1.7" @@ -775,21 +775,21 @@ "@eslint/config-helpers@^0.4.2": version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" + resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz" integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== dependencies: "@eslint/core" "^0.17.0" "@eslint/core@^0.17.0": version "0.17.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" + resolved "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz" integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== dependencies: "@types/json-schema" "^7.0.15" "@eslint/eslintrc@^3.3.1": version "3.3.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz" integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== dependencies: ajv "^6.12.4" @@ -804,17 +804,17 @@ "@eslint/js@9.39.1": version "9.39.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" + resolved "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz" integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== "@eslint/object-schema@^2.1.7": version "2.1.7" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" + resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz" integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== "@eslint/plugin-kit@^0.4.1": version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" + resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz" integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: "@eslint/core" "^0.17.0" @@ -822,12 +822,12 @@ "@fastify/busboy@^3.1.1": version "3.2.0" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz" integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== "@graphql-codegen/add@^3.2.1": version "3.2.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-3.2.3.tgz#f1ecee085987e7c21841edc4b1fd48877c663e1a" + resolved "https://registry.npmjs.org/@graphql-codegen/add/-/add-3.2.3.tgz" integrity sha512-sQOnWpMko4JLeykwyjFTxnhqjd/3NOG2OyMuvK76Wnnwh8DRrNf2VEs2kmSvLl7MndMlOj7Kh5U154dVcvhmKQ== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.1" @@ -835,7 +835,7 @@ "@graphql-codegen/add@^6.0.0": version "6.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.0.tgz#d195051d7c4bd6d3955a92361cc9eec7784bb541" + resolved "https://registry.npmjs.org/@graphql-codegen/add/-/add-6.0.0.tgz" integrity sha512-biFdaURX0KTwEJPQ1wkT6BRgNasqgQ5KbCI1a3zwtLtO7XTo7/vKITPylmiU27K5DSOWYnY/1jfSqUAEBuhZrQ== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -843,7 +843,7 @@ "@graphql-codegen/cli@^6.0.2": version "6.0.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.0.2.tgz#516330f5828630a0f83db3cbf810c164d00230ef" + resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-6.0.2.tgz" integrity sha512-W+0ime0xMrCyG77q+5xiPkkqPLuXJcTx0Zr9TTOxF4zIqWKVsuImS3qVxtpeTx+GRbb8VWv9IedWMtt91JGzQg== dependencies: "@babel/generator" "^7.18.13" @@ -883,7 +883,7 @@ "@graphql-codegen/client-preset@^5.1.2": version "5.1.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.1.3.tgz#cfc7eedb2906c0e1318841bd7423cd94e1b980eb" + resolved "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-5.1.3.tgz" integrity sha512-8nlKt8/gO/BovWahLb96taMssHKPibBfslry1ed9DIJtbOrceFYF3yNbFZuTHmI644C7ZvoYK93JkE3VzDlCyg== dependencies: "@babel/helper-plugin-utils" "^7.20.2" @@ -902,7 +902,7 @@ "@graphql-codegen/core@^5.0.0": version "5.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.0.tgz#c3fd07226a3cd2f75c80896c127adc191f42e179" + resolved "https://registry.npmjs.org/@graphql-codegen/core/-/core-5.0.0.tgz" integrity sha512-vLTEW0m8LbE4xgRwbFwCdYxVkJ1dBlVJbQyLb9Q7bHnVFgHAP982Xo8Uv7FuPBmON+2IbTjkCqhFLHVZbqpvjQ== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -912,7 +912,7 @@ "@graphql-codegen/fragment-matcher@^6.0.0": version "6.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz#4beb848c0fdb38bbdc7b99250de84ec4456e7a5d" + resolved "https://registry.npmjs.org/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz" integrity sha512-lXdFWrUSEbOL2KhmYxdeNPLllcBGZnNunwgV72C12+21v9lyKaOqFnYueyt0pT110MGCwUhKTnXzUtV1D5i9Jw== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -920,7 +920,7 @@ "@graphql-codegen/gql-tag-operations@5.0.5": version "5.0.5" - resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.0.5.tgz#691f53a4a9e09608fd4345622fec01364294fabf" + resolved "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.0.5.tgz" integrity sha512-DutUBwA3UMOB2AI6O1FDidYw7N0Br4d/ogGrYg6XOeeVuRYigc6i9wX4qiv4ofD34Ujfcfze0U2PI3ZOR33NKw== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -931,7 +931,7 @@ "@graphql-codegen/near-operation-file-preset@^3.1.0": version "3.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-3.1.0.tgz#5c60a9ec137bb78d83eeab39a97595a09ad6bd9a" + resolved "https://registry.npmjs.org/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-3.1.0.tgz" integrity sha512-sXIIi0BPP3IcARdzSztpE51oJTcGB67hi7ddBYfLinks/R/5aFG1Ry/J61707Kt+6Q5WhTnf5XAQUAqi6200yA== dependencies: "@graphql-codegen/add" "^3.2.1" @@ -943,7 +943,7 @@ "@graphql-codegen/plugin-helpers@^3.0.0", "@graphql-codegen/plugin-helpers@^3.1.1", "@graphql-codegen/plugin-helpers@^3.1.2": version "3.1.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz#69a2e91178f478ea6849846ade0a59a844d34389" + resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz" integrity sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg== dependencies: "@graphql-tools/utils" "^9.0.0" @@ -955,7 +955,7 @@ "@graphql-codegen/plugin-helpers@^6.0.0": version "6.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.0.0.tgz#8a913c82a95b9ff36b2180f7c56611cc56268625" + resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.0.0.tgz" integrity sha512-Z7P89vViJvQakRyMbq/JF2iPLruRFOwOB6IXsuSvV/BptuuEd7fsGPuEf8bdjjDxUY0pJZnFN8oC7jIQ8p9GKA== dependencies: "@graphql-tools/utils" "^10.0.0" @@ -967,7 +967,7 @@ "@graphql-codegen/schema-ast@^5.0.0": version "5.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz#9708d7484a01bb3a502685126708d7b139fd0721" + resolved "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz" integrity sha512-jn7Q3PKQc0FxXjbpo9trxzlz/GSFQWxL042l0iC8iSbM/Ar+M7uyBwMtXPsev/3Razk+osQyreghIz0d2+6F7Q== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -976,7 +976,7 @@ "@graphql-codegen/typed-document-node@^6.1.2": version "6.1.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.2.tgz#81b6a4cc0ba6f632153190caf4cd9e10b4d04436" + resolved "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.2.tgz" integrity sha512-DelLv7BY8Sx0toyCiEsc46W3FtqipiiqhprUnGnSalfKnKVB8KUodXKaf70migy6hWyDl5d1OJOp5wrttuIy2Q== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -987,7 +987,7 @@ "@graphql-codegen/typescript-operations@^5.0.4": version "5.0.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.4.tgz#052680d41f4bab3bb85af8e906c43bde29239e7b" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.4.tgz" integrity sha512-5Bu/BTmyNjdSfSLLBKjC0+4XWcY01uotVcnVIWIxxRdIHoRxnTW6PUkT5CoPHP5r/Uoo3OvIJxh+0LYSH5suwA== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -998,7 +998,7 @@ "@graphql-codegen/typescript@^5.0.4": version "5.0.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.4.tgz#a7f51505445568fd737b01ce7fad515c2b89897f" + resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-5.0.4.tgz" integrity sha512-q6S8hX+aR4BzeGgolac4gp22rBnXbLhedmOwT1UBT9e3lGNmNpYC7WJUEzAPjWf6z1lRSNmojLlwEjTnffhKNA== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -1009,7 +1009,7 @@ "@graphql-codegen/visitor-plugin-common@2.13.8": version "2.13.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz#09bc6317b227e5a278f394f4cef0d6c2d1910597" + resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz" integrity sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ== dependencies: "@graphql-codegen/plugin-helpers" "^3.1.2" @@ -1025,7 +1025,7 @@ "@graphql-codegen/visitor-plugin-common@6.1.2", "@graphql-codegen/visitor-plugin-common@^6.1.2": version "6.1.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.1.2.tgz#e614d6606402a152686d6f90e6363150a7a55bde" + resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.1.2.tgz" integrity sha512-zYdrhJKgk8kqE1Xz5/m/Ua42zk+rIvYB/FHh3dE1AhZ6b1IDqgKjF3LnkT+K2qenf9EfT4yNjXd5CEKMeXfHyg== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -1041,17 +1041,17 @@ "@graphql-hive/signal@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-1.0.0.tgz#6e2193660a47c925abadbe72293dfc9430e24f8f" + resolved "https://registry.npmjs.org/@graphql-hive/signal/-/signal-1.0.0.tgz" integrity sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag== "@graphql-hive/signal@^2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-2.0.0.tgz#205805328b118e1ae556417ed364257234800464" + resolved "https://registry.npmjs.org/@graphql-hive/signal/-/signal-2.0.0.tgz" integrity sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ== "@graphql-tools/apollo-engine-loader@^8.0.0": version "8.0.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.26.tgz#38bd348c999f70b714421da22c0b4e4e1ac9a695" + resolved "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.26.tgz" integrity sha512-vNeWX/hGpHMVaDJNBbf7yhmy95ueZ3f4rUjl6v819Emsfo0ItnyTM2FmOPUwpWIm7DpCKCSt134ERy/fgo14rg== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1061,7 +1061,7 @@ "@graphql-tools/batch-execute@^10.0.3": version "10.0.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.3.tgz#57d7fa56d507d32990efbd96e09e80fa22a80363" + resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-10.0.3.tgz" integrity sha512-QH9tKbBQcLeSToXufGKRKWZUpVFJ+gRzJ8wSjz0amQXxpHiF8sEAIJKy9CmgRfnhvt31BjOMTr1sUS4xbhj7lw== dependencies: "@graphql-tools/utils" "^10.10.1" @@ -1071,7 +1071,7 @@ "@graphql-tools/batch-execute@^9.0.19": version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz#b724f3542e2cee8a3a71262a711469afa39cae88" + resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz" integrity sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA== dependencies: "@graphql-tools/utils" "^10.9.1" @@ -1081,7 +1081,7 @@ "@graphql-tools/code-file-loader@^8.0.0": version "8.1.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.26.tgz#d1013bf6e780972cc9ec7df6d07b2f1a5039f501" + resolved "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.26.tgz" integrity sha512-VamhpBEbrABCjtJqEFBUrHBBVX4Iw7q4Ga8H3W0P7mO+sE1HuTfpWirSdBLlhc6nGcSyTb6FA1mEgGjjUASIHA== dependencies: "@graphql-tools/graphql-tag-pluck" "8.3.25" @@ -1092,7 +1092,7 @@ "@graphql-tools/delegate@^10.2.23": version "10.2.23" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-10.2.23.tgz#fcd42e7aa70149269ad644e03098c8d28810bd87" + resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.23.tgz" integrity sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w== dependencies: "@graphql-tools/batch-execute" "^9.0.19" @@ -1107,7 +1107,7 @@ "@graphql-tools/delegate@^11.1.2": version "11.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-11.1.2.tgz#e075bf6b7a4b7c31eb792785242e3c7bc051ca95" + resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-11.1.2.tgz" integrity sha512-C6W4DM+z3nN7OkV08CAuBUmCW+etmfiKzlpx09oEGBWzw61eYQdKlqp1qqeKtYebELam9LkHTa1n8/rilv5PXw== dependencies: "@graphql-tools/batch-execute" "^10.0.3" @@ -1121,7 +1121,7 @@ "@graphql-tools/documents@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/documents/-/documents-1.0.1.tgz#ae19cd5667d22c23b331d3a1429443ed7130faee" + resolved "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.1.tgz" integrity sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA== dependencies: lodash.sortby "^4.7.0" @@ -1129,7 +1129,7 @@ "@graphql-tools/executor-common@^0.0.4": version "0.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz#763603a6d7a22bb09d67ce682e84a0d730ff2bf9" + resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz" integrity sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q== dependencies: "@envelop/core" "^5.2.3" @@ -1137,7 +1137,7 @@ "@graphql-tools/executor-common@^0.0.6": version "0.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz#f681de443d7e90e1768c44bd64a4cd418dc356e3" + resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz" integrity sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow== dependencies: "@envelop/core" "^5.3.0" @@ -1145,7 +1145,7 @@ "@graphql-tools/executor-common@^1.0.4": version "1.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-1.0.4.tgz#805d7b55a5f47c84ca4b12b27ea6786028d8623e" + resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-1.0.4.tgz" integrity sha512-EKkVjo1Fbx+qK5ZeI+KneeoGzk6HU6aL4E0bX4J3lfnjyaZVESbODCDS/9bBcrV1X2dEby9MfWqA6fkFodP+hw== dependencies: "@envelop/core" "^5.4.0" @@ -1153,7 +1153,7 @@ "@graphql-tools/executor-graphql-ws@^2.0.1": version "2.0.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz#e8d7689d266b3ccea962b708d6f5d31a1a246c66" + resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz" integrity sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q== dependencies: "@graphql-tools/executor-common" "^0.0.6" @@ -1166,7 +1166,7 @@ "@graphql-tools/executor-graphql-ws@^3.1.2": version "3.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.2.tgz#92a662fc4277b8c6fcf9f5e6f8a3fc5813930547" + resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.2.tgz" integrity sha512-nlBXYrWkZ+VFIVU2QrRNnWImBwZglY/uuyK0/dkFMFy9FEjZmErLpGv62PZqQUSEjPakNgUil+T94CUHGebR9w== dependencies: "@graphql-tools/executor-common" "^1.0.4" @@ -1179,7 +1179,7 @@ "@graphql-tools/executor-http@^1.1.9": version "1.3.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz#057de79ebc90edbd242259cccb1268d37ee6c579" + resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz" integrity sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A== dependencies: "@graphql-hive/signal" "^1.0.0" @@ -1194,7 +1194,7 @@ "@graphql-tools/executor-http@^3.0.6": version "3.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.0.6.tgz#3381b5170a9a3bfb5c9a2605be7109de2fe86189" + resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-3.0.6.tgz" integrity sha512-gTcSdaJWbqYKg/aVQ/T+sIC6Isa4jjx+QhVMyawgivmdbLTg0UgsbR6g97eNVoxnRzFq/CuPSy71IHQeq8Q6iA== dependencies: "@graphql-hive/signal" "^2.0.0" @@ -1209,7 +1209,7 @@ "@graphql-tools/executor-legacy-ws@^1.1.19", "@graphql-tools/executor-legacy-ws@^1.1.23": version "1.1.23" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.23.tgz#f9891d2fd591ebb9c5b77b73b55c93d0acd87fa2" + resolved "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.23.tgz" integrity sha512-wwS6ZlJDaC+zxE1DcfYrPJk1ynQ0xcbOWS/x8dy4hO6ZCjRawkogoqN3Muab0E9RzuwF29LRu+aOH6isO5mQKg== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1220,7 +1220,7 @@ "@graphql-tools/executor@^1.4.11", "@graphql-tools/executor@^1.4.9": version "1.4.13" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.4.13.tgz#ebc35e6060ba9dd88e4a664dac02b3f4b0ff0582" + resolved "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.13.tgz" integrity sha512-2hTSRfH2kb4ua0ANOV/K6xUoCZsHAE6igE1bimtWUK7v0bowPIxGRKRPpF8JLbImpsJuTCC4HGOCMy7otg3FIQ== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1232,7 +1232,7 @@ "@graphql-tools/git-loader@^8.0.0": version "8.0.30" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.30.tgz#5d2955f965bd2b057c819034f163c989e1db8bf5" + resolved "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.30.tgz" integrity sha512-Rx3rphMFBKrILkCrjB6k5JF2z+3XYEnc5wUOw2rMxFAHBHeZjBfA6TDLUtd8EKnaf6h5y0pKpb+uZ72EYJaH2A== dependencies: "@graphql-tools/graphql-tag-pluck" "8.3.25" @@ -1244,7 +1244,7 @@ "@graphql-tools/github-loader@^9.0.0": version "9.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.4.tgz#c706df905398480258fd5836a34c4d6a0621cbc4" + resolved "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-9.0.4.tgz" integrity sha512-w8RRhOTS1Qn2/MUWO2ogtLok6/0p15IdAoYfkCW7duCTPWWG/blCtiYNDyrOZahjCuL4WrxL9+JZfj+6nXSpIQ== dependencies: "@graphql-tools/executor-http" "^3.0.6" @@ -1257,7 +1257,7 @@ "@graphql-tools/graphql-file-loader@^8.0.0": version "8.1.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.6.tgz#87aca1f87616a637436d995e1c8800f6e92ee6f9" + resolved "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.6.tgz" integrity sha512-BwK9kUrRbv9w8WMFUedSkPogGdQivopRSRs1aqT9FUD1J3iVLltEHMUx9KTnpPHO7OcULSflMqQs5ZI/3cW+7Q== dependencies: "@graphql-tools/import" "7.1.6" @@ -1268,7 +1268,7 @@ "@graphql-tools/graphql-tag-pluck@8.3.25", "@graphql-tools/graphql-tag-pluck@^8.3.25": version "8.3.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.25.tgz#5735cf30f6673b520ab43779ce42ed621c1fc4de" + resolved "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.25.tgz" integrity sha512-b8oTBe0mDQDh3zPcKCkaTPmjLv1TJslBUKXPNLfu5CWS2+gL8Z/z0UuAhCe5gTveuKDJYjkEO7xcct9JfcDi4g== dependencies: "@babel/core" "^7.26.10" @@ -1281,7 +1281,7 @@ "@graphql-tools/import@7.1.6": version "7.1.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.6.tgz#8c53941c13cb11f2364617a46b756fcaefd6dae0" + resolved "https://registry.npmjs.org/@graphql-tools/import/-/import-7.1.6.tgz" integrity sha512-aROsZq/ApWiAWE9k/xfYiKFUQiw81958e3DvL+HqGtbFQq+F11hIi0PgwOEiEereJZHIuVYZmIfbdQd4gFq4+w== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1291,7 +1291,7 @@ "@graphql-tools/json-file-loader@^8.0.0": version "8.0.24" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.24.tgz#feda38f8beaf5758f3153a42f53d4a54796c7be8" + resolved "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.24.tgz" integrity sha512-S2XKpUVAzY84hniaMpv6V0of2joPfLPaeY4XXUQZfi4w0uiPSzHWA/EnXohgojJsQLgN+FP4dXFfp2yA5GG6VA== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1301,7 +1301,7 @@ "@graphql-tools/load@^8.1.0": version "8.1.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.6.tgz#e07b06cda5459f58722e09f5308fba6b331b3ea1" + resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-8.1.6.tgz" integrity sha512-/bUYqGdB2Y6BflW42IjmauBDzxjec3LQmVAuImVGeiOqw1Rca/DDP7KRQe3vEv8yf/xMVj/PkIl+YRjoo12YxA== dependencies: "@graphql-tools/schema" "^10.0.29" @@ -1311,7 +1311,7 @@ "@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.1.5": version "9.1.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.5.tgz#db61fc3ee8f7cff4636fddded796037acad5e0c6" + resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.5.tgz" integrity sha512-eVcir6nCcOC/Wzv7ZAng3xec3dj6FehE8+h9TvgvUyrDEKVMdFfrO6etRFZ2hucWVcY8S6drx7zQx04N4lPM8Q== dependencies: "@graphql-tools/utils" "^10.10.3" @@ -1319,21 +1319,21 @@ "@graphql-tools/optimize@^1.3.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.4.0.tgz#20d6a9efa185ef8fc4af4fd409963e0907c6e112" + resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.4.0.tgz" integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== dependencies: tslib "^2.4.0" "@graphql-tools/optimize@^2.0.0": version "2.0.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-2.0.0.tgz#7a9779d180824511248a50c5a241eff6e7a2d906" + resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz" integrity sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg== dependencies: tslib "^2.4.0" "@graphql-tools/relay-operation-optimizer@^6.5.0": version "6.5.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz#a1b74a8e0a5d0c795b8a4d19629b654cf66aa5ab" + resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz" integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== dependencies: "@ardatan/relay-compiler" "12.0.0" @@ -1342,7 +1342,7 @@ "@graphql-tools/relay-operation-optimizer@^7.0.0": version "7.0.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.25.tgz#7667f435ac17a0c08e44cad7543dba1972f7fe7f" + resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.25.tgz" integrity sha512-1S7qq9eyO6ygPNWX2lZd+oxbpl63OhnTTw8+t5OWprM2Tzws9HEosLUpsMR85z1gbezeKtUDt9a2bsSyu4MMFg== dependencies: "@ardatan/relay-compiler" "^12.0.3" @@ -1351,7 +1351,7 @@ "@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.25", "@graphql-tools/schema@^10.0.27", "@graphql-tools/schema@^10.0.29": version "10.0.29" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.29.tgz#cbb0d0cdeb2c55559ace51e2ffadee39990d01c0" + resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.29.tgz" integrity sha512-+Htiupnq6U/AWOEAJerIOGT1pAf4u43Q3n2JmFpqFfYJchz6sKWZ7L9Lpe/NusaaUQty/IOF+eQlNFypEaWxhg== dependencies: "@graphql-tools/merge" "^9.1.5" @@ -1360,7 +1360,7 @@ "@graphql-tools/url-loader@^8.0.0": version "8.0.33" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz#1e5ebfa6a840e3bdc25336c231df2889028f7e0c" + resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz" integrity sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw== dependencies: "@graphql-tools/executor-graphql-ws" "^2.0.1" @@ -1378,7 +1378,7 @@ "@graphql-tools/url-loader@^9.0.0": version "9.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.4.tgz#0fd3893cb10616fb8db9a6e6da7482dca8244e5d" + resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-9.0.4.tgz" integrity sha512-UNLU9g3IcZjldb08hsBLp6G8YPzZwC38bZ8PFFs/QyXXPFXzoXECT8rHLzvt32w3YFGSbTpj8FiS/KMdTQHZCg== dependencies: "@graphql-tools/executor-graphql-ws" "^3.1.2" @@ -1396,7 +1396,7 @@ "@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.10.1", "@graphql-tools/utils@^10.10.3", "@graphql-tools/utils@^10.8.1", "@graphql-tools/utils@^10.9.1": version "10.10.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.10.3.tgz#1c6ad8d824ad3fd05571cd2bfdce863c497136d4" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.10.3.tgz" integrity sha512-2EdYiefeLLxsoeZTukSNZJ0E/Z5NnWBUGK2VJa0DQj1scDhVd93HeT1eW9TszJOYmIh3eWAKLv58ri/1XUmdsQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -1406,7 +1406,7 @@ "@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.2.1": version "9.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" + resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz" integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -1414,7 +1414,7 @@ "@graphql-tools/wrap@^10.0.16": version "10.1.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-10.1.4.tgz#46f94b00113693d4d4eccddcf262494164234ba6" + resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.1.4.tgz" integrity sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg== dependencies: "@graphql-tools/delegate" "^10.2.23" @@ -1425,7 +1425,7 @@ "@graphql-tools/wrap@^11.0.0": version "11.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.0.4.tgz#87390a99647d6019d3835e652902e2b2e275d0ac" + resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-11.0.4.tgz" integrity sha512-bX1X2QC1oFiH+1nTLhWVx4fp2xoXbDvL+eEiozuWCv7PeZoBs7EgklKZFtpWbcSdNA3L3EuA8JM6mf2jOG6/Sg== dependencies: "@graphql-tools/delegate" "^11.1.2" @@ -1436,17 +1436,17 @@ "@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== "@humanfs/core@^0.19.1": version "0.19.1" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": version "0.16.7" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" + resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz" integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: "@humanfs/core" "^0.19.1" @@ -1454,17 +1454,17 @@ "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": version "0.4.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" + resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@img/colour@^1.0.0": version "1.0.0" - resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" + resolved "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz" integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== "@img/sharp-darwin-arm64@0.34.5": @@ -1518,7 +1518,7 @@ "@img/sharp-libvips-linux-x64@1.2.4": version "1.2.4" - resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce" + resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz" integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw== "@img/sharp-libvips-linuxmusl-arm64@1.2.4": @@ -1568,7 +1568,7 @@ "@img/sharp-linux-x64@0.34.5": version "0.34.5" - resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8" + resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz" integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ== optionalDependencies: "@img/sharp-libvips-linux-x64" "1.2.4" @@ -1611,12 +1611,12 @@ "@inquirer/ansi@^1.0.2": version "1.0.2" - resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.2.tgz#674a4c4d81ad460695cb2a1fc69d78cd187f337e" + resolved "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz" integrity sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ== "@inquirer/checkbox@^4.3.2": version "4.3.2" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz#e1483e6519d6ffef97281a54d2a5baa0d81b3f3b" + resolved "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz" integrity sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1627,7 +1627,7 @@ "@inquirer/confirm@^5.1.21": version "5.1.21" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.21.tgz#610c4acd7797d94890a6e2dde2c98eb1e891dd12" + resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz" integrity sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ== dependencies: "@inquirer/core" "^10.3.2" @@ -1635,7 +1635,7 @@ "@inquirer/core@^10.3.2": version "10.3.2" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.2.tgz#535979ff3ff4fe1e7cc4f83e2320504c743b7e20" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz" integrity sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1649,7 +1649,7 @@ "@inquirer/editor@^4.2.23": version "4.2.23" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-4.2.23.tgz#fe046a3bfdae931262de98c1052437d794322e0b" + resolved "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz" integrity sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ== dependencies: "@inquirer/core" "^10.3.2" @@ -1658,7 +1658,7 @@ "@inquirer/expand@^4.0.23": version "4.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-4.0.23.tgz#a38b5f32226d75717c370bdfed792313b92bdc05" + resolved "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz" integrity sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew== dependencies: "@inquirer/core" "^10.3.2" @@ -1667,7 +1667,7 @@ "@inquirer/external-editor@^1.0.3": version "1.0.3" - resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz#c23988291ee676290fdab3fd306e64010a6d13b8" + resolved "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz" integrity sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA== dependencies: chardet "^2.1.1" @@ -1675,12 +1675,12 @@ "@inquirer/figures@^1.0.15": version "1.0.15" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a" + resolved "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz" integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g== "@inquirer/input@^4.3.1": version "4.3.1" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-4.3.1.tgz#778683b4c4c4d95d05d4b05c4a854964b73565b4" + resolved "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz" integrity sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g== dependencies: "@inquirer/core" "^10.3.2" @@ -1688,7 +1688,7 @@ "@inquirer/number@^3.0.23": version "3.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-3.0.23.tgz#3fdec2540d642093fd7526818fd8d4bdc7335094" + resolved "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz" integrity sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg== dependencies: "@inquirer/core" "^10.3.2" @@ -1696,7 +1696,7 @@ "@inquirer/password@^4.0.23": version "4.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-4.0.23.tgz#b9f5187c8c92fd7aa9eceb9d8f2ead0d7e7b000d" + resolved "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz" integrity sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1705,7 +1705,7 @@ "@inquirer/prompts@^7.8.2": version "7.10.1" - resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-7.10.1.tgz#e1436c0484cf04c22548c74e2cd239e989d5f847" + resolved "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz" integrity sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg== dependencies: "@inquirer/checkbox" "^4.3.2" @@ -1721,7 +1721,7 @@ "@inquirer/rawlist@^4.1.11": version "4.1.11" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz#313c8c3ffccb7d41e990c606465726b4a898a033" + resolved "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz" integrity sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw== dependencies: "@inquirer/core" "^10.3.2" @@ -1730,7 +1730,7 @@ "@inquirer/search@^3.2.2": version "3.2.2" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-3.2.2.tgz#4cc6fd574dcd434e4399badc37c742c3fd534ac8" + resolved "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz" integrity sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA== dependencies: "@inquirer/core" "^10.3.2" @@ -1740,7 +1740,7 @@ "@inquirer/select@^4.4.2": version "4.4.2" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-4.4.2.tgz#2ac8fca960913f18f1d1b35323ed8fcd27d89323" + resolved "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz" integrity sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1751,12 +1751,12 @@ "@inquirer/type@^3.0.10": version "3.0.10" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.10.tgz#11ed564ec78432a200ea2601a212d24af8150d50" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz" integrity sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA== "@isaacs/cliui@^8.0.2": version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" @@ -1768,7 +1768,7 @@ "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -1779,12 +1779,12 @@ "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.2.0.tgz#c52fcd5b58fdd2e8eb66b2fd8ae56f2f64d05b28" + resolved "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz" integrity sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ== dependencies: "@jest/types" "30.2.0" @@ -1796,7 +1796,7 @@ "@jest/core@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.2.0.tgz#813d59faa5abd5510964a8b3a7b17cc77b775275" + resolved "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz" integrity sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ== dependencies: "@jest/console" "30.2.0" @@ -1830,12 +1830,12 @@ "@jest/diff-sequences@30.0.1": version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" + resolved "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz" integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== "@jest/environment@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.2.0.tgz#1e673cdb8b93ded707cf6631b8353011460831fa" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz" integrity sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g== dependencies: "@jest/fake-timers" "30.2.0" @@ -1845,14 +1845,14 @@ "@jest/expect-utils@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz" integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== dependencies: "@jest/get-type" "30.1.0" "@jest/expect@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.2.0.tgz#9a5968499bb8add2bbb09136f69f7df5ddbf3185" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz" integrity sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA== dependencies: expect "30.2.0" @@ -1860,7 +1860,7 @@ "@jest/fake-timers@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz#0941ddc28a339b9819542495b5408622dc9e94ec" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz" integrity sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw== dependencies: "@jest/types" "30.2.0" @@ -1872,12 +1872,12 @@ "@jest/get-type@30.1.0": version "30.1.0" - resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" + resolved "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz" integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== "@jest/globals@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.2.0.tgz#2f4b696d5862664b89c4ee2e49ae24d2bb7e0988" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz" integrity sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw== dependencies: "@jest/environment" "30.2.0" @@ -1887,7 +1887,7 @@ "@jest/pattern@30.0.1": version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" + resolved "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz" integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== dependencies: "@types/node" "*" @@ -1895,7 +1895,7 @@ "@jest/reporters@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.2.0.tgz#a36b28fcbaf0c4595250b108e6f20e363348fd91" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz" integrity sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -1924,14 +1924,14 @@ "@jest/schemas@30.0.5": version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz" integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== dependencies: "@sinclair/typebox" "^0.34.0" "@jest/snapshot-utils@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz#387858eb90c2f98f67bff327435a532ac5309fbe" + resolved "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz" integrity sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug== dependencies: "@jest/types" "30.2.0" @@ -1941,7 +1941,7 @@ "@jest/source-map@30.0.1": version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-30.0.1.tgz#305ebec50468f13e658b3d5c26f85107a5620aaa" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz" integrity sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== dependencies: "@jridgewell/trace-mapping" "^0.3.25" @@ -1950,7 +1950,7 @@ "@jest/test-result@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.2.0.tgz#9c0124377fb7996cdffb86eda3dbc56eacab363d" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz" integrity sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg== dependencies: "@jest/console" "30.2.0" @@ -1960,7 +1960,7 @@ "@jest/test-sequencer@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz#bf0066bc72e176d58f5dfa7f212b6e7eee44f221" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz" integrity sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q== dependencies: "@jest/test-result" "30.2.0" @@ -1970,7 +1970,7 @@ "@jest/transform@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz" integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== dependencies: "@babel/core" "^7.27.4" @@ -1991,7 +1991,7 @@ "@jest/types@30.2.0": version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" + resolved "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz" integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== dependencies: "@jest/pattern" "30.0.1" @@ -2004,7 +2004,7 @@ "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -2012,7 +2012,7 @@ "@jridgewell/remapping@^2.3.5": version "2.3.5" - resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -2020,17 +2020,17 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.5" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" @@ -2038,7 +2038,7 @@ "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -2055,12 +2055,12 @@ "@next/env@16.0.3": version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.0.3.tgz#bda60d79929d4b8f554928854eadd7467e77ca4d" + resolved "https://registry.npmjs.org/@next/env/-/env-16.0.3.tgz" integrity sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ== "@next/eslint-plugin-next@16.0.3": version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.3.tgz#003142fb52928cf2e81ec1b159fab993fe58825c" + resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.3.tgz" integrity sha512-6sPWmZetzFWMsz7Dhuxsdmbu3fK+/AxKRtj7OB0/3OZAI2MHB/v2FeYh271LZ9abvnM1WIwWc/5umYjx0jo5sQ== dependencies: fast-glob "3.3.1" @@ -2087,7 +2087,7 @@ "@next/swc-linux-x64-gnu@16.0.3": version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz#3cea2178b1c5d859701628b595622c117807f201" + resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz" integrity sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ== "@next/swc-linux-x64-musl@16.0.3": @@ -2107,7 +2107,7 @@ "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -2115,12 +2115,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -2128,7 +2128,7 @@ "@nolyfill/is-core-module@1.0.39": version "1.0.39" - resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" + resolved "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== "@parcel/watcher-android-arm64@2.5.1": @@ -2173,7 +2173,7 @@ "@parcel/watcher-linux-x64-glibc@2.5.1": version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz" integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== "@parcel/watcher-linux-x64-musl@2.5.1": @@ -2198,7 +2198,7 @@ "@parcel/watcher@^2.5.1": version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" + resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz" integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== dependencies: detect-libc "^1.0.3" @@ -2222,24 +2222,24 @@ "@pkgjs/parseargs@^0.11.0": version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@pkgr/core@^0.2.9": version "0.2.9" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" + resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@rc-component/async-validator@^5.0.3": version "5.0.4" - resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" + resolved "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz" integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== dependencies: "@babel/runtime" "^7.24.4" "@rc-component/color-picker@~2.0.1": version "2.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" + resolved "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz" integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== dependencies: "@ant-design/fast-color" "^2.0.6" @@ -2249,7 +2249,7 @@ "@rc-component/context@^1.4.0": version "1.4.0" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" + resolved "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz" integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== dependencies: "@babel/runtime" "^7.10.1" @@ -2257,14 +2257,14 @@ "@rc-component/mini-decimal@^1.0.1": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" + resolved "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz" integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/mutate-observer@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" + resolved "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz" integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== dependencies: "@babel/runtime" "^7.18.0" @@ -2273,7 +2273,7 @@ "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" + resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz" integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== dependencies: "@babel/runtime" "^7.18.0" @@ -2282,7 +2282,7 @@ "@rc-component/qrcode@~1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.1.0.tgz#4e38f1d7c2c8aae7f62d60ab110a842c1395db3e" + resolved "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.0.tgz" integrity sha512-ABA80Yer0c6I2+moqNY0kF3Y1NxIT6wDP/EINIqbiRbfZKP1HtHpKMh8WuTXLgVGYsoWG2g9/n0PgM8KdnJb4Q== dependencies: "@babel/runtime" "^7.24.7" @@ -2290,7 +2290,7 @@ "@rc-component/tour@~1.15.1": version "1.15.1" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" + resolved "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz" integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== dependencies: "@babel/runtime" "^7.18.0" @@ -2301,7 +2301,7 @@ "@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.3.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.3.0.tgz#9499ada078daca9dd99d01f0f0743ee1ab9e398b" + resolved "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.0.tgz" integrity sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg== dependencies: "@babel/runtime" "^7.23.2" @@ -2313,43 +2313,43 @@ "@repeaterjs/repeater@^3.0.4", "@repeaterjs/repeater@^3.0.6": version "3.0.6" - resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" + resolved "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz" integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== "@rtsao/scc@^1.1.0": version "1.1.0" - resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" + resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== "@sinclair/typebox@^0.34.0": version "0.34.41" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz" integrity sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g== "@sinonjs/commons@^3.0.1": version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^13.0.0": version "13.0.5" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz#36b9dbc21ad5546486ea9173d6bea063eb1717d5" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz" integrity sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw== dependencies: "@sinonjs/commons" "^3.0.1" "@swc/helpers@0.5.15": version "0.5.15" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz" integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== dependencies: tslib "^2.8.0" "@theguild/federation-composition@^0.20.2": version "0.20.2" - resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.20.2.tgz#d6b449c6cf4d3f09426fcdd9bb20f2cb83742a6b" + resolved "https://registry.npmjs.org/@theguild/federation-composition/-/federation-composition-0.20.2.tgz" integrity sha512-QI4iSdrc4JvCWnMb1QbiHnEpdD33KGdiU66qfWOcM8ENebRGHkGjXDnUrVJ8F9g1dmCRMTNfn2NFGqTcDpeYXw== dependencies: constant-case "^3.0.4" @@ -2359,22 +2359,22 @@ "@tsconfig/node10@^1.0.7": version "1.0.12" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz" integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@tybys/wasm-util@^0.10.0": @@ -2386,7 +2386,7 @@ "@types/babel__core@^7.20.5": version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" + resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -2397,14 +2397,14 @@ "@types/babel__generator@*": version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz" integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" @@ -2412,112 +2412,112 @@ "@types/babel__traverse@*": version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" + resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz" integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== dependencies: "@babel/types" "^7.28.2" "@types/estree@^1.0.6": version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/hoist-non-react-statics@^3.3.6": version "3.3.7" - resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz" integrity sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g== dependencies: hoist-non-react-statics "^3.3.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.4": version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/json-schema@^7.0.15": version "7.0.15" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*", "@types/node@^24.10.1": version "24.10.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.1.tgz#91e92182c93db8bd6224fca031e2370cef9a8f01" + resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== dependencies: undici-types "~7.16.0" "@types/react-dom@19.2.3": version "19.2.3" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" + resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== "@types/react-highlight-words@^0.20.0": version "0.20.0" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c7e3e8302acb5082980e611ce2d93c6e6d3b4022" + resolved "https://registry.npmjs.org/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz" integrity sha512-Qm512TiOakvtNzHJ2+TNVHnLn5cJ2wLQV0+LrhuispVth6dRf5b8ydjq3Kc0thpZ7bz4s6RnG6meboAXHWRK+Q== dependencies: "@types/react" "*" "@types/react-test-renderer@^19.1.0": version "19.1.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" + resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz" integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== dependencies: "@types/react" "*" "@types/react@*", "@types/react@19.2.5": version "19.2.5" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.5.tgz#bb75da5c52a956ba7d2623dffbba0fdadc3e4145" + resolved "https://registry.npmjs.org/@types/react/-/react-19.2.5.tgz" integrity sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw== dependencies: csstype "^3.0.2" "@types/stack-utils@^2.0.3": version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/ws@^8.0.0": version "8.18.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.33": version "17.0.35" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz" integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz#c53edeec13a79483f4ca79c298d5231b02e9dc17" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz" integrity sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA== dependencies: "@eslint-community/regexpp" "^4.10.0" @@ -2532,7 +2532,7 @@ "@typescript-eslint/parser@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.47.0.tgz#51b14ab2be2057ec0f57073b9ff3a9c078b0a964" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz" integrity sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ== dependencies: "@typescript-eslint/scope-manager" "8.47.0" @@ -2543,7 +2543,7 @@ "@typescript-eslint/project-service@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.47.0.tgz#b8afc65e0527568018af911b702dcfbfdca16471" + resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz" integrity sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA== dependencies: "@typescript-eslint/tsconfig-utils" "^8.47.0" @@ -2552,7 +2552,7 @@ "@typescript-eslint/scope-manager@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz#d1c36a973a5499fed3a99e2e6a66aec5c9b1e542" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz" integrity sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg== dependencies: "@typescript-eslint/types" "8.47.0" @@ -2560,12 +2560,12 @@ "@typescript-eslint/tsconfig-utils@8.47.0", "@typescript-eslint/tsconfig-utils@^8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz#4f178b62813538759e0989dd081c5474fad39b84" + resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz" integrity sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g== "@typescript-eslint/type-utils@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz#b9b0141d99bd5bece3811d7eee68a002597ffa55" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz" integrity sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A== dependencies: "@typescript-eslint/types" "8.47.0" @@ -2576,12 +2576,12 @@ "@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.47.0.tgz#c7fc9b6642d03505f447a8392934b9d1850de5af" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz" integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== "@typescript-eslint/typescript-estree@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz#86416dad58db76c4b3bd6a899b1381f9c388489a" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz" integrity sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg== dependencies: "@typescript-eslint/project-service" "8.47.0" @@ -2597,7 +2597,7 @@ "@typescript-eslint/utils@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.47.0.tgz#d6c30690431dbfdab98fc027202af12e77c91419" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz" integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== dependencies: "@eslint-community/eslint-utils" "^4.7.0" @@ -2607,7 +2607,7 @@ "@typescript-eslint/visitor-keys@8.47.0": version "8.47.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz#35f36ed60a170dfc9d4d738e78387e217f24c29f" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz" integrity sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ== dependencies: "@typescript-eslint/types" "8.47.0" @@ -2615,17 +2615,17 @@ "@umijs/route-utils@^4.0.0": version "4.0.3" - resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-4.0.3.tgz#70779ee069ac048509786bcf316368d3516ca5fd" + resolved "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-4.0.3.tgz" integrity sha512-zPEcYhl1cSfkSRDzzGgoD1mDvGjxoOTJFvkn55srfgdQ3NZe2ZMCScCU6DEnOxuKP1XDVf8pqyqCDVd2+RCQIw== "@umijs/use-params@^1.0.9": version "1.0.9" - resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" + resolved "https://registry.npmjs.org/@umijs/use-params/-/use-params-1.0.9.tgz" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== "@ungap/structured-clone@^1.3.0": version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" + resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== "@unrs/resolver-binding-android-arm-eabi@1.11.1": @@ -2695,7 +2695,7 @@ "@unrs/resolver-binding-linux-x64-gnu@1.11.1": version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" + resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz" integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== "@unrs/resolver-binding-linux-x64-musl@1.11.1": @@ -2727,7 +2727,7 @@ "@whatwg-node/disposablestack@^0.0.6": version "0.0.6" - resolved "https://registry.yarnpkg.com/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz#2064a1425ea66194def6df0c7a1851b6939c82bb" + resolved "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz" integrity sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw== dependencies: "@whatwg-node/promise-helpers" "^1.0.0" @@ -2735,7 +2735,7 @@ "@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.12", "@whatwg-node/fetch@^0.10.13", "@whatwg-node/fetch@^0.10.4": version "0.10.13" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.10.13.tgz#2d47190c620f134dda31c7f827976a909b2cca7a" + resolved "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.13.tgz" integrity sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q== dependencies: "@whatwg-node/node-fetch" "^0.8.3" @@ -2743,7 +2743,7 @@ "@whatwg-node/node-fetch@^0.8.3": version "0.8.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz#72df6b3ea9d295bcedbc8f05cf2cfeabe654669f" + resolved "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz" integrity sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA== dependencies: "@fastify/busboy" "^3.1.1" @@ -2753,64 +2753,64 @@ "@whatwg-node/promise-helpers@^1.0.0", "@whatwg-node/promise-helpers@^1.2.1", "@whatwg-node/promise-helpers@^1.2.4", "@whatwg-node/promise-helpers@^1.3.0", "@whatwg-node/promise-helpers@^1.3.2": version "1.3.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" + resolved "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz" integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== dependencies: tslib "^2.6.3" "@wry/caches@^1.0.0": version "1.0.1" - resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" + resolved "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz" integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== dependencies: tslib "^2.3.0" "@wry/context@^0.7.0": version "0.7.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz" integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== dependencies: tslib "^2.3.0" "@wry/equality@^0.5.6": version "0.5.7" - resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz" integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== dependencies: tslib "^2.3.0" "@wry/trie@^0.5.0": version "0.5.0" - resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" + resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz" integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== dependencies: tslib "^2.3.0" abbrev@1: version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== ajv@^6.12.4: version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2820,55 +2820,55 @@ ajv@^6.12.4: ansi-escapes@^4.3.2: version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-escapes@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.2.0.tgz#31b25afa3edd3efc09d98c2fee831d460ff06b49" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz" integrity sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw== dependencies: environment "^1.0.0" ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.2.2" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.3" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== antd@^5.0: version "5.29.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.29.1.tgz#e124b1aaa709a534816c42d558da02a917b995cc" + resolved "https://registry.npmjs.org/antd/-/antd-5.29.1.tgz" integrity sha512-TTFVbpKbyL6cPfEoKq6Ya3BIjTUr7uDW9+7Z+1oysRv1gpcN7kQ4luH8r/+rXXwz4n6BIz1iBJ1ezKCdsdNW0w== dependencies: "@ant-design/colors" "^7.2.1" @@ -2923,7 +2923,7 @@ antd@^5.0: anymatch@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -2931,29 +2931,29 @@ anymatch@^3.1.3: arg@^4.1.0: version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.3.2: version "5.3.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz" integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== dependencies: call-bound "^1.0.3" @@ -2961,12 +2961,12 @@ array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: array-find-index@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: version "3.1.9" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz" integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== dependencies: call-bind "^1.0.8" @@ -2980,12 +2980,12 @@ array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: array-union@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.findlast@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" + resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== dependencies: call-bind "^1.0.7" @@ -2997,7 +2997,7 @@ array.prototype.findlast@^1.2.5: array.prototype.findlastindex@^1.2.6: version "1.2.6" - resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz" integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== dependencies: call-bind "^1.0.8" @@ -3010,7 +3010,7 @@ array.prototype.findlastindex@^1.2.6: array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz" integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== dependencies: call-bind "^1.0.8" @@ -3020,7 +3020,7 @@ array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: version "1.3.3" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz" integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: call-bind "^1.0.8" @@ -3030,7 +3030,7 @@ array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: array.prototype.tosorted@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz" integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: call-bind "^1.0.7" @@ -3041,7 +3041,7 @@ array.prototype.tosorted@^1.1.4: arraybuffer.prototype.slice@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz" integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== dependencies: array-buffer-byte-length "^1.0.1" @@ -3054,44 +3054,44 @@ arraybuffer.prototype.slice@^1.0.4: asap@^2.0.0, asap@~2.0.3: version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== ast-types-flow@^0.0.8: version "0.0.8" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== async-function@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" + resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== auto-bind@~4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" + resolved "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== available-typed-arrays@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: version "4.11.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6" + resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz" integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ== axobject-query@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" + resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== babel-jest@30.2.0, babel-jest@^30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz" integrity sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw== dependencies: "@jest/transform" "30.2.0" @@ -3104,7 +3104,7 @@ babel-jest@30.2.0, babel-jest@^30.2.0: babel-plugin-istanbul@^7.0.1: version "7.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz" integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -3115,19 +3115,19 @@ babel-plugin-istanbul@^7.0.1: babel-plugin-jest-hoist@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz#94c250d36b43f95900f3a219241e0f4648191ce2" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz" integrity sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA== dependencies: "@types/babel__core" "^7.20.5" babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: version "7.0.0-beta.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz#aa213c1435e2bffeb6fca842287ef534ad05d5cf" + resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz" integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== babel-preset-current-node-syntax@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz" integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -3148,7 +3148,7 @@ babel-preset-current-node-syntax@^1.2.0: babel-preset-fbjs@^3.4.0: version "3.4.0" - resolved "https://registry.yarnpkg.com/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz#38a14e5a7a3b285a3f3a86552d650dca5cf6111c" + resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz" integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== dependencies: "@babel/plugin-proposal-class-properties" "^7.0.0" @@ -3181,7 +3181,7 @@ babel-preset-fbjs@^3.4.0: babel-preset-jest@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz#04717843e561347781d6d7f69c81e6bcc3ed11ce" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz" integrity sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ== dependencies: babel-plugin-jest-hoist "30.2.0" @@ -3189,22 +3189,22 @@ babel-preset-jest@30.2.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.8.25: - version "2.8.29" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.29.tgz#d8800b71399c783cb1bf2068c2bcc3b6cfd7892c" - integrity sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA== +baseline-browser-mapping@^2.8.25, baseline-browser-mapping@^2.9.3: + version "2.9.3" + resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.3.tgz" + integrity sha512-8QdH6czo+G7uBsNo0GiUfouPN1lRzKdJTGnKXwe12gkFbnnOUaUKGN55dMkfy+mnxmvjwl9zcI4VncczcVXDhA== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== brace-expansion@^1.1.7: version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz" integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" @@ -3212,21 +3212,21 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" braces@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.24.0: version "4.28.0" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.0.tgz#9cefece0a386a17a3cd3d22ebf67b9deca1b5929" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz" integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== dependencies: baseline-browser-mapping "^2.8.25" @@ -3237,19 +3237,19 @@ browserslist@^4.24.0: bser@2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: es-errors "^1.3.0" @@ -3257,7 +3257,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply- call-bind@^1.0.7, call-bind@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: call-bind-apply-helpers "^1.0.0" @@ -3267,7 +3267,7 @@ call-bind@^1.0.7, call-bind@^1.0.8: call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: call-bind-apply-helpers "^1.0.2" @@ -3275,12 +3275,12 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camel-case@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: pascal-case "^3.1.2" @@ -3288,22 +3288,22 @@ camel-case@^4.1.2: camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.3.0: version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001754: version "1.0.30001756" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz#fe80104631102f88e58cad8aa203a2c3e5ec9ebd" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz" integrity sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A== capital-case@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + resolved "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz" integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== dependencies: no-case "^3.0.4" @@ -3312,7 +3312,7 @@ capital-case@^1.0.4: chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -3320,7 +3320,7 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: chalk@^2.4.1: version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3329,7 +3329,7 @@ chalk@^2.4.1: change-case-all@1.0.15: version "1.0.15" - resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.15.tgz#de29393167fc101d646cd76b0ef23e27d09756ad" + resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz" integrity sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ== dependencies: change-case "^4.1.2" @@ -3345,7 +3345,7 @@ change-case-all@1.0.15: change-case@^4.1.2: version "4.1.2" - resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + resolved "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz" integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== dependencies: camel-case "^4.1.2" @@ -3363,39 +3363,39 @@ change-case@^4.1.2: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-2.1.1.tgz#5c75593704a642f71ee53717df234031e65373c8" + resolved "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz" integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== ci-info@^4.2.0: version "4.3.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz" integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== cjs-module-lexer@^2.1.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz#bff23b0609cc9afa428bd35f1918f7d03b448562" + resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz" integrity sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ== classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: version "2.5.1" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" + resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-truncate@^5.0.0: version "5.1.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.1.1.tgz#455476face9904d94b7d11e98d9adbca15292ea5" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz" integrity sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A== dependencies: slice-ansi "^7.1.0" @@ -3403,17 +3403,17 @@ cli-truncate@^5.0.0: cli-width@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== client-only@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" + resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== cliui@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== dependencies: string-width "^4.2.0" @@ -3422,7 +3422,7 @@ cliui@^6.0.0: cliui@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -3431,61 +3431,61 @@ cliui@^8.0.1: co@^4.6.0: version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz" integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.20: version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== common-tags@1.8.2: version "1.8.2" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== compute-scroll-into-view@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz#02c3386ec531fb6a9881967388e53e8564f3e9aa" + resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz" integrity sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concurrently@^9.2.1: version "9.2.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.1.tgz#248ea21b95754947be2dad9c3e4b60f18ca4e44f" + resolved "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz" integrity sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng== dependencies: chalk "4.1.2" @@ -3497,7 +3497,7 @@ concurrently@^9.2.1: constant-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz" integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== dependencies: no-case "^3.0.4" @@ -3506,24 +3506,24 @@ constant-case@^3.0.4: convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.0.2.tgz#27360701532116bd3f1f9416929d176afe1e4610" + resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz" integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== copy-to-clipboard@^3.3.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: toggle-selection "^1.0.6" cosmiconfig@^8.1.0: version "8.3.6" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== dependencies: import-fresh "^3.3.0" @@ -3533,7 +3533,7 @@ cosmiconfig@^8.1.0: cosmiconfig@^9.0.0: version "9.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz" integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: env-paths "^2.2.1" @@ -3543,26 +3543,26 @@ cosmiconfig@^9.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.5: version "3.2.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz" integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== dependencies: node-fetch "^2.7.0" cross-inspect@1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" + resolved "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz" integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A== dependencies: tslib "^2.4.0" cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -3571,27 +3571,27 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: crypto-hash@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-4.0.0.tgz#c58a8737560980714fa086113de3b8f577b72178" + resolved "https://registry.npmjs.org/crypto-hash/-/crypto-hash-4.0.0.tgz" integrity sha512-KkkFriWj2teXIJOmftIRavBL4q8A7EZV24Nn9sWhIR4hdeS8u+q2A2p0tWZ1m4xVoP1zs3VJjVwwAUnztBb8uw== csstype@^3.0.2, csstype@^3.1.3: version "3.2.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== damerau-levenshtein@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== data-uri-to-buffer@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" + resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== data-view-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz" integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== dependencies: call-bound "^1.0.3" @@ -3600,7 +3600,7 @@ data-view-buffer@^1.0.2: data-view-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz" integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== dependencies: call-bound "^1.0.3" @@ -3609,7 +3609,7 @@ data-view-byte-length@^1.0.2: data-view-byte-offset@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz" integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== dependencies: call-bound "^1.0.2" @@ -3618,61 +3618,61 @@ data-view-byte-offset@^1.0.1: dataloader@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" + resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz" integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: version "1.11.19" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz" integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== debounce@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5" + resolved "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz" integrity sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw== debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0: version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" debug@^3.1.0, debug@^3.2.7: version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== decamelize@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== dedent@^1.6.0: version "1.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" + resolved "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.0, deepmerge@^4.3.1: version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -3681,7 +3681,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: define-properties@^1.1.3, define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" @@ -3690,42 +3690,42 @@ define-properties@^1.1.3, define-properties@^1.2.1: dependency-graph@^0.11.0: version "0.11.0" - resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" + resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== dependency-graph@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-1.0.0.tgz#bb5e85aec1310bc13b22dbd76e3196c4ee4c10d2" + resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz" integrity sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg== dequal@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-libc@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== detect-libc@^2.1.2: version "2.1.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== detect-newline@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== dezalgo@^1.0.0: version "1.0.4" - resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz" integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== dependencies: asap "^2.0.0" @@ -3733,26 +3733,26 @@ dezalgo@^1.0.0: diff@^4.0.1: version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" dot-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== dependencies: no-case "^3.0.4" @@ -3760,12 +3760,12 @@ dot-case@^3.0.4: dset@^3.1.2: version "3.1.4" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" + resolved "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz" integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -3774,59 +3774,59 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== electron-to-chromium@^1.5.249: version "1.5.256" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz#f07f78de0893ab060f60ac897195c029a3b50d1a" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz" integrity sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ== emittery@^0.13.1: version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^10.3.0: version "10.6.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz" integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== env-paths@^2.2.1: version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== environment@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" + resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== error-ex@^1.3.1: version "1.3.4" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz" integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz" integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== dependencies: array-buffer-byte-length "^1.0.2" @@ -3886,17 +3886,17 @@ es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23 es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" + resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz" integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== dependencies: call-bind "^1.0.8" @@ -3918,14 +3918,14 @@ es-iterator-helpers@^1.2.1: es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: es-errors "^1.3.0" @@ -3935,14 +3935,14 @@ es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz" integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== dependencies: hasown "^2.0.2" es-to-primitive@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz" integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: is-callable "^1.2.7" @@ -3951,27 +3951,27 @@ es-to-primitive@^1.3.0: escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@16.0.3: version "16.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.0.3.tgz#986cf220a9a879f4648f2ead3f4b45a4201c5a92" + resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.3.tgz" integrity sha512-5F6qDjcZldf0Y0ZbqvWvap9xzYUxyDf7/of37aeyhvkrQokj/4bT1JYWZdlWUr283aeVa+s52mPq9ogmGg+5dw== dependencies: "@next/eslint-plugin-next" "16.0.3" @@ -3986,7 +3986,7 @@ eslint-config-next@16.0.3: eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" @@ -3995,7 +3995,7 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: eslint-import-resolver-typescript@^3.5.2: version "3.10.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2" + resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz" integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ== dependencies: "@nolyfill/is-core-module" "1.0.39" @@ -4008,14 +4008,14 @@ eslint-import-resolver-typescript@^3.5.2: eslint-module-utils@^2.12.1: version "2.12.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz" integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== dependencies: debug "^3.2.7" eslint-plugin-import@^2.32.0: version "2.32.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz" integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== dependencies: "@rtsao/scc" "^1.1.0" @@ -4040,7 +4040,7 @@ eslint-plugin-import@^2.32.0: eslint-plugin-jsx-a11y@^6.10.0: version "6.10.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" + resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== dependencies: aria-query "^5.3.2" @@ -4061,7 +4061,7 @@ eslint-plugin-jsx-a11y@^6.10.0: eslint-plugin-react-hooks@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz" integrity sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA== dependencies: "@babel/core" "^7.24.4" @@ -4072,7 +4072,7 @@ eslint-plugin-react-hooks@^7.0.0: eslint-plugin-react@^7.37.0: version "7.37.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065" + resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz" integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA== dependencies: array-includes "^3.1.8" @@ -4096,7 +4096,7 @@ eslint-plugin-react@^7.37.0: eslint-scope@^8.4.0: version "8.4.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz" integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" @@ -4104,17 +4104,17 @@ eslint-scope@^8.4.0: eslint-visitor-keys@^3.4.3: version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint-visitor-keys@^4.2.1: version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.5.0: version "9.39.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" + resolved "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz" integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== dependencies: "@eslint-community/eslint-utils" "^4.8.0" @@ -4154,7 +4154,7 @@ eslint@^9.5.0: espree@^10.0.1, espree@^10.4.0: version "10.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" + resolved "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz" integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== dependencies: acorn "^8.15.0" @@ -4163,41 +4163,41 @@ espree@^10.0.1, espree@^10.4.0: esprima@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.5.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== execa@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4212,12 +4212,12 @@ execa@^5.1.1: exit-x@^0.2.2: version "0.2.2" - resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" + resolved "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz" integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== expect@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" + resolved "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz" integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== dependencies: "@jest/expect-utils" "30.2.0" @@ -4229,12 +4229,12 @@ expect@30.2.0: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@3.3.1: version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4245,7 +4245,7 @@ fast-glob@3.3.1: fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.3" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4256,36 +4256,36 @@ fast-glob@^3.2.9, fast-glob@^3.3.2: fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fbjs-css-vars@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" + resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: version "3.0.5" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + resolved "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz" integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== dependencies: cross-fetch "^3.1.5" @@ -4298,12 +4298,12 @@ fbjs@^3.0.0: fdir@^6.5.0: version "6.5.0" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" + resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz" integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: node-domexception "^1.0.0" @@ -4311,21 +4311,21 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: file-entry-cache@^8.0.0: version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: flat-cache "^4.0.0" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -4333,7 +4333,7 @@ find-up@^4.0.0, find-up@^4.1.0: find-up@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -4341,7 +4341,7 @@ find-up@^5.0.0: flat-cache@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" @@ -4349,19 +4349,19 @@ flat-cache@^4.0.0: flatted@^3.2.9: version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== dependencies: is-callable "^1.2.7" foreground-child@^3.1.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: cross-spawn "^7.0.6" @@ -4369,14 +4369,14 @@ foreground-child@^3.1.0: formdata-polyfill@^4.0.10: version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" + resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: fetch-blob "^3.1.2" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.3: @@ -4386,12 +4386,12 @@ fsevents@^2.3.3: function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: version "1.1.8" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz" integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== dependencies: call-bind "^1.0.8" @@ -4403,32 +4403,32 @@ function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== generator-function@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" + resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.0, get-east-asian-width@^1.3.1: version "1.4.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" + resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz" integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: call-bind-apply-helpers "^1.0.2" @@ -4444,12 +4444,12 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@ get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-proto@^1.0.0, get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" @@ -4457,12 +4457,12 @@ get-proto@^1.0.0, get-proto@^1.0.1: get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz" integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== dependencies: call-bound "^1.0.3" @@ -4471,28 +4471,28 @@ get-symbol-description@^1.1.0: get-tsconfig@^4.10.0: version "4.13.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" + resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz" integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== dependencies: resolve-pkg-maps "^1.0.0" glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob@^10.3.10: version "10.5.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" @@ -4504,7 +4504,7 @@ glob@^10.3.10: glob@^7.1.1, glob@^7.1.4: version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -4516,17 +4516,17 @@ glob@^7.1.1, glob@^7.1.4: globals@16.4.0: version "16.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" + resolved "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz" integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== globals@^14.0.0: version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globalthis@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: define-properties "^1.2.1" @@ -4534,7 +4534,7 @@ globalthis@^1.0.4: globby@^11.0.3: version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -4546,22 +4546,22 @@ globby@^11.0.3: gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graceful-fs@^4.1.2, graceful-fs@^4.2.11: version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== graphemer@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== graphql-config@^5.1.1: version "5.1.5" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-5.1.5.tgz#34e0bfba88e74b6eefd889716a9378086f595f7f" + resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.5.tgz" integrity sha512-mG2LL1HccpU8qg5ajLROgdsBzx/o2M6kgI3uAmoaXiSH9PCUbtIyLomLqUtCFaAeG2YCFsl0M5cfQ9rKmDoMVA== dependencies: "@graphql-tools/graphql-file-loader" "^8.0.0" @@ -4578,72 +4578,72 @@ graphql-config@^5.1.1: graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== dependencies: tslib "^2.1.0" graphql-ws@^6.0.6: version "6.0.6" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.6.tgz#e9c9ff85f4ddb5bbe6faa2721c0d27e6c11746d2" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.6.tgz" integrity sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw== graphql@^16.12.0: version "16.12.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7" + resolved "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz" integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ== has-bigints@^1.0.2: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz" integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-proto@^1.2.0: version "1.2.0" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz" integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== dependencies: dunder-proto "^1.0.0" has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" hasown@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" header-case@^2.0.4: version "2.0.4" - resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + resolved "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz" integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: capital-case "^1.0.4" @@ -4651,68 +4651,68 @@ header-case@^2.0.4: hermes-estree@0.25.1: version "0.25.1" - resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" + resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz" integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== hermes-parser@^0.25.1: version "0.25.1" - resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" + resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz" integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== dependencies: hermes-estree "0.25.1" highlight-words-core@^1.2.0: version "1.2.3" - resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" + resolved "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz" integrity sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ== hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@^0.7.0: version "0.7.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz" integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ignore@^5.2.0: version "5.3.2" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== ignore@^7.0.0: version "7.0.5" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" + resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== immutable@~3.7.6: version "3.7.6" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + resolved "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz" integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" @@ -4720,12 +4720,12 @@ import-fresh@^3.2.1, import-fresh@^3.3.0: import-from@4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-4.0.0.tgz#2710b8d66817d232e16f4166e319248d3d5492e2" + resolved "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz" integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== import-local@^3.2.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -4733,12 +4733,12 @@ import-local@^3.2.0: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" @@ -4746,12 +4746,12 @@ inflight@^1.0.4: inherits@2: version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== internal-slot@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz" integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== dependencies: es-errors "^1.3.0" @@ -4760,14 +4760,14 @@ internal-slot@^1.1.0: invariant@^2.2.4: version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" is-absolute@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" + resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" @@ -4775,7 +4775,7 @@ is-absolute@^1.0.0: is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz" integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== dependencies: call-bind "^1.0.8" @@ -4784,12 +4784,12 @@ is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-async-function@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" + resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz" integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== dependencies: async-function "^1.0.0" @@ -4800,14 +4800,14 @@ is-async-function@^2.0.0: is-bigint@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz" integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: has-bigints "^1.0.2" is-boolean-object@^1.2.1: version "1.2.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz" integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== dependencies: call-bound "^1.0.3" @@ -4815,26 +4815,26 @@ is-boolean-object@^1.2.1: is-bun-module@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" + resolved "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz" integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ== dependencies: semver "^7.7.1" is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.13.0, is-core-module@^2.16.1: version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" is-data-view@^1.0.1, is-data-view@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz" integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== dependencies: call-bound "^1.0.2" @@ -4843,7 +4843,7 @@ is-data-view@^1.0.1, is-data-view@^1.0.2: is-date-object@^1.0.5, is-date-object@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz" integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== dependencies: call-bound "^1.0.2" @@ -4851,36 +4851,36 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-finalizationregistry@^1.1.0: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz" integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== dependencies: call-bound "^1.0.3" is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== dependencies: get-east-asian-width "^1.3.1" is-generator-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-generator-function@^1.0.10: version "1.1.2" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" + resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: call-bound "^1.0.4" @@ -4891,31 +4891,31 @@ is-generator-function@^1.0.10: is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" + resolved "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz" integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ== dependencies: tslib "^2.0.3" is-map@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" + resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== is-negative-zero@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-object@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz" integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== dependencies: call-bound "^1.0.3" @@ -4923,12 +4923,12 @@ is-number-object@^1.1.1: is-number@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-regex@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: call-bound "^1.0.2" @@ -4938,31 +4938,31 @@ is-regex@^1.2.1: is-relative@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" + resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-set@^2.0.3: version "2.0.3" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" + resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== is-shared-array-buffer@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz" integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== dependencies: call-bound "^1.0.3" is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz" integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== dependencies: call-bound "^1.0.3" @@ -4970,7 +4970,7 @@ is-string@^1.1.1: is-symbol@^1.0.4, is-symbol@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz" integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== dependencies: call-bound "^1.0.2" @@ -4979,45 +4979,45 @@ is-symbol@^1.0.4, is-symbol@^1.1.1: is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: version "1.1.15" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: which-typed-array "^1.1.16" is-unc-path@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" + resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" + resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz" integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ== dependencies: tslib "^2.0.3" is-weakmap@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" + resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== is-weakref@^1.0.2, is-weakref@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz" integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== dependencies: call-bound "^1.0.3" is-weakset@^2.0.3: version "2.0.4" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" + resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz" integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== dependencies: call-bound "^1.0.3" @@ -5025,37 +5025,37 @@ is-weakset@^2.0.3: is-windows@^1.0.1: version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@^2.0.5: version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isomorphic-ws@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== isows@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" + resolved "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz" integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" @@ -5066,7 +5066,7 @@ istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: istanbul-lib-report@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -5075,7 +5075,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^5.0.0: version "5.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz" integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== dependencies: "@jridgewell/trace-mapping" "^0.3.23" @@ -5084,7 +5084,7 @@ istanbul-lib-source-maps@^5.0.0: istanbul-reports@^3.1.3: version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz" integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== dependencies: html-escaper "^2.0.0" @@ -5092,7 +5092,7 @@ istanbul-reports@^3.1.3: iterator.prototype@^1.1.4: version "1.1.5" - resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" + resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz" integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== dependencies: define-data-property "^1.1.4" @@ -5104,7 +5104,7 @@ iterator.prototype@^1.1.4: jackspeak@^3.1.2: version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" @@ -5113,12 +5113,12 @@ jackspeak@^3.1.2: javascript-stringify@^2.0.1: version "2.1.0" - resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" + resolved "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== jest-changed-files@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz#602266e478ed554e1e1469944faa7efd37cee61c" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz" integrity sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ== dependencies: execa "^5.1.1" @@ -5127,7 +5127,7 @@ jest-changed-files@30.2.0: jest-circus@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.2.0.tgz#98b8198b958748a2f322354311023d1d02e7603f" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz" integrity sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg== dependencies: "@jest/environment" "30.2.0" @@ -5153,7 +5153,7 @@ jest-circus@30.2.0: jest-cli@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.2.0.tgz#1780f8e9d66bf84a10b369aea60aeda7697dcc67" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz" integrity sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA== dependencies: "@jest/core" "30.2.0" @@ -5169,7 +5169,7 @@ jest-cli@30.2.0: jest-config@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.2.0.tgz#29df8c50e2ad801cc59c406b50176c18c362a90b" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz" integrity sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA== dependencies: "@babel/core" "^7.27.4" @@ -5199,7 +5199,7 @@ jest-config@30.2.0: jest-diff@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz" integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== dependencies: "@jest/diff-sequences" "30.0.1" @@ -5209,14 +5209,14 @@ jest-diff@30.2.0: jest-docblock@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz" integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== dependencies: detect-newline "^3.1.0" jest-each@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.2.0.tgz#39e623ae71641c2ac3ee69b3ba3d258fce8e768d" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz" integrity sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ== dependencies: "@jest/get-type" "30.1.0" @@ -5227,7 +5227,7 @@ jest-each@30.2.0: jest-environment-node@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz#3def7980ebd2fd86e74efd4d2e681f55ab38da0f" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz" integrity sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA== dependencies: "@jest/environment" "30.2.0" @@ -5240,7 +5240,7 @@ jest-environment-node@30.2.0: jest-haste-map@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz" integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== dependencies: "@jest/types" "30.2.0" @@ -5258,7 +5258,7 @@ jest-haste-map@30.2.0: jest-leak-detector@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz#292fdca7b7c9cf594e1e570ace140b01d8beb736" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz" integrity sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ== dependencies: "@jest/get-type" "30.1.0" @@ -5266,7 +5266,7 @@ jest-leak-detector@30.2.0: jest-matcher-utils@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz" integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== dependencies: "@jest/get-type" "30.1.0" @@ -5276,7 +5276,7 @@ jest-matcher-utils@30.2.0: jest-message-util@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz" integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== dependencies: "@babel/code-frame" "^7.27.1" @@ -5291,7 +5291,7 @@ jest-message-util@30.2.0: jest-mock@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz" integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== dependencies: "@jest/types" "30.2.0" @@ -5300,17 +5300,17 @@ jest-mock@30.2.0: jest-pnp-resolver@^1.2.3: version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@30.0.1: version "30.0.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz" integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== jest-resolve-dependencies@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz#3370e2c0b49cc560f6a7e8ec3a59dd99525e1a55" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz" integrity sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w== dependencies: jest-regex-util "30.0.1" @@ -5318,7 +5318,7 @@ jest-resolve-dependencies@30.2.0: jest-resolve@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.2.0.tgz#2e2009cbd61e8f1f003355d5ec87225412cebcd7" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz" integrity sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A== dependencies: chalk "^4.1.2" @@ -5332,7 +5332,7 @@ jest-resolve@30.2.0: jest-runner@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.2.0.tgz#c62b4c3130afa661789705e13a07bdbcec26a114" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz" integrity sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ== dependencies: "@jest/console" "30.2.0" @@ -5360,7 +5360,7 @@ jest-runner@30.2.0: jest-runtime@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.2.0.tgz#395ea792cde048db1b0cd1a92dc9cb9f1921bf8a" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz" integrity sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg== dependencies: "@jest/environment" "30.2.0" @@ -5388,7 +5388,7 @@ jest-runtime@30.2.0: jest-snapshot@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz#266fbbb4b95fc4665ce6f32f1f38eeb39f4e26d0" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz" integrity sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA== dependencies: "@babel/core" "^7.27.4" @@ -5415,7 +5415,7 @@ jest-snapshot@30.2.0: jest-util@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz" integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== dependencies: "@jest/types" "30.2.0" @@ -5427,7 +5427,7 @@ jest-util@30.2.0: jest-validate@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.2.0.tgz#273eaaed4c0963b934b5b31e96289edda6e0a2ef" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz" integrity sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw== dependencies: "@jest/get-type" "30.1.0" @@ -5439,7 +5439,7 @@ jest-validate@30.2.0: jest-watcher@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.2.0.tgz#f9c055de48e18c979e7756a3917e596e2d69b07b" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz" integrity sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg== dependencies: "@jest/test-result" "30.2.0" @@ -5453,7 +5453,7 @@ jest-watcher@30.2.0: jest-worker@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz" integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== dependencies: "@types/node" "*" @@ -5464,7 +5464,7 @@ jest-worker@30.2.0: jest@^30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-30.2.0.tgz#9f0a71e734af968f26952b5ae4b724af82681630" + resolved "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz" integrity sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A== dependencies: "@jest/core" "30.2.0" @@ -5474,17 +5474,17 @@ jest@^30.2.0: jiti@^2.0.0, jiti@^2.3.0: version "2.6.1" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" + resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz" integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz" integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" @@ -5492,39 +5492,39 @@ js-yaml@^3.13.1: js-yaml@^4.1.0: version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" jsesc@^3.0.2: version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-to-pretty-yaml@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" + resolved "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz" integrity sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A== dependencies: remedial "^1.0.7" @@ -5532,26 +5532,26 @@ json-to-pretty-yaml@^1.2.2: json2mq@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" + resolved "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz" integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== dependencies: string-convert "^0.2.0" json5@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.3: version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: array-includes "^3.1.6" @@ -5561,31 +5561,31 @@ json5@^2.1.2, json5@^2.2.3: keyv@^4.5.4: version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" language-subtag-registry@^0.3.20: version "0.3.23" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" + resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz" integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== language-tags@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" + resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz" integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== dependencies: language-subtag-registry "^0.3.20" leven@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -5593,7 +5593,7 @@ levn@^0.4.1: license-checker@^25.0.1: version "25.0.1" - resolved "https://registry.yarnpkg.com/license-checker/-/license-checker-25.0.1.tgz#4d14504478a5240a857bb3c21cd0491a00d761fa" + resolved "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz" integrity sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g== dependencies: chalk "^2.4.1" @@ -5609,12 +5609,12 @@ license-checker@^25.0.1: lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^9.0.0: version "9.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" + resolved "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz" integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== dependencies: cli-truncate "^5.0.0" @@ -5626,7 +5626,7 @@ listr2@^9.0.0: loader-utils@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" @@ -5635,41 +5635,41 @@ loader-utils@^2.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash-es@^4.17.21: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -5677,7 +5677,7 @@ log-symbols@^4.0.0: log-update@^6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" + resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: ansi-escapes "^7.0.0" @@ -5688,89 +5688,89 @@ log-update@^6.1.0: loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lower-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" + resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz" integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg== dependencies: tslib "^2.0.3" lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== dependencies: tslib "^2.0.3" lru-cache@^10.2.0: version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" make-dir@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: semver "^7.5.3" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.12: version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" map-cache@^0.2.0: version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== memoize-one@^4.0.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" + resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-4.1.0.tgz" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@^1.2.1, meros@^1.3.2: version "1.3.2" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.2.tgz#4cb0d7f3d22074c6e7ae1d66f72c080bca2e414b" + resolved "https://registry.npmjs.org/meros/-/meros-1.3.2.tgz" integrity sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A== micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -5778,73 +5778,73 @@ micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" + resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== mkdirp@^0.5.1: version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" ms@^2.1.1, ms@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" + resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz" integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== nanoid@^3.3.6: version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== napi-postinstall@^0.3.0: version "0.3.4" - resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" + resolved "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz" integrity sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@16.0.3: version "16.0.3" - resolved "https://registry.yarnpkg.com/next/-/next-16.0.3.tgz#f05d651805524320b9d6a644de87230779fa56bb" + resolved "https://registry.npmjs.org/next/-/next-16.0.3.tgz" integrity sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w== dependencies: "@next/env" "16.0.3" @@ -5865,7 +5865,7 @@ next@16.0.3: no-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: lower-case "^2.0.2" @@ -5873,24 +5873,24 @@ no-case@^3.0.4: node-addon-api@^7.0.0: version "7.1.1" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-domexception@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-fetch@^2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" node-fetch@^3.3.2: version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz" integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== dependencies: data-uri-to-buffer "^4.0.0" @@ -5899,17 +5899,17 @@ node-fetch@^3.3.2: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.27: version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz" integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== nopt@^4.0.1: version "4.0.3" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -5917,7 +5917,7 @@ nopt@^4.0.1: normalize-package-data@^2.0.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -5927,51 +5927,51 @@ normalize-package-data@^2.0.0: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-normalize-package-bin@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" + resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4, object.assign@^4.1.7: version "4.1.7" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== dependencies: call-bind "^1.0.8" @@ -5983,7 +5983,7 @@ object.assign@^4.1.4, object.assign@^4.1.7: object.entries@^1.1.9: version "1.1.9" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz" integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== dependencies: call-bind "^1.0.8" @@ -5993,7 +5993,7 @@ object.entries@^1.1.9: object.fromentries@^2.0.8: version "2.0.8" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: call-bind "^1.0.7" @@ -6003,7 +6003,7 @@ object.fromentries@^2.0.8: object.groupby@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== dependencies: call-bind "^1.0.7" @@ -6012,7 +6012,7 @@ object.groupby@^1.0.3: object.values@^1.1.6, object.values@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz" integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: call-bind "^1.0.8" @@ -6022,28 +6022,28 @@ object.values@^1.1.6, object.values@^1.2.1: once@^1.3.0: version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" onetime@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" + resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" optimism@^0.18.0: version "0.18.1" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz" integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== dependencies: "@wry/caches" "^1.0.0" @@ -6053,7 +6053,7 @@ optimism@^0.18.0: optionator@^0.9.3: version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" @@ -6065,17 +6065,17 @@ optionator@^0.9.3: os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== os-tmpdir@^1.0.0: version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== osenv@^0.1.4: version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -6083,7 +6083,7 @@ osenv@^0.1.4: own-keys@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + resolved "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz" integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== dependencies: get-intrinsic "^1.2.6" @@ -6092,45 +6092,45 @@ own-keys@^1.0.1: p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json-from-dist@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== param-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: dot-case "^3.0.4" @@ -6138,14 +6138,14 @@ param-case@^3.0.4: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-filepath@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" + resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" @@ -6154,7 +6154,7 @@ parse-filepath@^1.0.2: parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -6164,7 +6164,7 @@ parse-json@^5.2.0: pascal-case@^3.1.2: version "3.1.2" - resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: no-case "^3.0.4" @@ -6172,7 +6172,7 @@ pascal-case@^3.1.2: path-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz" integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: dot-case "^3.0.4" @@ -6180,39 +6180,39 @@ path-case@^3.0.4: path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" + resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" + resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" path-scurry@^1.11.1: version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" @@ -6220,49 +6220,49 @@ path-scurry@^1.11.1: path-to-regexp@8.2.0: version "8.2.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz" integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== path-type@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== picomatch@^4.0.2, picomatch@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pirates@^4.0.7: version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" possible-typed-array-names@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" + resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== postcss@8.4.31: version "8.4.31" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" @@ -6271,17 +6271,17 @@ postcss@8.4.31: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: version "3.6.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== pretty-format@30.2.0: version "30.2.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz" integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== dependencies: "@jest/schemas" "30.0.5" @@ -6290,14 +6290,14 @@ pretty-format@30.2.0: promise@^7.1.1: version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" prop-types@^15.8.1: version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -6306,22 +6306,22 @@ prop-types@^15.8.1: punycode@^2.1.0: version "2.3.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^7.0.0: version "7.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-7.0.1.tgz#6f53a5a9e3e4a47445822af96821ca509ed37566" + resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz" integrity sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== rc-cascader@~3.34.0: version "3.34.0" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.34.0.tgz#56f936ab6b1229bab7d558701ce9b9e96536582c" + resolved "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz" integrity sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag== dependencies: "@babel/runtime" "^7.25.7" @@ -6332,7 +6332,7 @@ rc-cascader@~3.34.0: rc-checkbox@~3.5.0: version "3.5.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz#3ae2441e3a321774d390f76539e706864fcf5ff0" + resolved "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz" integrity sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg== dependencies: "@babel/runtime" "^7.10.1" @@ -6341,7 +6341,7 @@ rc-checkbox@~3.5.0: rc-collapse@~3.9.0: version "3.9.0" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.9.0.tgz#972404ce7724e1c9d1d2476543e1175404a36806" + resolved "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz" integrity sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== dependencies: "@babel/runtime" "^7.10.1" @@ -6351,7 +6351,7 @@ rc-collapse@~3.9.0: rc-dialog@~9.6.0: version "9.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" + resolved "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz" integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== dependencies: "@babel/runtime" "^7.10.1" @@ -6362,7 +6362,7 @@ rc-dialog@~9.6.0: rc-drawer@~7.3.0: version "7.3.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.3.0.tgz#1bb5fe5f9da38b6a2b2a7dffc9fcb647252a328f" + resolved "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz" integrity sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg== dependencies: "@babel/runtime" "^7.23.9" @@ -6373,7 +6373,7 @@ rc-drawer@~7.3.0: rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: version "4.2.1" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz#44729eb2a4272e0353d31ac060da21e606accb1c" + resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz" integrity sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== dependencies: "@babel/runtime" "^7.18.3" @@ -6383,7 +6383,7 @@ rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: rc-field-form@~2.7.1: version "2.7.1" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.7.1.tgz#8bb1d7c6bf40e2b99b494816972252c327d9b5a0" + resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz" integrity sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A== dependencies: "@babel/runtime" "^7.18.0" @@ -6392,7 +6392,7 @@ rc-field-form@~2.7.1: rc-image@~7.12.0: version "7.12.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe" + resolved "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz" integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q== dependencies: "@babel/runtime" "^7.11.2" @@ -6404,7 +6404,7 @@ rc-image@~7.12.0: rc-input-number@~9.5.0: version "9.5.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38" + resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz" integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag== dependencies: "@babel/runtime" "^7.10.1" @@ -6415,7 +6415,7 @@ rc-input-number@~9.5.0: rc-input@~1.8.0: version "1.8.0" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974" + resolved "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz" integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA== dependencies: "@babel/runtime" "^7.11.1" @@ -6424,7 +6424,7 @@ rc-input@~1.8.0: rc-mentions@~2.20.0: version "2.20.0" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7" + resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz" integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ== dependencies: "@babel/runtime" "^7.22.5" @@ -6437,7 +6437,7 @@ rc-mentions@~2.20.0: rc-menu@~9.16.0, rc-menu@~9.16.1: version "9.16.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.16.1.tgz#9df1168e41d87dc7164c582173e1a1d32011899f" + resolved "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz" integrity sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== dependencies: "@babel/runtime" "^7.10.1" @@ -6449,7 +6449,7 @@ rc-menu@~9.16.0, rc-menu@~9.16.1: rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.5: version "2.9.5" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" + resolved "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz" integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== dependencies: "@babel/runtime" "^7.11.1" @@ -6458,7 +6458,7 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio rc-notification@~5.6.4: version "5.6.4" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544" + resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz" integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== dependencies: "@babel/runtime" "^7.10.1" @@ -6468,7 +6468,7 @@ rc-notification@~5.6.4: rc-overflow@^1.3.1, rc-overflow@^1.3.2: version "1.5.0" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.5.0.tgz#02e58a15199e392adfcc87e0d6e9e7c8e57f2771" + resolved "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz" integrity sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg== dependencies: "@babel/runtime" "^7.11.1" @@ -6478,7 +6478,7 @@ rc-overflow@^1.3.1, rc-overflow@^1.3.2: rc-pagination@~5.1.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-5.1.0.tgz#a6e63a2c5db29e62f991282eb18a2d3ee725ba8b" + resolved "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz" integrity sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ== dependencies: "@babel/runtime" "^7.10.1" @@ -6487,7 +6487,7 @@ rc-pagination@~5.1.0: rc-picker@~4.11.3: version "4.11.3" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.11.3.tgz#7e7e3ad83aa461c284b8391c697492d1c34d2cb8" + resolved "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz" integrity sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== dependencies: "@babel/runtime" "^7.24.7" @@ -6499,7 +6499,7 @@ rc-picker@~4.11.3: rc-progress@~4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" + resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz" integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== dependencies: "@babel/runtime" "^7.10.1" @@ -6508,7 +6508,7 @@ rc-progress@~4.0.0: rc-rate@~2.13.1: version "2.13.1" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.1.tgz#29af7a3d4768362e9d4388f955a8b6389526b7fd" + resolved "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz" integrity sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== dependencies: "@babel/runtime" "^7.10.1" @@ -6517,7 +6517,7 @@ rc-rate@~2.13.1: rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3: version "1.4.3" - resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" + resolved "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz" integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== dependencies: "@babel/runtime" "^7.20.7" @@ -6527,7 +6527,7 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-segmented@~2.7.0: version "2.7.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.7.0.tgz#f56c2044abf8f03958b3a9a9d32987f10dcc4fc4" + resolved "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz" integrity sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA== dependencies: "@babel/runtime" "^7.11.1" @@ -6537,7 +6537,7 @@ rc-segmented@~2.7.0: rc-select@~14.16.2, rc-select@~14.16.8: version "14.16.8" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97" + resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz" integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== dependencies: "@babel/runtime" "^7.10.1" @@ -6550,7 +6550,7 @@ rc-select@~14.16.2, rc-select@~14.16.8: rc-slider@~11.1.9: version "11.1.9" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.9.tgz#d872130fbf4ec51f28543d62e90451091d6f5208" + resolved "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz" integrity sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A== dependencies: "@babel/runtime" "^7.10.1" @@ -6559,7 +6559,7 @@ rc-slider@~11.1.9: rc-steps@~6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" + resolved "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz" integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== dependencies: "@babel/runtime" "^7.16.7" @@ -6568,7 +6568,7 @@ rc-steps@~6.0.1: rc-switch@~4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" + resolved "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz" integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== dependencies: "@babel/runtime" "^7.21.0" @@ -6577,7 +6577,7 @@ rc-switch@~4.1.0: rc-table@~7.54.0: version "7.54.0" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.54.0.tgz#dedd4ea18d1189f2acdf90a80f04d8ca0111e16a" + resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz" integrity sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw== dependencies: "@babel/runtime" "^7.10.1" @@ -6589,7 +6589,7 @@ rc-table@~7.54.0: rc-tabs@~15.7.0: version "15.7.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.7.0.tgz#14ca2ee6213d00491a8b67ae26e2d35c256bf19a" + resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz" integrity sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA== dependencies: "@babel/runtime" "^7.11.2" @@ -6602,7 +6602,7 @@ rc-tabs@~15.7.0: rc-textarea@~1.10.0, rc-textarea@~1.10.2: version "1.10.2" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.2.tgz#459e3574a95c32939c6793045a1e4db04cb514cc" + resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz" integrity sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ== dependencies: "@babel/runtime" "^7.10.1" @@ -6613,7 +6613,7 @@ rc-textarea@~1.10.0, rc-textarea@~1.10.2: rc-tooltip@~6.4.0: version "6.4.0" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz#e832ed0392872025e59928cfc1ad9045656467fd" + resolved "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz" integrity sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g== dependencies: "@babel/runtime" "^7.11.2" @@ -6623,7 +6623,7 @@ rc-tooltip@~6.4.0: rc-tree-select@~5.27.0: version "5.27.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz#3daa62972ae80846dac96bf4776d1a9dc9c7c4c6" + resolved "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz" integrity sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww== dependencies: "@babel/runtime" "^7.25.7" @@ -6634,7 +6634,7 @@ rc-tree-select@~5.27.0: rc-tree@~5.13.0, rc-tree@~5.13.1: version "5.13.1" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.13.1.tgz#f36a33a94a1282f4b09685216c01487089748910" + resolved "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz" integrity sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== dependencies: "@babel/runtime" "^7.10.1" @@ -6645,7 +6645,7 @@ rc-tree@~5.13.0, rc-tree@~5.13.1: rc-upload@~4.11.0: version "4.11.0" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.11.0.tgz#c2ced905a8b38b3e5c48493d388ca0e8373de18b" + resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.11.0.tgz" integrity sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA== dependencies: "@babel/runtime" "^7.18.3" @@ -6654,7 +6654,7 @@ rc-upload@~4.11.0: rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3, rc-util@^5.44.4: version "5.44.4" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" + resolved "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz" integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== dependencies: "@babel/runtime" "^7.18.3" @@ -6662,7 +6662,7 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18. rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: version "3.19.2" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz#1dd2d782c9a3ccbe537bb873447d73f83af8de0f" + resolved "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz" integrity sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA== dependencies: "@babel/runtime" "^7.20.0" @@ -6672,7 +6672,7 @@ rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: react-cookie@^8.0.1: version "8.0.1" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.0.1.tgz#0d7f5112c9003b61c70d3d53f1f0a5b741e9f2e9" + resolved "https://registry.npmjs.org/react-cookie/-/react-cookie-8.0.1.tgz" integrity sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA== dependencies: "@types/hoist-non-react-statics" "^3.3.6" @@ -6681,14 +6681,14 @@ react-cookie@^8.0.1: react-dom@19.2.0: version "19.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.0.tgz#00ed1e959c365e9a9d48f8918377465466ec3af8" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz" integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== dependencies: scheduler "^0.27.0" react-highlight-words@^0.21.0: version "0.21.0" - resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.21.0.tgz#a109acdf7dc6fac3ed7db82e9cba94e8d65c281c" + resolved "https://registry.npmjs.org/react-highlight-words/-/react-highlight-words-0.21.0.tgz" integrity sha512-SdWEeU9fIINArEPO1rO5OxPyuhdEKZQhHzZZP1ie6UeXQf+CjycT1kWaB+9bwGcVbR0NowuHK3RqgqNg6bgBDQ== dependencies: highlight-words-core "^1.2.0" @@ -6696,22 +6696,22 @@ react-highlight-words@^0.21.0: react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^18.2.0, react-is@^18.3.1: version "18.3.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react-is@^19.2.0: version "19.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.0.tgz#ddc3b4a4e0f3336c3847f18b806506388d7b9973" + resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz" integrity sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA== react-test-renderer@^19.2.0: version "19.2.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.0.tgz#5c9782b4a4ba0630a77d7ce092779fdf9ccde209" + resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.2.0.tgz" integrity sha512-zLCFMHFE9vy/w3AxO0zNxy6aAupnCuLSVOJYDe/Tp+ayGI1f2PLQsFVPANSD42gdSbmYx5oN+1VWDhcXtq7hAQ== dependencies: react-is "^19.2.0" @@ -6719,12 +6719,12 @@ react-test-renderer@^19.2.0: react@19.2.0: version "19.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.0.tgz#d33dd1721698f4376ae57a54098cb47fc75d93a5" + resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz" integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== read-installed@~4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" + resolved "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz" integrity sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ== dependencies: debuglog "^1.0.1" @@ -6738,7 +6738,7 @@ read-installed@~4.0.3: read-package-json@^2.0.0: version "2.1.2" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz" integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== dependencies: glob "^7.1.1" @@ -6748,7 +6748,7 @@ read-package-json@^2.0.0: readdir-scoped-modules@^1.0.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: debuglog "^1.0.1" @@ -6758,7 +6758,7 @@ readdir-scoped-modules@^1.0.0: reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" - resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz" integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== dependencies: call-bind "^1.0.8" @@ -6772,7 +6772,7 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: version "1.5.4" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz" integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== dependencies: call-bind "^1.0.8" @@ -6784,7 +6784,7 @@ regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: relay-runtime@12.0.0: version "12.0.0" - resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" + resolved "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz" integrity sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug== dependencies: "@babel/runtime" "^7.0.0" @@ -6793,59 +6793,59 @@ relay-runtime@12.0.0: remedial@^1.0.7: version "1.0.8" - resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" + resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-trailing-spaces@^1.0.6: version "1.0.9" - resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz#39c270a309ea16fda84253ffbdeb1b5afa0aa271" + resolved "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz" integrity sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== require-main-filename@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== resize-observer-polyfill@^1.5.1: version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-pkg-maps@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve@^1.10.0, resolve@^1.22.4: version "1.22.11" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz" integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: is-core-module "^2.16.1" @@ -6854,7 +6854,7 @@ resolve@^1.10.0, resolve@^1.22.4: resolve@^2.0.0-next.5: version "2.0.0-next.5" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz" integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: is-core-module "^2.13.0" @@ -6863,7 +6863,7 @@ resolve@^2.0.0-next.5: restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -6871,31 +6871,31 @@ restore-cursor@^5.0.0: reusify@^1.0.4: version "1.1.0" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@7.8.2, rxjs@^7.8.2: version "7.8.2" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" + resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== dependencies: tslib "^2.1.0" safe-array-concat@^1.1.3: version "1.1.3" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz" integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== dependencies: call-bind "^1.0.8" @@ -6906,7 +6906,7 @@ safe-array-concat@^1.1.3: safe-push-apply@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz" integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== dependencies: es-errors "^1.3.0" @@ -6914,7 +6914,7 @@ safe-push-apply@^1.0.0: safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== dependencies: call-bound "^1.0.2" @@ -6923,44 +6923,44 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: safe-stable-stringify@^2.4.3: version "2.5.0" - resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== scheduler@^0.27.0: version "0.27.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== scroll-into-view-if-needed@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" + resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz" integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== dependencies: compute-scroll-into-view "^3.0.2" "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.3.1: version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== sentence-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz" integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: no-case "^3.0.4" @@ -6969,12 +6969,12 @@ sentence-case@^3.0.4: set-blocking@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== set-function-length@^1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -6986,7 +6986,7 @@ set-function-length@^1.2.2: set-function-name@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: define-data-property "^1.1.4" @@ -6996,7 +6996,7 @@ set-function-name@^2.0.2: set-proto@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + resolved "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz" integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== dependencies: dunder-proto "^1.0.1" @@ -7005,12 +7005,12 @@ set-proto@^1.0.0: setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== sharp@^0.34.4, sharp@^0.34.5: version "0.34.5" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" + resolved "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz" integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== dependencies: "@img/colour" "^1.0.0" @@ -7044,24 +7044,24 @@ sharp@^0.34.4, sharp@^0.34.5: shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@1.8.3, shell-quote@^1.7.3: version "1.8.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== side-channel-list@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz" integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: es-errors "^1.3.0" @@ -7069,7 +7069,7 @@ side-channel-list@^1.0.0: side-channel-map@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: call-bound "^1.0.2" @@ -7079,7 +7079,7 @@ side-channel-map@^1.0.1: side-channel-weakmap@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== dependencies: call-bound "^1.0.2" @@ -7090,7 +7090,7 @@ side-channel-weakmap@^1.0.2: side-channel@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: es-errors "^1.3.0" @@ -7101,27 +7101,27 @@ side-channel@^1.1.0: signal-exit@^3.0.3: version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== signedsource@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" + resolved "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz" integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== slash@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz" integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== dependencies: ansi-styles "^6.2.1" @@ -7129,12 +7129,12 @@ slice-ansi@^7.1.0: slide@~1.1.3: version "1.1.6" - resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== snake-case@^3.0.4: version "3.0.4" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: dot-case "^3.0.4" @@ -7142,12 +7142,12 @@ snake-case@^3.0.4: source-map-js@^1.0.2: version "1.2.1" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@0.5.13: version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" @@ -7155,12 +7155,12 @@ source-map-support@0.5.13: source-map@^0.6.0: version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-compare@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7" + resolved "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz" integrity sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A== dependencies: array-find-index "^1.0.2" @@ -7169,7 +7169,7 @@ spdx-compare@^1.0.0: spdx-correct@^3.0.0: version "3.2.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" @@ -7177,12 +7177,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.5.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -7190,17 +7190,17 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.22" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz" integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== spdx-ranges@^2.0.0: version "2.1.1" - resolved "https://registry.yarnpkg.com/spdx-ranges/-/spdx-ranges-2.1.1.tgz#87573927ba51e92b3f4550ab60bfc83dd07bac20" + resolved "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz" integrity sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA== spdx-satisfies@^4.0.0: version "4.0.1" - resolved "https://registry.yarnpkg.com/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz#9a09a68d80f5f1a31cfaebb384b0c6009e4969fe" + resolved "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz" integrity sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA== dependencies: spdx-compare "^1.0.0" @@ -7209,31 +7209,31 @@ spdx-satisfies@^4.0.0: sponge-case@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" + resolved "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz" integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA== dependencies: tslib "^2.0.3" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== stable-hash@^0.0.5: version "0.0.5" - resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" + resolved "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz" integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== stack-utils@^2.0.6: version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" stop-iteration-iterator@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz" integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== dependencies: es-errors "^1.3.0" @@ -7241,17 +7241,17 @@ stop-iteration-iterator@^1.1.0: string-convert@^0.2.0: version "0.2.1" - resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" + resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz" integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== string-env-interpolation@^1.0.1: version "1.0.1" - resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" + resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== string-length@^4.0.2: version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -7259,7 +7259,7 @@ string-length@^4.0.2: "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -7268,7 +7268,7 @@ string-length@^4.0.2: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -7277,7 +7277,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" @@ -7286,7 +7286,7 @@ string-width@^5.0.1, string-width@^5.1.2: string-width@^7.0.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" @@ -7295,7 +7295,7 @@ string-width@^7.0.0: string-width@^8.0.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" + resolved "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz" integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== dependencies: get-east-asian-width "^1.3.0" @@ -7303,7 +7303,7 @@ string-width@^8.0.0: string.prototype.includes@^2.0.1: version "2.0.1" - resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" + resolved "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz" integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== dependencies: call-bind "^1.0.7" @@ -7312,7 +7312,7 @@ string.prototype.includes@^2.0.1: string.prototype.matchall@^4.0.12: version "4.0.12" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz" integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== dependencies: call-bind "^1.0.8" @@ -7331,7 +7331,7 @@ string.prototype.matchall@^4.0.12: string.prototype.repeat@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + resolved "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz" integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== dependencies: define-properties "^1.1.3" @@ -7339,7 +7339,7 @@ string.prototype.repeat@^1.0.0: string.prototype.trim@^1.2.10: version "1.2.10" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz" integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== dependencies: call-bind "^1.0.8" @@ -7352,7 +7352,7 @@ string.prototype.trim@^1.2.10: string.prototype.trimend@^1.0.9: version "1.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz" integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== dependencies: call-bind "^1.0.8" @@ -7362,7 +7362,7 @@ string.prototype.trimend@^1.0.9: string.prototype.trimstart@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== dependencies: call-bind "^1.0.7" @@ -7371,93 +7371,93 @@ string.prototype.trimstart@^1.0.8: "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== styled-jsx@5.1.6: version "5.1.6" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" + resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz" integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== dependencies: client-only "0.0.1" stylis@^4.3.4: version "4.3.6" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== supports-color@8.1.1, supports-color@^8.1.1: version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== swap-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" + resolved "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz" integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw== dependencies: tslib "^2.0.3" swr@^2.0.0: version "2.3.6" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.6.tgz#5fee0ee8a0762a16871ee371075cb09422b64f50" + resolved "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz" integrity sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw== dependencies: dequal "^2.0.3" @@ -7465,7 +7465,7 @@ swr@^2.0.0: sync-fetch@0.6.0-2: version "0.6.0-2" - resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.6.0-2.tgz#d82d6dc8efaf2d103a9015e7bd7ba0bfc8e078f2" + resolved "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.6.0-2.tgz" integrity sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A== dependencies: node-fetch "^3.3.2" @@ -7474,14 +7474,14 @@ sync-fetch@0.6.0-2: synckit@^0.11.8: version "0.11.11" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz" integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== dependencies: "@pkgr/core" "^0.2.9" test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -7490,17 +7490,17 @@ test-exclude@^6.0.0: throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: version "5.0.2" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" + resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz" integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== timeout-signal@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/timeout-signal/-/timeout-signal-2.0.0.tgz#23207ea448d50258bb0defe3beea4a467643abba" + resolved "https://registry.npmjs.org/timeout-signal/-/timeout-signal-2.0.0.tgz" integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== tinyglobby@^0.2.13: version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: fdir "^6.5.0" @@ -7508,56 +7508,56 @@ tinyglobby@^0.2.13: title-case@^3.0.3: version "3.0.3" - resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" + resolved "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz" integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA== dependencies: tslib "^2.0.3" tmpl@1.0.5: version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toggle-selection@^1.0.6: version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== tr46@~0.0.3: version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== tree-kill@1.2.2: version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== treeify@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-api-utils@^2.1.0: version "2.1.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz" integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== ts-log@^2.2.3: version "2.2.7" - resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" + resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz" integrity sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg== ts-node@^10.9.1: version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -7576,7 +7576,7 @@ ts-node@^10.9.1: tsconfig-paths@^3.15.0: version "3.15.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" @@ -7586,39 +7586,39 @@ tsconfig-paths@^3.15.0: tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== tslib@~2.4.0: version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tslib@~2.6.0: version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typed-array-buffer@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz" integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: call-bound "^1.0.3" @@ -7627,7 +7627,7 @@ typed-array-buffer@^1.0.3: typed-array-byte-length@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz" integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== dependencies: call-bind "^1.0.8" @@ -7638,7 +7638,7 @@ typed-array-byte-length@^1.0.3: typed-array-byte-offset@^1.0.4: version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz" integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== dependencies: available-typed-arrays "^1.0.7" @@ -7651,7 +7651,7 @@ typed-array-byte-offset@^1.0.4: typed-array-length@^1.0.7: version "1.0.7" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz" integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== dependencies: call-bind "^1.0.7" @@ -7663,7 +7663,7 @@ typed-array-length@^1.0.7: typescript-eslint@^8.46.0: version "8.47.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.47.0.tgz#bb8fcf4f2c69ffcd5d088f7f30cd52936ff05cbc" + resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz" integrity sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q== dependencies: "@typescript-eslint/eslint-plugin" "8.47.0" @@ -7673,17 +7673,17 @@ typescript-eslint@^8.46.0: typescript@^5.0.4: version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ua-parser-js@^1.0.35: version "1.0.41" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.41.tgz#bd04dc9ec830fcf9e4fad35cf22dcedd2e3b4e9c" + resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz" integrity sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug== unbox-primitive@^1.1.0: version "1.1.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== dependencies: call-bound "^1.0.3" @@ -7693,31 +7693,31 @@ unbox-primitive@^1.1.0: unc-path-regex@^0.1.2: version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" + resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== undici-types@~7.16.0: version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== universal-cookie@^8.0.0: version "8.0.1" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.0.1.tgz#19263fcda6889978a081c610a90fc395eeaeec64" + resolved "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.0.1.tgz" integrity sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg== dependencies: cookie "^1.0.2" unixify@^1.0.0: version "1.0.0" - resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" + resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz" integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: version "1.11.1" - resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" + resolved "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz" integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== dependencies: napi-postinstall "^0.3.0" @@ -7744,7 +7744,7 @@ unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: update-browserslist-db@^1.1.4: version "1.1.4" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz#7802aa2ae91477f255b86e0e46dbc787a206ad4a" + resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz" integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== dependencies: escalade "^3.2.0" @@ -7752,48 +7752,48 @@ update-browserslist-db@^1.1.4: upper-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz" integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== dependencies: tslib "^2.0.3" upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + resolved "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz" integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== dependencies: tslib "^2.0.3" uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urlpattern-polyfill@^10.0.0: version "10.1.0" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz#1b2517e614136c73ba32948d5e7a3a063cba8e74" + resolved "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz" integrity sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw== use-sync-external-store@^1.4.0: version "1.6.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz" integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== util-extend@^1.0.1: version "1.0.3" - resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" + resolved "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz" integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" @@ -7802,7 +7802,7 @@ v8-to-istanbul@^9.0.1: validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -7810,36 +7810,36 @@ validate-npm-package-license@^3.0.1: walker@^1.0.8: version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" warning@^4.0.3: version "4.0.3" - resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== dependencies: loose-envify "^1.0.0" web-streams-polyfill@^3.0.3: version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -7847,7 +7847,7 @@ whatwg-url@^5.0.0: which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz" integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: is-bigint "^1.1.0" @@ -7858,7 +7858,7 @@ which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: which-builtin-type@^1.2.1: version "1.2.1" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz" integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== dependencies: call-bound "^1.0.2" @@ -7877,7 +7877,7 @@ which-builtin-type@^1.2.1: which-collection@^1.0.2: version "1.0.2" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" + resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: is-map "^2.0.3" @@ -7887,12 +7887,12 @@ which-collection@^1.0.2: which-module@^2.0.0: version "2.0.1" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== which-typed-array@^1.1.16, which-typed-array@^1.1.19: version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz" integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== dependencies: available-typed-arrays "^1.0.7" @@ -7905,19 +7905,19 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.19: which@^2.0.1: version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" word-wrap@^1.2.5: version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -7926,7 +7926,7 @@ word-wrap@^1.2.5: wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -7935,7 +7935,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -7944,7 +7944,7 @@ wrap-ansi@^7.0.0: wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" @@ -7953,7 +7953,7 @@ wrap-ansi@^8.1.0: wrap-ansi@^9.0.0: version "9.0.2" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz" integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== dependencies: ansi-styles "^6.2.1" @@ -7962,12 +7962,12 @@ wrap-ansi@^9.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^5.0.1: version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" @@ -7975,27 +7975,27 @@ write-file-atomic@^5.0.1: ws@^8.17.1, ws@^8.18.3: version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== y18n@^4.0.0: version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== y18n@^5.0.5: version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml-loader@^0.8: version "0.8.1" - resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.1.tgz#034f901147073cfc307cdcce8bd44c1547e60ba1" + resolved "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.8.1.tgz" integrity sha512-BCEndnUoi3BaZmePkwGGe93txRxLgMhBa/gE725v1/GHnura8QvNs7c4+4C1yyhhKoj3Dg63M7IqhA++15j6ww== dependencies: javascript-stringify "^2.0.1" @@ -8004,12 +8004,12 @@ yaml-loader@^0.8: yaml@^2.0.0, yaml@^2.3.1: version "2.8.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz" integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== yargs-parser@^18.1.2: version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== dependencies: camelcase "^5.0.0" @@ -8017,12 +8017,12 @@ yargs-parser@^18.1.2: yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -8035,7 +8035,7 @@ yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: yargs@^15.3.1: version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== dependencies: cliui "^6.0.0" @@ -8052,25 +8052,25 @@ yargs@^15.3.1: yn@3.1.1: version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yoctocolors-cjs@^2.1.3: version "2.1.3" - resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" + resolved "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz" integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== "zod-validation-error@^3.5.0 || ^4.0.0": version "4.0.2" - resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz#bc605eba49ce0fcd598c127fee1c236be3f22918" + resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz" integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== "zod@^3.25.0 || ^4.0.0": version "4.1.12" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.12.tgz#64f1ea53d00eab91853195653b5af9eee68970f0" + resolved "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz" integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== From a5fefaf4fc857ff95f5a3f24f91aa9747a2695a3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 5 Dec 2025 21:48:59 +0100 Subject: [PATCH 606/878] Add GNU Make target to restart the backend container (needed because `--no-hot-reload` does not work properly anymore and without it HotChocolate fails to recreate the GraphQL Schema properly --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 483229bf..cb4e2d52 100644 --- a/Makefile +++ b/Makefile @@ -132,6 +132,10 @@ restart : ## Restart all stopped and running containers ${docker_compose} restart .PHONY : restart +restartb : ## Restart the backend container + ${docker_compose} backend restart +.PHONY : restartb + prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes docker system prune \ --volumes From 2ca18e0f07025536d924911410d8198feb2301e7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Dec 2025 18:29:27 +0100 Subject: [PATCH 607/878] Upgrade to latest Next.JS due to security vulnerability https://nextjs.org/blog/CVE-2025-66478 --- frontend/package.json | 18 +- frontend/yarn.lock | 2892 +++++++++++++++++------------------------ 2 files changed, 1238 insertions(+), 1672 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 4c4daf8d..edf791f1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,10 +21,10 @@ "crypto-hash": "^4.0.0", "dayjs": "^1.0", "graphql": "^16.12.0", - "next": "16.0.3", - "react": "19.2.0", + "next": "^16.0.8", + "react": "^19.2.1", "react-cookie": "^8.0.1", - "react-dom": "19.2.0", + "react-dom": "^19.2.1", "react-highlight-words": "^0.21.0", "rxjs": "^7.8.2", "sharp": "^0.34.5" @@ -33,12 +33,12 @@ "@babel/core": "^7.0", "@graphql-codegen/cli": "^6.0.2", "@graphql-codegen/fragment-matcher": "^6.0.0", - "@graphql-codegen/near-operation-file-preset": "^3.1.0", + "@graphql-codegen/near-operation-file-preset": "^4.0.0", "@graphql-codegen/typescript": "^5.0.4", "@graphql-codegen/typescript-operations": "^5.0.4", "@types/node": "^24.10.1", - "@types/react": "19.2.5", - "@types/react-dom": "19.2.3", + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3", "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", @@ -46,7 +46,7 @@ "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", - "eslint-config-next": "16.0.3", + "eslint-config-next": "^16.0.8", "graphql-tag": "^2.0", "jest": "^30.2.0", "license-checker": "^25.0.1", @@ -69,7 +69,7 @@ ] }, "resolutions": { - "@types/react": "19.2.5", - "@types/react-dom": "19.2.3" + "@types/react": "^19.2.7", + "@types/react-dom": "^19.2.3" } } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4e15e831..d2016358 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -4,14 +4,14 @@ "@ant-design/colors@^7.0.0", "@ant-design/colors@^7.2.1": version "7.2.1" - resolved "https://registry.npmjs.org/@ant-design/colors/-/colors-7.2.1.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.2.1.tgz#3bbc1c6c18550020d1622a0067ff03492318df98" integrity sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== dependencies: "@ant-design/fast-color" "^2.0.6" "@ant-design/cssinjs-utils@^1.1.3": version "1.1.3" - resolved "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== dependencies: "@ant-design/cssinjs" "^1.21.0" @@ -20,7 +20,7 @@ "@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1", "@ant-design/cssinjs@^1.23.0": version "1.24.0" - resolved "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz#7db091f03f189abc77a13cbd27a2293802cd7285" integrity sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg== dependencies: "@babel/runtime" "^7.11.1" @@ -33,19 +33,19 @@ "@ant-design/fast-color@^2.0.6": version "2.0.6" - resolved "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" integrity sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA== dependencies: "@babel/runtime" "^7.24.7" "@ant-design/icons-svg@^4.4.0": version "4.4.2" - resolved "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== "@ant-design/icons@^5.0.0", "@ant-design/icons@^5.6.1": version "5.6.1" - resolved "https://registry.npmjs.org/@ant-design/icons/-/icons-5.6.1.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.6.1.tgz#7290fcdc3d96ff3fca793ed399053cd29ad5dbd3" integrity sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== dependencies: "@ant-design/colors" "^7.0.0" @@ -56,7 +56,7 @@ "@ant-design/pro-layout@^7.10.3": version "7.22.7" - resolved "https://registry.npmjs.org/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz#7002b1838e1cdf879bbec813677b478c26fbbb77" integrity sha512-fvmtNA1r9SaasVIQIQt611VSlNxtVxDbQ3e+1GhYQza3tVJi/3gCZuDyfMfTnbLmf3PaW/YvLkn7MqDbzAzoLA== dependencies: "@ant-design/cssinjs" "^1.21.1" @@ -77,7 +77,7 @@ "@ant-design/pro-provider@2.16.2": version "2.16.2" - resolved "https://registry.npmjs.org/@ant-design/pro-provider/-/pro-provider-2.16.2.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-2.16.2.tgz#3ccec06ea9a69a4d48adc593a268413c813dab2f" integrity sha512-0KmCH1EaOND787Jz6VRMYtLNZmqfT0JPjdUfxhyOxFfnBRfrjyfZgIa6CQoAJLEUMWv57PccWS8wRHVUUk2Yiw== dependencies: "@ant-design/cssinjs" "^1.21.1" @@ -89,7 +89,7 @@ "@ant-design/pro-utils@2.18.0": version "2.18.0" - resolved "https://registry.npmjs.org/@ant-design/pro-utils/-/pro-utils-2.18.0.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-2.18.0.tgz#50a6bcc95742b71a7a17252f7db6c2ccd2ec60f4" integrity sha512-8+ikyrN8L8a8Ph4oeHTOJEiranTj18+9+WHCHjKNdEfukI7Rjn8xpYdLJWb2AUJkb9d4eoAqjd5+k+7w81Df0w== dependencies: "@ant-design/icons" "^5.0.0" @@ -105,7 +105,7 @@ "@ant-design/react-slick@~1.1.2": version "1.1.2" - resolved "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== dependencies: "@babel/runtime" "^7.10.4" @@ -116,7 +116,7 @@ "@apollo/client@^4.0.9": version "4.0.9" - resolved "https://registry.npmjs.org/@apollo/client/-/client-4.0.9.tgz" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.0.9.tgz#fdc0388f73a1640f3da30a8791e448b96ad0b49a" integrity sha512-Lh2drMzFE9x5jVS8RKmlGL5SORkvpyUJIT+wTErxDUR2HpWePiBfhhcHHRSlZFiCR866ewCv4euTc4IDF0GWxw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" @@ -127,32 +127,9 @@ optimism "^0.18.0" tslib "^2.3.0" -"@ardatan/relay-compiler@12.0.0": - version "12.0.0" - resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz" - integrity sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q== - dependencies: - "@babel/core" "^7.14.0" - "@babel/generator" "^7.14.0" - "@babel/parser" "^7.14.0" - "@babel/runtime" "^7.0.0" - "@babel/traverse" "^7.14.0" - "@babel/types" "^7.0.0" - babel-preset-fbjs "^3.4.0" - chalk "^4.0.0" - fb-watchman "^2.0.0" - fbjs "^3.0.0" - glob "^7.1.1" - immutable "~3.7.6" - invariant "^2.2.4" - nullthrows "^1.1.1" - relay-runtime "12.0.0" - signedsource "^1.0.0" - yargs "^15.3.1" - "@ardatan/relay-compiler@^12.0.3": version "12.0.3" - resolved "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz#a60824672da7f7cef2a3879ed833b120d292fa08" integrity sha512-mBDFOGvAoVlWaWqs3hm1AciGHSQE1rqFc/liZTyYz/Oek9yZdT5H26pH2zAFuEiTiBVPPyMuqf5VjOFPI2DGsQ== dependencies: "@babel/generator" "^7.26.10" @@ -168,21 +145,21 @@ "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== dependencies: "@babel/helper-validator-identifier" "^7.27.1" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.27.2": +"@babel/compat-data@^7.27.2": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== -"@babel/core@^7.0", "@babel/core@^7.14.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": +"@babel/core@^7.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== dependencies: "@babel/code-frame" "^7.27.1" @@ -201,9 +178,9 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.26.10", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5": +"@babel/generator@^7.18.13", "@babel/generator@^7.26.10", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== dependencies: "@babel/parser" "^7.28.5" @@ -212,16 +189,9 @@ "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.27.1", "@babel/helper-annotate-as-pure@^7.27.3": - version "7.27.3" - resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz" - integrity sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg== - dependencies: - "@babel/types" "^7.27.3" - -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.27.1", "@babel/helper-compilation-targets@^7.27.2": +"@babel/helper-compilation-targets@^7.27.2": version "7.27.2" - resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== dependencies: "@babel/compat-data" "^7.27.2" @@ -230,435 +200,206 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6": - version "7.28.5" - resolved "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz" - integrity sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.3" - "@babel/helper-member-expression-to-functions" "^7.28.5" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - "@babel/traverse" "^7.28.5" - semver "^6.3.1" - "@babel/helper-globals@^7.28.0": version "7.28.0" - resolved "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-member-expression-to-functions@^7.27.1", "@babel/helper-member-expression-to-functions@^7.28.5": - version "7.28.5" - resolved "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz" - integrity sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg== - dependencies: - "@babel/traverse" "^7.28.5" - "@babel/types" "^7.28.5" - "@babel/helper-module-imports@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: "@babel/traverse" "^7.27.1" "@babel/types" "^7.27.1" -"@babel/helper-module-transforms@^7.27.1", "@babel/helper-module-transforms@^7.28.3": +"@babel/helper-module-transforms@^7.28.3": version "7.28.3" - resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== dependencies: "@babel/helper-module-imports" "^7.27.1" "@babel/helper-validator-identifier" "^7.27.1" "@babel/traverse" "^7.28.3" -"@babel/helper-optimise-call-expression@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz" - integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== - dependencies: - "@babel/types" "^7.27.1" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== -"@babel/helper-replace-supers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz" - integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.27.1" - "@babel/helper-optimise-call-expression" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz" - integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== - dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" - "@babel/helper-string-parser@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== "@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== "@babel/helper-validator-option@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== "@babel/helpers@^7.28.4": version "7.28.4" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== dependencies: "@babel/template" "^7.27.2" "@babel/types" "^7.28.4" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== dependencies: "@babel/types" "^7.28.5" -"@babel/plugin-proposal-class-properties@^7.0.0": - version "7.18.6" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-object-rest-spread@^7.0.0": - version "7.20.7" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-bigint@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-class-properties@^7.0.0", "@babel/plugin-syntax-class-properties@^7.12.13": +"@babel/plugin-syntax-class-properties@^7.12.13": version "7.12.13" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== dependencies: "@babel/helper-plugin-utils" "^7.12.13" "@babel/plugin-syntax-class-static-block@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-flow@^7.0.0", "@babel/plugin-syntax-flow@^7.27.1": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz" - integrity sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-import-assertions@^7.26.0": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-attributes@^7.24.7": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-jsx@^7.0.0", "@babel/plugin-syntax-jsx@^7.27.1": +"@babel/plugin-syntax-jsx@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== dependencies: "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== dependencies: "@babel/helper-plugin-utils" "^7.10.4" "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-numeric-separator@^7.10.4": version "7.10.4" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== dependencies: "@babel/helper-plugin-utils" "^7.10.4" -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": +"@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== dependencies: "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-private-property-in-object@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-top-level-await@^7.14.5": version "7.14.5" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== dependencies: "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.27.1": version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== dependencies: "@babel/helper-plugin-utils" "^7.27.1" -"@babel/plugin-transform-arrow-functions@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz" - integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-block-scoped-functions@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz" - integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-block-scoping@^7.0.0": - version "7.28.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz" - integrity sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-classes@^7.0.0": - version "7.28.4" - resolved "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz" - integrity sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.3" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-globals" "^7.28.0" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - "@babel/traverse" "^7.28.4" - -"@babel/plugin-transform-computed-properties@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz" - integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/template" "^7.27.1" - -"@babel/plugin-transform-destructuring@^7.0.0": - version "7.28.5" - resolved "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz" - integrity sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/traverse" "^7.28.5" - -"@babel/plugin-transform-flow-strip-types@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz" - integrity sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-flow" "^7.27.1" - -"@babel/plugin-transform-for-of@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz" - integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - -"@babel/plugin-transform-function-name@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz" - integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== - dependencies: - "@babel/helper-compilation-targets" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/traverse" "^7.27.1" - -"@babel/plugin-transform-literals@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz" - integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-member-expression-literals@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz" - integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-modules-commonjs@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz" - integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== - dependencies: - "@babel/helper-module-transforms" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-object-super@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz" - integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-replace-supers" "^7.27.1" - -"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7": - version "7.27.7" - resolved "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz" - integrity sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-property-literals@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz" - integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-react-display-name@^7.0.0": - version "7.28.0" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz" - integrity sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-react-jsx@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz" - integrity sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.27.1" - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/types" "^7.27.1" - -"@babel/plugin-transform-shorthand-properties@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz" - integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - -"@babel/plugin-transform-spread@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz" - integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/helper-skip-transparent-expression-wrappers" "^7.27.1" - -"@babel/plugin-transform-template-literals@^7.0.0": - version "7.27.1" - resolved "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz" - integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== - dependencies: - "@babel/helper-plugin-utils" "^7.27.1" - "@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10": version "7.28.4" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.1", "@babel/template@^7.27.2": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.2": version "7.27.2" - resolved "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== dependencies: "@babel/code-frame" "^7.27.1" "@babel/parser" "^7.27.2" "@babel/types" "^7.27.1" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.26.10", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.4", "@babel/traverse@^7.28.5": +"@babel/traverse@^7.26.10", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== dependencies: "@babel/code-frame" "^7.27.1" @@ -671,7 +412,7 @@ "@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5": version "7.28.5" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== dependencies: "@babel/helper-string-parser" "^7.27.1" @@ -679,19 +420,19 @@ "@bcoe/v8-coverage@^0.2.3": version "0.2.3" - resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== "@cspotcode/source-map-support@^0.8.0": version "0.8.1" - resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== dependencies: "@jridgewell/trace-mapping" "0.3.9" "@ctrl/tinycolor@^3.4.0": version "3.6.1" - resolved "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz" + resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== "@emnapi/core@^1.4.3": @@ -718,17 +459,17 @@ "@emotion/hash@^0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== "@emotion/unitless@^0.7.5": version "0.7.5" - resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== "@envelop/core@^5.2.3", "@envelop/core@^5.3.0", "@envelop/core@^5.4.0": version "5.4.0" - resolved "https://registry.npmjs.org/@envelop/core/-/core-5.4.0.tgz" + resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.4.0.tgz#e79b8da5ab4f05eccd9758435df3dc50b11e607d" integrity sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ== dependencies: "@envelop/instrumentation" "^1.0.0" @@ -738,7 +479,7 @@ "@envelop/instrumentation@^1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@envelop/instrumentation/-/instrumentation-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@envelop/instrumentation/-/instrumentation-1.0.0.tgz#43268392e065d8ba975cacbdf4fc297dfe3e11e5" integrity sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw== dependencies: "@whatwg-node/promise-helpers" "^1.2.1" @@ -746,7 +487,7 @@ "@envelop/types@^5.2.1": version "5.2.1" - resolved "https://registry.npmjs.org/@envelop/types/-/types-5.2.1.tgz" + resolved "https://registry.yarnpkg.com/@envelop/types/-/types-5.2.1.tgz#6bc9713f2aea56d7de3ea39e8bb70035c0475b36" integrity sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg== dependencies: "@whatwg-node/promise-helpers" "^1.0.0" @@ -754,19 +495,19 @@ "@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": version "4.9.0" - resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== dependencies: eslint-visitor-keys "^3.4.3" "@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": version "4.12.2" - resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== "@eslint/config-array@^0.21.1": version "0.21.1" - resolved "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== dependencies: "@eslint/object-schema" "^2.1.7" @@ -775,22 +516,22 @@ "@eslint/config-helpers@^0.4.2": version "0.4.2" - resolved "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== dependencies: "@eslint/core" "^0.17.0" "@eslint/core@^0.17.0": version "0.17.0" - resolved "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== dependencies: "@types/json-schema" "^7.0.15" "@eslint/eslintrc@^3.3.1": - version "3.3.1" - resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz" - integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + version "3.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" + integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -798,23 +539,23 @@ globals "^14.0.0" ignore "^5.2.0" import-fresh "^3.2.1" - js-yaml "^4.1.0" + js-yaml "^4.1.1" minimatch "^3.1.2" strip-json-comments "^3.1.1" "@eslint/js@9.39.1": version "9.39.1" - resolved "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== "@eslint/object-schema@^2.1.7": version "2.1.7" - resolved "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== "@eslint/plugin-kit@^0.4.1": version "0.4.1" - resolved "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== dependencies: "@eslint/core" "^0.17.0" @@ -822,36 +563,28 @@ "@fastify/busboy@^3.1.1": version "3.2.0" - resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== -"@graphql-codegen/add@^3.2.1": - version "3.2.3" - resolved "https://registry.npmjs.org/@graphql-codegen/add/-/add-3.2.3.tgz" - integrity sha512-sQOnWpMko4JLeykwyjFTxnhqjd/3NOG2OyMuvK76Wnnwh8DRrNf2VEs2kmSvLl7MndMlOj7Kh5U154dVcvhmKQ== - dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.1" - tslib "~2.4.0" - "@graphql-codegen/add@^6.0.0": version "6.0.0" - resolved "https://registry.npmjs.org/@graphql-codegen/add/-/add-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.0.tgz#d195051d7c4bd6d3955a92361cc9eec7784bb541" integrity sha512-biFdaURX0KTwEJPQ1wkT6BRgNasqgQ5KbCI1a3zwtLtO7XTo7/vKITPylmiU27K5DSOWYnY/1jfSqUAEBuhZrQ== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" tslib "~2.6.0" "@graphql-codegen/cli@^6.0.2": - version "6.0.2" - resolved "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-6.0.2.tgz" - integrity sha512-W+0ime0xMrCyG77q+5xiPkkqPLuXJcTx0Zr9TTOxF4zIqWKVsuImS3qVxtpeTx+GRbb8VWv9IedWMtt91JGzQg== + version "6.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.1.0.tgz#3fa4c16a85863ffe8ee170d47655b4fe4b69de7b" + integrity sha512-7w3Zq5IFONVOBcyOiP01Nv9WRxGS/TEaBCAb/ALYA3xHq95dqKCpoGnxt/Ut9R18jiS+aMgT0gc8Tr8sHy44jA== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/client-preset" "^5.1.2" + "@graphql-codegen/client-preset" "^5.2.0" "@graphql-codegen/core" "^5.0.0" - "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.0" "@graphql-tools/apollo-engine-loader" "^8.0.0" "@graphql-tools/code-file-loader" "^8.0.0" "@graphql-tools/git-loader" "^8.0.0" @@ -881,20 +614,20 @@ yaml "^2.3.1" yargs "^17.0.0" -"@graphql-codegen/client-preset@^5.1.2": - version "5.1.3" - resolved "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-5.1.3.tgz" - integrity sha512-8nlKt8/gO/BovWahLb96taMssHKPibBfslry1ed9DIJtbOrceFYF3yNbFZuTHmI644C7ZvoYK93JkE3VzDlCyg== +"@graphql-codegen/client-preset@^5.2.0": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.2.1.tgz#0a5c13c61da21a30e4d60af05c881be45d8f574b" + integrity sha512-6qFjHQQUWrEH+MVvWs5sPUgme8X+Ivg3WfzaCESooRBQZ4/EnSFlXkPWUTbOKYLRUoMv4g6iTRcZQf6u1wtHZA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/gql-tag-operations" "5.0.5" - "@graphql-codegen/plugin-helpers" "^6.0.0" - "@graphql-codegen/typed-document-node" "^6.1.2" - "@graphql-codegen/typescript" "^5.0.4" - "@graphql-codegen/typescript-operations" "^5.0.4" - "@graphql-codegen/visitor-plugin-common" "^6.1.2" + "@graphql-codegen/gql-tag-operations" "5.1.1" + "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/typed-document-node" "^6.1.4" + "@graphql-codegen/typescript" "^5.0.6" + "@graphql-codegen/typescript-operations" "^5.0.6" + "@graphql-codegen/visitor-plugin-common" "^6.2.1" "@graphql-tools/documents" "^1.0.0" "@graphql-tools/utils" "^10.0.0" "@graphql-typed-document-node/core" "3.2.0" @@ -902,7 +635,7 @@ "@graphql-codegen/core@^5.0.0": version "5.0.0" - resolved "https://registry.npmjs.org/@graphql-codegen/core/-/core-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.0.tgz#c3fd07226a3cd2f75c80896c127adc191f42e179" integrity sha512-vLTEW0m8LbE4xgRwbFwCdYxVkJ1dBlVJbQyLb9Q7bHnVFgHAP982Xo8Uv7FuPBmON+2IbTjkCqhFLHVZbqpvjQ== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" @@ -912,51 +645,39 @@ "@graphql-codegen/fragment-matcher@^6.0.0": version "6.0.0" - resolved "https://registry.npmjs.org/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz#4beb848c0fdb38bbdc7b99250de84ec4456e7a5d" integrity sha512-lXdFWrUSEbOL2KhmYxdeNPLllcBGZnNunwgV72C12+21v9lyKaOqFnYueyt0pT110MGCwUhKTnXzUtV1D5i9Jw== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" tslib "~2.6.0" -"@graphql-codegen/gql-tag-operations@5.0.5": - version "5.0.5" - resolved "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.0.5.tgz" - integrity sha512-DutUBwA3UMOB2AI6O1FDidYw7N0Br4d/ogGrYg6XOeeVuRYigc6i9wX4qiv4ofD34Ujfcfze0U2PI3ZOR33NKw== +"@graphql-codegen/gql-tag-operations@5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.1.1.tgz#4be470728b03fc616c3422068b1fb6138b9af92c" + integrity sha512-XewD0XxN2sgKieEIFeGWV5yT5X2aNy+eg+K8bHlUD7QfyrN2bi67rv/O5Edu7LVDOJR69uqVBp++18d742mn3Q== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - "@graphql-codegen/visitor-plugin-common" "6.1.2" + "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/visitor-plugin-common" "6.2.1" "@graphql-tools/utils" "^10.0.0" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/near-operation-file-preset@^3.1.0": - version "3.1.0" - resolved "https://registry.npmjs.org/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-3.1.0.tgz" - integrity sha512-sXIIi0BPP3IcARdzSztpE51oJTcGB67hi7ddBYfLinks/R/5aFG1Ry/J61707Kt+6Q5WhTnf5XAQUAqi6200yA== +"@graphql-codegen/near-operation-file-preset@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-4.0.0.tgz#79fa9d0a587a54751fa4075d0f85473412c5372a" + integrity sha512-InHE3tN0dZKCZivrQ950y6Tnxtt/DJbvqqm5iCOmExJeIzhLoMOB0Rq2ex4KsRdSY9YtpYbYNCmO2xSU3MbRcg== dependencies: - "@graphql-codegen/add" "^3.2.1" - "@graphql-codegen/plugin-helpers" "^3.0.0" - "@graphql-codegen/visitor-plugin-common" "2.13.8" + "@graphql-codegen/add" "^6.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/visitor-plugin-common" "^6.2.1" "@graphql-tools/utils" "^10.0.0" parse-filepath "^1.0.2" tslib "^2.8.1" -"@graphql-codegen/plugin-helpers@^3.0.0", "@graphql-codegen/plugin-helpers@^3.1.1", "@graphql-codegen/plugin-helpers@^3.1.2": - version "3.1.2" - resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz" - integrity sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg== - dependencies: - "@graphql-tools/utils" "^9.0.0" - change-case-all "1.0.15" - common-tags "1.8.2" - import-from "4.0.0" - lodash "~4.17.0" - tslib "~2.4.0" - -"@graphql-codegen/plugin-helpers@^6.0.0": - version "6.0.0" - resolved "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.0.0.tgz" - integrity sha512-Z7P89vViJvQakRyMbq/JF2iPLruRFOwOB6IXsuSvV/BptuuEd7fsGPuEf8bdjjDxUY0pJZnFN8oC7jIQ8p9GKA== +"@graphql-codegen/plugin-helpers@^6.0.0", "@graphql-codegen/plugin-helpers@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.1.0.tgz#251d76dac2801a4baaef5613b9a22698c1218c47" + integrity sha512-JJypehWTcty9kxKiqH7TQOetkGdOYjY78RHlI+23qB59cV2wxjFFVf8l7kmuXS4cpGVUNfIjFhVr7A1W7JMtdA== dependencies: "@graphql-tools/utils" "^10.0.0" change-case-all "1.0.15" @@ -967,68 +688,52 @@ "@graphql-codegen/schema-ast@^5.0.0": version "5.0.0" - resolved "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz#9708d7484a01bb3a502685126708d7b139fd0721" integrity sha512-jn7Q3PKQc0FxXjbpo9trxzlz/GSFQWxL042l0iC8iSbM/Ar+M7uyBwMtXPsev/3Razk+osQyreghIz0d2+6F7Q== dependencies: "@graphql-codegen/plugin-helpers" "^6.0.0" "@graphql-tools/utils" "^10.0.0" tslib "~2.6.0" -"@graphql-codegen/typed-document-node@^6.1.2": - version "6.1.2" - resolved "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.2.tgz" - integrity sha512-DelLv7BY8Sx0toyCiEsc46W3FtqipiiqhprUnGnSalfKnKVB8KUodXKaf70migy6hWyDl5d1OJOp5wrttuIy2Q== +"@graphql-codegen/typed-document-node@^6.1.4": + version "6.1.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.4.tgz#a7ea8b556e140dfdc969718d8019af6e103b8fe0" + integrity sha512-ITWsA+qvT7R64z7KmYHXfgyD5ff069FAGq/hpR0EWVfzXT4RW1Xn/3Biw7/jvwMGsS1BTjo8ZLSIMNM8KjE3GA== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - "@graphql-codegen/visitor-plugin-common" "6.1.2" + "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/visitor-plugin-common" "6.2.1" auto-bind "~4.0.0" change-case-all "1.0.15" tslib "~2.6.0" -"@graphql-codegen/typescript-operations@^5.0.4": - version "5.0.4" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.4.tgz" - integrity sha512-5Bu/BTmyNjdSfSLLBKjC0+4XWcY01uotVcnVIWIxxRdIHoRxnTW6PUkT5CoPHP5r/Uoo3OvIJxh+0LYSH5suwA== +"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.6.tgz#4ef74afa125f26f7c2f757d8f11fda66aadea6dc" + integrity sha512-pkR/82qWO50OHWeV3BiDuVxNFxiJerpmNjFep71VlabADXiU3GIeSaDd6G9a1/SCniVTXZQk2ivCb0ZJiuwo1A== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - "@graphql-codegen/typescript" "^5.0.4" - "@graphql-codegen/visitor-plugin-common" "6.1.2" + "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/typescript" "^5.0.6" + "@graphql-codegen/visitor-plugin-common" "6.2.1" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/typescript@^5.0.4": - version "5.0.4" - resolved "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-5.0.4.tgz" - integrity sha512-q6S8hX+aR4BzeGgolac4gp22rBnXbLhedmOwT1UBT9e3lGNmNpYC7WJUEzAPjWf6z1lRSNmojLlwEjTnffhKNA== +"@graphql-codegen/typescript@^5.0.4", "@graphql-codegen/typescript@^5.0.6": + version "5.0.6" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.6.tgz#6085b57bd643e0c5f77412018a1784eecedb0146" + integrity sha512-rKW3wYInAnmO/DmKjhW3/KLMxUauUCZuMEPQmuoHChnwIuMjn5kVXCdArGyQqv+vVtFj55aS+sJLN4MPNNjSNg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.0" "@graphql-codegen/schema-ast" "^5.0.0" - "@graphql-codegen/visitor-plugin-common" "6.1.2" + "@graphql-codegen/visitor-plugin-common" "6.2.1" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/visitor-plugin-common@2.13.8": - version "2.13.8" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz" - integrity sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ== +"@graphql-codegen/visitor-plugin-common@6.2.1", "@graphql-codegen/visitor-plugin-common@^6.2.1": + version "6.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.2.1.tgz#76b02057bdbde00eeb852d72bc814d941ba46550" + integrity sha512-5QT1hCV3286mrmoIC7vlFXsTlwELMexhuFIkjh+oVGGL1E8hxkIPAU0kfH/lsPbQHKi8zKmic2pl3tAdyYxNyg== dependencies: - "@graphql-codegen/plugin-helpers" "^3.1.2" - "@graphql-tools/optimize" "^1.3.0" - "@graphql-tools/relay-operation-optimizer" "^6.5.0" - "@graphql-tools/utils" "^9.0.0" - auto-bind "~4.0.0" - change-case-all "1.0.15" - dependency-graph "^0.11.0" - graphql-tag "^2.11.0" - parse-filepath "^1.0.2" - tslib "~2.4.0" - -"@graphql-codegen/visitor-plugin-common@6.1.2", "@graphql-codegen/visitor-plugin-common@^6.1.2": - version "6.1.2" - resolved "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.1.2.tgz" - integrity sha512-zYdrhJKgk8kqE1Xz5/m/Ua42zk+rIvYB/FHh3dE1AhZ6b1IDqgKjF3LnkT+K2qenf9EfT4yNjXd5CEKMeXfHyg== - dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.0" "@graphql-tools/optimize" "^2.0.0" "@graphql-tools/relay-operation-optimizer" "^7.0.0" "@graphql-tools/utils" "^10.0.0" @@ -1041,37 +746,37 @@ "@graphql-hive/signal@^1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@graphql-hive/signal/-/signal-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-1.0.0.tgz#6e2193660a47c925abadbe72293dfc9430e24f8f" integrity sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag== "@graphql-hive/signal@^2.0.0": version "2.0.0" - resolved "https://registry.npmjs.org/@graphql-hive/signal/-/signal-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-2.0.0.tgz#205805328b118e1ae556417ed364257234800464" integrity sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ== "@graphql-tools/apollo-engine-loader@^8.0.0": - version "8.0.26" - resolved "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.26.tgz" - integrity sha512-vNeWX/hGpHMVaDJNBbf7yhmy95ueZ3f4rUjl6v819Emsfo0ItnyTM2FmOPUwpWIm7DpCKCSt134ERy/fgo14rg== + version "8.0.27" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.27.tgz#cd1216e1375415a7b57caa8e39d58d02b96ef98e" + integrity sha512-XT4BvqmRXkVaT8GgNb9/pr8u4M4vTcvGuI2GlvK+albrJNIV8VxTpsdVYma3kw+VtSIYrxEvLixlfDA/KdmDpg== dependencies: - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" "@whatwg-node/fetch" "^0.10.13" sync-fetch "0.6.0-2" tslib "^2.4.0" -"@graphql-tools/batch-execute@^10.0.3": - version "10.0.3" - resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-10.0.3.tgz" - integrity sha512-QH9tKbBQcLeSToXufGKRKWZUpVFJ+gRzJ8wSjz0amQXxpHiF8sEAIJKy9CmgRfnhvt31BjOMTr1sUS4xbhj7lw== +"@graphql-tools/batch-execute@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.4.tgz#1f61115b650ee1734338ed9ff5d0e57dc1cb9b8d" + integrity sha512-t8E0ILelbaIju0aNujMkKetUmbv3/07nxGSv0kEGLBk9GNtEmQ/Bjj8ZTo2WN35/Fy70zCHz2F/48Nx/Ec48cA== dependencies: - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/utils" "^10.10.3" "@whatwg-node/promise-helpers" "^1.3.2" dataloader "^2.2.3" tslib "^2.8.1" "@graphql-tools/batch-execute@^9.0.19": version "9.0.19" - resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz#b724f3542e2cee8a3a71262a711469afa39cae88" integrity sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA== dependencies: "@graphql-tools/utils" "^10.9.1" @@ -1080,19 +785,19 @@ tslib "^2.8.1" "@graphql-tools/code-file-loader@^8.0.0": - version "8.1.26" - resolved "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.26.tgz" - integrity sha512-VamhpBEbrABCjtJqEFBUrHBBVX4Iw7q4Ga8H3W0P7mO+sE1HuTfpWirSdBLlhc6nGcSyTb6FA1mEgGjjUASIHA== + version "8.1.27" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.27.tgz#bbeec7c97c03564b238b7466baea67274cac8f11" + integrity sha512-q3GDbm+7m3DiAnqxa+lYMgYZd49+ez6iGFfXHmzP6qAnf5WlBxRNKNjNVuxOgoV30DCr+vOJfoXeU7VN1qqGWQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.25" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/graphql-tag-pluck" "8.3.26" + "@graphql-tools/utils" "^10.11.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/delegate@^10.2.23": version "10.2.23" - resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.2.23.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-10.2.23.tgz#fcd42e7aa70149269ad644e03098c8d28810bd87" integrity sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w== dependencies: "@graphql-tools/batch-execute" "^9.0.19" @@ -1105,15 +810,15 @@ dset "^3.1.2" tslib "^2.8.1" -"@graphql-tools/delegate@^11.1.2": - version "11.1.2" - resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-11.1.2.tgz" - integrity sha512-C6W4DM+z3nN7OkV08CAuBUmCW+etmfiKzlpx09oEGBWzw61eYQdKlqp1qqeKtYebELam9LkHTa1n8/rilv5PXw== +"@graphql-tools/delegate@^12.0.2": + version "12.0.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.2.tgz#9f609918bc41b4e36a3a88cf2f9a21f71220bf72" + integrity sha512-1X93onxNgOzRvnZ8Xulwi6gNuBeuDxvGYOjUHEZyesPCsaWsyiVj1Wk6Pw/DTPGLy70sOFUKQGcaZbWnDORM2w== dependencies: - "@graphql-tools/batch-execute" "^10.0.3" - "@graphql-tools/executor" "^1.4.11" - "@graphql-tools/schema" "^10.0.27" - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/batch-execute" "^10.0.4" + "@graphql-tools/executor" "^1.4.13" + "@graphql-tools/schema" "^10.0.29" + "@graphql-tools/utils" "^10.10.3" "@repeaterjs/repeater" "^3.0.6" "@whatwg-node/promise-helpers" "^1.3.2" dataloader "^2.2.3" @@ -1121,7 +826,7 @@ "@graphql-tools/documents@^1.0.0": version "1.0.1" - resolved "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/documents/-/documents-1.0.1.tgz#ae19cd5667d22c23b331d3a1429443ed7130faee" integrity sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA== dependencies: lodash.sortby "^4.7.0" @@ -1129,7 +834,7 @@ "@graphql-tools/executor-common@^0.0.4": version "0.0.4" - resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz#763603a6d7a22bb09d67ce682e84a0d730ff2bf9" integrity sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q== dependencies: "@envelop/core" "^5.2.3" @@ -1137,23 +842,23 @@ "@graphql-tools/executor-common@^0.0.6": version "0.0.6" - resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz#f681de443d7e90e1768c44bd64a4cd418dc356e3" integrity sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow== dependencies: "@envelop/core" "^5.3.0" "@graphql-tools/utils" "^10.9.1" -"@graphql-tools/executor-common@^1.0.4": - version "1.0.4" - resolved "https://registry.npmjs.org/@graphql-tools/executor-common/-/executor-common-1.0.4.tgz" - integrity sha512-EKkVjo1Fbx+qK5ZeI+KneeoGzk6HU6aL4E0bX4J3lfnjyaZVESbODCDS/9bBcrV1X2dEby9MfWqA6fkFodP+hw== +"@graphql-tools/executor-common@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-1.0.5.tgz#cecd704fb9d84d40af80ee8fc7e6962f96f9fcb9" + integrity sha512-gsBRxP4ui8s7/ppKGCJUQ9xxTNoFpNYmEirgM52EHo74hL5hrpS5o4zOmBH33+9t2ZasBziIfupYtLNa0DgK0g== dependencies: "@envelop/core" "^5.4.0" - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/utils" "^10.10.3" "@graphql-tools/executor-graphql-ws@^2.0.1": version "2.0.7" - resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz#e8d7689d266b3ccea962b708d6f5d31a1a246c66" integrity sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q== dependencies: "@graphql-tools/executor-common" "^0.0.6" @@ -1165,12 +870,12 @@ ws "^8.18.3" "@graphql-tools/executor-graphql-ws@^3.1.2": - version "3.1.2" - resolved "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.2.tgz" - integrity sha512-nlBXYrWkZ+VFIVU2QrRNnWImBwZglY/uuyK0/dkFMFy9FEjZmErLpGv62PZqQUSEjPakNgUil+T94CUHGebR9w== + version "3.1.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.3.tgz#aac6605564f0e7c2f72611487c48960bd445bed3" + integrity sha512-q4k8KLoH2U51XdWJRdiW/KIKbBOtJ1mcILv0ALvBkOF99C3vwGj2zr4U0AMGCD3HzML2mPZuajhfYo/xB/pnZQ== dependencies: - "@graphql-tools/executor-common" "^1.0.4" - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/executor-common" "^1.0.5" + "@graphql-tools/utils" "^10.10.3" "@whatwg-node/disposablestack" "^0.0.6" graphql-ws "^6.0.6" isows "^1.0.7" @@ -1179,7 +884,7 @@ "@graphql-tools/executor-http@^1.1.9": version "1.3.3" - resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz#057de79ebc90edbd242259cccb1268d37ee6c579" integrity sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A== dependencies: "@graphql-hive/signal" "^1.0.0" @@ -1193,37 +898,37 @@ tslib "^2.8.1" "@graphql-tools/executor-http@^3.0.6": - version "3.0.6" - resolved "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-3.0.6.tgz" - integrity sha512-gTcSdaJWbqYKg/aVQ/T+sIC6Isa4jjx+QhVMyawgivmdbLTg0UgsbR6g97eNVoxnRzFq/CuPSy71IHQeq8Q6iA== + version "3.0.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.0.7.tgz#aaf1ed4db37a97c447dee1842099d71173977ad9" + integrity sha512-sHjtiUZmRtkjhpSzMhxT2ywAGzHjuB1rHsiaSLAq8U5BQg5WoLakKYD7BajgVHwNbfWEc+NnFiJI7ldyhiciiQ== dependencies: "@graphql-hive/signal" "^2.0.0" - "@graphql-tools/executor-common" "^1.0.4" - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/executor-common" "^1.0.5" + "@graphql-tools/utils" "^10.10.3" "@repeaterjs/repeater" "^3.0.4" "@whatwg-node/disposablestack" "^0.0.6" - "@whatwg-node/fetch" "^0.10.12" + "@whatwg-node/fetch" "^0.10.13" "@whatwg-node/promise-helpers" "^1.3.2" meros "^1.3.2" tslib "^2.8.1" -"@graphql-tools/executor-legacy-ws@^1.1.19", "@graphql-tools/executor-legacy-ws@^1.1.23": - version "1.1.23" - resolved "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.23.tgz" - integrity sha512-wwS6ZlJDaC+zxE1DcfYrPJk1ynQ0xcbOWS/x8dy4hO6ZCjRawkogoqN3Muab0E9RzuwF29LRu+aOH6isO5mQKg== +"@graphql-tools/executor-legacy-ws@^1.1.19", "@graphql-tools/executor-legacy-ws@^1.1.24": + version "1.1.24" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.24.tgz#b31c13951b393b461bf0e653fb770b5c87aa9dee" + integrity sha512-wfSpOJCxeBcwVXy3JS4TB4oLwVICuVKPlPQhcAjTRPWYwKerE0HosgUzxCX1fEQ4l1B1OMgKWRglGpoXExKqsQ== dependencies: - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" "@types/ws" "^8.0.0" isomorphic-ws "^5.0.0" tslib "^2.4.0" ws "^8.17.1" -"@graphql-tools/executor@^1.4.11", "@graphql-tools/executor@^1.4.9": - version "1.4.13" - resolved "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.4.13.tgz" - integrity sha512-2hTSRfH2kb4ua0ANOV/K6xUoCZsHAE6igE1bimtWUK7v0bowPIxGRKRPpF8JLbImpsJuTCC4HGOCMy7otg3FIQ== +"@graphql-tools/executor@^1.4.13", "@graphql-tools/executor@^1.4.9": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.5.0.tgz#5aca9a7af96f7edcb4659306428d7d970fd0e843" + integrity sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA== dependencies: - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" "@graphql-typed-document-node/core" "^3.2.0" "@repeaterjs/repeater" "^3.0.4" "@whatwg-node/disposablestack" "^0.0.6" @@ -1231,136 +936,120 @@ tslib "^2.4.0" "@graphql-tools/git-loader@^8.0.0": - version "8.0.30" - resolved "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.30.tgz" - integrity sha512-Rx3rphMFBKrILkCrjB6k5JF2z+3XYEnc5wUOw2rMxFAHBHeZjBfA6TDLUtd8EKnaf6h5y0pKpb+uZ72EYJaH2A== + version "8.0.31" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.31.tgz#d6ac1d30316fb3049afbebb2e67f01b3282a2a15" + integrity sha512-xVHM1JecjpU2P0aOj/IaIUc3w6It8sWOdrJElWFZdY9yfWRqXFYwfemtsn/JOrJDIJXYeGpJ304OeqJD5vFIEw== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.25" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/graphql-tag-pluck" "8.3.26" + "@graphql-tools/utils" "^10.11.0" is-glob "4.0.3" micromatch "^4.0.8" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/github-loader@^9.0.0": - version "9.0.4" - resolved "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-9.0.4.tgz" - integrity sha512-w8RRhOTS1Qn2/MUWO2ogtLok6/0p15IdAoYfkCW7duCTPWWG/blCtiYNDyrOZahjCuL4WrxL9+JZfj+6nXSpIQ== + version "9.0.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.5.tgz#b9e05e2819fc716fcc2ffd61a2ae687d2806d0fa" + integrity sha512-89FRDQGMlzL3607BCQtJhKEiQaZtTmdAnyC5Hmi9giTQXVzEXBbMEZOU0qILxj64cr+smNBx5XqxQ1xn0uZeEA== dependencies: "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/graphql-tag-pluck" "^8.3.25" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/graphql-tag-pluck" "^8.3.26" + "@graphql-tools/utils" "^10.11.0" "@whatwg-node/fetch" "^0.10.13" "@whatwg-node/promise-helpers" "^1.0.0" sync-fetch "0.6.0-2" tslib "^2.4.0" "@graphql-tools/graphql-file-loader@^8.0.0": - version "8.1.6" - resolved "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.6.tgz" - integrity sha512-BwK9kUrRbv9w8WMFUedSkPogGdQivopRSRs1aqT9FUD1J3iVLltEHMUx9KTnpPHO7OcULSflMqQs5ZI/3cW+7Q== + version "8.1.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.8.tgz#796eb75be354973d4f524f262d827faa0185741a" + integrity sha512-dZi9Cw+NWEzJAqzIUON9qjZfjebjcoT4H6jqLkEoAv6kRtTq52m4BLXgFWjMHU7PNLE9OOHB9St7UeZQL+GYrw== dependencies: - "@graphql-tools/import" "7.1.6" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/import" "7.1.8" + "@graphql-tools/utils" "^10.11.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@8.3.25", "@graphql-tools/graphql-tag-pluck@^8.3.25": - version "8.3.25" - resolved "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.25.tgz" - integrity sha512-b8oTBe0mDQDh3zPcKCkaTPmjLv1TJslBUKXPNLfu5CWS2+gL8Z/z0UuAhCe5gTveuKDJYjkEO7xcct9JfcDi4g== +"@graphql-tools/graphql-tag-pluck@8.3.26", "@graphql-tools/graphql-tag-pluck@^8.3.26": + version "8.3.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.26.tgz#79f9add9cd8d3001e87c60795d6df73258afd47a" + integrity sha512-hLsX++KA3YR/PnNJGBq1weSAY8XUUAQFfOSHanLHA2qs5lcNgU6KWbiLiRsJ/B/ZNi2ZO687dhzeZ4h4Yt0V6Q== dependencies: "@babel/core" "^7.26.10" "@babel/parser" "^7.26.10" "@babel/plugin-syntax-import-assertions" "^7.26.0" "@babel/traverse" "^7.26.10" "@babel/types" "^7.26.10" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" tslib "^2.4.0" -"@graphql-tools/import@7.1.6": - version "7.1.6" - resolved "https://registry.npmjs.org/@graphql-tools/import/-/import-7.1.6.tgz" - integrity sha512-aROsZq/ApWiAWE9k/xfYiKFUQiw81958e3DvL+HqGtbFQq+F11hIi0PgwOEiEereJZHIuVYZmIfbdQd4gFq4+w== +"@graphql-tools/import@7.1.8": + version "7.1.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.8.tgz#7a78c6292b08d8bb795dd63657f1075de3a3408c" + integrity sha512-aUKHMbaeHhCkS867mNCk9sJuvd9xE3Ocr+alwdvILkDxHf7Xaumx4mK8tN9FAXeKhQWGGD5QpkIBnUzt2xoX/A== dependencies: - "@graphql-tools/utils" "^10.10.3" - "@theguild/federation-composition" "^0.20.2" + "@graphql-tools/utils" "^10.11.0" + "@theguild/federation-composition" "^0.21.0" resolve-from "5.0.0" tslib "^2.4.0" "@graphql-tools/json-file-loader@^8.0.0": - version "8.0.24" - resolved "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.24.tgz" - integrity sha512-S2XKpUVAzY84hniaMpv6V0of2joPfLPaeY4XXUQZfi4w0uiPSzHWA/EnXohgojJsQLgN+FP4dXFfp2yA5GG6VA== + version "8.0.25" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.25.tgz#2688a5db08a08bf009adce9cfd2f1907d243fe0f" + integrity sha512-Dnr9z818Kdn3rfoZO/+/ZQUqWavjV7AhEp4edV1mGsX+J1HFkNC3WMl6MD3W0hth2HWLQpCFJDdOPnchxnFNfA== dependencies: - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/load@^8.1.0": - version "8.1.6" - resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-8.1.6.tgz" - integrity sha512-/bUYqGdB2Y6BflW42IjmauBDzxjec3LQmVAuImVGeiOqw1Rca/DDP7KRQe3vEv8yf/xMVj/PkIl+YRjoo12YxA== + version "8.1.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.7.tgz#43386396c6fc0bb5aa7fc6d259e266bafeb79103" + integrity sha512-RxrHOC4vVI50+Q1mwgpmTVCB/UDDYVEGD/g/hP3tT2BW9F3rJ7Z3Lmt/nGfPQuWPao3w6vgJ9oSAWtism7CU5w== dependencies: - "@graphql-tools/schema" "^10.0.29" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/schema" "^10.0.30" + "@graphql-tools/utils" "^10.11.0" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.1.5": - version "9.1.5" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.1.5.tgz" - integrity sha512-eVcir6nCcOC/Wzv7ZAng3xec3dj6FehE8+h9TvgvUyrDEKVMdFfrO6etRFZ2hucWVcY8S6drx7zQx04N4lPM8Q== - dependencies: - "@graphql-tools/utils" "^10.10.3" - tslib "^2.4.0" - -"@graphql-tools/optimize@^1.3.0": - version "1.4.0" - resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.4.0.tgz" - integrity sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw== +"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.1.6": + version "9.1.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.6.tgz#95eb8bb5ae314bc07a62938b7a1b8c2622e1dc32" + integrity sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw== dependencies: + "@graphql-tools/utils" "^10.11.0" tslib "^2.4.0" "@graphql-tools/optimize@^2.0.0": version "2.0.0" - resolved "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-2.0.0.tgz#7a9779d180824511248a50c5a241eff6e7a2d906" integrity sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg== dependencies: tslib "^2.4.0" -"@graphql-tools/relay-operation-optimizer@^6.5.0": - version "6.5.18" - resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz" - integrity sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg== - dependencies: - "@ardatan/relay-compiler" "12.0.0" - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - "@graphql-tools/relay-operation-optimizer@^7.0.0": - version "7.0.25" - resolved "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.25.tgz" - integrity sha512-1S7qq9eyO6ygPNWX2lZd+oxbpl63OhnTTw8+t5OWprM2Tzws9HEosLUpsMR85z1gbezeKtUDt9a2bsSyu4MMFg== + version "7.0.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.26.tgz#309ffed9e63038db97db15e7adae667571442af9" + integrity sha512-cVdS2Hw4hg/WgPVV2wRIzZM975pW5k4vdih3hR4SvEDQVr6MmozmlTQSqzMyi9yg8LKTq540Oz3bYQa286yGmg== dependencies: "@ardatan/relay-compiler" "^12.0.3" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^10.11.0" tslib "^2.4.0" -"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.25", "@graphql-tools/schema@^10.0.27", "@graphql-tools/schema@^10.0.29": - version "10.0.29" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.29.tgz" - integrity sha512-+Htiupnq6U/AWOEAJerIOGT1pAf4u43Q3n2JmFpqFfYJchz6sKWZ7L9Lpe/NusaaUQty/IOF+eQlNFypEaWxhg== +"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.25", "@graphql-tools/schema@^10.0.29", "@graphql-tools/schema@^10.0.30": + version "10.0.30" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.30.tgz#cdc9583e70a297d53ff0b344935bd81e3bf7d7ac" + integrity sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA== dependencies: - "@graphql-tools/merge" "^9.1.5" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/merge" "^9.1.6" + "@graphql-tools/utils" "^10.11.0" tslib "^2.4.0" "@graphql-tools/url-loader@^8.0.0": version "8.0.33" - resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz#1e5ebfa6a840e3bdc25336c231df2889028f7e0c" integrity sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw== dependencies: "@graphql-tools/executor-graphql-ws" "^2.0.1" @@ -1377,14 +1066,14 @@ ws "^8.17.1" "@graphql-tools/url-loader@^9.0.0": - version "9.0.4" - resolved "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-9.0.4.tgz" - integrity sha512-UNLU9g3IcZjldb08hsBLp6G8YPzZwC38bZ8PFFs/QyXXPFXzoXECT8rHLzvt32w3YFGSbTpj8FiS/KMdTQHZCg== + version "9.0.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.5.tgz#49ac043c4758ee521d6400a5c9f11eb4c9dcf1f7" + integrity sha512-EPNhZBBL48TudLdyenOw1wV9dI7vsinWLLxSTtkx4zUQxmU+p/LxMyf7MUwjmp3yFZhR/9XchsTZX6uvOyXWqA== dependencies: "@graphql-tools/executor-graphql-ws" "^3.1.2" "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/executor-legacy-ws" "^1.1.23" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/executor-legacy-ws" "^1.1.24" + "@graphql-tools/utils" "^10.11.0" "@graphql-tools/wrap" "^11.0.0" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.10.13" @@ -1394,27 +1083,19 @@ tslib "^2.4.0" ws "^8.17.1" -"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.10.1", "@graphql-tools/utils@^10.10.3", "@graphql-tools/utils@^10.8.1", "@graphql-tools/utils@^10.9.1": - version "10.10.3" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.10.3.tgz" - integrity sha512-2EdYiefeLLxsoeZTukSNZJ0E/Z5NnWBUGK2VJa0DQj1scDhVd93HeT1eW9TszJOYmIh3eWAKLv58ri/1XUmdsQ== +"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.10.3", "@graphql-tools/utils@^10.11.0", "@graphql-tools/utils@^10.8.1", "@graphql-tools/utils@^10.9.1": + version "10.11.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.11.0.tgz#2513dca9ca25bab5a2651da2d66c37b1e2616bdf" + integrity sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@whatwg-node/promise-helpers" "^1.0.0" cross-inspect "1.0.1" tslib "^2.4.0" -"@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.2.1": - version "9.2.1" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz" - integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - tslib "^2.4.0" - "@graphql-tools/wrap@^10.0.16": version "10.1.4" - resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.1.4.tgz" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-10.1.4.tgz#46f94b00113693d4d4eccddcf262494164234ba6" integrity sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg== dependencies: "@graphql-tools/delegate" "^10.2.23" @@ -1424,29 +1105,29 @@ tslib "^2.8.1" "@graphql-tools/wrap@^11.0.0": - version "11.0.4" - resolved "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-11.0.4.tgz" - integrity sha512-bX1X2QC1oFiH+1nTLhWVx4fp2xoXbDvL+eEiozuWCv7PeZoBs7EgklKZFtpWbcSdNA3L3EuA8JM6mf2jOG6/Sg== + version "11.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.2.tgz#abd122104d90cf0e00418edb8fc2093f6d95e9a6" + integrity sha512-TcKZzUzJNmuyMBQ1oMdnxhBUUacN/5VEJu0/1KVce2aIzCwTTaN9JTU3MgjO7l5Ixn4QLkc6XbxYNv0cHDQgtQ== dependencies: - "@graphql-tools/delegate" "^11.1.2" - "@graphql-tools/schema" "^10.0.27" - "@graphql-tools/utils" "^10.10.1" + "@graphql-tools/delegate" "^12.0.2" + "@graphql-tools/schema" "^10.0.29" + "@graphql-tools/utils" "^10.10.3" "@whatwg-node/promise-helpers" "^1.3.2" tslib "^2.8.1" "@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" - resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== "@humanfs/core@^0.19.1": version "0.19.1" - resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== "@humanfs/node@^0.16.6": version "0.16.7" - resolved "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== dependencies: "@humanfs/core" "^0.19.1" @@ -1454,17 +1135,17 @@ "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== "@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": version "0.4.3" - resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@img/colour@^1.0.0": version "1.0.0" - resolved "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== "@img/sharp-darwin-arm64@0.34.5": @@ -1518,7 +1199,7 @@ "@img/sharp-libvips-linux-x64@1.2.4": version "1.2.4" - resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz#df4183e8bd8410f7d61b66859a35edeab0a531ce" integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw== "@img/sharp-libvips-linuxmusl-arm64@1.2.4": @@ -1568,7 +1249,7 @@ "@img/sharp-linux-x64@0.34.5": version "0.34.5" - resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz" + resolved "https://registry.yarnpkg.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz#55abc7cd754ffca5002b6c2b719abdfc846819a8" integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ== optionalDependencies: "@img/sharp-libvips-linux-x64" "1.2.4" @@ -1611,12 +1292,12 @@ "@inquirer/ansi@^1.0.2": version "1.0.2" - resolved "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.2.tgz#674a4c4d81ad460695cb2a1fc69d78cd187f337e" integrity sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ== "@inquirer/checkbox@^4.3.2": version "4.3.2" - resolved "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz#e1483e6519d6ffef97281a54d2a5baa0d81b3f3b" integrity sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1627,7 +1308,7 @@ "@inquirer/confirm@^5.1.21": version "5.1.21" - resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.21.tgz#610c4acd7797d94890a6e2dde2c98eb1e891dd12" integrity sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ== dependencies: "@inquirer/core" "^10.3.2" @@ -1635,7 +1316,7 @@ "@inquirer/core@^10.3.2": version "10.3.2" - resolved "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.2.tgz#535979ff3ff4fe1e7cc4f83e2320504c743b7e20" integrity sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1649,7 +1330,7 @@ "@inquirer/editor@^4.2.23": version "4.2.23" - resolved "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-4.2.23.tgz#fe046a3bfdae931262de98c1052437d794322e0b" integrity sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ== dependencies: "@inquirer/core" "^10.3.2" @@ -1658,7 +1339,7 @@ "@inquirer/expand@^4.0.23": version "4.0.23" - resolved "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-4.0.23.tgz#a38b5f32226d75717c370bdfed792313b92bdc05" integrity sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew== dependencies: "@inquirer/core" "^10.3.2" @@ -1667,7 +1348,7 @@ "@inquirer/external-editor@^1.0.3": version "1.0.3" - resolved "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz#c23988291ee676290fdab3fd306e64010a6d13b8" integrity sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA== dependencies: chardet "^2.1.1" @@ -1675,12 +1356,12 @@ "@inquirer/figures@^1.0.15": version "1.0.15" - resolved "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a" integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g== "@inquirer/input@^4.3.1": version "4.3.1" - resolved "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-4.3.1.tgz#778683b4c4c4d95d05d4b05c4a854964b73565b4" integrity sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g== dependencies: "@inquirer/core" "^10.3.2" @@ -1688,7 +1369,7 @@ "@inquirer/number@^3.0.23": version "3.0.23" - resolved "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-3.0.23.tgz#3fdec2540d642093fd7526818fd8d4bdc7335094" integrity sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg== dependencies: "@inquirer/core" "^10.3.2" @@ -1696,7 +1377,7 @@ "@inquirer/password@^4.0.23": version "4.0.23" - resolved "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-4.0.23.tgz#b9f5187c8c92fd7aa9eceb9d8f2ead0d7e7b000d" integrity sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1705,7 +1386,7 @@ "@inquirer/prompts@^7.8.2": version "7.10.1" - resolved "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-7.10.1.tgz#e1436c0484cf04c22548c74e2cd239e989d5f847" integrity sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg== dependencies: "@inquirer/checkbox" "^4.3.2" @@ -1721,7 +1402,7 @@ "@inquirer/rawlist@^4.1.11": version "4.1.11" - resolved "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz#313c8c3ffccb7d41e990c606465726b4a898a033" integrity sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw== dependencies: "@inquirer/core" "^10.3.2" @@ -1730,7 +1411,7 @@ "@inquirer/search@^3.2.2": version "3.2.2" - resolved "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-3.2.2.tgz#4cc6fd574dcd434e4399badc37c742c3fd534ac8" integrity sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA== dependencies: "@inquirer/core" "^10.3.2" @@ -1740,7 +1421,7 @@ "@inquirer/select@^4.4.2": version "4.4.2" - resolved "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-4.4.2.tgz#2ac8fca960913f18f1d1b35323ed8fcd27d89323" integrity sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w== dependencies: "@inquirer/ansi" "^1.0.2" @@ -1751,12 +1432,12 @@ "@inquirer/type@^3.0.10": version "3.0.10" - resolved "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.10.tgz#11ed564ec78432a200ea2601a212d24af8150d50" integrity sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA== "@isaacs/cliui@^8.0.2": version "8.0.2" - resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" @@ -1768,7 +1449,7 @@ "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" - resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== dependencies: camelcase "^5.3.1" @@ -1779,12 +1460,12 @@ "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": version "0.1.3" - resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== "@jest/console@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/console/-/console-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.2.0.tgz#c52fcd5b58fdd2e8eb66b2fd8ae56f2f64d05b28" integrity sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ== dependencies: "@jest/types" "30.2.0" @@ -1796,7 +1477,7 @@ "@jest/core@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/core/-/core-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.2.0.tgz#813d59faa5abd5510964a8b3a7b17cc77b775275" integrity sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ== dependencies: "@jest/console" "30.2.0" @@ -1830,12 +1511,12 @@ "@jest/diff-sequences@30.0.1": version "30.0.1" - resolved "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz" + resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== "@jest/environment@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.2.0.tgz#1e673cdb8b93ded707cf6631b8353011460831fa" integrity sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g== dependencies: "@jest/fake-timers" "30.2.0" @@ -1845,14 +1526,14 @@ "@jest/expect-utils@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== dependencies: "@jest/get-type" "30.1.0" "@jest/expect@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.2.0.tgz#9a5968499bb8add2bbb09136f69f7df5ddbf3185" integrity sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA== dependencies: expect "30.2.0" @@ -1860,7 +1541,7 @@ "@jest/fake-timers@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz#0941ddc28a339b9819542495b5408622dc9e94ec" integrity sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw== dependencies: "@jest/types" "30.2.0" @@ -1872,12 +1553,12 @@ "@jest/get-type@30.1.0": version "30.1.0" - resolved "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== "@jest/globals@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.2.0.tgz#2f4b696d5862664b89c4ee2e49ae24d2bb7e0988" integrity sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw== dependencies: "@jest/environment" "30.2.0" @@ -1887,7 +1568,7 @@ "@jest/pattern@30.0.1": version "30.0.1" - resolved "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz" + resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== dependencies: "@types/node" "*" @@ -1895,7 +1576,7 @@ "@jest/reporters@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.2.0.tgz#a36b28fcbaf0c4595250b108e6f20e363348fd91" integrity sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" @@ -1924,14 +1605,14 @@ "@jest/schemas@30.0.5": version "30.0.5" - resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== dependencies: "@sinclair/typebox" "^0.34.0" "@jest/snapshot-utils@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz#387858eb90c2f98f67bff327435a532ac5309fbe" integrity sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug== dependencies: "@jest/types" "30.2.0" @@ -1941,7 +1622,7 @@ "@jest/source-map@30.0.1": version "30.0.1" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-30.0.1.tgz#305ebec50468f13e658b3d5c26f85107a5620aaa" integrity sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== dependencies: "@jridgewell/trace-mapping" "^0.3.25" @@ -1950,7 +1631,7 @@ "@jest/test-result@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.2.0.tgz#9c0124377fb7996cdffb86eda3dbc56eacab363d" integrity sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg== dependencies: "@jest/console" "30.2.0" @@ -1960,7 +1641,7 @@ "@jest/test-sequencer@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz#bf0066bc72e176d58f5dfa7f212b6e7eee44f221" integrity sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q== dependencies: "@jest/test-result" "30.2.0" @@ -1970,7 +1651,7 @@ "@jest/transform@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== dependencies: "@babel/core" "^7.27.4" @@ -1991,7 +1672,7 @@ "@jest/types@30.2.0": version "30.2.0" - resolved "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== dependencies: "@jest/pattern" "30.0.1" @@ -2004,7 +1685,7 @@ "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" - resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" @@ -2012,7 +1693,7 @@ "@jridgewell/remapping@^2.3.5": version "2.3.5" - resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== dependencies: "@jridgewell/gen-mapping" "^0.3.5" @@ -2020,17 +1701,17 @@ "@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" - resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.5" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@0.3.9": version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== dependencies: "@jridgewell/resolve-uri" "^3.0.3" @@ -2038,7 +1719,7 @@ "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" @@ -2053,61 +1734,61 @@ "@emnapi/runtime" "^1.4.3" "@tybys/wasm-util" "^0.10.0" -"@next/env@16.0.3": - version "16.0.3" - resolved "https://registry.npmjs.org/@next/env/-/env-16.0.3.tgz" - integrity sha512-IqgtY5Vwsm14mm/nmQaRMmywCU+yyMIYfk3/MHZ2ZTJvwVbBn3usZnjMi1GacrMVzVcAxJShTCpZlPs26EdEjQ== +"@next/env@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.0.8.tgz#6167b208ffde10fee64230e09daff387f3f451fd" + integrity sha512-xP4WrQZuj9MdmLJy3eWFHepo+R3vznsMSS8Dy3wdA7FKpjCiesQ6DxZvdGziQisj0tEtCgBKJzjcAc4yZOgLEQ== -"@next/eslint-plugin-next@16.0.3": - version "16.0.3" - resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.3.tgz" - integrity sha512-6sPWmZetzFWMsz7Dhuxsdmbu3fK+/AxKRtj7OB0/3OZAI2MHB/v2FeYh271LZ9abvnM1WIwWc/5umYjx0jo5sQ== +"@next/eslint-plugin-next@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.8.tgz#62018f7f9eb3eda013f72a67f0805bda51360eb0" + integrity sha512-1miV0qXDcLUaOdHridVPCh4i39ElRIAraseVIbb3BEqyZ5ol9sPyjTP/GNTPV5rBxqxjF6/vv5zQTVbhiNaLqA== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.3.tgz#262b2d091d7bef43f1fbdb636d23e23628a76727" - integrity sha512-MOnbd92+OByu0p6QBAzq1ahVWzF6nyfiH07dQDez4/Nku7G249NjxDVyEfVhz8WkLiOEU+KFVnqtgcsfP2nLXg== - -"@next/swc-darwin-x64@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.3.tgz#3fe4fce190c99c0e11cf6306ed2951466abcbd7d" - integrity sha512-i70C4O1VmbTivYdRlk+5lj9xRc2BlK3oUikt3yJeHT1unL4LsNtN7UiOhVanFdc7vDAgZn1tV/9mQwMkWOJvHg== - -"@next/swc-linux-arm64-gnu@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.3.tgz#d58d8db100ae4210ac88a431d85926f00523a490" - integrity sha512-O88gCZ95sScwD00mn/AtalyCoykhhlokxH/wi1huFK+rmiP5LAYVs/i2ruk7xST6SuXN4NI5y4Xf5vepb2jf6A== - -"@next/swc-linux-arm64-musl@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.3.tgz#9654ee5da32f0d29f1f9f9ad56924f30d3225449" - integrity sha512-CEErFt78S/zYXzFIiv18iQCbRbLgBluS8z1TNDQoyPi8/Jr5qhR3e8XHAIxVxPBjDbEMITprqELVc5KTfFj0gg== - -"@next/swc-linux-x64-gnu@16.0.3": - version "16.0.3" - resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.3.tgz" - integrity sha512-Tc3i+nwt6mQ+Dwzcri/WNDj56iWdycGVh5YwwklleClzPzz7UpfaMw1ci7bLl6GRYMXhWDBfe707EXNjKtiswQ== - -"@next/swc-linux-x64-musl@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.3.tgz#0e688deb07e25f2bf85ad4e712c2829f32ea1556" - integrity sha512-zTh03Z/5PBBPdTurgEtr6nY0vI9KR9Ifp/jZCcHlODzwVOEKcKRBtQIGrkc7izFgOMuXDEJBmirwpGqdM/ZixA== - -"@next/swc-win32-arm64-msvc@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.3.tgz#805e4f7dff4169e756c585c98924e7d73a325818" - integrity sha512-Jc1EHxtZovcJcg5zU43X3tuqzl/sS+CmLgjRP28ZT4vk869Ncm2NoF8qSTaL99gh6uOzgM99Shct06pSO6kA6g== - -"@next/swc-win32-x64-msvc@16.0.3": - version "16.0.3" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.3.tgz#e77575eef8e243b31ae12303086aeab16abccbc0" - integrity sha512-N7EJ6zbxgIYpI/sWNzpVKRMbfEGgsWuOIvzkML7wxAAZhPk1Msxuo/JDu1PKjWGrAoOLaZcIX5s+/pF5LIbBBg== +"@next/swc-darwin-arm64@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.8.tgz#0b28316ea33542365d3a7770c548910992b29cae" + integrity sha512-yjVMvTQN21ZHOclQnhSFbjBTEizle+1uo4NV6L4rtS9WO3nfjaeJYw+H91G+nEf3Ef43TaEZvY5mPWfB/De7tA== + +"@next/swc-darwin-x64@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.8.tgz#51139ce4b2fca924c1df48b0a0b33a7ba6641ed2" + integrity sha512-+zu2N3QQ0ZOb6RyqQKfcu/pn0UPGmg+mUDqpAAEviAcEVEYgDckemOpiMRsBP3IsEKpcoKuNzekDcPczEeEIzA== + +"@next/swc-linux-arm64-gnu@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.8.tgz#323625ec532c119495fbea6301c29c00fa25b767" + integrity sha512-LConttk+BeD0e6RG0jGEP9GfvdaBVMYsLJ5aDDweKiJVVCu6sGvo+Ohz9nQhvj7EQDVVRJMCGhl19DmJwGr6bQ== + +"@next/swc-linux-arm64-musl@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.8.tgz#19a988df87f26390233cbe8eb793edde7157c84b" + integrity sha512-JaXFAlqn8fJV+GhhA9lpg6da/NCN/v9ub98n3HoayoUSPOVdoxEEt86iT58jXqQCs/R3dv5ZnxGkW8aF4obMrQ== + +"@next/swc-linux-x64-gnu@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.8.tgz#71dc3516fab7ca793bf0fd424c638280175afcc1" + integrity sha512-O7M9it6HyNhsJp3HNAsJoHk5BUsfj7hRshfptpGcVsPZ1u0KQ/oVy8oxF7tlwxA5tR43VUP0yRmAGm1us514ng== + +"@next/swc-linux-x64-musl@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.8.tgz#6878cbd85eee73dbd18b3e34791e4e3cc9dc5f43" + integrity sha512-8+KClEC/GLI2dLYcrWwHu5JyC5cZYCFnccVIvmxpo6K+XQt4qzqM5L4coofNDZYkct/VCCyJWGbZZDsg6w6LFA== + +"@next/swc-win32-arm64-msvc@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.8.tgz#556fadbd1915060ef821eb0e38b86866194bcbcf" + integrity sha512-rpQ/PgTEgH68SiXmhu/cJ2hk9aZ6YgFvspzQWe2I9HufY6g7V02DXRr/xrVqOaKm2lenBFPNQ+KAaeveywqV+A== + +"@next/swc-win32-x64-msvc@16.0.8": + version "16.0.8" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.8.tgz#9cb3db37d0113ce099877e04b975868e57782204" + integrity sha512-jWpWjWcMQu2iZz4pEK2IktcfR+OA9+cCG8zenyLpcW8rN4rzjfOzH4yj/b1FiEAZHKS+5Vq8+bZyHi+2yqHbFA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" - resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== dependencies: "@nodelib/fs.stat" "2.0.5" @@ -2115,12 +1796,12 @@ "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" - resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== "@nodelib/fs.walk@^1.2.3": version "1.2.8" - resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== dependencies: "@nodelib/fs.scandir" "2.1.5" @@ -2128,7 +1809,7 @@ "@nolyfill/is-core-module@1.0.39": version "1.0.39" - resolved "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz" + resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== "@parcel/watcher-android-arm64@2.5.1": @@ -2173,7 +1854,7 @@ "@parcel/watcher-linux-x64-glibc@2.5.1": version "2.5.1" - resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== "@parcel/watcher-linux-x64-musl@2.5.1": @@ -2198,7 +1879,7 @@ "@parcel/watcher@^2.5.1": version "2.5.1" - resolved "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== dependencies: detect-libc "^1.0.3" @@ -2222,24 +1903,24 @@ "@pkgjs/parseargs@^0.11.0": version "0.11.0" - resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@pkgr/core@^0.2.9": version "0.2.9" - resolved "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== "@rc-component/async-validator@^5.0.3": version "5.0.4" - resolved "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== dependencies: "@babel/runtime" "^7.24.4" "@rc-component/color-picker@~2.0.1": version "2.0.1" - resolved "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== dependencies: "@ant-design/fast-color" "^2.0.6" @@ -2249,7 +1930,7 @@ "@rc-component/context@^1.4.0": version "1.4.0" - resolved "https://registry.npmjs.org/@rc-component/context/-/context-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== dependencies: "@babel/runtime" "^7.10.1" @@ -2257,14 +1938,14 @@ "@rc-component/mini-decimal@^1.0.1": version "1.1.0" - resolved "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== dependencies: "@babel/runtime" "^7.18.0" "@rc-component/mutate-observer@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== dependencies: "@babel/runtime" "^7.18.0" @@ -2273,7 +1954,7 @@ "@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": version "1.1.2" - resolved "https://registry.npmjs.org/@rc-component/portal/-/portal-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== dependencies: "@babel/runtime" "^7.18.0" @@ -2281,16 +1962,15 @@ rc-util "^5.24.4" "@rc-component/qrcode@~1.1.0": - version "1.1.0" - resolved "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.0.tgz" - integrity sha512-ABA80Yer0c6I2+moqNY0kF3Y1NxIT6wDP/EINIqbiRbfZKP1HtHpKMh8WuTXLgVGYsoWG2g9/n0PgM8KdnJb4Q== + version "1.1.1" + resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.1.1.tgz#909f181bb9a7469d32a6e96c7f35476d4bd92008" + integrity sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA== dependencies: "@babel/runtime" "^7.24.7" - classnames "^2.3.2" "@rc-component/tour@~1.15.1": version "1.15.1" - resolved "https://registry.npmjs.org/@rc-component/tour/-/tour-1.15.1.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== dependencies: "@babel/runtime" "^7.18.0" @@ -2301,7 +1981,7 @@ "@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.3.0": version "2.3.0" - resolved "https://registry.npmjs.org/@rc-component/trigger/-/trigger-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.3.0.tgz#9499ada078daca9dd99d01f0f0743ee1ab9e398b" integrity sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg== dependencies: "@babel/runtime" "^7.23.2" @@ -2313,44 +1993,44 @@ "@repeaterjs/repeater@^3.0.4", "@repeaterjs/repeater@^3.0.6": version "3.0.6" - resolved "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== "@rtsao/scc@^1.1.0": version "1.1.0" - resolved "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== "@sinclair/typebox@^0.34.0": version "0.34.41" - resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c" integrity sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g== "@sinonjs/commons@^3.0.1": version "3.0.1" - resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== dependencies: type-detect "4.0.8" "@sinonjs/fake-timers@^13.0.0": version "13.0.5" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz#36b9dbc21ad5546486ea9173d6bea063eb1717d5" integrity sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw== dependencies: "@sinonjs/commons" "^3.0.1" "@swc/helpers@0.5.15": version "0.5.15" - resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== dependencies: tslib "^2.8.0" -"@theguild/federation-composition@^0.20.2": - version "0.20.2" - resolved "https://registry.npmjs.org/@theguild/federation-composition/-/federation-composition-0.20.2.tgz" - integrity sha512-QI4iSdrc4JvCWnMb1QbiHnEpdD33KGdiU66qfWOcM8ENebRGHkGjXDnUrVJ8F9g1dmCRMTNfn2NFGqTcDpeYXw== +"@theguild/federation-composition@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.21.0.tgz#e2f3d434ac780255245d3e82e78522cd2e696473" + integrity sha512-cdQ9rDEtBpT553DLLtcsSjtSDIadibIxAD3K5r0eUuDOfxx+es7Uk+aOucfqMlNOM3eybsgJN3T2SQmEsINwmw== dependencies: constant-case "^3.0.4" debug "4.4.3" @@ -2359,22 +2039,22 @@ "@tsconfig/node10@^1.0.7": version "1.0.12" - resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== "@tsconfig/node12@^1.0.7": version "1.0.11" - resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== "@tsconfig/node14@^1.0.0": version "1.0.3" - resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== "@tsconfig/node16@^1.0.2": version "1.0.4" - resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== "@tybys/wasm-util@^0.10.0": @@ -2386,7 +2066,7 @@ "@types/babel__core@^7.20.5": version "7.20.5" - resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== dependencies: "@babel/parser" "^7.20.7" @@ -2397,14 +2077,14 @@ "@types/babel__generator@*": version "7.27.0" - resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== dependencies: "@babel/types" "^7.0.0" "@types/babel__template@*": version "7.4.4" - resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== dependencies: "@babel/parser" "^7.1.0" @@ -2412,220 +2092,218 @@ "@types/babel__traverse@*": version "7.28.0" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== dependencies: "@babel/types" "^7.28.2" "@types/estree@^1.0.6": version "1.0.8" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== "@types/hoist-non-react-statics@^3.3.6": version "3.3.7" - resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c" integrity sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g== dependencies: hoist-non-react-statics "^3.3.0" "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": version "2.0.6" - resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== "@types/istanbul-lib-report@*": version "3.0.3" - resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== dependencies: "@types/istanbul-lib-coverage" "*" "@types/istanbul-reports@^3.0.4": version "3.0.4" - resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== dependencies: "@types/istanbul-lib-report" "*" "@types/json-schema@^7.0.15": version "7.0.15" - resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" - resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*", "@types/node@^24.10.1": - version "24.10.1" - resolved "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz" - integrity sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ== + version "24.10.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.2.tgz#82a57476a19647d8f2c7750d0924788245e39b26" + integrity sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA== dependencies: undici-types "~7.16.0" -"@types/react-dom@19.2.3": +"@types/react-dom@^19.2.3": version "19.2.3" - resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== "@types/react-highlight-words@^0.20.0": version "0.20.0" - resolved "https://registry.npmjs.org/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz" + resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c7e3e8302acb5082980e611ce2d93c6e6d3b4022" integrity sha512-Qm512TiOakvtNzHJ2+TNVHnLn5cJ2wLQV0+LrhuispVth6dRf5b8ydjq3Kc0thpZ7bz4s6RnG6meboAXHWRK+Q== dependencies: "@types/react" "*" "@types/react-test-renderer@^19.1.0": version "19.1.0" - resolved "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@19.2.5": - version "19.2.5" - resolved "https://registry.npmjs.org/@types/react/-/react-19.2.5.tgz" - integrity sha512-keKxkZMqnDicuvFoJbzrhbtdLSPhj/rZThDlKWCDbgXmUg0rEUFtRssDXKYmtXluZlIqiC5VqkCgRwzuyLHKHw== +"@types/react@*", "@types/react@^19.2.7": + version "19.2.7" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" + integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg== dependencies: - csstype "^3.0.2" + csstype "^3.2.2" "@types/stack-utils@^2.0.3": version "2.0.3" - resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== "@types/ws@^8.0.0": version "8.18.1" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== dependencies: "@types/node" "*" "@types/yargs-parser@*": version "21.0.3" - resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== "@types/yargs@^17.0.33": version "17.0.35" - resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz" - integrity sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA== +"@typescript-eslint/eslint-plugin@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz#8ed8736b8415a9193989220eadb6031dbcd2260a" + integrity sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/type-utils" "8.47.0" - "@typescript-eslint/utils" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" - graphemer "^1.4.0" + "@typescript-eslint/scope-manager" "8.49.0" + "@typescript-eslint/type-utils" "8.49.0" + "@typescript-eslint/utils" "8.49.0" + "@typescript-eslint/visitor-keys" "8.49.0" ignore "^7.0.0" natural-compare "^1.4.0" ts-api-utils "^2.1.0" -"@typescript-eslint/parser@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz" - integrity sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ== +"@typescript-eslint/parser@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.49.0.tgz#0ede412d59e99239b770f0f08c76c42fba717fa2" + integrity sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA== dependencies: - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" + "@typescript-eslint/scope-manager" "8.49.0" + "@typescript-eslint/types" "8.49.0" + "@typescript-eslint/typescript-estree" "8.49.0" + "@typescript-eslint/visitor-keys" "8.49.0" debug "^4.3.4" -"@typescript-eslint/project-service@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz" - integrity sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA== +"@typescript-eslint/project-service@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.49.0.tgz#ce220525c88cb2d23792b391c07e14cb9697651a" + integrity sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.47.0" - "@typescript-eslint/types" "^8.47.0" + "@typescript-eslint/tsconfig-utils" "^8.49.0" + "@typescript-eslint/types" "^8.49.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz" - integrity sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg== +"@typescript-eslint/scope-manager@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz#a3496765b57fb48035d671174552e462e5bffa63" + integrity sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg== dependencies: - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" + "@typescript-eslint/types" "8.49.0" + "@typescript-eslint/visitor-keys" "8.49.0" -"@typescript-eslint/tsconfig-utils@8.47.0", "@typescript-eslint/tsconfig-utils@^8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz" - integrity sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g== +"@typescript-eslint/tsconfig-utils@8.49.0", "@typescript-eslint/tsconfig-utils@^8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz#857777c8e35dd1e564505833d8043f544442fbf4" + integrity sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA== -"@typescript-eslint/type-utils@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz" - integrity sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A== +"@typescript-eslint/type-utils@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz#d8118a0c1896a78a22f01d3c176e9945409b085b" + integrity sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg== dependencies: - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" - "@typescript-eslint/utils" "8.47.0" + "@typescript-eslint/types" "8.49.0" + "@typescript-eslint/typescript-estree" "8.49.0" + "@typescript-eslint/utils" "8.49.0" debug "^4.3.4" ts-api-utils "^2.1.0" -"@typescript-eslint/types@8.47.0", "@typescript-eslint/types@^8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz" - integrity sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A== +"@typescript-eslint/types@8.49.0", "@typescript-eslint/types@^8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.49.0.tgz#c1bd3ebf956d9e5216396349ca23c58d74f06aee" + integrity sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ== -"@typescript-eslint/typescript-estree@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz" - integrity sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg== +"@typescript-eslint/typescript-estree@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz#99c5a53275197ccb4e849786dad68344e9924135" + integrity sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA== dependencies: - "@typescript-eslint/project-service" "8.47.0" - "@typescript-eslint/tsconfig-utils" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/visitor-keys" "8.47.0" + "@typescript-eslint/project-service" "8.49.0" + "@typescript-eslint/tsconfig-utils" "8.49.0" + "@typescript-eslint/types" "8.49.0" + "@typescript-eslint/visitor-keys" "8.49.0" debug "^4.3.4" - fast-glob "^3.3.2" - is-glob "^4.0.3" minimatch "^9.0.4" semver "^7.6.0" + tinyglobby "^0.2.15" ts-api-utils "^2.1.0" -"@typescript-eslint/utils@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz" - integrity sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ== +"@typescript-eslint/utils@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.49.0.tgz#43b3b91d30afd6f6114532cf0b228f1790f43aff" + integrity sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA== dependencies: "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.47.0" - "@typescript-eslint/types" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" + "@typescript-eslint/scope-manager" "8.49.0" + "@typescript-eslint/types" "8.49.0" + "@typescript-eslint/typescript-estree" "8.49.0" -"@typescript-eslint/visitor-keys@8.47.0": - version "8.47.0" - resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz" - integrity sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ== +"@typescript-eslint/visitor-keys@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz#8e450cc502c0d285cad9e84d400cf349a85ced6c" + integrity sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA== dependencies: - "@typescript-eslint/types" "8.47.0" + "@typescript-eslint/types" "8.49.0" eslint-visitor-keys "^4.2.1" "@umijs/route-utils@^4.0.0": version "4.0.3" - resolved "https://registry.npmjs.org/@umijs/route-utils/-/route-utils-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-4.0.3.tgz#70779ee069ac048509786bcf316368d3516ca5fd" integrity sha512-zPEcYhl1cSfkSRDzzGgoD1mDvGjxoOTJFvkn55srfgdQ3NZe2ZMCScCU6DEnOxuKP1XDVf8pqyqCDVd2+RCQIw== "@umijs/use-params@^1.0.9": version "1.0.9" - resolved "https://registry.npmjs.org/@umijs/use-params/-/use-params-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== "@ungap/structured-clone@^1.3.0": version "1.3.0" - resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== "@unrs/resolver-binding-android-arm-eabi@1.11.1": @@ -2695,7 +2373,7 @@ "@unrs/resolver-binding-linux-x64-gnu@1.11.1": version "1.11.1" - resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== "@unrs/resolver-binding-linux-x64-musl@1.11.1": @@ -2727,15 +2405,15 @@ "@whatwg-node/disposablestack@^0.0.6": version "0.0.6" - resolved "https://registry.npmjs.org/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz" + resolved "https://registry.yarnpkg.com/@whatwg-node/disposablestack/-/disposablestack-0.0.6.tgz#2064a1425ea66194def6df0c7a1851b6939c82bb" integrity sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw== dependencies: "@whatwg-node/promise-helpers" "^1.0.0" tslib "^2.6.3" -"@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.12", "@whatwg-node/fetch@^0.10.13", "@whatwg-node/fetch@^0.10.4": +"@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.13", "@whatwg-node/fetch@^0.10.4": version "0.10.13" - resolved "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.10.13.tgz" + resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.10.13.tgz#2d47190c620f134dda31c7f827976a909b2cca7a" integrity sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q== dependencies: "@whatwg-node/node-fetch" "^0.8.3" @@ -2743,7 +2421,7 @@ "@whatwg-node/node-fetch@^0.8.3": version "0.8.4" - resolved "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz#72df6b3ea9d295bcedbc8f05cf2cfeabe654669f" integrity sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA== dependencies: "@fastify/busboy" "^3.1.1" @@ -2753,64 +2431,64 @@ "@whatwg-node/promise-helpers@^1.0.0", "@whatwg-node/promise-helpers@^1.2.1", "@whatwg-node/promise-helpers@^1.2.4", "@whatwg-node/promise-helpers@^1.3.0", "@whatwg-node/promise-helpers@^1.3.2": version "1.3.2" - resolved "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== dependencies: tslib "^2.6.3" "@wry/caches@^1.0.0": version "1.0.1" - resolved "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52" integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA== dependencies: tslib "^2.3.0" "@wry/context@^0.7.0": version "0.7.4" - resolved "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990" integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ== dependencies: tslib "^2.3.0" "@wry/equality@^0.5.6": version "0.5.7" - resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb" integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw== dependencies: tslib "^2.3.0" "@wry/trie@^0.5.0": version "0.5.0" - resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94" integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA== dependencies: tslib "^2.3.0" abbrev@1: version "1.1.1" - resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== acorn-jsx@^5.3.2: version "5.3.2" - resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: version "8.3.4" - resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== dependencies: acorn "^8.11.0" acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: version "8.15.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== ajv@^6.12.4: version "6.12.6" - resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== dependencies: fast-deep-equal "^3.1.1" @@ -2820,55 +2498,55 @@ ajv@^6.12.4: ansi-escapes@^4.3.2: version "4.3.2" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== dependencies: type-fest "^0.21.3" ansi-escapes@^7.0.0: version "7.2.0" - resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.2.0.tgz#31b25afa3edd3efc09d98c2fee831d460ff06b49" integrity sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw== dependencies: environment "^1.0.0" ansi-regex@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.2.2" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== ansi-styles@^3.2.1: version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.3" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== antd@^5.0: version "5.29.1" - resolved "https://registry.npmjs.org/antd/-/antd-5.29.1.tgz" + resolved "https://registry.yarnpkg.com/antd/-/antd-5.29.1.tgz#e124b1aaa709a534816c42d558da02a917b995cc" integrity sha512-TTFVbpKbyL6cPfEoKq6Ya3BIjTUr7uDW9+7Z+1oysRv1gpcN7kQ4luH8r/+rXXwz4n6BIz1iBJ1ezKCdsdNW0w== dependencies: "@ant-design/colors" "^7.2.1" @@ -2923,7 +2601,7 @@ antd@^5.0: anymatch@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" @@ -2931,29 +2609,29 @@ anymatch@^3.1.3: arg@^4.1.0: version "4.1.3" - resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== argparse@^1.0.7: version "1.0.10" - resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== aria-query@^5.3.2: version "5.3.2" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== dependencies: call-bound "^1.0.3" @@ -2961,12 +2639,12 @@ array-buffer-byte-length@^1.0.1, array-buffer-byte-length@^1.0.2: array-find-index@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: version "3.1.9" - resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.9.tgz#1f0ccaa08e90cdbc3eb433210f903ad0f17c3f3a" integrity sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ== dependencies: call-bind "^1.0.8" @@ -2980,12 +2658,12 @@ array-includes@^3.1.6, array-includes@^3.1.8, array-includes@^3.1.9: array-union@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== array.prototype.findlast@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz#3e4fbcb30a15a7f5bf64cf2faae22d139c2e4904" integrity sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ== dependencies: call-bind "^1.0.7" @@ -2997,7 +2675,7 @@ array.prototype.findlast@^1.2.5: array.prototype.findlastindex@^1.2.6: version "1.2.6" - resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz#cfa1065c81dcb64e34557c9b81d012f6a421c564" integrity sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ== dependencies: call-bind "^1.0.8" @@ -3010,7 +2688,7 @@ array.prototype.findlastindex@^1.2.6: array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== dependencies: call-bind "^1.0.8" @@ -3020,7 +2698,7 @@ array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.3: array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: version "1.3.3" - resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz#712cc792ae70370ae40586264629e33aab5dd38b" integrity sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg== dependencies: call-bind "^1.0.8" @@ -3030,7 +2708,7 @@ array.prototype.flatmap@^1.3.2, array.prototype.flatmap@^1.3.3: array.prototype.tosorted@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" integrity sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA== dependencies: call-bind "^1.0.7" @@ -3041,7 +2719,7 @@ array.prototype.tosorted@^1.1.4: arraybuffer.prototype.slice@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== dependencies: array-buffer-byte-length "^1.0.1" @@ -3054,44 +2732,44 @@ arraybuffer.prototype.slice@^1.0.4: asap@^2.0.0, asap@~2.0.3: version "2.0.6" - resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== ast-types-flow@^0.0.8: version "0.0.8" - resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6" integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ== async-function@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== auto-bind@~4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" integrity sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ== available-typed-arrays@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== dependencies: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: version "4.11.0" - resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6" integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ== axobject-query@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== babel-jest@30.2.0, babel-jest@^30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac" integrity sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw== dependencies: "@jest/transform" "30.2.0" @@ -3104,7 +2782,7 @@ babel-jest@30.2.0, babel-jest@^30.2.0: babel-plugin-istanbul@^7.0.1: version "7.0.1" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" @@ -3115,19 +2793,14 @@ babel-plugin-istanbul@^7.0.1: babel-plugin-jest-hoist@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz#94c250d36b43f95900f3a219241e0f4648191ce2" integrity sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA== dependencies: "@types/babel__core" "^7.20.5" -babel-plugin-syntax-trailing-function-commas@^7.0.0-beta.0: - version "7.0.0-beta.0" - resolved "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz" - integrity sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ== - babel-preset-current-node-syntax@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" @@ -3146,42 +2819,9 @@ babel-preset-current-node-syntax@^1.2.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-fbjs@^3.4.0: - version "3.4.0" - resolved "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz" - integrity sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow== - dependencies: - "@babel/plugin-proposal-class-properties" "^7.0.0" - "@babel/plugin-proposal-object-rest-spread" "^7.0.0" - "@babel/plugin-syntax-class-properties" "^7.0.0" - "@babel/plugin-syntax-flow" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.0.0" - "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - "@babel/plugin-transform-arrow-functions" "^7.0.0" - "@babel/plugin-transform-block-scoped-functions" "^7.0.0" - "@babel/plugin-transform-block-scoping" "^7.0.0" - "@babel/plugin-transform-classes" "^7.0.0" - "@babel/plugin-transform-computed-properties" "^7.0.0" - "@babel/plugin-transform-destructuring" "^7.0.0" - "@babel/plugin-transform-flow-strip-types" "^7.0.0" - "@babel/plugin-transform-for-of" "^7.0.0" - "@babel/plugin-transform-function-name" "^7.0.0" - "@babel/plugin-transform-literals" "^7.0.0" - "@babel/plugin-transform-member-expression-literals" "^7.0.0" - "@babel/plugin-transform-modules-commonjs" "^7.0.0" - "@babel/plugin-transform-object-super" "^7.0.0" - "@babel/plugin-transform-parameters" "^7.0.0" - "@babel/plugin-transform-property-literals" "^7.0.0" - "@babel/plugin-transform-react-display-name" "^7.0.0" - "@babel/plugin-transform-react-jsx" "^7.0.0" - "@babel/plugin-transform-shorthand-properties" "^7.0.0" - "@babel/plugin-transform-spread" "^7.0.0" - "@babel/plugin-transform-template-literals" "^7.0.0" - babel-plugin-syntax-trailing-function-commas "^7.0.0-beta.0" - babel-preset-jest@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz#04717843e561347781d6d7f69c81e6bcc3ed11ce" integrity sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ== dependencies: babel-plugin-jest-hoist "30.2.0" @@ -3189,22 +2829,22 @@ babel-preset-jest@30.2.0: balanced-match@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.8.25, baseline-browser-mapping@^2.9.3: - version "2.9.3" - resolved "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.3.tgz" - integrity sha512-8QdH6czo+G7uBsNo0GiUfouPN1lRzKdJTGnKXwe12gkFbnnOUaUKGN55dMkfy+mnxmvjwl9zcI4VncczcVXDhA== +baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.3: + version "2.9.5" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz#47f9549e0be1a84cd16651ac4c3b7d87a71408e6" + integrity sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA== big.js@^5.2.2: version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== brace-expansion@^1.1.7: version "1.1.12" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== dependencies: balanced-match "^1.0.0" @@ -3212,44 +2852,44 @@ brace-expansion@^1.1.7: brace-expansion@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" braces@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.28.0" - resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz" - integrity sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ== + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== dependencies: - baseline-browser-mapping "^2.8.25" - caniuse-lite "^1.0.30001754" - electron-to-chromium "^1.5.249" + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" node-releases "^2.0.27" - update-browserslist-db "^1.1.4" + update-browserslist-db "^1.2.0" bser@2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== dependencies: node-int64 "^0.4.0" buffer-from@^1.0.0: version "1.1.2" - resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== dependencies: es-errors "^1.3.0" @@ -3257,7 +2897,7 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply- call-bind@^1.0.7, call-bind@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== dependencies: call-bind-apply-helpers "^1.0.0" @@ -3267,7 +2907,7 @@ call-bind@^1.0.7, call-bind@^1.0.8: call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== dependencies: call-bind-apply-helpers "^1.0.2" @@ -3275,35 +2915,35 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: callsites@^3.0.0, callsites@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== camel-case@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== dependencies: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase@^5.0.0, camelcase@^5.3.1: +camelcase@^5.3.1: version "5.3.1" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== camelcase@^6.3.0: version "6.3.0" - resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001754: - version "1.0.30001756" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz" - integrity sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A== +caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: + version "1.0.30001760" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f" + integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw== capital-case@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== dependencies: no-case "^3.0.4" @@ -3312,7 +2952,7 @@ capital-case@^1.0.4: chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" @@ -3320,7 +2960,7 @@ chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: chalk@^2.4.1: version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" @@ -3329,7 +2969,7 @@ chalk@^2.4.1: change-case-all@1.0.15: version "1.0.15" - resolved "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-1.0.15.tgz#de29393167fc101d646cd76b0ef23e27d09756ad" integrity sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ== dependencies: change-case "^4.1.2" @@ -3345,7 +2985,7 @@ change-case-all@1.0.15: change-case@^4.1.2: version "4.1.2" - resolved "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== dependencies: camel-case "^4.1.2" @@ -3363,39 +3003,39 @@ change-case@^4.1.2: char-regex@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== chardet@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-2.1.1.tgz#5c75593704a642f71ee53717df234031e65373c8" integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== ci-info@^4.2.0: version "4.3.1" - resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== cjs-module-lexer@^2.1.0: version "2.1.1" - resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz#bff23b0609cc9afa428bd35f1918f7d03b448562" integrity sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ== classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: version "2.5.1" - resolved "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== cli-cursor@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-5.0.0.tgz#24a4831ecf5a6b01ddeb32fb71a4b2088b0dce38" integrity sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw== dependencies: restore-cursor "^5.0.0" cli-truncate@^5.0.0: version "5.1.1" - resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.1.1.tgz#455476face9904d94b7d11e98d9adbca15292ea5" integrity sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A== dependencies: slice-ansi "^7.1.0" @@ -3403,26 +3043,17 @@ cli-truncate@^5.0.0: cli-width@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== client-only@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - cliui@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" @@ -3431,61 +3062,61 @@ cliui@^8.0.1: co@^4.6.0: version "4.6.0" - resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== collect-v8-coverage@^1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== color-convert@^1.9.0: version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colorette@^2.0.20: version "2.0.20" - resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== common-tags@1.8.2: version "1.8.2" - resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== compute-scroll-into-view@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz#02c3386ec531fb6a9881967388e53e8564f3e9aa" integrity sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw== concat-map@0.0.1: version "0.0.1" - resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concurrently@^9.2.1: version "9.2.1" - resolved "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.1.tgz#248ea21b95754947be2dad9c3e4b60f18ca4e44f" integrity sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng== dependencies: chalk "4.1.2" @@ -3497,7 +3128,7 @@ concurrently@^9.2.1: constant-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== dependencies: no-case "^3.0.4" @@ -3506,24 +3137,24 @@ constant-case@^3.0.4: convert-source-map@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz" - integrity sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA== + version "1.1.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" + integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== copy-to-clipboard@^3.3.3: version "3.3.3" - resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: toggle-selection "^1.0.6" cosmiconfig@^8.1.0: version "8.3.6" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== dependencies: import-fresh "^3.3.0" @@ -3533,7 +3164,7 @@ cosmiconfig@^8.1.0: cosmiconfig@^9.0.0: version "9.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: env-paths "^2.2.1" @@ -3543,26 +3174,26 @@ cosmiconfig@^9.0.0: create-require@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-fetch@^3.1.5: version "3.2.0" - resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== dependencies: node-fetch "^2.7.0" cross-inspect@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" integrity sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A== dependencies: tslib "^2.4.0" cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" - resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" @@ -3571,27 +3202,27 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: crypto-hash@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/crypto-hash/-/crypto-hash-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-4.0.0.tgz#c58a8737560980714fa086113de3b8f577b72178" integrity sha512-KkkFriWj2teXIJOmftIRavBL4q8A7EZV24Nn9sWhIR4hdeS8u+q2A2p0tWZ1m4xVoP1zs3VJjVwwAUnztBb8uw== -csstype@^3.0.2, csstype@^3.1.3: +csstype@^3.1.3, csstype@^3.2.2: version "3.2.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== damerau-levenshtein@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== data-uri-to-buffer@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== data-view-buffer@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== dependencies: call-bound "^1.0.3" @@ -3600,7 +3231,7 @@ data-view-buffer@^1.0.2: data-view-byte-length@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== dependencies: call-bound "^1.0.3" @@ -3609,7 +3240,7 @@ data-view-byte-length@^1.0.2: data-view-byte-offset@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== dependencies: call-bound "^1.0.2" @@ -3618,61 +3249,56 @@ data-view-byte-offset@^1.0.1: dataloader@^2.2.3: version "2.2.3" - resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.3.tgz#42d10b4913515f5b37c6acedcb4960d6ae1b1517" integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: version "1.11.19" - resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== debounce@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/debounce/-/debounce-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5" integrity sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw== debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0: version "4.4.3" - resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" debug@^3.1.0, debug@^3.2.7: version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debuglog@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - dedent@^1.6.0: version "1.7.0" - resolved "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== deep-is@^0.1.3: version "0.1.4" - resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.0, deepmerge@^4.3.1: version "4.3.1" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" - resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== dependencies: es-define-property "^1.0.0" @@ -3681,51 +3307,46 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: define-properties@^1.1.3, define-properties@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== dependencies: define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" -dependency-graph@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz" - integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== - dependency-graph@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-1.0.0.tgz#bb5e85aec1310bc13b22dbd76e3196c4ee4c10d2" integrity sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg== dequal@^2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== detect-indent@^6.0.0: version "6.1.0" - resolved "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== detect-libc@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== detect-libc@^2.1.2: version "2.1.2" - resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== detect-newline@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== dezalgo@^1.0.0: version "1.0.4" - resolved "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== dependencies: asap "^2.0.0" @@ -3733,26 +3354,26 @@ dezalgo@^1.0.0: diff@^4.0.1: version "4.0.2" - resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: path-type "^4.0.0" doctrine@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== dependencies: esutils "^2.0.2" dot-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== dependencies: no-case "^3.0.4" @@ -3760,12 +3381,12 @@ dot-case@^3.0.4: dset@^3.1.2: version "3.1.4" - resolved "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz" + resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -3774,59 +3395,59 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: eastasianwidth@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.5.249: - version "1.5.256" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.256.tgz" - integrity sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ== +electron-to-chromium@^1.5.263: + version "1.5.267" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" + integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== emittery@^0.13.1: version "0.13.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== emoji-regex@^10.3.0: version "10.6.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== emoji-regex@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" - resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== emojis-list@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== env-paths@^2.2.1: version "2.2.1" - resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== environment@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1" integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q== error-ex@^1.3.1: version "1.3.4" - resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.4.tgz#b3a8d8bb6f92eecc1629e3e27d3c8607a8a32414" integrity sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ== dependencies: is-arrayish "^0.2.1" es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: version "1.24.0" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== dependencies: array-buffer-byte-length "^1.0.2" @@ -3886,17 +3507,17 @@ es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23 es-define-property@^1.0.0, es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== dependencies: call-bind "^1.0.8" @@ -3918,14 +3539,14 @@ es-iterator-helpers@^1.2.1: es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: es-errors "^1.3.0" @@ -3935,14 +3556,14 @@ es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz#438df35520dac5d105f3943d927549ea3b00f4b5" integrity sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw== dependencies: hasown "^2.0.2" es-to-primitive@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== dependencies: is-callable "^1.2.7" @@ -3951,30 +3572,30 @@ es-to-primitive@^1.3.0: escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-string-regexp@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-config-next@16.0.3: - version "16.0.3" - resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.0.3.tgz" - integrity sha512-5F6qDjcZldf0Y0ZbqvWvap9xzYUxyDf7/of37aeyhvkrQokj/4bT1JYWZdlWUr283aeVa+s52mPq9ogmGg+5dw== +eslint-config-next@^16.0.8: + version "16.0.8" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.0.8.tgz#0f5bb2c8c2a88479ef004258963112a31e4cc6a5" + integrity sha512-8J5cOAboXIV3f8OD6BOyj7Fik6n/as7J4MboiUSExWruf/lCu1OPR3ZVSdnta6WhzebrmAATEmNSBZsLWA6kbg== dependencies: - "@next/eslint-plugin-next" "16.0.3" + "@next/eslint-plugin-next" "16.0.8" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.32.0" @@ -3986,7 +3607,7 @@ eslint-config-next@16.0.3: eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: version "0.3.9" - resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" @@ -3995,7 +3616,7 @@ eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: eslint-import-resolver-typescript@^3.5.2: version "3.10.1" - resolved "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz#23dac32efa86a88e2b8232eb244ac499ad636db2" integrity sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ== dependencies: "@nolyfill/is-core-module" "1.0.39" @@ -4008,14 +3629,14 @@ eslint-import-resolver-typescript@^3.5.2: eslint-module-utils@^2.12.1: version "2.12.1" - resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== dependencies: debug "^3.2.7" eslint-plugin-import@^2.32.0: version "2.32.0" - resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz#602b55faa6e4caeaa5e970c198b5c00a37708980" integrity sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA== dependencies: "@rtsao/scc" "^1.1.0" @@ -4040,7 +3661,7 @@ eslint-plugin-import@^2.32.0: eslint-plugin-jsx-a11y@^6.10.0: version "6.10.2" - resolved "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz#d2812bb23bf1ab4665f1718ea442e8372e638483" integrity sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q== dependencies: aria-query "^5.3.2" @@ -4061,7 +3682,7 @@ eslint-plugin-jsx-a11y@^6.10.0: eslint-plugin-react-hooks@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" integrity sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA== dependencies: "@babel/core" "^7.24.4" @@ -4072,7 +3693,7 @@ eslint-plugin-react-hooks@^7.0.0: eslint-plugin-react@^7.37.0: version "7.37.5" - resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz#2975511472bdda1b272b34d779335c9b0e877065" integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA== dependencies: array-includes "^3.1.8" @@ -4096,7 +3717,7 @@ eslint-plugin-react@^7.37.0: eslint-scope@^8.4.0: version "8.4.0" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== dependencies: esrecurse "^4.3.0" @@ -4104,17 +3725,17 @@ eslint-scope@^8.4.0: eslint-visitor-keys@^3.4.3: version "3.4.3" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint-visitor-keys@^4.2.1: version "4.2.1" - resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== eslint@^9.5.0: version "9.39.1" - resolved "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== dependencies: "@eslint-community/eslint-utils" "^4.8.0" @@ -4154,7 +3775,7 @@ eslint@^9.5.0: espree@^10.0.1, espree@^10.4.0: version "10.4.0" - resolved "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== dependencies: acorn "^8.15.0" @@ -4163,41 +3784,41 @@ espree@^10.0.1, espree@^10.4.0: esprima@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== esquery@^1.5.0: version "1.6.0" - resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" esrecurse@^4.3.0: version "4.3.0" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" - resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== esutils@^2.0.2: version "2.0.3" - resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== eventemitter3@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== execa@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== dependencies: cross-spawn "^7.0.3" @@ -4212,12 +3833,12 @@ execa@^5.1.1: exit-x@^0.2.2: version "0.2.2" - resolved "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz" + resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== expect@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== dependencies: "@jest/expect-utils" "30.2.0" @@ -4229,12 +3850,12 @@ expect@30.2.0: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@3.3.1: version "3.3.1" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4243,9 +3864,9 @@ fast-glob@3.3.1: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.2.9, fast-glob@^3.3.2: +fast-glob@^3.2.9: version "3.3.3" - resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" @@ -4256,36 +3877,36 @@ fast-glob@^3.2.9, fast-glob@^3.3.2: fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== fast-levenshtein@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: version "1.19.1" - resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== dependencies: reusify "^1.0.4" fb-watchman@^2.0.0, fb-watchman@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" fbjs-css-vars@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== fbjs@^3.0.0: version "3.0.5" - resolved "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== dependencies: cross-fetch "^3.1.5" @@ -4298,12 +3919,12 @@ fbjs@^3.0.0: fdir@^6.5.0: version "6.5.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== fetch-blob@^3.1.2, fetch-blob@^3.1.4: version "3.2.0" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== dependencies: node-domexception "^1.0.0" @@ -4311,21 +3932,21 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: file-entry-cache@^8.0.0: version "8.0.0" - resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== dependencies: flat-cache "^4.0.0" fill-range@^7.1.1: version "7.1.1" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -4333,7 +3954,7 @@ find-up@^4.0.0, find-up@^4.1.0: find-up@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" @@ -4341,7 +3962,7 @@ find-up@^5.0.0: flat-cache@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== dependencies: flatted "^3.2.9" @@ -4349,19 +3970,19 @@ flat-cache@^4.0.0: flatted@^3.2.9: version "3.3.3" - resolved "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" - resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== dependencies: is-callable "^1.2.7" foreground-child@^3.1.0: version "3.3.1" - resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: cross-spawn "^7.0.6" @@ -4369,14 +3990,14 @@ foreground-child@^3.1.0: formdata-polyfill@^4.0.10: version "4.0.10" - resolved "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz" + resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== dependencies: fetch-blob "^3.1.2" fs.realpath@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@^2.3.3: @@ -4386,12 +4007,12 @@ fsevents@^2.3.3: function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: version "1.1.8" - resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== dependencies: call-bind "^1.0.8" @@ -4403,32 +4024,32 @@ function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: functions-have-names@^1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== generator-function@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/generator-function/-/generator-function-2.0.1.tgz#0e75dd410d1243687a0ba2e951b94eedb8f737a2" integrity sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g== gensync@^1.0.0-beta.2: version "1.0.0-beta.2" - resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.0, get-east-asian-width@^1.3.1: version "1.4.0" - resolved "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: call-bind-apply-helpers "^1.0.2" @@ -4444,12 +4065,12 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@ get-package-type@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== get-proto@^1.0.0, get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" @@ -4457,12 +4078,12 @@ get-proto@^1.0.0, get-proto@^1.0.1: get-stream@^6.0.0: version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== get-symbol-description@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== dependencies: call-bound "^1.0.3" @@ -4471,28 +4092,28 @@ get-symbol-description@^1.1.0: get-tsconfig@^4.10.0: version "4.13.0" - resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== dependencies: resolve-pkg-maps "^1.0.0" glob-parent@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-parent@^6.0.2: version "6.0.2" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== dependencies: is-glob "^4.0.3" glob@^10.3.10: version "10.5.0" - resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== dependencies: foreground-child "^3.1.0" @@ -4504,7 +4125,7 @@ glob@^10.3.10: glob@^7.1.1, glob@^7.1.4: version "7.2.3" - resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" @@ -4516,17 +4137,17 @@ glob@^7.1.1, glob@^7.1.4: globals@16.4.0: version "16.4.0" - resolved "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== globals@^14.0.0: version "14.0.0" - resolved "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== globalthis@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== dependencies: define-properties "^1.2.1" @@ -4534,7 +4155,7 @@ globalthis@^1.0.4: globby@^11.0.3: version "11.1.0" - resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== dependencies: array-union "^2.1.0" @@ -4546,22 +4167,17 @@ globby@^11.0.3: gopd@^1.0.1, gopd@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== graceful-fs@^4.1.2, graceful-fs@^4.2.11: version "4.2.11" - resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - graphql-config@^5.1.1: version "5.1.5" - resolved "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.5.tgz" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-5.1.5.tgz#34e0bfba88e74b6eefd889716a9378086f595f7f" integrity sha512-mG2LL1HccpU8qg5ajLROgdsBzx/o2M6kgI3uAmoaXiSH9PCUbtIyLomLqUtCFaAeG2YCFsl0M5cfQ9rKmDoMVA== dependencies: "@graphql-tools/graphql-file-loader" "^8.0.0" @@ -4578,72 +4194,72 @@ graphql-config@^5.1.1: graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" - resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" + resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== dependencies: tslib "^2.1.0" graphql-ws@^6.0.6: version "6.0.6" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.6.tgz" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.6.tgz#e9c9ff85f4ddb5bbe6faa2721c0d27e6c11746d2" integrity sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw== graphql@^16.12.0: version "16.12.0" - resolved "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7" integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ== has-bigints@^1.0.2: version "1.1.0" - resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.1.0.tgz#28607e965ac967e03cd2a2c70a2636a1edad49fe" integrity sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg== has-flag@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== dependencies: es-define-property "^1.0.0" has-proto@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== dependencies: dunder-proto "^1.0.0" has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" hasown@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" header-case@^2.0.4: version "2.0.4" - resolved "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: capital-case "^1.0.4" @@ -4651,68 +4267,68 @@ header-case@^2.0.4: hermes-estree@0.25.1: version "0.25.1" - resolved "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz" + resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.25.1.tgz#6aeec17d1983b4eabf69721f3aa3eb705b17f480" integrity sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw== hermes-parser@^0.25.1: version "0.25.1" - resolved "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz" + resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.25.1.tgz#5be0e487b2090886c62bd8a11724cd766d5f54d1" integrity sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA== dependencies: hermes-estree "0.25.1" highlight-words-core@^1.2.0: version "1.2.3" - resolved "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" integrity sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ== hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" - resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" hosted-git-info@^2.1.4: version "2.8.9" - resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== html-escaper@^2.0.0: version "2.0.2" - resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== human-signals@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@^0.7.0: version "0.7.0" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ignore@^5.2.0: version "5.3.2" - resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== ignore@^7.0.0: version "7.0.5" - resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== immutable@~3.7.6: version "3.7.6" - resolved "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.1" - resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== dependencies: parent-module "^1.0.0" @@ -4720,12 +4336,12 @@ import-fresh@^3.2.1, import-fresh@^3.3.0: import-from@4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-4.0.0.tgz#2710b8d66817d232e16f4166e319248d3d5492e2" integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== import-local@^3.2.0: version "3.2.0" - resolved "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== dependencies: pkg-dir "^4.2.0" @@ -4733,12 +4349,12 @@ import-local@^3.2.0: imurmurhash@^0.1.4: version "0.1.4" - resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" - resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" @@ -4746,12 +4362,12 @@ inflight@^1.0.4: inherits@2: version "2.0.4" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== internal-slot@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== dependencies: es-errors "^1.3.0" @@ -4760,14 +4376,14 @@ internal-slot@^1.1.0: invariant@^2.2.4: version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" is-absolute@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" @@ -4775,7 +4391,7 @@ is-absolute@^1.0.0: is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: version "3.0.5" - resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== dependencies: call-bind "^1.0.8" @@ -4784,12 +4400,12 @@ is-array-buffer@^3.0.4, is-array-buffer@^3.0.5: is-arrayish@^0.2.1: version "0.2.1" - resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== is-async-function@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523" integrity sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ== dependencies: async-function "^1.0.0" @@ -4800,14 +4416,14 @@ is-async-function@^2.0.0: is-bigint@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== dependencies: has-bigints "^1.0.2" is-boolean-object@^1.2.1: version "1.2.2" - resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== dependencies: call-bound "^1.0.3" @@ -4815,26 +4431,26 @@ is-boolean-object@^1.2.1: is-bun-module@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ== dependencies: semver "^7.7.1" is-callable@^1.2.7: version "1.2.7" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.13.0, is-core-module@^2.16.1: version "2.16.1" - resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" is-data-view@^1.0.1, is-data-view@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== dependencies: call-bound "^1.0.2" @@ -4843,7 +4459,7 @@ is-data-view@^1.0.1, is-data-view@^1.0.2: is-date-object@^1.0.5, is-date-object@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== dependencies: call-bound "^1.0.2" @@ -4851,36 +4467,36 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: is-extglob@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-finalizationregistry@^1.1.0: version "1.1.1" - resolved "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== dependencies: call-bound "^1.0.3" is-fullwidth-code-point@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-fullwidth-code-point@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== dependencies: get-east-asian-width "^1.3.1" is-generator-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== is-generator-function@^1.0.10: version "1.1.2" - resolved "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" integrity sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA== dependencies: call-bound "^1.0.4" @@ -4891,31 +4507,31 @@ is-generator-function@^1.0.10: is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-2.0.2.tgz#1c0884d3012c841556243483aa5d522f47396d2a" integrity sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ== dependencies: tslib "^2.0.3" is-map@^2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== is-negative-zero@^2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== is-number-object@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== dependencies: call-bound "^1.0.3" @@ -4923,12 +4539,12 @@ is-number-object@^1.1.1: is-number@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-regex@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: call-bound "^1.0.2" @@ -4938,31 +4554,31 @@ is-regex@^1.2.1: is-relative@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-set@^2.0.3: version "2.0.3" - resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.3.tgz#8ab209ea424608141372ded6e0cb200ef1d9d01d" integrity sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg== is-shared-array-buffer@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== dependencies: call-bound "^1.0.3" is-stream@^2.0.0: version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== is-string@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== dependencies: call-bound "^1.0.3" @@ -4970,7 +4586,7 @@ is-string@^1.1.1: is-symbol@^1.0.4, is-symbol@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== dependencies: call-bound "^1.0.2" @@ -4979,45 +4595,45 @@ is-symbol@^1.0.4, is-symbol@^1.1.1: is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15: version "1.1.15" - resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== dependencies: which-typed-array "^1.1.16" is-unc-path@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-2.0.2.tgz#f1105ced1fe4de906a5f39553e7d3803fd804649" integrity sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ== dependencies: tslib "^2.0.3" is-weakmap@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.2.tgz#bf72615d649dfe5f699079c54b83e47d1ae19cfd" integrity sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w== is-weakref@^1.0.2, is-weakref@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== dependencies: call-bound "^1.0.3" is-weakset@^2.0.3: version "2.0.4" - resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.4.tgz#c9f5deb0bc1906c6d6f1027f284ddf459249daca" integrity sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ== dependencies: call-bound "^1.0.3" @@ -5025,37 +4641,37 @@ is-weakset@^2.0.3: is-windows@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== isexe@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isomorphic-ws@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== isows@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" - resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: version "6.0.3" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== dependencies: "@babel/core" "^7.23.9" @@ -5066,7 +4682,7 @@ istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: istanbul-lib-report@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== dependencies: istanbul-lib-coverage "^3.0.0" @@ -5075,7 +4691,7 @@ istanbul-lib-report@^3.0.0: istanbul-lib-source-maps@^5.0.0: version "5.0.6" - resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== dependencies: "@jridgewell/trace-mapping" "^0.3.23" @@ -5084,7 +4700,7 @@ istanbul-lib-source-maps@^5.0.0: istanbul-reports@^3.1.3: version "3.2.0" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== dependencies: html-escaper "^2.0.0" @@ -5092,7 +4708,7 @@ istanbul-reports@^3.1.3: iterator.prototype@^1.1.4: version "1.1.5" - resolved "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== dependencies: define-data-property "^1.1.4" @@ -5104,7 +4720,7 @@ iterator.prototype@^1.1.4: jackspeak@^3.1.2: version "3.4.3" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" @@ -5113,12 +4729,12 @@ jackspeak@^3.1.2: javascript-stringify@^2.0.1: version "2.1.0" - resolved "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== jest-changed-files@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz#602266e478ed554e1e1469944faa7efd37cee61c" integrity sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ== dependencies: execa "^5.1.1" @@ -5127,7 +4743,7 @@ jest-changed-files@30.2.0: jest-circus@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.2.0.tgz#98b8198b958748a2f322354311023d1d02e7603f" integrity sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg== dependencies: "@jest/environment" "30.2.0" @@ -5153,7 +4769,7 @@ jest-circus@30.2.0: jest-cli@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.2.0.tgz#1780f8e9d66bf84a10b369aea60aeda7697dcc67" integrity sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA== dependencies: "@jest/core" "30.2.0" @@ -5169,7 +4785,7 @@ jest-cli@30.2.0: jest-config@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.2.0.tgz#29df8c50e2ad801cc59c406b50176c18c362a90b" integrity sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA== dependencies: "@babel/core" "^7.27.4" @@ -5199,7 +4815,7 @@ jest-config@30.2.0: jest-diff@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== dependencies: "@jest/diff-sequences" "30.0.1" @@ -5209,14 +4825,14 @@ jest-diff@30.2.0: jest-docblock@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== dependencies: detect-newline "^3.1.0" jest-each@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.2.0.tgz#39e623ae71641c2ac3ee69b3ba3d258fce8e768d" integrity sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ== dependencies: "@jest/get-type" "30.1.0" @@ -5227,7 +4843,7 @@ jest-each@30.2.0: jest-environment-node@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz#3def7980ebd2fd86e74efd4d2e681f55ab38da0f" integrity sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA== dependencies: "@jest/environment" "30.2.0" @@ -5240,7 +4856,7 @@ jest-environment-node@30.2.0: jest-haste-map@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== dependencies: "@jest/types" "30.2.0" @@ -5258,7 +4874,7 @@ jest-haste-map@30.2.0: jest-leak-detector@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz#292fdca7b7c9cf594e1e570ace140b01d8beb736" integrity sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ== dependencies: "@jest/get-type" "30.1.0" @@ -5266,7 +4882,7 @@ jest-leak-detector@30.2.0: jest-matcher-utils@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== dependencies: "@jest/get-type" "30.1.0" @@ -5276,7 +4892,7 @@ jest-matcher-utils@30.2.0: jest-message-util@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== dependencies: "@babel/code-frame" "^7.27.1" @@ -5291,7 +4907,7 @@ jest-message-util@30.2.0: jest-mock@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== dependencies: "@jest/types" "30.2.0" @@ -5300,17 +4916,17 @@ jest-mock@30.2.0: jest-pnp-resolver@^1.2.3: version "1.2.3" - resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== jest-regex-util@30.0.1: version "30.0.1" - resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== jest-resolve-dependencies@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz#3370e2c0b49cc560f6a7e8ec3a59dd99525e1a55" integrity sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w== dependencies: jest-regex-util "30.0.1" @@ -5318,7 +4934,7 @@ jest-resolve-dependencies@30.2.0: jest-resolve@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.2.0.tgz#2e2009cbd61e8f1f003355d5ec87225412cebcd7" integrity sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A== dependencies: chalk "^4.1.2" @@ -5332,7 +4948,7 @@ jest-resolve@30.2.0: jest-runner@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.2.0.tgz#c62b4c3130afa661789705e13a07bdbcec26a114" integrity sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ== dependencies: "@jest/console" "30.2.0" @@ -5360,7 +4976,7 @@ jest-runner@30.2.0: jest-runtime@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.2.0.tgz#395ea792cde048db1b0cd1a92dc9cb9f1921bf8a" integrity sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg== dependencies: "@jest/environment" "30.2.0" @@ -5388,7 +5004,7 @@ jest-runtime@30.2.0: jest-snapshot@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz#266fbbb4b95fc4665ce6f32f1f38eeb39f4e26d0" integrity sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA== dependencies: "@babel/core" "^7.27.4" @@ -5415,7 +5031,7 @@ jest-snapshot@30.2.0: jest-util@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== dependencies: "@jest/types" "30.2.0" @@ -5427,7 +5043,7 @@ jest-util@30.2.0: jest-validate@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.2.0.tgz#273eaaed4c0963b934b5b31e96289edda6e0a2ef" integrity sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw== dependencies: "@jest/get-type" "30.1.0" @@ -5439,7 +5055,7 @@ jest-validate@30.2.0: jest-watcher@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.2.0.tgz#f9c055de48e18c979e7756a3917e596e2d69b07b" integrity sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg== dependencies: "@jest/test-result" "30.2.0" @@ -5453,7 +5069,7 @@ jest-watcher@30.2.0: jest-worker@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== dependencies: "@types/node" "*" @@ -5464,7 +5080,7 @@ jest-worker@30.2.0: jest@^30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/jest/-/jest-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/jest/-/jest-30.2.0.tgz#9f0a71e734af968f26952b5ae4b724af82681630" integrity sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A== dependencies: "@jest/core" "30.2.0" @@ -5474,57 +5090,57 @@ jest@^30.2.0: jiti@^2.0.0, jiti@^2.3.0: version "2.6.1" - resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.1: version "3.14.2" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== dependencies: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0: +js-yaml@^4.1.0, js-yaml@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== dependencies: argparse "^2.0.1" jsesc@^3.0.2: version "3.1.0" - resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== json-buffer@3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== json-parse-even-better-errors@^2.3.0: version "2.3.1" - resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== json-to-pretty-yaml@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" integrity sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A== dependencies: remedial "^1.0.7" @@ -5532,26 +5148,26 @@ json-to-pretty-yaml@^1.2.2: json2mq@^0.2.0: version "0.2.0" - resolved "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz" + resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA== dependencies: string-convert "^0.2.0" json5@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== dependencies: minimist "^1.2.0" json5@^2.1.2, json5@^2.2.3: version "2.2.3" - resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" - resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: array-includes "^3.1.6" @@ -5561,31 +5177,31 @@ json5@^2.1.2, json5@^2.2.3: keyv@^4.5.4: version "4.5.4" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" language-subtag-registry@^0.3.20: version "0.3.23" - resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" integrity sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ== language-tags@^1.0.9: version "1.0.9" - resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.9.tgz#1ffdcd0ec0fafb4b1be7f8b11f306ad0f9c08777" integrity sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA== dependencies: language-subtag-registry "^0.3.20" leven@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== levn@^0.4.1: version "0.4.1" - resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== dependencies: prelude-ls "^1.2.1" @@ -5593,7 +5209,7 @@ levn@^0.4.1: license-checker@^25.0.1: version "25.0.1" - resolved "https://registry.npmjs.org/license-checker/-/license-checker-25.0.1.tgz" + resolved "https://registry.yarnpkg.com/license-checker/-/license-checker-25.0.1.tgz#4d14504478a5240a857bb3c21cd0491a00d761fa" integrity sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g== dependencies: chalk "^2.4.1" @@ -5609,12 +5225,12 @@ license-checker@^25.0.1: lines-and-columns@^1.1.6: version "1.2.4" - resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^9.0.0: version "9.0.5" - resolved "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== dependencies: cli-truncate "^5.0.0" @@ -5626,7 +5242,7 @@ listr2@^9.0.0: loader-utils@^2.0.0: version "2.0.4" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" @@ -5635,41 +5251,41 @@ loader-utils@^2.0.0: locate-path@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== dependencies: p-locate "^4.1.0" locate-path@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash-es@^4.17.21: version "4.17.21" - resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== lodash.merge@^4.6.2: version "4.6.2" - resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== lodash.sortby@^4.7.0: version "4.7.0" - resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" - resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.0.0: version "4.1.0" - resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" @@ -5677,7 +5293,7 @@ log-symbols@^4.0.0: log-update@^6.1.0: version "6.1.0" - resolved "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-6.1.0.tgz#1a04ff38166f94647ae1af562f4bd6a15b1b7cd4" integrity sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w== dependencies: ansi-escapes "^7.0.0" @@ -5688,89 +5304,89 @@ log-update@^6.1.0: loose-envify@^1.0.0, loose-envify@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" lower-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-2.0.2.tgz#64c2324a2250bf7c37c5901e76a5b5309301160b" integrity sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg== dependencies: tslib "^2.0.3" lower-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== dependencies: tslib "^2.0.3" lru-cache@^10.2.0: version "10.4.3" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^5.1.1: version "5.1.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== dependencies: yallist "^3.0.2" make-dir@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: semver "^7.5.3" make-error@^1.1.1: version "1.3.6" - resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== makeerror@1.0.12: version "1.0.12" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== dependencies: tmpl "1.0.5" map-cache@^0.2.0: version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== memoize-one@^4.0.0: version "4.1.0" - resolved "https://registry.npmjs.org/memoize-one/-/memoize-one-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== merge-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== meros@^1.2.1, meros@^1.3.2: version "1.3.2" - resolved "https://registry.npmjs.org/meros/-/meros-1.3.2.tgz" + resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.2.tgz#4cb0d7f3d22074c6e7ae1d66f72c080bca2e414b" integrity sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A== micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" @@ -5778,94 +5394,94 @@ micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: mimic-fn@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== mimic-function@^5.0.0: version "5.0.1" - resolved "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" - resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== mkdirp@^0.5.1: version "0.5.6" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== dependencies: minimist "^1.2.6" ms@^2.1.1, ms@^2.1.3: version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== nanoid@^3.3.6: version "3.3.11" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== napi-postinstall@^0.3.0: version "0.3.4" - resolved "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz" + resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" integrity sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== natural-compare@^1.4.0: version "1.4.0" - resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -next@16.0.3: - version "16.0.3" - resolved "https://registry.npmjs.org/next/-/next-16.0.3.tgz" - integrity sha512-Ka0/iNBblPFcIubTA1Jjh6gvwqfjrGq1Y2MTI5lbjeLIAfmC+p5bQmojpRZqgHHVu5cG4+qdIiwXiBSm/8lZ3w== +next@^16.0.8: + version "16.0.8" + resolved "https://registry.yarnpkg.com/next/-/next-16.0.8.tgz#10c0289e76e8617a8ad991210ec7402cb9cba87b" + integrity sha512-LmcZzG04JuzNXi48s5P+TnJBsTGPJunViNKV/iE4uM6kstjTQsQhvsAv+xF6MJxU2Pr26tl15eVbp0jQnsv6/g== dependencies: - "@next/env" "16.0.3" + "@next/env" "16.0.8" "@swc/helpers" "0.5.15" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.0.3" - "@next/swc-darwin-x64" "16.0.3" - "@next/swc-linux-arm64-gnu" "16.0.3" - "@next/swc-linux-arm64-musl" "16.0.3" - "@next/swc-linux-x64-gnu" "16.0.3" - "@next/swc-linux-x64-musl" "16.0.3" - "@next/swc-win32-arm64-msvc" "16.0.3" - "@next/swc-win32-x64-msvc" "16.0.3" + "@next/swc-darwin-arm64" "16.0.8" + "@next/swc-darwin-x64" "16.0.8" + "@next/swc-linux-arm64-gnu" "16.0.8" + "@next/swc-linux-arm64-musl" "16.0.8" + "@next/swc-linux-x64-gnu" "16.0.8" + "@next/swc-linux-x64-musl" "16.0.8" + "@next/swc-win32-arm64-msvc" "16.0.8" + "@next/swc-win32-x64-msvc" "16.0.8" sharp "^0.34.4" no-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== dependencies: lower-case "^2.0.2" @@ -5873,24 +5489,24 @@ no-case@^3.0.4: node-addon-api@^7.0.0: version "7.1.1" - resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== node-domexception@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== node-fetch@^2.7.0: version "2.7.0" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" node-fetch@^3.3.2: version "3.3.2" - resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== dependencies: data-uri-to-buffer "^4.0.0" @@ -5899,17 +5515,17 @@ node-fetch@^3.3.2: node-int64@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.27: version "2.0.27" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== nopt@^4.0.1: version "4.0.3" - resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" @@ -5917,7 +5533,7 @@ nopt@^4.0.1: normalize-package-data@^2.0.0: version "2.5.0" - resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== dependencies: hosted-git-info "^2.1.4" @@ -5927,51 +5543,51 @@ normalize-package-data@^2.0.0: normalize-path@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== dependencies: remove-trailing-separator "^1.0.1" normalize-path@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== npm-normalize-package-bin@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== npm-run-path@^4.0.1: version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== dependencies: path-key "^3.0.0" nullthrows@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" - resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-keys@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4, object.assign@^4.1.7: version "4.1.7" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== dependencies: call-bind "^1.0.8" @@ -5983,7 +5599,7 @@ object.assign@^4.1.4, object.assign@^4.1.7: object.entries@^1.1.9: version "1.1.9" - resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== dependencies: call-bind "^1.0.8" @@ -5993,7 +5609,7 @@ object.entries@^1.1.9: object.fromentries@^2.0.8: version "2.0.8" - resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.8.tgz#f7195d8a9b97bd95cbc1999ea939ecd1a2b00c65" integrity sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ== dependencies: call-bind "^1.0.7" @@ -6003,7 +5619,7 @@ object.fromentries@^2.0.8: object.groupby@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.3.tgz#9b125c36238129f6f7b61954a1e7176148d5002e" integrity sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ== dependencies: call-bind "^1.0.7" @@ -6012,7 +5628,7 @@ object.groupby@^1.0.3: object.values@^1.1.6, object.values@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== dependencies: call-bind "^1.0.8" @@ -6022,28 +5638,28 @@ object.values@^1.1.6, object.values@^1.2.1: once@^1.3.0: version "1.4.0" - resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" onetime@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" onetime@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" integrity sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ== dependencies: mimic-function "^5.0.0" optimism@^0.18.0: version "0.18.1" - resolved "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.1.tgz#5cf16847921413dbb0ac809907370388b9c6335f" integrity sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ== dependencies: "@wry/caches" "^1.0.0" @@ -6053,7 +5669,7 @@ optimism@^0.18.0: optionator@^0.9.3: version "0.9.4" - resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== dependencies: deep-is "^0.1.3" @@ -6065,17 +5681,17 @@ optionator@^0.9.3: os-homedir@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== os-tmpdir@^1.0.0: version "1.0.2" - resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== osenv@^0.1.4: version "0.1.5" - resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" @@ -6083,7 +5699,7 @@ osenv@^0.1.4: own-keys@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== dependencies: get-intrinsic "^1.2.6" @@ -6092,45 +5708,45 @@ own-keys@^1.0.1: p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-limit@^2.2.0: version "2.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== dependencies: p-try "^2.0.0" p-locate@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== dependencies: p-limit "^2.2.0" p-locate@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== package-json-from-dist@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== param-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== dependencies: dot-case "^3.0.4" @@ -6138,14 +5754,14 @@ param-case@^3.0.4: parent-module@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== dependencies: callsites "^3.0.0" parse-filepath@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" @@ -6154,7 +5770,7 @@ parse-filepath@^1.0.2: parse-json@^5.2.0: version "5.2.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== dependencies: "@babel/code-frame" "^7.0.0" @@ -6164,7 +5780,7 @@ parse-json@^5.2.0: pascal-case@^3.1.2: version "3.1.2" - resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: no-case "^3.0.4" @@ -6172,7 +5788,7 @@ pascal-case@^3.1.2: path-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: dot-case "^3.0.4" @@ -6180,39 +5796,39 @@ path-case@^3.0.4: path-exists@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" - resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" - resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" - resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" + resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" path-scurry@^1.11.1: version "1.11.1" - resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" @@ -6220,49 +5836,49 @@ path-scurry@^1.11.1: path-to-regexp@8.2.0: version "8.2.0" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== path-type@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== picocolors@^1.0.0, picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.3.1: version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== picomatch@^4.0.2, picomatch@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== pirates@^4.0.7: version "4.0.7" - resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== pkg-dir@^4.2.0: version "4.2.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== dependencies: find-up "^4.0.0" possible-typed-array-names@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== postcss@8.4.31: version "8.4.31" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" @@ -6271,17 +5887,17 @@ postcss@8.4.31: prelude-ls@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.6.2" - resolved "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz" - integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ== + version "3.7.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" + integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== pretty-format@30.2.0: version "30.2.0" - resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== dependencies: "@jest/schemas" "30.0.5" @@ -6290,14 +5906,14 @@ pretty-format@30.2.0: promise@^7.1.1: version "7.3.1" - resolved "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== dependencies: asap "~2.0.3" prop-types@^15.8.1: version "15.8.1" - resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== dependencies: loose-envify "^1.4.0" @@ -6306,22 +5922,22 @@ prop-types@^15.8.1: punycode@^2.1.0: version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== pure-rand@^7.0.0: version "7.0.1" - resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz" + resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-7.0.1.tgz#6f53a5a9e3e4a47445822af96821ca509ed37566" integrity sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== queue-microtask@^1.2.2: version "1.2.3" - resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== rc-cascader@~3.34.0: version "3.34.0" - resolved "https://registry.npmjs.org/rc-cascader/-/rc-cascader-3.34.0.tgz" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.34.0.tgz#56f936ab6b1229bab7d558701ce9b9e96536582c" integrity sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag== dependencies: "@babel/runtime" "^7.25.7" @@ -6332,7 +5948,7 @@ rc-cascader@~3.34.0: rc-checkbox@~3.5.0: version "3.5.0" - resolved "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-3.5.0.tgz" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz#3ae2441e3a321774d390f76539e706864fcf5ff0" integrity sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg== dependencies: "@babel/runtime" "^7.10.1" @@ -6341,7 +5957,7 @@ rc-checkbox@~3.5.0: rc-collapse@~3.9.0: version "3.9.0" - resolved "https://registry.npmjs.org/rc-collapse/-/rc-collapse-3.9.0.tgz" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.9.0.tgz#972404ce7724e1c9d1d2476543e1175404a36806" integrity sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== dependencies: "@babel/runtime" "^7.10.1" @@ -6351,7 +5967,7 @@ rc-collapse@~3.9.0: rc-dialog@~9.6.0: version "9.6.0" - resolved "https://registry.npmjs.org/rc-dialog/-/rc-dialog-9.6.0.tgz" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== dependencies: "@babel/runtime" "^7.10.1" @@ -6362,7 +5978,7 @@ rc-dialog@~9.6.0: rc-drawer@~7.3.0: version "7.3.0" - resolved "https://registry.npmjs.org/rc-drawer/-/rc-drawer-7.3.0.tgz" + resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.3.0.tgz#1bb5fe5f9da38b6a2b2a7dffc9fcb647252a328f" integrity sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg== dependencies: "@babel/runtime" "^7.23.9" @@ -6373,7 +5989,7 @@ rc-drawer@~7.3.0: rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: version "4.2.1" - resolved "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-4.2.1.tgz" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz#44729eb2a4272e0353d31ac060da21e606accb1c" integrity sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== dependencies: "@babel/runtime" "^7.18.3" @@ -6383,7 +5999,7 @@ rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: rc-field-form@~2.7.1: version "2.7.1" - resolved "https://registry.npmjs.org/rc-field-form/-/rc-field-form-2.7.1.tgz" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.7.1.tgz#8bb1d7c6bf40e2b99b494816972252c327d9b5a0" integrity sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A== dependencies: "@babel/runtime" "^7.18.0" @@ -6392,7 +6008,7 @@ rc-field-form@~2.7.1: rc-image@~7.12.0: version "7.12.0" - resolved "https://registry.npmjs.org/rc-image/-/rc-image-7.12.0.tgz" + resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe" integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q== dependencies: "@babel/runtime" "^7.11.2" @@ -6404,7 +6020,7 @@ rc-image@~7.12.0: rc-input-number@~9.5.0: version "9.5.0" - resolved "https://registry.npmjs.org/rc-input-number/-/rc-input-number-9.5.0.tgz" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38" integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag== dependencies: "@babel/runtime" "^7.10.1" @@ -6415,7 +6031,7 @@ rc-input-number@~9.5.0: rc-input@~1.8.0: version "1.8.0" - resolved "https://registry.npmjs.org/rc-input/-/rc-input-1.8.0.tgz" + resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974" integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA== dependencies: "@babel/runtime" "^7.11.1" @@ -6424,7 +6040,7 @@ rc-input@~1.8.0: rc-mentions@~2.20.0: version "2.20.0" - resolved "https://registry.npmjs.org/rc-mentions/-/rc-mentions-2.20.0.tgz" + resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7" integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ== dependencies: "@babel/runtime" "^7.22.5" @@ -6437,7 +6053,7 @@ rc-mentions@~2.20.0: rc-menu@~9.16.0, rc-menu@~9.16.1: version "9.16.1" - resolved "https://registry.npmjs.org/rc-menu/-/rc-menu-9.16.1.tgz" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.16.1.tgz#9df1168e41d87dc7164c582173e1a1d32011899f" integrity sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== dependencies: "@babel/runtime" "^7.10.1" @@ -6449,7 +6065,7 @@ rc-menu@~9.16.0, rc-menu@~9.16.1: rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.5: version "2.9.5" - resolved "https://registry.npmjs.org/rc-motion/-/rc-motion-2.9.5.tgz" + resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== dependencies: "@babel/runtime" "^7.11.1" @@ -6458,7 +6074,7 @@ rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motio rc-notification@~5.6.4: version "5.6.4" - resolved "https://registry.npmjs.org/rc-notification/-/rc-notification-5.6.4.tgz" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544" integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== dependencies: "@babel/runtime" "^7.10.1" @@ -6468,7 +6084,7 @@ rc-notification@~5.6.4: rc-overflow@^1.3.1, rc-overflow@^1.3.2: version "1.5.0" - resolved "https://registry.npmjs.org/rc-overflow/-/rc-overflow-1.5.0.tgz" + resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.5.0.tgz#02e58a15199e392adfcc87e0d6e9e7c8e57f2771" integrity sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg== dependencies: "@babel/runtime" "^7.11.1" @@ -6478,7 +6094,7 @@ rc-overflow@^1.3.1, rc-overflow@^1.3.2: rc-pagination@~5.1.0: version "5.1.0" - resolved "https://registry.npmjs.org/rc-pagination/-/rc-pagination-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-5.1.0.tgz#a6e63a2c5db29e62f991282eb18a2d3ee725ba8b" integrity sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ== dependencies: "@babel/runtime" "^7.10.1" @@ -6487,7 +6103,7 @@ rc-pagination@~5.1.0: rc-picker@~4.11.3: version "4.11.3" - resolved "https://registry.npmjs.org/rc-picker/-/rc-picker-4.11.3.tgz" + resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.11.3.tgz#7e7e3ad83aa461c284b8391c697492d1c34d2cb8" integrity sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== dependencies: "@babel/runtime" "^7.24.7" @@ -6499,7 +6115,7 @@ rc-picker@~4.11.3: rc-progress@~4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/rc-progress/-/rc-progress-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== dependencies: "@babel/runtime" "^7.10.1" @@ -6508,7 +6124,7 @@ rc-progress@~4.0.0: rc-rate@~2.13.1: version "2.13.1" - resolved "https://registry.npmjs.org/rc-rate/-/rc-rate-2.13.1.tgz" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.1.tgz#29af7a3d4768362e9d4388f955a8b6389526b7fd" integrity sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== dependencies: "@babel/runtime" "^7.10.1" @@ -6517,7 +6133,7 @@ rc-rate@~2.13.1: rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3: version "1.4.3" - resolved "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz" + resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== dependencies: "@babel/runtime" "^7.20.7" @@ -6527,7 +6143,7 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-segmented@~2.7.0: version "2.7.0" - resolved "https://registry.npmjs.org/rc-segmented/-/rc-segmented-2.7.0.tgz" + resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.7.0.tgz#f56c2044abf8f03958b3a9a9d32987f10dcc4fc4" integrity sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA== dependencies: "@babel/runtime" "^7.11.1" @@ -6537,7 +6153,7 @@ rc-segmented@~2.7.0: rc-select@~14.16.2, rc-select@~14.16.8: version "14.16.8" - resolved "https://registry.npmjs.org/rc-select/-/rc-select-14.16.8.tgz" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97" integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== dependencies: "@babel/runtime" "^7.10.1" @@ -6550,7 +6166,7 @@ rc-select@~14.16.2, rc-select@~14.16.8: rc-slider@~11.1.9: version "11.1.9" - resolved "https://registry.npmjs.org/rc-slider/-/rc-slider-11.1.9.tgz" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.9.tgz#d872130fbf4ec51f28543d62e90451091d6f5208" integrity sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A== dependencies: "@babel/runtime" "^7.10.1" @@ -6559,7 +6175,7 @@ rc-slider@~11.1.9: rc-steps@~6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/rc-steps/-/rc-steps-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== dependencies: "@babel/runtime" "^7.16.7" @@ -6568,7 +6184,7 @@ rc-steps@~6.0.1: rc-switch@~4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/rc-switch/-/rc-switch-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== dependencies: "@babel/runtime" "^7.21.0" @@ -6577,7 +6193,7 @@ rc-switch@~4.1.0: rc-table@~7.54.0: version "7.54.0" - resolved "https://registry.npmjs.org/rc-table/-/rc-table-7.54.0.tgz" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.54.0.tgz#dedd4ea18d1189f2acdf90a80f04d8ca0111e16a" integrity sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw== dependencies: "@babel/runtime" "^7.10.1" @@ -6589,7 +6205,7 @@ rc-table@~7.54.0: rc-tabs@~15.7.0: version "15.7.0" - resolved "https://registry.npmjs.org/rc-tabs/-/rc-tabs-15.7.0.tgz" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.7.0.tgz#14ca2ee6213d00491a8b67ae26e2d35c256bf19a" integrity sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA== dependencies: "@babel/runtime" "^7.11.2" @@ -6602,7 +6218,7 @@ rc-tabs@~15.7.0: rc-textarea@~1.10.0, rc-textarea@~1.10.2: version "1.10.2" - resolved "https://registry.npmjs.org/rc-textarea/-/rc-textarea-1.10.2.tgz" + resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.2.tgz#459e3574a95c32939c6793045a1e4db04cb514cc" integrity sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ== dependencies: "@babel/runtime" "^7.10.1" @@ -6613,7 +6229,7 @@ rc-textarea@~1.10.0, rc-textarea@~1.10.2: rc-tooltip@~6.4.0: version "6.4.0" - resolved "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-6.4.0.tgz" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz#e832ed0392872025e59928cfc1ad9045656467fd" integrity sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g== dependencies: "@babel/runtime" "^7.11.2" @@ -6623,7 +6239,7 @@ rc-tooltip@~6.4.0: rc-tree-select@~5.27.0: version "5.27.0" - resolved "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-5.27.0.tgz" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz#3daa62972ae80846dac96bf4776d1a9dc9c7c4c6" integrity sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww== dependencies: "@babel/runtime" "^7.25.7" @@ -6634,7 +6250,7 @@ rc-tree-select@~5.27.0: rc-tree@~5.13.0, rc-tree@~5.13.1: version "5.13.1" - resolved "https://registry.npmjs.org/rc-tree/-/rc-tree-5.13.1.tgz" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.13.1.tgz#f36a33a94a1282f4b09685216c01487089748910" integrity sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== dependencies: "@babel/runtime" "^7.10.1" @@ -6645,7 +6261,7 @@ rc-tree@~5.13.0, rc-tree@~5.13.1: rc-upload@~4.11.0: version "4.11.0" - resolved "https://registry.npmjs.org/rc-upload/-/rc-upload-4.11.0.tgz" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.11.0.tgz#c2ced905a8b38b3e5c48493d388ca0e8373de18b" integrity sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA== dependencies: "@babel/runtime" "^7.18.3" @@ -6654,7 +6270,7 @@ rc-upload@~4.11.0: rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3, rc-util@^5.44.4: version "5.44.4" - resolved "https://registry.npmjs.org/rc-util/-/rc-util-5.44.4.tgz" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== dependencies: "@babel/runtime" "^7.18.3" @@ -6662,7 +6278,7 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18. rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: version "3.19.2" - resolved "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz" + resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz#1dd2d782c9a3ccbe537bb873447d73f83af8de0f" integrity sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA== dependencies: "@babel/runtime" "^7.20.0" @@ -6672,23 +6288,23 @@ rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: react-cookie@^8.0.1: version "8.0.1" - resolved "https://registry.npmjs.org/react-cookie/-/react-cookie-8.0.1.tgz" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.0.1.tgz#0d7f5112c9003b61c70d3d53f1f0a5b741e9f2e9" integrity sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA== dependencies: "@types/hoist-non-react-statics" "^3.3.6" hoist-non-react-statics "^3.3.2" universal-cookie "^8.0.0" -react-dom@19.2.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz" - integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== +react-dom@^19.2.1: + version "19.2.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.1.tgz#ce3527560bda4f997e47d10dab754825b3061f59" + integrity sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg== dependencies: scheduler "^0.27.0" react-highlight-words@^0.21.0: version "0.21.0" - resolved "https://registry.npmjs.org/react-highlight-words/-/react-highlight-words-0.21.0.tgz" + resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.21.0.tgz#a109acdf7dc6fac3ed7db82e9cba94e8d65c281c" integrity sha512-SdWEeU9fIINArEPO1rO5OxPyuhdEKZQhHzZZP1ie6UeXQf+CjycT1kWaB+9bwGcVbR0NowuHK3RqgqNg6bgBDQ== dependencies: highlight-words-core "^1.2.0" @@ -6696,35 +6312,35 @@ react-highlight-words@^0.21.0: react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== react-is@^18.2.0, react-is@^18.3.1: version "18.3.1" - resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-is@^19.2.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.0.tgz" - integrity sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA== +react-is@^19.2.1: + version "19.2.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.1.tgz#712fa6ee30c8398dc34260017325dad92b9224c5" + integrity sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA== react-test-renderer@^19.2.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-19.2.0.tgz" - integrity sha512-zLCFMHFE9vy/w3AxO0zNxy6aAupnCuLSVOJYDe/Tp+ayGI1f2PLQsFVPANSD42gdSbmYx5oN+1VWDhcXtq7hAQ== + version "19.2.1" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.1.tgz#a2c91e0a47539431f1c7979c1076b1a25a8accdb" + integrity sha512-xsyf515ij+d8Rs/tsDZSJYXn+GdYO/IOw9BVFtjJbrrFR+dL1yZQQN1ChxY6otYAsCeAHhU0XsKyJUzP6omyvQ== dependencies: - react-is "^19.2.0" + react-is "^19.2.1" scheduler "^0.27.0" -react@19.2.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz" - integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== +react@^19.2.1: + version "19.2.1" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.1.tgz#8600fa205e58e2e807f6ef431c9f6492591a2700" + integrity sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw== read-installed@~4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/read-installed/-/read-installed-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" integrity sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ== dependencies: debuglog "^1.0.1" @@ -6738,7 +6354,7 @@ read-installed@~4.0.3: read-package-json@^2.0.0: version "2.1.2" - resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== dependencies: glob "^7.1.1" @@ -6748,7 +6364,7 @@ read-package-json@^2.0.0: readdir-scoped-modules@^1.0.0: version "1.1.0" - resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== dependencies: debuglog "^1.0.1" @@ -6758,7 +6374,7 @@ readdir-scoped-modules@^1.0.0: reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: version "1.0.10" - resolved "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== dependencies: call-bind "^1.0.8" @@ -6772,7 +6388,7 @@ reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: version "1.5.4" - resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== dependencies: call-bind "^1.0.8" @@ -6784,7 +6400,7 @@ regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: relay-runtime@12.0.0: version "12.0.0" - resolved "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz" + resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" integrity sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug== dependencies: "@babel/runtime" "^7.0.0" @@ -6793,59 +6409,54 @@ relay-runtime@12.0.0: remedial@^1.0.7: version "1.0.8" - resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== remove-trailing-separator@^1.0.1: version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== remove-trailing-spaces@^1.0.6: version "1.0.9" - resolved "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz#39c270a309ea16fda84253ffbdeb1b5afa0aa271" integrity sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA== require-directory@^2.1.1: version "2.1.1" - resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - resize-observer-polyfill@^1.5.1: version "1.5.1" - resolved "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== resolve-cwd@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== dependencies: resolve-from "^5.0.0" resolve-from@5.0.0, resolve-from@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== resolve-from@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== resolve-pkg-maps@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== resolve@^1.10.0, resolve@^1.22.4: version "1.22.11" - resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== dependencies: is-core-module "^2.16.1" @@ -6854,7 +6465,7 @@ resolve@^1.10.0, resolve@^1.22.4: resolve@^2.0.0-next.5: version "2.0.0-next.5" - resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: is-core-module "^2.13.0" @@ -6863,7 +6474,7 @@ resolve@^2.0.0-next.5: restore-cursor@^5.0.0: version "5.1.0" - resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-5.1.0.tgz#0766d95699efacb14150993f55baf0953ea1ebe7" integrity sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA== dependencies: onetime "^7.0.0" @@ -6871,31 +6482,31 @@ restore-cursor@^5.0.0: reusify@^1.0.4: version "1.1.0" - resolved "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.1.0.tgz#0fe13b9522e1473f51b558ee796e08f11f9b489f" integrity sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw== rfdc@^1.4.1: version "1.4.1" - resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== run-parallel@^1.1.9: version "1.2.0" - resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== dependencies: queue-microtask "^1.2.2" rxjs@7.8.2, rxjs@^7.8.2: version "7.8.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b" integrity sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA== dependencies: tslib "^2.1.0" safe-array-concat@^1.1.3: version "1.1.3" - resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== dependencies: call-bind "^1.0.8" @@ -6906,7 +6517,7 @@ safe-array-concat@^1.1.3: safe-push-apply@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== dependencies: es-errors "^1.3.0" @@ -6914,7 +6525,7 @@ safe-push-apply@^1.0.0: safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== dependencies: call-bound "^1.0.2" @@ -6923,58 +6534,53 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: safe-stable-stringify@^2.4.3: version "2.5.0" - resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" - resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== scheduler@^0.27.0: version "0.27.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.27.0.tgz#0c4ef82d67d1e5c1e359e8fc76d3a87f045fe5bd" integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== scroll-into-view-if-needed@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz#fa9524518c799b45a2ef6bbffb92bcad0296d01f" integrity sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ== dependencies: compute-scroll-into-view "^3.0.2" "semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^6.3.1: version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: version "7.7.3" - resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== sentence-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== dependencies: no-case "^3.0.4" tslib "^2.0.3" upper-case-first "^2.0.2" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - set-function-length@^1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== dependencies: define-data-property "^1.1.4" @@ -6986,7 +6592,7 @@ set-function-length@^1.2.2: set-function-name@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== dependencies: define-data-property "^1.1.4" @@ -6996,7 +6602,7 @@ set-function-name@^2.0.2: set-proto@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== dependencies: dunder-proto "^1.0.1" @@ -7005,12 +6611,12 @@ set-proto@^1.0.0: setimmediate@^1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== sharp@^0.34.4, sharp@^0.34.5: version "0.34.5" - resolved "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== dependencies: "@img/colour" "^1.0.0" @@ -7044,24 +6650,24 @@ sharp@^0.34.4, sharp@^0.34.5: shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== shell-quote@1.8.3, shell-quote@^1.7.3: version "1.8.3" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== side-channel-list@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: es-errors "^1.3.0" @@ -7069,7 +6675,7 @@ side-channel-list@^1.0.0: side-channel-map@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: call-bound "^1.0.2" @@ -7079,7 +6685,7 @@ side-channel-map@^1.0.1: side-channel-weakmap@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== dependencies: call-bound "^1.0.2" @@ -7090,7 +6696,7 @@ side-channel-weakmap@^1.0.2: side-channel@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: es-errors "^1.3.0" @@ -7101,27 +6707,27 @@ side-channel@^1.1.0: signal-exit@^3.0.3: version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.0.1, signal-exit@^4.1.0: version "4.1.0" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== signedsource@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== slash@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== slice-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-7.1.2.tgz#adf7be70aa6d72162d907cd0e6d5c11f507b5403" integrity sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w== dependencies: ansi-styles "^6.2.1" @@ -7129,12 +6735,12 @@ slice-ansi@^7.1.0: slide@~1.1.3: version "1.1.6" - resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== snake-case@^3.0.4: version "3.0.4" - resolved "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== dependencies: dot-case "^3.0.4" @@ -7142,12 +6748,12 @@ snake-case@^3.0.4: source-map-js@^1.0.2: version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== source-map-support@0.5.13: version "0.5.13" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== dependencies: buffer-from "^1.0.0" @@ -7155,12 +6761,12 @@ source-map-support@0.5.13: source-map@^0.6.0: version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-compare@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/spdx-compare/-/spdx-compare-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7" integrity sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A== dependencies: array-find-index "^1.0.2" @@ -7169,7 +6775,7 @@ spdx-compare@^1.0.0: spdx-correct@^3.0.0: version "3.2.0" - resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" @@ -7177,12 +6783,12 @@ spdx-correct@^3.0.0: spdx-exceptions@^2.1.0: version "2.5.0" - resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" @@ -7190,17 +6796,17 @@ spdx-expression-parse@^3.0.0: spdx-license-ids@^3.0.0: version "3.0.22" - resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== spdx-ranges@^2.0.0: version "2.1.1" - resolved "https://registry.npmjs.org/spdx-ranges/-/spdx-ranges-2.1.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-ranges/-/spdx-ranges-2.1.1.tgz#87573927ba51e92b3f4550ab60bfc83dd07bac20" integrity sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA== spdx-satisfies@^4.0.0: version "4.0.1" - resolved "https://registry.npmjs.org/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz" + resolved "https://registry.yarnpkg.com/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz#9a09a68d80f5f1a31cfaebb384b0c6009e4969fe" integrity sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA== dependencies: spdx-compare "^1.0.0" @@ -7209,31 +6815,31 @@ spdx-satisfies@^4.0.0: sponge-case@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-1.0.1.tgz#260833b86453883d974f84854cdb63aecc5aef4c" integrity sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA== dependencies: tslib "^2.0.3" sprintf-js@~1.0.2: version "1.0.3" - resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== stable-hash@^0.0.5: version "0.0.5" - resolved "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz" + resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== stack-utils@^2.0.6: version "2.0.6" - resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== dependencies: escape-string-regexp "^2.0.0" stop-iteration-iterator@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== dependencies: es-errors "^1.3.0" @@ -7241,17 +6847,17 @@ stop-iteration-iterator@^1.1.0: string-convert@^0.2.0: version "0.2.1" - resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz" + resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== string-env-interpolation@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== string-length@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== dependencies: char-regex "^1.0.2" @@ -7259,7 +6865,7 @@ string-length@^4.0.2: "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -7268,7 +6874,7 @@ string-length@^4.0.2: string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" - resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" @@ -7277,7 +6883,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" - resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" @@ -7286,7 +6892,7 @@ string-width@^5.0.1, string-width@^5.1.2: string-width@^7.0.0: version "7.2.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" @@ -7295,7 +6901,7 @@ string-width@^7.0.0: string-width@^8.0.0: version "8.1.0" - resolved "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== dependencies: get-east-asian-width "^1.3.0" @@ -7303,7 +6909,7 @@ string-width@^8.0.0: string.prototype.includes@^2.0.1: version "2.0.1" - resolved "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz#eceef21283640761a81dbe16d6c7171a4edf7d92" integrity sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg== dependencies: call-bind "^1.0.7" @@ -7312,7 +6918,7 @@ string.prototype.includes@^2.0.1: string.prototype.matchall@^4.0.12: version "4.0.12" - resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz#6c88740e49ad4956b1332a911e949583a275d4c0" integrity sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA== dependencies: call-bind "^1.0.8" @@ -7331,7 +6937,7 @@ string.prototype.matchall@^4.0.12: string.prototype.repeat@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== dependencies: define-properties "^1.1.3" @@ -7339,7 +6945,7 @@ string.prototype.repeat@^1.0.0: string.prototype.trim@^1.2.10: version "1.2.10" - resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== dependencies: call-bind "^1.0.8" @@ -7352,7 +6958,7 @@ string.prototype.trim@^1.2.10: string.prototype.trimend@^1.0.9: version "1.0.9" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== dependencies: call-bind "^1.0.8" @@ -7362,7 +6968,7 @@ string.prototype.trimend@^1.0.9: string.prototype.trimstart@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== dependencies: call-bind "^1.0.7" @@ -7371,101 +6977,101 @@ string.prototype.trimstart@^1.0.8: "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.2" - resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== dependencies: ansi-regex "^6.0.1" strip-bom@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== strip-bom@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== strip-final-newline@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== strip-json-comments@^3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== styled-jsx@5.1.6: version "5.1.6" - resolved "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" integrity sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA== dependencies: client-only "0.0.1" stylis@^4.3.4: version "4.3.6" - resolved "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== supports-color@8.1.1, supports-color@^8.1.1: version "8.1.1" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-color@^5.3.0: version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== swap-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-2.0.2.tgz#671aedb3c9c137e2985ef51c51f9e98445bf70d9" integrity sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw== dependencies: tslib "^2.0.3" swr@^2.0.0: - version "2.3.6" - resolved "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz" - integrity sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw== + version "2.3.7" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.7.tgz#93ca89c9c06a6a8dab72e9d8e85a687123f40356" + integrity sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g== dependencies: dequal "^2.0.3" use-sync-external-store "^1.4.0" sync-fetch@0.6.0-2: version "0.6.0-2" - resolved "https://registry.npmjs.org/sync-fetch/-/sync-fetch-0.6.0-2.tgz" + resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.6.0-2.tgz#d82d6dc8efaf2d103a9015e7bd7ba0bfc8e078f2" integrity sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A== dependencies: node-fetch "^3.3.2" @@ -7474,14 +7080,14 @@ sync-fetch@0.6.0-2: synckit@^0.11.8: version "0.11.11" - resolved "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== dependencies: "@pkgr/core" "^0.2.9" test-exclude@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== dependencies: "@istanbuljs/schema" "^0.1.2" @@ -7490,17 +7096,17 @@ test-exclude@^6.0.0: throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: version "5.0.2" - resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" integrity sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A== timeout-signal@^2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/timeout-signal/-/timeout-signal-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/timeout-signal/-/timeout-signal-2.0.0.tgz#23207ea448d50258bb0defe3beea4a467643abba" integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== -tinyglobby@^0.2.13: +tinyglobby@^0.2.13, tinyglobby@^0.2.15: version "0.2.15" - resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== dependencies: fdir "^6.5.0" @@ -7508,56 +7114,56 @@ tinyglobby@^0.2.13: title-case@^3.0.3: version "3.0.3" - resolved "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-3.0.3.tgz#bc689b46f02e411f1d1e1d081f7c3deca0489982" integrity sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA== dependencies: tslib "^2.0.3" tmpl@1.0.5: version "1.0.5" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-regex-range@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toggle-selection@^1.0.6: version "1.0.6" - resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== tr46@~0.0.3: version "0.0.3" - resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== tree-kill@1.2.2: version "1.2.2" - resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== treeify@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== ts-api-utils@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== ts-log@^2.2.3: version "2.2.7" - resolved "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" integrity sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg== ts-node@^10.9.1: version "10.9.2" - resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== dependencies: "@cspotcode/source-map-support" "^0.8.0" @@ -7576,7 +7182,7 @@ ts-node@^10.9.1: tsconfig-paths@^3.15.0: version "3.15.0" - resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" @@ -7586,39 +7192,34 @@ tsconfig-paths@^3.15.0: tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1: version "2.8.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tslib@~2.4.0: - version "2.4.1" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== - tslib@~2.6.0: version "2.6.3" - resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== dependencies: prelude-ls "^1.2.1" type-detect@4.0.8: version "4.0.8" - resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== type-fest@^0.21.3: version "0.21.3" - resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== typed-array-buffer@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== dependencies: call-bound "^1.0.3" @@ -7627,7 +7228,7 @@ typed-array-buffer@^1.0.3: typed-array-byte-length@^1.0.3: version "1.0.3" - resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== dependencies: call-bind "^1.0.8" @@ -7638,7 +7239,7 @@ typed-array-byte-length@^1.0.3: typed-array-byte-offset@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== dependencies: available-typed-arrays "^1.0.7" @@ -7651,7 +7252,7 @@ typed-array-byte-offset@^1.0.4: typed-array-length@^1.0.7: version "1.0.7" - resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== dependencies: call-bind "^1.0.7" @@ -7662,28 +7263,28 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.46.0: - version "8.47.0" - resolved "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz" - integrity sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q== + version "8.49.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.49.0.tgz#4a8b608ae48c0db876c8fb2a2724839fc5a7147c" + integrity sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg== dependencies: - "@typescript-eslint/eslint-plugin" "8.47.0" - "@typescript-eslint/parser" "8.47.0" - "@typescript-eslint/typescript-estree" "8.47.0" - "@typescript-eslint/utils" "8.47.0" + "@typescript-eslint/eslint-plugin" "8.49.0" + "@typescript-eslint/parser" "8.49.0" + "@typescript-eslint/typescript-estree" "8.49.0" + "@typescript-eslint/utils" "8.49.0" typescript@^5.0.4: version "5.9.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ua-parser-js@^1.0.35: version "1.0.41" - resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.41.tgz#bd04dc9ec830fcf9e4fad35cf22dcedd2e3b4e9c" integrity sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug== unbox-primitive@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== dependencies: call-bound "^1.0.3" @@ -7693,31 +7294,31 @@ unbox-primitive@^1.1.0: unc-path-regex@^0.1.2: version "0.1.2" - resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" + resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== undici-types@~7.16.0: version "7.16.0" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== universal-cookie@^8.0.0: version "8.0.1" - resolved "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.0.1.tgz" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.0.1.tgz#19263fcda6889978a081c610a90fc395eeaeec64" integrity sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg== dependencies: cookie "^1.0.2" unixify@^1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" integrity sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg== dependencies: normalize-path "^2.1.1" unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: version "1.11.1" - resolved "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== dependencies: napi-postinstall "^0.3.0" @@ -7742,58 +7343,58 @@ unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" -update-browserslist-db@^1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz" - integrity sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A== +update-browserslist-db@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz#cfb4358afa08b3d5731a2ecd95eebf4ddef8033e" + integrity sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA== dependencies: escalade "^3.2.0" picocolors "^1.1.1" upper-case-first@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== dependencies: tslib "^2.0.3" upper-case@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== dependencies: tslib "^2.0.3" uri-js@^4.2.2: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" urlpattern-polyfill@^10.0.0: version "10.1.0" - resolved "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz#1b2517e614136c73ba32948d5e7a3a063cba8e74" integrity sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw== use-sync-external-store@^1.4.0: version "1.6.0" - resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== util-extend@^1.0.1: version "1.0.3" - resolved "https://registry.npmjs.org/util-extend/-/util-extend-1.0.3.tgz" + resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== v8-compile-cache-lib@^3.0.1: version "3.0.1" - resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== v8-to-istanbul@^9.0.1: version "9.3.0" - resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== dependencies: "@jridgewell/trace-mapping" "^0.3.12" @@ -7802,7 +7403,7 @@ v8-to-istanbul@^9.0.1: validate-npm-package-license@^3.0.1: version "3.0.4" - resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" @@ -7810,36 +7411,36 @@ validate-npm-package-license@^3.0.1: walker@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== dependencies: makeerror "1.0.12" warning@^4.0.3: version "4.0.3" - resolved "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== dependencies: loose-envify "^1.0.0" web-streams-polyfill@^3.0.3: version "3.3.3" - resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== webidl-conversions@^3.0.0: version "3.0.1" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== whatwg-mimetype@^4.0.0: version "4.0.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== whatwg-url@^5.0.0: version "5.0.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== dependencies: tr46 "~0.0.3" @@ -7847,7 +7448,7 @@ whatwg-url@^5.0.0: which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== dependencies: is-bigint "^1.1.0" @@ -7858,7 +7459,7 @@ which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: which-builtin-type@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== dependencies: call-bound "^1.0.2" @@ -7877,7 +7478,7 @@ which-builtin-type@^1.2.1: which-collection@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== dependencies: is-map "^2.0.3" @@ -7885,14 +7486,9 @@ which-collection@^1.0.2: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-module@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz" - integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== - which-typed-array@^1.1.16, which-typed-array@^1.1.19: version "1.1.19" - resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== dependencies: available-typed-arrays "^1.0.7" @@ -7905,19 +7501,19 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.19: which@^2.0.1: version "2.0.2" - resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" word-wrap@^1.2.5: version "1.2.5" - resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -7926,7 +7522,7 @@ word-wrap@^1.2.5: wrap-ansi@^6.2.0: version "6.2.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: ansi-styles "^4.0.0" @@ -7935,7 +7531,7 @@ wrap-ansi@^6.2.0: wrap-ansi@^7.0.0: version "7.0.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" @@ -7944,7 +7540,7 @@ wrap-ansi@^7.0.0: wrap-ansi@^8.1.0: version "8.1.0" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" @@ -7953,7 +7549,7 @@ wrap-ansi@^8.1.0: wrap-ansi@^9.0.0: version "9.0.2" - resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== dependencies: ansi-styles "^6.2.1" @@ -7962,12 +7558,12 @@ wrap-ansi@^9.0.0: wrappy@1: version "1.0.2" - resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^5.0.1: version "5.0.1" - resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== dependencies: imurmurhash "^0.1.4" @@ -7975,27 +7571,22 @@ write-file-atomic@^5.0.1: ws@^8.17.1, ws@^8.18.3: version "8.18.3" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - y18n@^5.0.5: version "5.0.8" - resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^3.0.2: version "3.1.1" - resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== yaml-loader@^0.8: version "0.8.1" - resolved "https://registry.npmjs.org/yaml-loader/-/yaml-loader-0.8.1.tgz" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.1.tgz#034f901147073cfc307cdcce8bd44c1547e60ba1" integrity sha512-BCEndnUoi3BaZmePkwGGe93txRxLgMhBa/gE725v1/GHnura8QvNs7c4+4C1yyhhKoj3Dg63M7IqhA++15j6ww== dependencies: javascript-stringify "^2.0.1" @@ -8003,26 +7594,18 @@ yaml-loader@^0.8: yaml "^2.0.0" yaml@^2.0.0, yaml@^2.3.1: - version "2.8.1" - resolved "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz" - integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" + version "2.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" + integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== yargs-parser@^21.1.1: version "21.1.1" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: version "17.7.2" - resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" @@ -8033,44 +7616,27 @@ yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - yn@3.1.1: version "3.1.1" - resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== yocto-queue@^0.1.0: version "0.1.0" - resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yoctocolors-cjs@^2.1.3: version "2.1.3" - resolved "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== "zod-validation-error@^3.5.0 || ^4.0.0": version "4.0.2" - resolved "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz#bc605eba49ce0fcd598c127fee1c236be3f22918" integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== "zod@^3.25.0 || ^4.0.0": - version "4.1.12" - resolved "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz" - integrity sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ== + version "4.1.13" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.13.tgz#93699a8afe937ba96badbb0ce8be6033c0a4b6b1" + integrity sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig== From ba28cda6ce3291de4e18754ed818952a4ebb9ae9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Dec 2025 18:40:34 +0100 Subject: [PATCH 608/878] Correct paths to binaries --- README.md | 4 ++-- backend/Dockerfile-production | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 64d29670..1d195f2a 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ To debug the [ASP.NET Core web application](https://learn.microsoft.com/en-us/aspnet/core/introduction-to-aspnet-core), attach Visual Studio Code to the `database-backend-*` container, [press `Ctrl+Shift+P`, select "Debug: Attach to a .NET 5+ or .NET Core process"](https://code.visualstudio.com/docs/csharp/debugging#_attaching-to-a-process), -and choose the process `/app/src/bin/Debug/net9.0/Database run` titled +and choose the process `/app/src/bin/Debug/net10.0/Database run` titled `Database` or alternatively navigate to the "Run and Debug" pane (`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the "Start Debugging" icon (`F5`), and select the same process as above. Then, for @@ -235,7 +235,7 @@ and that attempt to solve that. Those extensions don't work in our case though, as they try to restart `dotnet watch` themselves, instead of waiting for the polling file watcher of `dotnet watch` to restart -`/app/src/bin/Debug/net9.0/Database run` and attach to that process. +`/app/src/bin/Debug/net10.0/Database run` and attach to that process. ### Troubleshooting diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index c1927020..71422883 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -118,7 +118,7 @@ WORKDIR /app COPY \ --from=build \ - /app/src/bin/Release/net9.0/publish \ + /app/src/bin/Release/net10.0/publish \ ./ RUN \ From 01a2914a6f9c59555479a002398fb71a0a8f42b5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 9 Dec 2025 20:04:37 +0100 Subject: [PATCH 609/878] Do not ignore GPG files as we need them in the production build (they are copied into the Docker image via the `Include` in the csproj file) --- backend/.dockerignore | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/.dockerignore b/backend/.dockerignore index b8c6308a..f12b3ac4 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -3,7 +3,6 @@ **/.* **/*.md **/*.bak -**/*.gpg Dockerfile* **/core From b0b81c8bbff869a7e0f5ffe465fbf23f4863d930 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Dec 2025 12:09:54 +0100 Subject: [PATCH 610/878] Set a dotnet watch cleanup timeout of 5ms which sould solve the problem of the port not being released before it is tried to being used again --- docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 52fc0972..c053f684 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -67,7 +67,7 @@ services: command: [ "dotnet", "watch", "--non-interactive", - # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port. Just using `--non-interactive` is suggested in https://github.com/dotnet/sdk/issues/50965#issuecomment-3523103468 + "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx "--project", "/app/src/Database.csproj", "--", "run", @@ -122,6 +122,8 @@ services: - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - DOTNET_USE_POLLING_FILE_WATCHER=true + # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 + - DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS=5 # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose # - ASPNETCORE_Kestrel__Certificates__Default__Password=${SSL_CERTIFICATE_PASSWORD} # - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx From b6b65ee5fa4a81f5508bf1d9f1992e54dd244d1b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 22 Dec 2025 20:13:12 +0100 Subject: [PATCH 611/878] Avoid unnecessary name duplication in logger extensions --- backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs | 2 +- .../RecomputeGetHttpsResourceHashValuesMutation.cs | 2 +- .../ResponseApprovals/CreateResponseApprovalsMutation.cs | 2 +- .../ResponseApprovals/UpdateResponseApprovalsMutation.cs | 2 +- backend/src/Program.cs | 2 +- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/ResponseApprovalService.cs | 2 +- backend/src/Services/SigningService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 0112e5e5..5f9a5aa7 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -69,7 +69,7 @@ public sealed record AddDataApprovalPayload( IReadOnlyCollection? Errors ) : Payload; -public static partial class AddDataApprovalMutationLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Warning, diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 1cd07c27..1b433e2c 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -54,7 +54,7 @@ IFilterInputTypeDescriptor descriptor } } -public static partial class RecomputeGetHttpsResourceHashValuesMutationLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Warning, diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index baa9bd5e..69ec8d0a 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -36,7 +36,7 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public static partial class CreateResponseApprovalsMutationLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Warning, diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index dc6b8b5e..0ef866f5 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -35,7 +35,7 @@ IReadOnlyList Path ) : UserErrorBase(Code, Message, Path); -public static partial class UpdateResponseApprovalsMutationLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Warning, diff --git a/backend/src/Program.cs b/backend/src/Program.cs index c8fb9a93..4829e6ab 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -19,7 +19,7 @@ namespace Database; -public static partial class LoggerExtensions +public static partial class Log { [LoggerMessage( EventId = 0, diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 0f17bfb0..213e986f 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -10,7 +10,7 @@ namespace Database.Services; -public static partial class AccessRightServiceLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Information, diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 06f255d4..8450fbe8 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -16,7 +16,7 @@ namespace Database.Services; -public static partial class ResponseApprovalServiceLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Debug, diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index d97ac08c..2f64272a 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -15,7 +15,7 @@ public enum SigantureVerificationResult BAD_SIGNATURE } -public static partial class SigningServiceLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Debug, diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index aa54094e..f202dc22 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -12,7 +12,7 @@ namespace Database.Services; -public static partial class UserServiceLogging +public static partial class Log { [LoggerMessage( Level = LogLevel.Debug, From 3aeb8734ee691ddaa7340d08b5caeeb44e133965 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Dec 2025 09:27:41 +0100 Subject: [PATCH 612/878] Remove unnecessary scopes to protect users --- backend/src/Configuration/AuthConfiguration.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index f2d6ed6e..1e7e130e 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -218,11 +218,6 @@ X509Certificate2 signingCertificate // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims Scopes = { - OpenIddictConstants.Scopes.Address, - OpenIddictConstants.Scopes.Email, - OpenIddictConstants.Scopes.Phone, - OpenIddictConstants.Scopes.Profile, - OpenIddictConstants.Scopes.Roles, ReadApiScope, WriteApiScope }, From ba8e4b1765e4600e2c2725e5b7341130d687910a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Dec 2025 09:29:26 +0100 Subject: [PATCH 613/878] Add scope to manage database --- backend/src/Configuration/AuthConfiguration.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 1e7e130e..e1abdb19 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -20,6 +20,7 @@ public abstract class AuthConfiguration { public const string ReadApiScope = "api:read"; public const string WriteApiScope = "api:write"; + public const string ManageDatabaseApiScope = "api:database:manage"; public static void ConfigureServices( IServiceCollection services, @@ -219,7 +220,8 @@ X509Certificate2 signingCertificate Scopes = { ReadApiScope, - WriteApiScope + WriteApiScope, + ManageDatabaseApiScope, }, // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint From 9492bd5b6321cc7874089277908641121434b0a0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Dec 2025 10:43:18 +0100 Subject: [PATCH 614/878] Use implicit "getter" notation in app settings --- backend/src/AppSettings.cs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 6ec4a089..dcfd131a 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -8,23 +8,15 @@ public sealed class AppSettings { private const string GraphQlPathSegment = "/graphql/"; + public string Host { get; private set; } = ""; + public Uri HostUri => new(Host, UriKind.Absolute); // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - public Uri DatabaseGraphQlEndpoint - { - get => new UriBuilder(new Uri(Host, UriKind.Absolute)) { Path = GraphQlPathSegment }.Uri; - } - - public Uri MetabaseGraphQlEndpoint - { - get => new UriBuilder(new Uri(MetabaseHost, UriKind.Absolute)) { Path = GraphQlPathSegment }.Uri; - } - - public string Host { get; private set; } = ""; - public Uri HostUri { get => new(Host, UriKind.Absolute); } + public Uri DatabaseGraphQlEndpoint => new UriBuilder(HostUri) { Path = GraphQlPathSegment }.Uri; public string MetabaseHost { get; private set; } = ""; - public Uri MetabaseHostUri { get => new(MetabaseHost, UriKind.Absolute); } + public Uri MetabaseHostUri => new(MetabaseHost, UriKind.Absolute); + public Uri MetabaseGraphQlEndpoint => new UriBuilder(MetabaseHostUri) { Path = GraphQlPathSegment }.Uri; public Guid DatabaseId { get; private set; } public Guid OperatorId { get; private set; } @@ -54,7 +46,7 @@ public sealed class GnupgSecretSigningKeySettings { // Keep file name {FINGERPRINT}.gpg in sync with the one in the GNU // Make target `gpg` in the `Makefile` - public string FileName { get => $"{Fingerprint}.gpg"; } + public string FileName => $"{Fingerprint}.gpg"; public string Passphrase { get; private set; } = ""; public string Fingerprint { get; private set; } = ""; } From 46246fcd4a8c4418d13578bf7a3c54fc44ffc8b6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Dec 2025 11:39:11 +0100 Subject: [PATCH 615/878] Ask for the profile scope because we want to display the user name --- backend/src/Configuration/AuthConfiguration.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index e1abdb19..d69830b4 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -219,6 +219,7 @@ X509Certificate2 signingCertificate // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims Scopes = { + OpenIddictConstants.Scopes.Profile, ReadApiScope, WriteApiScope, ManageDatabaseApiScope, From d83c22e4adadfd74d4ea30adb596daa6dc2a4f3f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Dec 2025 11:39:22 +0100 Subject: [PATCH 616/878] Do not ask for user roles --- backend/src/Controllers/AuthenticationController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index f0558ad7..69c3895a 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -160,8 +160,7 @@ public async Task LogInCallback(string provider, CancellationToken // Build an identity based on the external claims and that will be used to create the authentication cookie. var identity = new ClaimsIdentity( CookieAuthenticationDefaults.AuthenticationScheme, - ClaimTypes.Name, - ClaimTypes.Role + ClaimTypes.Name ); // By default, OpenIddict will automatically try to map the email/name and name identifier claims from From 562fdfe4aa95a98e9728ad15b9a99fcf969b759c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 13 Jan 2026 18:13:12 +0100 Subject: [PATCH 617/878] Install latest GnuPG version --- backend/Dockerfile | 6 +++--- backend/Dockerfile-bootstrap | 9 +++++---- backend/Dockerfile-production | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index a93547d1..2e7c965c 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -105,12 +105,12 @@ RUN \ update-ca-certificates # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions see https://repos.gnupg.org/deb/gnupg/ -# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# For available versions and the signing-key fingerprint see +# https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_VERSION=2.5.16-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 39879665..3e3016fd 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -5,6 +5,7 @@ # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk +# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 ARG GROUP_ID @@ -33,12 +34,12 @@ RUN \ /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions see https://repos.gnupg.org/deb/gnupg/ -# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# For available versions and the signing-key fingerprint see +# https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_VERSION=2.5.16-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ @@ -76,7 +77,7 @@ RUN set -eux && \ --recursive \ --force \ /var/lib/apt/lists/* -WORKDIR /app + ENV HOME=/home/me RUN \ mkdir --parents ${HOME} && \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 71422883..e1a02b23 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -70,12 +70,12 @@ RUN \ /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions see https://repos.gnupg.org/deb/gnupg/ -# The fingerprint was taken from https://repos.gnupg.org/deb/gnupg/bookworm/ +# For available versions and the signing-key fingerprint see +# https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.4.8-2 +ENV GNUPG_VERSION=2.5.16-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 RUN set -eux && \ version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ From 2c703ee9a570ef4bcd60374c19e83a324309fdc1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 03:46:50 +0100 Subject: [PATCH 618/878] Check group existence after possible deleting the user because when a user is deleted its primary group is also deleted unless that group also contains other users --- backend/Dockerfile | 2 +- backend/Dockerfile-bootstrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 2e7c965c..50453be9 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -24,8 +24,8 @@ RUN \ --no-install-recommends \ adduser && \ existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ + existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me && \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 3e3016fd..c003cdd4 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -20,8 +20,8 @@ RUN \ --no-install-recommends \ adduser && \ existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ + existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ adduser --system --uid ${USER_ID} --ingroup us me && \ From 3e8652155215a36236e149b7c7eb0798999abe52 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 16:46:54 +0100 Subject: [PATCH 619/878] Fully-qualify serilog static `Log` interface --- backend/src/Program.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 4829e6ab..5513c28f 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Database.Data; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -13,8 +14,6 @@ using Serilog.Events; using Serilog.Formatting.Compact; using ILogger = Microsoft.Extensions.Logging.ILogger; -using Log = Serilog.Log; -using Database.Data; using Database.Services; namespace Database; @@ -59,7 +58,7 @@ string[] commandLineArguments ConfigureBootstrapLogging(environment); try { - Log.Information("Starting web host"); + Serilog.Log.Information("Starting web host"); // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication var builder = CreateWebApplicationBuilder(commandLineArguments); var startup = new Startup(builder.Environment, builder.Configuration); @@ -86,12 +85,12 @@ string[] commandLineArguments } catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { - Log.Fatal(exception, "Host terminated unexpectedly"); + Serilog.Log.Fatal(exception, "Host terminated unexpectedly"); return 1; } finally { - Log.CloseAndFlush(); + Serilog.Log.CloseAndFlush(); } } @@ -102,7 +101,7 @@ string environment var configuration = new LoggerConfiguration() .MinimumLevel.Override("Microsoft", LogEventLevel.Information); ConfigureLogging(configuration, environment); - Log.Logger = configuration.CreateBootstrapLogger(); + Serilog.Log.Logger = configuration.CreateBootstrapLogger(); } private static void ConfigureLogging( From 86755b40705e79c0e986db785ec50e2072f53e92 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 16:47:51 +0100 Subject: [PATCH 620/878] Add missing claims-type role --- backend/src/Controllers/AuthenticationController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 69c3895a..f0558ad7 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -160,7 +160,8 @@ public async Task LogInCallback(string provider, CancellationToken // Build an identity based on the external claims and that will be used to create the authentication cookie. var identity = new ClaimsIdentity( CookieAuthenticationDefaults.AuthenticationScheme, - ClaimTypes.Name + ClaimTypes.Name, + ClaimTypes.Role ); // By default, OpenIddict will automatically try to map the email/name and name identifier claims from From 41ef16749637a4b1f829decc0ffd2c07461aa1bf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 17:38:34 +0100 Subject: [PATCH 621/878] Make email nullable because it is null if the email scope has not been granted --- backend/src/ApiRequests/GetUserInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index 217bd5fc..590c30e2 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -15,7 +15,7 @@ public static Uri GetEndpoint(AppSettings appSettings) => public sealed record UserInfo( Address? Address, - string Email, + string? Email, bool EmailVerified, string Name, string? PhoneNumber, From b8919c8052094ff91fedf88e581850c0b8297ba8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 17:41:44 +0100 Subject: [PATCH 622/878] Add link to metabase user page --- frontend/pages/user-info.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index e7bc193f..6302f9b2 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -5,6 +5,7 @@ import { Skeleton, Result, Descriptions, Typography, message } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { useEffect } from "react"; import { stringifyApolloError } from "../lib/apollo"; +import paths from "../paths"; function Page() { const { loading, error, data } = useQuery(CurrentUserInfoDocument); @@ -40,6 +41,11 @@ function Page() { backIcon={false} > + + + {paths.metabase.user(currentUserInfo.sub)} + + {currentUserInfo.email && ( {currentUserInfo.email} ( From 926a29c69bceb7298fa2c23fb6ed559253d8e7af Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 17:55:30 +0100 Subject: [PATCH 623/878] Do not set illegal path at it results in an error that hides the original one --- backend/src/ApiRequests/GraphQlRequestHelper.cs | 6 ++---- backend/src/ApiRequests/UpdateDatabase.cs | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/src/ApiRequests/GraphQlRequestHelper.cs b/backend/src/ApiRequests/GraphQlRequestHelper.cs index f8942268..b00169ab 100644 --- a/backend/src/ApiRequests/GraphQlRequestHelper.cs +++ b/backend/src/ApiRequests/GraphQlRequestHelper.cs @@ -36,10 +36,8 @@ Uri databaseLocator throw new GraphQLException( ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") - .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []) - .ToList()) // TODO Splitting the path at '.' is wrong in general. - .SetMessage( - $"Failed to deserialize GraphQL response of request to the endpoint '{databaseLocator}'. The details given are: Zero-based number of bytes read within the current line before the exception are {e.BytePositionInLine}, zero-based number of lines read before the exception are {e.LineNumber}, message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is '{e.Path}'.") + .SetPath(resolverContext.Path) // TODO Add the error path. I would do it as follows as a workaround, however splitting the path at '.' is wrong in general: .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []).ToList()) + .SetMessage($"Failed to deserialize the GraphQL response of the request to the endpoint '{databaseLocator}'. The details given are: Zero-based number of bytes read within the current line before the exception are '{e.BytePositionInLine}', zero-based number of lines read before the exception are '{e.LineNumber}', message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is '{e.Path}'.") .SetException(e) .Build() ); diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 32fdb52d..4855311b 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using GraphQL; using Database.Services; using System; From 457714100d25ada089725b332a752f2b20d64ced Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 18:10:17 +0100 Subject: [PATCH 624/878] Fix GNU Make target `restartb` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index cb4e2d52..1cdcad9a 100644 --- a/Makefile +++ b/Makefile @@ -133,7 +133,7 @@ restart : ## Restart all stopped and running containers .PHONY : restart restartb : ## Restart the backend container - ${docker_compose} backend restart + ${docker_compose} restart backend .PHONY : restartb prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes From b800cab6723480027bd8e8ae89d2b3238978aeae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 18:11:31 +0100 Subject: [PATCH 625/878] Correct update database response C# representation --- backend/src/ApiRequests/GraphQlRequestHelper.cs | 2 +- backend/src/ApiRequests/UpdateDatabase.cs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backend/src/ApiRequests/GraphQlRequestHelper.cs b/backend/src/ApiRequests/GraphQlRequestHelper.cs index b00169ab..e2591ac8 100644 --- a/backend/src/ApiRequests/GraphQlRequestHelper.cs +++ b/backend/src/ApiRequests/GraphQlRequestHelper.cs @@ -37,7 +37,7 @@ Uri databaseLocator ErrorBuilder.New() .SetCode("JSON_DESERIALIZATION_FAILED") .SetPath(resolverContext.Path) // TODO Add the error path. I would do it as follows as a workaround, however splitting the path at '.' is wrong in general: .SetPath(resolverContext.Path.ToList().Concat(e.Path?.Split('.') ?? []).ToList()) - .SetMessage($"Failed to deserialize the GraphQL response of the request to the endpoint '{databaseLocator}'. The details given are: Zero-based number of bytes read within the current line before the exception are '{e.BytePositionInLine}', zero-based number of lines read before the exception are '{e.LineNumber}', message that describes the current exception is '{e.Message}', path within the JSON where the exception was encountered is '{e.Path}'.") + .SetMessage($"Failed to deserialize the GraphQL response of the request to the endpoint '{databaseLocator}'. The details given are: Zero-based number of bytes read within the current line before the exception are '{e.BytePositionInLine}', zero-based number of lines read before the exception are '{e.LineNumber}', message that describes the current exception is \"{e.Message}\", path within the JSON where the exception was encountered is '{e.Path}'.") .SetException(e) .Build() ); diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 4855311b..06a3ed64 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -41,6 +41,8 @@ public sealed record UpdateDatabaseError( IReadOnlyList Path ); + private sealed record UpdateDatabaseData(UpdateDatabasePayload? UpdateDatabase); + public static async Task Do( UpdateDatabaseInput updateDatabaseInput, AppSettings appSettings, @@ -48,19 +50,21 @@ IReadOnlyList Path CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( + return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( await GraphQlQueryHelpers.Construct( UpdateDatabaseFileName - ), + ), new { input = updateDatabaseInput }, "UpdateDatabase" - ), + ), cancellationToken - )).Data; + )) + .Data + .UpdateDatabase; } } \ No newline at end of file From 4fd151022e595b514039e2b349ddf3a70f98ae61 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 19:33:00 +0100 Subject: [PATCH 626/878] Refresh access tokens automatically when they are about to expire --- backend/src/AppSettings.cs | 8 + .../Authentication/AuthenticationConstants.cs | 11 + .../Authentication/AuthenticationHandler.cs | 203 ++++++++++++++++++ .../Authentication/AuthenticationTokens.cs | 10 + ...dBearerTokenAuthenticationSchemeHandler.cs | 24 +++ ...dBearerTokenAuthenticationSchemeOptions.cs | 8 + .../Authentication/OpenIdConnectConstants.cs | 12 ++ .../src/Authentication/OpenIdConnectScope.cs | 18 ++ .../src/Configuration/AuthConfiguration.cs | 127 +++++------ .../src/Configuration/GraphQlConfiguration.cs | 32 ++- .../HttpContextAuthentication.cs | 48 ----- .../src/Controllers/AntiforgeryController.cs | 21 +- .../Controllers/AuthenticationController.cs | 101 +++++---- backend/src/Data/User.cs | 3 +- backend/src/Services/EmailSender.cs | 16 +- backend/src/Startup.cs | 45 ++-- 16 files changed, 488 insertions(+), 199 deletions(-) create mode 100644 backend/src/Authentication/AuthenticationConstants.cs create mode 100644 backend/src/Authentication/AuthenticationHandler.cs create mode 100644 backend/src/Authentication/AuthenticationTokens.cs create mode 100644 backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeHandler.cs create mode 100644 backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeOptions.cs create mode 100644 backend/src/Authentication/OpenIdConnectConstants.cs create mode 100644 backend/src/Authentication/OpenIdConnectScope.cs delete mode 100644 backend/src/Configuration/HttpContextAuthentication.cs diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index dcfd131a..e0c708ba 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -7,9 +7,17 @@ namespace Database; public sealed class AppSettings { private const string GraphQlPathSegment = "/graphql/"; + private const string WwwSubdomain = "www."; public string Host { get; private set; } = ""; public Uri HostUri => new(Host, UriKind.Absolute); + public Uri NonWwwHostUri => + HostUri.Host.StartsWith(WwwSubdomain, StringComparison.OrdinalIgnoreCase) + ? new UriBuilder(HostUri) + { + Host = HostUri.Host[WwwSubdomain.Length..] + }.Uri + : HostUri; // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 public Uri DatabaseGraphQlEndpoint => new UriBuilder(HostUri) { Path = GraphQlPathSegment }.Uri; diff --git a/backend/src/Authentication/AuthenticationConstants.cs b/backend/src/Authentication/AuthenticationConstants.cs new file mode 100644 index 00000000..9819538b --- /dev/null +++ b/backend/src/Authentication/AuthenticationConstants.cs @@ -0,0 +1,11 @@ +namespace Database.Authentication; + +public static class AuthenticationConstants +{ + // `IdentityConstants.ApplicationScheme` is not a constant but only read-only. It can thus not + // be used in the `Authorize` attribute. See the corresponding issue + // https://github.com/dotnet/aspnetcore/issues/20122 and un-merged pull request https://github.com/dotnet/aspnetcore/pull/21343/files + public const string IdentityConstantsApplicationScheme = "Identity.Application"; + + public const string CookieAndBearerTokenAuthenticationScheme = "Database.Bearer"; +} \ No newline at end of file diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs new file mode 100644 index 00000000..3d364a2d --- /dev/null +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -0,0 +1,203 @@ +using System; +using System.Globalization; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Headers; +using OpenIddict.Abstractions; +using OpenIddict.Client; +using OpenIddict.Client.AspNetCore; +using OpenIddict.Validation.AspNetCore; +using static OpenIddict.Client.OpenIddictClientModels; + +namespace Database.Authentication; + +public sealed class AuthenticationHandler( + OpenIddictClientService openIddictClientService +) +{ + private static string? GetBackchannelAccessToken(AuthenticateResult authenticateResult) => + authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken); + + private static DateTimeOffset? GetBackchannelAccessTokenExpirationDate(AuthenticateResult authenticateResult) => + DateTimeOffset.TryParse( + authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate), + CultureInfo.InvariantCulture, + out var date + ) + ? date + : null; + + private static string? GetRefreshToken(AuthenticateResult authenticateResult) => + authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken); + + private static async Task SetAuthenticationTokensAsync( + HttpContext httpContext, + ClaimsPrincipal claimsPrincipal, + AuthenticationProperties authenticationProperties, + AuthenticationTokens freshTokens + ) + { + // Override the tokens using the values returned in the token response. + var properties = authenticationProperties.Clone(); + // Keep in sync with `GetTokenValue` in `AddRequestTransform` above + properties.UpdateTokenValue( + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken, + freshTokens.AccessToken + ); + properties.UpdateTokenValue( + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate, + freshTokens.AccessTokenExpirationDate?.ToString(CultureInfo.InvariantCulture) ?? string.Empty + ); + properties.UpdateTokenValue( + OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken, + freshTokens.IdentityToken + ); + // Note: if no refresh token was returned, preserve the refresh token initially returned. + if (!string.IsNullOrEmpty(freshTokens.RefreshToken)) + { + properties.UpdateTokenValue( + OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken, + freshTokens.RefreshToken + ); + } + // Remove the redirect URI from the authentication properties + // to prevent the cookies handler from genering a 302 response. + properties.RedirectUri = null; + // Set the creation and expiration dates of the ticket to null to decorrelate the lifetime + // of the resulting authentication cookie from the lifetime of the identity token returned by + // the authorization server (if applicable). In this case, the expiration date time will be + // automatically computed by the cookie handler using the lifetime configured in the options. + // + // Applications that prefer binding the lifetime of the ticket stored in the authentication cookie + // to the identity token returned by the identity provider can remove or comment these two lines: + properties.IssuedUtc = null; + properties.ExpiresUtc = null; + // Note: this flag controls whether the authentication cookie that will be returned to the + // browser will be treated as a session cookie (i.e destroyed when the browser is closed) + // or as a persistent cookie. In both cases, the lifetime of the authentication ticket is + // always stored as protected data, preventing malicious users from trying to use an + // authentication cookie beyond the lifetime of the authentication ticket itself. + properties.IsPersistent = false; + // If multiple HTTP responses for the same user are returned in parallel, the browser will + // always store the latest cookie received and the refresh tokens stored in the other cookies + // will be discarded. + await httpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + claimsPrincipal, + properties + ); + } + + private static void SetBearerToken(HttpContext httpContext, string accessToken) => + httpContext.Request.Headers.Authorization = $"{OpenIddictConstants.Schemes.Bearer} {accessToken}"; + + private async Task FetchAndRefreshAccessTokenFromCookieAuthenticationAsync( + HttpContext httpContext, + CancellationToken cancellationToken + ) + { + var cookieAuthenticationResult = await httpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + if (cookieAuthenticationResult is not { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + return null; + } + var accessToken = GetBackchannelAccessToken(cookieAuthenticationResult); + var expirationDate = GetBackchannelAccessTokenExpirationDate(cookieAuthenticationResult); + if (accessToken is not null + && expirationDate is not null + && TimeProvider.System.GetUtcNow() <= expirationDate?.Subtract(OpenIdConnectConstants.AccessAndIdentityTokenLifetime.Divide(3)) + ) + { + return accessToken; + } + var refreshToken = GetRefreshToken(cookieAuthenticationResult); + if (refreshToken is not null) + { + var refreshTokenAuthenticationResult = await openIddictClientService.AuthenticateWithRefreshTokenAsync( + new RefreshTokenAuthenticationRequest + { + DisableUserInfo = true, + RefreshToken = refreshToken, + CancellationToken = cancellationToken, + } + ); + await SetAuthenticationTokensAsync( + httpContext, + cookieAuthenticationResult.Principal, + cookieAuthenticationResult.Properties, + new AuthenticationTokens( + AccessToken: refreshTokenAuthenticationResult.AccessToken, + AccessTokenExpirationDate: refreshTokenAuthenticationResult.AccessTokenExpirationDate, + IdentityToken: refreshTokenAuthenticationResult.IdentityToken, + RefreshToken: refreshTokenAuthenticationResult.RefreshToken + ) + ); + return refreshTokenAuthenticationResult.AccessToken; + } + return null; + } + + // Inspired by https://github.com/dotnet/aspnetcore/blob/85565dbb30d724c955179e1989c109c677e7b263/src/Http/Http.Extensions/src/RequestHeaders.cs#L338-L342 + public static Uri? GetOrigin(RequestHeaders headers) + { + if (Uri.TryCreate(headers.Headers.Origin, UriKind.RelativeOrAbsolute, out var uri)) + { + return uri; + } + return null; + } + + private static bool IsSameOriginOrReferer(HttpRequest request) + { + var headers = request.GetTypedHeaders(); + var originOrReferer = GetOrigin(headers) ?? headers.Referer; + if (originOrReferer is null) + { + return true; + } + return request.Host == HostString.FromUriComponent(originOrReferer); + } + + public async Task AuthenticateAsync( + HttpContext httpContext, + CancellationToken cancellationToken + ) + { + if (IsSameOriginOrReferer(httpContext.Request)) + { + // For the Next.js Web frontend, the database acts as OpenId Connect + // Client and uses the cookie scheme for authentication. See + // `AuthConfiguration#ConfigureAuthenticationAndAuthorizationServices` + // for the configuration of the "cookie scheme" cookie. This cookie + // is set by methods in `AuthenticationController` and is related to + // `OpenIddictBuilder#AddClient` in + // `AuthConfiguration#ConfigureOpenIddictServices`. + var accessToken = await FetchAndRefreshAccessTokenFromCookieAuthenticationAsync( + httpContext, + cancellationToken + ); + if (accessToken is not null) + { + SetBearerToken(httpContext, accessToken); + } + } + // For third-party frontends, the database acts as resource server + // and uses authorization-header bearer tokens for authentication, + // that is JavaScript Web Tokens (JWT), aka, Access Tokens, provided + // as `Authorization` HTTP header with the prefix `Bearer` as issued + // by OpenIddict. This Access Token includes Scopes and Claims. The + // scheme is configured in + // `AuthConfiguration#ConfigureOpenIddictServices` by + // `OpenIddictBuilder#AddValidation`. + var bearerAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + if (bearerAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + return bearerAuthenticateResult; + } + return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); + } +} \ No newline at end of file diff --git a/backend/src/Authentication/AuthenticationTokens.cs b/backend/src/Authentication/AuthenticationTokens.cs new file mode 100644 index 00000000..3827e9f5 --- /dev/null +++ b/backend/src/Authentication/AuthenticationTokens.cs @@ -0,0 +1,10 @@ +using System; + +namespace Database.Authentication; + +public sealed record AuthenticationTokens( + string AccessToken, + DateTimeOffset? AccessTokenExpirationDate, + string? IdentityToken, + string? RefreshToken +); \ No newline at end of file diff --git a/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeHandler.cs b/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeHandler.cs new file mode 100644 index 00000000..d0326e02 --- /dev/null +++ b/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeHandler.cs @@ -0,0 +1,24 @@ +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Database.Authentication; + +public sealed class CookieAndBearerTokenAuthenticationSchemeHandler( + IOptionsMonitor options, + ILoggerFactory logger, + UrlEncoder encoder, + AuthenticationHandler authenticationHandler +) +: AuthenticationHandler(options, logger, encoder) +{ + protected override Task HandleAuthenticateAsync() + { + return authenticationHandler.AuthenticateAsync( + Context, + Context.RequestAborted + ); + } +} \ No newline at end of file diff --git a/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeOptions.cs b/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeOptions.cs new file mode 100644 index 00000000..84ddf02b --- /dev/null +++ b/backend/src/Authentication/CookieAndBearerTokenAuthenticationSchemeOptions.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Authentication; + +namespace Database.Authentication; + +public sealed class CookieAndBearerTokenAuthenticationSchemeOptions +: AuthenticationSchemeOptions +{ +} \ No newline at end of file diff --git a/backend/src/Authentication/OpenIdConnectConstants.cs b/backend/src/Authentication/OpenIdConnectConstants.cs new file mode 100644 index 00000000..05c48ccd --- /dev/null +++ b/backend/src/Authentication/OpenIdConnectConstants.cs @@ -0,0 +1,12 @@ +using System; + +namespace Database.Authentication; + +public static class OpenIdConnectConstants +{ + public static readonly TimeSpan AccessAndIdentityTokenLifetime = TimeSpan.FromHours(1); + + public const string MetabaseRegistrationId = "metabase"; + public const string MetabaseClientId = "metabase"; + public const string DatabaseQuartzSchedulerId = "database"; +} \ No newline at end of file diff --git a/backend/src/Authentication/OpenIdConnectScope.cs b/backend/src/Authentication/OpenIdConnectScope.cs new file mode 100644 index 00000000..45222ae0 --- /dev/null +++ b/backend/src/Authentication/OpenIdConnectScope.cs @@ -0,0 +1,18 @@ +using OpenIddict.Abstractions; + +namespace Database.Authentication; + +public static class OpenIdConnectScope +{ + public const string ReadApiScope = "api:read"; + public const string WriteApiScope = "api:write"; + public const string ManageDatabaseApiScope = "api:database:manage"; + + public static readonly string[] Scopes = + [ + OpenIddictConstants.Scopes.Profile, + ReadApiScope, + WriteApiScope, + ManageDatabaseApiScope + ]; +} \ No newline at end of file diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index d69830b4..c08f6f2f 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -11,27 +11,22 @@ using OpenIddict.Client; using Quartz; using Database.Authorization; +using Database.Authentication; namespace Database.Configuration; -// Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Startup.cs -// https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Startup.cs -public abstract class AuthConfiguration +public static class AuthConfiguration { - public const string ReadApiScope = "api:read"; - public const string WriteApiScope = "api:write"; - public const string ManageDatabaseApiScope = "api:database:manage"; - + private static readonly TimeSpan s_cookieExpirationTimeSpan = TimeSpan.FromDays(1); public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment, AppSettings appSettings ) { - var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", - appSettings.JsonWebToken.EncryptionCertificatePassword); - var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", - appSettings.JsonWebToken.SigningCertificatePassword); + var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); + var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); + services.AddScoped(); ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); ConfigureOpenIddictServices(services, environment, appSettings, encryptionCertificate, signingCertificate); @@ -75,12 +70,10 @@ IServiceCollection services // https://learn.microsoft.com/en-us/aspnet/core/security/authentication/?view=aspnetcore-7.0#defaultscheme AppContext.SetSwitch("Microsoft.AspNetCore.Authentication.SuppressAutoDefaultScheme", true); // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs - services - .AddAuthentication(_ => + services.AddAuthentication(_ => { - // To make the various authentication control flows obvious, do - // not use default schemes for anything and always be explicit - // instead. + // To make the various authentication control flows obvious, do not use default + // schemes for anything and always be explicit instead. _.DefaultAuthenticateScheme = null; _.DefaultChallengeScheme = null; _.DefaultForbidScheme = null; @@ -88,15 +81,24 @@ IServiceCollection services _.DefaultSignInScheme = null; _.DefaultSignOutScheme = null; }) + // The cookie is used by the database acting as client application through the + // authentication scheme `CookieAuthenticationDefaults.AuthenticationScheme`, that is, "Cookies". .AddCookie(_ => { _.AccessDeniedPath = "/unauthorized"; _.LoginPath = "/connect/login"; _.LogoutPath = "/connect/logout"; _.ReturnUrlParameter = "returnTo"; - _.ExpireTimeSpan = TimeSpan.FromMinutes(50); - _.SlidingExpiration = false; - }); + _.ExpireTimeSpan = s_cookieExpirationTimeSpan; + _.SlidingExpiration = true; + }) + .AddScheme< + CookieAndBearerTokenAuthenticationSchemeOptions, + CookieAndBearerTokenAuthenticationSchemeHandler + >( + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme, + _ => { } + ); } private static void ConfigureTaskScheduling( @@ -104,13 +106,12 @@ private static void ConfigureTaskScheduling( IWebHostEnvironment environment ) { - // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks - // (like pruning orphaned authorizations/tokens from the database) at regular intervals. - // For configuring Quartz see - // https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/hosted-services-integration.html + // OpenIddict offers native integration with Quartz.NET to perform scheduled tasks (like + // pruning orphaned authorizations/tokens from the database) at regular intervals. For + // configuring Quartz see https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/hosted-services-integration.html services.AddQuartz(_ => { - _.SchedulerId = "database"; + _.SchedulerId = OpenIdConnectConstants.DatabaseQuartzSchedulerId; _.SchedulerName = "Database"; _.UseSimpleTypeLoader(); _.UseInMemoryStore(); @@ -119,8 +120,9 @@ IWebHostEnvironment environment ); if (environment.IsEnvironment(Program.TestEnvironment)) { - // See https://gitter.im/MassTransit/MassTransit?at=5db2d058f6db7f4f856fb404 - _.SchedulerName = Guid.NewGuid().ToString(); + var probablyUniqueId = Guid.NewGuid().ToString(); + _.SchedulerId = $"{OpenIdConnectConstants.DatabaseQuartzSchedulerId}-{probablyUniqueId}"; + _.SchedulerName = $"Metabase-{probablyUniqueId}"; } }); // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. @@ -154,7 +156,7 @@ X509Certificate2 signingCertificate // retrieve the issuer signing keys used to validate tokens. _.SetIssuer(appSettings.MetabaseHostUri); // Configure the audience accepted by this resource server. - _.AddAudiences(appSettings.OpenIdConnectClient.Id); + _.AddAudiences(OpenIdConnectConstants.MetabaseClientId); // Configure the validation handler to use introspection and // register the client credentials used when communicating with // the remote introspection endpoint. @@ -164,6 +166,10 @@ X509Certificate2 signingCertificate .SetClientSecret(appSettings.OpenIdConnectClient.Secret); // Register the ASP.NET Core host. _.UseAspNetCore(); + // Enable token entry validation: https://documentation.openiddict.com/configuration/token-storage.html#enabling-token-entry-validation-at-the-api-level + _.EnableTokenEntryValidation(); + // Enable authorization entry validation: https://documentation.openiddict.com/configuration/authorization-storage.html#enabling-authorization-entry-validation-at-the-api-level + _.EnableAuthorizationEntryValidation(); // Register the System.Net.Http integration. _.UseSystemNetHttp() .ConfigureHttpClientHandler(handler => @@ -177,18 +183,18 @@ X509Certificate2 signingCertificate }) .AddClient(_ => { - _.AllowAuthorizationCodeFlow(); + _.AllowAuthorizationCodeFlow() + .AllowRefreshTokenFlow(); - // Register the signing and encryption credentials. - // See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 + // Register the signing and encryption credentials. See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 _.AddEncryptionCertificate(encryptionCertificate) - .AddSigningCertificate(signingCertificate); + .AddSigningCertificate(signingCertificate); // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. _.UseAspNetCore() - .EnableStatusCodePagesIntegration() // https://documentation.openiddict.com/integrations/aspnet-core#status-code-pages-middleware-integration - .EnableRedirectionEndpointPassthrough() // https://documentation.openiddict.com/integrations/aspnet-core#pass-through-mode - .EnablePostLogoutRedirectionEndpointPassthrough(); + .EnableStatusCodePagesIntegration() // https://documentation.openiddict.com/integrations/aspnet-core#status-code-pages-middleware-integration + .EnableRedirectionEndpointPassthrough() // https://documentation.openiddict.com/integrations/aspnet-core#pass-through-mode + .EnablePostLogoutRedirectionEndpointPassthrough(); // .DisableTransportSecurityRequirement(); // https://documentation.openiddict.com/integrations/aspnet-core#transport-security-requirement // Register the System.Net.Http integration and use the identity of the current @@ -205,34 +211,31 @@ X509Certificate2 signingCertificate } }); - // Add a client registration matching the client application definition in the server project. - _.AddRegistration( - new OpenIddictClientRegistration - { - Issuer = appSettings.MetabaseHostUri, - - // Note: these settings must match the application details - // inserted in the database at the server level. - ClientId = appSettings.OpenIdConnectClient.Id, - ClientSecret = appSettings.OpenIdConnectClient.Secret, - - // https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes#standard-claims - Scopes = - { - OpenIddictConstants.Scopes.Profile, - ReadApiScope, - WriteApiScope, - ManageDatabaseApiScope, - }, - - // Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint - // URI per provider, unless all the registered providers support returning a special "iss" - // parameter containing their URL as part of authorization responses. For more information, - // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - RedirectUri = new Uri("connect/callback/login/metabase", UriKind.Relative), - PostLogoutRedirectUri = new Uri("connect/callback/logout/metabase", UriKind.Relative) - } - ); + // Add a client registration matching the client application definition in the + // server project. + var clientRegistration = new OpenIddictClientRegistration + { + RegistrationId = OpenIdConnectConstants.MetabaseRegistrationId, + Issuer = appSettings.MetabaseHostUri, + + // Note: these settings must match the application details inserted in the + // database at the server level. + ClientId = appSettings.OpenIdConnectClient.Id, + ClientSecret = appSettings.OpenIdConnectClient.Secret, + + // Note: to mitigate mix-up attacks, it's recommended to use a unique + // redirection endpoint URI per provider, unless all the registered + // providers support returning a special "iss" parameter containing their + // URL as part of authorization responses. For more information, see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. + RedirectUri = new Uri($"connect/callback/login/{OpenIdConnectConstants.MetabaseClientId}", UriKind.Relative), + PostLogoutRedirectUri = new Uri($"connect/callback/logout/{OpenIdConnectConstants.MetabaseClientId}", UriKind.Relative) + }; + clientRegistration.Scopes.UnionWith([ + OpenIddictConstants.Scopes.OfflineAccess, + OpenIddictConstants.Scopes.OpenId, + ..OpenIdConnectScope.Scopes + ]); + _.AddRegistration(clientRegistration); }); } } \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index ace1ccab..a02dbaee 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -16,9 +16,23 @@ using Database.GraphQl; using Database.GraphQl.DataX; using NodaTime; +using Database.Authentication; +using HotChocolate.AspNetCore; namespace Database.Configuration; +public static partial class Log +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Error, + Message = "Failed to authenticate the claims principal in the HTTP context.")] + public static partial void FailedToAuthenticate( + this ILogger logger, + Exception exception + ); +} + public static class GraphQlConfiguration { public static void ConfigureServices( @@ -89,7 +103,10 @@ IWebHostEnvironment environment ) // Configure // `https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Validation/Options/ValidationOptions.cs`. - // But how? Subscriptions + // .AddMaxExecutionDepthRule(5) + // .SetIntrospectionAllowedDepth(maxAllowedOfTypeDepth: 16, maxAllowedListRecursiveDepth: 1) + // .SetMaxAllowedValidationErrors(5) + // Subscriptions /* .AddInMemorySubscriptions() */ // Persisted queries /* .AddFileSystemOperationDocumentStorage("./persisted_operations") */ @@ -103,12 +120,17 @@ IWebHostEnvironment environment { try { - await HttpContextAuthentication.Authenticate(httpContext); + var authenticationHandler = httpContext.RequestServices.GetRequiredService(); + var authenticateResult = await authenticationHandler.AuthenticateAsync(httpContext, cancellationToken); + if (authenticateResult.Principal is not null) + { + httpContext.User = authenticateResult.Principal; + } } - catch (Exception e) + catch (Exception exception) { - // TODO Log to a `ILogger` instead. - Console.WriteLine(e); + var logger = httpContext.RequestServices.GetRequiredService>(); + logger.FailedToAuthenticate(exception); } }) .AddDiagnosticEventListener(_ => diff --git a/backend/src/Configuration/HttpContextAuthentication.cs b/backend/src/Configuration/HttpContextAuthentication.cs deleted file mode 100644 index 94f166f8..00000000 --- a/backend/src/Configuration/HttpContextAuthentication.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Http; -using OpenIddict.Validation.AspNetCore; - -namespace Database.Configuration; - -public static class HttpContextAuthentication -{ - public static async Task Authenticate( - HttpContext httpContext - ) - { - // For the Next.js Web frontend, the database acts as OpenId Connect - // Client and uses the cookie scheme for authentication. See - // `AuthConfiguration#ConfigureAuthenticationAndAuthorizationServices` - // for the configuration of the "cookie scheme" cookie. This cookie - // is set by methods in `AuthenticationController` and is related to - // `OpenIddictBuilder#AddClient` in - // `AuthConfiguration#ConfigureOpenIddictServices`. - var cookieAuthenticateResult = await httpContext - .AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); - if (cookieAuthenticateResult.Succeeded && cookieAuthenticateResult.Principal is not null) - { - httpContext.User = cookieAuthenticateResult.Principal; - return cookieAuthenticateResult; - } - - // For third-party frontends, the database acts as resource server - // and uses authorization-header bearer tokens for authentication, - // that is JavaScript Web Tokens (JWT), aka, Access Tokens, provided - // as `Authorization` HTTP header with the prefix `Bearer` as issued - // by OpenIddict. This Access Token includes Scopes and Claims. The - // scheme is configured in - // `AuthConfiguration#ConfigureOpenIddictServices` by - // `OpenIddictBuilder#AddValidation`. - var jwtAuthenticateResult = await httpContext - .AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); - if (jwtAuthenticateResult.Succeeded && jwtAuthenticateResult.Principal is not null) - { - httpContext.User = jwtAuthenticateResult.Principal; - return jwtAuthenticateResult; - } - - return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); - } -} \ No newline at end of file diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 794ee333..f5d35c6f 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -1,6 +1,7 @@ using System; +using System.Threading; using System.Threading.Tasks; -using Database.Configuration; +using Database.Authentication; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; @@ -10,7 +11,11 @@ namespace Database.Controllers; // For gotchas regarding antiforgery tokens read // [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) -public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Controller +public sealed class AntiforgeryController( + IAntiforgery antiforgeryService, + AuthenticationHandler authenticationHandler +) +: Controller { private const string XsrfCookieKey = "XSRF-TOKEN"; @@ -23,12 +28,18 @@ public sealed class AntiforgeryController(IAntiforgery antiforgeryService) : Con SameSite = SameSiteMode.Strict }; + [HttpGet("~/antiforgery/token")] [EndpointName("AntiforgeryToken")] [EndpointDescription("Get an antiforgery token.")] - [HttpGet("~/antiforgery/token")] - public async Task Token() + public async Task Token( + CancellationToken cancellationToken + ) { - await HttpContextAuthentication.Authenticate(HttpContext); + var authenticateResult = await authenticationHandler.AuthenticateAsync(HttpContext, cancellationToken); + if (authenticateResult.Principal is not null) + { + HttpContext.User = authenticateResult.Principal; + } var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append( XsrfCookieKey, diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index f0558ad7..98536cb3 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -9,14 +9,14 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; using Microsoft.AspNetCore.Routing; +using Database.Authentication; +using System.Globalization; namespace Database.Controllers; @@ -25,13 +25,9 @@ namespace Database.Controllers; [EndpointGroupName("Authentication")] public sealed class AuthenticationController( AppSettings appSettings, - IOptions identityOptions, ApplicationDbContext context - ) : Controller +) : Controller { - private readonly IdentityOptions _identityOptions = identityOptions.Value ?? - throw new InvalidOperationException("There are no identity options."); - private readonly ApplicationDbContext _context = context; private readonly Uri _issuer = appSettings.MetabaseHostUri; private bool _disposed; @@ -41,7 +37,7 @@ protected override void Dispose(bool disposing) if (!_disposed) { // Dispose of resources held by this instance. - _context.Dispose(); + context.Dispose(); _disposed = true; } } @@ -73,8 +69,8 @@ public ActionResult LogIn(string? returnUrl) ); } + [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [HttpPost("~/connect/logout")] - [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] [ValidateAntiForgeryToken] public async Task LogOut(string? returnUrl) { @@ -101,8 +97,7 @@ public async Task LogOut(string? returnUrl) // While not required, the specification encourages sending an id_token_hint // parameter containing an identity token returned by the server for this user. [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = - result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens - .BackchannelIdentityToken) + result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken) } ) { @@ -120,7 +115,10 @@ public async Task LogOut(string? returnUrl) [HttpPost("~/connect/callback/login/{provider}")] [IgnoreAntiforgeryToken] [SuppressMessage("Style", "IDE0060")] - public async Task LogInCallback(string provider, CancellationToken cancellationToken) + public async Task LogInCallback( + string provider, + CancellationToken cancellationToken + ) { // Retrieve the authorization data validated by OpenIddict as part of the callback handling. var result = await HttpContext.AuthenticateAsync(OpenIddictClientAspNetCoreDefaults.AuthenticationScheme); @@ -151,22 +149,29 @@ public async Task LogInCallback(string provider, CancellationToken // Such identities cannot be used as-is to build an authentication cookie in ASP.NET Core (as the // antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but // the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls. - if (result.Principal is not ClaimsPrincipal { Identity.IsAuthenticated: true }) + if (result is not { Succeeded: true, Principal.Identity.IsAuthenticated: true }) { - throw new InvalidOperationException( - "The external authorization data cannot be used for authentication."); + throw new InvalidOperationException("The external authorization data cannot be used for authentication."); } + var accessToken = result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken) + ?? throw new InvalidOperationException($"The external authorization data misses an access token."); + var subject = result.Principal.GetClaim(ClaimTypes.NameIdentifier) + ?? throw new InvalidOperationException($"The external authorization data misses the claim {ClaimTypes.NameIdentifier}."); + var name = result.Principal.GetClaim(ClaimTypes.NameIdentifier) + ?? throw new InvalidOperationException($"The external authorization data misses the claim {ClaimTypes.Name}."); + // Build an identity based on the external claims and that will be used to create the authentication cookie. var identity = new ClaimsIdentity( - CookieAuthenticationDefaults.AuthenticationScheme, - ClaimTypes.Name, - ClaimTypes.Role + authenticationType: "ExternalLogin", + nameType: ClaimTypes.Name, + roleType: ClaimTypes.Role ); // By default, OpenIddict will automatically try to map the email/name and name identifier claims from // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional - // claims can be resolved from the external identity and copied to the final authentication cookie. For example + // claims can be resolved from the external identity and copied to the final authentication cookie. + // For example // .SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email)) // The claims are fetched from the userinfo endpoint of the authorization provider. // We add the claim `IdentityOptions.ClaimsIdentity.UserIdClaimType` to make @@ -175,9 +180,8 @@ public async Task LogInCallback(string provider, CancellationToken // iss, exp, iat, aud, sub, role, name, preferred_username, email, azp, nonce, at_hash, email_verified, // phone_number_verified, as, oi_reg_id, http://schemas.xmlsoat.org/.../emailaddress, // http://schemas.xmlsoat.org/.../name, http://schemas.xmlsoat.org/.../nameidentifier - identity.SetClaim(_identityOptions.ClaimsIdentity.UserIdClaimType, result.Principal.GetClaim(ClaimTypes.NameIdentifier)) - .SetClaim(ClaimTypes.Name, result.Principal.GetClaim(ClaimTypes.Name)) - .SetClaim(ClaimTypes.NameIdentifier, result.Principal.GetClaim(ClaimTypes.NameIdentifier)); + identity.SetClaim(ClaimTypes.Name, name) + .SetClaim(ClaimTypes.NameIdentifier, subject); // Preserve `client_id` for usage by access-rights service. identity.SetClaim(Claims.AuthorizedParty, result.Principal.GetClaim(Claims.AuthorizedParty)); @@ -186,43 +190,45 @@ public async Task LogInCallback(string provider, CancellationToken identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId)) .SetClaim(Claims.Private.ProviderName, result.Principal.GetClaim(Claims.Private.ProviderName)); - AuthenticationProperties properties; - if (result.Properties is null) - { - properties = new AuthenticationProperties(); - } - else + // Build the authentication properties based on the properties that were added when the challenge was triggered. + var properties = new AuthenticationProperties(result.Properties.Items) { - // Build the authentication properties based on the properties that were added when the challenge was triggered. - properties = new AuthenticationProperties(result.Properties.Items); - // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. - // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. - properties.StoreTokens(result.Properties.GetTokens().Where(token => token.Name is + RedirectUri = result.Properties.RedirectUri ?? "/", + // Set the creation and expiration dates of the ticket to null to decorrelate the lifetime + // of the resulting authentication cookie from the lifetime of the identity token returned by + // the authorization server (if applicable). In this case, the expiration date time will be + // automatically computed by the cookie handler using the lifetime configured in the options. + // + // Applications that prefer binding the lifetime of the ticket stored in the authentication cookie + // to the identity token returned by the identity provider can remove or comment these two lines: + IssuedUtc = null, + ExpiresUtc = null, + // Note: this flag controls whether the authentication cookie that will be returned to the + // browser will be treated as a session cookie (i.e destroyed when the browser is closed) + // or as a persistent cookie. In both cases, the lifetime of the authentication ticket is + // always stored as protected data, preventing malicious users from trying to use an + // authentication cookie beyond the lifetime of the authentication ticket itself. + IsPersistent = false + }; + properties.StoreTokens( + result.Properties.GetTokens().Where(token => token.Name is // Preserve the access, identity and refresh tokens returned in the token response, if available. OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate or OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken - )); - } + ) + ); // Add the user to the database if he/she does not exist. - var subject = - identity.FindFirst(c => c.Type == ClaimTypes.NameIdentifier)?.Value - ?? throw new InvalidOperationException( - $"Impossible! The claim {ClaimTypes.NameIdentifier}, which is the subject of the token, is missing for the identity with name {identity.Name}."); - var name = - identity.FindFirst(c => c.Type == ClaimTypes.Name)?.Value - ?? throw new InvalidOperationException( - $"Impossible! The claim {ClaimTypes.Name} is missing for the identity with subject {subject}."); var user = await - _context.Users.AsQueryable() + context.Users.AsQueryable() .SingleOrDefaultAsync( u => u.Subject == subject, cancellationToken ); if (user is null) { - _context.Users.Add( + context.Users.Add( new User( subject, name @@ -234,8 +240,9 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate user.Update(name); } - await _context.SaveChangesAsync(cancellationToken); - // Ask the cookie authentication handler to return a new cookie and redirect + await context.SaveChangesAsync(cancellationToken); + + // Ask the cookie authentication sign-in handler to return a new cookie and redirect // the user agent to the return URL stored in the authentication properties. return SignIn( new ClaimsPrincipal(identity), diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs index 564862f4..1ca1b218 100644 --- a/backend/src/Data/User.cs +++ b/backend/src/Data/User.cs @@ -3,7 +3,8 @@ namespace Database.Data; public sealed class User( string subject, string name - ) : Entity +) +: Entity { public string Subject { get; private set; } = subject; public string Name { get; private set; } = name; diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 42eb7291..faf77986 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; @@ -17,32 +18,29 @@ public static partial void AboutToSendEmail( (string name, string address) Recipient, string Subject, string Body - ); + ); } public sealed class EmailSender( string smtpHost, int smtpPort, + Uri nonWwwHost, ILogger logger ) : IEmailSender { - private readonly ILogger _logger = logger; - private readonly string _smtpHost = smtpHost; - private readonly int _smtpPort = smtpPort; - public Task SendAsync( (string name, string address) recipient, string subject, string body ) { - _logger.AboutToSendEmail(recipient, subject, body); + logger.AboutToSendEmail(recipient, subject, body); var message = new MimeMessage(); message.From.Add( new MailboxAddress( "Database", - "database@solarbuildingenvelopes.com" + $"database@s{nonWwwHost.Host}" ) ); message.To.Add( @@ -59,8 +57,8 @@ string body using (var client = new SmtpClient()) { client.Connect( - _smtpHost, - _smtpPort, + smtpHost, + smtpPort, SecureSocketOptions.StartTlsWhenAvailable ); // client.Authenticate("joey", "password"); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index f8799503..2a02d283 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -59,7 +59,7 @@ public void ConfigureServices(IServiceCollection services) ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); - ConfigureSessionServices(services, _environment); + // ConfigureSessionServices(services, _environment); // Not used services.AddAntiforgery(_ => { _.HeaderName = AntiforgeryHeaderName; }); services .AddDataProtection() @@ -138,31 +138,32 @@ private void ConfigureMessageSenderServices(IServiceCollection services) new EmailSender( _appSettings.Email.SmtpHost, _appSettings.Email.SmtpPort, + _appSettings.NonWwwHostUri, serviceProvider.GetRequiredService>() ) ); } - private static void ConfigureSessionServices( - IServiceCollection services, - IWebHostEnvironment environment - ) - { - // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state - services.AddDistributedMemoryCache(); - services.AddSession(options => - { - // Set a short timeout for easy testing in development and a long - // one otherwise. - options.IdleTimeout = - environment.IsDevelopment() - ? TimeSpan.FromSeconds(10) - : TimeSpan.FromMinutes(30); - options.Cookie.HttpOnly = true; - // Make the session cookie essential - options.Cookie.IsEssential = true; - }); - } + // private static void ConfigureSessionServices( + // IServiceCollection services, + // IWebHostEnvironment environment + // ) + // { + // // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state + // services.AddDistributedMemoryCache(); + // services.AddSession(options => + // { + // // Set a short timeout for easy testing in development and a long + // // one otherwise. + // options.IdleTimeout = + // environment.IsDevelopment() + // ? TimeSpan.FromSeconds(10) + // : TimeSpan.FromMinutes(30); + // options.Cookie.HttpOnly = true; + // // Make the session cookie essential + // options.Cookie.IsEssential = true; + // }); + // } private static void ConfigureDatabaseContext( DbContextOptionsBuilder options, @@ -293,7 +294,7 @@ public void Configure(WebApplication app) // app.UseCertificateForwarding(); // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0#other-web-proxies app.UseAuthentication(); app.UseAuthorization(); - app.UseSession(); + // app.UseSession(); // Not used // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx // app.UseWebSockets(); From 6dabb177bdea06aa8905167909ff1a283f3c5f91 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 19:36:53 +0100 Subject: [PATCH 627/878] Use generic `ILogger` wherever possible --- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- backend/src/Controllers/FileUploadController.cs | 4 ++-- backend/src/Data/DbSeeder.cs | 2 +- .../src/GraphQl/LoggingDiagnosticEventListener.cs | 14 +++++++------- backend/src/Program.cs | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index a02dbaee..4f87b7cf 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -28,7 +28,7 @@ public static partial class Log Level = LogLevel.Error, Message = "Failed to authenticate the claims principal in the HTTP context.")] public static partial void FailedToAuthenticate( - this ILogger logger, + this ILogger logger, Exception exception ); } diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 3644781c..95150d3c 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -28,7 +28,7 @@ public static partial class Log Level = LogLevel.Information, Message = "Uploaded file '{FileNameForDisplay}' to '{FilePath}'.")] public static partial void SavedUploadedFile( - this ILogger logger, + this ILogger logger, string? fileNameForDisplay, string filePath ); @@ -38,7 +38,7 @@ string filePath Level = LogLevel.Error, Message = "Failed to create response approval for data with ID {DataId}.")] public static partial void FailedToCreateResponseApproval( - this ILogger logger, + this ILogger logger, Guid dataId, Exception exception ); diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index a6db34e1..fad4bd56 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -12,7 +12,7 @@ public static partial class Log Level = LogLevel.Debug, Message = "Seeding the database")] public static partial void SeedingDatabase( - this ILogger logger + this ILogger logger ); } diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index 8f4a80ab..745e1f04 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -20,7 +20,7 @@ public static partial class Log Level = LogLevel.Error, Message = "Failed executing the document {Document}.")] public static partial void FailedQueryExecution( - this ILogger logger, + this ILogger logger, Exception exception, IOperationDocument? document ); @@ -30,7 +30,7 @@ public static partial void FailedQueryExecution( Level = LogLevel.Error, Message = "Failed executing the operation {Operation} with the error {Error}.")] public static partial void FailedOperationExecution( - this ILogger logger, + this ILogger logger, Exception? exception, IOperation operation, string error @@ -41,7 +41,7 @@ string error Level = LogLevel.Error, Message = "Failed handling the subscription event of the operation {Operation}.")] public static partial void FailedSubscriptionEvent( - this ILogger logger, + this ILogger logger, Exception exception, IOperation operation ); @@ -51,7 +51,7 @@ IOperation operation Level = LogLevel.Error, Message = "Failed transporting the subscription of the operation {Operation}.")] public static partial void FailedSubscriptionTransport( - this ILogger logger, + this ILogger logger, Exception exception, IOperation operation ); @@ -61,7 +61,7 @@ IOperation operation Level = LogLevel.Error, Message = "The query {Document} has the syntax error {Error}.")] public static partial void FailedSyntax( - this ILogger logger, + this ILogger logger, Exception? exception, IOperationDocument? document, string error @@ -72,7 +72,7 @@ string error Level = LogLevel.Error, Message = "Failed processing the task of kind {Kind} with status {Status} with the error {Error}.")] public static partial void FailedTask( - this ILogger logger, + this ILogger logger, Exception? exception, ExecutionTaskKind kind, ExecutionTaskStatus status, @@ -84,7 +84,7 @@ string error Level = LogLevel.Error, Message = "Failed validating the query {Document} with the error {Error}.")] public static partial void FailedValidation( - this ILogger logger, + this ILogger logger, Exception? exception, IOperationDocument? document, string error diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 5513c28f..3856ef43 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -25,7 +25,7 @@ public static partial class Log Level = LogLevel.Error, Message = "An error occurred creating the database.")] public static partial void FailedToCreateDatabase( - this ILogger logger, + this ILogger logger, // The first exception is implicitly taken care of as detailed in // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy Exception exception @@ -36,7 +36,7 @@ Exception exception Level = LogLevel.Error, Message = "An error occurred seeding the database.")] public static partial void FailedToSeedDatabase( - this ILogger logger, + this ILogger logger, // The first exception is implicitly taken care of as detailed in // https://learn.microsoft.com/en-us/dotnet/core/extensions/logger-message-generator#log-method-anatomy Exception exception From 68e72aa184d5e6ac9b9f6317ce7c2905063710bf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 20:00:37 +0100 Subject: [PATCH 628/878] Disable token and authorization entry validation because it cannot be used with introspection --- backend/src/Configuration/AuthConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index c08f6f2f..ff5ab314 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -167,9 +167,9 @@ X509Certificate2 signingCertificate // Register the ASP.NET Core host. _.UseAspNetCore(); // Enable token entry validation: https://documentation.openiddict.com/configuration/token-storage.html#enabling-token-entry-validation-at-the-api-level - _.EnableTokenEntryValidation(); + // _.EnableTokenEntryValidation(); // Token entry validation cannot be enabled when using introspection. // Enable authorization entry validation: https://documentation.openiddict.com/configuration/authorization-storage.html#enabling-authorization-entry-validation-at-the-api-level - _.EnableAuthorizationEntryValidation(); + // _.EnableAuthorizationEntryValidation(); // Authorization entry validation cannot be enabled when using introspection. // Register the System.Net.Http integration. _.UseSystemNetHttp() .ConfigureHttpClientHandler(handler => From 6f5b1ed028c0795d53671d193ad1087bd550fdae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 20:44:54 +0100 Subject: [PATCH 629/878] Set `HttpContext.User` only when the authentication result is successful and the principal is authenticated --- backend/src/Authentication/AuthenticationHandler.cs | 11 +++-------- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- backend/src/Controllers/AntiforgeryController.cs | 5 ++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index 3d364a2d..acaa2741 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -34,7 +34,7 @@ out var date private static string? GetRefreshToken(AuthenticateResult authenticateResult) => authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken); - private static async Task SetAuthenticationTokensAsync( + private static async Task UpdateTokenValuesAsync( HttpContext httpContext, ClaimsPrincipal claimsPrincipal, AuthenticationProperties authenticationProperties, @@ -125,7 +125,7 @@ CancellationToken cancellationToken CancellationToken = cancellationToken, } ); - await SetAuthenticationTokensAsync( + await UpdateTokenValuesAsync( httpContext, cookieAuthenticationResult.Principal, cookieAuthenticationResult.Properties, @@ -193,11 +193,6 @@ CancellationToken cancellationToken // scheme is configured in // `AuthConfiguration#ConfigureOpenIddictServices` by // `OpenIddictBuilder#AddValidation`. - var bearerAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); - if (bearerAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) - { - return bearerAuthenticateResult; - } - return AuthenticateResult.Fail("All available authentication schemes failed or yielded no claims principal."); + return await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); } } \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 4f87b7cf..b9301c67 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -122,7 +122,7 @@ IWebHostEnvironment environment { var authenticationHandler = httpContext.RequestServices.GetRequiredService(); var authenticateResult = await authenticationHandler.AuthenticateAsync(httpContext, cancellationToken); - if (authenticateResult.Principal is not null) + if (authenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) { httpContext.User = authenticateResult.Principal; } diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index f5d35c6f..86339e7d 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -36,7 +36,7 @@ CancellationToken cancellationToken ) { var authenticateResult = await authenticationHandler.AuthenticateAsync(HttpContext, cancellationToken); - if (authenticateResult.Principal is not null) + if (authenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) { HttpContext.User = authenticateResult.Principal; } @@ -44,8 +44,7 @@ CancellationToken cancellationToken HttpContext.Response.Cookies.Append( XsrfCookieKey, tokens.RequestToken ?? - throw new InvalidOperationException( - "The request token supposed to be generated by the antiforgery service is null."), + throw new InvalidOperationException("The request token supposed to be generated by the antiforgery service is null."), _xsrfCookieOptions ); return new StatusCodeResult(StatusCodes.Status200OK); From 15694a73f76303abd3605afce35d7529eb2f7e25 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 20:51:02 +0100 Subject: [PATCH 630/878] Use the subject claim of the access token from the OpenIddict auth scheme to find the current user --- backend/src/GraphQl/Users/UserQueries.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 0a4f772d..2eeca3e3 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -22,14 +22,14 @@ public sealed class UserQueries CancellationToken cancellationToken ) { - if (!claimsPrincipal.HasClaim(ClaimTypes.NameIdentifier)) + if (!claimsPrincipal.HasClaim(OpenIddictConstants.Claims.Subject)) { return null; } return await context.Users.AsNoTracking() .SingleOrDefaultAsync( - u => claimsPrincipal.GetClaims(ClaimTypes.NameIdentifier).Contains(u.Subject), + u => claimsPrincipal.GetClaims(OpenIddictConstants.Claims.Subject).Contains(u.Subject), cancellationToken ); } From def4830a022f13e1da7dbe911335db88152b8fdf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 20:53:46 +0100 Subject: [PATCH 631/878] Use the name as name instead of the name identifier --- backend/src/Controllers/AuthenticationController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 98536cb3..b0e5de5f 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -158,7 +158,7 @@ CancellationToken cancellationToken ?? throw new InvalidOperationException($"The external authorization data misses an access token."); var subject = result.Principal.GetClaim(ClaimTypes.NameIdentifier) ?? throw new InvalidOperationException($"The external authorization data misses the claim {ClaimTypes.NameIdentifier}."); - var name = result.Principal.GetClaim(ClaimTypes.NameIdentifier) + var name = result.Principal.GetClaim(ClaimTypes.Name) ?? throw new InvalidOperationException($"The external authorization data misses the claim {ClaimTypes.Name}."); // Build an identity based on the external claims and that will be used to create the authentication cookie. From 6a99da1666fac5a65bb4d04bfaea3dbc2545c781 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 14 Jan 2026 21:46:45 +0100 Subject: [PATCH 632/878] Extract the bearer token only from the HTTP auth header (this works when a bearer token is passed from outside and when we extracted it from the cookie and set it in the auth handler) --- .../Authentication/AuthenticationHandler.cs | 6 +-- .../Authentication/OpenIdConnectConstants.cs | 1 + .../HttpContextAccessorExtensions.cs | 40 ------------------- .../src/Extensions/HttpContextExtensions.cs | 26 ++++++++++++ backend/src/Services/ApiRequestService.cs | 21 ++++++---- backend/src/Services/UserService.cs | 34 ++++++---------- 6 files changed, 55 insertions(+), 73 deletions(-) delete mode 100644 backend/src/Extensions/HttpContextAccessorExtensions.cs create mode 100644 backend/src/Extensions/HttpContextExtensions.cs diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index acaa2741..c747a881 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -3,6 +3,7 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; +using Database.Extensions; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; @@ -92,9 +93,6 @@ await httpContext.SignInAsync( ); } - private static void SetBearerToken(HttpContext httpContext, string accessToken) => - httpContext.Request.Headers.Authorization = $"{OpenIddictConstants.Schemes.Bearer} {accessToken}"; - private async Task FetchAndRefreshAccessTokenFromCookieAuthenticationAsync( HttpContext httpContext, CancellationToken cancellationToken @@ -182,7 +180,7 @@ CancellationToken cancellationToken ); if (accessToken is not null) { - SetBearerToken(httpContext, accessToken); + httpContext.SetBearerToken(accessToken); } } // For third-party frontends, the database acts as resource server diff --git a/backend/src/Authentication/OpenIdConnectConstants.cs b/backend/src/Authentication/OpenIdConnectConstants.cs index 05c48ccd..3faa8e0e 100644 --- a/backend/src/Authentication/OpenIdConnectConstants.cs +++ b/backend/src/Authentication/OpenIdConnectConstants.cs @@ -9,4 +9,5 @@ public static class OpenIdConnectConstants public const string MetabaseRegistrationId = "metabase"; public const string MetabaseClientId = "metabase"; public const string DatabaseQuartzSchedulerId = "database"; + public const string AuthorizationHeaderBearer = "Bearer"; } \ No newline at end of file diff --git a/backend/src/Extensions/HttpContextAccessorExtensions.cs b/backend/src/Extensions/HttpContextAccessorExtensions.cs deleted file mode 100644 index 76fb9c59..00000000 --- a/backend/src/Extensions/HttpContextAccessorExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Globalization; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Http; -using OpenIddict.Client.AspNetCore; - -namespace Database.Extensions; - -/// -/// Extensions of -/// -public static class HttpContextAccessorExtensions -{ - /// - /// Extract bearer token from . Only returns token if not expired. - /// - /// - /// Bearer token or null. - public static async Task ExtractBearerToken(this IHttpContextAccessor httpContextAccessor) - { - if (httpContextAccessor.HttpContext is null) - { - return null; - } - var token = await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken); - var expirationDate = await httpContextAccessor.HttpContext.GetTokenAsync( - CookieAuthenticationDefaults.AuthenticationScheme, - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate); - return expirationDate is not null && LiesInTheFuture(expirationDate) ? token : null; - } - - private static bool LiesInTheFuture(string expirationDate) - { - return DateTime.Parse(expirationDate, CultureInfo.InvariantCulture) > DateTime.UtcNow; - } -} \ No newline at end of file diff --git a/backend/src/Extensions/HttpContextExtensions.cs b/backend/src/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..ed3897ca --- /dev/null +++ b/backend/src/Extensions/HttpContextExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Database.Authentication; +using Microsoft.AspNetCore.Http; + +namespace Database.Extensions; + +public static class HttpContextExtensions +{ + public static void SetBearerToken(this HttpContext httpContext, string accessToken) => + httpContext.Request.Headers.Authorization = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} {accessToken}"; + + public static string? ExtractBearerToken(this HttpContext httpContext) + { + var bearerTokenPrefix = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} "; + return httpContext.Request?.Headers?.Authorization + .FirstOrDefault( + x => x is not null + && x.TrimStart().StartsWith(bearerTokenPrefix, StringComparison.Ordinal) + ) + ?.TrimStart() + ?[bearerTokenPrefix.Length..] + ?.TrimEnd(); + } +} \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index d19b6b60..ef0cc14b 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -7,6 +7,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.Authentication; using Database.Extensions; using Database.Json; using GraphQL; @@ -157,23 +158,28 @@ CancellationToken cancellationToken ) { using var httpClient = httpClientFactory.CreateClient(CustomHttpClient); - var bearerToken = await httpContextAccessor.ExtractBearerToken(); + var bearerToken = httpContextAccessor.HttpContext?.ExtractBearerToken(); using var httpRequestMessage = new HttpRequestMessage( httpMethod, uri ); - httpRequestMessage.Content = httpContent; if (bearerToken is not null) { - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken); + httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue( + OpenIdConnectConstants.AuthorizationHeaderBearer, + bearerToken + ); } + httpRequestMessage.Content = httpContent; using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken); - if (httpResponseMessage.StatusCode != HttpStatusCode.OK) + if (httpResponseMessage.StatusCode is not HttpStatusCode.OK) { throw new HttpRequestException( - $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", null, - httpResponseMessage.StatusCode); + $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", + null, + httpResponseMessage.StatusCode + ); } return await read(httpResponseMessage.Content); } @@ -191,8 +197,7 @@ TContent content JsonSerializerSettings.GraphQl ) ); - result.Headers.ContentType = - new MediaTypeHeaderValue("application/json"); + result.Headers.ContentType = new MediaTypeHeaderValue("application/json"); return result; } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index f202dc22..f7c83578 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -12,14 +12,6 @@ namespace Database.Services; -public static partial class Log -{ - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Extracted Bearer Token: {Token}")] - public static partial void ExtractedToken(this ILogger logger, string? token); -} - /// /// Service to get current user from Metabase /// @@ -32,8 +24,7 @@ public sealed class UserService( AppSettings appSettings, ApiRequestService apiRequestService, IHttpContextAccessor httpContextAccessor, - CacheService cacheService, - ILogger logger + CacheService cacheService ) { /// @@ -68,27 +59,28 @@ public async Task GetCurrentUserOrApplicationAsync( CancellationToken cancellationToken ) { - // Extract token - var token = await httpContextAccessor.ExtractBearerToken(); - logger.ExtractedToken(token); + var httpContext = httpContextAccessor.HttpContext; + // If there is no HTTP context or there is no authenticated user, return early. + if (httpContext is null || httpContext.User is null) + { + return new CurrentUserOrApplication(null, null); + } + // If there is an authenticated user, then the bearer token exists and is valid. + var token = httpContextAccessor.HttpContext?.ExtractBearerToken(); if (token is null) { - return await QueryCurrentUserOrApplication.Do( - appSettings, - apiRequestService, - cancellationToken - ); + return new CurrentUserOrApplication(null, null); } - // Check if there is already a user for token + // Try to get the cached user or application for the token. if (!cacheService.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) { - // Get user from Metabase + // If it is not cached, fetch it from the metabase. cachedUserOrApplication = await QueryCurrentUserOrApplication.Do( appSettings, apiRequestService, cancellationToken ); - // Store user in cache + // And store it in the cache. cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); } return cachedUserOrApplication; From 101dc5141d6b65d3b9f48b814941af653aa8266b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 15 Jan 2026 18:54:58 +0100 Subject: [PATCH 633/878] Protect all same-site queries from antiforgery except those originating from `/graphql`, simplify GraphQL apollo client setup passing the up-to-date antiforgery cookie always, and use the cached user or application only when it is really safe to do so --- backend/src/AntiforgeryConstants.cs | 7 + .../QueryCurrentUserOrApplication.cs | 2 + .../Authentication/AuthenticationHandler.cs | 23 +-- .../Authentication/AuthenticationHelpers.cs | 47 ++++++ ...phQlAuthenticationAndAntiforgeryHandler.cs | 89 +++++++++++ .../src/Configuration/AuthConfiguration.cs | 1 + .../src/Configuration/GraphQlConfiguration.cs | 31 +--- .../src/Controllers/AntiforgeryController.cs | 4 +- backend/src/GraphQl/Entities/EntityType.cs | 1 + backend/src/GraphQl/GraphQlConstants.cs | 13 +- backend/src/OpenApiConstants.cs | 8 + backend/src/Services/ApiRequestService.cs | 10 +- backend/src/Services/UserService.cs | 41 +++-- backend/src/Startup.cs | 41 +++-- frontend/components/NavBar.tsx | 4 +- frontend/lib/apollo.ts | 145 ++++++------------ frontend/pages/_app.tsx | 17 +- 17 files changed, 277 insertions(+), 207 deletions(-) create mode 100644 backend/src/AntiforgeryConstants.cs create mode 100644 backend/src/Authentication/AuthenticationHelpers.cs create mode 100644 backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs create mode 100644 backend/src/OpenApiConstants.cs diff --git a/backend/src/AntiforgeryConstants.cs b/backend/src/AntiforgeryConstants.cs new file mode 100644 index 00000000..78427dd8 --- /dev/null +++ b/backend/src/AntiforgeryConstants.cs @@ -0,0 +1,7 @@ +namespace Database; + +internal static class AntiforgeryConstants +{ + internal const string HeaderName = "X-XSRF-TOKEN"; + internal const string CookieKey = "XSRF-TOKEN"; +} \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs index 257a4567..7509d3b6 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -14,6 +14,8 @@ public sealed class QueryCurrentUserOrApplication { private const string QueryFileName = "CurrentUserOrApplication.graphql"; + public static readonly CurrentUserOrApplication Empty = new(null, null); + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index c747a881..69ffc668 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -139,33 +139,12 @@ await UpdateTokenValuesAsync( return null; } - // Inspired by https://github.com/dotnet/aspnetcore/blob/85565dbb30d724c955179e1989c109c677e7b263/src/Http/Http.Extensions/src/RequestHeaders.cs#L338-L342 - public static Uri? GetOrigin(RequestHeaders headers) - { - if (Uri.TryCreate(headers.Headers.Origin, UriKind.RelativeOrAbsolute, out var uri)) - { - return uri; - } - return null; - } - - private static bool IsSameOriginOrReferer(HttpRequest request) - { - var headers = request.GetTypedHeaders(); - var originOrReferer = GetOrigin(headers) ?? headers.Referer; - if (originOrReferer is null) - { - return true; - } - return request.Host == HostString.FromUriComponent(originOrReferer); - } - public async Task AuthenticateAsync( HttpContext httpContext, CancellationToken cancellationToken ) { - if (IsSameOriginOrReferer(httpContext.Request)) + if (AuthenticationHelpers.IsSameOriginOrReferer(httpContext.Request)) { // For the Next.js Web frontend, the database acts as OpenId Connect // Client and uses the cookie scheme for authentication. See diff --git a/backend/src/Authentication/AuthenticationHelpers.cs b/backend/src/Authentication/AuthenticationHelpers.cs new file mode 100644 index 00000000..fe1f81d5 --- /dev/null +++ b/backend/src/Authentication/AuthenticationHelpers.cs @@ -0,0 +1,47 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Headers; + +namespace Database.Authentication; + +internal static class AuthenticationHelpers +{ + // Inspired by https://github.com/dotnet/aspnetcore/blob/85565dbb30d724c955179e1989c109c677e7b263/src/Http/Http.Extensions/src/RequestHeaders.cs#L338-L342 + private static Uri? GetOrigin(RequestHeaders headers) + { + if (Uri.TryCreate( + headers.Headers.Origin, + UriKind.RelativeOrAbsolute, + out var uri + )) + { + return uri; + } + return null; + } + + internal static bool IsSameOriginOrReferer(HttpRequest request) + { + var headers = request.GetTypedHeaders(); + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin#description + var originOrReferer = GetOrigin(headers) ?? headers.Referer; + if (originOrReferer is null) + { + return false; + } + return request.Host == HostString.FromUriComponent(originOrReferer); + } + + internal static bool IsReferredToFromSubpath( + HttpRequest request, + string subpath + ) + { + var path = request.GetTypedHeaders().Referer?.PathAndQuery; + return + path is not null + && path.Length >= subpath.Length + && path.StartsWith(subpath, StringComparison.InvariantCulture) + && (path.Length == subpath.Length || path[subpath.Length] == '?' || path[subpath.Length] == '/'); + } +} \ No newline at end of file diff --git a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs new file mode 100644 index 00000000..050e6954 --- /dev/null +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using HotChocolate; +using Database.GraphQl; +using Microsoft.AspNetCore.Antiforgery; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; + +namespace Database.Authentication; + +public static partial class Log +{ + [LoggerMessage( + EventId = 0, + Level = LogLevel.Error, + Message = "Failed to authenticate the claims principal in the HTTP context.")] + internal static partial void FailedToAuthenticate( + this ILogger logger, + Exception exception + ); +} + +internal sealed class GraphQlAuthenticationAndAntiforgeryHandler( + AuthenticationHandler authenticationHandler, + IAntiforgery antiforgeryService, + ILogger logger +) +{ + private async Task AuthenticateAsync( + HttpContext httpContext, + CancellationToken cancellationToken + ) + { + try + { + var authenticateResult = await authenticationHandler.AuthenticateAsync(httpContext, cancellationToken); + if (authenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + httpContext.User = authenticateResult.Principal; + } + } + catch (Exception exception) + { + logger.FailedToAuthenticate(exception); + throw new GraphQLException( + ErrorBuilder + .New() + .SetMessage("Failed to authenticate the claims principal in the HTTP context.") + .SetException(exception) + .Build() + ); + } + } + + private async Task ValidateAntiforgeryTokenAsync( + HttpContext httpContext + ) + { + try + { + await antiforgeryService.ValidateRequestAsync(httpContext); + } + catch (AntiforgeryValidationException exception) + { + throw new GraphQLException( + ErrorBuilder + .New() + .SetMessage("The antiforgery token is invalid.") + .SetException(exception) + .Build() + ); + } + } + + public async Task HandleAsync( + HttpContext httpContext, + CancellationToken cancellationToken + ) + { + await AuthenticateAsync(httpContext, cancellationToken); + if (AuthenticationHelpers.IsSameOriginOrReferer(httpContext.Request) + && !AuthenticationHelpers.IsReferredToFromSubpath(httpContext.Request, GraphQlConstants.EndpointPath)) + { + await ValidateAntiforgeryTokenAsync(httpContext); + } + } +} \ No newline at end of file diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index ff5ab314..285ed6a0 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -27,6 +27,7 @@ AppSettings appSettings var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); services.AddScoped(); + services.AddScoped(); ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); ConfigureOpenIddictServices(services, environment, appSettings, encryptionCertificate, signingCertificate); diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index b9301c67..624ea056 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -21,18 +21,6 @@ namespace Database.Configuration; -public static partial class Log -{ - [LoggerMessage( - EventId = 0, - Level = LogLevel.Error, - Message = "Failed to authenticate the claims principal in the HTTP context.")] - public static partial void FailedToAuthenticate( - this ILogger logger, - Exception exception - ); -} - public static class GraphQlConfiguration { public static void ConfigureServices( @@ -118,24 +106,13 @@ IWebHostEnvironment environment // flows. .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => { - try - { - var authenticationHandler = httpContext.RequestServices.GetRequiredService(); - var authenticateResult = await authenticationHandler.AuthenticateAsync(httpContext, cancellationToken); - if (authenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) - { - httpContext.User = authenticateResult.Principal; - } - } - catch (Exception exception) - { - var logger = httpContext.RequestServices.GetRequiredService>(); - logger.FailedToAuthenticate(exception); - } + await httpContext.RequestServices + .GetRequiredService() + .HandleAsync(httpContext, cancellationToken); }) .AddDiagnosticEventListener(_ => new LoggingDiagnosticEventListener( - _.GetApplicationService>() + _.GetRequiredService>() ) ) // Scalar Types diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index 86339e7d..c82d8ab2 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -17,8 +17,6 @@ AuthenticationHandler authenticationHandler ) : Controller { - private const string XsrfCookieKey = "XSRF-TOKEN"; - private readonly IAntiforgery _antiforgeryService = antiforgeryService; private readonly CookieOptions _xsrfCookieOptions = @@ -42,7 +40,7 @@ CancellationToken cancellationToken } var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append( - XsrfCookieKey, + AntiforgeryConstants.CookieKey, tokens.RequestToken ?? throw new InvalidOperationException("The request token supposed to be generated by the antiforgery service is null."), _xsrfCookieOptions diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index dceacdae..6e1402c1 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -33,6 +33,7 @@ IObjectTypeDescriptor descriptor descriptor .Field(t => t.Version) .Type>() + .Name(GraphQlConstants.VersionFieldName) .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs index c60b3ebd..77d482f7 100644 --- a/backend/src/GraphQl/GraphQlConstants.cs +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -1,9 +1,12 @@ namespace Database.GraphQl; -public static class GraphQlConstants +internal static class GraphQlConstants { - public const string TypeDiscriminatorPropertyName = "__typename"; - public const string FilterInputSuffix = "FilterInput"; - public const string SortInputSuffix = "SortInput"; - public const string UuidFieldName = "uuid"; + internal const string EndpointPath = "/graphql"; + internal const string CorsPolicy = "GraphQlCorsPolicy"; + internal const string TypeDiscriminatorPropertyName = "__typename"; + internal const string FilterInputSuffix = "FilterInput"; + internal const string SortInputSuffix = "SortInput"; + internal const string UuidFieldName = "uuid"; + internal const string VersionFieldName = "version"; } \ No newline at end of file diff --git a/backend/src/OpenApiConstants.cs b/backend/src/OpenApiConstants.cs new file mode 100644 index 00000000..fd528cd5 --- /dev/null +++ b/backend/src/OpenApiConstants.cs @@ -0,0 +1,8 @@ +namespace Database; + +internal static class OpenApiConstants +{ + internal const string DocumentName = "v1"; + internal const string RoutePattern = "/openapi/{documentName}.json"; + internal const string DocsRoute = "/openapi/docs"; +} \ No newline at end of file diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index ef0cc14b..9adb9bdd 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -12,6 +12,7 @@ using Database.Json; using GraphQL; using Microsoft.AspNetCore.Http; +using Microsoft.Net.Http.Headers; namespace Database.Services; @@ -20,7 +21,8 @@ namespace Database.Services; /// public sealed class ApiRequestService( IHttpClientFactory httpClientFactory, - IHttpContextAccessor httpContextAccessor + IHttpContextAccessor httpContextAccessor, + AppSettings appSettings ) { public const string CustomHttpClient = "Custom"; @@ -163,6 +165,10 @@ CancellationToken cancellationToken httpMethod, uri ); + httpRequestMessage.Headers.Add( + HeaderNames.Origin, + appSettings.Host + ); if (bearerToken is not null) { httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue( @@ -197,7 +203,7 @@ TContent content JsonSerializerSettings.GraphQl ) ); - result.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + result.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); return result; } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index f7c83578..1832a049 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -4,10 +4,8 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; -using static OpenIddict.Abstractions.OpenIddictConstants; using Database.ApiRequests; using Database.Extensions; -using static Database.ApiRequests.QueryCurrentUserOrApplication; using System; namespace Database.Services; @@ -32,55 +30,64 @@ CacheService cacheService /// public string? GetOpenIdConnectClientId() { - return httpContextAccessor.HttpContext?.User.GetClaim(Claims.AuthorizedParty); + return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); } public async Task UserOrApplicationAsync( - Func> authorizeUser, - Func> authorizeApplication, + Func> handleUser, + Func> handleApplication, CancellationToken cancellationToken ) { var userOrApplication = await GetCurrentUserOrApplicationAsync(cancellationToken); if (userOrApplication.CurrentApplication is not null) { - return await authorizeApplication(userOrApplication.CurrentApplication); + return await handleApplication(userOrApplication.CurrentApplication); } else { - return await authorizeUser(userOrApplication.CurrentUser); + return await handleUser(userOrApplication.CurrentUser); } } /// /// Get current user or OpenId Connect application from Metabase (or the local cache). /// - public async Task GetCurrentUserOrApplicationAsync( + public async Task GetCurrentUserOrApplicationAsync( CancellationToken cancellationToken ) { var httpContext = httpContextAccessor.HttpContext; - // If there is no HTTP context or there is no authenticated user, return early. - if (httpContext is null || httpContext.User is null) + if (httpContext is null) { - return new CurrentUserOrApplication(null, null); + return QueryCurrentUserOrApplication.Empty; } - // If there is an authenticated user, then the bearer token exists and is valid. - var token = httpContextAccessor.HttpContext?.ExtractBearerToken(); + var token = httpContext.ExtractBearerToken(); if (token is null) { - return new CurrentUserOrApplication(null, null); + return QueryCurrentUserOrApplication.Empty; } - // Try to get the cached user or application for the token. + // If there is no authenticated user, then the bearer token may be + // invalid or expired, so we cannot trust a possibly-cached result. + if (httpContext.User is null) + { + return await QueryCurrentUserOrApplication.Do( + appSettings, + apiRequestService, + cancellationToken + ); + } + // If there is an authenticated user, then the bearer token is valid and + // we try to get the cached user or application. if (!cacheService.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) { - // If it is not cached, fetch it from the metabase. + // If it is not cached, fetch it ... cachedUserOrApplication = await QueryCurrentUserOrApplication.Do( appSettings, apiRequestService, cancellationToken ); - // And store it in the cache. + // ... and store it in the cache. cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); } return cachedUserOrApplication; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2a02d283..264297db 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -5,6 +5,12 @@ using System.Text.Json; using System.Threading.Tasks; using HotChocolate.AspNetCore; +using Database.Configuration; +using Database.Data; +using Database.Data.Extensions; +using Database.Enumerations; +using Database.Services; +using Laraue.EfCoreTriggers.PostgreSql.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Diagnostics.HealthChecks; @@ -21,12 +27,7 @@ using Microsoft.OpenApi; using Scalar.AspNetCore; using Serilog; -using Database.Configuration; -using Database.Data; -using Database.Data.Extensions; -using Database.Enumerations; -using Database.Services; -using Laraue.EfCoreTriggers.PostgreSql.Extensions; +using Database.GraphQl; namespace Database; @@ -35,13 +36,6 @@ public sealed class Startup( IConfiguration configuration ) { - private const string GraphQlCorsPolicy = "GraphQlCorsPolicy"; - private const string AntiforgeryHeaderName = "X-XSRF-TOKEN"; - - private const string OpenApiDocumentName = "v1"; - private const string OpenApiRoutePattern = "/openapi/{documentName}.json"; - private const string OpenApiDocsRoute = "/openapi/docs"; - private readonly AppSettings _appSettings = configuration.Get(_ => { @@ -60,7 +54,10 @@ public void ConfigureServices(IServiceCollection services) ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); // ConfigureSessionServices(services, _environment); // Not used - services.AddAntiforgery(_ => { _.HeaderName = AntiforgeryHeaderName; }); + services.AddAntiforgery(_ => + { + _.HeaderName = AntiforgeryConstants.HeaderName; + }); services .AddDataProtection() .PersistKeysToDbContext(); @@ -89,7 +86,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services ); services.AddCors(_ => _.AddPolicy( - GraphQlCorsPolicy, + GraphQlConstants.CorsPolicy, policy => policy .AllowAnyOrigin() @@ -125,7 +122,7 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // TODO I consider the flattened structure a bug. How can we solve this? } ); - services.AddOpenApi(OpenApiDocumentName, _ => + services.AddOpenApi(OpenApiConstants.DocumentName, _ => { _.OpenApiVersion = OpenApiSpecVersion.OpenApi3_0; _.AddScalarTransformers(); @@ -298,12 +295,12 @@ public void Configure(WebApplication app) // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx // app.UseWebSockets(); - app.MapOpenApi(OpenApiRoutePattern); - app.MapScalarApiReference(OpenApiDocsRoute, _ => + app.MapOpenApi(OpenApiConstants.RoutePattern); + app.MapScalarApiReference(OpenApiConstants.DocsRoute, _ => { _.Servers = []; // https://github.com/dotnet/aspnetcore/issues/57332#issuecomment-2480939916 - _.AddDocument(OpenApiDocumentName); // For multiple documents see https://guides.scalar.com/scalar/scalar-api-references/integrations/net-aspnet-core/integration#configuration-options__multiple-openapi-documents - _.WithOpenApiRoutePattern(OpenApiRoutePattern); + _.AddDocument(OpenApiConstants.DocumentName); // For multiple documents see https://guides.scalar.com/scalar/scalar-api-references/integrations/net-aspnet-core/integration#configuration-options__multiple-openapi-documents + _.WithOpenApiRoutePattern(OpenApiConstants.RoutePattern); }); app.MapGraphQL() .WithOptions( @@ -319,13 +316,13 @@ public void Configure(WebApplication app) DisableTelemetry = true, Enable = true, // _environment.IsDevelopment() IncludeCookies = false, - GraphQLEndpoint = "/graphql", + GraphQLEndpoint = GraphQlConstants.EndpointPath, HttpMethod = DefaultHttpMethod.Post, Title = "GraphQL" } } ) - .RequireCors(GraphQlCorsPolicy); + .RequireCors(GraphQlConstants.CorsPolicy); app.MapControllers(); app.MapHealthChecks("/health", new HealthCheckOptions diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 7651eb27..b3516e3f 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -5,7 +5,7 @@ import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; import { CurrentUserDocument } from "../queries/currentUser.generated"; -import { getXsrfToken } from "../lib/apollo"; +import { extractXsrfTokenFromCookie } from "../lib/apollo"; import { Route } from "next"; type NavItemProps = { @@ -65,7 +65,7 @@ export default function NavBar({ items }: NavBarProps) { type="hidden" value={ typeof window !== "undefined" - ? (getXsrfToken() ?? undefined) + ? (extractXsrfTokenFromCookie() ?? "") : "" } /> diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 91cfe3e3..f09f111f 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -1,5 +1,3 @@ -import { IncomingMessage, ServerResponse } from "http"; -import { useMemo } from "react"; import { ApolloClient, InMemoryCache, @@ -10,18 +8,15 @@ import { Defer20220824Handler } from "@apollo/client/incremental"; import { LocalState } from "@apollo/client/local-state"; import { CombinedGraphQLErrors } from "@apollo/client/errors"; import { PersistedQueryLink } from "@apollo/client/link/persisted-queries"; -import merge from "deepmerge"; +import { SetContextLink } from "@apollo/client/link/context"; import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin -let apolloClient: ApolloClient | undefined; +const AntiforgeryCookieKey = "XSRF-TOKEN"; +const AntiforgeryHeaderName = "X-XSRF-TOKEN"; +const GraphQlEndpointPath = "/graphql/"; -export type ResolverContext = { - req?: IncomingMessage; - res?: ServerResponse; -}; - -function getCookie(name: string, cookies: string): string | null { +function extractCookie(name: string, cookies: string): string | null { return ( cookies .split(";") @@ -35,98 +30,61 @@ function getCookie(name: string, cookies: string): string | null { ); } -export function getXsrfToken(): string | null { - return getCookie("XSRF-TOKEN", document.cookie); +export function extractXsrfTokenFromCookie(): string | null { + return extractCookie(AntiforgeryCookieKey, document.cookie); } -function createIsomorphLink(context: ResolverContext = {}) { - var headers: Record = {}; - headers["accept"] = "application/json"; - var cookie = context.req?.headers?.cookie; - if (cookie) { - headers["cookie"] = cookie; - } - if (typeof window !== "undefined") { - var antiforgeryToken = getXsrfToken(); - if (antiforgeryToken) headers["X-XSRF-TOKEN"] = antiforgeryToken; +// inspired by https://www.apollographql.com/docs/react/networking/authentication#header +const xsrfLink = new SetContextLink(({ headers }) => { + const antiforgeryToken = extractXsrfTokenFromCookie(); + if (antiforgeryToken) { + return { + headers: { + ...headers, + [AntiforgeryHeaderName]: antiforgeryToken, + }, + }; } - return new PersistedQueryLink({ - sha256, - useGETForHashedQueries: false, - }).concat( - new HttpLink({ - uri: - // In the case `typeof window === "undefined"`, that is on the server - // side, because we require HTTPS for the JWT bearer token, we cannot use - // "http://backend:8080/graphql/" to make local requests. When the reverse - // proxy NGINX serves the certificate like in development we could use - // "https://nginx:443/graphql/" to make local requests, which this does - // however not work in production. Thus, on the server side we also use - // `${process.env.NEXT_PUBLIC_DATABASE_URL}/graphql/` as on the client - // side. - typeof window === "undefined" - ? new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href - : new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL).href, - useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors - credentials: "same-origin", - headers: headers, - }), - ); -} + return { headers: headers }; +}); -function createApolloClient(context?: ResolverContext) { - return new ApolloClient({ - ssrMode: typeof window === "undefined", - link: createIsomorphLink(context), - cache: new InMemoryCache({ - possibleTypes: fragmentMatcherTypes.possibleTypes, - }), - localState: new LocalState({}), - incrementalHandler: new Defer20220824Handler(), - }); -} +const persistedQueryLink = new PersistedQueryLink({ + sha256, + useGETForHashedQueries: false, +}); -export function initializeApollo( - initialState: any = null, - // Pages with Next.js data fetching methods, like `getStaticProps`, can send - // a custom context which will be used by `SchemaLink` to server render pages - context?: ResolverContext, -) { - const _apolloClient = apolloClient ?? createApolloClient(context); +const httpLink = new HttpLink({ + uri: new URL(GraphQlEndpointPath, process.env.NEXT_PUBLIC_DATABASE_URL).href, + useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors + credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie + headers: { "accept": "application/json" }, +}); - // If your page has Next.js data fetching methods that use Apollo Client, the initial state - // get hydrated here - if (initialState) { - // Get existing cache, loaded during client side data fetching - const existingCache = _apolloClient.extract(); - // Merge the existing cache into data passed from getStaticProps/getServerSideProps - const data = merge(initialState, existingCache as any); - // Restore the cache with the merged data - _apolloClient.cache.restore(data); - } - // For SSG and SSR always create a new Apollo Client - if (typeof window === "undefined") return _apolloClient; - // Create the Apollo Client once in the client - if (!apolloClient) apolloClient = _apolloClient; +const apolloLink = xsrfLink.concat(persistedQueryLink).concat(httpLink); - return _apolloClient; -} +export const apolloClient = new ApolloClient({ + ssrMode: false, + link: apolloLink, + cache: new InMemoryCache({ + possibleTypes: fragmentMatcherTypes.possibleTypes, + }), + localState: new LocalState({}), + incrementalHandler: new Defer20220824Handler(), +}); // See https://www.apollographql.com/docs/react/data/error-handling export function stringifyApolloError(error: ErrorLike) { if (CombinedGraphQLErrors.is(error)) { - return `Name(${error.name}); Message(${error.message}); Extensions(${ - error.extensions - }); Stack(${error.stack}); GraphQL Errors(${error.errors - .map( - (e) => - `[Message(${e.message}); Path(${e.path?.join( - ".", - )}); Locations(${e.locations?.join(", ")}); Extensions(${ - e.extensions - })]`, - ) - .join(", ")})`; + return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions + }); Stack(${error.stack}); GraphQL Errors(${error.errors + .map( + (e) => + `[Message(${e.message}); Path(${e.path?.join( + ".", + )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions + })]`, + ) + .join(", ")})`; // } if (CombinedProtocolErrors.is(error)) { // Handle multipart subscription protocol errors // } if (LocalStateError.is(error)) { @@ -141,11 +99,6 @@ export function stringifyApolloError(error: ErrorLike) { return `Name(${error.name}); Message(${error.message}); Stack(${error.stack})`; } -export function useApollo(initialState: any) { - const store = useMemo(() => initializeApollo(initialState), [initialState]); - return store; -} - /* Copy the contents of this block into a `.d.ts` file in your project to enable correct response types in your custom links. If you do not use the `@defer` directive in your application, you can safely remove this block. diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 603f33be..d254cc0a 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -1,23 +1,18 @@ import { AppProps } from "next/app"; -import { useApollo } from "../lib/apollo"; +import { apolloClient } from "../lib/apollo"; import { ApolloProvider } from "@apollo/client/react"; import { CookiesProvider } from "react-cookie"; -import { ConfigProvider, message } from "antd"; +import { App, ConfigProvider } from "antd"; import "../styles/global.css"; -export default function App({ Component, pageProps }: AppProps) { - const apolloClient = useApollo(pageProps.initialApolloState); - - message.config({ - top: 100, - duration: 2, - }); - +export default function NextApp({ Component, pageProps }: AppProps) { return ( - + + + From 7d994aa64f2ccb10e579fcede33e6b61428bc9da Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 16 Jan 2026 10:59:36 +0100 Subject: [PATCH 634/878] Preserve only the necessary claims in the cookie and move token logic in `AuthenticationTokens` to detect incompatibilities easily --- .../Authentication/AuthenticationHandler.cs | 44 +++----------- .../Authentication/AuthenticationTokens.cs | 57 ++++++++++++++++++- .../Authentication/OpenIdConnectConstants.cs | 2 +- .../Controllers/AuthenticationController.cs | 36 +++--------- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index 69ffc668..5b7ff0e6 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -7,12 +6,8 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Headers; -using OpenIddict.Abstractions; using OpenIddict.Client; -using OpenIddict.Client.AspNetCore; using OpenIddict.Validation.AspNetCore; -using static OpenIddict.Client.OpenIddictClientModels; namespace Database.Authentication; @@ -20,21 +15,6 @@ public sealed class AuthenticationHandler( OpenIddictClientService openIddictClientService ) { - private static string? GetBackchannelAccessToken(AuthenticateResult authenticateResult) => - authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken); - - private static DateTimeOffset? GetBackchannelAccessTokenExpirationDate(AuthenticateResult authenticateResult) => - DateTimeOffset.TryParse( - authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate), - CultureInfo.InvariantCulture, - out var date - ) - ? date - : null; - - private static string? GetRefreshToken(AuthenticateResult authenticateResult) => - authenticateResult.Properties?.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken); - private static async Task UpdateTokenValuesAsync( HttpContext httpContext, ClaimsPrincipal claimsPrincipal, @@ -44,24 +24,19 @@ AuthenticationTokens freshTokens { // Override the tokens using the values returned in the token response. var properties = authenticationProperties.Clone(); - // Keep in sync with `GetTokenValue` in `AddRequestTransform` above properties.UpdateTokenValue( - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken, + AuthenticationTokens.AccessTokenName, freshTokens.AccessToken ); properties.UpdateTokenValue( - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate, - freshTokens.AccessTokenExpirationDate?.ToString(CultureInfo.InvariantCulture) ?? string.Empty - ); - properties.UpdateTokenValue( - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken, - freshTokens.IdentityToken + AuthenticationTokens.AccessTokenExpirationDateName, + freshTokens.AccessTokenExpirationDateAsString ?? string.Empty ); // Note: if no refresh token was returned, preserve the refresh token initially returned. if (!string.IsNullOrEmpty(freshTokens.RefreshToken)) { properties.UpdateTokenValue( - OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken, + AuthenticationTokens.RefreshTokenName, freshTokens.RefreshToken ); } @@ -103,20 +78,20 @@ CancellationToken cancellationToken { return null; } - var accessToken = GetBackchannelAccessToken(cookieAuthenticationResult); - var expirationDate = GetBackchannelAccessTokenExpirationDate(cookieAuthenticationResult); + var accessToken = AuthenticationTokens.ExtractAccessToken(cookieAuthenticationResult); + var expirationDate = AuthenticationTokens.ExtractAccessTokenExpirationDate(cookieAuthenticationResult); if (accessToken is not null && expirationDate is not null - && TimeProvider.System.GetUtcNow() <= expirationDate?.Subtract(OpenIdConnectConstants.AccessAndIdentityTokenLifetime.Divide(3)) + && TimeProvider.System.GetUtcNow() <= expirationDate?.Subtract(OpenIdConnectConstants.AccessTokenLifetime.Divide(3)) ) { return accessToken; } - var refreshToken = GetRefreshToken(cookieAuthenticationResult); + var refreshToken = AuthenticationTokens.ExtractRefreshToken(cookieAuthenticationResult); if (refreshToken is not null) { var refreshTokenAuthenticationResult = await openIddictClientService.AuthenticateWithRefreshTokenAsync( - new RefreshTokenAuthenticationRequest + new OpenIddictClientModels.RefreshTokenAuthenticationRequest { DisableUserInfo = true, RefreshToken = refreshToken, @@ -130,7 +105,6 @@ await UpdateTokenValuesAsync( new AuthenticationTokens( AccessToken: refreshTokenAuthenticationResult.AccessToken, AccessTokenExpirationDate: refreshTokenAuthenticationResult.AccessTokenExpirationDate, - IdentityToken: refreshTokenAuthenticationResult.IdentityToken, RefreshToken: refreshTokenAuthenticationResult.RefreshToken ) ); diff --git a/backend/src/Authentication/AuthenticationTokens.cs b/backend/src/Authentication/AuthenticationTokens.cs index 3827e9f5..6d245646 100644 --- a/backend/src/Authentication/AuthenticationTokens.cs +++ b/backend/src/Authentication/AuthenticationTokens.cs @@ -1,10 +1,63 @@ using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.AspNetCore.Authentication; +using OpenIddict.Client.AspNetCore; namespace Database.Authentication; public sealed record AuthenticationTokens( string AccessToken, DateTimeOffset? AccessTokenExpirationDate, - string? IdentityToken, string? RefreshToken -); \ No newline at end of file +) +{ + internal const string AccessTokenName = OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken; + internal const string AccessTokenExpirationDateName = OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate; + internal const string IdentityTokenName = OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken; + internal const string RefreshTokenName = OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken; + + public string? AccessTokenExpirationDateAsString => AccessTokenExpirationDate?.ToString(CultureInfo.InvariantCulture); + private static DateTimeOffset? ParseAccessTokenExpirationDate(string? dateString) => + DateTimeOffset.TryParse( + dateString, + CultureInfo.InvariantCulture, + out var date + ) + ? date + : null; + + public IEnumerable AsEnumerable() + { + yield return new AuthenticationToken { Name = AccessTokenName, Value = AccessToken }; + if (AccessTokenExpirationDateAsString is not null) + { + yield return new AuthenticationToken { Name = AccessTokenExpirationDateName, Value = AccessTokenExpirationDateAsString }; + } + if (RefreshToken is not null) + { + yield return new AuthenticationToken { Name = RefreshTokenName, Value = RefreshToken }; + } + } + + public static string? ExtractAccessToken(AuthenticateResult result) => + result.Properties?.GetTokenValue(AccessTokenName); + + public static DateTimeOffset? ExtractAccessTokenExpirationDate(AuthenticateResult result) => + ParseAccessTokenExpirationDate(result.Properties?.GetTokenValue(AccessTokenExpirationDateName)); + + public static string? ExtractIdentityToken(AuthenticateResult result) => + result.Properties?.GetTokenValue(IdentityTokenName); + + public static string? ExtractRefreshToken(AuthenticateResult result) => + result.Properties?.GetTokenValue(RefreshTokenName); + + public static AuthenticationTokens From(string accessToken, AuthenticateResult result) + { + return new( + AccessToken: accessToken, + AccessTokenExpirationDate: ExtractAccessTokenExpirationDate(result), + RefreshToken: ExtractRefreshToken(result) + ); + } +}; \ No newline at end of file diff --git a/backend/src/Authentication/OpenIdConnectConstants.cs b/backend/src/Authentication/OpenIdConnectConstants.cs index 3faa8e0e..8214e1cd 100644 --- a/backend/src/Authentication/OpenIdConnectConstants.cs +++ b/backend/src/Authentication/OpenIdConnectConstants.cs @@ -4,7 +4,7 @@ namespace Database.Authentication; public static class OpenIdConnectConstants { - public static readonly TimeSpan AccessAndIdentityTokenLifetime = TimeSpan.FromHours(1); + public static readonly TimeSpan AccessTokenLifetime = TimeSpan.FromHours(1); public const string MetabaseRegistrationId = "metabase"; public const string MetabaseClientId = "metabase"; diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index b0e5de5f..5bb1ef7e 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -16,7 +16,6 @@ using static OpenIddict.Abstractions.OpenIddictConstants; using Microsoft.AspNetCore.Routing; using Database.Authentication; -using System.Globalization; namespace Database.Controllers; @@ -96,8 +95,7 @@ public async Task LogOut(string? returnUrl) [OpenIddictClientAspNetCoreConstants.Properties.Issuer] = _issuer.AbsoluteUri, // While not required, the specification encourages sending an id_token_hint // parameter containing an identity token returned by the server for this user. - [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = - result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelIdentityToken) + [OpenIddictClientAspNetCoreConstants.Properties.IdentityTokenHint] = AuthenticationTokens.ExtractIdentityToken(result) } ) { @@ -154,7 +152,7 @@ CancellationToken cancellationToken throw new InvalidOperationException("The external authorization data cannot be used for authentication."); } - var accessToken = result.Properties.GetTokenValue(OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken) + var accessToken = AuthenticationTokens.ExtractAccessToken(result) ?? throw new InvalidOperationException($"The external authorization data misses an access token."); var subject = result.Principal.GetClaim(ClaimTypes.NameIdentifier) ?? throw new InvalidOperationException($"The external authorization data misses the claim {ClaimTypes.NameIdentifier}."); @@ -171,24 +169,8 @@ CancellationToken cancellationToken // By default, OpenIddict will automatically try to map the email/name and name identifier claims from // their standard OpenID Connect or provider-specific equivalent, if available. If needed, additional // claims can be resolved from the external identity and copied to the final authentication cookie. - // For example - // .SetClaim(ClaimTypes.Email, result.Principal.GetClaim(ClaimTypes.Email)) // The claims are fetched from the userinfo endpoint of the authorization provider. - // We add the claim `IdentityOptions.ClaimsIdentity.UserIdClaimType` to make - // `UserManager.GetUserAsync(claimsPrincipal)` return the authenticated user. - // You can use the debugger to see which claims are present. Just now it were: - // iss, exp, iat, aud, sub, role, name, preferred_username, email, azp, nonce, at_hash, email_verified, - // phone_number_verified, as, oi_reg_id, http://schemas.xmlsoat.org/.../emailaddress, - // http://schemas.xmlsoat.org/.../name, http://schemas.xmlsoat.org/.../nameidentifier - identity.SetClaim(ClaimTypes.Name, name) - .SetClaim(ClaimTypes.NameIdentifier, subject); - - // Preserve `client_id` for usage by access-rights service. - identity.SetClaim(Claims.AuthorizedParty, result.Principal.GetClaim(Claims.AuthorizedParty)); - - // Preserve the registration details to be able to resolve them later. - identity.SetClaim(Claims.Private.RegistrationId, result.Principal.GetClaim(Claims.Private.RegistrationId)) - .SetClaim(Claims.Private.ProviderName, result.Principal.GetClaim(Claims.Private.ProviderName)); + identity.SetClaim(ClaimTypes.NameIdentifier, subject); // Build the authentication properties based on the properties that were added when the challenge was triggered. var properties = new AuthenticationProperties(result.Properties.Items) @@ -210,13 +192,12 @@ CancellationToken cancellationToken // authentication cookie beyond the lifetime of the authentication ticket itself. IsPersistent = false }; + + // If needed, the tokens returned by the authorization server can be stored in the authentication cookie. + // To make cookies less heavy, tokens that are not used are filtered out before creating the cookie. properties.StoreTokens( - result.Properties.GetTokens().Where(token => token.Name is - // Preserve the access, identity and refresh tokens returned in the token response, if available. - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessToken or - OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate or - OpenIddictClientAspNetCoreConstants.Tokens.RefreshToken - ) + // Preserve the access, identity and refresh tokens returned in the token response, if available. + AuthenticationTokens.From(accessToken, result).AsEnumerable() ); // Add the user to the database if he/she does not exist. @@ -239,7 +220,6 @@ OpenIddictClientAspNetCoreConstants.Tokens.BackchannelAccessTokenExpirationDate { user.Update(name); } - await context.SaveChangesAsync(cancellationToken); // Ask the cookie authentication sign-in handler to return a new cookie and redirect From a1b73708f2259ddf23eb59e914d65ae159f3e30f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 16 Jan 2026 12:52:42 +0100 Subject: [PATCH 635/878] Make user-info fields for which we do not have the scope nullable --- backend/src/ApiRequests/GetUserInfo.cs | 6 +++--- .../__snapshots__/GraphQlSchemaTests.IsUnchanged.snap | 6 +++--- frontend/type-defs.graphqls | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index 590c30e2..68488032 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -16,10 +16,10 @@ public static Uri GetEndpoint(AppSettings appSettings) => public sealed record UserInfo( Address? Address, string? Email, - bool EmailVerified, - string Name, + bool? EmailVerified, + string Name, // is not null because the OpenId Connect client asks for the scope `Profile` string? PhoneNumber, - bool PhoneNumberVerified, + bool? PhoneNumberVerified, IReadOnlyList? Roles, string Sub, // Subject string? Website diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 427cabc6..032bd601 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -864,11 +864,11 @@ type User implements Node { type UserInfo { address: Address - email: String! - emailVerified: Boolean! + email: String + emailVerified: Boolean name: String! phoneNumber: String - phoneNumberVerified: Boolean! + phoneNumberVerified: Boolean roles: [String!] sub: String! website: String diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 427cabc6..032bd601 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -864,11 +864,11 @@ type User implements Node { type UserInfo { address: Address - email: String! - emailVerified: Boolean! + email: String + emailVerified: Boolean name: String! phoneNumber: String - phoneNumberVerified: Boolean! + phoneNumberVerified: Boolean roles: [String!] sub: String! website: String From 00adc90e1e780f4b7a786dab895df1f25d69e8e1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 16 Jan 2026 13:00:02 +0100 Subject: [PATCH 636/878] Use message from `App` (instead of `messageApi`) --- frontend/components/data/calorimetric/CalorimetricData.tsx | 7 +++---- frontend/components/data/geometric/GeometricData.tsx | 7 +++---- frontend/components/data/hygrothermal/HygrothermalData.tsx | 7 +++---- frontend/components/data/optical/OpticalData.tsx | 7 +++---- frontend/components/data/photovoltaic/PhotovoltaicData.tsx | 7 +++---- frontend/components/databases/Database.tsx | 7 +++---- frontend/pages/data-protection-information.tsx | 3 ++- frontend/pages/data/calorimetric/index.tsx | 7 +++---- frontend/pages/data/geometric/index.tsx | 7 +++---- frontend/pages/data/hygrothermal/index.tsx | 7 +++---- frontend/pages/data/optical/index.tsx | 7 +++---- frontend/pages/data/photovoltaic/index.tsx | 7 +++---- frontend/pages/legal-notice.tsx | 3 ++- frontend/pages/user-info.tsx | 7 +++---- 14 files changed, 40 insertions(+), 50 deletions(-) diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 2a266f40..3e04a127 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,6 +1,6 @@ import { Scalars } from "../../../__generated__/graphql"; import { CalorimetricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions, message } from "antd"; +import { Skeleton, Result, Descriptions, App } from "antd"; import { useEffect } from "react"; import { stringifyApolloError } from "../../../lib/apollo"; import DataPageHeader from "../DataPageHeader"; @@ -19,11 +19,11 @@ export default function CalorimetricData({ }, }); const calorimetricData = data?.calorimetricData; - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); useEffect(() => { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -43,7 +43,6 @@ export default function CalorimetricData({ return ( <> - {contextHolder} { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -43,7 +43,6 @@ export default function GeometricData({ geometricDataId }: GeometricDataProps) { return ( <> - {contextHolder} { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -43,7 +43,6 @@ export default function HygrothermalData({ return ( <> - {contextHolder} { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -41,7 +41,6 @@ export default function OpticalData({ opticalDataId }: OpticalDataProps) { return ( <> - {contextHolder} { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -43,7 +43,6 @@ export default function PhotovoltaicData({ return ( <> - {contextHolder} { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -36,7 +36,6 @@ export default function Database({}: DatabaseProps) { return ( <> - {contextHolder} (null); const [loading, setLoading] = useState(false); + const { message } = App.useApp(); useEffect(() => { const fetchData = async () => { diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index 1d3ba3a3..e23a22c7 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -1,7 +1,7 @@ import Layout from "../../../components/Layout"; import { Table, - message, + App, Form, Button, Alert, @@ -92,7 +92,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -210,7 +210,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.message); + message.error(error.message); } // TODO Casting to `CalorimetricData` is wrong and error prone! setData( @@ -233,7 +233,6 @@ function Page() { return ( - {contextHolder} Calorimetric Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index bf953f5c..fa7601fd 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -1,7 +1,7 @@ import Layout from "../../../components/Layout"; import { Table, - message, + App, Form, Button, Alert, @@ -80,7 +80,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); const [data, setData] = useState([]); const allGeometricDataQuery = useQuery(AllGeometricDataDocument, { @@ -168,7 +168,7 @@ function Page() { ); if (error) { console.log(error); - messageApi.error(error.message); + message.error(error.message); } setData( (data?.allGeometricData?.edges?.map((x) => x.node) || @@ -188,7 +188,6 @@ function Page() { return ( - {contextHolder} Geometric Data {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 238d722d..09f72d9f 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -1,5 +1,5 @@ import Layout from "../../../components/Layout"; -import { Table, message, Form, Button, Alert, Typography } from "antd"; +import { Table, App, Form, Button, Alert, Typography } from "antd"; import { AllHygrothermalDataDocument } from "../../../queries/data.generated"; import { HygrothermalData, @@ -80,7 +80,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -150,7 +150,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.message); + message.error(error.message); } // TODO Casting to `HygrothermalData` is wrong and error prone! setData( @@ -173,7 +173,6 @@ function Page() { return ( - {contextHolder} Hygrothermal Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index a7cd664c..b48a66ff 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -1,7 +1,7 @@ import Layout from "../../../components/Layout"; import { Table, - message, + App, Form, Button, Alert, @@ -92,7 +92,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -295,7 +295,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.message); + message.error(error.message); } // TODO Casting to `OpticalData` is wrong and error prone! setData( @@ -318,7 +318,6 @@ function Page() { return ( - {contextHolder} Optical Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index 929fde56..66ee80e7 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -1,5 +1,5 @@ import Layout from "../../../components/Layout"; -import { Table, message, Form, Button, Alert, Typography } from "antd"; +import { Table, App, Form, Button, Alert, Typography } from "antd"; import { AllPhotovoltaicDataDocument } from "../../../queries/data.generated"; import { PhotovoltaicData, @@ -80,7 +80,7 @@ function Page() { const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); const [data, setData] = useState([]); // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 @@ -150,7 +150,7 @@ function Page() { if (error) { // TODO Handle properly. console.log(error); - messageApi.error(error.message); + message.error(error.message); } // TODO Casting to `PhotovoltaicData` is wrong and error prone! setData( @@ -173,7 +173,6 @@ function Page() { return ( - {contextHolder} Photovoltaic Data {/* TODO Display error messages in a list? */} {globalErrorMessages.length > 0 && ( diff --git a/frontend/pages/legal-notice.tsx b/frontend/pages/legal-notice.tsx index 2a13097a..c883d501 100644 --- a/frontend/pages/legal-notice.tsx +++ b/frontend/pages/legal-notice.tsx @@ -1,4 +1,4 @@ -import { message, Skeleton, Typography } from "antd"; +import { App, Skeleton, Typography } from "antd"; import { useEffect, useState } from "react"; import Layout from "../components/Layout"; @@ -10,6 +10,7 @@ function Page() { // Inspired by https://www.robinwieruch.de/react-hooks-fetch-data const [data, setData] = useState(null); const [loading, setLoading] = useState(false); + const { message } = App.useApp(); useEffect(() => { const fetchData = async () => { diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 6302f9b2..813c8cf6 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@apollo/client/react"; import Layout from "../components/Layout"; import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; -import { Skeleton, Result, Descriptions, Typography, message } from "antd"; +import { Skeleton, Result, Descriptions, Typography, App } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import { useEffect } from "react"; import { stringifyApolloError } from "../lib/apollo"; @@ -10,11 +10,11 @@ import paths from "../paths"; function Page() { const { loading, error, data } = useQuery(CurrentUserInfoDocument); const currentUserInfo = data?.currentUserInfo; - const [messageApi, contextHolder] = message.useMessage(); + const { message } = App.useApp(); useEffect(() => { if (error) { - messageApi.error(stringifyApolloError(error)); + message.error(stringifyApolloError(error)); } }, [error]); @@ -34,7 +34,6 @@ function Page() { return ( - {contextHolder} Date: Wed, 21 Jan 2026 13:44:01 +0100 Subject: [PATCH 637/878] Set permissions of environment files with secrets to 600 --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1d195f2a..f5510788 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,10 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. Initialize, fetch, and checkout possibly-nested submodules by running `git submodule update --init --recursive`. An alternative would have been passing `--recurse-submodules` to `git clone` above. -1. Prepare your environment by running `cp ./.env.sample ./.env`, - `cp ./frontend/.env.local.sample ./frontend/.env.local`, and adding the line - `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. +1. Prepare your environment by running `cp ./.env.sample ./.env && chmod 600 ./.env`, + `cp ./frontend/.env.local.sample ./frontend/.env.local && chmod 600 ./.env`, + and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your + `/etc/hosts` file. 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and [GNU Make](https://www.gnu.org/software/make/). 1. List all GNU Make targets by running `make help`. @@ -314,12 +315,13 @@ and the pages following it. for example, `staging.solarbuildingenvelopes.com` or `www.solarbuildingenvelopes.com` for the product-data database of the TestLab Solar Facades. - 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env`, - `cp ./frontend/.env.local.sample ./frontend/.env.local`, and by adjusting - variable values in the copies to your needs, in particular, by setting - passwords to newly generated ones, where random passwords may be - generated by running `openssl rand -base64 32`. Here is some information - on what the variables mean + 1. Prepare the environment by running + `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, + `cp ./frontend/.env.local.sample ./frontend/.env.local && chmod 600 ./.env`, + and by adjusting variable values in the copies to your needs, in + particular, by setting passwords to newly generated ones, where random + passwords may be generated by running `openssl rand -base64 32`. Here is + some information on what the variables mean - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; - `HOST` is the domain name with sub-domain of the deployment, in From 68e4542266c996ab2a4d3fc625709fd18328f5f1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 Jan 2026 17:04:42 +0100 Subject: [PATCH 638/878] Assert that all variables in `./.env.sample` are available in `./.env` before building or uping docker compose services --- Makefile | 10 +++++++++- Makefile.production | 12 ++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 1cdcad9a..989f9cf1 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,14 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name +dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./.env.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./.env | sort) \ + " +.PHONY : dotenv + # ----------------------------- # # Interface with Docker Compose # # ----------------------------- # @@ -54,7 +62,7 @@ pull : ## Pull images # To debug errors during build add `--progress plain \` to get additional # output. -build : check pull ## Build images +build : dotenv check pull ## Build images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ diff --git a/Makefile.production b/Makefile.production index a7bccd42..0fb10425 100644 --- a/Makefile.production +++ b/Makefile.production @@ -33,6 +33,14 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name +dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./.env.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./.env | sort) \ + " +.PHONY : dotenv + config : ## Parse, resolve and render compose file in canonical format COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ @@ -55,7 +63,7 @@ pull : ## Pull images ${docker_compose} pull .PHONY : pull -build : check pull ## Build images +build : dotenv check pull ## Build images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ @@ -82,7 +90,7 @@ bake : ## Print docker-compose file equivalent bake file # --no-cache .PHONY : bake -up : ## (Re)create and start containers +up : dotenv ## (Re)create and start containers ${docker_compose} up \ --remove-orphans \ --detach From 94de0f1d8cc4a538fab0bf277b75398e2b68e8cc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 Jan 2026 18:42:24 +0100 Subject: [PATCH 639/878] Compare .env with production sample --- Makefile.production | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.production b/Makefile.production index 0fb10425..0358ccc8 100644 --- a/Makefile.production +++ b/Makefile.production @@ -33,11 +33,11 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name -dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` +dotenv : ## Assert that all variables in ./.env.production.sample are available in ./.env bash -c " \ diff \ - <(cut --delimiter='=' --fields=1 ./.env.sample | sort) \ - <(cut --delimiter='=' --fields=1 ./.env | sort) \ + <(cut --delimiter='=' --fields=1 ./.env.production.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./.env | sort) \ " .PHONY : dotenv From c369d7158cd223cb42dc4e93b89452ff25d82e93 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 Jan 2026 18:53:02 +0100 Subject: [PATCH 640/878] Add frontend environment samples for production and staging --- frontend/.env.local.production.sample | 2 ++ frontend/.env.local.staging.sample | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 frontend/.env.local.production.sample create mode 100644 frontend/.env.local.staging.sample diff --git a/frontend/.env.local.production.sample b/frontend/.env.local.production.sample new file mode 100644 index 00000000..0232bdb6 --- /dev/null +++ b/frontend/.env.local.production.sample @@ -0,0 +1,2 @@ +NEXT_PUBLIC_DATABASE_URL=https://www.solarbuildingenvelopes.com +NEXT_PUBLIC_METABASE_URL=https://www.buildingenvelopedata.org diff --git a/frontend/.env.local.staging.sample b/frontend/.env.local.staging.sample new file mode 100644 index 00000000..9dc491da --- /dev/null +++ b/frontend/.env.local.staging.sample @@ -0,0 +1,2 @@ +NEXT_PUBLIC_DATABASE_URL=https://staging.solarbuildingenvelopes.com +NEXT_PUBLIC_METABASE_URL=https://staging.buildingenvelopedata.org From 8fcc52774cde0012212db373948287c3b8d4b356 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 Jan 2026 18:55:59 +0100 Subject: [PATCH 641/878] Mention environment-specific .env file in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f5510788..7ea7ae71 100644 --- a/README.md +++ b/README.md @@ -317,7 +317,7 @@ and the pages following it. TestLab Solar Facades. 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, - `cp ./frontend/.env.local.sample ./frontend/.env.local && chmod 600 ./.env`, + `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./.env`, and by adjusting variable values in the copies to your needs, in particular, by setting passwords to newly generated ones, where random passwords may be generated by running `openssl rand -base64 32`. Here is From cbdf9156b397491c1fc104ff06aa38d93b6343e9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 26 Jan 2026 19:32:47 +0100 Subject: [PATCH 642/878] Use minimal required OpenId Connect scopes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7ea7ae71..842d5200 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ and the pages following it. end session, revocation, token; - grant types: authorization code and refresh token; - response types: code; - - scopes: address, email, phone, profile, roles, read:api, write:api; + - scopes: profile, read:api, write:api, api:database:manage; - requirements: proof key for code exchange and pushed authorization requests; - post logout redirect URI: `https://${HOST}/connect/callback/logout/metabase` From c08120ca00fe4720be82ae5d9d9d61b104999a09 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 27 Jan 2026 11:19:39 +0100 Subject: [PATCH 643/878] Wait for services to be healthy when up"ing" and also when depending on containers, use double quotes around string values in YAML to have it not misinterpret special characters, and error out on missing or empty variables --- Makefile | 20 ++-- Makefile.production | 14 +-- docker-compose.production.yml | 158 +++++++++++++------------ docker-compose.yml | 212 +++++++++++++++++----------------- 4 files changed, 206 insertions(+), 198 deletions(-) diff --git a/Makefile b/Makefile index 989f9cf1..6266b941 100644 --- a/Makefile +++ b/Makefile @@ -127,7 +127,7 @@ remove-data : ## Remove data volumes up : build ## (Re)create, and start containers (after building images if necessary) ${docker_compose} up \ --remove-orphans \ - --detach + --wait .PHONY : up down : ## Stop containers and remove containers and networks created by `up` and clear backend logs @@ -158,7 +158,7 @@ logs : ## Follow logs exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ ${CONTAINER} ${docker_compose} exec \ --user $(shell id --user):$(shell id --group) \ @@ -206,7 +206,7 @@ shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in the `backend` traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ backend ${docker_compose} exec \ --privileged \ @@ -219,7 +219,7 @@ traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for examp shelln : ## Enter shell in the `nginx` container ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ nginx ${docker_compose} exec \ nginx \ @@ -229,7 +229,7 @@ shelln : ## Enter shell in the `nginx` container psql : ## Enter PostgreSQL interactive terminal in the running `database` container ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database ${docker_compose} exec \ database \ @@ -253,7 +253,7 @@ createdb : DBNAME = ${database_name} createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database ${docker_compose} exec \ database \ @@ -266,7 +266,7 @@ dropdb : DBNAME = ${database_name} dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database ${docker_compose} exec \ database \ @@ -278,7 +278,7 @@ dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` sql : ## Run the SQL script in the file `${SQL}` in the running `database` service, for example, `make SQL=./my.sql sql` ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database cat ${SQL} \ | ${docker_compose} exec \ @@ -321,7 +321,7 @@ backup : ## Backup database and related data to directory with absolute path `${ docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database .PHONY : backup @@ -352,7 +352,7 @@ restore : ## Restore database and related data from directory with absolute path docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database .PHONY : restore diff --git a/Makefile.production b/Makefile.production index 0358ccc8..06375ecb 100644 --- a/Makefile.production +++ b/Makefile.production @@ -93,7 +93,7 @@ bake : ## Print docker-compose file equivalent bake file up : dotenv ## (Re)create and start containers ${docker_compose} up \ --remove-orphans \ - --detach + --wait .PHONY : up down : ## Stop containers and remove containers, networks, volumes, and images created by `up` @@ -114,7 +114,7 @@ logs : ## Follow logs exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ ${CONTAINER} ${docker_compose} exec \ ${CONTAINER} \ @@ -179,7 +179,7 @@ createdb : ## Create database psql : ## Enter PostgreSQL interactive terminal in the running `database` service ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database ${docker_compose} exec \ database \ @@ -259,7 +259,7 @@ backup : ## Backup database and related data to directory with absolute path `${ docker container rm --volumes ${FILES_CONTAINER_NAME} ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database .PHONY : backup @@ -341,7 +341,7 @@ restore : ## Restore database and related data from directory with absolute path docker container rm --volumes ${FILES_CONTAINER_NAME} ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database .PHONY : restore @@ -417,7 +417,7 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} up \ --remove-orphans \ - --detach \ + --wait \ database .PHONY : migrate @@ -432,7 +432,7 @@ deploy-services : build ## Deploy services --force-recreate \ --renew-anon-volumes \ --remove-orphans \ - --detach + --wait .PHONY : deploy-services run-tests : COMMAND = true diff --git a/docker-compose.production.yml b/docker-compose.production.yml index f56c2c30..3c85d3ba 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,9 +1,9 @@ # Keep setup in sync with the one in ./docker-compose.yml services: database: # https://hub.docker.com/_/postgres - restart: always + restart: "always" # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` - image: postgres:13.23-trixie + image: "postgres:13.23-trixie" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -14,124 +14,130 @@ services: healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: ["CMD-SHELL", "pg_isready --username=postgres"] - interval: 5s - timeout: 5s - retries: 5 + interval: "5s" + timeout: "5s" + retries: "5" environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password - - POSTGRES_DB=postgres - - PGPASSFILE=/run/secrets/postgres_passwords + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD_FILE: "/run/secrets/postgres_password" + POSTGRES_DB: "postgres" + PGPASSFILE: "/run/secrets/postgres_passwords" volumes: - - data:/var/lib/postgresql/data - - ./secrets:/run/secrets:ro + - "data:/var/lib/postgresql/data" + - "./secrets:/run/secrets:ro" email: # https://hub.docker.com/r/rnwood/smtp4dev - restart: always - image: rnwood/smtp4dev:v3.10 + restart: "always" + image: "rnwood/smtp4dev:v3.10" # For all configuration options see # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml healthcheck: test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" volumes: - - emails:/smtp4dev + - "emails:/smtp4dev" environment: - - ServerOptions__BasePath=/email - - ServerOptions__HostName=${HOST} - - ServerOptions__Database=database.db - - ServerOptions__NumberOfMessagesToKeep=100 - - ServerOptions__NumberOfSessionsToKeep=100 - - ServerOptions__TlsMode=None - - ServerOptions__TlsCertificate= - - RelayOptions__SmtpServer=${RELAY_SMTP_HOST} - - RelayOptions__SmtpPort=${RELAY_SMTP_PORT} - - RelayOptions__AllowedEmailsString=${RELAY_ALLOWED_EMAILS} + ServerOptions__BasePath: "/email" + ServerOptions__HostName: "${HOST:?error}" + ServerOptions__Database: "database.db" + ServerOptions__NumberOfMessagesToKeep: "100" + ServerOptions__NumberOfSessionsToKeep: "100" + ServerOptions__TlsMode: "None" + ServerOptions__TlsCertificate: "" + RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?error}" + RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?error}" + RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS:?error}" backend: - restart: always + restart: "always" build: - context: ./backend - dockerfile: ./Dockerfile-production + context: "./backend" + dockerfile: "./Dockerfile-production" # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] command: ["dotnet", "/app/App.dll"] healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" environment: - - ASPNETCORE_URLS=http://0.0.0.0:8080 - - ASPNETCORE_ENVIRONMENT=production - - XBASE_Host=https://${HOST} - - XBASE_MetabaseHost=https://${METABASE_HOST} - - XBASE_DatabaseId=${DATABASE_ID} - - XBASE_OperatorId=${OPERATOR_ID} - - XBASE_OpenIdConnectClient__Id=${OPEN_ID_CONNECT_CLIENT_ID} - - XBASE_OpenIdConnectClient__Secret=${OPEN_ID_CONNECT_CLIENT_SECRET} - - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - - XBASE_Email__SmtpHost=${SMTP_HOST} - - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} - - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} + ASPNETCORE_URLS: "http://0.0.0.0:8080" + ASPNETCORE_ENVIRONMENT: "production" + XBASE_Host: "https://${HOST:?error}" + XBASE_MetabaseHost: "https://${METABASE_HOST:?error}" + XBASE_DatabaseId: "${DATABASE_ID:?error}" + XBASE_OperatorId: "${OPERATOR_ID:?error}" + XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?error}" + XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?error}" + XBASE_VerificationCode: "${VERIFICATION_CODE:?error}" + XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?error}" + XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?error}" + XBASE_Email__SmtpHost: "${SMTP_HOST:?error}" + XBASE_Email__SmtpPort: "${SMTP_PORT:?error}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?error}" + XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?error}" # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - - GPG_TTY=/dev/pts/0 + GPG_TTY: "/dev/pts/0" volumes: - - backend_logs:/app/logs - - files:/app/files - - ./secrets:/run/secrets:ro + - "backend_logs:/app/logs" + - "files:/app/files" + - "./secrets:/run/secrets:ro" # Create volume for GnuPG - - gnupg:/root/.gnupg + - "gnupg:/root/.gnupg" depends_on: - - database - - email + database: + condition: "service_healthy" + email: + condition: "service_healthy" frontend: - restart: always + restart: "always" build: - context: ./frontend - dockerfile: ./Dockerfile-production + context: "./frontend" + dockerfile: "./Dockerfile-production" command: ["yarn", "start"] healthcheck: test: ["CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" environment: - - NODE_ENV=production + NODE_ENV: "production" depends_on: - - backend + backend: + condition: "service_healthy" nginx: # https://hub.docker.com/_/nginx - restart: always - image: nginx:1.29-trixie-otel + restart: "always" + image: "nginx:1.29-trixie-otel" command: [ "nginx", "-g", "daemon off;" ] healthcheck: test: ["CMD-SHELL", "service nginx status || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" ports: - - ${HTTP_PORT}:80 + - "${HTTP_PORT:?error}:80" volumes: - - ./nginx/templates/production:/etc/nginx/templates:ro - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./nginx/html:/etc/nginx/html:ro - - files:/data/files:ro + - "./nginx/templates/production:/etc/nginx/templates:ro" + - "./nginx/nginx.conf:/etc/nginx/nginx.conf:ro" + - "./nginx/html:/etc/nginx/html:ro" + - "files:/data/files:ro" depends_on: - - frontend - - backend - - email + frontend: + condition: "service_healthy" + backend: + condition: "service_healthy" + email: + condition: "service_healthy" volumes: data: diff --git a/docker-compose.yml b/docker-compose.yml index c053f684..6d17e0d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,9 +2,9 @@ services: database: # https://hub.docker.com/_/postgres # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` - image: postgres:13.23-trixie + image: "postgres:13.23-trixie" # ports: - # - 5432:5432 + # - "5432:5432" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -19,51 +19,51 @@ services: healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: ["CMD-SHELL", "pg_isready --username=postgres"] - interval: 5s - timeout: 5s - retries: 5 + interval: "5s" + timeout: "5s" + retries: "5" environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - - POSTGRES_DB=postgres + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + POSTGRES_DB: "postgres" volumes: - - data:/var/lib/postgresql/data + - "data:/var/lib/postgresql/data" email: # https://hub.docker.com/r/rnwood/smtp4dev - image: rnwood/smtp4dev:v3.10 + image: "rnwood/smtp4dev:v3.10" # For all configuration options see # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml # ports: # Web interface - # - 4042:80 + # - "4042:80" # SMTP server - # - 25:25 + # - "25:25" # Imap server - # - 143:143 + # - "143:143" healthcheck: test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" volumes: - - emails:/smtp4dev + - "emails:/smtp4dev" environment: - - ServerOptions__BasePath=/email - - ServerOptions__HostName=${HOST} - - ServerOptions__Database=database.db - - ServerOptions__NumberOfMessagesToKeep=100 - - ServerOptions__NumberOfSessionsToKeep=100 - - ServerOptions__TlsMode=None - - ServerOptions__TlsCertificate= + ServerOptions__BasePath: "/email" + ServerOptions__HostName: "${HOST:?error}" + ServerOptions__Database: "database.db" + ServerOptions__NumberOfMessagesToKeep: "100" + ServerOptions__NumberOfSessionsToKeep: "100" + ServerOptions__TlsMode: "None" + ServerOptions__TlsCertificate: "" backend: build: - context: ./backend - dockerfile: ./Dockerfile + context: "./backend" + dockerfile: "./Dockerfile" args: - - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - - CERTIFICATE_AUTHORITY_PASSWORD=${CERTIFICATE_AUTHORITY_PASSWORD} - # user: 1000:1000 + CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?error}" + CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?error}" + # user: "1000:1000" command: [ "dotnet", "watch", "--non-interactive", @@ -77,132 +77,134 @@ services: healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" volumes: - - ./backend:/app - - files:/app/src/files - - backend_vscode_server:/home/me/.vscode-server - - backend_vscode_server_insiders:/home/me/.vscode-server-insiders + - "./backend:/app" + - "files:/app/src/files" + - "backend_vscode_server:/home/me/.vscode-server" + - "backend_vscode_server_insiders:/home/me/.vscode-server-insiders" # Create volume for GnuPG - - gnupg:/home/me/.gnupg + - "gnupg:/home/me/.gnupg" # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. - # - /app/node_modules - - /app/src/logs - - /app/src/bin - - /app/src/obj - - /app/test/bin - - /app/test/obj + # - "/app/node_modules" + - "/app/src/logs" + - "/app/src/bin" + - "/app/src/obj" + - "/app/test/bin" + - "/app/test/obj" # Create anonymous volumes for dotnet tools - - /home/me/.nuget - - /home/me/.dotnet + - "/home/me/.nuget" + - "/home/me/.dotnet" # ports: - # - 8080:8080 + # - "8080:8080" environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables - # - XBASE_DATABASE__CONNECTION_STRING="Host=database;Port=5432;Database=xbase;User Id=postgres;Password=postgres;" - - ASPNETCORE_URLS=http://0.0.0.0:8080 - - ASPNETCORE_ENVIRONMENT=development - - XBASE_Host=https://${HOST}:${HTTPS_PORT} - - XBASE_MetabaseHost=https://${METABASE_HOST}:${METABASE_HTTPS_PORT} - - XBASE_DatabaseId=${DATABASE_ID} - - XBASE_OperatorId=${OPERATOR_ID} - - XBASE_OpenIdConnectClient__Id=${OPEN_ID_CONNECT_CLIENT_ID} - - XBASE_OpenIdConnectClient__Secret=${OPEN_ID_CONNECT_CLIENT_SECRET} - - XBASE_VerificationCode=${VERIFICATION_CODE} - - XBASE_JsonWebToken__EncryptionCertificatePassword=${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} - - XBASE_JsonWebToken__SigningCertificatePassword=${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} - - XBASE_Email__SmtpHost=${SMTP_HOST} - - XBASE_Email__SmtpPort=${SMTP_PORT} - - XBASE_GnupgSecretSigningKey__Fingerprint=${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT} - - XBASE_GnupgSecretSigningKey__Passphrase=${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} + ASPNETCORE_URLS: "http://0.0.0.0:8080" + ASPNETCORE_ENVIRONMENT: "development" + XBASE_Host: "https://${HOST:?error}:${HTTPS_PORT:?error}" + XBASE_MetabaseHost: "https://${METABASE_HOST:?error}:${METABASE_HTTPS_PORT:?error}" + XBASE_DatabaseId: "${DATABASE_ID:?error}" + XBASE_OperatorId: "${OPERATOR_ID:?error}" + XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?error}" + XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?error}" + XBASE_VerificationCode: "${VERIFICATION_CODE:?error}" + XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?error}" + XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?error}" + XBASE_Email__SmtpHost: "${SMTP_HOST:?error}" + XBASE_Email__SmtpPort: "${SMTP_PORT:?error}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?error}" + XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?error}" # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables - - DOTNET_USE_POLLING_FILE_WATCHER=true + DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 - - DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS=5 + DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "5" # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose - # - ASPNETCORE_Kestrel__Certificates__Default__Password=${SSL_CERTIFICATE_PASSWORD} - # - ASPNETCORE_Kestrel__Certificates__Default__Path=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx - # - SSL_CERT_FILE=/app/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx - # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING=1 - # - ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER=all + # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING: "1" + # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER: "all" # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html - - GPG_TTY=/dev/pts/0 + GPG_TTY: "/dev/pts/0" extra_hosts: - - ${HOST}:host-gateway - - ${METABASE_HOST}:host-gateway + - "${HOST:?error}:host-gateway" + - "${METABASE_HOST:?error}:host-gateway" depends_on: - - database - - email + database: + condition: "service_healthy" + email: + condition: "service_healthy" frontend: build: - context: ./frontend - dockerfile: ./Dockerfile + context: "./frontend" + dockerfile: "./Dockerfile" args: - - CERTIFICATE_AUTHORITY_BASE_FILE_NAME=${CERTIFICATE_AUTHORITY_BASE_FILE_NAME} - # user: 1000:1000 + CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?error}" + # user: "1000:1000" # ports: - # - 8081:8080 + # - "8081:8080" command: [ "yarn", "dev" ] healthcheck: test: ["CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" volumes: - - ./frontend:/app + - "./frontend:/app" # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. - - /app/.next - - /app/__generated__ - - /app/node_modules + - "/app/.next" + - "/app/__generated__" + - "/app/node_modules" extra_hosts: - - ${HOST}:host-gateway + - "${HOST:?error}:host-gateway" environment: - - NODE_ENV=development + NODE_ENV: "development" depends_on: - - backend + backend: + condition: "service_healthy" nginx: # https://hub.docker.com/_/nginx - image: nginx:1.29-trixie-otel + image: "nginx:1.29-trixie-otel" command: [ "nginx-debug", "-g", "daemon off;" ] # run in debug mode healthcheck: test: ["CMD-SHELL", "service nginx status || exit 1"] - interval: 30s - timeout: 30s - retries: 3 + interval: "30s" + timeout: "30s" + retries: "3" volumes: - - ./ssl:/etc/nginx/ssl:ro - - ./nginx/templates/development:/etc/nginx/templates:ro - - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - - ./nginx/html:/etc/nginx/html:ro - - files:/data/files:ro + - "./ssl:/etc/nginx/ssl:ro" + - "./nginx/templates/development:/etc/nginx/templates:ro" + - "./nginx/nginx.conf:/etc/nginx/nginx.conf:ro" + - "./nginx/html:/etc/nginx/html:ro" + - "files:/data/files:ro" # - /etc/nginx/conf.d # run in read-only mode ... # - /var/cache/nginx # ... # - /var/run # ... ports: - - ${HTTP_PORT}:80 - - ${HTTPS_PORT}:443 + - "${HTTP_PORT:?error}:80" + - "${HTTPS_PORT:?error}:443" environment: - - SERVER_NAME=${HOST} - - SSL_CERTIFICATE_BASE_FILE_NAME=${SSL_CERTIFICATE_BASE_FILE_NAME} + SERVER_NAME: "${HOST:?error}" + SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?error}" depends_on: - - frontend - - backend - - email + frontend: + condition: "service_healthy" + backend: + condition: "service_healthy" + email: + condition: "service_healthy" volumes: data: From c878cae4aab189e5e5c5f1014769857f14f18e1f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 Jan 2026 17:39:22 +0100 Subject: [PATCH 644/878] Add home directory to user and also check production and staing environment and frontend environment samples for consistency --- Makefile | 17 ++++++++++++++++- Makefile.production | 17 ++++++++++++++++- backend/Dockerfile | 2 +- backend/Dockerfile-bootstrap | 2 +- 4 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 6266b941..1a3a7749 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html -include .env +include ./.env docker_compose = \ docker compose \ @@ -30,6 +30,21 @@ dotenv : ## Assert that all variables in `./.env.sample` are available in `./.en <(cut --delimiter='=' --fields=1 ./.env.sample | sort) \ <(cut --delimiter='=' --fields=1 ./.env | sort) \ " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local | sort) \ + " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./.env.production.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./.env.staging.sample | sort) \ + " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local.production.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local.staging.sample | sort) \ + " .PHONY : dotenv # ----------------------------- # diff --git a/Makefile.production b/Makefile.production index 06375ecb..7667247a 100644 --- a/Makefile.production +++ b/Makefile.production @@ -1,7 +1,7 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html -include .env +include ./.env # Use bash with pipefail to have backup and restore fail when whatever comes # before the pipe fails. The options given to bash below are described in @@ -39,6 +39,21 @@ dotenv : ## Assert that all variables in ./.env.production.sample are available <(cut --delimiter='=' --fields=1 ./.env.production.sample | sort) \ <(cut --delimiter='=' --fields=1 ./.env | sort) \ " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local.production.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local | sort) \ + " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./.env.staging.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./.env | sort) \ + " + bash -c " \ + diff \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local.staging.sample | sort) \ + <(cut --delimiter='=' --fields=1 ./frontend/.env.local | sort) \ + " .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format diff --git a/backend/Dockerfile b/backend/Dockerfile index 50453be9..a6e5e870 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -28,7 +28,7 @@ RUN \ existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me && \ + adduser --system --home /home/me --uid ${USER_ID} --ingroup us me && \ apt-get remove \ --assume-yes \ adduser && \ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index c003cdd4..8a5ee58f 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -24,7 +24,7 @@ RUN \ existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --uid ${USER_ID} --ingroup us me && \ + adduser --system --home /home/me --uid ${USER_ID} --ingroup us me && \ apt-get remove \ --assume-yes \ adduser && \ From e7a2dd7e027723b87dbb28dbe9c92252884e1bbf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 28 Jan 2026 18:10:00 +0100 Subject: [PATCH 645/878] Pass .env file explicitly to docker compose --- Makefile | 3 ++- Makefile.production | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1a3a7749..6595725d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,8 @@ include ./.env docker_compose = \ docker compose \ - --file docker-compose.yml \ + --file ./docker-compose.yml \ + --env-file ./.env \ --project-name ${NAME} database_name = xbase diff --git a/Makefile.production b/Makefile.production index 7667247a..1ffd828e 100644 --- a/Makefile.production +++ b/Makefile.production @@ -16,6 +16,7 @@ SHELL=/bin/bash -eux -o pipefail docker_compose = \ docker compose \ --file docker-compose.production.yml \ + --env-file ./.env \ --project-name ${NAME} dump_archive_name = postgresql_dumpall.gz From 3476ef9b3e77112898f06a7131ea9f4093857126 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 12:46:53 +0100 Subject: [PATCH 646/878] Use `required` as error message when variable is missing --- docker-compose.production.yml | 36 +++++++++++++------------- docker-compose.yml | 48 +++++++++++++++++------------------ 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 3c85d3ba..2ee4c28e 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -40,15 +40,15 @@ services: - "emails:/smtp4dev" environment: ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${HOST:?error}" + ServerOptions__HostName: "${HOST:?required}" ServerOptions__Database: "database.db" ServerOptions__NumberOfMessagesToKeep: "100" ServerOptions__NumberOfSessionsToKeep: "100" ServerOptions__TlsMode: "None" ServerOptions__TlsCertificate: "" - RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?error}" - RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?error}" - RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS:?error}" + RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?required}" + RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" + RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS:?required}" backend: restart: "always" @@ -67,19 +67,19 @@ services: environment: ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "production" - XBASE_Host: "https://${HOST:?error}" - XBASE_MetabaseHost: "https://${METABASE_HOST:?error}" - XBASE_DatabaseId: "${DATABASE_ID:?error}" - XBASE_OperatorId: "${OPERATOR_ID:?error}" - XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?error}" - XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?error}" - XBASE_VerificationCode: "${VERIFICATION_CODE:?error}" - XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?error}" - XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?error}" - XBASE_Email__SmtpHost: "${SMTP_HOST:?error}" - XBASE_Email__SmtpPort: "${SMTP_PORT:?error}" - XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?error}" - XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?error}" + XBASE_Host: "https://${HOST:?required}" + XBASE_MetabaseHost: "https://${METABASE_HOST:?required}" + XBASE_DatabaseId: "${DATABASE_ID:?required}" + XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" + XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" + XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" + XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" + XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" + XBASE_Email__SmtpHost: "${SMTP_HOST:?required}" + XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" + XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html GPG_TTY: "/dev/pts/0" @@ -125,7 +125,7 @@ services: timeout: "30s" retries: "3" ports: - - "${HTTP_PORT:?error}:80" + - "${HTTP_PORT:?required}:80" volumes: - "./nginx/templates/production:/etc/nginx/templates:ro" - "./nginx/nginx.conf:/etc/nginx/nginx.conf:ro" diff --git a/docker-compose.yml b/docker-compose.yml index 6d17e0d7..01dcbbc0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -49,7 +49,7 @@ services: - "emails:/smtp4dev" environment: ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${HOST:?error}" + ServerOptions__HostName: "${HOST:?required}" ServerOptions__Database: "database.db" ServerOptions__NumberOfMessagesToKeep: "100" ServerOptions__NumberOfSessionsToKeep: "100" @@ -61,8 +61,8 @@ services: context: "./backend" dockerfile: "./Dockerfile" args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?error}" - CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?error}" + CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" + CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?required}" # user: "1000:1000" command: [ "dotnet", "watch", @@ -106,19 +106,19 @@ services: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "development" - XBASE_Host: "https://${HOST:?error}:${HTTPS_PORT:?error}" - XBASE_MetabaseHost: "https://${METABASE_HOST:?error}:${METABASE_HTTPS_PORT:?error}" - XBASE_DatabaseId: "${DATABASE_ID:?error}" - XBASE_OperatorId: "${OPERATOR_ID:?error}" - XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?error}" - XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?error}" - XBASE_VerificationCode: "${VERIFICATION_CODE:?error}" - XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?error}" - XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?error}" - XBASE_Email__SmtpHost: "${SMTP_HOST:?error}" - XBASE_Email__SmtpPort: "${SMTP_PORT:?error}" - XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?error}" - XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?error}" + XBASE_Host: "https://${HOST:?required}:${HTTPS_PORT:?required}" + XBASE_MetabaseHost: "https://${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" + XBASE_DatabaseId: "${DATABASE_ID:?required}" + XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" + XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" + XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" + XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" + XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" + XBASE_Email__SmtpHost: "${SMTP_HOST:?required}" + XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" + XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 @@ -130,8 +130,8 @@ services: # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html GPG_TTY: "/dev/pts/0" extra_hosts: - - "${HOST:?error}:host-gateway" - - "${METABASE_HOST:?error}:host-gateway" + - "${HOST:?required}:host-gateway" + - "${METABASE_HOST:?required}:host-gateway" depends_on: database: condition: "service_healthy" @@ -143,7 +143,7 @@ services: context: "./frontend" dockerfile: "./Dockerfile" args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?error}" + CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" # user: "1000:1000" # ports: # - "8081:8080" @@ -165,7 +165,7 @@ services: - "/app/__generated__" - "/app/node_modules" extra_hosts: - - "${HOST:?error}:host-gateway" + - "${HOST:?required}:host-gateway" environment: NODE_ENV: "development" depends_on: @@ -193,11 +193,11 @@ services: # - /var/cache/nginx # ... # - /var/run # ... ports: - - "${HTTP_PORT:?error}:80" - - "${HTTPS_PORT:?error}:443" + - "${HTTP_PORT:?required}:80" + - "${HTTPS_PORT:?required}:443" environment: - SERVER_NAME: "${HOST:?error}" - SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?error}" + SERVER_NAME: "${HOST:?required}" + SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?required}" depends_on: frontend: condition: "service_healthy" From 8c88770985076f265256456b8eabc58d1ec402e1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 16:45:48 +0100 Subject: [PATCH 647/878] Use unix end of lines --- .../20250530151605_ModelReferenceProperly.Designer.cs | 2 +- backend/src/Migrations/20250530151605_ModelReferenceProperly.cs | 2 +- ...530155318_AddRequiredPropertyExistsToPublication.Designer.cs | 2 +- .../20250530155318_AddRequiredPropertyExistsToPublication.cs | 2 +- .../20250725172127_UpgradeOpenIddictToVersion7.Designer.cs | 2 +- .../Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs | 2 +- ...0731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs | 2 +- .../20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs | 2 +- .../20250812171810_AddQueryVariablesToApprovals.Designer.cs | 2 +- .../Migrations/20250812171810_AddQueryVariablesToApprovals.cs | 2 +- ...22837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs | 2 +- ...20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs | 2 +- .../20250907130041_AddApproverIdToResponseApproval.Designer.cs | 2 +- .../20250907130041_AddApproverIdToResponseApproval.cs | 2 +- .../20251010164110_UpdateCoatedSideEnumeration.Designer.cs | 2 +- .../Migrations/20251010164110_UpdateCoatedSideEnumeration.cs | 2 +- .../20251023152609_RememberUserWhoCreatedDataSet.Designer.cs | 2 +- .../Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs | 2 +- ...1024124734_AddObserverAndIlluminantToCielabColor.Designer.cs | 2 +- .../20251024124734_AddObserverAndIlluminantToCielabColor.cs | 2 +- .../20251030170839_AddPublishingStateToDataSets.Designer.cs | 2 +- .../Migrations/20251030170839_AddPublishingStateToDataSets.cs | 2 +- .../20251031125736_AddFileExtensionToResource.Designer.cs | 2 +- .../src/Migrations/20251031125736_AddFileExtensionToResource.cs | 2 +- .../20251113172901_CheckResourcesForConsistency.Designer.cs | 2 +- .../Migrations/20251113172901_CheckResourcesForConsistency.cs | 2 +- ...251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs | 2 +- .../20251125181102_MirrorWidthAndHeightOfGeometricData.cs | 2 +- backend/src/Migrations/ApplicationDbContextModelSnapshot.cs | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs index 3ab43180..8ac56ea6 100644 --- a/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs index abf707d4..c7469b44 100644 --- a/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs +++ b/backend/src/Migrations/20250530151605_ModelReferenceProperly.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs index 48ed88ae..774d025a 100644 --- a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs index ddd221bb..80bcf2ca 100644 --- a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs index 36c74d78..7d9164ea 100644 --- a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs index 3f7955d8..5788109f 100644 --- a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs index 0b2457f4..0a3267c8 100644 --- a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs index 015656ef..9116a8be 100644 --- a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs index b4475460..f47fe2ff 100644 --- a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs index 64b87b19..fb40b8b9 100644 --- a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs index 2a5fa3f7..be0870f9 100644 --- a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs index 3bb8a4ca..c6104729 100644 --- a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs index 5822cb4d..9bb42b37 100644 --- a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs +++ b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs index e8736e79..e1daa378 100644 --- a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs +++ b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs index 0d52f368..a914cfe7 100644 --- a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs +++ b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs index 313f4c49..ffa54ac3 100644 --- a/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs +++ b/backend/src/Migrations/20251010164110_UpdateCoatedSideEnumeration.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs index c649a375..1a1e99e5 100644 --- a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs +++ b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs index af86e744..293c4fdd 100644 --- a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs +++ b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs index 24e8dacd..71088e51 100644 --- a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs +++ b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs index 49590553..e83a625f 100644 --- a/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs +++ b/backend/src/Migrations/20251024124734_AddObserverAndIlluminantToCielabColor.cs @@ -1,4 +1,4 @@ -using System.Text.Json; +using System.Text.Json; using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs index 08762d26..734a9fed 100644 --- a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs +++ b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs index a09f678b..837bfa25 100644 --- a/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs +++ b/backend/src/Migrations/20251030170839_AddPublishingStateToDataSets.cs @@ -1,4 +1,4 @@ -using Database.Enumerations; +using Database.Enumerations; using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs index 70f2055b..f3bffeae 100644 --- a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs +++ b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs index c16db1ba..ae7ba745 100644 --- a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs +++ b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs index c4006733..22f4aeef 100644 --- a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs +++ b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs index 0da87b49..686dfb3b 100644 --- a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs +++ b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.Json; using Microsoft.EntityFrameworkCore.Migrations; diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs index f7528f17..d3d91d7f 100644 --- a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs index 38f1bbff..090d05ed 100644 --- a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 04b42a39..9acae4c4 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Collections.Generic; using System.Text.Json; From 99f8c2d1aee19d711967d277d9b7c8e84b16abee Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 17:26:32 +0100 Subject: [PATCH 648/878] Lint and fix docker (compose) files and run scripts in bash strict mode --- .dclintrc | 14 +++ .hadolint.yml | 4 + Dockerfile-show-build-context | 4 +- Makefile | 53 +++++++- Makefile.production | 4 + backend/Dockerfile | 77 +++++++----- backend/Dockerfile-bootstrap | 48 ++++--- backend/Dockerfile-production | 16 ++- backend/Makefile | 6 + docker-compose.production.yml | 139 ++++++++++----------- docker-compose.yml | 220 +++++++++++++++++---------------- frontend/Dockerfile | 17 ++- frontend/Dockerfile-production | 13 +- frontend/Makefile | 4 + 14 files changed, 375 insertions(+), 244 deletions(-) create mode 100644 .dclintrc create mode 100644 .hadolint.yml diff --git a/.dclintrc b/.dclintrc new file mode 100644 index 00000000..6739024d --- /dev/null +++ b/.dclintrc @@ -0,0 +1,14 @@ +{ + "$schema": "https://raw.githubusercontent.com/zavoloklom/docker-compose-linter/refs/heads/main/schemas/linter-config.schema.json", + "rules": { + "services-alphabetical-order": 0, + "require-quotes-in-ports": [ + 1, + { + "quoteType": "double" + } + ] + }, + "quiet": false, + "debug": true +} diff --git a/.hadolint.yml b/.hadolint.yml new file mode 100644 index 00000000..75ba64b6 --- /dev/null +++ b/.hadolint.yml @@ -0,0 +1,4 @@ +ignored: + - DL3008 + +strict-labels: true diff --git a/Dockerfile-show-build-context b/Dockerfile-show-build-context index e4babe29..ffe73e3f 100644 --- a/Dockerfile-show-build-context +++ b/Dockerfile-show-build-context @@ -1,10 +1,10 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile -FROM busybox +FROM busybox:1.37 RUN mkdir /tmp/build/ COPY . /tmp/build/ diff --git a/Makefile b/Makefile index 6595725d..12a93092 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ include ./.env +SHELL := /bin/bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + docker_compose = \ docker compose \ --file ./docker-compose.yml \ @@ -265,6 +269,49 @@ list : ## List all containers with health status --all .PHONY : list +dclint = \ + docker run \ + --rm \ + --tty \ + --volume .:/app \ + zavoloklom/dclint \ + --config /app/.dclintrc + +hadolint = \ + docker run \ + --rm \ + --interactive \ + --volume ./.hadolint.yml:/.config/.hadolint.yaml \ + hadolint/hadolint \ + hadolint \ + --config /.config/.hadolint.yaml + +# docker run \ +# --workdir / \ +# --volume ./checkmake.ini:/checkmake.ini \ +# --volume ./Makefile:/Makefile \ +# --volume ./Makefile.production:/Makefile.production \ +# --volume ./backend/Makefile:/Makefile.backend \ +# --volume ./frontend/Makefile:/Makefile.frontend \ +# quay.io/checkmake/checkmake \ +# /Makefile \ +# /Makefile.production \ +# /Makefile.backend \ +# /Makefile.frontend +lint : ## Lint Docker Compose files and Dockerfiles + ${dclint} . + ${hadolint} - < ./backend/Dockerfile + ${hadolint} - < ./backend/Dockerfile-bootstrap + ${hadolint} - < ./backend/Dockerfile-production + ${hadolint} - < ./Dockerfile-show-build-context + ${hadolint} - < ./frontend/Dockerfile + ${hadolint} - < ./frontend/Dockerfile-production +.PHONY : lint + +fix : ## Fix Docker Compoe linting violations + ${dclint} --fix . +.PHONY : fix + createdb : DBNAME = ${database_name} createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} up \ @@ -443,9 +490,9 @@ jwt-certificates : build-bootstrap ## Create JWT encryption and signing certific # For an introduction to how HTTPS works see https://howhttps.works ssl : ## Generate and trust certificate authority, and generate SSL certificates - make generate-certificate-authority - make generate-ssl-certificate - make trust-certificate-authority + $(MAKE) generate-certificate-authority + $(MAKE) generate-ssl-certificate + $(MAKE) trust-certificate-authority .PHONY : ssl # Creating Self-Signed ECDSA SSL Certificate using OpenSSL: http://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/ diff --git a/Makefile.production b/Makefile.production index 1ffd828e..4829b237 100644 --- a/Makefile.production +++ b/Makefile.production @@ -3,6 +3,10 @@ include ./.env +SHELL := /bin/bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + # Use bash with pipefail to have backup and restore fail when whatever comes # before the pipe fails. The options given to bash below are described in # https://explainshell.com/explain?cmd=set+-eux+-o+pipefail diff --git a/backend/Dockerfile b/backend/Dockerfile index a6e5e870..33852c35 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true;skip=SecretsUsedInArgOrEnv # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md @@ -9,26 +9,45 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] ARG GROUP_ID ARG USER_ID ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME ARG CERTIFICATE_AUTHORITY_PASSWORD +RUN \ + if [ -z "$GROUP_ID" ]; then echo "required 'GROUP_ID'"; exit 1; fi && \ + if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi && \ + if [ -z "$CERTIFICATE_AUTHORITY_BASE_FILE_NAME" ]; then echo "required 'CERTIFICATE_AUTHORITY_BASE_FILE_NAME'"; exit 1; fi && \ + if [ -z "$CERTIFICATE_AUTHORITY_PASSWORD" ]; then echo "required 'CERTIFICATE_AUTHORITY_PASSWORD'"; exit 1; fi + # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) -# id --user --name ${USER_ID} 2>/dev/null +# id --user --name "${USER_ID}" 2>/dev/null RUN \ apt-get update && \ apt-get install \ --assume-yes \ --no-install-recommends \ adduser && \ - existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ - existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ - addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --home /home/me --uid ${USER_ID} --ingroup us me && \ + existing_user_name="$( (getent passwd ${USER_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_user_name}"; then \ + deluser "${existing_user_name}"; \ + fi && \ + existing_group_name="$( (getent group ${GROUP_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_group_name}"; then \ + delgroup "${existing_group_name}"; \ + fi && \ + addgroup \ + --system \ + --gid "${GROUP_ID}" \ + us && \ + adduser \ + --system \ + --home /home/me \ + --uid "${USER_ID}" \ + --ingroup us \ + me && \ apt-get remove \ --assume-yes \ adduser && \ @@ -99,7 +118,7 @@ RUN \ # See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 # and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 COPY \ - ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" \ /usr/local/share/ca-certificates RUN \ update-ca-certificates @@ -110,10 +129,11 @@ RUN \ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.5.16-1 +ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 -RUN set -eux && \ - version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ +# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. +RUN \ + version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ apt-get update && \ apt-get install \ --assume-yes \ @@ -123,7 +143,7 @@ RUN set -eux && \ curl \ --silent \ --show-error \ - https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ fingerprint=$(gpg \ @@ -143,7 +163,7 @@ RUN set -eux && \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2=${GNUPG_VERSION} && \ + gnupg2="${GNUPG_VERSION}" && \ rm \ --recursive \ --force \ @@ -151,14 +171,14 @@ RUN set -eux && \ ENV HOME=/home/me RUN \ - mkdir --parents ${HOME}/app && \ + mkdir --parents "${HOME}/app" && \ chown \ me:us \ - ${HOME} && \ + "${HOME}" && \ chown \ me:us \ - ${HOME}/app && \ - ln --symbolic ${HOME}/app /app + "${HOME}/app" && \ + ln --symbolic "${HOME}/app" /app ########### # As `me` # @@ -345,19 +365,20 @@ RUN \ mkdir ./ssl COPY \ --chown=me:us \ - ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ + "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ ./ssl/ RUN \ dotnet tool run \ certificate-tool add \ - --file ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ + --file "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ --store-name Root \ - --password ${CERTIFICATE_AUTHORITY_PASSWORD} + --password "${CERTIFICATE_AUTHORITY_PASSWORD}" # bash parameter completion for the dotnet CLI # see https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash # for the here-document syntax used to append to the ~/.bashrc file # see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 +# hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. RUN \ cat <<-"EOT" >> ${HOME}/.bashrc function _dotnet_bash_complete() @@ -392,17 +413,17 @@ RUN \ ./test/bin \ ./test/obj && \ mkdir --parents \ - ${HOME}/.vscode-server \ - ${HOME}/.vscode-server-insiders && \ + "${HOME}/.vscode-server" \ + "${HOME}/.vscode-server-insiders" && \ mkdir --parents \ - ${HOME}/.nuget \ - ${HOME}/.dotnet && \ + "${HOME}/.nuget" \ + "${HOME}/.dotnet" && \ mkdir --parents \ - ${HOME}/.gnupg && \ + "${HOME}/.gnupg" && \ chmod u=rwx,g=,o= \ - ${HOME}/.gnupg && \ + "${HOME}/.gnupg" && \ echo "allow-loopback-pinentry" \ - > ${HOME}/.gnupg/gpg-agent.conf + > "${HOME}/.gnupg/gpg-agent.conf" ENV SHELL=/bin/bash diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 8a5ee58f..492e4cb2 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md @@ -7,10 +7,15 @@ # https://hub.docker.com/_/microsoft-dotnet-sdk # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] ARG GROUP_ID ARG USER_ID +RUN \ + if [ -z "$GROUP_ID" ]; then echo "required 'GROUP_ID'"; exit 1; fi && \ + if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi + # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) # id --user --name ${USER_ID} 2>/dev/null RUN \ @@ -19,12 +24,24 @@ RUN \ --assume-yes \ --no-install-recommends \ adduser && \ - existing_user_name="$(getent passwd ${USER_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_user_name}"; then deluser "${existing_user_name}"; fi && \ - existing_group_name="$(getent group ${GROUP_ID} 2>/dev/null | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_group_name}"; then delgroup "${existing_group_name}"; fi && \ - addgroup --system --gid ${GROUP_ID} us && \ - adduser --system --home /home/me --uid ${USER_ID} --ingroup us me && \ + existing_user_name="$( (getent passwd ${USER_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_user_name}"; then \ + deluser "${existing_user_name}"; \ + fi && \ + existing_group_name="$( (getent group ${GROUP_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ + if test -n "${existing_group_name}"; then \ + delgroup "${existing_group_name}"; \ + fi && \ + addgroup \ + --system \ + --gid "${GROUP_ID}" \ + us && \ + adduser \ + --system \ + --home /home/me \ + --uid "${USER_ID}" \ + --ingroup us \ + me && \ apt-get remove \ --assume-yes \ adduser && \ @@ -39,10 +56,11 @@ RUN \ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.5.16-1 +ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 -RUN set -eux && \ - version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ +# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. +RUN \ + version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ apt-get update && \ apt-get install \ --assume-yes \ @@ -52,7 +70,7 @@ RUN set -eux && \ curl \ --silent \ --show-error \ - https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ fingerprint=$(gpg \ @@ -72,7 +90,7 @@ RUN set -eux && \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2=${GNUPG_VERSION} && \ + gnupg2="${GNUPG_VERSION}" && \ rm \ --recursive \ --force \ @@ -80,16 +98,16 @@ RUN set -eux && \ ENV HOME=/home/me RUN \ - mkdir --parents ${HOME} && \ + mkdir --parents "${HOME}" && \ chown \ me:us \ - ${HOME} + "${HOME}" USER me WORKDIR /app RUN \ - mkdir --parents ${HOME}/.dotnet + mkdir --parents "${HOME}/.dotnet" ENV PATH=$PATH:/home/me/.dotnet/tools RUN \ diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index e1a02b23..411997d6 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md @@ -17,6 +17,7 @@ # https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags # https://github.com/dotnet/dotnet-docker/blob/main/README.sdk.md FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 AS build +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] # RUN \ # apt-get update && \ @@ -42,6 +43,7 @@ RUN \ # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish#options # We cannot use this option though to specify a custom folder as explained on # https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid +# hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. RUN \ dotnet publish \ --configuration release \ @@ -56,6 +58,7 @@ RUN \ # https://mcr.microsoft.com/artifact/mar/dotnet/aspnet/tags # https://github.com/dotnet/dotnet-docker/blob/main/README.aspnet.md FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-amd64 +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] RUN \ apt-get update && \ @@ -75,10 +78,11 @@ RUN \ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the # `local.yml` Ansible script. -ENV GNUPG_VERSION=2.5.16-1 +ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 -RUN set -eux && \ - version_codename=$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME') && \ +# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. +RUN \ + version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ apt-get update && \ apt-get install \ --assume-yes \ @@ -88,7 +92,7 @@ RUN set -eux && \ curl \ --silent \ --show-error \ - https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ | gpg --dearmor \ > /usr/share/keyrings/gnupg-keyring.gpg && \ fingerprint=$(gpg \ @@ -108,7 +112,7 @@ RUN set -eux && \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2=${GNUPG_VERSION} && \ + gnupg2="${GNUPG_VERSION}" && \ rm \ --recursive \ --force \ diff --git a/backend/Makefile b/backend/Makefile index c058f049..3973cf5d 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,6 +1,10 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html +SHELL := /bin/bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -119,6 +123,7 @@ dos2unix : ## Strip the byte-order mark, also known as, BOM, and remove carriage # See https://www.jetbrains.com/help/resharper/Code_Cleanup__Index.html#profiles format : ## Format code dotnet format ./Database.sln + $(MAKE) dos2unix .PHONY : format update-packages : ## Update packages @@ -212,6 +217,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot --output $${rollback_path} && \ sed -i -e "$(shell printf '1s/^\357\273\277//')" -e "s/\r//" $${migrate_path} $${rollback_path} && \ cat $${migrate_path} >> ./src/Migrations/migrate.sql && \ + $(MAKE) dos2unix echo "Review the generated C# and SQL files and make necessary adjusments consistenty across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." .PHONY : migration diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 2ee4c28e..40327e5d 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -1,43 +1,35 @@ # Keep setup in sync with the one in ./docker-compose.yml +name: "${NAME}" + services: - database: # https://hub.docker.com/_/postgres - restart: "always" + database: # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` image: "postgres:13.23-trixie" + volumes: + - data:/var/lib/postgresql/data + - ./secrets:/run/secrets:ro + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD_FILE: "/run/secrets/postgres_password" + POSTGRES_DB: "postgres" + PGPASSFILE: "/run/secrets/postgres_passwords" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see # https://www.postgresql.org/docs/current/runtime-config.html - command: [ - "-c", "max_connections=100" - ] + command: [ "-c", "max_connections=100" ] + restart: "always" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: ["CMD-SHELL", "pg_isready --username=postgres"] + test: [ "CMD-SHELL", "pg_isready --username=postgres" ] interval: "5s" timeout: "5s" retries: "5" - environment: - POSTGRES_USER: "postgres" - POSTGRES_PASSWORD_FILE: "/run/secrets/postgres_password" - POSTGRES_DB: "postgres" - PGPASSFILE: "/run/secrets/postgres_passwords" - volumes: - - "data:/var/lib/postgresql/data" - - "./secrets:/run/secrets:ro" - email: # https://hub.docker.com/r/rnwood/smtp4dev - restart: "always" + email: image: "rnwood/smtp4dev:v3.10" - # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml - healthcheck: - test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1"] - interval: "30s" - timeout: "30s" - retries: "3" volumes: - - "emails:/smtp4dev" + - emails:/smtp4dev environment: ServerOptions__BasePath: "/email" ServerOptions__HostName: "${HOST:?required}" @@ -49,21 +41,30 @@ services: RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?required}" RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS:?required}" - - backend: restart: "always" - build: - context: "./backend" - dockerfile: "./Dockerfile-production" - # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line - # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] - command: ["dotnet", "/app/App.dll"] + # For all configuration options see + # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml healthcheck: - # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example - test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] + test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] interval: "30s" timeout: "30s" retries: "3" + + backend: + build: + context: "./backend" + dockerfile: "./Dockerfile-production" + depends_on: + database: + condition: "service_healthy" + email: + condition: "service_healthy" + volumes: + - backend_logs:/app/logs + - files:/app/files + - ./secrets:/run/secrets:ro + # Create volume for GnuPG + - gnupg:/root/.gnupg environment: ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "production" @@ -83,61 +84,57 @@ services: # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html GPG_TTY: "/dev/pts/0" - volumes: - - "backend_logs:/app/logs" - - "files:/app/files" - - "./secrets:/run/secrets:ro" - # Create volume for GnuPG - - "gnupg:/root/.gnupg" - depends_on: - database: - condition: "service_healthy" - email: - condition: "service_healthy" - - frontend: + # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line + # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] + command: [ "dotnet", "/app/App.dll" ] restart: "always" - build: - context: "./frontend" - dockerfile: "./Dockerfile-production" - command: ["yarn", "start"] healthcheck: - test: ["CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1"] + # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example + test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] interval: "30s" timeout: "30s" retries: "3" - environment: - NODE_ENV: "production" + + frontend: + build: + context: "./frontend" + dockerfile: "./Dockerfile-production" depends_on: backend: condition: "service_healthy" - - nginx: # https://hub.docker.com/_/nginx + environment: + NODE_ENV: "production" + command: [ "yarn", "start" ] restart: "always" - image: "nginx:1.29-trixie-otel" - command: [ - "nginx", - "-g", "daemon off;" - ] healthcheck: - test: ["CMD-SHELL", "service nginx status || exit 1"] + test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] interval: "30s" timeout: "30s" retries: "3" - ports: - - "${HTTP_PORT:?required}:80" - volumes: - - "./nginx/templates/production:/etc/nginx/templates:ro" - - "./nginx/nginx.conf:/etc/nginx/nginx.conf:ro" - - "./nginx/html:/etc/nginx/html:ro" - - "files:/data/files:ro" + + nginx: + image: "nginx:1.29-trixie-otel" depends_on: - frontend: - condition: "service_healthy" backend: condition: "service_healthy" email: condition: "service_healthy" + frontend: + condition: "service_healthy" + volumes: + - ./nginx/templates/production:/etc/nginx/templates:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/html:/etc/nginx/html:ro + - files:/data/files:ro + ports: + - "127.0.0.1:${HTTP_PORT:?required}:80" + command: [ "nginx", "-g", "daemon off;" ] + restart: "always" + healthcheck: + test: [ "CMD-SHELL", "service nginx status || exit 1" ] + interval: "30s" + timeout: "30s" + retries: "3" volumes: data: diff --git a/docker-compose.yml b/docker-compose.yml index 01dcbbc0..e2d459e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,52 +1,41 @@ # Keep setup in sync with the one in ./docker-compose.production.yml +name: "${NAME}" + services: - database: # https://hub.docker.com/_/postgres - # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` + database: image: "postgres:13.23-trixie" + volumes: + - data:/var/lib/postgresql/data + environment: + POSTGRES_USER: "postgres" + POSTGRES_PASSWORD: "postgres" + POSTGRES_DB: "postgres" # ports: - # - "5432:5432" + # - "127.0.0.1:5432:5432" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see # https://www.postgresql.org/docs/current/runtime-config.html # For an introduction to logging see # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ - command: [ - "-c", "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" - ] + command: + [ + "-c", + "max_connections=100" + # "-c", "log_statement=all", + # "-c", "log_duration=1" + ] healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: ["CMD-SHELL", "pg_isready --username=postgres"] + test: [ "CMD-SHELL", "pg_isready --username=postgres" ] interval: "5s" timeout: "5s" retries: "5" - environment: - POSTGRES_USER: "postgres" - POSTGRES_PASSWORD: "postgres" - POSTGRES_DB: "postgres" - volumes: - - "data:/var/lib/postgresql/data" - email: # https://hub.docker.com/r/rnwood/smtp4dev + email: image: "rnwood/smtp4dev:v3.10" - # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml - # ports: - # Web interface - # - "4042:80" - # SMTP server - # - "25:25" - # Imap server - # - "143:143" - healthcheck: - test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1"] - interval: "30s" - timeout: "30s" - retries: "3" volumes: - - "emails:/smtp4dev" + - emails:/smtp4dev environment: ServerOptions__BasePath: "/email" ServerOptions__HostName: "${HOST:?required}" @@ -55,6 +44,20 @@ services: ServerOptions__NumberOfSessionsToKeep: "100" ServerOptions__TlsMode: "None" ServerOptions__TlsCertificate: "" + # For all configuration options see + # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml + # ports: + # Web interface + # - "127.0.0.1:4042:80" + # SMTP server + # - "127.0.0.1:25:25" + # Imap server + # - "127.0.0.1:143:143" + healthcheck: + test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] + interval: "30s" + timeout: "30s" + retries: "3" backend: build: @@ -63,45 +66,33 @@ services: args: CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?required}" - # user: "1000:1000" - command: [ - "dotnet", "watch", - "--non-interactive", - "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx - "--project", "/app/src/Database.csproj", - "--", - "run", - "--no-launch-profile", - "--project", "/app/src/Database.csproj" - ] - healthcheck: - # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example - test: ["CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1"] - interval: "30s" - timeout: "30s" - retries: "3" + depends_on: + database: + condition: "service_healthy" + email: + condition: "service_healthy" volumes: - - "./backend:/app" - - "files:/app/src/files" - - "backend_vscode_server:/home/me/.vscode-server" - - "backend_vscode_server_insiders:/home/me/.vscode-server-insiders" + - ./backend:/app + - files:/app/src/files + - backend_vscode_server:/home/me/.vscode-server + - backend_vscode_server_insiders:/home/me/.vscode-server-insiders # Create volume for GnuPG - - "gnupg:/home/me/.gnupg" + - gnupg:/home/me/.gnupg # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. # - "/app/node_modules" - - "/app/src/logs" - - "/app/src/bin" - - "/app/src/obj" - - "/app/test/bin" - - "/app/test/obj" + - /app/src/logs + - /app/src/bin + - /app/src/obj + - /app/test/bin + - /app/test/obj # Create anonymous volumes for dotnet tools - - "/home/me/.nuget" - - "/home/me/.dotnet" + - /home/me/.nuget + - /home/me/.dotnet # ports: - # - "8080:8080" + # - "127.0.0.1:8080:8080" environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" @@ -132,11 +123,27 @@ services: extra_hosts: - "${HOST:?required}:host-gateway" - "${METABASE_HOST:?required}:host-gateway" - depends_on: - database: - condition: "service_healthy" - email: - condition: "service_healthy" + # user: "1000:1000" + command: + [ + "dotnet", + "watch", + "--non-interactive", + "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx + "--project", + "/app/src/Database.csproj", + "--", + "run", + "--no-launch-profile", + "--project", + "/app/src/Database.csproj" + ] + healthcheck: + # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example + test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] + interval: "30s" + timeout: "30s" + retries: "3" frontend: build: @@ -144,67 +151,62 @@ services: dockerfile: "./Dockerfile" args: CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" - # user: "1000:1000" - # ports: - # - "8081:8080" - command: [ - "yarn", "dev" - ] - healthcheck: - test: ["CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1"] - interval: "30s" - timeout: "30s" - retries: "3" + depends_on: + backend: + condition: "service_healthy" volumes: - - "./frontend:/app" + - ./frontend:/app # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file # system that was bound by the `/app` binding. - - "/app/.next" - - "/app/__generated__" - - "/app/node_modules" - extra_hosts: - - "${HOST:?required}:host-gateway" + - /app/.next + - /app/__generated__ + - /app/node_modules environment: NODE_ENV: "development" - depends_on: - backend: - condition: "service_healthy" - - nginx: # https://hub.docker.com/_/nginx - image: "nginx:1.29-trixie-otel" - command: [ - "nginx-debug", - "-g", "daemon off;" - ] # run in debug mode + extra_hosts: + - "${HOST:?required}:host-gateway" + # user: "1000:1000" + # ports: + # - "127.0.0.1:8081:8080" + command: [ "yarn", "dev" ] healthcheck: - test: ["CMD-SHELL", "service nginx status || exit 1"] + test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] interval: "30s" timeout: "30s" retries: "3" + + nginx: + image: "nginx:1.29-trixie-otel" + depends_on: + backend: + condition: "service_healthy" + email: + condition: "service_healthy" + frontend: + condition: "service_healthy" volumes: - - "./ssl:/etc/nginx/ssl:ro" - - "./nginx/templates/development:/etc/nginx/templates:ro" - - "./nginx/nginx.conf:/etc/nginx/nginx.conf:ro" - - "./nginx/html:/etc/nginx/html:ro" - - "files:/data/files:ro" + - ./ssl:/etc/nginx/ssl:ro + - ./nginx/templates/development:/etc/nginx/templates:ro + - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ./nginx/html:/etc/nginx/html:ro + - files:/data/files:ro # - /etc/nginx/conf.d # run in read-only mode ... # - /var/cache/nginx # ... # - /var/run # ... - ports: - - "${HTTP_PORT:?required}:80" - - "${HTTPS_PORT:?required}:443" environment: SERVER_NAME: "${HOST:?required}" SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?required}" - depends_on: - frontend: - condition: "service_healthy" - backend: - condition: "service_healthy" - email: - condition: "service_healthy" + ports: + - "127.0.0.1:${HTTP_PORT:?required}:80" + - "127.0.0.1:${HTTPS_PORT:?required}:443" + command: [ "nginx-debug", "-g", "daemon off;" ] # run in debug mode + healthcheck: + test: [ "CMD-SHELL", "service nginx status || exit 1" ] + interval: "30s" + timeout: "30s" + retries: "3" volumes: data: diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 1069bec2..43276972 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md @@ -8,9 +8,13 @@ # https://hub.docker.com/_/node FROM node:24.11-trixie-slim +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME +RUN \ + if [ -z "$CERTIFICATE_AUTHORITY_BASE_FILE_NAME" ]; then echo "required 'CERTIFICATE_AUTHORITY_BASE_FILE_NAME'"; exit 1; fi + # TODO directory node_modules is created on host machine when container is run # and does not belong to current user. Avoid it being created at all or at # least make it belong to the current user @@ -39,18 +43,18 @@ RUN \ /var/lib/apt/lists/* COPY \ - ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" \ /usr/local/share/ca-certificates RUN \ update-ca-certificates ENV HOME=/home/node RUN \ - mkdir --parents ${HOME}/app && \ + mkdir --parents "${HOME}/app" && \ chown \ node:node \ - ${HOME}/app && \ - ln --symbolic ${HOME}/app /app + "${HOME}/app" && \ + ln --symbolic "${HOME}/app" /app ############# # As `node` # @@ -67,7 +71,8 @@ COPY \ ./yarn.lock ./ RUN \ yarn install \ - --pure-lockfile + --pure-lockfile && \ + yarn cache clean # Create empty directories for user `me` and group `us` to make the respective # mounted volumes be owned by the user `me` and the group `us`. diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 952231d4..2f894591 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.17 +# syntax=docker/dockerfile:1.21 # check=error=true # The first line fixes the Dockerfile frontend used by BuildKit. For details # see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md @@ -7,6 +7,7 @@ # Inspired by https://nextjs.org/docs/deployment#docker-image FROM node:24.11-trixie-slim AS dependencies +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -19,6 +20,7 @@ COPY ./package.json ./yarn.lock ./ RUN yarn install --pure-lockfile FROM node:24.11-trixie-slim AS build +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] RUN \ apt-get update && \ apt-get upgrade --assume-yes && \ @@ -32,6 +34,7 @@ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build FROM node:24.11-trixie-slim +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] WORKDIR /app ENV NODE_ENV=production COPY --from=build /app/.env.local ./ @@ -52,7 +55,9 @@ RUN \ --force \ /var/lib/apt/lists/* RUN npx --no-install next telemetry disable -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 --ingroup nodejs nextjs -RUN chown --recursive nextjs:nodejs /app/.next +# hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. +RUN \ + addgroup --system --gid 1001 nodejs \ + adduser --system --uid 1001 --ingroup nodejs nextjs \ + chown --recursive nextjs:nodejs /app/.next USER nextjs diff --git a/frontend/Makefile b/frontend/Makefile index 985fe8d9..cfad0137 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -1,6 +1,10 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html +SHELL := /bin/bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ From 85cc40404da59f978be6c61e3caf117f30826a55 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 17:26:58 +0100 Subject: [PATCH 649/878] Format code with `dotnet format` --- .../ApiRequests/IsGnuPgFingerprintValid.cs | 4 ++-- backend/src/ApiRequests/QueryDatabase.cs | 2 +- backend/src/ApiRequests/UpdateDatabase.cs | 4 ++-- ...phQlAuthenticationAndAntiforgeryHandler.cs | 2 +- .../src/Configuration/AuthConfiguration.cs | 6 ++--- .../src/Configuration/GraphQlConfiguration.cs | 20 ++++++++--------- .../Controllers/AuthenticationController.cs | 4 ++-- .../src/Controllers/FileUploadController.cs | 4 ++-- backend/src/Data/CalorimetricData.cs | 2 +- backend/src/Data/CielabColor.cs | 2 +- backend/src/Data/ColorRenderingIndex.cs | 2 +- .../src/Data/CrossDatabaseDataReference.cs | 2 +- backend/src/Data/DataApproval.cs | 2 +- backend/src/Data/DataX.cs | 2 +- backend/src/Data/GeometricData.cs | 2 +- backend/src/Data/HygrothermalData.cs | 2 +- backend/src/Data/IApproval.cs | 4 ++-- backend/src/Data/IData.cs | 2 +- backend/src/Data/OpticalData.cs | 2 +- backend/src/Data/PhotovoltaicData.cs | 2 +- backend/src/Data/Reference.cs | 2 +- backend/src/Data/ResponseApproval.cs | 2 +- .../CielabColorResultsJsonExtractor.cs | 4 ++-- .../src/Extractors/ClassJsonPathExtractor.cs | 4 ++-- ...ColorRenderingIndexResultsJsonExtractor.cs | 2 +- .../DoubleResultsOpticalDataJsonExtractor.cs | 2 +- .../src/Extractors/StructJsonPathExtractor.cs | 2 +- .../AddInstitutionAccessRightsMutation.cs | 6 ++--- .../UpdateDataAccessRightsMutation.cs | 9 ++++---- .../UpdateInstitutionAccessRightsMutation.cs | 6 ++--- .../CalorimetricDataQueries.cs | 4 ++-- .../CalorimetricDataX/CalorimetricDataType.cs | 2 +- .../CreateCalorimetricDataMutation.cs | 14 ++++++------ .../Common/OpenEndedDateTimeRangeType.cs | 2 +- backend/src/GraphQl/Connection.cs | 2 +- .../CrossDatabaseDataReferenceInput.cs | 2 +- .../DataApprovals/AddDataApprovalMutation.cs | 10 ++++----- .../GraphQl/DataApprovals/DataApprovalType.cs | 2 +- .../RemoveDataApprovalMutation.cs | 6 ++--- backend/src/GraphQl/DataMutationBase.cs | 14 ++++++------ .../GraphQl/DataX/CreateDataMutationBase.cs | 12 +++++----- backend/src/GraphQl/DataX/DataQueriesBase.cs | 12 +++++----- backend/src/GraphQl/DataX/DataResolvers.cs | 12 +++++----- backend/src/GraphQl/DataX/DataType.cs | 2 +- backend/src/GraphQl/DataX/DataTypeBase.cs | 4 ++-- .../src/GraphQl/DataX/DeleteDataMutation.cs | 6 ++--- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 4 ++-- .../GetHttpsResourceTreeNonRootVertex.cs | 3 ++- ...ttpsResourceTreeNonRootVertexFilterType.cs | 2 +- ...tHttpsResourceTreeNonRootVertexSortType.cs | 2 +- .../src/GraphQl/DataX/PublishDataMutation.cs | 4 ++-- .../src/GraphQl/DataX/RetractDataMutation.cs | 2 +- .../src/GraphQl/DataX/UpdateDataMutation.cs | 6 ++--- .../src/GraphQl/Databases/DatabaseQueries.cs | 4 ++-- .../Databases/UpdateDatabaseMutation.cs | 4 ++-- .../Extensions/ResolverContextExtensions.cs | 2 +- .../Extensions/SortingContextExtensions.cs | 2 +- backend/src/GraphQl/ForkingConnection.cs | 2 +- .../CreateGeometricDataMutation.cs | 14 ++++++------ .../GeometricDataX/GeometricDataQueries.cs | 4 ++-- .../GeometricDataX/GeometricDataType.cs | 2 +- .../CreateGetHttpsResourceMutation.cs | 14 ++++++------ .../DeleteGetHttpsResourceMutation.cs | 10 ++++----- .../GetHttpsResourceFilterType.cs | 4 ++-- .../GetHttpsResourceQueries.cs | 10 ++++----- .../GetHttpsResourceResolvers.cs | 4 ++-- ...mputeGetHttpsResourceHashValuesMutation.cs | 22 +++++++++---------- .../SetGetHttpsResourceParentMutation.cs | 14 ++++++------ .../UpdateChildGetHttpsResourceMutation.cs | 14 ++++++------ .../UpdateRootGetHttpsResourceMutation.cs | 14 ++++++------ .../CreateHygrothermalDataMutation.cs | 14 ++++++------ .../HygrothermalDataQueries.cs | 2 +- .../HygrothermalDataX/HygrothermalDataType.cs | 2 +- backend/src/GraphQl/Methods/MethodQueries.cs | 4 ++-- .../OpticalDataX/CreateOpticalDataMutation.cs | 16 +++++++------- .../OpticalDataX/OpticalDataQueries.cs | 8 +++---- .../GraphQl/OpticalDataX/OpticalDataType.cs | 2 +- .../CreatePhotovoltaicDataMutation.cs | 14 ++++++------ .../PhotovoltaicDataQueries.cs | 4 ++-- .../PhotovoltaicDataX/PhotovoltaicDataType.cs | 2 +- .../Publications/PublicationFilterType.cs | 2 +- .../Publications/PublicationSortType.cs | 2 +- .../GraphQl/References/ReferenceFilterType.cs | 2 +- .../GraphQl/References/ReferenceSortType.cs | 2 +- .../CreateResponseApprovalsMutation.cs | 10 ++++----- .../UpdateResponseApprovalsMutation.cs | 12 +++++----- .../20211019125407_InitialCreate.cs | 2 +- .../20230126153325_RenameXminToVersion.cs | 2 +- .../20230210135413_AddDataProtectionKeys.cs | 2 +- .../Migrations/20230420143838_OpenIddict.cs | 2 +- .../20230427133946_CreateUserTable.cs | 2 +- .../20240522120225_UpgradeOpenIddict.cs | 2 +- ...240709144935_AddStatementToDataApproval.cs | 2 +- ...722133619_MoveEnumTypesToMetabaseSchema.cs | 2 +- .../20240917073732_AddGeometricData.cs | 2 +- .../20250218172859_MakeDescriptionOptional.cs | 2 +- .../20250331141152_AddAccessRights.cs | 2 +- .../20250527144412_AddDataAccessRights.cs | 2 +- ...50527150325_MoveInstitutionAccessRights.cs | 2 +- ..._AddRequiredPropertyExistsToPublication.cs | 2 +- ...50725172127_UpgradeOpenIddictToVersion7.cs | 2 +- ...6_MakeInstitutionIdOfAccessRightsUnique.cs | 2 +- ...0812171810_AddQueryVariablesToApprovals.cs | 2 +- ...ameResponsePropertyOfApprovalsToMessage.cs | 2 +- ...7130041_AddApproverIdToResponseApproval.cs | 2 +- ...023152609_RememberUserWhoCreatedDataSet.cs | 2 +- ...251031125736_AddFileExtensionToResource.cs | 2 +- ...1113172901_CheckResourcesForConsistency.cs | 2 +- ...102_MirrorWidthAndHeightOfGeometricData.cs | 2 +- backend/src/Program.cs | 2 +- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/DataApprovalService.cs | 2 +- .../src/Services/ResponseApprovalService.cs | 14 ++++++------ backend/src/Services/UserService.cs | 6 ++--- backend/src/Startup.cs | 4 ++-- .../CustomWebApplicationFactory.cs | 4 ++-- backend/test/Integration/IntegrationTests.cs | 6 ++--- .../Methods/SpectralToIntegralMethodTests.cs | 18 +++++++-------- 118 files changed, 291 insertions(+), 291 deletions(-) diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index 4e477c60..caf4eb9d 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -1,11 +1,11 @@ -using NodaTime; using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.Services; using GraphQL; using Microsoft.AspNetCore.Http; -using Database.Services; +using NodaTime; namespace Database.ApiRequests; diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index a0dc3f45..a6c11448 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -1,8 +1,8 @@ using System; using System.Threading; using System.Threading.Tasks; -using GraphQL; using Database.Services; +using GraphQL; namespace Database.ApiRequests; diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 06a3ed64..1382f6b3 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -1,10 +1,10 @@ +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using GraphQL; using Database.Services; -using System; +using GraphQL; namespace Database.ApiRequests; diff --git a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs index 050e6954..56364faa 100644 --- a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -2,8 +2,8 @@ using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; -using HotChocolate; using Database.GraphQl; +using HotChocolate; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 285ed6a0..62578848 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,8 +1,10 @@ using System; using System.IO; -using System.Reflection; using System.Net.Http; +using System.Reflection; using System.Security.Cryptography.X509Certificates; +using Database.Authentication; +using Database.Authorization; using Database.Data; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -10,8 +12,6 @@ using OpenIddict.Abstractions; using OpenIddict.Client; using Quartz; -using Database.Authorization; -using Database.Authentication; namespace Database.Configuration; diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 624ea056..a2ad7f93 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,23 +1,23 @@ using System; using System.Text.RegularExpressions; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using Database.Authentication; +using Database.Data; +using Database.GraphQl; +using Database.GraphQl.DataX; +using HotChocolate.AspNetCore; using HotChocolate.Configuration; using HotChocolate.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; -using HotChocolate.Types.NodaTime; using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl; -using Database.GraphQl.DataX; +using HotChocolate.Types.NodaTime; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using NodaTime; -using Database.Authentication; -using HotChocolate.AspNetCore; namespace Database.Configuration; diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 5bb1ef7e..b33e59b7 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -5,17 +5,17 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; +using Database.Authentication; using Database.Data; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; using static OpenIddict.Abstractions.OpenIddictConstants; -using Microsoft.AspNetCore.Routing; -using Database.Authentication; namespace Database.Controllers; diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 95150d3c..7ddbd030 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -10,14 +10,14 @@ using Database.Filters; using Database.Services; using Database.Utilities; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.WebUtilities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; namespace Database.Controllers; diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 5c3af10f..eb118c32 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,9 +1,9 @@ -using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Extractors; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/CielabColor.cs b/backend/src/Data/CielabColor.cs index aab0ae35..0790b6c1 100644 --- a/backend/src/Data/CielabColor.cs +++ b/backend/src/Data/CielabColor.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using Microsoft.EntityFrameworkCore; using Database.Enumerations; using Database.Json; +using Microsoft.EntityFrameworkCore; namespace Database.Data; diff --git a/backend/src/Data/ColorRenderingIndex.cs b/backend/src/Data/ColorRenderingIndex.cs index 066f095e..cc47d0dd 100644 --- a/backend/src/Data/ColorRenderingIndex.cs +++ b/backend/src/Data/ColorRenderingIndex.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; -using Microsoft.EntityFrameworkCore; using Database.Enumerations; using Database.Json; +using Microsoft.EntityFrameworkCore; namespace Database.Data; diff --git a/backend/src/Data/CrossDatabaseDataReference.cs b/backend/src/Data/CrossDatabaseDataReference.cs index 28243f7e..c0399f43 100644 --- a/backend/src/Data/CrossDatabaseDataReference.cs +++ b/backend/src/Data/CrossDatabaseDataReference.cs @@ -1,7 +1,7 @@ -using NodaTime; using System; using Database.Enumerations; using Microsoft.EntityFrameworkCore; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index e1e6da8d..8eebb3cd 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,7 +1,7 @@ -using NodaTime; using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 6289763c..eddc6d40 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,9 +1,9 @@ -using NodaTime; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Database.Enumerations; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 44803846..5be4f13c 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,9 +1,9 @@ -using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Extractors; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 12e5b97a..3d33a023 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,8 +1,8 @@ -using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index cff9dc13..e2dd197f 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -1,9 +1,9 @@ -using NodaTime; using System; using System.Text.Json; using System.Text.Json.Serialization; -using HotChocolate.Types; using Database.GraphQl; +using HotChocolate.Types; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 31b39f03..d9989a55 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,10 +1,10 @@ -using NodaTime; using System; using System.Collections.Generic; using System.Text.Json.Serialization; using System.Threading.Tasks; using Database.Enumerations; using Database.GraphQl; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 55678f93..7f0173c0 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,4 +1,3 @@ -using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; @@ -6,6 +5,7 @@ using Database.Enumerations; using Database.Enumerations.DataPoints; using Database.Extractors; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 950df818..41d15961 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,8 +1,8 @@ -using NodaTime; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Data/Reference.cs b/backend/src/Data/Reference.cs index a56ca3c5..065baac0 100644 --- a/backend/src/Data/Reference.cs +++ b/backend/src/Data/Reference.cs @@ -1,8 +1,8 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; -using Microsoft.EntityFrameworkCore; using Database.Json; +using Microsoft.EntityFrameworkCore; namespace Database.Data; diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 019bc2c7..2390b901 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,7 +1,7 @@ -using NodaTime; using System; using System.Text.Json; using Microsoft.EntityFrameworkCore; +using NodaTime; namespace Database.Data; diff --git a/backend/src/Extractors/CielabColorResultsJsonExtractor.cs b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs index 83409e43..12fec5ba 100644 --- a/backend/src/Extractors/CielabColorResultsJsonExtractor.cs +++ b/backend/src/Extractors/CielabColorResultsJsonExtractor.cs @@ -1,8 +1,8 @@ -using Json.Path; +using System.Text.Json; using Database.Data; using Database.Enumerations.DataPoints; -using System.Text.Json; using Database.Json; +using Json.Path; namespace Database.Extractors; diff --git a/backend/src/Extractors/ClassJsonPathExtractor.cs b/backend/src/Extractors/ClassJsonPathExtractor.cs index 39eae141..bdd36446 100644 --- a/backend/src/Extractors/ClassJsonPathExtractor.cs +++ b/backend/src/Extractors/ClassJsonPathExtractor.cs @@ -1,11 +1,11 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; +using Database.Extensions; using Json.More; using Json.Path; -using Database.Extensions; -using System.Collections.Generic; namespace Database.Extractors; diff --git a/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs index adf983f7..5dfc3844 100644 --- a/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs +++ b/backend/src/Extractors/ColorRenderingIndexResultsJsonExtractor.cs @@ -1,5 +1,5 @@ -using Json.Path; using Database.Enumerations.DataPoints; +using Json.Path; namespace Database.Extractors; diff --git a/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs index ace3d211..f6b33220 100644 --- a/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs +++ b/backend/src/Extractors/DoubleResultsOpticalDataJsonExtractor.cs @@ -1,5 +1,5 @@ -using Json.Path; using Database.Enumerations.DataPoints; +using Json.Path; namespace Database.Extractors; diff --git a/backend/src/Extractors/StructJsonPathExtractor.cs b/backend/src/Extractors/StructJsonPathExtractor.cs index d1d0ba23..2dfbd086 100644 --- a/backend/src/Extractors/StructJsonPathExtractor.cs +++ b/backend/src/Extractors/StructJsonPathExtractor.cs @@ -2,9 +2,9 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; +using Database.Extensions; using Json.More; using Json.Path; -using Database.Extensions; namespace Database.Extractors; diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs index 18b8b90c..1ebadb17 100644 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs @@ -1,14 +1,14 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.Extensions; using Database.Services; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.Extensions; using NodaTime; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index a0f8f94f..b085b483 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -1,15 +1,14 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.Enumerations; +using Database.Extensions; using Database.Services; using HotChocolate.Types; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using Database.Extensions; - -using Database.Enumerations; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs index 68a8a002..3d4aa0f3 100644 --- a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs @@ -1,14 +1,14 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.Extensions; using Database.Services; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.Extensions; using NodaTime; namespace Database.GraphQl.AccessRights; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index e4ddda3b..1e4e0755 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -2,9 +2,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; -using Database.GraphQl.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -12,7 +13,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.Authorization; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs index edd6754a..6951cdf9 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs @@ -1,6 +1,6 @@ using Database.Data; -using HotChocolate.Types; using Database.GraphQl.DataX; +using HotChocolate.Types; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index be567d74..93001f41 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -1,17 +1,17 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; +using Database.ApiRequests; using Database.Authorization; using Database.Data; -using Database.Services; -using HotChocolate; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Database.Extensions; using Database.GraphQl.DataX; -using Database.ApiRequests; +using Database.Services; +using HotChocolate; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.CalorimetricDataX; diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs index 7febcbe5..c0989874 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -1,5 +1,5 @@ -using NodaTime; using HotChocolate.Types; +using NodaTime; using NpgsqlTypes; namespace Database.GraphQl.Common; diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index f48817de..595ae887 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -4,9 +4,9 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; using GreenDonut.Data; -using Database.Data; namespace Database.GraphQl; diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs index 64f58761..59f47b16 100644 --- a/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceInput.cs @@ -1,7 +1,7 @@ -using NodaTime; using System; using Database.Data; using Database.Enumerations; +using NodaTime; namespace Database.GraphQl; diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 5f9a5aa7..2ab5953e 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -1,6 +1,6 @@ -using NodaTime; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http; using System.Text.Json; @@ -8,7 +8,10 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.Enumerations; +using Database.Extensions; using Database.GraphQl.Publications; +using Database.GraphQl.References; using Database.GraphQl.Standards; using Database.Json; using Database.Services; @@ -16,10 +19,7 @@ using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; using Microsoft.Extensions.Logging; -using System.Diagnostics.CodeAnalysis; -using Database.Enumerations; -using Database.GraphQl.References; -using Database.Extensions; +using NodaTime; namespace Database.GraphQl.DataApprovals; diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs index 097bed90..9d306bbf 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs @@ -1,6 +1,6 @@ -using HotChocolate.Types; using Database.Data; using Database.GraphQl.References; +using HotChocolate.Types; namespace Database.GraphQl.DataApprovals; diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs index 8a66999d..abe9a716 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.Enumerations; +using Database.Extensions; using Database.Services; using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; -using System.Diagnostics.CodeAnalysis; -using Database.Enumerations; -using Database.Extensions; namespace Database.GraphQl.DataApprovals; diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index ba5e0fa3..e1382891 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -1,17 +1,17 @@ using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using GraphQL.Client.Abstractions.Utilities; +using Database.ApiRequests; using Database.Data; using Database.Enumerations; -using Database.Services; -using System.Collections.Generic; -using static Database.ApiRequests.DataFormatDataLoader; -using Database.ApiRequests; using Database.Extensions; -using System.Linq; -using System.Globalization; +using Database.Services; +using GraphQL.Client.Abstractions.Utilities; using HotChocolate; +using static Database.ApiRequests.DataFormatDataLoader; namespace Database.GraphQl; diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 27987001..07d6edcd 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -1,15 +1,15 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Globalization; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; using Database.ApiRequests; -using GraphQL.Client.Abstractions.Utilities; using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using NodaTime; using static Database.ApiRequests.DataFormatDataLoader; -using System.Linq; -using System.Globalization; -using System.Collections.Immutable; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 96006485..08887c8e 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -2,16 +2,16 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Authorization; +using Database.Data; +using Database.GraphQl.Entities; +using Database.GraphQl.Extensions; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; -using Database.Data; -using Database.GraphQl.Extensions; -using Database.Services; -using Database.GraphQl.Entities; -using Database.Authorization; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 0196e891..68ab7997 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,16 +1,16 @@ -using NodaTime; using System; using System.Threading; using System.Threading.Tasks; -using HotChocolate; using Database.Data; +using Database.Extensions; +using Database.GraphQl.Extensions; +using Database.GraphQl.GetHttpsResources; +using GreenDonut; using GreenDonut.Data; +using HotChocolate; using HotChocolate.Data; -using GreenDonut; -using Database.GraphQl.GetHttpsResources; using HotChocolate.Resolvers; -using Database.GraphQl.Extensions; -using Database.Extensions; +using NodaTime; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 321f2536..0af49f42 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,5 +1,5 @@ -using HotChocolate.Types; using Database.Data; +using HotChocolate.Types; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index f7711e56..4c8373ef 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -1,7 +1,7 @@ -using HotChocolate.Types; -using GreenDonut; using System; using Database.Data; +using GreenDonut; +using HotChocolate.Types; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 5ab4239a..6eb50647 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; -using System.Linq; +using HotChocolate.Types; using Microsoft.AspNetCore.Mvc; -using System.IO; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 13a23a55..5751c639 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -2,12 +2,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; +using Database.GraphQl.Extensions; using GreenDonut; using GreenDonut.Data; -using Database.Data; using HotChocolate.Data; using HotChocolate.Resolvers; -using Database.GraphQl.Extensions; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs index 56403530..97b038d9 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertex.cs @@ -20,7 +20,8 @@ GetHttpsResource value public ToTreeVertexAppliedConversionMethod AppliedConversionMethod { get; } = value.AppliedConversionMethod ?? throw new InvalidOperationException("Each non-root vertex has an applied conversion method."); - [GraphQLType>] public string VertexId => + [GraphQLType>] + public string VertexId => GetHttpsResource.ConstructVertexId(Value.Id); public GetHttpsResource Value { get; } = value; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index 81b5ac54..d6f3d3dd 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -1,6 +1,6 @@ -using HotChocolate.Data.Filters; using Database.Data; using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Filters; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs index 1b775d90..55c525c2 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexSortType.cs @@ -1,6 +1,6 @@ -using HotChocolate.Data.Sorting; using Database.Data; using Database.GraphQl.GetHttpsResources; +using HotChocolate.Data.Sorting; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index 736916c9..6991c912 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; using Database.Services; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using System.Linq; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs index 0ea18077..5201745b 100644 --- a/backend/src/GraphQl/DataX/RetractDataMutation.cs +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -3,12 +3,12 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; using Database.Services; +using HotChocolate.Types; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index 4d69330a..f9f5ae1b 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -1,16 +1,16 @@ -using NodaTime; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate; -using HotChocolate.Types; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; using Database.Services; +using HotChocolate; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.DataX; diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 40962cd8..3f473cf3 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,10 +1,10 @@ using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.ApiRequests; -using Database.Services; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index a34ba85b..93246e56 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -1,9 +1,9 @@ using System.Threading; using System.Threading.Tasks; -using HotChocolate.Resolvers; -using HotChocolate.Types; using Database.ApiRequests; using Database.Services; +using HotChocolate.Resolvers; +using HotChocolate.Types; using static Database.ApiRequests.UpdateDatabase; namespace Database.GraphQl.Databases; diff --git a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs index aecc462e..9c677115 100644 --- a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs +++ b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs @@ -20,4 +20,4 @@ public static QueryContext GetQueryContext(this IResolverContext context) filterContext?.AsPredicate(), sortContext?.AsSortDefinition()); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs index 2c12031c..65b43071 100644 --- a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs +++ b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs @@ -1,7 +1,7 @@ using System.Linq; -using HotChocolate.Data.Sorting; using Database.Data; using GreenDonut.Data; +using HotChocolate.Data.Sorting; namespace Database.GraphQl.Extensions; diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs index ec8bd7cf..4007eee7 100644 --- a/backend/src/GraphQl/ForkingConnection.cs +++ b/backend/src/GraphQl/ForkingConnection.cs @@ -3,9 +3,9 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Database.Data; using GreenDonut; using GreenDonut.Data; -using Database.Data; namespace Database.GraphQl; diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index ce222d80..685795f7 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -1,19 +1,19 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; +using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Extensions; +using Database.GraphQl.DataX; using Database.Services; using Database.Utilities; -using System.Diagnostics.CodeAnalysis; using HotChocolate; -using System.Collections.Generic; -using Database.GraphQl.DataX; -using Database.Extensions; -using Database.ApiRequests; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 8f82808c..882d0e74 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -2,9 +2,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; -using Database.GraphQl.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -12,7 +13,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.Authorization; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs index 3329969e..978f7481 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs @@ -1,6 +1,6 @@ using Database.Data; -using HotChocolate.Types; using Database.GraphQl.DataX; +using HotChocolate.Types; namespace Database.GraphQl.GeometricDataX; diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index d3ce497c..52e9aeeb 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -1,20 +1,20 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Database.ApiRequests; using Database.Authorization; +using Database.Data; using Database.Enumerations; -using HotChocolate.Types; +using Database.Extensions; using Database.Services; using Database.Utilities; -using GreenDonut.Data; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.Extensions; using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.ApiRequests; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs index 7c9d29d5..55d2088d 100644 --- a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -1,16 +1,16 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; using Database.Authorization; +using Database.Data; using Database.Enumerations; -using HotChocolate.Types; -using Database.Services; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; using Database.Extensions; +using Database.Services; using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index c511e2e6..2d367a62 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,6 +1,6 @@ -using HotChocolate.Data.Filters; -using Database.GraphQl.Entities; using Database.Data; +using Database.GraphQl.Entities; +using HotChocolate.Data.Filters; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 3d5c048c..bbf8910b 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -2,14 +2,14 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; +using Database.Authorization; +using Database.Data; +using Database.Enumerations; +using Database.GraphQl.Extensions; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.Extensions; -using Database.Enumerations; -using Database.Authorization; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 20b9df77..fd5c290e 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,13 +1,13 @@ using System; using System.Threading; using System.Threading.Tasks; -using HotChocolate; using Database.Data; using Database.GraphQl.CalorimetricDataX; +using Database.GraphQl.GeometricDataX; using Database.GraphQl.HygrothermalDataX; using Database.GraphQl.OpticalDataX; -using Database.GraphQl.GeometricDataX; using Database.GraphQl.PhotovoltaicDataX; +using HotChocolate; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 1b433e2c..00e5503a 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -1,22 +1,22 @@ using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; using Database.Authorization; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; +using Database.Data; +using Database.Extensions; +using Database.GraphQl.Extensions; using Database.Services; -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; -using HotChocolate.Data; using GreenDonut.Data; -using Database.GraphQl.Extensions; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.Extensions; -using HotChocolate.Resolvers; +using HotChocolate.Data; using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs index bbfc7aec..6b3847e0 100644 --- a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -1,18 +1,18 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Database.ApiRequests; using Database.Authorization; -using HotChocolate.Types; -using Database.Services; -using GreenDonut.Data; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; +using Database.Data; using Database.Extensions; +using Database.Services; using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.ApiRequests; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index ba74b9f9..5d2430c4 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -1,18 +1,18 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Database.ApiRequests; using Database.Authorization; -using HotChocolate.Types; -using Database.Services; -using GreenDonut.Data; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; +using Database.Data; using Database.Extensions; +using Database.Services; using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.ApiRequests; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index 9d4771ce..40eebbdf 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -1,18 +1,18 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Data; +using Database.ApiRequests; using Database.Authorization; -using HotChocolate.Types; -using Database.Services; -using GreenDonut.Data; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; +using Database.Data; using Database.Extensions; +using Database.Services; using GraphQL.Client.Abstractions.Utilities; +using GreenDonut.Data; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.ApiRequests; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 43c9d748..34e75a48 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -1,17 +1,17 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; +using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Extensions; +using Database.GraphQl.DataX; using Database.Services; using HotChocolate; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.GraphQl.DataX; -using Database.Extensions; -using Database.ApiRequests; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.HygrothermalDataX; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index afa98581..1067c670 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; using Database.Services; @@ -10,7 +11,6 @@ using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Authorization; namespace Database.GraphQl.HygrothermalDataX; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs index b6b3375a..78a411af 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs @@ -1,6 +1,6 @@ using Database.Data; -using HotChocolate.Types; using Database.GraphQl.DataX; +using HotChocolate.Types; namespace Database.GraphQl.HygrothermalDataX; diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index ce21d262..f21cc328 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -11,8 +13,6 @@ using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; namespace Database.GraphQl.Methods; diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index da78cd07..a1e9af41 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -1,19 +1,19 @@ -using NodaTime; using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Collections.Generic; -using HotChocolate.Types; +using Database.ApiRequests; using Database.Authorization; -using Database.Extensions; using Database.Data; -using Database.Services; +using Database.Enumerations; +using Database.Extensions; using Database.GraphQl.DataX; +using Database.Services; using HotChocolate; -using Database.Enumerations; -using System.Linq; -using Database.ApiRequests; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index d2a49032..035a71fc 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -2,15 +2,15 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.GraphQl.DataX; +using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Database.Data; -using Database.GraphQl.DataX; -using Database.Services; -using Database.Authorization; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs index c2f6d962..b1e97cf3 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs @@ -1,6 +1,6 @@ -using HotChocolate.Types; using Database.Data; using Database.GraphQl.DataX; +using HotChocolate.Types; namespace Database.GraphQl.OpticalDataX; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 75f890a9..7b97bdaf 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -1,19 +1,19 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; using System.Threading.Tasks; -using HotChocolate.Types; +using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Extensions; +using Database.GraphQl.DataX; using Database.Services; using Database.Utilities; using HotChocolate; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; -using Database.Extensions; -using Database.GraphQl.DataX; -using Database.ApiRequests; +using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.PhotovoltaicDataX; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index e5028377..acb59d8e 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -2,9 +2,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; -using Database.GraphQl.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; @@ -12,7 +13,6 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; -using Database.Authorization; namespace Database.GraphQl.PhotovoltaicDataX; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs index 40676b56..2a445324 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs @@ -1,6 +1,6 @@ using Database.Data; -using HotChocolate.Types; using Database.GraphQl.DataX; +using HotChocolate.Types; namespace Database.GraphQl.PhotovoltaicDataX; diff --git a/backend/src/GraphQl/Publications/PublicationFilterType.cs b/backend/src/GraphQl/Publications/PublicationFilterType.cs index e1ac3ce0..3829100f 100644 --- a/backend/src/GraphQl/Publications/PublicationFilterType.cs +++ b/backend/src/GraphQl/Publications/PublicationFilterType.cs @@ -1,5 +1,5 @@ -using HotChocolate.Data.Filters; using Database.Data; +using HotChocolate.Data.Filters; namespace Database.GraphQl.Publications; diff --git a/backend/src/GraphQl/Publications/PublicationSortType.cs b/backend/src/GraphQl/Publications/PublicationSortType.cs index dcf57125..4a27f84f 100644 --- a/backend/src/GraphQl/Publications/PublicationSortType.cs +++ b/backend/src/GraphQl/Publications/PublicationSortType.cs @@ -1,5 +1,5 @@ -using HotChocolate.Data.Sorting; using Database.Data; +using HotChocolate.Data.Sorting; namespace Database.GraphQl.Publications; diff --git a/backend/src/GraphQl/References/ReferenceFilterType.cs b/backend/src/GraphQl/References/ReferenceFilterType.cs index 6512a5f5..f047a823 100644 --- a/backend/src/GraphQl/References/ReferenceFilterType.cs +++ b/backend/src/GraphQl/References/ReferenceFilterType.cs @@ -1,5 +1,5 @@ -using HotChocolate.Data.Filters; using Database.Data; +using HotChocolate.Data.Filters; namespace Database.GraphQl.References; diff --git a/backend/src/GraphQl/References/ReferenceSortType.cs b/backend/src/GraphQl/References/ReferenceSortType.cs index 3cce721b..8413910f 100644 --- a/backend/src/GraphQl/References/ReferenceSortType.cs +++ b/backend/src/GraphQl/References/ReferenceSortType.cs @@ -1,5 +1,5 @@ -using HotChocolate.Data.Sorting; using Database.Data; +using HotChocolate.Data.Sorting; namespace Database.GraphQl.References; diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index 69ec8d0a..09643128 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -5,17 +5,17 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using GreenDonut.Data; using Database.Authorization; using Database.Data; +using Database.Extensions; +using Database.GraphQl.Extensions; using Database.Services; +using GreenDonut.Data; using HotChocolate.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.Extensions.Logging; -using Database.Extensions; -using Database.GraphQl.Extensions; -using HotChocolate.Resolvers; -using HotChocolate.Data.Filters; namespace Database.GraphQl.ResponseApprovals; diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 0ef866f5..8903d6fa 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -4,17 +4,17 @@ using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; -using GreenDonut.Data; using Database.Authorization; using Database.Data; -using Database.Services; -using HotChocolate.Types; -using HotChocolate.Data; -using Microsoft.Extensions.Logging; using Database.Extensions; using Database.GraphQl.Extensions; -using HotChocolate.Resolvers; +using Database.Services; +using GreenDonut.Data; +using HotChocolate.Data; using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.Extensions.Logging; namespace Database.GraphQl.ResponseApprovals; diff --git a/backend/src/Migrations/20211019125407_InitialCreate.cs b/backend/src/Migrations/20211019125407_InitialCreate.cs index c253233b..6dd968b9 100644 --- a/backend/src/Migrations/20211019125407_InitialCreate.cs +++ b/backend/src/Migrations/20211019125407_InitialCreate.cs @@ -658,4 +658,4 @@ protected override void Down(MigrationBuilder migrationBuilder) .OldAnnotation("Npgsql:Enum:public.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs index 0217bd84..2878e2ea 100644 --- a/backend/src/Migrations/20230126153325_RenameXminToVersion.cs +++ b/backend/src/Migrations/20230126153325_RenameXminToVersion.cs @@ -231,4 +231,4 @@ protected override void Down(MigrationBuilder migrationBuilder) oldClrType: typeof(DateTime), oldType: "timestamp with time zone"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs index 9cf47b0d..1acd1293 100644 --- a/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs +++ b/backend/src/Migrations/20230210135413_AddDataProtectionKeys.cs @@ -34,4 +34,4 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "DataProtectionKeys", schema: "database"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20230420143838_OpenIddict.cs b/backend/src/Migrations/20230420143838_OpenIddict.cs index afafaa93..11b89873 100644 --- a/backend/src/Migrations/20230420143838_OpenIddict.cs +++ b/backend/src/Migrations/20230420143838_OpenIddict.cs @@ -178,4 +178,4 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "OpenIddictApplications", schema: "database"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20230427133946_CreateUserTable.cs b/backend/src/Migrations/20230427133946_CreateUserTable.cs index 1b78e1fd..3c50ce01 100644 --- a/backend/src/Migrations/20230427133946_CreateUserTable.cs +++ b/backend/src/Migrations/20230427133946_CreateUserTable.cs @@ -35,4 +35,4 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "user", schema: "database"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs index 085d01f8..a09ad391 100644 --- a/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs +++ b/backend/src/Migrations/20240522120225_UpgradeOpenIddict.cs @@ -76,4 +76,4 @@ protected override void Down(MigrationBuilder migrationBuilder) nullable: false, defaultValue: ""); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs index 1d5c2644..5b36e5d4 100644 --- a/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs +++ b/backend/src/Migrations/20240709144935_AddStatementToDataApproval.cs @@ -845,4 +845,4 @@ protected override void Down(MigrationBuilder migrationBuilder) .OldAnnotation("Npgsql:Enum:public.standardizer", "aerc,agi,ashrae,breeam,bs,bsi,cen,cie,dgnb,din,dvwg,iec,ies,ift,iso,jis,leed,nfrc,riba,ul,unece,vdi,vff,well") .OldAnnotation("Npgsql:PostgresExtension:pgcrypto", ",,"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs index 55125a47..b344458c 100644 --- a/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs +++ b/backend/src/Migrations/20240722133619_MoveEnumTypesToMetabaseSchema.cs @@ -183,4 +183,4 @@ protected override void Down(MigrationBuilder migrationBuilder) oldType: "database.standardizer[]", oldNullable: true); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20240917073732_AddGeometricData.cs b/backend/src/Migrations/20240917073732_AddGeometricData.cs index 2126f6af..bfb91261 100644 --- a/backend/src/Migrations/20240917073732_AddGeometricData.cs +++ b/backend/src/Migrations/20240917073732_AddGeometricData.cs @@ -194,4 +194,4 @@ protected override void Down(MigrationBuilder migrationBuilder) .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data") .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,hygrothermal_data,optical_data,photovoltaic_data,geometric_data"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs index 9ca698a4..41634a87 100644 --- a/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs +++ b/backend/src/Migrations/20250218172859_MakeDescriptionOptional.cs @@ -34,4 +34,4 @@ protected override void Down(MigrationBuilder migrationBuilder) oldType: "text", oldNullable: true); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250331141152_AddAccessRights.cs b/backend/src/Migrations/20250331141152_AddAccessRights.cs index 6dc2799b..47b2b61e 100644 --- a/backend/src/Migrations/20250331141152_AddAccessRights.cs +++ b/backend/src/Migrations/20250331141152_AddAccessRights.cs @@ -338,4 +338,4 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "calorimetric_data"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250527144412_AddDataAccessRights.cs b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs index af7403ee..035933b2 100644 --- a/backend/src/Migrations/20250527144412_AddDataAccessRights.cs +++ b/backend/src/Migrations/20250527144412_AddDataAccessRights.cs @@ -196,4 +196,4 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "calorimetric_data"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs index f9cb6e21..887c02d3 100644 --- a/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs +++ b/backend/src/Migrations/20250527150325_MoveInstitutionAccessRights.cs @@ -48,4 +48,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "access_rights", column: "Id"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs index 80bcf2ca..71cb72ac 100644 --- a/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs +++ b/backend/src/Migrations/20250530155318_AddRequiredPropertyExistsToPublication.cs @@ -74,4 +74,4 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "calorimetric_data_Approvals"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs index 5788109f..e7253c78 100644 --- a/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs +++ b/backend/src/Migrations/20250725172127_UpgradeOpenIddictToVersion7.cs @@ -38,4 +38,4 @@ protected override void Down(MigrationBuilder migrationBuilder) oldMaxLength: 150, oldNullable: true); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs index 9116a8be..a21b43b2 100644 --- a/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs +++ b/backend/src/Migrations/20250731163636_MakeInstitutionIdOfAccessRightsUnique.cs @@ -26,4 +26,4 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "institution_access_rights"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs index fb40b8b9..40be0f47 100644 --- a/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs +++ b/backend/src/Migrations/20250812171810_AddQueryVariablesToApprovals.cs @@ -145,4 +145,4 @@ protected override void Down(MigrationBuilder migrationBuilder) schema: "database", table: "calorimetric_data"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs index c6104729..d3f78b42 100644 --- a/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs +++ b/backend/src/Migrations/20250902122837_RenameResponsePropertyOfApprovalsToMessage.cs @@ -134,4 +134,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "calorimetric_data", newName: "Approval_Response"); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs index e1daa378..819ba2af 100644 --- a/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs +++ b/backend/src/Migrations/20250907130041_AddApproverIdToResponseApproval.cs @@ -76,4 +76,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "calorimetric_data"); } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs index 293c4fdd..da38a0f2 100644 --- a/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs +++ b/backend/src/Migrations/20251023152609_RememberUserWhoCreatedDataSet.cs @@ -81,4 +81,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "calorimetric_data"); } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs index ae7ba745..6e8bd06d 100644 --- a/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs +++ b/backend/src/Migrations/20251031125736_AddFileExtensionToResource.cs @@ -27,4 +27,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "get_https_resource"); } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs index 686dfb3b..3213579e 100644 --- a/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs +++ b/backend/src/Migrations/20251113172901_CheckResourcesForConsistency.cs @@ -454,4 +454,4 @@ protected override void Down(MigrationBuilder migrationBuilder) principalColumn: "Id"); } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs index 090d05ed..b0052c87 100644 --- a/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs +++ b/backend/src/Migrations/20251125181102_MirrorWidthAndHeightOfGeometricData.cs @@ -41,4 +41,4 @@ protected override void Down(MigrationBuilder migrationBuilder) table: "geometric_data"); } } -} +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 3856ef43..40781415 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using Database.Data; +using Database.Services; using Microsoft.AspNetCore.Builder; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -14,7 +15,6 @@ using Serilog.Events; using Serilog.Formatting.Compact; using ILogger = Microsoft.Extensions.Logging.ILogger; -using Database.Services; namespace Database; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 213e986f..b7994586 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -3,9 +3,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Database.Data; using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Services; diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs index cff92371..94fb43b6 100644 --- a/backend/src/Services/DataApprovalService.cs +++ b/backend/src/Services/DataApprovalService.cs @@ -1,7 +1,7 @@ -using NodaTime; using System; using System.Threading; using System.Threading.Tasks; +using NodaTime; namespace Database.Services; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 8450fbe8..e529aa1c 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,18 +1,18 @@ -using NodaTime; using System; +using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; +using Database.ApiRequests; using Database.Data; -using GraphQL; +using Database.Extensions; using Database.Json; +using GraphQL; using HotChocolate; -using System.Linq; using HotChocolate.Execution; -using Database.ApiRequests; -using System.Collections.Generic; -using Database.Extensions; +using Microsoft.Extensions.Logging; +using NodaTime; namespace Database.Services; diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 1832a049..f2e8caf0 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,12 +1,12 @@ +using System; using System.Net.Http; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; -using Database.ApiRequests; -using Database.Extensions; -using System; namespace Database.Services; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 264297db..e81e0b63 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -4,12 +4,13 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; -using HotChocolate.AspNetCore; using Database.Configuration; using Database.Data; using Database.Data.Extensions; using Database.Enumerations; +using Database.GraphQl; using Database.Services; +using HotChocolate.AspNetCore; using Laraue.EfCoreTriggers.PostgreSql.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; @@ -27,7 +28,6 @@ using Microsoft.OpenApi; using Scalar.AspNetCore; using Serilog; -using Database.GraphQl; namespace Database; diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index 6326dade..8ab023c1 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -5,11 +5,11 @@ using System; using System.Linq; using System.Threading.Tasks; +using Database.Data; +using Database.Services; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; -using Database.Data; -using Database.Services; namespace Database.Tests.Integration; diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs index 162f75a0..a5673acb 100644 --- a/backend/test/Integration/IntegrationTests.cs +++ b/backend/test/Integration/IntegrationTests.cs @@ -10,13 +10,13 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; +using Database.Data; +using Database.Json; using FluentAssertions; using Json.Path; +using Microsoft.AspNetCore.Mvc.Testing; using NUnit.Framework; using Snapshooter; -using Microsoft.AspNetCore.Mvc.Testing; -using Database.Data; -using Database.Json; namespace Database.Tests.Integration; diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index 1ab29919..d27eb99c 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -1,15 +1,15 @@ -using NUnit.Framework; -using FluentAssertions; -using Database.Methods.SpectralToIntegral; -using System.Threading.Tasks; -using System.Text.Json; -using Database.Methods; -using System.IO; using System; -using System.Reflection; -using FluentAssertions.Execution; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Reflection; +using System.Text.Json; +using System.Threading.Tasks; using Database.Json; +using Database.Methods; +using Database.Methods.SpectralToIntegral; +using FluentAssertions; +using FluentAssertions.Execution; +using NUnit.Framework; namespace Database.Tests.Methods; From 68440262f5563ed6226217c32dceedb837a1013f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 18:48:47 +0100 Subject: [PATCH 650/878] Listen on all network interfaces --- docker-compose.production.yml | 2 +- docker-compose.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 40327e5d..4ad22cb7 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -127,7 +127,7 @@ services: - ./nginx/html:/etc/nginx/html:ro - files:/data/files:ro ports: - - "127.0.0.1:${HTTP_PORT:?required}:80" + - "0.0.0.0:${HTTP_PORT:?required}:80" command: [ "nginx", "-g", "daemon off;" ] restart: "always" healthcheck: diff --git a/docker-compose.yml b/docker-compose.yml index e2d459e0..df37accd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,7 @@ services: POSTGRES_PASSWORD: "postgres" POSTGRES_DB: "postgres" # ports: - # - "127.0.0.1:5432:5432" + # - "0.0.0.0:5432:5432" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -48,11 +48,11 @@ services: # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml # ports: # Web interface - # - "127.0.0.1:4042:80" + # - "0.0.0.0:4042:80" # SMTP server - # - "127.0.0.1:25:25" + # - "0.0.0.0:25:25" # Imap server - # - "127.0.0.1:143:143" + # - "0.0.0.0:143:143" healthcheck: test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] interval: "30s" @@ -92,7 +92,7 @@ services: - /home/me/.nuget - /home/me/.dotnet # ports: - # - "127.0.0.1:8080:8080" + # - "0.0.0.0:8080:8080" environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" @@ -169,7 +169,7 @@ services: - "${HOST:?required}:host-gateway" # user: "1000:1000" # ports: - # - "127.0.0.1:8081:8080" + # - "0.0.0.0:8081:8080" command: [ "yarn", "dev" ] healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] @@ -199,8 +199,8 @@ services: SERVER_NAME: "${HOST:?required}" SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?required}" ports: - - "127.0.0.1:${HTTP_PORT:?required}:80" - - "127.0.0.1:${HTTPS_PORT:?required}:443" + - "0.0.0.0:${HTTP_PORT:?required}:80" + - "0.0.0.0:${HTTPS_PORT:?required}:443" command: [ "nginx-debug", "-g", "daemon off;" ] # run in debug mode healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] From 90ed14a646f9c2c93ef9fd2d6912f1e95c230dae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 19:12:53 +0100 Subject: [PATCH 651/878] Allow empty string for relay allowed emails --- docker-compose.production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 4ad22cb7..7a10fbc5 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -40,7 +40,7 @@ services: ServerOptions__TlsCertificate: "" RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?required}" RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" - RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS:?required}" + RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS?required}" restart: "always" # For all configuration options see # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml From cd34156b49e7115644a9f5fcbc7b713d6f057cfe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 19:15:27 +0100 Subject: [PATCH 652/878] Allow empty string for relay SMTP host --- docker-compose.production.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 7a10fbc5..304827ec 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -38,7 +38,7 @@ services: ServerOptions__NumberOfSessionsToKeep: "100" ServerOptions__TlsMode: "None" ServerOptions__TlsCertificate: "" - RelayOptions__SmtpServer: "${RELAY_SMTP_HOST:?required}" + RelayOptions__SmtpServer: "${RELAY_SMTP_HOST?required}" RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS?required}" restart: "always" From 934f29e00de5964d34b613e5832370a03c17921f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 19:22:19 +0100 Subject: [PATCH 653/878] Allow `./` to be copied in Dockerfile-production (running `docker compose ... build --check` fails otherwise) --- backend/.dockerignore | 2 ++ frontend/.dockerignore | 2 ++ 2 files changed, 4 insertions(+) diff --git a/backend/.dockerignore b/backend/.dockerignore index f12b3ac4..6a90a764 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -8,6 +8,8 @@ Dockerfile* **/core **/TestResult*/ +!./ + ### Csharp ### ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 92be1395..c4de8a51 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -6,6 +6,8 @@ **/*.bak Dockerfile* +!./ + # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies From 02e98c61e2b61e5756e7078eefaf441e55c1f4fd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 19:28:16 +0100 Subject: [PATCH 654/878] Chain commands with && --- frontend/Dockerfile-production | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 2f894591..743f8df3 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -57,7 +57,7 @@ RUN \ RUN npx --no-install next telemetry disable # hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. RUN \ - addgroup --system --gid 1001 nodejs \ - adduser --system --uid 1001 --ingroup nodejs nextjs \ + addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 --ingroup nodejs nextjs && \ chown --recursive nextjs:nodejs /app/.next USER nextjs From 01e2f05c766141ffeedba3279f4cec75395e2400 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 29 Jan 2026 19:54:17 +0100 Subject: [PATCH 655/878] Mention how to use a mutation directly --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 842d5200..46c271ca 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,34 @@ and the pages following it. for example, `staging.solarbuildingenvelopes.com` or `www.solarbuildingenvelopes.com` for the product-data database of the TestLab Solar Facades. + Alternatively, after logging in, open + `https://www.buildingenvelopedata.org/graphql/` and run the following + mutation with your institution ID and host filled-in: + ``` + mutation { + createOpenIdConnectApplication( + input: { + institutionId: "00000000-0000-0000-0000-000000000000" + clientId: "my-client" + consentType: EXPLICIT + displayName: "My Client" + endpoints: [AUTHORIZATION, PUSHED_AUTHORIZATION, INTROSPECTION, END_SESSION, REVOCATION, TOKEN] + grantTypes: [AUTHORIZATION_CODE, REFRESH_TOKEN] + postLogoutRedirectUri: "https://${HOST}/connect/callback/logout/metabase" + redirectUri: "https://${HOST}/connect/callback/login/metabase" + responseTypes: [CODE] + scopes: [PROFILE, READ_API] + } + ) { + clientSecret + errors { + code + message + path + } + } + } + ``` 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./.env`, From c362bbaaa38e125871224bfd886427adc61d107e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 30 Jan 2026 15:03:06 +0100 Subject: [PATCH 656/878] Fail if a transaction needs to be rolled back during migration --- Makefile | 18 ++++++++++-------- Makefile.production | 20 ++++++-------------- backend/src/Migrations/migrate.sql | 2 ++ 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/Makefile b/Makefile index 12a93092..2ce232a1 100644 --- a/Makefile +++ b/Makefile @@ -32,23 +32,23 @@ name : ## Print value of variable `NAME` dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` bash -c " \ diff \ - <(cut --delimiter='=' --fields=1 ./.env.sample | sort) \ - <(cut --delimiter='=' --fields=1 ./.env | sort) \ + Date: Fri, 30 Jan 2026 15:42:57 +0100 Subject: [PATCH 657/878] Add missing ( --- Makefile | 16 ++++++++-------- Makefile.production | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 2ce232a1..b35b5e3c 100644 --- a/Makefile +++ b/Makefile @@ -32,23 +32,23 @@ name : ## Print value of variable `NAME` dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` bash -c " \ diff \ - Date: Mon, 2 Feb 2026 10:37:50 +0100 Subject: [PATCH 658/878] Find bash in PATH --- Makefile | 2 +- Makefile.production | 12 +----------- backend/Makefile | 2 +- frontend/Makefile | 2 +- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index b35b5e3c..178881b6 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include ./.env -SHELL := /bin/bash +SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables diff --git a/Makefile.production b/Makefile.production index 1301a8ab..712825fd 100644 --- a/Makefile.production +++ b/Makefile.production @@ -3,20 +3,10 @@ include ./.env -SHELL := /bin/bash +SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables -# Use bash with pipefail to have backup and restore fail when whatever comes -# before the pipe fails. The options given to bash below are described in -# https://explainshell.com/explain?cmd=set+-eux+-o+pipefail -# and -# https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425 -# These settings could just be applied where needed instead of globally as -# described in -# https://stackoverflow.com/questions/23079651/equivalent-of-pipefail-in-gnu-make/23080721#23080721 -SHELL=/bin/bash -eux -o pipefail - docker_compose = \ docker compose \ --file docker-compose.production.yml \ diff --git a/backend/Makefile b/backend/Makefile index 3973cf5d..c4398496 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -1,7 +1,7 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html -SHELL := /bin/bash +SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables diff --git a/frontend/Makefile b/frontend/Makefile index cfad0137..dc30d3ac 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -1,7 +1,7 @@ # Concise introduction to GNU Make: # https://swcarpentry.github.io/make-novice/reference.html -SHELL := /bin/bash +SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables From 369795357f8b730c870fcccc7b58261e4cc95c25 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Feb 2026 10:43:20 +0100 Subject: [PATCH 659/878] Sync Postgres versions --- backend/src/Startup.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index e81e0b63..a951493f 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -173,7 +173,8 @@ AppSettings appSettings .UseNpgsql( appSettings.Database.ConnectionString, _ => _ - .SetPostgresVersion(13, 13) + // Keep version in sync with the one in ./docker-compose.*.yml + .SetPostgresVersion(13, 23) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .UseNodaTime() .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, appSettings.Database.SchemaName) From b989941a35705de49ae0954d66a2cd5ab6c65b4f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Feb 2026 11:26:48 +0100 Subject: [PATCH 660/878] Format Dockerfiles with `make format` --- Makefile | 37 ++++++-- backend/Dockerfile | 157 +++++++++++++------------------- backend/Dockerfile-bootstrap | 13 +-- backend/Dockerfile-production | 44 ++++----- backend/dotnet-certificate-tool | 2 +- frontend/Dockerfile | 49 ++++------ frontend/Dockerfile-production | 32 +++---- 7 files changed, 148 insertions(+), 186 deletions(-) diff --git a/Makefile b/Makefile index 178881b6..0bd52c66 100644 --- a/Makefile +++ b/Makefile @@ -273,16 +273,20 @@ dclint = \ docker run \ --rm \ --tty \ + --user $(shell id --user):$(shell id --group) \ --volume .:/app \ - zavoloklom/dclint \ + --pull "always" \ + zavoloklom/dclint:latest \ --config /app/.dclintrc hadolint = \ docker run \ --rm \ --interactive \ + --user $(shell id --user):$(shell id --group) \ --volume ./.hadolint.yml:/.config/.hadolint.yaml \ - hadolint/hadolint \ + --pull "always" \ + hadolint/hadolint:latest \ hadolint \ --config /.config/.hadolint.yaml @@ -298,20 +302,33 @@ hadolint = \ # /Makefile.production \ # /Makefile.backend \ # /Makefile.frontend -lint : ## Lint Docker Compose files and Dockerfiles +lint : ## Lint Docker Compose and Dockerfiles + @echo Lint Docker Compose Files ${dclint} . - ${hadolint} - < ./backend/Dockerfile - ${hadolint} - < ./backend/Dockerfile-bootstrap - ${hadolint} - < ./backend/Dockerfile-production - ${hadolint} - < ./Dockerfile-show-build-context - ${hadolint} - < ./frontend/Dockerfile - ${hadolint} - < ./frontend/Dockerfile-production + @echo Lint Dockerfiles + for dockerfile in $(shell find . -name "Dockerfile*"); do \ + echo "... $${dockerfile}" \ + ${hadolint} - < $${dockerfile} ; \ + done .PHONY : lint -fix : ## Fix Docker Compoe linting violations +fix : ## Fix Docker Compose linting violations ${dclint} --fix . .PHONY : fix +format : ## Format Dockerfiles + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume $(shell pwd):/pwd \ + --pull "always" \ + ghcr.io/reteps/dockerfmt:latest \ + --indent 2 \ + --newline \ + --write \ + $(shell find . -name "Dockerfile*" -printf "/pwd/%h/%f ") +.PHONY : format + createdb : DBNAME = ${database_name} createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` ${docker_compose} up \ diff --git a/backend/Dockerfile b/backend/Dockerfile index 33852c35..87e333fe 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -79,9 +79,8 @@ RUN \ # https://lttng.org/docs/#doc-tracing-your-own-user-application # For best practices on using `apt-get` see # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run -RUN \ - apt-get update && \ - apt-get install \ +RUN apt-get update \ + && apt-get install \ --assume-yes \ --no-install-recommends \ curl \ @@ -93,23 +92,22 @@ RUN \ # npm \ openssl \ procps \ - tini && \ - rm \ + tini \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* # Add dependencies for interactive examples in ./examples # `jq` can be used to format and filter JSON data -RUN \ - apt-get update && \ - apt-get install \ +RUN apt-get update \ + && apt-get install \ --assume-yes \ --no-install-recommends \ bash \ jq \ - neovim && \ - rm \ + neovim \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* @@ -117,11 +115,8 @@ RUN \ # Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 # See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 # and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -COPY \ - "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" \ - /usr/local/share/ca-certificates -RUN \ - update-ca-certificates +COPY "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" /usr/local/share/ca-certificates +RUN update-ca-certificates # Install up-to-date GnuPG from the official GnuPG repository. # For available versions and the signing-key fingerprint see @@ -170,15 +165,14 @@ RUN \ /var/lib/apt/lists/* ENV HOME=/home/me -RUN \ - mkdir --parents "${HOME}/app" && \ - chown \ +RUN mkdir --parents "${HOME}/app" \ + && chown \ me:us \ - "${HOME}" && \ - chown \ + "${HOME}" \ + && chown \ me:us \ - "${HOME}/app" && \ - ln --symbolic "${HOME}/app" /app + "${HOME}/app" \ + && ln --symbolic "${HOME}/app" /app ########### # As `me` # @@ -196,33 +190,22 @@ WORKDIR /app # Information on the `dotnet tool` command-line interface can be found under # * https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install # * https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool -COPY \ - --chown=me:us \ - ./dotnet-tools.json ./ -COPY \ - --chown=me:us \ - ./dotnet-certificate-tool ./dotnet-certificate-tool +COPY --chown=me:us ./dotnet-tools.json ./ +COPY --chown=me:us ./dotnet-certificate-tool ./dotnet-certificate-tool RUN \ # `dotnet pack` is run automatically by `dotnet build` because `GeneratePackageOnBuild` is set to `True`. dotnet build \ - ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj && \ - dotnet tool restore \ + ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj \ + && dotnet tool restore \ --tool-manifest ./dotnet-tools.json \ - --add-source ./dotnet-certificate-tool/src/nupkg && \ - rm --recursive --force \ + --add-source ./dotnet-certificate-tool/src/nupkg \ + && rm --recursive --force \ ./dotnet-certificate-tool -RUN \ - mkdir ./src && \ - mkdir ./test -COPY \ - --chown=me:us \ - ./src/Database.csproj \ - ./src/ -COPY \ - --chown=me:us \ - ./test/Database.Tests.csproj \ - ./test/ +RUN mkdir ./src \ + && mkdir ./test +COPY --chown=me:us ./src/Database.csproj ./src/ +COPY --chown=me:us ./test/Database.Tests.csproj ./test/ # https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile.alpine-x64 # https://github.com/dotnet/dotnet-docker/blob/master/samples/run-aspnetcore-https-development.md @@ -234,16 +217,12 @@ COPY \ # `dotnet dev-certs` does not generate certificates that work in Linux # distributions # RUN \ - # dotnet dev-certs https --export-path ./.secrets/https/icon.pfx --password crypticpassword && \ +# dotnet dev-certs https --export-path ./.secrets/https/icon.pfx --password crypticpassword && \ -COPY \ - --chown=me:us \ - ./Database.sln \ - ./ -RUN \ - dotnet clean && \ - dotnet restore && \ - dotnet tool run dotnet-sos install # https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-instructions.md +COPY --chown=me:us ./Database.sln ./ +RUN dotnet clean \ + && dotnet restore \ + && dotnet tool run dotnet-sos install # https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-instructions.md # Add the certificate authority PKCS#12 file to the X.509 certificate store for # trusted root certificate authorities named `Root`, see @@ -361,34 +340,27 @@ RUN \ # } # } # ``` -RUN \ - mkdir ./ssl -COPY \ - --chown=me:us \ - "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ - ./ssl/ -RUN \ - dotnet tool run \ - certificate-tool add \ - --file "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ - --store-name Root \ - --password "${CERTIFICATE_AUTHORITY_PASSWORD}" +RUN mkdir ./ssl +COPY --chown=me:us "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" ./ssl/ +RUN dotnet tool run \ + certificate-tool add \ + --file "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ + --store-name Root \ + --password "${CERTIFICATE_AUTHORITY_PASSWORD}" # bash parameter completion for the dotnet CLI # see https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash # for the here-document syntax used to append to the ~/.bashrc file # see https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04 # hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. -RUN \ - cat <<-"EOT" >> ${HOME}/.bashrc - function _dotnet_bash_complete() - { - local cur="${COMP_WORDS[COMP_CWORD]}" IFS=$'\n' - local candidates - read -d '' -ra candidates < <(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2>/dev/null) - read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]:-}" -- "$cur") - } - complete -f -F _dotnet_bash_complete dotnet +RUN cat <<-"EOT" >> ${HOME}/.bashrc +function _dotnet_bash_complete() { + local cur="${COMP_WORDS[COMP_CWORD]}" IFS=$'\n' + local candidates + read -d '' -ra candidates < <(dotnet complete --position "${COMP_POINT}" "${COMP_LINE}" 2>/dev/null) + read -d '' -ra COMPREPLY < <(compgen -W "${candidates[*]:-}" -- "$cur") +} +complete -f -F _dotnet_bash_complete dotnet EOT # COPY \ @@ -401,29 +373,28 @@ EOT # respective mounted volumes be owned by the user `me` and the group `us`. # For an explanation for the Visual Studio Code Server directories see # https://code.visualstudio.com/docs/remote/containers-advanced#_avoiding-extension-reinstalls-on-container-rebuild -RUN \ - mkdir --parents \ - ./src/files && \ - mkdir --parents \ - ./src/logs && \ - mkdir --parents \ +RUN mkdir --parents \ + ./src/files \ + && mkdir --parents \ + ./src/logs \ + && mkdir --parents \ ./src/bin \ - ./src/obj && \ - mkdir --parents \ + ./src/obj \ + && mkdir --parents \ ./test/bin \ - ./test/obj && \ - mkdir --parents \ + ./test/obj \ + && mkdir --parents \ "${HOME}/.vscode-server" \ - "${HOME}/.vscode-server-insiders" && \ - mkdir --parents \ + "${HOME}/.vscode-server-insiders" \ + && mkdir --parents \ "${HOME}/.nuget" \ - "${HOME}/.dotnet" && \ - mkdir --parents \ - "${HOME}/.gnupg" && \ - chmod u=rwx,g=,o= \ - "${HOME}/.gnupg" && \ - echo "allow-loopback-pinentry" \ - > "${HOME}/.gnupg/gpg-agent.conf" + "${HOME}/.dotnet" \ + && mkdir --parents \ + "${HOME}/.gnupg" \ + && chmod u=rwx,g=,o= \ + "${HOME}/.gnupg" \ + && echo "allow-loopback-pinentry" \ + >"${HOME}/.gnupg/gpg-agent.conf" ENV SHELL=/bin/bash diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 492e4cb2..3a9f7b7b 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -97,19 +97,16 @@ RUN \ /var/lib/apt/lists/* ENV HOME=/home/me -RUN \ - mkdir --parents "${HOME}" && \ - chown \ +RUN mkdir --parents "${HOME}" \ + && chown \ me:us \ "${HOME}" USER me WORKDIR /app -RUN \ - mkdir --parents "${HOME}/.dotnet" +RUN mkdir --parents "${HOME}/.dotnet" ENV PATH=$PATH:/home/me/.dotnet/tools -RUN \ - dotnet tool install --global \ - dotnet-script +RUN dotnet tool install --global \ + dotnet-script diff --git a/backend/Dockerfile-production b/backend/Dockerfile-production index 411997d6..a3593d9a 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile-production @@ -35,8 +35,7 @@ COPY ./ /app WORKDIR /app -RUN \ - dotnet restore +RUN dotnet restore # `dotnet publish` writes its output to # [project_file_folder]/bin/[configuration]/[framework]/publish/ # as told in the description of the option `--output` on @@ -44,11 +43,10 @@ RUN \ # We cannot use this option though to specify a custom folder as explained on # https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid # hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. -RUN \ - dotnet publish \ - --configuration release \ - --self-contained false \ - --no-restore +RUN dotnet publish \ + --configuration release \ + --self-contained false \ + --no-restore ########### # RUNTIME # @@ -60,14 +58,13 @@ RUN \ FROM mcr.microsoft.com/dotnet/aspnet:10.0-noble-amd64 SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -RUN \ - apt-get update && \ - apt-get upgrade --assume-yes && \ - apt-get install \ +RUN apt-get update \ + && apt-get upgrade --assume-yes \ + && apt-get install \ --assume-yes \ --no-install-recommends \ - curl && \ - rm \ + curl \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* @@ -120,21 +117,16 @@ RUN \ WORKDIR /app -COPY \ - --from=build \ - /app/src/bin/Release/net10.0/publish \ - ./ +COPY --from=build /app/src/bin/Release/net10.0/publish ./ -RUN \ - mkdir --parents \ - /root/.gnupg && \ - chmod u=rwx,g=,o= \ - /root/.gnupg && \ - echo "allow-loopback-pinentry" \ - > /root/.gnupg/gpg-agent.conf +RUN mkdir --parents \ + /root/.gnupg \ + && chmod u=rwx,g=,o= \ + /root/.gnupg \ + && echo "allow-loopback-pinentry" \ + >/root/.gnupg/gpg-agent.conf -RUN \ - ln --symbolic ./Database.dll ./App.dll +RUN ln --symbolic ./Database.dll ./App.dll # See: https://github.com/dotnet/announcements/issues/20 # Uncomment to enable globalization APIs diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool index d5847eb8..1b8cd879 160000 --- a/backend/dotnet-certificate-tool +++ b/backend/dotnet-certificate-tool @@ -1 +1 @@ -Subproject commit d5847eb8110297c129a21723c4f8a522b3337638 +Subproject commit 1b8cd879ce448dd9a7d8150e8fa5136fec748672 diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 43276972..cbc81b06 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -27,34 +27,29 @@ RUN \ # `openssl` is needed to debug SSL certificates # For best practices on using `apt-get` see # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run -RUN \ - apt-get update && \ - apt-get install \ +RUN apt-get update \ + && apt-get install \ --assume-yes \ --no-install-recommends \ ca-certificates \ make \ openssl \ tini \ - wget && \ - rm \ + wget \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* -COPY \ - "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" \ - /usr/local/share/ca-certificates -RUN \ - update-ca-certificates +COPY "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" /usr/local/share/ca-certificates +RUN update-ca-certificates ENV HOME=/home/node -RUN \ - mkdir --parents "${HOME}/app" && \ - chown \ +RUN mkdir --parents "${HOME}/app" \ + && chown \ node:node \ - "${HOME}/app" && \ - ln --symbolic "${HOME}/app" /app + "${HOME}/app" \ + && ln --symbolic "${HOME}/app" /app ############# # As `node` # @@ -63,23 +58,17 @@ USER node WORKDIR /app ENV PATH=/app/node_modules/.bin:$PATH -COPY \ - --chown=node:node \ - ./package.json ./ -COPY \ - --chown=node:node \ - ./yarn.lock ./ -RUN \ - yarn install \ - --pure-lockfile && \ - yarn cache clean +COPY --chown=node:node ./package.json ./ +COPY --chown=node:node ./yarn.lock ./ +RUN yarn install \ + --pure-lockfile \ + && yarn cache clean # Create empty directories for user `me` and group `us` to make the respective # mounted volumes be owned by the user `me` and the group `us`. -RUN \ - mkdir --parents \ - ./.next \ - ./__generated__ \ - ./node_modules +RUN mkdir --parents \ + ./.next \ + ./__generated__ \ + ./node_modules ENTRYPOINT ["/usr/bin/tini", "--"] diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile-production index 743f8df3..7f851b83 100644 --- a/frontend/Dockerfile-production +++ b/frontend/Dockerfile-production @@ -8,10 +8,9 @@ FROM node:24.11-trixie-slim AS dependencies SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -RUN \ - apt-get update && \ - apt-get upgrade --assume-yes && \ - rm \ +RUN apt-get update \ + && apt-get upgrade --assume-yes \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* @@ -21,10 +20,9 @@ RUN yarn install --pure-lockfile FROM node:24.11-trixie-slim AS build SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -RUN \ - apt-get update && \ - apt-get upgrade --assume-yes && \ - rm \ +RUN apt-get update \ + && apt-get upgrade --assume-yes \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* @@ -43,21 +41,19 @@ COPY --from=build /app/next.config.ts ./ COPY --from=build /app/node_modules ./node_modules COPY --from=build /app/package.json ./ COPY --from=build /app/public ./public -RUN \ - apt-get update && \ - apt-get upgrade --assume-yes && \ - apt-get install \ +RUN apt-get update \ + && apt-get upgrade --assume-yes \ + && apt-get install \ --assume-yes \ --no-install-recommends \ - wget && \ - rm \ + wget \ + && rm \ --recursive \ --force \ /var/lib/apt/lists/* RUN npx --no-install next telemetry disable # hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. -RUN \ - addgroup --system --gid 1001 nodejs && \ - adduser --system --uid 1001 --ingroup nodejs nextjs && \ - chown --recursive nextjs:nodejs /app/.next +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 --ingroup nodejs nextjs \ + && chown --recursive nextjs:nodejs /app/.next USER nextjs From 2257737c2f24b7fa3b26820e9466a775cbf2406c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Feb 2026 12:14:11 +0100 Subject: [PATCH 661/878] Use dotenv-linter to lint and fix .env files --- .env.sample | 4 +-- Makefile | 58 ++++++++++++++++++++--------------- Makefile.production | 21 +++++++------ docker-compose.production.yml | 1 - 4 files changed, 47 insertions(+), 37 deletions(-) diff --git a/.env.sample b/.env.sample index 4e93730a..e49473c8 100644 --- a/.env.sample +++ b/.env.sample @@ -18,14 +18,14 @@ VERIFICATION_CODE=secret CERTIFICATE_AUTHORITY_HOST=ca.org CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password -CERTIFICATE_AUTHORITY_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy +CERTIFICATE_AUTHORITY_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase SSL_CERTIFICATE_BASE_FILE_NAME=database SSL_CERTIFICATE_PASSWORD=password -SSL_CERTIFICATE_SUBJECT=/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy +SSL_CERTIFICATE_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password diff --git a/Makefile b/Makefile index 0bd52c66..a7c66146 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,15 @@ docker_compose = \ --env-file ./.env \ --project-name ${NAME} +dotenv-linter = \ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/mnt" \ + --pull "always" \ + --quiet \ + dotenvlinter/dotenv-linter:latest + database_name = xbase dump_archive_name = postgresql_dumpall.gz @@ -30,26 +39,10 @@ name : ## Print value of variable `NAME` .PHONY : name dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env | sort) \ - " - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local | sort) \ - " - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env.production.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env.staging.sample | sort) \ - " - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local.production.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local.staging.sample | sort) \ - " + ${dotenv-linter} diff /mnt/.env.sample /mnt/.env + ${dotenv-linter} diff /mnt/frontend/.env.local.sample /mnt/frontend/.env.local + ${dotenv-linter} diff /mnt/.env.production.sample /mnt/.env.staging.sample + ${dotenv-linter} diff /mnt/frontend/.env.local.production.sample /mnt/frontend/.env.local.staging.sample .PHONY : dotenv # ----------------------------- # @@ -274,8 +267,9 @@ dclint = \ --rm \ --tty \ --user $(shell id --user):$(shell id --group) \ - --volume .:/app \ + --volume "$(shell pwd):/app" \ --pull "always" \ + --quiet \ zavoloklom/dclint:latest \ --config /app/.dclintrc @@ -286,6 +280,7 @@ hadolint = \ --user $(shell id --user):$(shell id --group) \ --volume ./.hadolint.yml:/.config/.hadolint.yaml \ --pull "always" \ + --quiet \ hadolint/hadolint:latest \ hadolint \ --config /.config/.hadolint.yaml @@ -302,7 +297,13 @@ hadolint = \ # /Makefile.production \ # /Makefile.backend \ # /Makefile.frontend -lint : ## Lint Docker Compose and Dockerfiles +lint : ## Lint .env files, Docker Compose files, and Dockerfiles + @echo Lint .env Files + ${dotenv-linter} \ + check \ + --recursive \ + --ignore-checks UnorderedKey \ + . @echo Lint Docker Compose Files ${dclint} . @echo Lint Dockerfiles @@ -312,7 +313,15 @@ lint : ## Lint Docker Compose and Dockerfiles done .PHONY : lint -fix : ## Fix Docker Compose linting violations +fix : ## Fix .env files and Docker Compose linting violations + @echo Fix .env Files + ${dotenv-linter} \ + fix \ + --no-backup \ + --recursive \ + --ignore-checks UnorderedKey \ + . + @echo Fix Docker Compose Files ${dclint} --fix . .PHONY : fix @@ -320,8 +329,9 @@ format : ## Format Dockerfiles docker run \ --rm \ --user $(shell id --user):$(shell id --group) \ - --volume $(shell pwd):/pwd \ + --volume "$(shell pwd):/pwd" \ --pull "always" \ + --quiet \ ghcr.io/reteps/dockerfmt:latest \ --indent 2 \ --newline \ diff --git a/Makefile.production b/Makefile.production index 712825fd..327ab1d4 100644 --- a/Makefile.production +++ b/Makefile.production @@ -13,6 +13,15 @@ docker_compose = \ --env-file ./.env \ --project-name ${NAME} +dotenv-linter = \ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/mnt:ro" \ + --pull "always" \ + --quiet \ + dotenvlinter/dotenv-linter:latest + dump_archive_name = postgresql_dumpall.gz files_archive_name = files.gz @@ -29,16 +38,8 @@ name : ## Print value of variable `NAME` .PHONY : name dotenv : ## Assert that all variables in ./.env.production.sample are available in ./.env - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env.production.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./.env | sort) \ - " - bash -c " \ - diff \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local.production.sample | sort) \ - <(cut --only-delimited --delimiter='=' --fields=1 ./frontend/.env.local | sort) \ - " + ${dotenv-linter} diff /mnt/.env.production.sample /mnt/.env + ${dotenv-linter} diff /mnt/frontend/.env.local.production.sample /mnt/frontend/.env.local .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 304827ec..15054bfc 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -3,7 +3,6 @@ name: "${NAME}" services: database: - # Keep version in sync with the one in ./backend/src/Startup.cs set by calling `SetPostgresVersion` image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data From 36c3b87c62802c5983e72a96b2b1bd4a48d470dc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Feb 2026 15:59:43 +0100 Subject: [PATCH 662/878] Switch .env diff order to improve error output --- Makefile | 8 ++++---- Makefile.production | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index a7c66146..1090f3cf 100644 --- a/Makefile +++ b/Makefile @@ -39,10 +39,10 @@ name : ## Print value of variable `NAME` .PHONY : name dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` - ${dotenv-linter} diff /mnt/.env.sample /mnt/.env - ${dotenv-linter} diff /mnt/frontend/.env.local.sample /mnt/frontend/.env.local - ${dotenv-linter} diff /mnt/.env.production.sample /mnt/.env.staging.sample - ${dotenv-linter} diff /mnt/frontend/.env.local.production.sample /mnt/frontend/.env.local.staging.sample + ${dotenv-linter} diff /mnt/.env /mnt/.env.sample + ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.sample + ${dotenv-linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample + ${dotenv-linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample .PHONY : dotenv # ----------------------------- # diff --git a/Makefile.production b/Makefile.production index 327ab1d4..8e7c58b9 100644 --- a/Makefile.production +++ b/Makefile.production @@ -38,8 +38,8 @@ name : ## Print value of variable `NAME` .PHONY : name dotenv : ## Assert that all variables in ./.env.production.sample are available in ./.env - ${dotenv-linter} diff /mnt/.env.production.sample /mnt/.env - ${dotenv-linter} diff /mnt/frontend/.env.local.production.sample /mnt/frontend/.env.local + ${dotenv-linter} diff /mnt/.env /mnt/.env.production.sample + ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.production.sample .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format From dc1dd287008342522dd64159c5568d9a654aef22 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Feb 2026 16:00:07 +0100 Subject: [PATCH 663/878] Improve healthcheck --- docker-compose.production.yml | 24 ++++++++++++------------ docker-compose.yml | 24 ++++++++++++------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 15054bfc..94c82b24 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -45,9 +45,9 @@ services: # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml healthcheck: test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" backend: build: @@ -90,9 +90,9 @@ services: healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" frontend: build: @@ -107,9 +107,9 @@ services: restart: "always" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" nginx: image: "nginx:1.29-trixie-otel" @@ -131,9 +131,9 @@ services: restart: "always" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" volumes: data: diff --git a/docker-compose.yml b/docker-compose.yml index df37accd..3cc169e4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,9 +55,9 @@ services: # - "0.0.0.0:143:143" healthcheck: test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" backend: build: @@ -141,9 +141,9 @@ services: healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" frontend: build: @@ -173,9 +173,9 @@ services: command: [ "yarn", "dev" ] healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" nginx: image: "nginx:1.29-trixie-otel" @@ -204,9 +204,9 @@ services: command: [ "nginx-debug", "-g", "daemon off;" ] # run in debug mode healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] - interval: "30s" - timeout: "30s" - retries: "3" + interval: "5s" + timeout: "5s" + retries: "20" volumes: data: From 2166290d0b168ec735700c82bcf5e89ed44c14c9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 4 Feb 2026 14:48:39 +0100 Subject: [PATCH 664/878] Send logs and metrics to an OTLP gPRC source --- .env.production.sample | 3 + .env.sample | 3 + .env.staging.sample | 3 + backend/dotnet-tools.json | 7 -- backend/src/AppSettings.cs | 99 ++++++++++--------- ...phQlAuthenticationAndAntiforgeryHandler.cs | 1 - .../src/Controllers/FileUploadController.cs | 2 - backend/src/Data/DbSeeder.cs | 1 - backend/src/Database.csproj | 49 +++++---- .../GraphQl/LoggingDiagnosticEventListener.cs | 7 -- backend/src/Program.cs | 49 ++++++--- backend/src/Services/EmailSender.cs | 1 - backend/src/Startup.cs | 99 +++++++++++++------ docker-compose.production.yml | 4 + docker-compose.yml | 5 + 15 files changed, 200 insertions(+), 133 deletions(-) diff --git a/.env.production.sample b/.env.production.sample index 36c281e4..7b2559f9 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -11,6 +11,9 @@ OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET= +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4317 + VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= diff --git a/.env.sample b/.env.sample index e49473c8..547f61ba 100644 --- a/.env.sample +++ b/.env.sample @@ -13,6 +13,9 @@ OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET=secret +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4317 + VERIFICATION_CODE=secret CERTIFICATE_AUTHORITY_HOST=ca.org diff --git a/.env.staging.sample b/.env.staging.sample index 2a138f14..ca9c253c 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -11,6 +11,9 @@ OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET= +OPEN_TELEMETRY_HOST=host.docker.internal +OPEN_TELEMETRY_GRPC_PORT=4317 + VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index cb3221e8..3f04612e 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -79,13 +79,6 @@ ], "rollForward": false }, - "strawberryshake.tools": { - "version": "15.1.11", - "commands": [ - "dotnet-graphql" - ], - "rollForward": false - }, "dotnet-certificate-tool": { "version": "3.2.0", "commands": [ diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index e0c708ba..42643188 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,15 +1,16 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited using System; +using Microsoft.Extensions.Hosting; namespace Database; -public sealed class AppSettings +public sealed record AppSettings { private const string GraphQlPathSegment = "/graphql/"; private const string WwwSubdomain = "www."; - public string Host { get; private set; } = ""; + public string Host { get; init; } = ""; public Uri HostUri => new(Host, UriKind.Absolute); public Uri NonWwwHostUri => HostUri.Host.StartsWith(WwwSubdomain, StringComparison.OrdinalIgnoreCase) @@ -22,71 +23,71 @@ public sealed class AppSettings // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 public Uri DatabaseGraphQlEndpoint => new UriBuilder(HostUri) { Path = GraphQlPathSegment }.Uri; - public string MetabaseHost { get; private set; } = ""; + public string MetabaseHost { get; init; } = ""; public Uri MetabaseHostUri => new(MetabaseHost, UriKind.Absolute); public Uri MetabaseGraphQlEndpoint => new UriBuilder(MetabaseHostUri) { Path = GraphQlPathSegment }.Uri; - public Guid DatabaseId { get; private set; } - public Guid OperatorId { get; private set; } + public Guid DatabaseId { get; init; } + public Guid OperatorId { get; init; } - public string VerificationCode { get; private set; } - = ""; + public string VerificationCode { get; init; } = ""; - public OpenIdConnectClientSettings OpenIdConnectClient { get; private set; } = new(); + public OpenIdConnectClientSettings OpenIdConnectClient { get; init; } = new(); + public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; init; } = new(); + public LoggingSettings Logging { get; init; } = new(); + public JsonWebTokenSettings JsonWebToken { get; init; } = new(); + public EmailSettings Email { get; init; } = new(); + public DatabaseSettings Database { get; init; } = new(); + public OpenTelemetrySettings OpenTelemetry { get; init; } = new(); - public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; private set; } = new(); - - public LoggingSettings Logging { get; private set; } = new(); - - public JsonWebTokenSettings JsonWebToken { get; private set; } = new(); - - public EmailSettings Email { get; private set; } = new(); - - public DatabaseSettings Database { get; private set; } = new(); - - public sealed class OpenIdConnectClientSettings + public sealed record OpenIdConnectClientSettings { - public string Id { get; private set; } = ""; - public string Secret { get; private set; } = ""; - } + public string Id { get; init; } = ""; + public string Secret { get; init; } = ""; + }; - public sealed class GnupgSecretSigningKeySettings + public sealed record GnupgSecretSigningKeySettings { // Keep file name {FINGERPRINT}.gpg in sync with the one in the GNU // Make target `gpg` in the `Makefile` public string FileName => $"{Fingerprint}.gpg"; - public string Passphrase { get; private set; } = ""; - public string Fingerprint { get; private set; } = ""; - } + public string Passphrase { get; init; } = ""; + public string Fingerprint { get; init; } = ""; + }; - public sealed class LoggingSettings + public sealed record LoggingSettings { - public bool EnableSensitiveDataLogging { get; private set; } - } + public bool EnableSensitiveDataLogging { get; init; } + }; - public sealed class JsonWebTokenSettings + public sealed record JsonWebTokenSettings { - public string EncryptionCertificatePassword { get; private set; } - = ""; - - public string SigningCertificatePassword { get; private set; } - = ""; - } + public string EncryptionCertificatePassword { get; init; } = ""; + public string SigningCertificatePassword { get; init; } = ""; + }; - public sealed class EmailSettings + public sealed record EmailSettings { - public string SmtpHost { get; private set; } - = ""; + public string SmtpHost { get; init; } = ""; + public int SmtpPort { get; init; } + }; - public int SmtpPort { get; private set; } - } - - public sealed class DatabaseSettings + public sealed record DatabaseSettings { - public string ConnectionString { get; set; } - = ""; + public string ConnectionString { get; set; } = ""; + public string SchemaName { get; init; } = ""; + }; - public string SchemaName { get; private set; } - = ""; - } -} \ No newline at end of file + public sealed record OpenTelemetrySettings + { + public string Host { get; init; } = ""; + public int GrpcPort { get; init; } + public Uri GrpcUri => + new UriBuilder( + scheme: "http", + host: Host, + portNumber: GrpcPort + ) + .Uri; + }; +}; \ No newline at end of file diff --git a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs index 56364faa..cfb35a3b 100644 --- a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -13,7 +13,6 @@ namespace Database.Authentication; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Error, Message = "Failed to authenticate the claims principal in the HTTP context.")] internal static partial void FailedToAuthenticate( diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 7ddbd030..8a2225f4 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -24,7 +24,6 @@ namespace Database.Controllers; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Information, Message = "Uploaded file '{FileNameForDisplay}' to '{FilePath}'.")] public static partial void SavedUploadedFile( @@ -34,7 +33,6 @@ string filePath ); [LoggerMessage( - EventId = 1, Level = LogLevel.Error, Message = "Failed to create response approval for data with ID {DataId}.")] public static partial void FailedToCreateResponseApproval( diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index fad4bd56..e7fa1723 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -8,7 +8,6 @@ namespace Database.Data; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Debug, Message = "Seeding the database")] public static partial void SeedingDatabase( diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 26f5731d..ce6dacd9 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -22,34 +22,33 @@ - + - - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + + - @@ -57,13 +56,21 @@ + + + + + + - - + + + + diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs index 745e1f04..e12e1bf0 100644 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs @@ -16,7 +16,6 @@ namespace Database.GraphQl; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Error, Message = "Failed executing the document {Document}.")] public static partial void FailedQueryExecution( @@ -26,7 +25,6 @@ public static partial void FailedQueryExecution( ); [LoggerMessage( - EventId = 1, Level = LogLevel.Error, Message = "Failed executing the operation {Operation} with the error {Error}.")] public static partial void FailedOperationExecution( @@ -37,7 +35,6 @@ string error ); [LoggerMessage( - EventId = 2, Level = LogLevel.Error, Message = "Failed handling the subscription event of the operation {Operation}.")] public static partial void FailedSubscriptionEvent( @@ -47,7 +44,6 @@ IOperation operation ); [LoggerMessage( - EventId = 3, Level = LogLevel.Error, Message = "Failed transporting the subscription of the operation {Operation}.")] public static partial void FailedSubscriptionTransport( @@ -57,7 +53,6 @@ IOperation operation ); [LoggerMessage( - EventId = 4, Level = LogLevel.Error, Message = "The query {Document} has the syntax error {Error}.")] public static partial void FailedSyntax( @@ -68,7 +63,6 @@ string error ); [LoggerMessage( - EventId = 5, Level = LogLevel.Error, Message = "Failed processing the task of kind {Kind} with status {Status} with the error {Error}.")] public static partial void FailedTask( @@ -80,7 +74,6 @@ string error ); [LoggerMessage( - EventId = 6, Level = LogLevel.Error, Message = "Failed validating the query {Document} with the error {Error}.")] public static partial void FailedValidation( diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 40781415..7be7f24c 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -12,16 +12,16 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Serilog; +using Serilog.Enrichers.Span; using Serilog.Events; using Serilog.Formatting.Compact; -using ILogger = Microsoft.Extensions.Logging.ILogger; +using Serilog.Sinks.OpenTelemetry; namespace Database; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Error, Message = "An error occurred creating the database.")] public static partial void FailedToCreateDatabase( @@ -32,7 +32,6 @@ Exception exception ); [LoggerMessage( - EventId = 1, Level = LogLevel.Error, Message = "An error occurred seeding the database.")] public static partial void FailedToSeedDatabase( @@ -46,6 +45,8 @@ Exception exception public sealed class Program { public const string TestEnvironment = "test"; + private const string ProductionEnvironment = "production"; + private const string LogsPath = "./logs/serilog.json"; public static async Task Main( string[] commandLineArguments @@ -54,13 +55,24 @@ string[] commandLineArguments var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? throw new ArgumentException("Unknown enrivornment."); + var openTelemetryHost = new UriBuilder( + scheme: "http", + host: Environment.GetEnvironmentVariable("XBASE_OpenTelemetry__Host") + ?? throw new ArgumentException("Unknown OpenTelemetry host."), + portNumber: int.Parse( + Environment.GetEnvironmentVariable("XBASE_OpenTelemetry__GrpcPort") + ?? throw new ArgumentException("Unknown OpenTelemetry gRPC port."), + CultureInfo.InvariantCulture + ) + ) + .Uri; // https://github.com/serilog/serilog-aspnetcore#two-stage-initialization - ConfigureBootstrapLogging(environment); + ConfigureBootstrapLogging(environment, openTelemetryHost); try { Serilog.Log.Information("Starting web host"); // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis/webapplication - var builder = CreateWebApplicationBuilder(commandLineArguments); + var builder = CreateWebApplicationBuilder(commandLineArguments, openTelemetryHost); var startup = new Startup(builder.Environment, builder.Configuration); startup.ConfigureServices(builder.Services); var application = builder.Build(); @@ -95,33 +107,42 @@ string[] commandLineArguments } private static void ConfigureBootstrapLogging( - string environment + string environment, + Uri openTelemetryHost ) { var configuration = new LoggerConfiguration() .MinimumLevel.Override("Microsoft", LogEventLevel.Information); - ConfigureLogging(configuration, environment); + ConfigureLogging(configuration, environment, openTelemetryHost); Serilog.Log.Logger = configuration.CreateBootstrapLogger(); } private static void ConfigureLogging( LoggerConfiguration configuration, - string environment + string environment, + Uri openTelemetryHost ) { configuration .Enrich.FromLogContext() .Enrich.WithMachineName() .Enrich.WithProperty("Environment", environment) + .Enrich.WithSpan() // add trace context .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) + // inspired by https://last9.io/blog/serilog-and-opentelemetry/ + .WriteTo.OpenTelemetry( + endpoint: openTelemetryHost.AbsoluteUri, + protocol: OtlpProtocol.Grpc + ) .WriteTo.File( new CompactJsonFormatter(), - "./logs/serilog.json", + LogsPath, fileSizeLimitBytes: 1073741824, // 1 GB rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, - retainedFileCountLimit: 7); - if (environment != "production") + retainedFileCountLimit: 7 + ); + if (environment != ProductionEnvironment) { configuration.WriteTo.Debug(formatProvider: CultureInfo.InvariantCulture); } @@ -185,7 +206,8 @@ await services // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host private static WebApplicationBuilder CreateWebApplicationBuilder( - string[] commandLineArguments + string[] commandLineArguments, + Uri openTelemetryHost ) { var builder = WebApplication.CreateBuilder( @@ -213,7 +235,8 @@ string[] commandLineArguments { ConfigureLogging( loggerConfiguration, - webHostBuilderContext.HostingEnvironment.EnvironmentName + webHostBuilderContext.HostingEnvironment.EnvironmentName, + openTelemetryHost ); loggerConfiguration .ReadFrom.Configuration(webHostBuilderContext.Configuration); diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index faf77986..00888cc3 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -10,7 +10,6 @@ namespace Database.Services; public static partial class Log { [LoggerMessage( - EventId = 0, Level = LogLevel.Debug, Message = "About to send email to `{Recipient}` with subject `{Subject}` and body `{Body}`")] public static partial void AboutToSendEmail( diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index a951493f..709d1666 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -26,6 +26,10 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; +using OpenTelemetry.Exporter; +using OpenTelemetry.Logs; +using OpenTelemetry.Metrics; +using OpenTelemetry.Trace; using Scalar.AspNetCore; using Serilog; @@ -44,16 +48,15 @@ IConfiguration configuration }) ?? throw new InvalidOperationException("Failed to get application settings from configuration."); - private readonly IWebHostEnvironment _environment = environment; - public void ConfigureServices(IServiceCollection services) { - AuthConfiguration.ConfigureServices(services, _environment, _appSettings); - GraphQlConfiguration.ConfigureServices(services, _environment); + AuthConfiguration.ConfigureServices(services, environment, _appSettings); + GraphQlConfiguration.ConfigureServices(services, environment); ConfigureDatabaseServices(services); ConfigureMessageSenderServices(services); ConfigureRequestResponseServices(services); - // ConfigureSessionServices(services, _environment); // Not used + // ConfigureSessionServices(services); // Not used + ConfigureTelemetryServices(services); services.AddAntiforgery(_ => { _.HeaderName = AntiforgeryConstants.HeaderName; @@ -61,13 +64,13 @@ public void ConfigureServices(IServiceCollection services) services .AddDataProtection() .PersistKeysToDbContext(); - ConfigureHttpClientServices(services, _environment); + ConfigureHttpClientServices(services); services.AddHttpContextAccessor(); services .AddHealthChecks() .AddDbContextCheck(); services.AddSingleton(_appSettings); - services.AddSingleton(_environment); + services.AddSingleton(environment); // services.AddDatabaseDeveloperPageExceptionFilter(); ConfigureCustomServices(services); } @@ -142,8 +145,7 @@ private void ConfigureMessageSenderServices(IServiceCollection services) } // private static void ConfigureSessionServices( - // IServiceCollection services, - // IWebHostEnvironment environment + // IServiceCollection services // ) // { // // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/app-state#session-state @@ -162,31 +164,68 @@ private void ConfigureMessageSenderServices(IServiceCollection services) // }); // } - private static void ConfigureDatabaseContext( - DbContextOptionsBuilder options, - IWebHostEnvironment environment, - AppSettings appSettings + private void ConfigureTelemetryServices( + IServiceCollection services + ) + { + services.AddOpenTelemetry() + // .WithTracing(_ => + // { + // _.AddAspNetCoreInstrumentation(); + // _.AddHttpClientInstrumentation(); + // _.AddOtlpExporter(_ => + // { + // _.Endpoint = _appSettings.OpenTelemetry.GrpcUri; + // _.Protocol = OtlpExportProtocol.Grpc; + // } + // ); + // if (!environment.IsProduction()) + // { + // _.AddConsoleExporter(); + // } + // } + // ) + .WithMetrics(_ => + { + _.AddAspNetCoreInstrumentation(); // inbound requests + _.AddHttpClientInstrumentation(); // outbound requests + _.AddOtlpExporter(_ => + { + _.Endpoint = _appSettings.OpenTelemetry.GrpcUri; + _.Protocol = OtlpExportProtocol.Grpc; + } + ); + // if (!environment.IsProduction()) + // { + // _.AddConsoleExporter(); + // } + } + ); + } + + private void ConfigureDatabaseContext( + DbContextOptionsBuilder options ) { // https://www.npgsql.org/efcore/mapping/enum.html options .UseNpgsql( - appSettings.Database.ConnectionString, + _appSettings.Database.ConnectionString, _ => _ // Keep version in sync with the one in ./docker-compose.*.yml .SetPostgresVersion(13, 23) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .UseNodaTime() - .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.CoatedSideTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.DataKindTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.IlluminantTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.PublishingStateTypeName, appSettings.Database.SchemaName) - .MapEnum(ApplicationDbContext.StandardizerTypeName, appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.CoatedSideTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.DataKindTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.IlluminantTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.PublishingStateTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.StandardizerTypeName, _appSettings.Database.SchemaName) ) - .UseSchemaName(appSettings.Database.SchemaName) + .UseSchemaName(_appSettings.Database.SchemaName) .UseOpenIddict() .UseProjectables() .UsePostgreSqlTriggers(); @@ -209,9 +248,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) { // Configure the database-context options only once as suggested in // https://github.com/npgsql/efcore.pg/issues/3375#issuecomment-2509746639 - services.AddPooledDbContextFactory(options => - ConfigureDatabaseContext(options, _environment, _appSettings) - ); + services.AddPooledDbContextFactory(ConfigureDatabaseContext); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. services.AddDbContext(options => @@ -219,13 +256,13 @@ private void ConfigureDatabaseServices(IServiceCollection services) contextLifetime: ServiceLifetime.Transient, optionsLifetime: ServiceLifetime.Singleton ); - // services.ConfigureDbContext(options => - // ConfigureDatabaseContext(options, _environment, _appSettings), + // services.ConfigureDbContext( + // ConfigureDatabaseContext, // optionsLifetime: ServiceLifetime.Singleton // ); } - private static void ConfigureHttpClientServices(IServiceCollection services, IWebHostEnvironment environment) + private void ConfigureHttpClientServices(IServiceCollection services) { services.AddHttpClient(); var httpClientBuilder = services.AddHttpClient(ApiRequestService.CustomHttpClient); @@ -255,7 +292,7 @@ public static void ConfigureCustomServices(IServiceCollection services) public void Configure(WebApplication app) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ - if (_environment.IsDevelopment() || _environment.IsEnvironment(Program.TestEnvironment)) + if (environment.IsDevelopment() || environment.IsEnvironment(Program.TestEnvironment)) { app.UseDeveloperExceptionPage(); // app.UseMigrationsEndPoint(); @@ -315,7 +352,7 @@ public void Configure(WebApplication app) Tool = { DisableTelemetry = true, - Enable = true, // _environment.IsDevelopment() + Enable = true, // environment.IsDevelopment() IncludeCookies = false, GraphQLEndpoint = GraphQlConstants.EndpointPath, HttpMethod = DefaultHttpMethod.Post, diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 94c82b24..c54445d2 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -80,9 +80,13 @@ services: XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" + XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" + XBASE_OpenTelemetry__GrpcPort: "${OPEN_TELEMETRY_GRPC_PORT:?required}" # The value `/dev/pts/0` is the output of running `tty` within `shellb` # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html GPG_TTY: "/dev/pts/0" + extra_hosts: + - "host.docker.internal:host-gateway" # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] command: [ "dotnet", "/app/App.dll" ] diff --git a/docker-compose.yml b/docker-compose.yml index 3cc169e4..0d75333f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,6 +110,8 @@ services: XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" + XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" + XBASE_OpenTelemetry__GrpcPort: "${OPEN_TELEMETRY_GRPC_PORT:?required}" # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 @@ -121,6 +123,7 @@ services: # https://www.gnupg.org/documentation/manuals/gnupg/Invoking-GPG_002dAGENT.html GPG_TTY: "/dev/pts/0" extra_hosts: + - "host.docker.internal:host-gateway" - "${HOST:?required}:host-gateway" - "${METABASE_HOST:?required}:host-gateway" # user: "1000:1000" @@ -128,6 +131,7 @@ services: [ "dotnet", "watch", + "--verbose", "--non-interactive", "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx "--project", @@ -144,6 +148,7 @@ services: interval: "5s" timeout: "5s" retries: "20" + stdin_open: true frontend: build: From 8d7027656f72af77560374f2c8b011c1b91f1e79 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 4 Feb 2026 15:09:56 +0100 Subject: [PATCH 665/878] Update JsonPath.Net package --- backend/test/Database.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index e3c9fd25..368a0129 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -18,7 +18,7 @@ - + From b673e5fd3e0735f3ed19b6b6708e09a2c83c1589 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 4 Feb 2026 17:41:45 +0100 Subject: [PATCH 666/878] Add application lifecycle healthcheck --- backend/src/Database.csproj | 1 + backend/src/Startup.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index ce6dacd9..b42c3c21 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -44,6 +44,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 709d1666..2aea924e 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -68,6 +68,7 @@ public void ConfigureServices(IServiceCollection services) services.AddHttpContextAccessor(); services .AddHealthChecks() + .AddApplicationLifecycleHealthCheck() .AddDbContextCheck(); services.AddSingleton(_appSettings); services.AddSingleton(environment); From df4e49d324235fcf7a1971cb5276eab078e17ac1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 09:40:06 +0100 Subject: [PATCH 667/878] Make JSON controller actions show up in OpenAPI document --- resolves https://github.com/building-envelope-data/database/issues/81 --- .../GraphQlAuthenticationAndAntiforgeryHandler.cs | 2 ++ backend/src/Configuration/AuthConfiguration.cs | 5 +++-- backend/src/Controllers/AntiforgeryController.cs | 11 +++++++---- backend/src/Controllers/AuthenticationController.cs | 6 +++--- backend/src/Controllers/FileUploadController.cs | 5 ++++- backend/src/Services/ApiRequestService.cs | 4 +++- backend/src/Startup.cs | 1 + backend/test/Integration/IntegrationTests.cs | 3 ++- docker-compose.yml | 2 +- 9 files changed, 26 insertions(+), 13 deletions(-) diff --git a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs index cfb35a3b..4244bb40 100644 --- a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Immutable; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Database.GraphQl; @@ -32,6 +33,7 @@ private async Task AuthenticateAsync( CancellationToken cancellationToken ) { + httpContext.User = new ClaimsPrincipal(); // clear possibly-existing identities try { var authenticateResult = await authenticationHandler.AuthenticateAsync(httpContext, cancellationToken); diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 62578848..9a3efcdc 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -73,8 +73,9 @@ IServiceCollection services // Inspired by https://github.com/openiddict/openiddict-samples/blob/01cb2ce4600cab15867e34826b0287622e6dd71b/samples/Velusia/Velusia.Client/Startup.cs services.AddAuthentication(_ => { - // To make the various authentication control flows obvious, do not use default - // schemes for anything and always be explicit instead. + // To make the various authentication control flows obvious, do + // not use default schemes for anything and always be explicit + // instead. _.DefaultAuthenticateScheme = null; _.DefaultChallengeScheme = null; _.DefaultForbidScheme = null; diff --git a/backend/src/Controllers/AntiforgeryController.cs b/backend/src/Controllers/AntiforgeryController.cs index c82d8ab2..42c8bbdd 100644 --- a/backend/src/Controllers/AntiforgeryController.cs +++ b/backend/src/Controllers/AntiforgeryController.cs @@ -1,4 +1,6 @@ using System; +using System.Net.Mime; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Database.Authentication; @@ -10,15 +12,14 @@ namespace Database.Controllers; // For gotchas regarding antiforgery tokens read -// [Clarity around IAntiforgery and ValidateAntiForgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) +// [Clarity around IAntiforgery and RequireAntiforgeryToken](https://github.com/dotnet/aspnetcore/issues/2783) +[ApiController] public sealed class AntiforgeryController( IAntiforgery antiforgeryService, AuthenticationHandler authenticationHandler ) : Controller { - private readonly IAntiforgery _antiforgeryService = antiforgeryService; - private readonly CookieOptions _xsrfCookieOptions = new() { @@ -29,16 +30,18 @@ AuthenticationHandler authenticationHandler [HttpGet("~/antiforgery/token")] [EndpointName("AntiforgeryToken")] [EndpointDescription("Get an antiforgery token.")] + [Produces(MediaTypeNames.Application.Json)] public async Task Token( CancellationToken cancellationToken ) { + HttpContext.User = new ClaimsPrincipal(); // clear possibly-existing identities var authenticateResult = await authenticationHandler.AuthenticateAsync(HttpContext, cancellationToken); if (authenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) { HttpContext.User = authenticateResult.Principal; } - var tokens = _antiforgeryService.GetAndStoreTokens(HttpContext); + var tokens = antiforgeryService.GetAndStoreTokens(HttpContext); HttpContext.Response.Cookies.Append( AntiforgeryConstants.CookieKey, tokens.RequestToken ?? diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index b33e59b7..19a5ab0a 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Database.Authentication; using Database.Data; +using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; @@ -15,7 +16,6 @@ using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; -using static OpenIddict.Abstractions.OpenIddictConstants; namespace Database.Controllers; @@ -68,9 +68,9 @@ public ActionResult LogIn(string? returnUrl) ); } - [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [HttpPost("~/connect/logout")] - [ValidateAntiForgeryToken] + [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] + [RequireAntiforgeryToken] public async Task LogOut(string? returnUrl) { // Retrieve the identity stored in the local authentication cookie. If it's not available, diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 8a2225f4..fd71fd6d 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Mime; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -45,6 +46,7 @@ Exception exception // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ [EndpointGroupName("FileUpload")] +[ApiController] public sealed class FileUploadController( ILogger logger ) : Controller @@ -87,13 +89,14 @@ protected override void Dispose(bool disposing) [HttpPost("~/api/upload-file")] [DisableFormValueModelBinding] - // TODO Add this `[ValidateAntiForgeryToken]` once we know where to set the generation token cookie! + // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! // TODO Where to put: [GenerateAntiforgeryTokenCookie] ? // TODO Are both RequestFormLimits and RequestSizeLimit needed? // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#multipart-body-length-limit [RequestFormLimits(MultipartBodyLengthLimit = 10737418240)] // 10 GiB // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#kestrel-maximum-request-body-size [RequestSizeLimit(10737418240)] // 10 GiB + [Produces(MediaTypeNames.Application.Json)] public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, [FromServices] ApplicationDbContext context, diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 9adb9bdd..2227c5dc 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -4,6 +4,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.Mime; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -203,7 +204,8 @@ TContent content JsonSerializerSettings.GraphQl ) ); - result.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + result.Headers.ContentType = + new System.Net.Http.Headers.MediaTypeHeaderValue(MediaTypeNames.Application.Json); return result; } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2aea924e..4ba7af4a 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -330,6 +330,7 @@ public void Configure(WebApplication app) // app.UseCertificateForwarding(); // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0#other-web-proxies app.UseAuthentication(); app.UseAuthorization(); + app.UseAntiforgery(); // app.UseSession(); // Not used // app.UseResponseCompression(); // Done by Nginx // app.UseResponseCaching(); // Done by Nginx diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs index a5673acb..94e3db87 100644 --- a/backend/test/Integration/IntegrationTests.cs +++ b/backend/test/Integration/IntegrationTests.cs @@ -5,6 +5,7 @@ using System.Net; using System.Net.Http; using System.Net.Http.Headers; +using System.Net.Mime; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; @@ -413,7 +414,7 @@ TContent content ) ); result.Headers.ContentType = - new MediaTypeHeaderValue("application/json"); + new MediaTypeHeaderValue(MediaTypeNames.Application.Json); return result; } diff --git a/docker-compose.yml b/docker-compose.yml index 0d75333f..0494394c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -115,7 +115,7 @@ services: # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 - DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "5" + DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "10" # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING: "1" # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER: "all" From 734245b86123d2b077b6ded5b0033607f47dd959 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 13:26:50 +0100 Subject: [PATCH 668/878] Remove file extension and size checks from file uploads and protect the file upload endpoint directly with an Authorize attribute --- README.md | 58 +++--- .../src/Authorization/CommonAuthorization.cs | 6 +- .../Controllers/AuthenticationController.cs | 2 - .../src/Controllers/FileUploadController.cs | 20 +- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/UserService.cs | 15 +- backend/src/Utilities/FileHelpers.cs | 179 +----------------- 7 files changed, 54 insertions(+), 228 deletions(-) diff --git a/README.md b/README.md index 46c271ca..42361668 100644 --- a/README.md +++ b/README.md @@ -117,15 +117,17 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. Create the PostgreSQL database and schema by running `make createdb migrate`. 1. Start all services and follow their logs by running `make up logs`. -1. To see the web frontend navigate to - `https://local.solarbuildingenvelopes.com:5051` in your web browser, to see - the GraphQL API navigate to - `https://local.solarbuildingenvelopes.com:5051/graphql/`, and to see sent - emails navigate to - `https://local.solarbuildingenvelopes.com:5051/email/`. Note that the port is - `5051` by default. If you set the variable `HTTPS_PORT` within the `./.env` - to some other value though, you need to use that value instead within the - URL. +1. In your web browser, navigate to the + - web frontend at `https://local.solarbuildingenvelopes.com:5051`, + - GraphQL API at `https://local.solarbuildingenvelopes.com:5051/graphql/`, + - REST API `https://local.solarbuildingenvelopes.com:5051/openapi/docs//`, + - dummy email server at `https://local.solarbuildingenvelopes.com:5051/email/` + (to view for example the confirmation email sent during registration), + - OpenId Connect configuration navigate to + `https://local.solarbuildingenvelopes.com:5051/.well-known/openid-configuration` + Note that the port is `5051` by default. If you set the variable + `HTTPS_PORT` within the `./.env` to some other value though, you need to use + that value instead within the URLs. In another shell @@ -217,7 +219,7 @@ and choose the process `/app/src/bin/Debug/net10.0/Database run` titled (`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the "Start Debugging" icon (`F5`), and select the same process as above. Then, for example, open some source files to set breakpoints, navigate through the -website https://local.buildingenvelopedata.org:4041, which will stop at +website https://local.solarbuildingenvelopes.com:5051, which will stop at breakpoints, and inspect the information provided by the debugger at the breakpoints. For details on debugging C# in Visual Studio Code, see [Debugging](https://code.visualstudio.com/docs/csharp/debugging). @@ -295,6 +297,8 @@ and the pages following it. ``` 1. For each of the two environments staging and production referred to by `${environment}` below: + 1. Set the variable `environment` by running `environment=staging` or + `environment=production`. 1. Change into the clone `${environment}` by running `cd /app/${environment}`. 1. Open `https://www.buildingenvelopedata.org` in your favorite web browser, log into your account, navigate to the institution operating this @@ -351,31 +355,31 @@ and the pages following it. passwords may be generated by running `openssl rand -base64 32`. Here is some information on what the variables mean - `NAME` is the name Docker project name, in particular, it is the prefix - of the Docker container names listed by `docker ps --all`; + of the Docker container names listed by `docker ps --all`; - `HOST` is the domain name with sub-domain of the deployment, in - particular, it is used to make resource locators absolute; + particular, it is used to make resource locators absolute; - `HTTP_PORT` is the HTTP port to which the reverse proxy NGINX forwards - for HTTPS requests (see `PRODUCTION_HTTP_PORT` and - `STAGING_HTTP_PORT` in the `.env` file of your clone of - [machine](https://github.com/building-envelope-data/machine)); + for HTTPS requests (see `PRODUCTION_HTTP_PORT` and + `STAGING_HTTP_PORT` in the `.env` file of your clone of + [machine](https://github.com/building-envelope-data/machine)); - `METABASE_HOST` is the domain name with sub-domain of the metabase, in - particular, to use it as OpenId Connect provider and to ask it for - information about logged-in users needed for authorization; + particular, to use it as OpenId Connect provider and to ask it for + information about logged-in users needed for authorization; - `DATABASE_ID` is the UUID that was assigned to this product-data - database upon registering it at the metabase; + database upon registering it at the metabase; - `OPERATOR_ID` is the UUID of the institution that operates this - product-data database; + product-data database; - `VERIFICATION_CODE` is the verification code that was generated for - this product-data database upon registering it at the metabase; + this product-data database upon registering it at the metabase; - `OPEN_ID_CONNECT_CLIENT_ID` and `OPEN_ID_CONNECT_CLIENT_SECRET` are the - OpenId Connect client identifier and secret of this product-data - database as a client of the metabase acting as identity provider (the - client secret is given when registering an OpenId Connect client at - the metabase); + OpenId Connect client identifier and secret of this product-data + database as a client of the metabase acting as identity provider (the + client secret is given when registering an OpenId Connect client at + the metabase); - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` and - `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase - of the exported GnuPG secret key for signing in the file - `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`; + `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase + of the exported GnuPG secret key for signing in the file + `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`; - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to encrypt and sign JSON web tokens (JWT) used by OpenId Connect; diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 36915d8f..a929f0ed 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -13,12 +13,12 @@ UserService userService { public Task GetCurrentUserOrApplicationAsync(CancellationToken cancellationToken) { - return userService.GetCurrentUserOrApplicationAsync(cancellationToken); + return userService.FetchCurrentUserOrApplicationAsync(cancellationToken); } public Task IsAuthenticated(CancellationToken cancellationToken) { - return userService.UserOrApplicationAsync( + return userService.SwitchUserOrApplicationAsync( user => Task.FromResult(user is not null), application => Task.FromResult(application is not null), cancellationToken @@ -27,7 +27,7 @@ public Task IsAuthenticated(CancellationToken cancellationToken) public Task IsDatabaseOperator(CancellationToken cancellationToken) { - return userService.UserOrApplicationAsync( + return userService.SwitchUserOrApplicationAsync( user => Task.FromResult( user is not null && user.IsAtLeastAssistantManagerOfDatabaseOperator() diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index 19a5ab0a..c661c6ef 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -12,7 +12,6 @@ using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; using Microsoft.EntityFrameworkCore; using OpenIddict.Abstractions; using OpenIddict.Client.AspNetCore; @@ -21,7 +20,6 @@ namespace Database.Controllers; // Inspired by https://github.com/openiddict/openiddict-samples/blob/dev/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs // https://github.com/openiddict/openiddict-samples/blob/855c31f91d6bf5cde735ef3f96fcc3c015b51d79/samples/Velusia/Velusia.Client/Controllers/AuthenticationController.cs -[EndpointGroupName("Authentication")] public sealed class AuthenticationController( AppSettings appSettings, ApplicationDbContext context diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index fd71fd6d..9aadba7a 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -6,11 +6,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Database.Authentication; using Database.Authorization; using Database.Data; using Database.Filters; using Database.Services; using Database.Utilities; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Mvc; @@ -45,20 +47,15 @@ Exception exception // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ -[EndpointGroupName("FileUpload")] [ApiController] public sealed class FileUploadController( ILogger logger ) : Controller { - private const long FileSizeLimit = 10737418240; // 10 GiB = 10 * 1024 MiB = 10 * 1024 * 1024^2 Byte = 10 * 1024 * 1048576 Byte = 10737418240 Byte - // Get the default form options so that we can use them to set the default // limits for request body data. private static readonly FormOptions s_defaultFormOptions = new(); - private readonly string[] _permittedExtensions = [".json", ".xml", ".txt", ".csv", ".ifc", ".rad", ".svg", ".pdf", ".png"]; - private bool _disposed; protected override void Dispose(bool disposing) @@ -88,14 +85,11 @@ protected override void Dispose(bool disposing) // request header and then falls back to reading the body. [HttpPost("~/api/upload-file")] + [EndpointName("UploadFile")] + [EndpointDescription("Upload file for GET HTTP resource")] [DisableFormValueModelBinding] // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! - // TODO Where to put: [GenerateAntiforgeryTokenCookie] ? - // TODO Are both RequestFormLimits and RequestSizeLimit needed? - // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#multipart-body-length-limit - [RequestFormLimits(MultipartBodyLengthLimit = 10737418240)] // 10 GiB - // See https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#kestrel-maximum-request-body-size - [RequestSizeLimit(10737418240)] // 10 GiB + [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [Produces(MediaTypeNames.Application.Json)] public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, @@ -164,9 +158,7 @@ CancellationToken cancellationToken var streamedFileContent = await FileHelpers.ProcessStreamedFile( section, contentDisposition, - ModelState, - _permittedExtensions, - FileSizeLimit + ModelState ); if (!ModelState.IsValid) { diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index b7994586..d78bd35b 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -48,7 +48,7 @@ CancellationToken cancellationToken IReadOnlyList? institutionIds = null; IReadOnlyList? institutionAccessRights = null; - var currentUserOrApplication = await userService.GetCurrentUserOrApplicationAsync(cancellationToken); + var currentUserOrApplication = await userService.FetchCurrentUserOrApplicationAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectClientId(); if (currentUserOrApplication.CurrentUser is not null) diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index f2e8caf0..7674d803 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,23 +1,16 @@ using System; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using Database.Extensions; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; using OpenIddict.Abstractions; namespace Database.Services; /// -/// Service to get current user from Metabase +/// Service to fetch current user or application from the metabase /// -/// -/// -/// -/// to store already known users. -/// Instance of public sealed class UserService( AppSettings appSettings, ApiRequestService apiRequestService, @@ -33,13 +26,13 @@ CacheService cacheService return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); } - public async Task UserOrApplicationAsync( + public async Task SwitchUserOrApplicationAsync( Func> handleUser, Func> handleApplication, CancellationToken cancellationToken ) { - var userOrApplication = await GetCurrentUserOrApplicationAsync(cancellationToken); + var userOrApplication = await FetchCurrentUserOrApplicationAsync(cancellationToken); if (userOrApplication.CurrentApplication is not null) { return await handleApplication(userOrApplication.CurrentApplication); @@ -53,7 +46,7 @@ CancellationToken cancellationToken /// /// Get current user or OpenId Connect application from Metabase (or the local cache). /// - public async Task GetCurrentUserOrApplicationAsync( + public async Task FetchCurrentUserOrApplicationAsync( CancellationToken cancellationToken ) { diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index e0d8b3e1..42b4013b 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -15,60 +15,12 @@ namespace Database.Utilities; public static class FileHelpers { - // If you require a check on specific characters in the IsValidFileExtensionAndSignature - // method, supply the characters in the _allowedChars field. - // private static readonly byte[] _allowedChars = Array.Empty(); - // For more file signatures, see the File Signatures Database (https://www.filesignatures.net/) - // and the official specifications for the file types you wish to add. - private static readonly Dictionary> _fileSignature = new() - { - { ".gif", new List { "GIF8"u8.ToArray() } }, - { ".png", new List { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } }, - { - ".jpeg", - new List - { - new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 } - } - }, - { - ".jpg", - new List - { - new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 }, - new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 } - } - }, - { - ".zip", - new List - { - new byte[] { 0x50, 0x4B, 0x03, 0x04 }, - "PKLITE"u8.ToArray(), - "PKSpX"u8.ToArray(), - new byte[] { 0x50, 0x4B, 0x05, 0x06 }, - new byte[] { 0x50, 0x4B, 0x07, 0x08 }, - "WinZip"u8.ToArray() - } - } - }; - - // **WARNING!** - // In the following file processing methods, the file's content isn't scanned. - // In most production scenarios, an anti-virus/anti-malware scanner API is - // used on the file before making the file available to users or other - // systems. For more information, see the topic that accompanies this sample - // app. - - public static async Task ProcessFormFile(IFormFile formFile, - ModelStateDictionary modelState, string[] permittedExtensions, - long sizeLimit) + public static async Task ProcessFormFile( + IFormFile formFile, + ModelStateDictionary modelState + ) { var fieldDisplayName = string.Empty; - // Use reflection to obtain the display name for the model // property associated with this IFormFile. If a display // name isn't found, error messages simply won't show @@ -76,7 +28,6 @@ public static async Task ProcessFormFile(IFormFile formFile, var property = typeof(T).GetProperty( formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); - if (property is not null) { if (property.GetCustomAttribute() is @@ -85,37 +36,22 @@ public static async Task ProcessFormFile(IFormFile formFile, fieldDisplayName = $"{displayAttribute.Name} "; } } - // Don't trust the file name sent by the client. To display // the file name, HTML-encode the value. var trustedFileNameForDisplay = WebUtility.HtmlEncode( formFile.FileName); - - // Check the file length. This check doesn't catch files that only have + // Check the file length. This check doesn't catch files that only have // a BOM as their content. if (formFile.Length == 0) { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - return []; } - - if (formFile.Length > sizeLimit) - { - var megabyteSizeLimit = sizeLimit / 1048576; // 1 MiB = 1024^2 Byte = 1048576 Byte - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " + - $"{megabyteSizeLimit:N1} MB."); - - return []; - } - try { using var memoryStream = new MemoryStream(); await formFile.CopyToAsync(memoryStream); - // Check the content length in case the file's only // content was a BOM and the content is actually // empty after removing the BOM. @@ -124,15 +60,6 @@ public static async Task ProcessFormFile(IFormFile formFile, modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); } - - if (!IsValidFileExtensionAndSignature( - formFile.FileName, memoryStream, permittedExtensions)) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) file " + - "type isn't permitted or the file's signature " + - "doesn't match the file's extension."); - } else { return memoryStream.ToArray(); @@ -145,38 +72,23 @@ public static async Task ProcessFormFile(IFormFile formFile, $"Please contact the Help Desk for support. Error: {exception.HResult}"); // Log the exception } - return []; } public static async Task ProcessStreamedFile( - MultipartSection section, ContentDispositionHeaderValue contentDisposition, - ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit) + MultipartSection section, + ContentDispositionHeaderValue contentDisposition, + ModelStateDictionary modelState + ) { try { using var memoryStream = new MemoryStream(); await section.Body.CopyToAsync(memoryStream); - - // Check if the file is empty or exceeds the size limit. if (memoryStream.Length == 0) { modelState.AddModelError("File", "The file is empty."); } - else if (memoryStream.Length > sizeLimit) - { - var megabyteSizeLimit = sizeLimit / 1048576; - modelState.AddModelError("File", - $"The file exceeds {megabyteSizeLimit:N1} MB."); - } - else if (!IsValidFileExtensionAndSignature( - contentDisposition.FileName.Value, memoryStream, - permittedExtensions)) - { - modelState.AddModelError("File", - "The file type isn't permitted or the file's " + - "signature doesn't match the file's extension."); - } else { return memoryStream.ToArray(); @@ -189,79 +101,6 @@ public static async Task ProcessStreamedFile( $" for support. Error: {exception.HResult}"); // Log the exception } - return []; } - - private static bool IsValidFileExtensionAndSignature(string? fileName, Stream data, string[] permittedExtensions) - { - if (string.IsNullOrEmpty(fileName) || data is null || data.Length == 0) - { - return false; - } - - var ext = Path.GetExtension(fileName).ToLowerInvariant(); - - if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext)) - { - return false; - } - - data.Position = 0; - - using var reader = new BinaryReader(data); - // TODO Uncomment the following but allow UTF-X, at least for `.txt` files - // if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn")) - // { - // if (_allowedChars.Length == 0) - // { - // // Limits characters to ASCII encoding. - // for (var i = 0; i < data.Length; i++) - // { - // if (reader.ReadByte() > sbyte.MaxValue) - // { - // return false; - // } - // } - // } - // else - // { - // // Limits characters to ASCII encoding and - // // values of the _allowedChars array. - // for (var i = 0; i < data.Length; i++) - // { - // var b = reader.ReadByte(); - // if (b > sbyte.MaxValue || - // !_allowedChars.Contains(b)) - // { - // return false; - // } - // } - // } - - // return true; - // } - - // Uncomment the following code block if you must permit - // files whose signature isn't provided in the _fileSignature - // dictionary. We recommend that you add file signatures - // for files (when possible) for all file types you intend - // to allow on the system and perform the file signature - // check. - if (!_fileSignature.TryGetValue(ext, out var value)) - { - return true; - } - - // File signature check - // -------------------- - // With the file signatures provided in the _fileSignature - // dictionary, the following code tests the input content's - // file signature. - var signatures = value; - var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length)); - - return signatures.Any(signature => - headerBytes.Take(signature.Length).SequenceEqual(signature)); - } } \ No newline at end of file From 83e99d75deb20b4c9effd7b4e9ee6db3bce81347 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 15:11:33 +0100 Subject: [PATCH 669/878] Improve GraphQL error logging --- .../src/Configuration/GraphQlConfiguration.cs | 4 +- .../ErrorLoggingDiagnosticEventListener.cs | 256 ++++++++++++++++++ .../GraphQl/LoggingDiagnosticEventListener.cs | 230 ---------------- backend/src/Startup.cs | 5 +- 4 files changed, 262 insertions(+), 233 deletions(-) create mode 100644 backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs delete mode 100644 backend/src/GraphQl/LoggingDiagnosticEventListener.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index a2ad7f93..69b551f1 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -111,8 +111,8 @@ await httpContext.RequestServices .HandleAsync(httpContext, cancellationToken); }) .AddDiagnosticEventListener(_ => - new LoggingDiagnosticEventListener( - _.GetRequiredService>() + new ErrorLoggingDiagnosticEventListener( + _.GetRequiredService>() ) ) // Scalar Types diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs new file mode 100644 index 00000000..a714ec2d --- /dev/null +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; +using HotChocolate; +using HotChocolate.Execution; +using HotChocolate.Execution.Instrumentation; +using HotChocolate.Execution.Processing; +using HotChocolate.Resolvers; +using Microsoft.Extensions.Logging; + +namespace Database.GraphQl; + +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Request error. Document: {Document}" + )] + public static partial void RequestError( + this ILogger logger, + Exception exception, + IOperationDocument? document + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Resolver error. Field: {FieldName}" + )] + public static partial void ResolverError( + this ILogger logger, + Exception? exception, + string fieldName, + [LogProperties] IOperation operation, + [LogProperties] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error + )] + public static partial void SubscriptionEventError( + this ILogger logger, + Exception exception, + [LogProperties] IOperation operation + ); + + [LoggerMessage( + Level = LogLevel.Error + )] + public static partial void SubscriptionTransportError( + this ILogger logger, + Exception exception, + [LogProperties] IOperation operation + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Syntax error. Document: {Document}" + )] + public static partial void SyntaxError( + this ILogger logger, + Exception? exception, + IOperationDocument? document, + [LogProperties] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Task error. Kind: {Kind}. Status: {Status}." + )] + public static partial void TaskError( + this ILogger logger, + Exception? exception, + ExecutionTaskKind kind, + ExecutionTaskStatus status, + [LogProperties] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Validation error. Document: {Document}")] + public static partial void ValidationError( + this ILogger logger, + Exception? exception, + IOperationDocument? document, + [LogProperties] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Execution returned an error: {Document} with {Variables}")] + public static partial void OperationError( + this ILogger logger, + IOperationDocument? document, + string? variables, + [LogProperties] IError error + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Unexpected execution error while executing: {Document} with {Variables}")] + public static partial void UnexpectedExecutionException( + this ILogger logger, + IOperationDocument? document, + string? variables, + Exception exception + ); +} + +// Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate +// and https://chillicream.com/blog/2021/01/10/hot-chocolate-logging +public sealed partial class ErrorLoggingDiagnosticEventListener( + ILogger logger +) +: ExecutionDiagnosticEventListener +{ + // this diagnostic event is raised when a request is executed ... + public override IDisposable ExecuteRequest(IRequestContext context) + { + // ... we will return an activity scope that is used to signal when the request is finished. + return new RequestScope(logger, context); + } + + public override void RequestError( + IRequestContext context, + Exception exception + ) + { + logger.RequestError(exception, context.Request.Document); + base.RequestError(context, exception); + } + + public override void ResolverError( + IMiddlewareContext context, + IError error + ) + { + logger.ResolverError(error.Exception, context.Selection.Field.Name, context.Operation, error); + base.ResolverError(context, error); + } + + public override void SubscriptionEventError( + SubscriptionEventContext context, + Exception exception + ) + { + logger.SubscriptionEventError(exception, context.Subscription.Operation); + base.SubscriptionEventError(context, exception); + } + + public override void SubscriptionTransportError( + ISubscription subscription, + Exception exception + ) + { + logger.SubscriptionTransportError(exception, subscription.Operation); + base.SubscriptionTransportError(subscription, exception); + } + + public override void SyntaxError( + IRequestContext context, + IError error + ) + { + logger.SyntaxError(error.Exception, context.Request.Document, error); + base.SyntaxError(context, error); + } + + public override void TaskError( + IExecutionTask task, + IError error + ) + { + logger.TaskError(error.Exception, task.Kind, task.Status, error); + base.TaskError(task, error); + } + + public override void ValidationErrors( + IRequestContext context, + IReadOnlyList errors + ) + { + foreach (var error in errors) + { + logger.ValidationError(error.Exception, context.Request.Document, error); + } + base.ValidationErrors(context, errors); + } + + private sealed partial class RequestScope(ILogger logger, IRequestContext context) : IDisposable + { + [GeneratedRegex(@"apiKey|authKey|privateKey|password|passphrase|secret|secure|security|token", RegexOptions.IgnoreCase, "")] + private static partial Regex SecretRegex(); + + private string? _variables; + + private string? StringifyVariables() + { + if (_variables is not null) + { + return _variables; + } + if (context.Variables is null) + { + return null; + } + StringBuilder stringBuilder = new(); + foreach (var variableValueCollection in context.Variables) + { + foreach (var variableValue in variableValueCollection) + { + try + { + stringBuilder.AppendFormat( + CultureInfo.InvariantCulture, + $"{variableValue.Name} : {variableValue.Type} = " + ); + stringBuilder.Append('\''); + stringBuilder.Append( + SecretRegex().IsMatch(variableValue.Name) + ? "" + : variableValue.Value.ToString() + ); + stringBuilder.Append('\''); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + catch (Exception exception) + { + // all input type records will land here. + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Failed stringifying the value: {exception.Message}"); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + } + } + _variables = stringBuilder.ToString(); + return _variables; + } + + public void Dispose() + { + // when the request is finished it will dispose the activity scope + if (context.Result is IOperationResult { Errors.Count: > 0 } operationResult) + { + foreach (var error in operationResult.Errors) + { + logger.OperationError(context.Request.Document, StringifyVariables(), error); + } + } + if (context.Exception is { }) + { + logger.UnexpectedExecutionException(context.Request.Document, StringifyVariables(), context.Exception); + } + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs b/backend/src/GraphQl/LoggingDiagnosticEventListener.cs deleted file mode 100644 index e12e1bf0..00000000 --- a/backend/src/GraphQl/LoggingDiagnosticEventListener.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.Linq; -using System.Text; -using HotChocolate; -using HotChocolate.Execution; -using HotChocolate.Execution.Instrumentation; -using HotChocolate.Execution.Processing; -using HotChocolate.Resolvers; -using Microsoft.Extensions.Logging; - -namespace Database.GraphQl; - -public static partial class Log -{ - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed executing the document {Document}.")] - public static partial void FailedQueryExecution( - this ILogger logger, - Exception exception, - IOperationDocument? document - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed executing the operation {Operation} with the error {Error}.")] - public static partial void FailedOperationExecution( - this ILogger logger, - Exception? exception, - IOperation operation, - string error - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed handling the subscription event of the operation {Operation}.")] - public static partial void FailedSubscriptionEvent( - this ILogger logger, - Exception exception, - IOperation operation - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed transporting the subscription of the operation {Operation}.")] - public static partial void FailedSubscriptionTransport( - this ILogger logger, - Exception exception, - IOperation operation - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "The query {Document} has the syntax error {Error}.")] - public static partial void FailedSyntax( - this ILogger logger, - Exception? exception, - IOperationDocument? document, - string error - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed processing the task of kind {Kind} with status {Status} with the error {Error}.")] - public static partial void FailedTask( - this ILogger logger, - Exception? exception, - ExecutionTaskKind kind, - ExecutionTaskStatus status, - string error - ); - - [LoggerMessage( - Level = LogLevel.Error, - Message = "Failed validating the query {Document} with the error {Error}.")] - public static partial void FailedValidation( - this ILogger logger, - Exception? exception, - IOperationDocument? document, - string error - ); -} - -// Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate -// and https://chillicream.com/blog/2021/01/10/hot-chocolate-logging -public sealed class LoggingDiagnosticEventListener( - ILogger logger - ) - : ExecutionDiagnosticEventListener -{ - private static Stopwatch s_queryTimer = null!; - private readonly ILogger _logger = logger; - - // this diagnostic event is raised when a request is executed ... - public override IDisposable ExecuteRequest(IRequestContext context) - { - // ... we will return an activity scope that is used to signal when the request is finished. - return new RequestScope(_logger, context); - } - - public override void RequestError( - IRequestContext context, - Exception exception - ) - { - _logger.FailedQueryExecution(exception, context.Request.Document); - } - - public override void ResolverError( - IMiddlewareContext context, - IError error - ) - { - _logger.FailedOperationExecution(error.Exception, context.Operation, ConvertErrorToString(error)); - } - - public override void SubscriptionEventError(SubscriptionEventContext context, Exception exception) - { - _logger.FailedSubscriptionEvent(exception, context.Subscription.Operation); - } - - public override void SubscriptionTransportError(ISubscription subscription, Exception exception) - { - _logger.FailedSubscriptionTransport(exception, subscription.Operation); - } - - public override void SyntaxError( - IRequestContext context, - IError error - ) - { - _logger.FailedSyntax(error.Exception, context.Request.Document, ConvertErrorToString(error)); - } - - public override void TaskError( - IExecutionTask task, - IError error - ) - { - _logger.FailedTask(error.Exception, task.Kind, task.Status, ConvertErrorToString(error)); - } - - public override void ValidationErrors( - IRequestContext context, - IReadOnlyList errors - ) - { - foreach (var error in errors) - { - _logger.FailedValidation(error.Exception, context.Request.Document, ConvertErrorToString(error)); - } - } - - private static string ConvertErrorToString( - IError error - ) - { - return $"{error}(Code {error.Code}, Message {error.Message}, Path {error.Path?.Print()})"; - } - - private sealed class RequestScope : IDisposable - { - private readonly IRequestContext _context; - private readonly ILogger _logger; - - public RequestScope(ILogger logger, IRequestContext context) - { - _logger = logger; - _context = context; - s_queryTimer = new Stopwatch(); - s_queryTimer.Start(); - } - - public void Dispose() - { - // when the request is finished it will dispose the activity scope and - // this is when we print the parsed query. - if (_context.Document is not null) - { - // we just need to do a ToString on the Document which represents the parsed - // GraphQL request document. - StringBuilder stringBuilder = new(_context.Document.ToString(true)); - stringBuilder.AppendLine(); - - if (_context.Variables is not null) - { - var variablesConcrete = _context.Variables!.ToList(); - if (variablesConcrete.Count > 0) - { - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, - $"Variables {Environment.NewLine}"); - foreach (var variableValueCollection in _context.Variables) - { - foreach (var variableValue in variableValueCollection) - { - try - { - stringBuilder.AppendFormat( - CultureInfo.InvariantCulture, - $" {variableValue.Name} : "); - stringBuilder.Append(variableValue.Value.ToString()); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" : {variableValue.Type}"); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - } - catch (Exception exception) - { - // all input type records will land here. - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $" Formatting the variable '{variableValue.Name}' failed with the exception '{exception}'."); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - } - } - } - } - } - - s_queryTimer.Stop(); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, - $"Ellapsed time for query is {s_queryTimer.Elapsed.TotalMilliseconds:0.#} milliseconds."); -#pragma warning disable CA1848 // Use the LoggerMessage delegates -#pragma warning disable CA2254 // Template should be a static expression - _logger.LogInformation(stringBuilder.ToString()); -#pragma warning restore CA2254 // Template should be a static expression -#pragma warning restore CA1848 // Use the LoggerMessage delegates - } - } - } -} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 4ba7af4a..71237d6c 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -328,7 +328,10 @@ public void Configure(WebApplication app) }); app.UseCors(); // app.UseCertificateForwarding(); // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-5.0#other-web-proxies - app.UseAuthentication(); + app.UseWhen(context => context.Request.Path.StartsWithSegments("/connect"), appBuilder => + { + app.UseAuthentication(); + }); app.UseAuthorization(); app.UseAntiforgery(); // app.UseSession(); // Not used From 2687b68b4c107811b3f57aca215c63ec72133056 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 16:05:18 +0100 Subject: [PATCH 670/878] Set autoheal label used by the autoheal service of the machine project --- https://github.com/willfarrell/docker-autoheal?tab=readme-ov-file#docker-compose-example --- docker-compose.production.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docker-compose.production.yml b/docker-compose.production.yml index c54445d2..406f7b35 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -24,6 +24,8 @@ services: interval: "5s" timeout: "5s" retries: "5" + labels: + autoheal: "true" email: image: "rnwood/smtp4dev:v3.10" @@ -48,6 +50,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" backend: build: @@ -97,6 +101,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" frontend: build: @@ -114,6 +120,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" nginx: image: "nginx:1.29-trixie-otel" @@ -138,6 +146,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" volumes: data: From 7f71dab26e1e4197cd1d962e844108c1455710aa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 16:45:38 +0100 Subject: [PATCH 671/878] Pass database information via environment variables instead of appsettings to put them in the same file with the corresponding definitions in the `database` service --- backend/src/appsettings.development.json | 3 --- backend/src/appsettings.json | 5 +---- backend/src/appsettings.production.json | 3 --- docker-compose.production.yml | 2 ++ docker-compose.yml | 2 ++ 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index b44c13df..6b673de1 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -6,8 +6,5 @@ "MinimumLevel": { "Default": "Verbose" } - }, - "Database": { - "ConnectionString": "Host=database; Port=5432; Database=xbase; User Id=postgres; Password=postgres; Maximum Pool Size=90;" } } diff --git a/backend/src/appsettings.json b/backend/src/appsettings.json index 50baee85..4faf77ac 100644 --- a/backend/src/appsettings.json +++ b/backend/src/appsettings.json @@ -1,6 +1,3 @@ { - "AllowedHosts": "*", - "Database": { - "SchemaName": "database" - } + "AllowedHosts": "*" } diff --git a/backend/src/appsettings.production.json b/backend/src/appsettings.production.json index ca27b03a..07899b05 100644 --- a/backend/src/appsettings.production.json +++ b/backend/src/appsettings.production.json @@ -12,8 +12,5 @@ "System": "Error" } } - }, - "Database": { - "ConnectionString": "Host=database; Port=5432; Database=xbase; User Id=postgres; Passfile=/run/secrets/postgres_passwords; Maximum Pool Size=90;" } } diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 406f7b35..c5113ea4 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -75,6 +75,8 @@ services: XBASE_MetabaseHost: "https://${METABASE_HOST:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_Database__SchemaName: "metabase" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database=xbase; User Id=postgres; Passfile=/run/secrets/postgres_passwords; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" diff --git a/docker-compose.yml b/docker-compose.yml index 0494394c..771b310f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -101,6 +101,8 @@ services: XBASE_MetabaseHost: "https://${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_Database__SchemaName: "metabase" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database=xbase; User Id=postgres; Password=postgres; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" From e4098716d9c869c75cf188f311d8bb104e56d949 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 5 Feb 2026 20:02:43 +0100 Subject: [PATCH 672/878] Separate development and production environments cleanly --- .env.sample => .env.development.sample | 0 .github/workflows/{dotnet.yml => dotnet.yaml} | 0 ...new-release.yml => draft-new-release.yaml} | 0 .../{label-issues.yml => label-issues.yaml} | 0 ...w-release.yml => publish-new-release.yaml} | 0 .gitignore | 10 +- .hadolint.yml => .hadolint.yaml | 0 Makefile | 739 ------------------ Makefile.development | 312 ++++++++ Makefile.production | 420 ++-------- README.md | 124 +-- backend/.gitignore | 1 + backend/Dockerfile-bootstrap | 2 +- .../{Dockerfile => Dockerfile.development} | 2 +- ...rfile-production => Dockerfile.production} | 2 +- backend/src/Startup.cs | 2 +- certificates.mk | 262 +++++++ database.mk | 254 ++++++ deploy.mk | 118 +++ ...ose.yml => docker-compose.development.yaml | 8 +- ...tion.yml => docker-compose.production.yaml | 8 +- ...l.sample => .env.local.development.sample} | 0 frontend/.gitignore | 1 + .../{.graphql-let.yml => .graphql-let.yaml} | 0 .../{Dockerfile => Dockerfile.development} | 0 ...rfile-production => Dockerfile.production} | 0 frontend/README.md | 2 +- gpg.mk | 54 ++ 28 files changed, 1148 insertions(+), 1173 deletions(-) rename .env.sample => .env.development.sample (100%) rename .github/workflows/{dotnet.yml => dotnet.yaml} (100%) rename .github/workflows/{draft-new-release.yml => draft-new-release.yaml} (100%) rename .github/workflows/{label-issues.yml => label-issues.yaml} (100%) rename .github/workflows/{publish-new-release.yml => publish-new-release.yaml} (100%) rename .hadolint.yml => .hadolint.yaml (100%) delete mode 100644 Makefile create mode 100644 Makefile.development rename backend/{Dockerfile => Dockerfile.development} (99%) rename backend/{Dockerfile-production => Dockerfile.production} (99%) create mode 100755 certificates.mk create mode 100755 database.mk create mode 100755 deploy.mk rename docker-compose.yml => docker-compose.development.yaml (98%) rename docker-compose.production.yml => docker-compose.production.yaml (97%) rename frontend/{.env.local.sample => .env.local.development.sample} (100%) rename frontend/{.graphql-let.yml => .graphql-let.yaml} (100%) rename frontend/{Dockerfile => Dockerfile.development} (100%) rename frontend/{Dockerfile-production => Dockerfile.production} (100%) create mode 100755 gpg.mk diff --git a/.env.sample b/.env.development.sample similarity index 100% rename from .env.sample rename to .env.development.sample diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yaml similarity index 100% rename from .github/workflows/dotnet.yml rename to .github/workflows/dotnet.yaml diff --git a/.github/workflows/draft-new-release.yml b/.github/workflows/draft-new-release.yaml similarity index 100% rename from .github/workflows/draft-new-release.yml rename to .github/workflows/draft-new-release.yaml diff --git a/.github/workflows/label-issues.yml b/.github/workflows/label-issues.yaml similarity index 100% rename from .github/workflows/label-issues.yml rename to .github/workflows/label-issues.yaml diff --git a/.github/workflows/publish-new-release.yml b/.github/workflows/publish-new-release.yaml similarity index 100% rename from .github/workflows/publish-new-release.yml rename to .github/workflows/publish-new-release.yaml diff --git a/.gitignore b/.gitignore index 6b7a9f17..e1b71c74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,15 @@ +/Makefile +/docker-compose.yaml +*/Dockerfile /examples/*.json /secrets /ssl /machine/*.retry /nginx/html/maintenance.html -/commit -/backup /tags* +/commit -.env +/.env # Compiled output dist @@ -25,6 +27,8 @@ coverage # Backups *.bak +/backup +/backups # Image Diagrams /diagrams/*.png diff --git a/.hadolint.yml b/.hadolint.yaml similarity index 100% rename from .hadolint.yml rename to .hadolint.yaml diff --git a/Makefile b/Makefile deleted file mode 100644 index 1090f3cf..00000000 --- a/Makefile +++ /dev/null @@ -1,739 +0,0 @@ -# Concise introduction to GNU Make: -# https://swcarpentry.github.io/make-novice/reference.html - -include ./.env - -SHELL := /usr/bin/env bash -.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c -MAKEFLAGS += --warn-undefined-variables - -docker_compose = \ - docker compose \ - --file ./docker-compose.yml \ - --env-file ./.env \ - --project-name ${NAME} - -dotenv-linter = \ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/mnt" \ - --pull "always" \ - --quiet \ - dotenvlinter/dotenv-linter:latest - -database_name = xbase - -dump_archive_name = postgresql_dumpall.gz - -# Taken from https://www.client9.com/self-documenting-makefiles/ -help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ - printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ - }' $(MAKEFILE_LIST) -.PHONY : help -.DEFAULT_GOAL := help - -name : ## Print value of variable `NAME` - @echo ${NAME} -.PHONY : name - -dotenv : ## Assert that all variables in `./.env.sample` are available in `./.env` - ${dotenv-linter} diff /mnt/.env /mnt/.env.sample - ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.sample - ${dotenv-linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample - ${dotenv-linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample -.PHONY : dotenv - -# ----------------------------- # -# Interface with Docker Compose # -# ----------------------------- # - -config : ## Parse, resolve and render compose file in canonical format - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} config -.PHONY : config - -check : ## Check build configuration - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --check \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) -.PHONY : check - -pull : ## Pull images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull -.PHONY : pull - -# To debug errors during build add `--progress plain \` to get additional -# output. -build : dotenv check pull ## Build images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) -.PHONY : build - -build-bootstrap : ## Build the bootstrap image - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - --tag ${NAME}_bootstrap \ - --file ./backend/Dockerfile-bootstrap \ - ./backend -.PHONY : build-bootstrap - -bake : ## Print docker-compose file equivalent bake file - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --print \ - --pull - # --no-cache -.PHONY : bake - -backend-build-context : ## Show the build context configured by `./backend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file Dockerfile-show-build-context \ - ./backend -.PHONY : backend-build-context - -frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file Dockerfile-show-build-context \ - ./frontend -.PHONY : frontend-build-context - -remove : ## Remove stopped containers - ${docker_compose} rm \ - --volumes -.PHONY : remove - -remove-data : ## Remove data volumes - docker volume rm \ - ${NAME}_data -.PHONY : remove-data - -# TODO `docker compose up` does not support `--user`, see https://github.com/docker/compose/issues/1532 -up : build ## (Re)create, and start containers (after building images if necessary) - ${docker_compose} up \ - --remove-orphans \ - --wait -.PHONY : up - -down : ## Stop containers and remove containers and networks created by `up` and clear backend logs - ${docker_compose} down \ - --remove-orphans - -rm ./frontend/queries/*.generated.ts -.PHONY : down - -restart : ## Restart all stopped and running containers - ${docker_compose} restart -.PHONY : restart - -restartb : ## Restart the backend container - ${docker_compose} restart backend -.PHONY : restartb - -prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes - docker system prune \ - --volumes -.PHONY : prune - -logs : ## Follow logs - ${docker_compose} logs \ - --since=1h \ - --follow -.PHONY : logs - -exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - ${CONTAINER} - ${docker_compose} exec \ - --user $(shell id --user):$(shell id --group) \ - ${CONTAINER} \ - ${COMMAND} -.PHONY : exec - -execf : CONTAINER = frontend -execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container -.PHONY : execf - -execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container -.PHONY : execb - -run : ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container - ${docker_compose} run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - ${CONTAINER} \ - ${COMMAND} -.PHONY : run - -runf : CONTAINER = frontend -runf : run ## Run the one-time command `${COMMAND}` against a fresh `frontend` container -.PHONY : runf - -runb : CONTAINER = backend -runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container -.PHONY : runb - -shellf : COMMAND = bash -shellf : execf ## Enter shell in the `frontend` container -.PHONY : shellf - -shellb : COMMAND = bash -shellb : runb ## Enter shell in a fresh `backend` container -.PHONY : shellb - -shellb-examples : COMMAND = bash -c "cd ./examples && bash" -shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in the `backend` container -.PHONY : shellb-examples - -# Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md -traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` - ${docker_compose} up \ - --remove-orphans \ - --wait \ - backend - ${docker_compose} exec \ - --privileged \ - backend \ - bash -c " \ - make trace \ - " -.PHONY : traceb - -shelln : ## Enter shell in the `nginx` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - nginx - ${docker_compose} exec \ - nginx \ - bash -.PHONY : shelln - -psql : ## Enter PostgreSQL interactive terminal in the running `database` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - ${docker_compose} exec \ - database \ - psql \ - --username postgres \ - --dbname ${database_name} -.PHONY : psql - -shelld : CONTAINER = database -shelld : COMMAND = bash -shelld : exec ## Enter shell in the `database` container -.PHONY : shelld - -list : ## List all containers with health status - ${docker_compose} ps \ - --no-trunc \ - --all -.PHONY : list - -dclint = \ - docker run \ - --rm \ - --tty \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/app" \ - --pull "always" \ - --quiet \ - zavoloklom/dclint:latest \ - --config /app/.dclintrc - -hadolint = \ - docker run \ - --rm \ - --interactive \ - --user $(shell id --user):$(shell id --group) \ - --volume ./.hadolint.yml:/.config/.hadolint.yaml \ - --pull "always" \ - --quiet \ - hadolint/hadolint:latest \ - hadolint \ - --config /.config/.hadolint.yaml - -# docker run \ -# --workdir / \ -# --volume ./checkmake.ini:/checkmake.ini \ -# --volume ./Makefile:/Makefile \ -# --volume ./Makefile.production:/Makefile.production \ -# --volume ./backend/Makefile:/Makefile.backend \ -# --volume ./frontend/Makefile:/Makefile.frontend \ -# quay.io/checkmake/checkmake \ -# /Makefile \ -# /Makefile.production \ -# /Makefile.backend \ -# /Makefile.frontend -lint : ## Lint .env files, Docker Compose files, and Dockerfiles - @echo Lint .env Files - ${dotenv-linter} \ - check \ - --recursive \ - --ignore-checks UnorderedKey \ - . - @echo Lint Docker Compose Files - ${dclint} . - @echo Lint Dockerfiles - for dockerfile in $(shell find . -name "Dockerfile*"); do \ - echo "... $${dockerfile}" \ - ${hadolint} - < $${dockerfile} ; \ - done -.PHONY : lint - -fix : ## Fix .env files and Docker Compose linting violations - @echo Fix .env Files - ${dotenv-linter} \ - fix \ - --no-backup \ - --recursive \ - --ignore-checks UnorderedKey \ - . - @echo Fix Docker Compose Files - ${dclint} --fix . -.PHONY : fix - -format : ## Format Dockerfiles - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/pwd" \ - --pull "always" \ - --quiet \ - ghcr.io/reteps/dockerfmt:latest \ - --indent 2 \ - --newline \ - --write \ - $(shell find . -name "Dockerfile*" -printf "/pwd/%h/%f ") -.PHONY : format - -createdb : DBNAME = ${database_name} -createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - ${docker_compose} exec \ - database \ - bash -c " \ - createdb --username postgres ${DBNAME} ; \ - " -.PHONY : createdb - -dropdb : DBNAME = ${database_name} -dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - ${docker_compose} exec \ - database \ - bash -c " \ - dropdb --username postgres ${DBNAME} ; \ - " -.PHONY : dropdb - -sql : ## Run the SQL script in the file `${SQL}` in the running `database` service, for example, `make SQL=./my.sql sql` - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - cat ${SQL} \ - | ${docker_compose} exec \ - --no-TTY \ - database \ - psql \ - --echo-all \ - --set=ON_ERROR_STOP=1 \ - --file=- \ - --username=postgres \ - --dbname=${database_name} -.PHONY : sql - -migrate : SQL = ./backend/src/Migrations/migrate.sql -migrate : sql ## Migrate the database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql -.PHONY : migrate - -# Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL -# Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html -backup : CONTAINER_NAME = backup_${NAME}_database -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=./backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` - mkdir --parents ${BACKUP_DIRECTORY} - ${docker_compose} down \ - --remove-orphans \ - database - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ - --name ${CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - docker exec \ - ${CONTAINER_NAME} \ - pg_dumpall \ - --clean \ - --username=postgres \ - | gzip \ - > ${BACKUP_DIRECTORY}/${dump_archive_name} - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database -.PHONY : backup - -restore : CONTAINER_NAME = restore_${NAME}_database -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=./backups/2021-04-22_15_43_35/ restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` - ${docker_compose} down \ - --remove-orphans \ - database - docker volume rm \ - ${NAME}_data - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ - --name ${CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ - | docker exec \ - --interactive \ - ${CONTAINER_NAME} \ - psql \ - --echo-all \ - --set=ON_ERROR_STOP=1 \ - --file=- \ - --username=postgres \ - --dbname=postgres - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database -.PHONY : restore - -begin-maintenance : ## Begin maintenance - cp \ - ./nginx/html/maintenance.off.html \ - ./nginx/html/maintenance.html -.PHONY : begin-maintenance - -end-maintenance : ## End maintenance - rm ./nginx/html/maintenance.html -.PHONY : begin-maintenance - -# Keep file name .gpg in sync with the one in `AppSettings.cs` -gpg : COMMENT = -gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ - bash -ceuxo pipefail " \ - gpg \ - --quick-generate-key \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - ed25519 \ - sign \ - never && \ - fingerprint=\$$(gpg \ - --list-secret-keys \ - --with-colons \ - --keyid-format=long \ - ${EMAIL} \ - | grep \ - --before=3 \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ - echo fingerprint=\$${fingerprint} && \ - mkdir --parents \ - ./backend/src/gpg-keys && \ - gpg \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - --armor \ - --export-secret-keys \$${fingerprint} \ - > /app/src/gpg-keys/\$${fingerprint}.gpg \ - " - -# --------------------- # -# Generate Certificates # -# --------------------- # - -# TODO Pass passwords in a more secure way! -jwt-certificates : build-bootstrap ## Create JWT encryption and signing certificates if necessary - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ - bash -ceux " \ - dotnet-script \ - /app/create-certificates.csx \ - -- \ - ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ - ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ - " -.PHONY : jwt-certificates - -# For an introduction to how HTTPS works see https://howhttps.works -ssl : ## Generate and trust certificate authority, and generate SSL certificates - $(MAKE) generate-certificate-authority - $(MAKE) generate-ssl-certificate - $(MAKE) trust-certificate-authority -.PHONY : ssl - -# Creating Self-Signed ECDSA SSL Certificate using OpenSSL: http://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/ -# See also https://gist.github.com/Soarez/9688998 -# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm -# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 -# Which file extensions are meant for which file layout? https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file/9717#9717 -generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate - mkdir --parents ./ssl/ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.29-trixie-otel \ - bash -ceux " \ - echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ - openssl ecparam \ - -genkey \ - -name secp384r1 \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key && \ - echo \"# Check and print the private key's elliptic curve parameters\" && \ - openssl ecparam \ - -check \ - -text \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -noout && \ - echo \"# Generate the PKCS#10 certificate request '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req' with common name '${CERTIFICATE_AUTHORITY_HOST}' from the private key\" && \ - openssl req \ - -new \ - -subj \"${CERTIFICATE_AUTHORITY_SUBJECT}/CN=${CERTIFICATE_AUTHORITY_HOST}\" \ - -key /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req && \ - echo \"# Verify and print the request\" && \ - openssl req \ - -verify \ - -text \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ - -noout && \ - echo \"# Convert the request into the self-signed certificate '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt'\" && \ - openssl x509 \ - -req \ - -trustout \ - -days 365 \ - -extfile <(printf ' \ - basicConstraints = critical, CA:TRUE, pathlen:0\n \ - subjectKeyIdentifier = hash\n \ - authorityKeyIdentifier = keyid:always, issuer:always\n \ - subjectAltName = DNS:${CERTIFICATE_AUTHORITY_HOST}\n \ - issuerAltName = issuer:copy\n \ - keyUsage = critical, cRLSign, digitalSignature, keyCertSign\n \ - ') \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ - -signkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ - echo \"# Print and Verify the self-signed certificate\" && \ - openssl x509 \ - -text \ - -purpose \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -noout && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ - echo \"# Create the PKCS#12 file '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx' from the self-signed certificate\" && \ - openssl pkcs12 \ - -export \ - -passout pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -inkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx && \ - echo \"# Verify the PKCS#12 file\" && \ - ( \ - openssl pkcs12 \ - -info \ - -passin pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ - -noout && \ - echo \"PKCS#12 file is valid\" && \ - exit 0 \ - ) || echo \"PFX file is invalid\" \ - " - mkdir --parents ./backend/ssl - cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./backend/ssl - mkdir --parents ./frontend/ssl - cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./frontend/ssl -.PHONY : generate-certificate-authority - -# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 -# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -# and https://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu/719047#719047 -# For debugging purposes, the following commands can be helpful -# cat /etc/ssl/certs/ca-certificates.crt -# cat ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -# sudo cat /etc/ssl/certs/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pem -# Note that Firefox and Google Chrome use their own certificate stores: -# * Firefox: https://www.cyberciti.biz/faq/firefox-adding-trusted-ca/ -# * Google Chrome: https://rshankar.com/blog/2010/07/08/how-to-import-a-certificate-in-google-chrome/ -trust-certificate-authority : ## Trust the authority's SSL certificate - sudo cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt /usr/local/share/ca-certificates - sudo update-ca-certificates - openssl verify ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -.PHONY : trust-certificate-authority - -# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 -# and https://superuser.com/questions/226192/avoid-password-prompt-for-keys-and-prompts-for-dn-information/226229#226229 -# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -# For an explanation of the distinction between `cert` and `pfx` files, see -# https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 -# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm -# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 -# What are PKCS#12 files? https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 -# Process substitution `<( ... )`: https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html -# Note that extensions are not transferred to certificate requests and vice versa as said on https://www.openssl.org/docs/man1.1.0/man1/x509.html#BUGS -generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate signed by our certificate authority - mkdir --parents ./ssl/ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.29-trixie-otel \ - bash -ceux " \ - echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ - openssl ecparam \ - -genkey \ - -name secp384r1 \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key && \ - echo \"# Check and print the private key's elliptic curve parameters\" && \ - openssl ecparam \ - -check \ - -text \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -noout && \ - echo \"# Generate the PKCS#10 certificate request '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req' with common name '${HOST}' from the private key\" && \ - openssl req \ - -new \ - -subj \"${SSL_CERTIFICATE_SUBJECT}/CN=${HOST}\" \ - -key /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req && \ - echo \"# Verify and print the request\" && \ - openssl req \ - -verify \ - -text \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ - -noout && \ - echo \"# Sign the request with certificate authority '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt' and key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' resulting in the signed certificate '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt'\" && \ - openssl x509 \ - -req \ - -days 365 \ - -CA /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -CAkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -CAcreateserial \ - -extfile <(printf ' \ - basicConstraints = critical, CA:FALSE\n \ - subjectKeyIdentifier = hash\n \ - authorityKeyIdentifier = keyid:always, issuer:always\n \ - subjectAltName = DNS:${HOST}\n \ - issuerAltName = issuer:copy\n \ - keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement\n \ - extendedKeyUsage = critical, clientAuth, serverAuth\n \ - ') \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ - echo \"# Print and Verify the signed certificate\" && \ - openssl x509 \ - -text \ - -purpose \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - -noout && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ - echo \"# Chain the certificate and the certificate authority's certificate in that order, see http://nginx.org/en/docs/http/configuring_https_servers.html#chains\" && \ - cat \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - > /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ - echo \"# Verify the chained certificate\" && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ - echo \"# Create the PKCS#12 file chain '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx' from the un-chained signed certificate\" && \ - openssl pkcs12 \ - -export \ - -chain \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -passout pass:${SSL_CERTIFICATE_PASSWORD} \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - -inkey /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx && \ - echo \"# Verify the PKCS#12 file\" && \ - ( \ - openssl pkcs12 \ - -info \ - -passin pass:${SSL_CERTIFICATE_PASSWORD} \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx \ - -noout && \ - echo \"PKCS#12 file is valid\" && \ - exit 0 \ - ) || echo \"PFX file is invalid\" \ - " -.PHONY : generate-ssl-certificate - -fetch-ssl-certificate : ## Fetch the SSL certificate of the server - openssl s_client ${HOST}:${HTTPS_PORT} -.PHONY : fetch-ssl-certificate - -ssl-certificate : ## Print the SSL certificate - openssl x509 -text -noout -in ./ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt -.PHONY : ssl-certificate - -certificate-authority : ## View the certificate authority - openssl x509 -text -noout -in ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -.PHONY : certificate-authority diff --git a/Makefile.development b/Makefile.development new file mode 100644 index 00000000..bef471b0 --- /dev/null +++ b/Makefile.development @@ -0,0 +1,312 @@ +# Concise introduction to GNU Make: +# https://swcarpentry.github.io/make-novice/reference.html + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +docker_compose = \ + docker compose \ + --file ./docker-compose.development.yaml \ + --env-file ./.env + +dotenv-linter = \ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/mnt:ro" \ + --pull "always" \ + --quiet \ + dotenvlinter/dotenv-linter:latest + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +name : ## Print value of variable `NAME` + @echo ${NAME} +.PHONY : name + +dotenv : ## Assert that all variables in `./.env.development.sample` are available in `./.env` + ${dotenv-linter} diff /mnt/.env /mnt/.env.development.sample + ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.development.sample + ${dotenv-linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample + ${dotenv-linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample +.PHONY : dotenv + +config : ## Parse, resolve and render compose file in canonical format + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} config +.PHONY : config + +check : ## Check build configuration + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --check \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) +.PHONY : check + +pull : ## Pull images + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} pull +.PHONY : pull + +# To debug errors during build add `--progress plain \` to get additional +# output. +build : dotenv check pull ## Build images + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) +.PHONY : build + +bake : ## Print docker-compose file equivalent bake file + COMPOSE_BAKE=true \ + COMPOSE_DOCKER_CLI_BUILD=1 \ + DOCKER_BUILDKIT=1 \ + ${docker_compose} build \ + --print \ + --pull + # --no-cache +.PHONY : bake + +backend-build-context : ## Show the build context configured by `./backend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./backend +.PHONY : backend-build-context + +frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./frontend +.PHONY : frontend-build-context + +remove : ## Remove stopped containers + ${docker_compose} rm \ + --volumes +.PHONY : remove + +remove-data : ## Remove data volumes + docker volume rm \ + ${NAME}_data +.PHONY : remove-data + +# TODO `docker compose up` does not support `--user`, see https://github.com/docker/compose/issues/1532 +up : build ## (Re)create, and start containers (after building images if necessary) + ${docker_compose} up \ + --remove-orphans \ + --wait +.PHONY : up + +down : ## Stop containers and remove containers and networks created by `up` + ${docker_compose} down \ + --remove-orphans + -rm ./frontend/queries/*.generated.ts +.PHONY : down + +restart : ## Restart all stopped and running containers + ${docker_compose} restart +.PHONY : restart + +restartb : ## Restart the backend container + ${docker_compose} restart backend +.PHONY : restartb + +attachb : ## Attach to the backend container (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) + ${docker_compose} attach backend +.PHONY : attachb + +prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes + docker system prune \ + --volumes +.PHONY : prune + +logs : ## Follow logs + ${docker_compose} logs \ + --since=1h \ + --follow +.PHONY : logs + +exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container + ${docker_compose} up \ + --remove-orphans \ + --wait \ + ${CONTAINER} + ${docker_compose} exec \ + --user $(shell id --user):$(shell id --group) \ + ${CONTAINER} \ + ${COMMAND} +.PHONY : exec + +execb : CONTAINER = backend +execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container +.PHONY : execb + +execf : CONTAINER = frontend +execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container +.PHONY : execf + +run : ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container + ${docker_compose} run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + ${CONTAINER} \ + ${COMMAND} +.PHONY : run + +runb : CONTAINER = backend +runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container +.PHONY : runb + +runf : CONTAINER = frontend +runf : run ## Run the one-time command `${COMMAND}` against a fresh `frontend` container +.PHONY : runf + +shellb : COMMAND = bash +shellb : runb ## Enter shell in a fresh `backend` container +.PHONY : shellb + +shellf : COMMAND = bash +shellf : execf ## Enter shell in the `frontend` container +.PHONY : shellf + +shellb-examples : COMMAND = bash -c "cd ./examples && bash" +shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in the `backend` container +.PHONY : shellb-examples + +# Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md +traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` + ${docker_compose} up \ + --remove-orphans \ + --wait \ + backend + ${docker_compose} exec \ + --privileged \ + backend \ + bash -c " \ + make trace \ + " +.PHONY : traceb + +shelln : ## Enter shell in the `nginx` container + ${docker_compose} up \ + --remove-orphans \ + --wait \ + nginx + ${docker_compose} exec \ + nginx \ + bash +.PHONY : shelln + +shelld : CONTAINER = database +shelld : COMMAND = bash +shelld : exec ## Enter shell in the `database` container +.PHONY : shelld + +list : ## List all containers with health status + ${docker_compose} ps \ + --no-trunc \ + --all +.PHONY : list + +dclint = \ + docker run \ + --rm \ + --tty \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/app" \ + --pull "always" \ + --quiet \ + zavoloklom/dclint:latest \ + --config /app/.dclintrc + +hadolint = \ + docker run \ + --rm \ + --interactive \ + --user $(shell id --user):$(shell id --group) \ + --volume ./.hadolint.yaml:/.config/.hadolint.yaml \ + --pull "always" \ + --quiet \ + hadolint/hadolint:latest \ + hadolint \ + --config /.config/.hadolint.yaml + +# docker run \ +# --workdir / \ +# --volume ./checkmake.ini:/checkmake.ini \ +# --volume ./Makefile.development:/Makefile.development \ +# --volume ./Makefile.production:/Makefile.production \ +# --volume ./backend/Makefile:/Makefile.backend \ +# --volume ./frontend/Makefile:/Makefile.frontend \ +# quay.io/checkmake/checkmake \ +# /Makefile.development \ +# /Makefile.production \ +# /Makefile.backend \ +# /Makefile.frontend +lint : ## Lint .env files, Docker Compose files, and Dockerfiles + @echo Lint .env Files + ${dotenv-linter} \ + check \ + --recursive \ + --ignore-checks UnorderedKey \ + . + @echo Lint Docker Compose Files + ${dclint} . + @echo Lint Dockerfiles + for dockerfile in $(shell find . -name "Dockerfile*"); do \ + echo "... $${dockerfile}" \ + ${hadolint} - < $${dockerfile} ; \ + done +.PHONY : lint + +fix : ## Fix .env files and Docker Compose linting violations + @echo Fix .env Files + ${dotenv-linter} \ + fix \ + --no-backup \ + --recursive \ + --ignore-checks UnorderedKey \ + . + @echo Fix Docker Compose Files + ${dclint} --fix . +.PHONY : fix + +format : ## Format Dockerfiles + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/pwd" \ + --pull "always" \ + --quiet \ + ghcr.io/reteps/dockerfmt:latest \ + --indent 2 \ + --newline \ + --write \ + $(shell find . -name "Dockerfile*" -printf "/pwd/%h/%f ") +.PHONY : format diff --git a/Makefile.production b/Makefile.production index 8e7c58b9..a660c4c9 100644 --- a/Makefile.production +++ b/Makefile.production @@ -9,9 +9,8 @@ MAKEFLAGS += --warn-undefined-variables docker_compose = \ docker compose \ - --file docker-compose.production.yml \ - --env-file ./.env \ - --project-name ${NAME} + --file ./docker-compose.production.yaml \ + --env-file ./.env dotenv-linter = \ docker run \ @@ -22,9 +21,6 @@ dotenv-linter = \ --quiet \ dotenvlinter/dotenv-linter:latest -dump_archive_name = postgresql_dumpall.gz -files_archive_name = files.gz - # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -54,7 +50,9 @@ check : ## Check build configuration COMPOSE_DOCKER_CLI_BUILD=1 \ DOCKER_BUILDKIT=1 \ ${docker_compose} build \ - --check + --check \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : check pull : ## Pull images @@ -64,6 +62,8 @@ pull : ## Pull images ${docker_compose} pull .PHONY : pull +# To debug errors during build add `--progress plain \` to get additional +# output. build : dotenv check pull ## Build images COMPOSE_BAKE=true \ COMPOSE_DOCKER_CLI_BUILD=1 \ @@ -91,13 +91,47 @@ bake : ## Print docker-compose file equivalent bake file # --no-cache .PHONY : bake +backend-build-context : ## Show the build context configured by `./backend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./backend +.PHONY : backend-build-context + +frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./frontend +.PHONY : frontend-build-context + +remove : ## Remove stopped containers + ${docker_compose} rm \ + --volumes +.PHONY : remove + +remove-data : ## Remove data volumes (after creating a backup in ./backup) + $(MAKE) \ + --file="$(shell pwd)/database.mk" \ + BACKUP_DIRECTORY="$(shell pwd)/backup" \ + backup + docker volume rm \ + ${NAME}_data +.PHONY : remove-data + up : dotenv ## (Re)create and start containers ${docker_compose} up \ --remove-orphans \ --wait .PHONY : up -down : ## Stop containers and remove containers, networks, volumes, and images created by `up` +down : ## Stop containers and remove containers and networks created by `up` ${docker_compose} down \ --remove-orphans .PHONY : down @@ -106,9 +140,14 @@ restart : ## Restart all stopped and running containers ${docker_compose} restart .PHONY : restart +prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes + docker system prune \ + --volumes +.PHONY : prune + logs : ## Follow logs ${docker_compose} logs \ - --since=24h \ + --since=1h \ --follow .PHONY : logs @@ -118,6 +157,7 @@ exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` c --wait \ ${CONTAINER} ${docker_compose} exec \ + --user $(shell id --user):$(shell id --group) \ ${CONTAINER} \ ${COMMAND} .PHONY : exec @@ -138,14 +178,20 @@ shellf : COMMAND = bash shellf : execf ## Enter shell in a running `frontend` container .PHONY : shellf -shelln : CONTAINER = nginx -shelln : COMMAND = bash -shelln : exec ## Enter shell in a running `nginx` container +shelln : ## Enter shell in the `nginx` container + ${docker_compose} up \ + --remove-orphans \ + --wait \ + nginx + ${docker_compose} exec \ + nginx \ + bash +.PHONY : shelln .PHONY : shelln shelld : CONTAINER = database shelld : COMMAND = bash -shelld : exec ## Enter shell in a running `database` container +shelld : exec ## Enter shell in the `database` container .PHONY : shelld list : ## List all containers with health status @@ -158,351 +204,3 @@ list-services : ## List all services specified in the docker-compose file (used ${docker_compose} config \ --services .PHONY : list-services - -createdb : CONTAINER_NAME = create_${NAME}_database -createdb : ## Create database - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ - --name ${CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - docker exec \ - ${CONTAINER_NAME} \ - createdb \ - --username postgres \ - xbase - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} -.PHONY : createdb - -psql : ## Enter PostgreSQL interactive terminal in the running `database` service - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - ${docker_compose} exec \ - database \ - psql \ - --username=postgres \ - --dbname=xbase -.PHONY : psql - -# TODO The entrypoint file of the PostgreSQL image uses the file refered to by `POSTGRES_PASSWORD_FILE` and this file needs to be accessible by `other`. Why? We do not want all people to be able to read secrets! -postgres_password : ## Generate PostgreSQL password file with nothing but one password in plain text (note that if the data volume already exists, then it either needs to be removed resulting in the loss of all data or the password of the PostgreSQL user needs to be changed manually by executing the SQL query `ALTER USER postgres with password '...'`) - mkdir -p ./secrets - chmod 0755 ./secrets - touch ./secrets/postgres_password - chmod 0644 ./secrets/postgres_password - openssl rand -base64 32 \ - > ./secrets/postgres_password -.PHONY : postgres_password - # | openssl md5 \ - # | awk '{print $$2}' \ - -# https://www.postgresql.org/docs/current/libpq-pgpass.html -postgres_passwords : postgres_password ## Generate PostgreSQL passwords file whose entries are of the form `hostname:port:database:username:password` (note that if the data volume already exists, then it either needs to be removed resulting in the loss of all data or the password of the PostgreSQL user needs to be changed manually by executing the SQL query `ALTER USER postgres with password '...'`) - mkdir -p ./secrets - chmod 0755 ./secrets - touch ./secrets/postgres_passwords - chmod 0600 ./secrets/postgres_passwords - echo "*:*:*:*:$$(cat ./secrets/postgres_password)" \ - > ./secrets/postgres_passwords -.PHONY : postgres_passwords - -# Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL -# Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html -# Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes -# We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ -backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database -backup : FILES_CONTAINER_NAME = backup_${NAME}_files -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` - mkdir --parents ${BACKUP_DIRECTORY} - ${docker_compose} down \ - --remove-orphans \ - database - -docker container stop ${DATABASE_CONTAINER_NAME} - -docker container rm --volumes ${DATABASE_CONTAINER_NAME} - ${docker_compose} run \ - --name ${DATABASE_CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - docker exec \ - ${DATABASE_CONTAINER_NAME} \ - pg_dumpall \ - --clean \ - --username=postgres \ - | gzip \ - > ${BACKUP_DIRECTORY}/${dump_archive_name} - docker container stop ${DATABASE_CONTAINER_NAME} - docker container rm --volumes ${DATABASE_CONTAINER_NAME} - -docker container stop ${FILES_CONTAINER_NAME} - -docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} run \ - --name ${FILES_CONTAINER_NAME} \ - --detach \ - backend - docker run \ - --rm \ - --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${BACKUP_DIRECTORY}:/backup \ - debian:bullseye-slim \ - tar \ - --verbose \ - --create \ - --gzip \ - --file=/backup/${files_archive_name} \ - --directory=/app \ - ./files - docker container stop ${FILES_CONTAINER_NAME} - docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database -.PHONY : backup - -# Inspired by https://stackoverflow.com/questions/25785/delete-all-but-the-most-recent-x-files-in-bash/34862475#34862475 -prune-backups : ## Keep the most recent 7 backups, delete the rest - find /app/data/backups \ - -mindepth 1 \ - -maxdepth 1 \ - -type d \ - -execdir rmdir --ignore-fail-on-non-empty '{}' \; - cd /app/data/backups && \ - ls -t --indicator-style=slash \ - | grep '/$$' \ - | tail --lines=+8 \ - | xargs \ - --delimiter='\n' \ - --no-run-if-empty \ - rm --recursive --dir -- -.PHONY : prune-backups - -remove-data : BACKUP_DIRECTORY = $(shell pwd)/backup -remove-data : backup ## Remove data volumes (after creating a backup in ./backup) - docker volume rm \ - ${NAME}_data -.PHONY : remove-data - -restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database -restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make --file=Makefile.production restart`)` - ${docker_compose} down \ - --remove-orphans \ - database - docker volume rm \ - ${NAME}_data - -docker container stop ${DATABASE_CONTAINER_NAME} - -docker container rm --volumes ${DATABASE_CONTAINER_NAME} - ${docker_compose} run \ - --name ${DATABASE_CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ - | docker exec \ - --interactive \ - ${DATABASE_CONTAINER_NAME} \ - psql \ - --echo-all \ - --set=ON_ERROR_STOP=1 \ - --file=- \ - --username=postgres \ - --dbname=postgres - docker container stop ${DATABASE_CONTAINER_NAME} - docker container rm --volumes ${DATABASE_CONTAINER_NAME} - -docker container stop ${FILES_CONTAINER_NAME} - -docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} run \ - --name ${FILES_CONTAINER_NAME} \ - --detach \ - backend - docker run \ - --rm \ - --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${BACKUP_DIRECTORY}:/backup \ - debian:bullseye-slim \ - bash -cx " \ - cd /app/files && \ - rm \ - --recursive \ - --force \ - --dir \ - * && \ - tar \ - --verbose \ - --extract \ - --gunzip \ - --strip-components=2 \ - --file=/backup/${files_archive_name} \ - " - docker container stop ${FILES_CONTAINER_NAME} - docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database -.PHONY : restore - -begin-maintenance : ## Begin maintenance - cp \ - ./nginx/html/maintenance.off.html \ - ./nginx/html/maintenance.html -.PHONY : begin-maintenance - -end-maintenance : ## End maintenance - rm ./nginx/html/maintenance.html -.PHONY : end-maintenance - -# To deploy `develop`, I usually just take all docker services down by running -# `cd /app/machine; make down; cd /app/production; make --file=Makefile.production down; cd /app/staging; make --file=Makefile.production down;` -# pull the latest code by running -# `cd /app/machine; git pull -p; cd /app/production; git pull -p; cd /app/staging; git pull -p;` -# redeploy the reverse proxy by running -# `cd /app/machine; make deploy;` -# backup, migrate, and deploy all services by running -# `make --file=Makefile.production BACKUP_DIRECTORY=$(pwd)/backup down backup migrate deploy-services` -# first in `cd /app/staging` and checking that everything works as expected, -# and finally in `cd /app/production` and checking that everything works as -# expected. Before trying it on staging, I usually play the database from -# production into staging. -deploy : BACKUP_DIRECTORY = $(shell pwd)/backup -deploy : begin-maintenance store-commit backup fetch-all checkout-target migrate deploy-services run-tests end-maintenance ## Deploy tag, branch, or commit `${TARGET}`, for example, `make --file=Makefile.production TARGET=v1.0.0 deploy` -.PHONY : deploy - -rollback : TARGET = $(shell cat ./commit) -rollback : BACKUP_DIRECTORY = $(shell pwd)/backup -rollback : begin-maintenance checkout-target restore deploy-services run-tests end-maintenance ## Rollback deployment attempt (use commit hash stored in `./commit` and database backup stored in `./backup/`) -.PHONY : rollback - -store-commit : ## Store current commit - git rev-parse \ - --verify \ - HEAD \ - > commit -.PHONY : store-commit - -fetch-all : ## Fetch all - git fetch --all -.PHONY : fetch-all - -# Inspired by https://grimoire.ca/git/stop-using-git-pull-to-deploy/ -checkout-target : ## Fetch and checkout `${TARGET}` - git checkout --force "${TARGET}" -.PHONY : checkout-target - -sql : CONTAINER_NAME = sql_${NAME}_database -sql : ## Run the SQL script in the file `${SQL}` in the database service, for example, `make SQL=./my.sql sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - ${docker_compose} down \ - --remove-orphans \ - database - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ - --name ${CONTAINER_NAME} \ - --detach \ - database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - cat ${SQL} \ - | docker exec \ - --interactive \ - ${CONTAINER_NAME} \ - psql \ - --echo-all \ - --set=ON_ERROR_STOP=1 \ - --file=- \ - --username=postgres \ - --dbname=xbase - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database -.PHONY : migrate - -migrate : SQL = ./backend/src/Migrations/migrate.sql -migrate : sql ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) -.PHONY : migrate - -# Note that NGINX is because of its dependencies taken down and up last and in -# one go so the maintenance page is only down very shortly. -deploy-services : build ## Deploy services - ${docker_compose} up \ - --force-recreate \ - --renew-anon-volumes \ - --remove-orphans \ - --wait -.PHONY : deploy-services - -run-tests : COMMAND = true -run-tests : execb ## Run tests -.PHONY : run-tests - -build-bootstrap : ## Build the bootstrap image - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - --tag ${NAME}_bootstrap \ - --file ./backend/Dockerfile-bootstrap \ - ./backend -.PHONY : build-bootstrap - -# TODO Pass passwords in a more secure way! -jwt-certificates : build-bootstrap ## Create JWT encryption and signing certificates if necessary - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ - bash -ceux " \ - dotnet-script \ - ./create-certificates.csx \ - -- \ - ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ - ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ - " -.PHONY : jwt-certificates - -gpg : COMMENT = -gpg : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ - bash -ceuxo pipefail " \ - gpg \ - --quick-generate-key \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - ed25519 \ - sign \ - never && \ - fingerprint=\$$(gpg \ - --list-secret-keys \ - --with-colons \ - --keyid-format=long \ - ${EMAIL} \ - | grep \ - --before=3 \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ - echo fingerprint=\$${fingerprint} && \ - mkdir --parents \ - ./backend/src/gpg-keys && \ - gpg \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - --armor \ - --export-secret-keys \$${fingerprint} \ - > /app/src/gpg-keys/\$${fingerprint}.gpg \ - " diff --git a/README.md b/README.md index 42361668..5aedb7e8 100644 --- a/README.md +++ b/README.md @@ -63,38 +63,43 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. Initialize, fetch, and checkout possibly-nested submodules by running `git submodule update --init --recursive`. An alternative would have been passing `--recurse-submodules` to `git clone` above. -1. Prepare your environment by running `cp ./.env.sample ./.env && chmod 600 ./.env`, - `cp ./frontend/.env.local.sample ./frontend/.env.local && chmod 600 ./.env`, +1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env`, + `cp ./frontend/.env.local.development.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. +1. Prepare your remote controls GNU Make, Docker Compose, and Docker by running + - `ln --symbolic ./Makefile.development ./Makefile`, + - `ln --symbolic ./docker-compose.development.yaml ./docker-compose.yaml`. + - `ln --symbolic ./backend/Dockerfile.development ./backend/Dockerfile`, and + - `ln --symbolic ./frontend/Dockerfile.development ./frontend/Dockerfile`. 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and [GNU Make](https://www.gnu.org/software/make/). 1. List all GNU Make targets by running `make help`. 1. Generate and trust a self-signed certificate authority and SSL certificates - by running `make ssl`. If you are locally working on the metabase and the + by running `./certificates.mk ssl`. If you are locally working on the metabase and the database and if you need them to communicate over HTTPS, then instead of - running `make ssl`, make the `CERTIFICATE_AUTHORITY_*` variable values in - the `.env` file match the ones from the metabase (these variables match in - the `.env.sample` files), copy the certificate authority files from the - directories `./ssl`, `./backend/ssl`, and `./frontend/ssl` of the metabase - project into the respective directories in the database project (if the - repository reside alongside each other by running `mkdir ./ssl ./backend/ssl - ./frontend/ssl && cp ../metabase/ssl/ca.* ./ssl && cp - ../metabase/backend/ssl/ca.* ./backend/ssl && cp - ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command `make - generate-ssl-certificate`. + running `./certificates.mk ssl`, make the `CERTIFICATE_AUTHORITY_*` variable + values in the `.env` file match the ones from the metabase (these variables + match in the `.env.development.sample` files), copy the certificate + authority files from the directories `./ssl`, `./backend/ssl`, and + `./frontend/ssl` of the metabase project into the respective directories in + the database project (if the repository reside alongside each other by + running `mkdir ./ssl ./backend/ssl ./frontend/ssl && cp ../metabase/ssl/ca.* + ./ssl && cp ../metabase/backend/ssl/ca.* ./backend/ssl && cp + ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command + `./certificates.mk generate-ssl-certificate`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running - `make jwt-certificates`. + `./certificates.mk jwt`. 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `make - PERSON=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information - filled in, for example, `make NAME="Anna Smith" COMMENT=first - EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which + file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk + PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information + filled in, for example, `./gpg.mk NAME="Anna Smith" COMMENT=first + EMAIL=anna.smith@fraunhofer.de key`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. - Instead of using the GNU Make target `gpg`, you may + Instead of using the GNU Make target `key`, you may 1. install [GnuPG](https://gnupg.org) as described on [Download GnuPG](https://gnupg.org/download/index.html) or [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), @@ -115,7 +120,7 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. 1. Create the PostgreSQL database and schema by running - `make createdb migrate`. + `./database.mk createdb migrate`. 1. Start all services and follow their logs by running `make up logs`. 1. In your web browser, navigate to the - web frontend at `https://local.solarbuildingenvelopes.com:5051`, @@ -144,8 +149,8 @@ In another shell IDs 0. If there is an ID collision, then you can either change the user and group ID on the host machine (for example by logging in as another user) or you can replace all occurrences of `shell id --group` and `shell id --user` - in `Makefile` and `Makefile.production` by fixed non-colliding IDs like - 1000. If you know a better way, please + in `Makefile` by fixed non-colliding IDs like 1000. If you know a better + way, please [let use know on GitHub](https://github.com/building-envelope-data/database/issues/new). 1. List all backend GNU Make targets by running `make help`. 1. For example, update packages and tools by running `make update`. @@ -159,7 +164,7 @@ After changing the domain model in `./backend/src/data`, you need to migrate the database by dropping into `make shellb`, adding a migration with `make NAME=${MIGRATION_NAME} migration`, verifying and if necessary adapting the new migration C# code and SQL scripts, exiting the container with `exit`, and -applying the new migration to the PostgreSQL database with `make migrate`. See +applying the new migration to the PostgreSQL database with `./database.mk migrate`. See [Migrations Overview](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) and the following pages for details. @@ -192,7 +197,7 @@ On the very first usage: and [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens). -Note that the Docker containers are configured in `./docker-compose.yml` in +Note that the Docker containers are configured in `./docker-compose.development.yaml` in such a way that Visual Studio Code extensions installed within containers are retained in Docker volumes and thus remain installed across `make down` and `make up` cycles. @@ -227,7 +232,7 @@ breakpoints. For details on debugging C# in Visual Studio Code, see Note that the debugger detaches after the [polling file watcher](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables) restarts the process, which happens for example after editing a source file -because `dotnet watch` is configured in `./docker-compose.yml` with +because `dotnet watch` is configured in `./docker-compose.development.yaml` with `DOTNET_USE_POLLING_FILE_WATCHER` set to `true`. As of this writing, there is an [open feature request to reattach the debugger automatically](https://github.com/dotnet/vscode-csharp/issues/4822). @@ -349,7 +354,7 @@ and the pages following it. ``` 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, - `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./.env`, + `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, and by adjusting variable values in the copies to your needs, in particular, by setting passwords to newly generated ones, where random passwords may be generated by running `openssl rand -base64 32`. Here is @@ -389,12 +394,17 @@ and the pages following it. - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are host and port of the message transfer agent and a list of allowed email addresses to send emails to even in the staging environment. + 1. Prepare your remote controls GNU Make, Docker Compose, and Docker by running + - `ln --symbolic ./Makefile.${environment} ./Makefile`, + - `ln --symbolic ./docker-compose.${environment}.yaml ./docker-compose.yaml`. + - `ln --symbolic ./backend/Dockerfile.${environment} ./backend/Dockerfile`, and + - `ln --symbolic ./frontend/Dockerfile.${environment} ./frontend/Dockerfile`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running - `make --file=Makefile.production jwt-certificates`. + `./certificates.mk jwt`. 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `make - PERSON=${name} COMMENT=${comment} EMAIL=${email} gpg` with your information + file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk + PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information filled in, for example, `make NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which is output by the command and set it as the value of the @@ -421,15 +431,15 @@ and the pages following it. 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. 1. Prepare PostgreSQL by generating new password files by running - `make --file=Makefile.production postgres_passwords` + `./database.mk postgres_passwords` and creating the database by running - `make --file=Makefile.production createdb`. + `./database.mk createdb`. ### Creating a release 1. Draft a new release with a new version according to [Semantic Versioning](https://semver.org) by running the GitHub action - [Draft a new release](https://github.com/building-envelope-data/database/actions/workflows/draft-new-release.yml) + [Draft a new release](https://github.com/building-envelope-data/database/actions/workflows/draft-new-release.yaml) which, creates a new branch named `release/v*.*.*`, creates a corresponding pull request, updates the [Changelog](https://github.com/building-envelope-data/database/blob/develop/CHANGELOG.md), @@ -440,28 +450,29 @@ and the pages following it. [Releases](https://github.com/building-envelope-data/database/releases). 1. Fetch the release branch by running `git fetch` and check it out by running `git checkout release/v*.*.*`, where `*.*.*` is the version. -1. Apply pending migrations with `make --file=Makefile.production migrate`. +1. Apply pending migrations with `./database.mk migrate`. 1. Make sure that all tests succeed and try out any new features manually. -1. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yml) +1. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yaml) by merging the release branch into `main` whereby a new pull request from `main` into `develop` is created that you need to merge to finish of. ### Deploying a release 1. Enter a shell on the production machine using `ssh`. +1. Navigate into `/app/production` by running `cd /app/production`. 1. Back up the production database by running - `make --directory /app/production --file=Makefile.production BACKUP_DIRECTORY=/app/production/backup backup`. + `./database.mk BACKUP_DIRECTORY=/app/production/backup backup`. 1. Change to the staging environment by running `cd /app/staging`. 1. Restore the staging database from the production backup by running - `make --file=Makefile.production BACKUP_DIRECTORY=/app/production/backup restore`. + `./database.mk BACKUP_DIRECTORY=/app/production/backup restore`. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.staging.sample` file of the release to be deployed. 1. Deploy the new release in the staging environment by running - `make --file=Makefile.production TARGET=${TAG} deploy`, where `${TAG}` is + `./deploy.mk TARGET=${TAG} deploy`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running - `make --file=Makefile.production rollback`, + `./deploy.mk rollback`, figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by @@ -476,29 +487,28 @@ and the pages following it. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.production.sample` file of the release to be deployed. 1. Deploy the new release in the production environment by running - `make --file=Makefile.production TARGET=${TAG} deploy`, where `${TAG}` is + `./deploy.mk TARGET=${TAG} deploy`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running - `make --file=Makefile.production rollback`, + `./deploy.mk rollback`, figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. ### Troubleshooting -The file `Makefile.production` contains GNU Make targets to manage Docker -containers like `up` and `down`, to follow Docker container logs with `logs`, -to drop into shells inside running Docker containers like `shellb` for the -backend service and `shellf` for the frontend service and `psql` for the -database service, and to list information about Docker like `list` and -`list-services`. +The files `Makefile.*` contain GNU Make targets to manage Docker containers +like `up` and `down`, to follow Docker container logs with `logs`, to drop into +shells inside running Docker containers like `shellb` for the backend service +and `shellf` for the frontend service, and to list information about Docker +like `list` and `list-services`. -And the file contains GNU Make targets to deploy a new release or rollback it -back as mentioned above. These targets depend on several smaller targets like -`begin-maintenance` and `end-maintenance` to begin or end displaying -maintenance information to end users that try to interact with the website, and -`backup` to backup all data before deploying a new version, `migrate` to -migrate the database, and `run-tests` to run tests. +The Makefile `./deploy.mk` contains GNU Make targets to deploy a new release or +rollback it back as mentioned above. These targets depend on several smaller +targets like `begin-maintenance` and `end-maintenance` to begin or end +displaying maintenance information to end users that try to interact with the +website, and `backup` to backup all data before deploying a new version, +`migrate` to migrate the database, and `run-tests` to run tests. If for some reason the website displays the maintenance page without maintenance happening at the moment, then drop into a shell on the production @@ -524,14 +534,14 @@ database was not shut down cleanly. One solution is to restore the database from a backup by running ``` -make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/20XX-XX-XX_XX_XX_XX/ restore +./database.mk BACKUP_DIRECTORY=/app/data/backups/20XX-XX-XX_XX_XX_XX/ restore ``` where the `X`s need to be replaced by proper values. Another solution is to reset the transaction log by entering the database container with ``` -docker compose --file=docker-compose.production.yml --project-name database_production run database bash +docker compose run database bash ``` and dry-running @@ -559,11 +569,11 @@ under /app/staging before doing it in `production` under /app/production. `ssh -CvX -A cloud@IpAdressOfCloudServer`. 1. Navigate to the production environment by running `cd /app/production`. 1. Make a database backup by running `DATE=$(date +"%Y-%m-%d_%H_%M_%S")` and - `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/${DATE} backup` + `./database.mk BACKUP_DIRECTORY=/app/data/backups/${DATE} backup` 1. Navigate to the staging environment by running `cd /app/staging`. 1. Load the backup into the staging database by running - `make --file=Makefile.production BACKUP_DIRECTORY=/app/data/backups/${DATE} restore`. -1. Drop into `psql` by running `make --file=Makefile.production psql`. + `./database.mk BACKUP_DIRECTORY=/app/data/backups/${DATE} restore`. +1. Drop into `psql` by running `./database.mk psql`. 1. List all tables in the schema `database` by running `\dt database.*`. 1. List all optical data records by running `select * from database.optical_data;` and remember for example one identifier of a record diff --git a/backend/.gitignore b/backend/.gitignore index 1f38ce67..3c605139 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,4 @@ +/Dockerfile /src/core /src/tempkey.rsa /src/wwwroot/assets/ diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile-bootstrap index 3a9f7b7b..97f54f3d 100644 --- a/backend/Dockerfile-bootstrap +++ b/backend/Dockerfile-bootstrap @@ -55,7 +55,7 @@ RUN \ # https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the -# `local.yml` Ansible script. +# `local.yaml` Ansible script. ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. diff --git a/backend/Dockerfile b/backend/Dockerfile.development similarity index 99% rename from backend/Dockerfile rename to backend/Dockerfile.development index 87e333fe..1620d00e 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile.development @@ -123,7 +123,7 @@ RUN update-ca-certificates # https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the -# `local.yml` Ansible script. +# `local.yaml` Ansible script. ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. diff --git a/backend/Dockerfile-production b/backend/Dockerfile.production similarity index 99% rename from backend/Dockerfile-production rename to backend/Dockerfile.production index a3593d9a..d1f82d94 100644 --- a/backend/Dockerfile-production +++ b/backend/Dockerfile.production @@ -74,7 +74,7 @@ RUN apt-get update \ # https://repos.gnupg.org/deb/gnupg/noble/ # Keep the version in sync with the one in the other Dockerfiles of this # repository and with the one of the `machine` repository specified in the -# `local.yml` Ansible script. +# `local.yaml` Ansible script. ENV GNUPG_VERSION=2.5.17-1 ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 71237d6c..0bcd1619 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -213,7 +213,7 @@ DbContextOptionsBuilder options .UseNpgsql( _appSettings.Database.ConnectionString, _ => _ - // Keep version in sync with the one in ./docker-compose.*.yml + // Keep version in sync with the one in ./docker-compose.*.yaml .SetPostgresVersion(13, 23) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .UseNodaTime() diff --git a/certificates.mk b/certificates.mk new file mode 100755 index 00000000..30956a02 --- /dev/null +++ b/certificates.mk @@ -0,0 +1,262 @@ +#!/usr/bin/env -S make --file + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +build-bootstrap : ## Build the bootstrap image + DOCKER_BUILDKIT=1 \ + docker build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + --tag ${NAME}_bootstrap \ + --file ./backend/Dockerfile-bootstrap \ + ./backend +.PHONY : build-bootstrap + +jwt : build-bootstrap ## Create JWT encryption and signing certificates if necessary + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -ceux -o pipefail " \ + dotnet-script \ + /app/create-certificates.csx \ + -- \ + ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ + ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ + " +.PHONY : jwt + +# For an introduction to how HTTPS works see https://howhttps.works +ssl : ## Generate and trust certificate authority, and generate SSL certificates + $(MAKE) generate-certificate-authority + $(MAKE) generate-ssl-certificate + $(MAKE) trust-certificate-authority +.PHONY : ssl + +# Creating Self-Signed ECDSA SSL Certificate using OpenSSL: http://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/ +# See also https://gist.github.com/Soarez/9688998 +# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm +# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 +# Which file extensions are meant for which file layout? https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file/9717#9717 +generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate + mkdir --parents ./ssl/ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ + nginx:1.29-trixie-otel \ + bash -ceux -o pipefail " \ + echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ + openssl ecparam \ + -genkey \ + -name secp384r1 \ + -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key && \ + echo \"# Check and print the private key's elliptic curve parameters\" && \ + openssl ecparam \ + -check \ + -text \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ + -noout && \ + echo \"# Generate the PKCS#10 certificate request '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req' with common name '${CERTIFICATE_AUTHORITY_HOST}' from the private key\" && \ + openssl req \ + -new \ + -subj \"${CERTIFICATE_AUTHORITY_SUBJECT}/CN=${CERTIFICATE_AUTHORITY_HOST}\" \ + -key /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ + -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req && \ + echo \"# Verify and print the request\" && \ + openssl req \ + -verify \ + -text \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ + -noout && \ + echo \"# Convert the request into the self-signed certificate '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt'\" && \ + openssl x509 \ + -req \ + -trustout \ + -days 365 \ + -extfile <(printf ' \ + basicConstraints = critical, CA:TRUE, pathlen:0\n \ + subjectKeyIdentifier = hash\n \ + authorityKeyIdentifier = keyid:always, issuer:always\n \ + subjectAltName = DNS:${CERTIFICATE_AUTHORITY_HOST}\n \ + issuerAltName = issuer:copy\n \ + keyUsage = critical, cRLSign, digitalSignature, keyCertSign\n \ + ') \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ + -signkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ + -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ + echo \"# Print and Verify the self-signed certificate\" && \ + openssl x509 \ + -text \ + -purpose \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + -noout && \ + openssl verify \ + -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ + echo \"# Create the PKCS#12 file '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx' from the self-signed certificate\" && \ + openssl pkcs12 \ + -export \ + -passout pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + -inkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ + -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx && \ + echo \"# Verify the PKCS#12 file\" && \ + ( \ + openssl pkcs12 \ + -info \ + -passin pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ + -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ + -noout && \ + echo \"PKCS#12 file is valid\" && \ + exit 0 \ + ) || echo \"PFX file is invalid\" \ + " + mkdir --parents ./backend/ssl + cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./backend/ssl + mkdir --parents ./frontend/ssl + cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./frontend/ssl +.PHONY : generate-certificate-authority + +# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 +# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 +# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 +# and https://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu/719047#719047 +# For debugging purposes, the following commands can be helpful +# cat /etc/ssl/certs/ca-certificates.crt +# cat ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt +# sudo cat /etc/ssl/certs/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pem +# Note that Firefox and Google Chrome use their own certificate stores: +# * Firefox: https://www.cyberciti.biz/faq/firefox-adding-trusted-ca/ +# * Google Chrome: https://rshankar.com/blog/2010/07/08/how-to-import-a-certificate-in-google-chrome/ +trust-certificate-authority : ## Trust the authority's SSL certificate + sudo cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt /usr/local/share/ca-certificates + sudo update-ca-certificates + openssl verify ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt +.PHONY : trust-certificate-authority + +# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 +# and https://superuser.com/questions/226192/avoid-password-prompt-for-keys-and-prompts-for-dn-information/226229#226229 +# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 +# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 +# For an explanation of the distinction between `cert` and `pfx` files, see +# https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 +# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm +# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 +# What are PKCS#12 files? https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 +# Process substitution `<( ... )`: https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html +# Note that extensions are not transferred to certificate requests and vice versa as said on https://www.openssl.org/docs/man1.1.0/man1/x509.html#BUGS +generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate signed by our certificate authority + mkdir --parents ./ssl/ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ + nginx:1.29-trixie-otel \ + bash -ceux -o pipefail " \ + echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ + openssl ecparam \ + -genkey \ + -name secp384r1 \ + -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key && \ + echo \"# Check and print the private key's elliptic curve parameters\" && \ + openssl ecparam \ + -check \ + -text \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ + -noout && \ + echo \"# Generate the PKCS#10 certificate request '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req' with common name '${HOST}' from the private key\" && \ + openssl req \ + -new \ + -subj \"${SSL_CERTIFICATE_SUBJECT}/CN=${HOST}\" \ + -key /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ + -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req && \ + echo \"# Verify and print the request\" && \ + openssl req \ + -verify \ + -text \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ + -noout && \ + echo \"# Sign the request with certificate authority '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt' and key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' resulting in the signed certificate '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt'\" && \ + openssl x509 \ + -req \ + -days 365 \ + -CA /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + -CAkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ + -CAcreateserial \ + -extfile <(printf ' \ + basicConstraints = critical, CA:FALSE\n \ + subjectKeyIdentifier = hash\n \ + authorityKeyIdentifier = keyid:always, issuer:always\n \ + subjectAltName = DNS:${HOST}\n \ + issuerAltName = issuer:copy\n \ + keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement\n \ + extendedKeyUsage = critical, clientAuth, serverAuth\n \ + ') \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ + -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ + echo \"# Print and Verify the signed certificate\" && \ + openssl x509 \ + -text \ + -purpose \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ + -noout && \ + openssl verify \ + -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ + echo \"# Chain the certificate and the certificate authority's certificate in that order, see http://nginx.org/en/docs/http/configuring_https_servers.html#chains\" && \ + cat \ + /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ + /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + > /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ + echo \"# Verify the chained certificate\" && \ + openssl verify \ + -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ + echo \"# Create the PKCS#12 file chain '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx' from the un-chained signed certificate\" && \ + openssl pkcs12 \ + -export \ + -chain \ + -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ + -passout pass:${SSL_CERTIFICATE_PASSWORD} \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ + -inkey /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ + -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx && \ + echo \"# Verify the PKCS#12 file\" && \ + ( \ + openssl pkcs12 \ + -info \ + -passin pass:${SSL_CERTIFICATE_PASSWORD} \ + -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx \ + -noout && \ + echo \"PKCS#12 file is valid\" && \ + exit 0 \ + ) || echo \"PFX file is invalid\" \ + " +.PHONY : generate-ssl-certificate + +fetch-ssl-certificate : ## Fetch the SSL certificate from the server + openssl s_client ${HOST}:${HTTPS_PORT} +.PHONY : fetch-ssl-certificate + +ssl-certificate : ## Print the SSL certificate + openssl x509 -text -noout -in ./ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt +.PHONY : ssl-certificate + +certificate-authority : ## View the certificate authority + openssl x509 -text -noout -in ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt +.PHONY : certificate-authority diff --git a/database.mk b/database.mk new file mode 100755 index 00000000..1cecfe24 --- /dev/null +++ b/database.mk @@ -0,0 +1,254 @@ +#!/usr/bin/env -S make --file + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +docker_compose = \ + docker compose \ + --file ./docker-compose.yaml \ + --env-file ./.env + +database_name = xbase + +dump_archive_name = postgresql_dumpall.gz +files_archive_name = files.gz + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +psql : ## Enter PostgreSQL interactive terminal in the running `database` container + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database + ${docker_compose} exec \ + database \ + psql \ + --username postgres \ + --dbname ${database_name} +.PHONY : psql + +# ${docker_compose} up \ +# --remove-orphans \ +# --wait \ +# database +# ${docker_compose} exec \ +# database \ +# bash -c " \ +# createdb --username postgres ${DBNAME} ; \ +# " +createdb : DBNAME = ${database_name} +createdb : CONTAINER_NAME = create_${NAME}_database +createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} run \ + --name ${CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + docker exec \ + ${CONTAINER_NAME} \ + createdb \ + --username postgres \ + ${DBNAME} + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} +.PHONY : createdb + +dropdb : DBNAME = ${database_name} +dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database + ${docker_compose} exec \ + database \ + dropdb \ + --username postgres \ + ${DBNAME} +.PHONY : dropdb + +sql : CONTAINER_NAME = sql_${NAME}_database +sql : ## Run the SQL script in the file `${SQL}` in the database service, for example, `make SQL=./my.sql sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database + cat ${SQL} \ + | ${docker_compose} exec \ + --no-tty \ + database \ + psql \ + --echo-all \ + --set=ON_ERROR_STOP=1 \ + --file=- \ + --username=postgres \ + --dbname=${database_name} + ${docker_compose} down \ + --remove-orphans \ + database + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} run \ + --name ${CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + cat ${SQL} \ + | docker exec \ + --no-tty \ + ${CONTAINER_NAME} \ + psql \ + --echo-all \ + --set=ON_ERROR_STOP=1 \ + --file=- \ + --username=postgres \ + --dbname=${database_name} + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database +.PHONY : sql + +migrate : SQL = ./backend/src/Migrations/migrate.sql +migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +.PHONY : migrate + +# Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL +# Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html +# Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes +# We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ +backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database +backup : FILES_CONTAINER_NAME = backup_${NAME}_files +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` + mkdir --parents ${BACKUP_DIRECTORY} + ${docker_compose} down \ + --remove-orphans \ + database + -docker container stop ${DATABASE_CONTAINER_NAME} + -docker container rm --volumes ${DATABASE_CONTAINER_NAME} + ${docker_compose} run \ + --name ${DATABASE_CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + docker exec \ + ${DATABASE_CONTAINER_NAME} \ + pg_dumpall \ + --clean \ + --username=postgres \ + | gzip \ + > ${BACKUP_DIRECTORY}/${dump_archive_name} + docker container stop ${DATABASE_CONTAINER_NAME} + docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop ${FILES_CONTAINER_NAME} + -docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} run \ + --name ${FILES_CONTAINER_NAME} \ + --detach \ + backend + docker run \ + --rm \ + --volumes-from ${FILES_CONTAINER_NAME} \ + --volume ${BACKUP_DIRECTORY}:/backup \ + debian:bullseye-slim \ + tar \ + --verbose \ + --create \ + --gzip \ + --file=/backup/${files_archive_name} \ + --directory=/app \ + ./files + docker container stop ${FILES_CONTAINER_NAME} + docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database +.PHONY : backup + +restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database +restore : FILES_CONTAINER_NAME = restore_${NAME}_files +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` + ${docker_compose} down \ + --remove-orphans \ + database + docker volume rm \ + ${NAME}_data + -docker container stop ${DATABASE_CONTAINER_NAME} + -docker container rm --volumes ${DATABASE_CONTAINER_NAME} + ${docker_compose} run \ + --name ${DATABASE_CONTAINER_NAME} \ + --detach \ + database + while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ + | docker exec \ + --interactive \ + ${DATABASE_CONTAINER_NAME} \ + psql \ + --echo-all \ + --set=ON_ERROR_STOP=1 \ + --file=- \ + --username=postgres \ + --dbname=postgres + docker container stop ${DATABASE_CONTAINER_NAME} + docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop ${FILES_CONTAINER_NAME} + -docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} run \ + --name ${FILES_CONTAINER_NAME} \ + --detach \ + backend + docker run \ + --rm \ + --volumes-from ${FILES_CONTAINER_NAME} \ + --volume ${BACKUP_DIRECTORY}:/backup \ + debian:bullseye-slim \ + bash -cx " \ + cd /app/files && \ + rm \ + --recursive \ + --force \ + --dir \ + * && \ + tar \ + --verbose \ + --extract \ + --gunzip \ + --strip-components=2 \ + --file=/backup/${files_archive_name} \ + " + docker container stop ${FILES_CONTAINER_NAME} + docker container rm --volumes ${FILES_CONTAINER_NAME} + ${docker_compose} up \ + --remove-orphans \ + --wait \ + database +.PHONY : restore + +# TODO The entrypoint file of the PostgreSQL image uses the file refered to by `POSTGRES_PASSWORD_FILE` and this file needs to be accessible by `other`. Why? We do not want all people to be able to read secrets! +# https://www.postgresql.org/docs/current/libpq-pgpass.html +postgres_passwords : ## Generate PostgreSQL password and passwords files whose entries are nothing but one clear-text password or of the form `hostname:port:database:username:password` (note that if the data volume already exists, then it either needs to be removed resulting in the loss of all data or the password of the PostgreSQL user needs to be changed manually by executing the SQL query `ALTER USER postgres with password '...'` or running `\password postgres` within `make psql`) + mkdir -p ./secrets + chmod 0755 ./secrets + touch ./secrets/postgres_password + chmod 0644 ./secrets/postgres_password + openssl rand -base64 32 \ + > ./secrets/postgres_password + touch ./secrets/postgres_passwords + chmod 0600 ./secrets/postgres_passwords + echo "*:*:*:*:$$(cat ./secrets/postgres_password)" \ + > ./secrets/postgres_passwords +.PHONY : postgres_passwords diff --git a/deploy.mk b/deploy.mk new file mode 100755 index 00000000..f6b5f482 --- /dev/null +++ b/deploy.mk @@ -0,0 +1,118 @@ +#!/usr/bin/env -S make --file + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +docker_compose = \ + docker compose \ + --file ./docker-compose.yaml \ + --env-file ./.env + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +# To deploy `develop`, I usually just take all docker services down by running +# `cd /app/machine; make down; cd /app/production; make down; cd /app/staging; make down;` +# pull the latest code by running +# `cd /app/machine; git pull -p; cd /app/production; git pull -p; cd /app/staging; git pull -p;` +# redeploy the reverse proxy by running +# `cd /app/machine; make deploy;` +# backup, migrate, and deploy all services by running +# `make BACKUP_DIRECTORY=$(pwd)/backup down backup migrate deploy-services` +# first in `cd /app/staging` and checking that everything works as expected, +# and finally in `cd /app/production` and checking that everything works as +# expected. Before trying it on staging, I usually play the database from +# production into staging. +deploy : BACKUP_DIRECTORY = $(shell pwd)/backup +deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `make TARGET=v1.0.0 deploy` + $(MAKE) begin-maintenance + $(MAKE) store-commit + $(MAKE) backup + $(MAKE) fetch-all + $(MAKE) checkout-target + $(MAKE) migrate + $(MAKE) deploy-services + $(MAKE) run-tests + $(MAKE) end-maintenance +.PHONY : deploy + +rollback : TARGET = $(shell cat ./commit) +rollback : BACKUP_DIRECTORY = $(shell pwd)/backup +rollback : ## Rollback deployment attempt (use commit hash stored in `./commit` and database backup stored in `./backup/`) + $(MAKE) begin-maintenance + $(MAKE) checkout-target + $(MAKE) restore + $(MAKE) deploy-services + $(MAKE) run-tests + $(MAKE) end-maintenance +.PHONY : rollback + +begin-maintenance : ## Begin maintenance + cp \ + ./nginx/html/maintenance.off.html \ + ./nginx/html/maintenance.html +.PHONY : begin-maintenance + +end-maintenance : ## End maintenance + rm ./nginx/html/maintenance.html +.PHONY : end-maintenance + +backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` + $(MAKE) \ + --file="$(shell pwd)/database.mk" \ + BACKUP_DIRECTORY=${BACKUP_DIRECTORY} \ + backup +.PHONY : backup + +restore : CONTAINER_NAME = restore_${NAME}_database +restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35/ restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` + $(MAKE) \ + --file="$(shell pwd)/database.mk" \ + BACKUP_DIRECTORY=${BACKUP_DIRECTORY} \ + backup +.PHONY : restore + +migrate : ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) + $(MAKE) \ + --file="$(shell pwd)/database.mk" \ + migrate +.PHONY : migrate + +store-commit : ## Store current commit + git rev-parse \ + --verify \ + HEAD \ + > ./commit +.PHONY : store-commit + +fetch-all : ## Fetch all + git fetch --all +.PHONY : fetch-all + +# Inspired by https://grimoire.ca/git/stop-using-git-pull-to-deploy/ +checkout-target : ## Fetch and checkout `${TARGET}` + git checkout --force "${TARGET}" +.PHONY : checkout-target + +# Note that NGINX is because of its dependencies taken down and up last and in +# one go so the maintenance page is only down very shortly. +deploy-services : ## Deploy services + $(MAKE) build + ${docker_compose} up \ + --force-recreate \ + --renew-anon-volumes \ + --remove-orphans \ + --wait +.PHONY : deploy-services + +run-tests : COMMAND = true +run-tests : execb ## Run tests +.PHONY : run-tests diff --git a/docker-compose.yml b/docker-compose.development.yaml similarity index 98% rename from docker-compose.yml rename to docker-compose.development.yaml index 771b310f..37555998 100644 --- a/docker-compose.yml +++ b/docker-compose.development.yaml @@ -1,4 +1,4 @@ -# Keep setup in sync with the one in ./docker-compose.production.yml +# Keep setup in sync with the one in ./docker-compose.production.yaml name: "${NAME}" services: @@ -45,7 +45,7 @@ services: ServerOptions__TlsMode: "None" ServerOptions__TlsCertificate: "" # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml + # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yaml # ports: # Web interface # - "0.0.0.0:4042:80" @@ -62,7 +62,7 @@ services: backend: build: context: "./backend" - dockerfile: "./Dockerfile" + dockerfile: "./Dockerfile.development" args: CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?required}" @@ -155,7 +155,7 @@ services: frontend: build: context: "./frontend" - dockerfile: "./Dockerfile" + dockerfile: "./Dockerfile.development" args: CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" depends_on: diff --git a/docker-compose.production.yml b/docker-compose.production.yaml similarity index 97% rename from docker-compose.production.yml rename to docker-compose.production.yaml index c5113ea4..6f814e6b 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yaml @@ -1,4 +1,4 @@ -# Keep setup in sync with the one in ./docker-compose.yml +# Keep setup in sync with the one in ./docker-compose.development.yaml name: "${NAME}" services: @@ -44,7 +44,7 @@ services: RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS?required}" restart: "always" # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yml + # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.development.yaml healthcheck: test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] interval: "5s" @@ -56,7 +56,7 @@ services: backend: build: context: "./backend" - dockerfile: "./Dockerfile-production" + dockerfile: "./Dockerfile.production" depends_on: database: condition: "service_healthy" @@ -109,7 +109,7 @@ services: frontend: build: context: "./frontend" - dockerfile: "./Dockerfile-production" + dockerfile: "./Dockerfile.production" depends_on: backend: condition: "service_healthy" diff --git a/frontend/.env.local.sample b/frontend/.env.local.development.sample similarity index 100% rename from frontend/.env.local.sample rename to frontend/.env.local.development.sample diff --git a/frontend/.gitignore b/frontend/.gitignore index c66fbf5f..1d070c64 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,3 +1,4 @@ +/Dockerfile /secrets /ssl diff --git a/frontend/.graphql-let.yml b/frontend/.graphql-let.yaml similarity index 100% rename from frontend/.graphql-let.yml rename to frontend/.graphql-let.yaml diff --git a/frontend/Dockerfile b/frontend/Dockerfile.development similarity index 100% rename from frontend/Dockerfile rename to frontend/Dockerfile.development diff --git a/frontend/Dockerfile-production b/frontend/Dockerfile.production similarity index 100% rename from frontend/Dockerfile-production rename to frontend/Dockerfile.production diff --git a/frontend/README.md b/frontend/README.md index fa868934..5800f313 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -15,7 +15,7 @@ const News = () => { } ``` -By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yml` are consistent. +By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yaml` are consistent. ## Deploy your own diff --git a/gpg.mk b/gpg.mk new file mode 100755 index 00000000..be023015 --- /dev/null +++ b/gpg.mk @@ -0,0 +1,54 @@ +#!/usr/bin/env -S make --file + +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +# Keep file name .gpg in sync with the one in `AppSettings.cs` +key : COMMENT = +key : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --mount type=bind,source="$(shell pwd)/backend",target=/app \ + ${NAME}_bootstrap \ + bash -ceuxo pipefail " \ + gpg \ + --quick-generate-key \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + ed25519 \ + sign \ + never && \ + fingerprint=\$$(gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + '${PERSON} (${COMMENT}) <${EMAIL}>' \ + | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ + echo fingerprint=\$${fingerprint} && \ + mkdir --parents \ + ./backend/src/gpg-keys && \ + gpg \ + --batch \ + --pinentry-mode loopback \ + --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ + --armor \ + --export-secret-keys \$${fingerprint} \ + > /app/src/gpg-keys/\$${fingerprint}.gpg \ + " From 5069298e8d6018c722183fdd1c9b11bcbb0c2838 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 08:52:13 +0100 Subject: [PATCH 673/878] Separate domain name and subdomain --- .env.development.sample | 21 ++++++++++--------- .env.production.sample | 3 ++- .env.staging.sample | 3 ++- backend/src/AppSettings.cs | 13 +++--------- .../GetHttpsResourceResolvers.cs | 2 +- backend/src/Services/ApiRequestService.cs | 2 +- backend/src/Services/EmailSender.cs | 13 +++++------- backend/src/Startup.cs | 14 +------------ docker-compose.development.yaml | 17 ++++++++++++--- docker-compose.production.yaml | 5 +++-- 10 files changed, 43 insertions(+), 50 deletions(-) diff --git a/.env.development.sample b/.env.development.sample index 547f61ba..81edd7fe 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -1,6 +1,7 @@ NAME=database -HOST=local.solarbuildingenvelopes.com +HOST=solarbuildingenvelopes.com +SUBDOMAIN=local HTTP_PORT=5050 HTTPS_PORT=5051 @@ -18,20 +19,20 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE=secret -CERTIFICATE_AUTHORITY_HOST=ca.org -CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca -CERTIFICATE_AUTHORITY_PASSWORD=ca-password -CERTIFICATE_AUTHORITY_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" - GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase -SSL_CERTIFICATE_BASE_FILE_NAME=database -SSL_CERTIFICATE_PASSWORD=password -SSL_CERTIFICATE_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" - JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password SMTP_HOST=email SMTP_PORT=25 + +CERTIFICATE_AUTHORITY_HOST=ca.org +CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca +CERTIFICATE_AUTHORITY_PASSWORD=ca-password +CERTIFICATE_AUTHORITY_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" + +SSL_CERTIFICATE_BASE_FILE_NAME=database +SSL_CERTIFICATE_PASSWORD=password +SSL_CERTIFICATE_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" diff --git a/.env.production.sample b/.env.production.sample index 7b2559f9..1a5ea40b 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,6 +1,7 @@ NAME=database_production -HOST=www.solarbuildingenvelopes.com +HOST=solarbuildingenvelopes.com +SUBDOMAIN=www HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org diff --git a/.env.staging.sample b/.env.staging.sample index ca9c253c..0878bf0c 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -1,6 +1,7 @@ NAME=database_staging -HOST=staging.solarbuildingenvelopes.com +HOST=solarbuildingenvelopes.com +SUBDOMAIN=staging HTTP_PORT=9000 METABASE_HOST=staging.buildingenvelopedata.org diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 42643188..5079e356 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -8,20 +8,13 @@ namespace Database; public sealed record AppSettings { private const string GraphQlPathSegment = "/graphql/"; - private const string WwwSubdomain = "www."; public string Host { get; init; } = ""; - public Uri HostUri => new(Host, UriKind.Absolute); - public Uri NonWwwHostUri => - HostUri.Host.StartsWith(WwwSubdomain, StringComparison.OrdinalIgnoreCase) - ? new UriBuilder(HostUri) - { - Host = HostUri.Host[WwwSubdomain.Length..] - }.Uri - : HostUri; + public string Subdomain { get; init; } = ""; + public Uri Uri => new($"https://{Subdomain}.{Host}", UriKind.Absolute); // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 - public Uri DatabaseGraphQlEndpoint => new UriBuilder(HostUri) { Path = GraphQlPathSegment }.Uri; + public Uri DatabaseGraphQlEndpoint => new UriBuilder(Uri) { Path = GraphQlPathSegment }.Uri; public string MetabaseHost { get; init; } = ""; public Uri MetabaseHostUri => new(MetabaseHost, UriKind.Absolute); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index fd5c290e..b6b3b251 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -66,7 +66,7 @@ public Uri GetLocator( AppSettings appSettings ) { - return new UriBuilder(appSettings.HostUri) + return new UriBuilder(appSettings.Uri) { Path = $"/api/resources/{getHttpsResource.FileName}" }.Uri; diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 2227c5dc..6a9c0fe6 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -168,7 +168,7 @@ CancellationToken cancellationToken ); httpRequestMessage.Headers.Add( HeaderNames.Origin, - appSettings.Host + appSettings.Uri.AbsoluteUri ); if (bearerToken is not null) { diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs index 00888cc3..155d1e9d 100644 --- a/backend/src/Services/EmailSender.cs +++ b/backend/src/Services/EmailSender.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.Tasks; using MailKit.Net.Smtp; using MailKit.Security; @@ -21,11 +20,9 @@ string Body } public sealed class EmailSender( - string smtpHost, - int smtpPort, - Uri nonWwwHost, + AppSettings appSettings, ILogger logger - ) +) : IEmailSender { public Task SendAsync( @@ -39,7 +36,7 @@ string body message.From.Add( new MailboxAddress( "Database", - $"database@s{nonWwwHost.Host}" + $"database@{appSettings.Host}" ) ); message.To.Add( @@ -56,8 +53,8 @@ string body using (var client = new SmtpClient()) { client.Connect( - smtpHost, - smtpPort, + appSettings.Email.SmtpHost, + appSettings.Email.SmtpPort, SecureSocketOptions.StartTlsWhenAvailable ); // client.Authenticate("joey", "password"); diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 0bcd1619..8ebb893d 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -53,7 +53,7 @@ public void ConfigureServices(IServiceCollection services) AuthConfiguration.ConfigureServices(services, environment, _appSettings); GraphQlConfiguration.ConfigureServices(services, environment); ConfigureDatabaseServices(services); - ConfigureMessageSenderServices(services); + services.AddTransient(); ConfigureRequestResponseServices(services); // ConfigureSessionServices(services); // Not used ConfigureTelemetryServices(services); @@ -133,18 +133,6 @@ private static void ConfigureRequestResponseServices(IServiceCollection services }); } - private void ConfigureMessageSenderServices(IServiceCollection services) - { - services.AddTransient(serviceProvider => - new EmailSender( - _appSettings.Email.SmtpHost, - _appSettings.Email.SmtpPort, - _appSettings.NonWwwHostUri, - serviceProvider.GetRequiredService>() - ) - ); - } - // private static void ConfigureSessionServices( // IServiceCollection services // ) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 37555998..074f7088 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -31,6 +31,8 @@ services: interval: "5s" timeout: "5s" retries: "5" + labels: + autoheal: "true" email: image: "rnwood/smtp4dev:v3.10" @@ -58,6 +60,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" backend: build: @@ -97,8 +101,9 @@ services: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "development" - XBASE_Host: "https://${HOST:?required}:${HTTPS_PORT:?required}" - XBASE_MetabaseHost: "https://${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" + XBASE_Host: "${HOST:?required}:${HTTPS_PORT:?required}" + XBASE_Subdomain: "${SUBDOMAIN:?required}" + XBASE_MetabaseHost: "${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "metabase" @@ -150,6 +155,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" stdin_open: true frontend: @@ -183,6 +190,8 @@ services: interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" nginx: image: "nginx:1.29-trixie-otel" @@ -208,12 +217,14 @@ services: ports: - "0.0.0.0:${HTTP_PORT:?required}:80" - "0.0.0.0:${HTTPS_PORT:?required}:443" - command: [ "nginx-debug", "-g", "daemon off;" ] # run in debug mode + command: [ "nginx-debug", "-g", "daemon off;" ] healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] interval: "5s" timeout: "5s" retries: "20" + labels: + autoheal: "true" volumes: data: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 6f814e6b..075fbcc1 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -71,8 +71,9 @@ services: environment: ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "production" - XBASE_Host: "https://${HOST:?required}" - XBASE_MetabaseHost: "https://${METABASE_HOST:?required}" + XBASE_Host: "${HOST:?required}" + XBASE_Subdomain: "${SUBDOMAIN:?required}" + XBASE_MetabaseHost: "${METABASE_HOST:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "metabase" From bda8e43225dedda4052fc4df588659b9f6563f0a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 09:27:02 +0100 Subject: [PATCH 674/878] Remove unsued email sender --- backend/src/Services/EmailSender.cs | 67 ------------------- backend/src/Services/IEmailSender.cs | 12 ---- backend/src/Startup.cs | 1 - .../test/Integration/CollectingEmailSender.cs | 46 ------------- .../CustomWebApplicationFactory.cs | 13 ---- backend/test/Integration/IntegrationTests.cs | 15 ----- 6 files changed, 154 deletions(-) delete mode 100644 backend/src/Services/EmailSender.cs delete mode 100644 backend/src/Services/IEmailSender.cs delete mode 100644 backend/test/Integration/CollectingEmailSender.cs diff --git a/backend/src/Services/EmailSender.cs b/backend/src/Services/EmailSender.cs deleted file mode 100644 index 155d1e9d..00000000 --- a/backend/src/Services/EmailSender.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Threading.Tasks; -using MailKit.Net.Smtp; -using MailKit.Security; -using Microsoft.Extensions.Logging; -using MimeKit; - -namespace Database.Services; - -public static partial class Log -{ - [LoggerMessage( - Level = LogLevel.Debug, - Message = "About to send email to `{Recipient}` with subject `{Subject}` and body `{Body}`")] - public static partial void AboutToSendEmail( - this ILogger logger, - (string name, string address) Recipient, - string Subject, - string Body - ); -} - -public sealed class EmailSender( - AppSettings appSettings, - ILogger logger -) -: IEmailSender -{ - public Task SendAsync( - (string name, string address) recipient, - string subject, - string body - ) - { - logger.AboutToSendEmail(recipient, subject, body); - var message = new MimeMessage(); - message.From.Add( - new MailboxAddress( - "Database", - $"database@{appSettings.Host}" - ) - ); - message.To.Add( - new MailboxAddress( - recipient.name, - recipient.address - ) - ); - message.Subject = subject; - message.Body = new TextPart("plain") - { - Text = body - }; - using (var client = new SmtpClient()) - { - client.Connect( - appSettings.Email.SmtpHost, - appSettings.Email.SmtpPort, - SecureSocketOptions.StartTlsWhenAvailable - ); - // client.Authenticate("joey", "password"); - client.Send(message); - client.Disconnect(true); - } - - return Task.FromResult(0); - } -} \ No newline at end of file diff --git a/backend/src/Services/IEmailSender.cs b/backend/src/Services/IEmailSender.cs deleted file mode 100644 index 1c7c7130..00000000 --- a/backend/src/Services/IEmailSender.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Threading.Tasks; - -namespace Database.Services; - -public interface IEmailSender -{ - public Task SendAsync( - (string name, string address) recipient, - string subject, - string body - ); -} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 8ebb893d..490b5e3b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -53,7 +53,6 @@ public void ConfigureServices(IServiceCollection services) AuthConfiguration.ConfigureServices(services, environment, _appSettings); GraphQlConfiguration.ConfigureServices(services, environment); ConfigureDatabaseServices(services); - services.AddTransient(); ConfigureRequestResponseServices(services); // ConfigureSessionServices(services); // Not used ConfigureTelemetryServices(services); diff --git a/backend/test/Integration/CollectingEmailSender.cs b/backend/test/Integration/CollectingEmailSender.cs deleted file mode 100644 index b9cd5ee6..00000000 --- a/backend/test/Integration/CollectingEmailSender.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Database.Services; - -namespace Database.Tests.Integration; - -public sealed class CollectingEmailSender - : IEmailSender -{ - private readonly List _emails; - - public CollectingEmailSender() - { - _emails = []; - } - - public IReadOnlyCollection Emails => _emails.AsReadOnly(); - - public Task SendAsync( - (string name, string address) recipient, - string subject, - string body - ) - { - _emails.Add( - new Email - ( - recipient, - subject, - body - ) - ); - return Task.FromResult(0); - } - - public void Clear() - { - _emails.Clear(); - } - - public sealed record Email( - (string name, string address) Recipient, - string Subject, - string Body - ); -} \ No newline at end of file diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index 8ab023c1..c5034708 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -20,11 +20,8 @@ public sealed class CustomWebApplicationFactory public CustomWebApplicationFactory() { - EmailSender = new CollectingEmailSender(); } - public CollectingEmailSender EmailSender { get; } - private T Get(Func what) { using var scope = Services.CreateScope(); @@ -114,16 +111,6 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) // serviceCollection.Remove(appSettingsServiceDescriptor); // } // serviceCollection.AddSingleton(newAppSettings); - // Configure `IEmailSender` - var emailSenderServiceDescriptor = - serviceCollection.SingleOrDefault(d => - d.ServiceType == typeof(IEmailSender) - ); - if (emailSenderServiceDescriptor is not null) - { - serviceCollection.Remove(emailSenderServiceDescriptor); - } - serviceCollection.AddTransient(_ => EmailSender); } ); } diff --git a/backend/test/Integration/IntegrationTests.cs b/backend/test/Integration/IntegrationTests.cs index 94e3db87..6a12c0c9 100644 --- a/backend/test/Integration/IntegrationTests.cs +++ b/backend/test/Integration/IntegrationTests.cs @@ -35,8 +35,6 @@ protected IntegrationTests() private CustomWebApplicationFactory Factory { get; } - protected CollectingEmailSender EmailSender => Factory.EmailSender; - protected AppSettings AppSettings => Factory.AppSettings; protected HttpClient HttpClient { get; } @@ -389,19 +387,6 @@ protected static string Base64Decode(string text) ); } - protected void EmailsShouldContainSingle( - (string name, string address) recipient, - string subject, - string bodyRegEx - ) - { - EmailSender.Emails.Should().ContainSingle(); - var email = EmailSender.Emails.First(); - email.Recipient.Should().Be(recipient); - email.Subject.Should().Be(subject); - email.Body.Should().MatchRegex(bodyRegEx); - } - private static ByteArrayContent MakeJsonHttpContent( TContent content ) From e4af6e95f7136487c84b6032774db3dbf365f18c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 11:22:21 +0100 Subject: [PATCH 675/878] Store PostgreSQL password in .env file --- .env.development.sample | 4 ++ .env.production.sample | 4 ++ .env.staging.sample | 4 ++ Makefile.production | 2 +- README.md | 5 +-- backend/src/AppSettings.cs | 5 +-- database.mk | 74 ++++++++++++--------------------- docker-compose.development.yaml | 10 ++--- docker-compose.production.yaml | 12 +++--- 9 files changed, 52 insertions(+), 68 deletions(-) diff --git a/.env.development.sample b/.env.development.sample index 81edd7fe..fb99a0dc 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -8,6 +8,10 @@ HTTPS_PORT=5051 METABASE_HOST=local.buildingenvelopedata.org METABASE_HTTPS_PORT=4041 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DATABASE_NAME=xbase + DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 diff --git a/.env.production.sample b/.env.production.sample index 1a5ea40b..a6146d92 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -6,6 +6,10 @@ HTTP_PORT=8000 METABASE_HOST=www.buildingenvelopedata.org +POSTGRES_USER=postgres +POSTGRES_PASSWORD= +POSTGRES_DATABASE_NAME=xbase + DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 diff --git a/.env.staging.sample b/.env.staging.sample index 0878bf0c..23f2cd62 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -6,6 +6,10 @@ HTTP_PORT=9000 METABASE_HOST=staging.buildingenvelopedata.org +POSTGRES_USER=postgres +POSTGRES_PASSWORD= +POSTGRES_DATABASE_NAME=xbase + DATABASE_ID=8a27aa0d-6026-4124-b185-4efd5cead953 OPERATOR_ID=82b9f95c-3261-463a-90fe-0e9da707af17 diff --git a/Makefile.production b/Makefile.production index a660c4c9..154dc586 100644 --- a/Makefile.production +++ b/Makefile.production @@ -171,7 +171,7 @@ execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` .PHONY : execf shellb : COMMAND = bash -shellb : execb ## Enter shell in a running `backend` container (to test database access from within the shell run `apk add postgresql-client` and `psql "host=database port=5432 user=postgres passfile=/run/secrets/postgres_passwords"`) +shellb : execb ## Enter shell in a running `backend` container .PHONY : shellb shellf : COMMAND = bash diff --git a/README.md b/README.md index 5aedb7e8..459e0dbd 100644 --- a/README.md +++ b/README.md @@ -430,10 +430,7 @@ and the pages following it. `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. - 1. Prepare PostgreSQL by generating new password files by running - `./database.mk postgres_passwords` - and creating the database by running - `./database.mk createdb`. + 1. Create the PostgreSQL database by running `./database.mk createdb`. ### Creating a release diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 5079e356..f970e0f2 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,7 +1,6 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited using System; -using Microsoft.Extensions.Hosting; namespace Database; @@ -9,8 +8,8 @@ public sealed record AppSettings { private const string GraphQlPathSegment = "/graphql/"; - public string Host { get; init; } = ""; - public string Subdomain { get; init; } = ""; + public string Host { private get; init; } = ""; + public string Subdomain { private get; init; } = ""; public Uri Uri => new($"https://{Subdomain}.{Host}", UriKind.Absolute); // TODO Consider using [Flurl](https://flurl.dev) to construct URIs. For the pitfalls of // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 diff --git a/database.mk b/database.mk index 1cecfe24..d789cdae 100755 --- a/database.mk +++ b/database.mk @@ -11,8 +11,6 @@ docker_compose = \ --file ./docker-compose.yaml \ --env-file ./.env -database_name = xbase - dump_archive_name = postgresql_dumpall.gz files_archive_name = files.gz @@ -32,22 +30,12 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` contai ${docker_compose} exec \ database \ psql \ - --username postgres \ - --dbname ${database_name} + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" .PHONY : psql -# ${docker_compose} up \ -# --remove-orphans \ -# --wait \ -# database -# ${docker_compose} exec \ -# database \ -# bash -c " \ -# createdb --username postgres ${DBNAME} ; \ -# " -createdb : DBNAME = ${database_name} createdb : CONTAINER_NAME = create_${NAME}_database -createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` +createdb : ## Create database with name `${POSTGRES_DATABASE_NAME}` -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} run \ @@ -58,23 +46,28 @@ createdb : ## Create database with name `${DBNAME}` defaulting to `xbase` docker exec \ ${CONTAINER_NAME} \ createdb \ - --username postgres \ - ${DBNAME} + --username="${POSTGRES_USER}" \ + ${POSTGRES_DATABASE_NAME} docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} .PHONY : createdb -dropdb : DBNAME = ${database_name} -dropdb : ## Drop database with name `${DBNAME}` defaulting to `xbase` - ${docker_compose} up \ - --remove-orphans \ - --wait \ +dropdb : CONTAINER_NAME = drop_${NAME}_database +dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` + -docker container stop ${CONTAINER_NAME} + -docker container rm --volumes ${CONTAINER_NAME} + ${docker_compose} run \ + --name ${CONTAINER_NAME} \ + --detach \ database - ${docker_compose} exec \ - database \ + while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + docker exec \ + ${CONTAINER_NAME} \ dropdb \ - --username postgres \ - ${DBNAME} + --username="${POSTGRES_USER}" \ + ${POSTGRES_DATABASE_NAME} + docker container stop ${CONTAINER_NAME} + docker container rm --volumes ${CONTAINER_NAME} .PHONY : dropdb sql : CONTAINER_NAME = sql_${NAME}_database @@ -91,8 +84,8 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex --echo-all \ --set=ON_ERROR_STOP=1 \ --file=- \ - --username=postgres \ - --dbname=${database_name} + --username="${POSTGRES_USER}" \ + --dbname=${POSTGRES_DATABASE_NAME} ${docker_compose} down \ --remove-orphans \ database @@ -111,8 +104,8 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex --echo-all \ --set=ON_ERROR_STOP=1 \ --file=- \ - --username=postgres \ - --dbname=${database_name} + --username="${POSTGRES_USER}" \ + --dbname=${POSTGRES_DATABASE_NAME} docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} ${docker_compose} up \ @@ -147,7 +140,7 @@ backup : ## Backup database and related data to directory with absolute path `${ ${DATABASE_CONTAINER_NAME} \ pg_dumpall \ --clean \ - --username=postgres \ + --username="${POSTGRES_USER}" \ | gzip \ > ${BACKUP_DIRECTORY}/${dump_archive_name} docker container stop ${DATABASE_CONTAINER_NAME} @@ -201,8 +194,8 @@ restore : ## Restore database and related data from directory with absolute path --echo-all \ --set=ON_ERROR_STOP=1 \ --file=- \ - --username=postgres \ - --dbname=postgres + --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" docker container stop ${DATABASE_CONTAINER_NAME} docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} @@ -237,18 +230,3 @@ restore : ## Restore database and related data from directory with absolute path --wait \ database .PHONY : restore - -# TODO The entrypoint file of the PostgreSQL image uses the file refered to by `POSTGRES_PASSWORD_FILE` and this file needs to be accessible by `other`. Why? We do not want all people to be able to read secrets! -# https://www.postgresql.org/docs/current/libpq-pgpass.html -postgres_passwords : ## Generate PostgreSQL password and passwords files whose entries are nothing but one clear-text password or of the form `hostname:port:database:username:password` (note that if the data volume already exists, then it either needs to be removed resulting in the loss of all data or the password of the PostgreSQL user needs to be changed manually by executing the SQL query `ALTER USER postgres with password '...'` or running `\password postgres` within `make psql`) - mkdir -p ./secrets - chmod 0755 ./secrets - touch ./secrets/postgres_password - chmod 0644 ./secrets/postgres_password - openssl rand -base64 32 \ - > ./secrets/postgres_password - touch ./secrets/postgres_passwords - chmod 0600 ./secrets/postgres_passwords - echo "*:*:*:*:$$(cat ./secrets/postgres_password)" \ - > ./secrets/postgres_passwords -.PHONY : postgres_passwords diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 074f7088..423347bf 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -7,9 +7,9 @@ services: volumes: - data:/var/lib/postgresql/data environment: - POSTGRES_USER: "postgres" - POSTGRES_PASSWORD: "postgres" - POSTGRES_DB: "postgres" + POSTGRES_USER: "${POSTGRES_USER}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" + POSTGRES_DB: "postgres" # default database # ports: # - "0.0.0.0:5432:5432" # For possible command-line arguments see @@ -27,7 +27,7 @@ services: ] healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username=postgres" ] + test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] interval: "5s" timeout: "5s" retries: "5" @@ -107,7 +107,7 @@ services: XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "metabase" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database=xbase; User Id=postgres; Password=postgres; Maximum Pool Size=90;" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 075fbcc1..4a0def28 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -6,12 +6,10 @@ services: image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data - - ./secrets:/run/secrets:ro environment: - POSTGRES_USER: "postgres" - POSTGRES_PASSWORD_FILE: "/run/secrets/postgres_password" - POSTGRES_DB: "postgres" - PGPASSFILE: "/run/secrets/postgres_passwords" + POSTGRES_USER: "${POSTGRES_USER}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" + POSTGRES_DB: "postgres" # default database # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see @@ -20,7 +18,7 @@ services: restart: "always" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username=postgres" ] + test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] interval: "5s" timeout: "5s" retries: "5" @@ -77,7 +75,7 @@ services: XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "metabase" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database=xbase; User Id=postgres; Passfile=/run/secrets/postgres_passwords; Maximum Pool Size=90;" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" From 119fb8c1094ee93e663d42d9f477014a442908d3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 11:34:03 +0100 Subject: [PATCH 676/878] Align development and production docker compose setup --- docker-compose.development.yaml | 10 +++++--- docker-compose.production.yaml | 44 ++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 423347bf..78d6acdb 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -20,11 +20,11 @@ services: # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: [ - "-c", - "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" + "-c", "max_connections=100" + # "-c", "log_statement=all", + # "-c", "log_duration=1" ] + restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] @@ -149,6 +149,7 @@ services: "--project", "/app/src/Database.csproj" ] + restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] @@ -218,6 +219,7 @@ services: - "0.0.0.0:${HTTP_PORT:?required}:80" - "0.0.0.0:${HTTPS_PORT:?required}:443" command: [ "nginx-debug", "-g", "daemon off;" ] + restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] interval: "5s" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 4a0def28..55c6009b 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -10,12 +10,21 @@ services: POSTGRES_USER: "${POSTGRES_USER}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" POSTGRES_DB: "postgres" # default database + # ports: + # - "0.0.0.0:5432:5432" # For possible command-line arguments see # https://www.postgresql.org/docs/current/app-postgres.html # and for possible configuration parameters like `max_connections` see # https://www.postgresql.org/docs/current/runtime-config.html - command: [ "-c", "max_connections=100" ] - restart: "always" + # For an introduction to logging see + # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ + command: + [ + "-c", "max_connections=100" + # "-c", "log_statement=all", + # "-c", "log_duration=1" + ] + restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] @@ -40,9 +49,16 @@ services: RelayOptions__SmtpServer: "${RELAY_SMTP_HOST?required}" RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS?required}" - restart: "always" + restart: "unless-stopped" # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.development.yaml + # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yaml + # ports: + # Web interface + # - "0.0.0.0:4042:80" + # SMTP server + # - "0.0.0.0:25:25" + # Imap server + # - "0.0.0.0:143:143" healthcheck: test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] interval: "5s" @@ -93,9 +109,18 @@ services: extra_hosts: - "host.docker.internal:host-gateway" # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line - # command: ["dotnet", "App.dll", "/Serilog:MinimumLevel:Default=Debug", "/Serilog:MinimumLevel:Microsoft=Debug", "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", "/Serilog:MinimumLevel:Override:Microsoft=Debug", "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", "/Serilog:MinimumLevel:Override:System=Debug"] - command: [ "dotnet", "/app/App.dll" ] - restart: "always" + command: + [ + "dotnet", + "/app/App.dll" + # "/Serilog:MinimumLevel:Default=Debug", + # "/Serilog:MinimumLevel:Microsoft=Debug", + # "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", + # "/Serilog:MinimumLevel:Override:Microsoft=Debug", + # "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", + # "/Serilog:MinimumLevel:Override:System=Debug" + ] + restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] @@ -104,6 +129,7 @@ services: retries: "20" labels: autoheal: "true" + stdin_open: true frontend: build: @@ -115,7 +141,7 @@ services: environment: NODE_ENV: "production" command: [ "yarn", "start" ] - restart: "always" + restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] interval: "5s" @@ -141,7 +167,7 @@ services: ports: - "0.0.0.0:${HTTP_PORT:?required}:80" command: [ "nginx", "-g", "daemon off;" ] - restart: "always" + restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] interval: "5s" From 0fa9f9949f3ecac2162c37ecb56733252c479eb5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 12:06:24 +0100 Subject: [PATCH 677/878] Set `COMPOSE_BAKE=true` globally and remove by now unnecessary `COMPOSE_DOCKER_CLI_BUILD=1` and `DOCKER_BUILDKIT=1` --- Makefile.development | 71 ++++++++++++++++----------------------- Makefile.production | 80 +++++++++++++++++--------------------------- 2 files changed, 58 insertions(+), 93 deletions(-) diff --git a/Makefile.development b/Makefile.development index bef471b0..bde99a5c 100644 --- a/Makefile.development +++ b/Makefile.development @@ -21,6 +21,8 @@ dotenv-linter = \ --quiet \ dotenvlinter/dotenv-linter:latest +COMPOSE_BAKE=true + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -41,69 +43,52 @@ dotenv : ## Assert that all variables in `./.env.development.sample` are availab .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} config + ${docker_compose} config .PHONY : config check : ## Check build configuration - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --check \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + ${docker_compose} build \ + --check \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : check pull : ## Pull images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull + ${docker_compose} pull .PHONY : pull # To debug errors during build add `--progress plain \` to get additional # output. build : dotenv check pull ## Build images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + ${docker_compose} build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : build bake : ## Print docker-compose file equivalent bake file - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --print \ - --pull - # --no-cache + ${docker_compose} build \ + --print \ + --pull + # --no-cache .PHONY : bake backend-build-context : ## Show the build context configured by `./backend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./backend + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./backend .PHONY : backend-build-context frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./frontend + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./frontend .PHONY : frontend-build-context remove : ## Remove stopped containers diff --git a/Makefile.production b/Makefile.production index 154dc586..c8ebd40d 100644 --- a/Makefile.production +++ b/Makefile.production @@ -39,76 +39,56 @@ dotenv : ## Assert that all variables in ./.env.production.sample are available .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} config + ${docker_compose} config .PHONY : config check : ## Check build configuration - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --check \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + ${docker_compose} build \ + --check \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) .PHONY : check pull : ## Pull images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} pull + ${docker_compose} pull .PHONY : pull # To debug errors during build add `--progress plain \` to get additional # output. build : dotenv check pull ## Build images - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - frontend - # --no-cache - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --pull \ - backend - # --no-cache + ${docker_compose} build \ + --pull \ + frontend + # --no-cache + ${docker_compose} build \ + --pull \ + backend + # --no-cache .PHONY : build bake : ## Print docker-compose file equivalent bake file - COMPOSE_BAKE=true \ - COMPOSE_DOCKER_CLI_BUILD=1 \ - DOCKER_BUILDKIT=1 \ - ${docker_compose} build \ - --print \ - --pull - # --no-cache + ${docker_compose} build \ + --print \ + --pull + # --no-cache .PHONY : bake backend-build-context : ## Show the build context configured by `./backend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./backend + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./backend .PHONY : backend-build-context frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./frontend + docker build \ + --pull \ + --no-cache \ + --progress plain \ + --file ./Dockerfile-show-build-context \ + ./frontend .PHONY : frontend-build-context remove : ## Remove stopped containers From cfd1603601c5d123c5b4c0510544863daf1cdd3f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 13:09:17 +0100 Subject: [PATCH 678/878] Put docker remote control into `docker.mk` for development and production to reduce duplication (and by using the same Makefile in development, the targets are used regularly making sure that they keep functioning) --- .env.development.sample | 1 + .env.production.sample | 1 + .env.staging.sample | 1 + Makefile.development | 297 ------------------------------- README.md | 21 +-- Makefile.production => docker.mk | 111 +++++------- tools.mk | 117 ++++++++++++ 7 files changed, 170 insertions(+), 379 deletions(-) delete mode 100644 Makefile.development rename Makefile.production => docker.mk (52%) mode change 100644 => 100755 create mode 100755 tools.mk diff --git a/.env.development.sample b/.env.development.sample index fb99a0dc..d741b361 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -1,4 +1,5 @@ NAME=database +ENVIRONMENT=development HOST=solarbuildingenvelopes.com SUBDOMAIN=local diff --git a/.env.production.sample b/.env.production.sample index a6146d92..c6caca25 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,4 +1,5 @@ NAME=database_production +ENVIRONMENT=production HOST=solarbuildingenvelopes.com SUBDOMAIN=www diff --git a/.env.staging.sample b/.env.staging.sample index 23f2cd62..04e79b67 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -1,4 +1,5 @@ NAME=database_staging +ENVIRONMENT=staging HOST=solarbuildingenvelopes.com SUBDOMAIN=staging diff --git a/Makefile.development b/Makefile.development deleted file mode 100644 index bde99a5c..00000000 --- a/Makefile.development +++ /dev/null @@ -1,297 +0,0 @@ -# Concise introduction to GNU Make: -# https://swcarpentry.github.io/make-novice/reference.html - -include ./.env - -SHELL := /usr/bin/env bash -.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c -MAKEFLAGS += --warn-undefined-variables - -docker_compose = \ - docker compose \ - --file ./docker-compose.development.yaml \ - --env-file ./.env - -dotenv-linter = \ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/mnt:ro" \ - --pull "always" \ - --quiet \ - dotenvlinter/dotenv-linter:latest - -COMPOSE_BAKE=true - -# Taken from https://www.client9.com/self-documenting-makefiles/ -help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ - printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ - }' $(MAKEFILE_LIST) -.PHONY : help -.DEFAULT_GOAL := help - -name : ## Print value of variable `NAME` - @echo ${NAME} -.PHONY : name - -dotenv : ## Assert that all variables in `./.env.development.sample` are available in `./.env` - ${dotenv-linter} diff /mnt/.env /mnt/.env.development.sample - ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.development.sample - ${dotenv-linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample - ${dotenv-linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample -.PHONY : dotenv - -config : ## Parse, resolve and render compose file in canonical format - ${docker_compose} config -.PHONY : config - -check : ## Check build configuration - ${docker_compose} build \ - --check \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) -.PHONY : check - -pull : ## Pull images - ${docker_compose} pull -.PHONY : pull - -# To debug errors during build add `--progress plain \` to get additional -# output. -build : dotenv check pull ## Build images - ${docker_compose} build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) -.PHONY : build - -bake : ## Print docker-compose file equivalent bake file - ${docker_compose} build \ - --print \ - --pull - # --no-cache -.PHONY : bake - -backend-build-context : ## Show the build context configured by `./backend/.dockerignore` - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./backend -.PHONY : backend-build-context - -frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./frontend -.PHONY : frontend-build-context - -remove : ## Remove stopped containers - ${docker_compose} rm \ - --volumes -.PHONY : remove - -remove-data : ## Remove data volumes - docker volume rm \ - ${NAME}_data -.PHONY : remove-data - -# TODO `docker compose up` does not support `--user`, see https://github.com/docker/compose/issues/1532 -up : build ## (Re)create, and start containers (after building images if necessary) - ${docker_compose} up \ - --remove-orphans \ - --wait -.PHONY : up - -down : ## Stop containers and remove containers and networks created by `up` - ${docker_compose} down \ - --remove-orphans - -rm ./frontend/queries/*.generated.ts -.PHONY : down - -restart : ## Restart all stopped and running containers - ${docker_compose} restart -.PHONY : restart - -restartb : ## Restart the backend container - ${docker_compose} restart backend -.PHONY : restartb - -attachb : ## Attach to the backend container (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) - ${docker_compose} attach backend -.PHONY : attachb - -prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes - docker system prune \ - --volumes -.PHONY : prune - -logs : ## Follow logs - ${docker_compose} logs \ - --since=1h \ - --follow -.PHONY : logs - -exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - ${CONTAINER} - ${docker_compose} exec \ - --user $(shell id --user):$(shell id --group) \ - ${CONTAINER} \ - ${COMMAND} -.PHONY : exec - -execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container -.PHONY : execb - -execf : CONTAINER = frontend -execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container -.PHONY : execf - -run : ## Run the one-time command `${COMMAND}` against a fresh `${CONTAINER}` container - ${docker_compose} run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - ${CONTAINER} \ - ${COMMAND} -.PHONY : run - -runb : CONTAINER = backend -runb : run ## runute the one-time command `${COMMAND}` against a fresh `backend` container -.PHONY : runb - -runf : CONTAINER = frontend -runf : run ## Run the one-time command `${COMMAND}` against a fresh `frontend` container -.PHONY : runf - -shellb : COMMAND = bash -shellb : runb ## Enter shell in a fresh `backend` container -.PHONY : shellb - -shellf : COMMAND = bash -shellf : execf ## Enter shell in the `frontend` container -.PHONY : shellf - -shellb-examples : COMMAND = bash -c "cd ./examples && bash" -shellb-examples : runb ## Enter Bourne-again shell, aka, bash, in the `backend` container -.PHONY : shellb-examples - -# Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md -traceb : ## Trace backend container with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` - ${docker_compose} up \ - --remove-orphans \ - --wait \ - backend - ${docker_compose} exec \ - --privileged \ - backend \ - bash -c " \ - make trace \ - " -.PHONY : traceb - -shelln : ## Enter shell in the `nginx` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - nginx - ${docker_compose} exec \ - nginx \ - bash -.PHONY : shelln - -shelld : CONTAINER = database -shelld : COMMAND = bash -shelld : exec ## Enter shell in the `database` container -.PHONY : shelld - -list : ## List all containers with health status - ${docker_compose} ps \ - --no-trunc \ - --all -.PHONY : list - -dclint = \ - docker run \ - --rm \ - --tty \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/app" \ - --pull "always" \ - --quiet \ - zavoloklom/dclint:latest \ - --config /app/.dclintrc - -hadolint = \ - docker run \ - --rm \ - --interactive \ - --user $(shell id --user):$(shell id --group) \ - --volume ./.hadolint.yaml:/.config/.hadolint.yaml \ - --pull "always" \ - --quiet \ - hadolint/hadolint:latest \ - hadolint \ - --config /.config/.hadolint.yaml - -# docker run \ -# --workdir / \ -# --volume ./checkmake.ini:/checkmake.ini \ -# --volume ./Makefile.development:/Makefile.development \ -# --volume ./Makefile.production:/Makefile.production \ -# --volume ./backend/Makefile:/Makefile.backend \ -# --volume ./frontend/Makefile:/Makefile.frontend \ -# quay.io/checkmake/checkmake \ -# /Makefile.development \ -# /Makefile.production \ -# /Makefile.backend \ -# /Makefile.frontend -lint : ## Lint .env files, Docker Compose files, and Dockerfiles - @echo Lint .env Files - ${dotenv-linter} \ - check \ - --recursive \ - --ignore-checks UnorderedKey \ - . - @echo Lint Docker Compose Files - ${dclint} . - @echo Lint Dockerfiles - for dockerfile in $(shell find . -name "Dockerfile*"); do \ - echo "... $${dockerfile}" \ - ${hadolint} - < $${dockerfile} ; \ - done -.PHONY : lint - -fix : ## Fix .env files and Docker Compose linting violations - @echo Fix .env Files - ${dotenv-linter} \ - fix \ - --no-backup \ - --recursive \ - --ignore-checks UnorderedKey \ - . - @echo Fix Docker Compose Files - ${dclint} --fix . -.PHONY : fix - -format : ## Format Dockerfiles - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --volume "$(shell pwd):/pwd" \ - --pull "always" \ - --quiet \ - ghcr.io/reteps/dockerfmt:latest \ - --indent 2 \ - --newline \ - --write \ - $(shell find . -name "Dockerfile*" -printf "/pwd/%h/%f ") -.PHONY : format diff --git a/README.md b/README.md index 459e0dbd..11de15ce 100644 --- a/README.md +++ b/README.md @@ -67,11 +67,9 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF `cp ./frontend/.env.local.development.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. -1. Prepare your remote controls GNU Make, Docker Compose, and Docker by running - - `ln --symbolic ./Makefile.development ./Makefile`, +1. Prepare your remote controls GNU Make and Docker Compose by running + - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.development.yaml ./docker-compose.yaml`. - - `ln --symbolic ./backend/Dockerfile.development ./backend/Dockerfile`, and - - `ln --symbolic ./frontend/Dockerfile.development ./frontend/Dockerfile`. 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and [GNU Make](https://www.gnu.org/software/make/). 1. List all GNU Make targets by running `make help`. @@ -121,7 +119,8 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF the `./.env` file to the remembered fingerprint in your favorite editor. 1. Create the PostgreSQL database and schema by running `./database.mk createdb migrate`. -1. Start all services and follow their logs by running `make up logs`. +1. Build and start all services and follow their logs by running + `make build up logs`. 1. In your web browser, navigate to the - web frontend at `https://local.solarbuildingenvelopes.com:5051`, - GraphQL API at `https://local.solarbuildingenvelopes.com:5051/graphql/`, @@ -252,8 +251,8 @@ manually or upgrading Npgsql, the service `backend` may throw exceptions regarding the object-relational mapping (Npgsql or EF Core). In that case it may be necessary to restart the service `backend`, for example, by running `make down up` and it may even be necessary recreate the database from scratch -by running `make down remove-data up`. Note that the latter will remove all -data from PostgreSQL, recreate the database and its schema, and seed it +by running `make down remove-data-volume up`. Note that the latter will remove +all data from PostgreSQL, recreate the database and its schema, and seed it freshly. When your hard-disk starts to grow full, it may be the case that Docker does @@ -394,11 +393,9 @@ and the pages following it. - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are host and port of the message transfer agent and a list of allowed email addresses to send emails to even in the staging environment. - 1. Prepare your remote controls GNU Make, Docker Compose, and Docker by running - - `ln --symbolic ./Makefile.${environment} ./Makefile`, - - `ln --symbolic ./docker-compose.${environment}.yaml ./docker-compose.yaml`. - - `ln --symbolic ./backend/Dockerfile.${environment} ./backend/Dockerfile`, and - - `ln --symbolic ./frontend/Dockerfile.${environment} ./frontend/Dockerfile`. + 1. Prepare your remote controls GNU Make and Docker Compose by running + - `ln --symbolic ./docker.mk ./Makefile` and + - `ln --symbolic ./docker-compose.production.yaml ./docker-compose.yaml`. 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `./certificates.mk jwt`. 1. Generate and export a GnuPG key with the passphrase diff --git a/Makefile.production b/docker.mk old mode 100644 new mode 100755 similarity index 52% rename from Makefile.production rename to docker.mk index c8ebd40d..2d65cf9e --- a/Makefile.production +++ b/docker.mk @@ -1,6 +1,3 @@ -# Concise introduction to GNU Make: -# https://swcarpentry.github.io/make-novice/reference.html - include ./.env SHELL := /usr/bin/env bash @@ -9,10 +6,9 @@ MAKEFLAGS += --warn-undefined-variables docker_compose = \ docker compose \ - --file ./docker-compose.production.yaml \ --env-file ./.env -dotenv-linter = \ +dotenv_linter = \ docker run \ --rm \ --user $(shell id --user):$(shell id --group) \ @@ -21,6 +17,8 @@ dotenv-linter = \ --quiet \ dotenvlinter/dotenv-linter:latest +COMPOSE_BAKE=true + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -33,9 +31,11 @@ name : ## Print value of variable `NAME` @echo ${NAME} .PHONY : name -dotenv : ## Assert that all variables in ./.env.production.sample are available in ./.env - ${dotenv-linter} diff /mnt/.env /mnt/.env.production.sample - ${dotenv-linter} diff /mnt/frontend/.env.local /mnt/frontend/.env.local.production.sample +dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are available in ./.env + ${dotenv_linter} diff /mnt/.env "/mnt/.env.${ENVIRONMENT}.sample" + ${dotenv_linter} diff /mnt/frontend/.env.local "/mnt/frontend/.env.local.${ENVIRONMENT}.sample" + ${dotenv_linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample + ${dotenv_linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format @@ -58,11 +58,8 @@ pull : ## Pull images build : dotenv check pull ## Build images ${docker_compose} build \ --pull \ - frontend - # --no-cache - ${docker_compose} build \ - --pull \ - backend + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) # --no-cache .PHONY : build @@ -70,56 +67,48 @@ bake : ## Print docker-compose file equivalent bake file ${docker_compose} build \ --print \ --pull - # --no-cache .PHONY : bake -backend-build-context : ## Show the build context configured by `./backend/.dockerignore` +build-context : ## Show the build context configured by `./${SERVICE}/.dockerignore`, for example, `make build-context SERVICE=backend` docker build \ --pull \ --no-cache \ --progress plain \ --file ./Dockerfile-show-build-context \ - ./backend -.PHONY : backend-build-context + ./${SERVICE} +.PHONY : build-context -frontend-build-context : ## Show the build context configured by `./frontend/.dockerignore` - docker build \ - --pull \ - --no-cache \ - --progress plain \ - --file ./Dockerfile-show-build-context \ - ./frontend -.PHONY : frontend-build-context - -remove : ## Remove stopped containers +remove : ## Remove stopped services ${docker_compose} rm \ --volumes .PHONY : remove -remove-data : ## Remove data volumes (after creating a backup in ./backup) - $(MAKE) \ - --file="$(shell pwd)/database.mk" \ - BACKUP_DIRECTORY="$(shell pwd)/backup" \ - backup +remove-data-volume : ## Remove data volumes docker volume rm \ ${NAME}_data .PHONY : remove-data -up : dotenv ## (Re)create and start containers +up : dotenv ## (Re)create and start services ${docker_compose} up \ --remove-orphans \ --wait .PHONY : up -down : ## Stop containers and remove containers and networks created by `up` +down : ## Stop services and remove services and networks created by `up` ${docker_compose} down \ --remove-orphans + -rm ./frontend/queries/*.generated.ts .PHONY : down -restart : ## Restart all stopped and running containers - ${docker_compose} restart +restart : SERVICES = "" +restart : ## Restart all or specific stopped and running services, for example, `make restart` or `make restart SERVICES="database backend"` + ${docker_compose} restart ${SERVICES} .PHONY : restart +attach : ## Attach to the `${SERVICE}` service, for example, `make attach SERVICE=backend` (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) + ${docker_compose} attach ${SERVICE} +.PHONY : attach + prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes docker system prune \ --volumes @@ -131,50 +120,32 @@ logs : ## Follow logs --follow .PHONY : logs -exec : ## Execute the one-time command `${COMMAND}` against the `${CONTAINER}` container +exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service ${docker_compose} up \ --remove-orphans \ --wait \ - ${CONTAINER} + ${SERVICE} ${docker_compose} exec \ - --user $(shell id --user):$(shell id --group) \ - ${CONTAINER} \ + ${SERVICE} \ ${COMMAND} .PHONY : exec -execb : CONTAINER = backend -execb : exec ## Execute the one-time command `${COMMAND}` against the `backend` container -.PHONY : execb - -execf : CONTAINER = frontend -execf : exec ## Execute the one-time command `${COMMAND}` against the `frontend` container -.PHONY : execf - -shellb : COMMAND = bash -shellb : execb ## Enter shell in a running `backend` container -.PHONY : shellb - -shellf : COMMAND = bash -shellf : execf ## Enter shell in a running `frontend` container -.PHONY : shellf +enter : COMMAND = bash +enter : exec ## Enter shell in the running `${SERVICE}` service, for example, `make enter SERVICE=database` or `make enter SERVICE=nginx` +.PHONY : enter -shelln : ## Enter shell in the `nginx` container - ${docker_compose} up \ - --remove-orphans \ - --wait \ - nginx - ${docker_compose} exec \ - nginx \ - bash -.PHONY : shelln -.PHONY : shelln +run : ## Run the one-time command `${COMMAND}` against a fresh `${SERVICE}` service + ${docker_compose} run \ + --rm \ + ${SERVICE} \ + ${COMMAND} +.PHONY : run -shelld : CONTAINER = database -shelld : COMMAND = bash -shelld : exec ## Enter shell in the `database` container -.PHONY : shelld +shell : COMMAND = bash +shell : run ## Enter shell in a fresh `${SERVICE}` service, for example, `make shell SERVICE=backend` or `make shell SERVICE=frontend` +.PHONY : shell -list : ## List all containers with health status +list : ## List all services with health status ${docker_compose} ps \ --no-trunc \ --all diff --git a/tools.mk b/tools.mk new file mode 100755 index 00000000..74b549b7 --- /dev/null +++ b/tools.mk @@ -0,0 +1,117 @@ +include ./.env + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +docker_compose = \ + docker compose \ + --env-file ./.env + +dotenv_linter = \ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/mnt:ro" \ + --pull "always" \ + --quiet \ + dotenvlinter/dotenv-linter:latest + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +# Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md +trace-backend : ## Trace backend service with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` + ${docker_compose} up \ + --remove-orphans \ + --wait \ + backend + ${docker_compose} exec \ + --privileged \ + backend \ + bash -c " \ + make trace \ + " +.PHONY : traceb + +dclint = \ + docker run \ + --rm \ + --tty \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/app" \ + --pull "always" \ + --quiet \ + zavoloklom/dclint:latest \ + --config /app/.dclintrc + +hadolint = \ + docker run \ + --rm \ + --interactive \ + --user $(shell id --user):$(shell id --group) \ + --volume ./.hadolint.yaml:/.config/.hadolint.yaml \ + --pull "always" \ + --quiet \ + hadolint/hadolint:latest \ + hadolint \ + --config /.config/.hadolint.yaml + +# docker run \ +# --workdir / \ +# --volume ./checkmake.ini:/checkmake.ini \ +# --volume ./Makefile.development:/Makefile.development \ +# --volume ./Makefile.production:/Makefile.production \ +# --volume ./backend/Makefile:/Makefile.backend \ +# --volume ./frontend/Makefile:/Makefile.frontend \ +# quay.io/checkmake/checkmake \ +# /Makefile.development \ +# /Makefile.production \ +# /Makefile.backend \ +# /Makefile.frontend +lint : ## Lint .env files, Docker Compose files, and Dockerfiles + @echo Lint .env Files + ${dotenv_linter} \ + check \ + --recursive \ + --ignore-checks UnorderedKey \ + . + @echo Lint Docker Compose Files + ${dclint} . + @echo Lint Dockerfiles + for dockerfile in $(shell find . -name "Dockerfile*"); do \ + echo "... $${dockerfile}" \ + ${hadolint} - < $${dockerfile} ; \ + done +.PHONY : lint + +fix : ## Fix .env files and Docker Compose linting violations + @echo Fix .env Files + ${dotenv_linter} \ + fix \ + --no-backup \ + --recursive \ + --ignore-checks UnorderedKey \ + . + @echo Fix Docker Compose Files + ${dclint} --fix . +.PHONY : fix + +format : ## Format Dockerfiles + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/pwd" \ + --pull "always" \ + --quiet \ + ghcr.io/reteps/dockerfmt:latest \ + --indent 2 \ + --newline \ + --write \ + $(shell find . -name "Dockerfile*" -printf "/pwd/%h/%f ") +.PHONY : format From 7610955b9d849bc55097bb06562c56e38de72cb3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 15:16:50 +0100 Subject: [PATCH 679/878] Simplify make targets and correct examples --- README.md | 118 +++++++++++++++++++++++++++++++--------------------- database.mk | 22 +++++----- deploy.mk | 16 +++---- 3 files changed, 89 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 11de15ce..a4f8d1b1 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ the tag `question`! All ways to contribute are presented by [CONTRIBUTING.md](https://github.com/building-envelope-data/database/blob/develop/CONTRIBUTING.md). The basis for our collaboration is decribed by our [Code of Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). + ## Contents [Getting started](#getting-started) @@ -50,6 +51,7 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF [Z shell, aka, `zsh`](https://www.zsh.org/), or shiny new [`fish`](https://fishshell.com/). + 1. Install [Git](https://git-scm.com/) by running `sudo apt install git-all` on [Debian](https://www.debian.org/)-based distributions like [Ubuntu](https://ubuntu.com/), or @@ -57,22 +59,30 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF [RPM-Package-Manager](https://rpm.org/)-based distributions like [CentOS](https://www.centos.org/). For further information see [Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). + 1. Clone the source code by running `git clone git@github.com:building-envelope-data/database.git` and navigate into the new directory `database` by running `cd ./database`. + 1. Initialize, fetch, and checkout possibly-nested submodules by running `git submodule update --init --recursive`. An alternative would have been passing `--recurse-submodules` to `git clone` above. + 1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env`, `cp ./frontend/.env.local.development.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. + 1. Prepare your remote controls GNU Make and Docker Compose by running + - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.development.yaml ./docker-compose.yaml`. + 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and [GNU Make](https://www.gnu.org/software/make/). + 1. List all GNU Make targets by running `make help`. + 1. Generate and trust a self-signed certificate authority and SSL certificates by running `./certificates.mk ssl`. If you are locally working on the metabase and the database and if you need them to communicate over HTTPS, then instead of @@ -82,22 +92,23 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF authority files from the directories `./ssl`, `./backend/ssl`, and `./frontend/ssl` of the metabase project into the respective directories in the database project (if the repository reside alongside each other by - running `mkdir ./ssl ./backend/ssl ./frontend/ssl && cp ../metabase/ssl/ca.* - ./ssl && cp ../metabase/backend/ssl/ca.* ./backend/ssl && cp - ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command + running `mkdir ./ssl ./backend/ssl ./frontend/ssl && cp ../metabase/ssl/ca.* ./ssl && cp ../metabase/backend/ssl/ca.* ./backend/ssl && cp ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command `./certificates.mk generate-ssl-certificate`. + 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `./certificates.mk jwt`. + 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk - PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information - filled in, for example, `./gpg.mk NAME="Anna Smith" COMMENT=first - EMAIL=anna.smith@fraunhofer.de key`. Then copy the key's fingerprint which + file `./backend/src/gpg-keys/.gpg` by running + `./gpg.mk key PERSON=${name} COMMENT=${comment} EMAIL=${email}` with your information + filled in, for example, + `./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. Instead of using the GNU Make target `key`, you may + 1. install [GnuPG](https://gnupg.org) as described on [Download GnuPG](https://gnupg.org/download/index.html) or [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), @@ -117,11 +128,15 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. + 1. Create the PostgreSQL database and schema by running `./database.mk createdb migrate`. + 1. Build and start all services and follow their logs by running `make build up logs`. + 1. In your web browser, navigate to the + - web frontend at `https://local.solarbuildingenvelopes.com:5051`, - GraphQL API at `https://local.solarbuildingenvelopes.com:5051/graphql/`, - REST API `https://local.solarbuildingenvelopes.com:5051/openapi/docs//`, @@ -129,6 +144,7 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF (to view for example the confirmation email sent during registration), - OpenId Connect configuration navigate to `https://local.solarbuildingenvelopes.com:5051/.well-known/openid-configuration` + Note that the port is `5051` by default. If you set the variable `HTTPS_PORT` within the `./.env` to some other value though, you need to use that value instead within the URLs. @@ -137,33 +153,32 @@ In another shell 1. Drop into `bash` with the working directory `/app`, which is mounted to the host's `./backend` directory, inside a fresh Docker container based on - `./backend/Dockerfile` by running `make shellb`. If necessary, the Docker - image is (re)built automatically, which takes a while the first time. Note - that the Docker image and containers try to use the same user and group IDs - as the ones on the host machine. This has the upside that files created - within containers in mounted directories are owned by the host user. It has - the downside that the Docker image may fail to build because the IDs may - already be taken by other users and groups in the base image. This happens - for example if you are `root` on the host machine with the user and group - IDs 0. If there is an ID collision, then you can either change the user and - group ID on the host machine (for example by logging in as another user) or - you can replace all occurrences of `shell id --group` and `shell id --user` - in `Makefile` by fixed non-colliding IDs like 1000. If you know a better - way, please + `./backend/Dockerfile` by running `make shell SERVICE=backend`. If + necessary, the Docker image is (re)built automatically, which takes a while + the first time. Note that the Docker image and containers try to use the + same user and group IDs as the ones on the host machine. This has the upside + that files created within containers in mounted directories are owned by the + host user. It has the downside that the Docker image may fail to build + because the IDs may already be taken by other users and groups in the base + image. This happens for example if you are `root` on the host machine with + the user and group IDs 0. If there is an ID collision, then you can either + change the user and group ID on the host machine (for example by logging in + as another user) or you can replace all occurrences of `shell id --group` + and `shell id --user` in `Makefile` by fixed non-colliding IDs like 1000. If + you know a better way, please [let use know on GitHub](https://github.com/building-envelope-data/database/issues/new). 1. List all backend GNU Make targets by running `make help`. 1. For example, update packages and tools by running `make update`. 1. Drop out of the container by running `exit` or pressing `Ctrl-D`. -The same works for frontend containers by running `make shellf`. - ### Migrating the Database After changing the domain model in `./backend/src/data`, you need to migrate -the database by dropping into `make shellb`, adding a migration with `make -NAME=${MIGRATION_NAME} migration`, verifying and if necessary adapting the new -migration C# code and SQL scripts, exiting the container with `exit`, and -applying the new migration to the PostgreSQL database with `./database.mk migrate`. See +the database by dropping into `make shell SERVICE=backend`, adding a migration +with `make migration NAME=${MIGRATION_NAME}`, verifying and if necessary +adapting the new migration C# code and SQL scripts, exiting the container with +`exit`, and applying the new migration to the PostgreSQL database with +`./database.mk migrate`. See [Migrations Overview](https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/) and the following pages for details. @@ -274,12 +289,10 @@ typescript: { }, ``` -The same can happen in development when running `make build` (or `yarn run -build`) in the shell entered by `make shellf`. In that case, remove the -offending import manually in the file and try again, for example using tail -like so `tail -n +5 ./__generated__/queries/... > x.tmp && mv x.tmp ...` . Do -not disable build errors in development because when you do so, build errors in -non-generated files may leak into the code base. +The same can happen in development when running `make build` (or `yarn run build`) in the shell entered by `make shell SERVICE=frontend`. In that case, +remove the offending import manually in the file and try again, for example +using tail like so `tail -n +5 ./__generated__/queries/... > x.tmp && mv x.tmp ...` . Do not disable build errors in development because when you do so, build +errors in non-generated files may leak into the code base. ## Deployment @@ -303,11 +316,14 @@ and the pages following it. `${environment}` below: 1. Set the variable `environment` by running `environment=staging` or `environment=production`. + 1. Change into the clone `${environment}` by running `cd /app/${environment}`. + 1. Open `https://www.buildingenvelopedata.org` in your favorite web browser, log into your account, navigate to the institution operating this database (which you should be a representative of), add an OpenId Connect Application with + - client ID and display name of your choice; - consent type: explicit; - endpoints: authorization, pushed authorization, introspection, @@ -323,9 +339,11 @@ and the pages following it. for example, `staging.solarbuildingenvelopes.com` or `www.solarbuildingenvelopes.com` for the product-data database of the TestLab Solar Facades. + Alternatively, after logging in, open `https://www.buildingenvelopedata.org/graphql/` and run the following mutation with your institution ID and host filled-in: + ``` mutation { createOpenIdConnectApplication( @@ -351,6 +369,7 @@ and the pages following it. } } ``` + 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, @@ -358,6 +377,7 @@ and the pages following it. particular, by setting passwords to newly generated ones, where random passwords may be generated by running `openssl rand -base64 32`. Here is some information on what the variables mean + - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; - `HOST` is the domain name with sub-domain of the deployment, in @@ -393,21 +413,24 @@ and the pages following it. - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are host and port of the message transfer agent and a list of allowed email addresses to send emails to even in the staging environment. + 1. Prepare your remote controls GNU Make and Docker Compose by running + - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.production.yaml ./docker-compose.yaml`. + 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `./certificates.mk jwt`. + 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk - PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information - filled in, for example, `make NAME="Anna Smith" COMMENT=first - EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which + file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information + filled in, for example, `make NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. Instead of using the GNU Make target `gpg`, you may + 1. install [GnuPG](https://gnupg.org) as described on [Download GnuPG](https://gnupg.org/download/index.html) or [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), @@ -427,6 +450,7 @@ and the pages following it. `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. + 1. Create the PostgreSQL database by running `./database.mk createdb`. ### Creating a release @@ -455,10 +479,10 @@ and the pages following it. 1. Enter a shell on the production machine using `ssh`. 1. Navigate into `/app/production` by running `cd /app/production`. 1. Back up the production database by running - `./database.mk BACKUP_DIRECTORY=/app/production/backup backup`. + `./database.mk backup DIR=/app/production/backup`. 1. Change to the staging environment by running `cd /app/staging`. 1. Restore the staging database from the production backup by running - `./database.mk BACKUP_DIRECTORY=/app/production/backup restore`. + `./database.mk restore DIR=/app/production/backup`. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.staging.sample` file of the release to be deployed. 1. Deploy the new release in the staging environment by running @@ -491,10 +515,10 @@ and the pages following it. ### Troubleshooting -The files `Makefile.*` contain GNU Make targets to manage Docker containers +The file `docker.mk` contain GNU Make targets to manage Docker containers like `up` and `down`, to follow Docker container logs with `logs`, to drop into -shells inside running Docker containers like `shellb` for the backend service -and `shellf` for the frontend service, and to list information about Docker +shells inside running Docker containers like `shell SERVICE=backend` for the backend service +and `shell SERVICE=frontend` for the frontend service, and to list information about Docker like `list` and `list-services`. The Makefile `./deploy.mk` contains GNU Make targets to deploy a new release or @@ -521,14 +545,13 @@ If the database container restarts indefinitely and its logs say PANIC: could not locate a valid checkpoint record ``` -for example preceded by `LOG: invalid resource manager ID in primary checkpoint -record` or `LOG: invalid primary checkpoint record`, then the database is +for example preceded by `LOG: invalid resource manager ID in primary checkpoint record` or `LOG: invalid primary checkpoint record`, then the database is corrupt. For example, the write-ahead log (WAL) may be corrupt because the database was not shut down cleanly. One solution is to restore the database from a backup by running ``` -./database.mk BACKUP_DIRECTORY=/app/data/backups/20XX-XX-XX_XX_XX_XX/ restore +./database.mk restore DIR=/app/data/backups/20XX-XX-XX_XX_XX_XX/ ``` where the `X`s need to be replaced by proper values. Another solution is to @@ -563,14 +586,13 @@ under /app/staging before doing it in `production` under /app/production. `ssh -CvX -A cloud@IpAdressOfCloudServer`. 1. Navigate to the production environment by running `cd /app/production`. 1. Make a database backup by running `DATE=$(date +"%Y-%m-%d_%H_%M_%S")` and - `./database.mk BACKUP_DIRECTORY=/app/data/backups/${DATE} backup` + `./database.mk backup DIR=/app/data/backups/${DATE}` 1. Navigate to the staging environment by running `cd /app/staging`. 1. Load the backup into the staging database by running - `./database.mk BACKUP_DIRECTORY=/app/data/backups/${DATE} restore`. + `./database.mk restore DIR=/app/data/backups/${DATE}`. 1. Drop into `psql` by running `./database.mk psql`. 1. List all tables in the schema `database` by running `\dt database.*`. -1. List all optical data records by running `select * from - database.optical_data;` and remember for example one identifier of a record +1. List all optical data records by running `select * from database.optical_data;` and remember for example one identifier of a record that you want to update. 1. Update a single field by running `update database.optical_data set "Description" = '...' where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. 1. Delete a faulty record by running `delete from database.optical_data where "Id" = 'f07499ab-f119-471f-8aad-d3c016676bce';`. diff --git a/database.mk b/database.mk index d789cdae..eb7044df 100755 --- a/database.mk +++ b/database.mk @@ -71,12 +71,12 @@ dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` .PHONY : dropdb sql : CONTAINER_NAME = sql_${NAME}_database -sql : ## Run the SQL script in the file `${SQL}` in the database service, for example, `make SQL=./my.sql sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `./database.mk sql SCRIPT=./my.sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) ${docker_compose} up \ --remove-orphans \ --wait \ database - cat ${SQL} \ + cat ${SCRIPT} \ | ${docker_compose} exec \ --no-tty \ database \ @@ -96,7 +96,7 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex --detach \ database while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - cat ${SQL} \ + cat ${SCRIPT} \ | docker exec \ --no-tty \ ${CONTAINER_NAME} \ @@ -114,7 +114,7 @@ sql : ## Run the SQL script in the file `${SQL}` in the database service, for ex database .PHONY : sql -migrate : SQL = ./backend/src/Migrations/migrate.sql +migrate : SCRIPT = ./backend/src/Migrations/migrate.sql migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) .PHONY : migrate @@ -124,8 +124,8 @@ migrate : sql ## Migrate database by running the idempotent SQL script ./backen # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database backup : FILES_CONTAINER_NAME = backup_${NAME}_files -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` - mkdir --parents ${BACKUP_DIRECTORY} +backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` + mkdir --parents ${DIR} ${docker_compose} down \ --remove-orphans \ database @@ -142,7 +142,7 @@ backup : ## Backup database and related data to directory with absolute path `${ --clean \ --username="${POSTGRES_USER}" \ | gzip \ - > ${BACKUP_DIRECTORY}/${dump_archive_name} + > ${DIR}/${dump_archive_name} docker container stop ${DATABASE_CONTAINER_NAME} docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} @@ -154,7 +154,7 @@ backup : ## Backup database and related data to directory with absolute path `${ docker run \ --rm \ --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${BACKUP_DIRECTORY}:/backup \ + --volume ${DIR}:/backup \ debian:bullseye-slim \ tar \ --verbose \ @@ -173,7 +173,7 @@ backup : ## Backup database and related data to directory with absolute path `${ restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35 restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` +restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` ${docker_compose} down \ --remove-orphans \ database @@ -186,7 +186,7 @@ restore : ## Restore database and related data from directory with absolute path --detach \ database while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - gunzip --stdout ${BACKUP_DIRECTORY}/${dump_archive_name} \ + gunzip --stdout ${DIR}/${dump_archive_name} \ | docker exec \ --interactive \ ${DATABASE_CONTAINER_NAME} \ @@ -207,7 +207,7 @@ restore : ## Restore database and related data from directory with absolute path docker run \ --rm \ --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${BACKUP_DIRECTORY}:/backup \ + --volume ${DIR}:/backup \ debian:bullseye-slim \ bash -cx " \ cd /app/files && \ diff --git a/deploy.mk b/deploy.mk index f6b5f482..b6d2a530 100755 --- a/deploy.mk +++ b/deploy.mk @@ -26,13 +26,13 @@ help : ## Print this help # redeploy the reverse proxy by running # `cd /app/machine; make deploy;` # backup, migrate, and deploy all services by running -# `make BACKUP_DIRECTORY=$(pwd)/backup down backup migrate deploy-services` +# `./deploy.mk down backup migrate deploy-services DIR=$(pwd)/backup` # first in `cd /app/staging` and checking that everything works as expected, # and finally in `cd /app/production` and checking that everything works as # expected. Before trying it on staging, I usually play the database from # production into staging. -deploy : BACKUP_DIRECTORY = $(shell pwd)/backup -deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `make TARGET=v1.0.0 deploy` +deploy : DIR = $(shell pwd)/backup +deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk TARGET=v1.0.0 deploy` $(MAKE) begin-maintenance $(MAKE) store-commit $(MAKE) backup @@ -45,7 +45,7 @@ deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `make TARGET .PHONY : deploy rollback : TARGET = $(shell cat ./commit) -rollback : BACKUP_DIRECTORY = $(shell pwd)/backup +rollback : DIR = $(shell pwd)/backup rollback : ## Rollback deployment attempt (use commit hash stored in `./commit` and database backup stored in `./backup/`) $(MAKE) begin-maintenance $(MAKE) checkout-target @@ -65,18 +65,18 @@ end-maintenance : ## End maintenance rm ./nginx/html/maintenance.html .PHONY : end-maintenance -backup : ## Backup database and related data to directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `make BACKUP_DIRECTORY=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") backup` +backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./deploy.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` $(MAKE) \ --file="$(shell pwd)/database.mk" \ - BACKUP_DIRECTORY=${BACKUP_DIRECTORY} \ + DIR=${DIR} \ backup .PHONY : backup restore : CONTAINER_NAME = restore_${NAME}_database -restore : ## Restore database and related data from directory with absolute path `${BACKUP_DIRECTORY}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `make BACKUP_DIRECTORY=/app/data/backups/2021-04-22_15_43_35/ restore (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` +restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./deploy.mk restore DIR=/app/data/backups/2021-04-22_15_43_35/` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` $(MAKE) \ --file="$(shell pwd)/database.mk" \ - BACKUP_DIRECTORY=${BACKUP_DIRECTORY} \ + DIR=${DIR} \ backup .PHONY : restore From 8b0eff2a1ff6a09d1f1dee0ccb0c3d6fe6cee1b2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 15:37:41 +0100 Subject: [PATCH 680/878] Add subdomain to extra hosts --- docker-compose.development.yaml | 10 +++++----- docker-compose.production.yaml | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 78d6acdb..fb17b75d 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -40,7 +40,7 @@ services: - emails:/smtp4dev environment: ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${HOST:?required}" + ServerOptions__HostName: "${SUBDOMAIN:?required}.${HOST:?required}" ServerOptions__Database: "database.db" ServerOptions__NumberOfMessagesToKeep: "100" ServerOptions__NumberOfSessionsToKeep: "100" @@ -106,7 +106,7 @@ services: XBASE_MetabaseHost: "${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" - XBASE_Database__SchemaName: "metabase" + XBASE_Database__SchemaName: "database" XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" @@ -131,7 +131,7 @@ services: GPG_TTY: "/dev/pts/0" extra_hosts: - "host.docker.internal:host-gateway" - - "${HOST:?required}:host-gateway" + - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" - "${METABASE_HOST:?required}:host-gateway" # user: "1000:1000" command: @@ -181,7 +181,7 @@ services: environment: NODE_ENV: "development" extra_hosts: - - "${HOST:?required}:host-gateway" + - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" # user: "1000:1000" # ports: # - "0.0.0.0:8081:8080" @@ -213,7 +213,7 @@ services: # - /var/cache/nginx # ... # - /var/run # ... environment: - SERVER_NAME: "${HOST:?required}" + SERVER_NAME: "${SUBDOMAIN:?required}.${HOST:?required}" SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?required}" ports: - "0.0.0.0:${HTTP_PORT:?required}:80" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 55c6009b..9a9314db 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -40,7 +40,7 @@ services: - emails:/smtp4dev environment: ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${HOST:?required}" + ServerOptions__HostName: "${SUBDOMAIN:?required}.${HOST:?required}" ServerOptions__Database: "database.db" ServerOptions__NumberOfMessagesToKeep: "100" ServerOptions__NumberOfSessionsToKeep: "100" @@ -90,7 +90,7 @@ services: XBASE_MetabaseHost: "${METABASE_HOST:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" - XBASE_Database__SchemaName: "metabase" + XBASE_Database__SchemaName: "database" XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" From f24b2b5d0f6948428943881c5a85df4508474a7c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 16:07:24 +0100 Subject: [PATCH 681/878] Suppress browser refresh --- docker-compose.development.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index fb17b75d..8800bab5 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -123,6 +123,8 @@ services: DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "10" + DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER: "true" + DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH: "true" # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING: "1" # ASPNETCORE_DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_LOGGING_SPECIFIER: "all" From 42f1a18dd33acd6ac95f743fe31ed5ab4f3a0069 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 16:12:07 +0100 Subject: [PATCH 682/878] Remove empty services --- docker.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker.mk b/docker.mk index 2d65cf9e..71810017 100755 --- a/docker.mk +++ b/docker.mk @@ -100,7 +100,7 @@ down : ## Stop services and remove services and networks created by `up` -rm ./frontend/queries/*.generated.ts .PHONY : down -restart : SERVICES = "" +restart : SERVICES = restart : ## Restart all or specific stopped and running services, for example, `make restart` or `make restart SERVICES="database backend"` ${docker_compose} restart ${SERVICES} .PHONY : restart From 32d24d512bd823770a3c0c17dba397f5be2f31cb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 16:19:27 +0100 Subject: [PATCH 683/878] Add missing shebang lines --- database.mk | 2 ++ deploy.mk | 4 +++- docker.mk | 6 ++++-- tools.mk | 4 ++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/database.mk b/database.mk index eb7044df..947fa361 100755 --- a/database.mk +++ b/database.mk @@ -6,6 +6,8 @@ SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables +COMPOSE_BAKE=true + docker_compose = \ docker compose \ --file ./docker-compose.yaml \ diff --git a/deploy.mk b/deploy.mk index b6d2a530..13e03977 100755 --- a/deploy.mk +++ b/deploy.mk @@ -6,6 +6,8 @@ SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables +COMPOSE_BAKE=true + docker_compose = \ docker compose \ --file ./docker-compose.yaml \ @@ -105,7 +107,7 @@ checkout-target : ## Fetch and checkout `${TARGET}` # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. deploy-services : ## Deploy services - $(MAKE) build + $(MAKE) --file=./docker.mk build ${docker_compose} up \ --force-recreate \ --renew-anon-volumes \ diff --git a/docker.mk b/docker.mk index 71810017..2a0333be 100755 --- a/docker.mk +++ b/docker.mk @@ -1,9 +1,13 @@ +#!/usr/bin/env -S make --file + include ./.env SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables +COMPOSE_BAKE=true + docker_compose = \ docker compose \ --env-file ./.env @@ -17,8 +21,6 @@ dotenv_linter = \ --quiet \ dotenvlinter/dotenv-linter:latest -COMPOSE_BAKE=true - # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ diff --git a/tools.mk b/tools.mk index 74b549b7..db60f200 100755 --- a/tools.mk +++ b/tools.mk @@ -1,9 +1,13 @@ +#!/usr/bin/env -S make --file + include ./.env SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables +COMPOSE_BAKE=true + docker_compose = \ docker compose \ --env-file ./.env From e1bae0868de72f8f8ec54affec9b8429d1870e71 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 16:42:05 +0100 Subject: [PATCH 684/878] Place argument after target --- README.md | 2 +- backend/Makefile | 10 +++++----- gpg.mk | 2 +- tools.mk | 8 +++----- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a4f8d1b1..a6451ea5 100644 --- a/README.md +++ b/README.md @@ -425,7 +425,7 @@ and the pages following it. 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information - filled in, for example, `make NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de gpg`. Then copy the key's fingerprint which + filled in, for example, `make gpg NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. diff --git a/backend/Makefile b/backend/Makefile index c4398496..c358bdf5 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -26,7 +26,7 @@ help : ## Print this help # dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal # dotnet retest -- --filter "FullyQualifiedName~${x}" --verbosity minimal test : ARGS = -test : ## Run (previously failed) tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"' test` +test : ## Run (previously failed) tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make test ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"'` @echo "--------------------------------------------------" @echo "Complete test list" @echo "--------------------------------------------------" @@ -82,7 +82,7 @@ analyze : ## Analyze with Roslynator .PHONY : analyze # https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html#install-and-use-resharper-command-line-tools-as-net-core-tools -inspect : ## Inspect with ReSharper, for example, `make OUTPUT=./inspection.xml inspect` +inspect : ## Inspect with ReSharper, for example, `make inspect OUTPUT=./inspection.xml` dotnet tool run jb inspectcode --build ./Database.sln -o ${OUTPUT} .PHONY : inspect @@ -148,7 +148,7 @@ update : update-packages update-tools ## Update packages and tools .PHONY : update restore : VERBOSITY = normal -restore : ## Restore packages and tools with verbosity level `${VERBOSITY}` that defaults to `normal` (allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]), for example, `make VERBOSITY=detailed restore` +restore : ## Restore packages and tools with verbosity level `${VERBOSITY}` that defaults to `normal` (allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic]), for example, `make restore VERBOSITY=detailed` dotnet restore --verbosity ${VERBOSITY} .PHONY : restore @@ -156,7 +156,7 @@ dotbuild : ## Build projects dotnet build .PHONY : dotbuild -dedup : ## Dedeuplicate code lines matching the pattern `${PATTERN}`, for example, `make PATTERN="using Infrastructure.Aggregates" dedup` +dedup : ## Dedeuplicate code lines matching the pattern `${PATTERN}`, for example, `make dedup PATTERN="using Infrastructure.Aggregates"` find . -name "*.cs" \ | xargs -n 1 \ gawk -i inplace "{ \ @@ -187,7 +187,7 @@ has-pending-model-changes : ## Check whether there have been any model changes m .PHONY : has-pending-model-changes # https://docs.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#sql-scripts -migration : ## Add migration with name `${NAME}`, generate corresponding idempotent migration SQL script, and append it to ./src/Migrations/migrate.sql, for example, `make NAME=AddStatementToDataApproval migration` +migration : ## Add migration with name `${NAME}`, generate corresponding idempotent migration SQL script, and append it to ./src/Migrations/migrate.sql, for example, `make migration NAME=AddStatementToDataApproval` old=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ echo old=$$old && \ dotnet ef \ diff --git a/gpg.mk b/gpg.mk index be023015..a5473b30 100755 --- a/gpg.mk +++ b/gpg.mk @@ -16,7 +16,7 @@ help : ## Print this help # Keep file name .gpg in sync with the one in `AppSettings.cs` key : COMMENT = -key : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de gpg` +key : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make gpg PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` docker run \ --rm \ --user $(shell id --user):$(shell id --group) \ diff --git a/tools.mk b/tools.mk index db60f200..3924afa9 100755 --- a/tools.mk +++ b/tools.mk @@ -30,7 +30,7 @@ help : ## Print this help .DEFAULT_GOAL := help # Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md -trace-backend : ## Trace backend service with identifier `${CONTAINER_ID}`, for example, `make CONTAINER_ID=c1b82eb6e03c trace-backend` +trace-backend : ## Trace the dotnet process `Metabase` within the backend service ${docker_compose} up \ --remove-orphans \ --wait \ @@ -38,10 +38,8 @@ trace-backend : ## Trace backend service with identifier `${CONTAINER_ID}`, for ${docker_compose} exec \ --privileged \ backend \ - bash -c " \ - make trace \ - " -.PHONY : traceb + make trace +.PHONY : trace-backend dclint = \ docker run \ From 4e8683974bfa5a2ae9b9e2f020148804ced6536a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 17:01:14 +0100 Subject: [PATCH 685/878] Start nginx even if some services are unhealthy to make at least the other services usable --- docker-compose.development.yaml | 6 +++--- docker-compose.production.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 8800bab5..d17fce82 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -200,11 +200,11 @@ services: image: "nginx:1.29-trixie-otel" depends_on: backend: - condition: "service_healthy" + # condition: "service_healthy" email: - condition: "service_healthy" + # condition: "service_healthy" frontend: - condition: "service_healthy" + # condition: "service_healthy" volumes: - ./ssl:/etc/nginx/ssl:ro - ./nginx/templates/development:/etc/nginx/templates:ro diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 9a9314db..9383d712 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -154,11 +154,11 @@ services: image: "nginx:1.29-trixie-otel" depends_on: backend: - condition: "service_healthy" + # condition: "service_healthy" email: - condition: "service_healthy" + # condition: "service_healthy" frontend: - condition: "service_healthy" + # condition: "service_healthy" volumes: - ./nginx/templates/production:/etc/nginx/templates:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro From ed9db69060e9565656ce8dd4f90b0771bf6c4e64 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 17:04:33 +0100 Subject: [PATCH 686/878] Specify the depends-on condition explicitly --- docker-compose.development.yaml | 6 +++--- docker-compose.production.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index d17fce82..124a9ebf 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -200,11 +200,11 @@ services: image: "nginx:1.29-trixie-otel" depends_on: backend: - # condition: "service_healthy" + condition: "service_started" email: - # condition: "service_healthy" + condition: "service_started" frontend: - # condition: "service_healthy" + condition: "service_started" volumes: - ./ssl:/etc/nginx/ssl:ro - ./nginx/templates/development:/etc/nginx/templates:ro diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 9383d712..7f2d783d 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -154,11 +154,11 @@ services: image: "nginx:1.29-trixie-otel" depends_on: backend: - # condition: "service_healthy" + condition: "service_started" email: - # condition: "service_healthy" + condition: "service_started" frontend: - # condition: "service_healthy" + condition: "service_started" volumes: - ./nginx/templates/production:/etc/nginx/templates:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro From 8f99b7373383ce5569e57608acdd7787a66a337c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 17:10:56 +0100 Subject: [PATCH 687/878] Add scheme to metabase host --- backend/src/AppSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index f970e0f2..01e96109 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -16,7 +16,7 @@ public sealed record AppSettings public Uri DatabaseGraphQlEndpoint => new UriBuilder(Uri) { Path = GraphQlPathSegment }.Uri; public string MetabaseHost { get; init; } = ""; - public Uri MetabaseHostUri => new(MetabaseHost, UriKind.Absolute); + public Uri MetabaseHostUri => new($"https://{MetabaseHost}", UriKind.Absolute); public Uri MetabaseGraphQlEndpoint => new UriBuilder(MetabaseHostUri) { Path = GraphQlPathSegment }.Uri; public Guid DatabaseId { get; init; } @@ -72,8 +72,8 @@ public sealed record DatabaseSettings public sealed record OpenTelemetrySettings { - public string Host { get; init; } = ""; - public int GrpcPort { get; init; } + public string Host { private get; init; } = ""; + public int GrpcPort { private get; init; } public Uri GrpcUri => new UriBuilder( scheme: "http", From 53c15f8680b7b2144e7128f2a7941859b5498d25 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 17:32:13 +0100 Subject: [PATCH 688/878] Remove unused email setup --- .env.development.sample | 3 -- .env.production.sample | 6 --- .env.staging.sample | 6 --- README.md | 6 --- backend/src/AppSettings.cs | 15 +------ docker-compose.development.yaml | 36 ----------------- docker-compose.production.yaml | 40 ------------------- .../development/default.conf.template | 18 --------- .../production/default.conf.template | 18 --------- 9 files changed, 1 insertion(+), 147 deletions(-) diff --git a/.env.development.sample b/.env.development.sample index d741b361..dc5c80d9 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -30,9 +30,6 @@ GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password -SMTP_HOST=email -SMTP_PORT=25 - CERTIFICATE_AUTHORITY_HOST=ca.org CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca CERTIFICATE_AUTHORITY_PASSWORD=ca-password diff --git a/.env.production.sample b/.env.production.sample index c6caca25..b1ca6662 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -27,9 +27,3 @@ GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= - -SMTP_HOST=mail-mtaKA26.fraunhofer.de -SMTP_PORT=25 -RELAY_SMTP_HOST= -RELAY_SMTP_PORT=25 -RELAY_ALLOWED_EMAILS= diff --git a/.env.staging.sample b/.env.staging.sample index 04e79b67..db809a15 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -27,9 +27,3 @@ GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= - -SMTP_HOST=email -SMTP_PORT=25 -RELAY_SMTP_HOST=mail-mtaKA26.fraunhofer.de -RELAY_SMTP_PORT=25 -RELAY_ALLOWED_EMAILS= diff --git a/README.md b/README.md index a6451ea5..ab3f72f3 100644 --- a/README.md +++ b/README.md @@ -407,12 +407,6 @@ and the pages following it. - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to encrypt and sign JSON web tokens (JWT) used by OpenId Connect; - - `SMTP_HOST` and `SMTP_PORT` are host and port of the message transfer - agent to be used to send emails through the Simple Mail Transfer - Protocol (SMTP); - - `RELAY_SMTP_HOST`, `RELAY_SMTP_PORT`, and `RELAY_ALLOWED_EMAILS` are - host and port of the message transfer agent and a list of allowed - email addresses to send emails to even in the staging environment. 1. Prepare your remote controls GNU Make and Docker Compose by running diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 01e96109..35a69293 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -15,7 +15,7 @@ public sealed record AppSettings // using `Uri` as below see the comments to https://stackoverflow.com/questions/372865/path-combine-for-urls/1527643#1527643 public Uri DatabaseGraphQlEndpoint => new UriBuilder(Uri) { Path = GraphQlPathSegment }.Uri; - public string MetabaseHost { get; init; } = ""; + public string MetabaseHost { private get; init; } = ""; public Uri MetabaseHostUri => new($"https://{MetabaseHost}", UriKind.Absolute); public Uri MetabaseGraphQlEndpoint => new UriBuilder(MetabaseHostUri) { Path = GraphQlPathSegment }.Uri; @@ -26,9 +26,7 @@ public sealed record AppSettings public OpenIdConnectClientSettings OpenIdConnectClient { get; init; } = new(); public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; init; } = new(); - public LoggingSettings Logging { get; init; } = new(); public JsonWebTokenSettings JsonWebToken { get; init; } = new(); - public EmailSettings Email { get; init; } = new(); public DatabaseSettings Database { get; init; } = new(); public OpenTelemetrySettings OpenTelemetry { get; init; } = new(); @@ -47,23 +45,12 @@ public sealed record GnupgSecretSigningKeySettings public string Fingerprint { get; init; } = ""; }; - public sealed record LoggingSettings - { - public bool EnableSensitiveDataLogging { get; init; } - }; - public sealed record JsonWebTokenSettings { public string EncryptionCertificatePassword { get; init; } = ""; public string SigningCertificatePassword { get; init; } = ""; }; - public sealed record EmailSettings - { - public string SmtpHost { get; init; } = ""; - public int SmtpPort { get; init; } - }; - public sealed record DatabaseSettings { public string ConnectionString { get; set; } = ""; diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 124a9ebf..ec591258 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -34,35 +34,6 @@ services: labels: autoheal: "true" - email: - image: "rnwood/smtp4dev:v3.10" - volumes: - - emails:/smtp4dev - environment: - ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${SUBDOMAIN:?required}.${HOST:?required}" - ServerOptions__Database: "database.db" - ServerOptions__NumberOfMessagesToKeep: "100" - ServerOptions__NumberOfSessionsToKeep: "100" - ServerOptions__TlsMode: "None" - ServerOptions__TlsCertificate: "" - # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yaml - # ports: - # Web interface - # - "0.0.0.0:4042:80" - # SMTP server - # - "0.0.0.0:25:25" - # Imap server - # - "0.0.0.0:143:143" - healthcheck: - test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] - interval: "5s" - timeout: "5s" - retries: "20" - labels: - autoheal: "true" - backend: build: context: "./backend" @@ -73,8 +44,6 @@ services: depends_on: database: condition: "service_healthy" - email: - condition: "service_healthy" volumes: - ./backend:/app - files:/app/src/files @@ -113,8 +82,6 @@ services: XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" - XBASE_Email__SmtpHost: "${SMTP_HOST:?required}" - XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" @@ -201,8 +168,6 @@ services: depends_on: backend: condition: "service_started" - email: - condition: "service_started" frontend: condition: "service_started" volumes: @@ -232,7 +197,6 @@ services: volumes: data: - emails: files: gnupg: backend_vscode_server: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 7f2d783d..b5e1d732 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -34,39 +34,6 @@ services: labels: autoheal: "true" - email: - image: "rnwood/smtp4dev:v3.10" - volumes: - - emails:/smtp4dev - environment: - ServerOptions__BasePath: "/email" - ServerOptions__HostName: "${SUBDOMAIN:?required}.${HOST:?required}" - ServerOptions__Database: "database.db" - ServerOptions__NumberOfMessagesToKeep: "100" - ServerOptions__NumberOfSessionsToKeep: "100" - ServerOptions__TlsMode: "None" - ServerOptions__TlsCertificate: "" - RelayOptions__SmtpServer: "${RELAY_SMTP_HOST?required}" - RelayOptions__SmtpPort: "${RELAY_SMTP_PORT:?required}" - RelayOptions__AllowedEmailsString: "${RELAY_ALLOWED_EMAILS?required}" - restart: "unless-stopped" - # For all configuration options see - # https://github.com/rnwood/smtp4dev/blob/master/docker-compose.yaml - # ports: - # Web interface - # - "0.0.0.0:4042:80" - # SMTP server - # - "0.0.0.0:25:25" - # Imap server - # - "0.0.0.0:143:143" - healthcheck: - test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://email:80 || exit 1" ] - interval: "5s" - timeout: "5s" - retries: "20" - labels: - autoheal: "true" - backend: build: context: "./backend" @@ -74,8 +41,6 @@ services: depends_on: database: condition: "service_healthy" - email: - condition: "service_healthy" volumes: - backend_logs:/app/logs - files:/app/files @@ -97,8 +62,6 @@ services: XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" - XBASE_Email__SmtpHost: "${SMTP_HOST:?required}" - XBASE_Email__SmtpPort: "${SMTP_PORT:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" @@ -155,8 +118,6 @@ services: depends_on: backend: condition: "service_started" - email: - condition: "service_started" frontend: condition: "service_started" volumes: @@ -178,7 +139,6 @@ services: volumes: data: - emails: files: gnupg: backend_logs: diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index d84d2cad..1289a046 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -28,7 +28,6 @@ limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=email:10m rate=30r/s; server_tokens off; @@ -52,10 +51,6 @@ upstream frontend { server frontend:8080; } -upstream email { - server email:80; -} - server { listen *:80; add_header Strict-Transport-Security max-age=15768000; @@ -140,19 +135,6 @@ server { limit_req zone=health burst=2 nodelay; } - location /email { - proxy_pass http://email; - limit_req zone=email burst=20 nodelay; - } - - location /email/hubs { - proxy_pass http://email; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_cache_bypass $http_upgrade; - limit_req zone=email burst=20 nodelay; - } - error_page 503 @maintenance; location @maintenance { internal; diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index c6b8b98e..d1d09622 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -28,7 +28,6 @@ limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=email:10m rate=30r/s; server_tokens off; @@ -52,10 +51,6 @@ upstream frontend { server frontend:8080; } -upstream email { - server email:80; -} - server { listen *:80; @@ -123,19 +118,6 @@ server { limit_req zone=health burst=2 nodelay; } - location /email { - proxy_pass http://email; - limit_req zone=email burst=20 nodelay; - } - - location /email/hubs { - proxy_pass http://email; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_cache_bypass $http_upgrade; - limit_req zone=email burst=20 nodelay; - } - error_page 503 @maintenance; location @maintenance { internal; From 56462a738c7342112c6a5c509978b522045ec637 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 17:38:38 +0100 Subject: [PATCH 689/878] Log GraphQL query and variables in debug mode --- .../ErrorLoggingDiagnosticEventListener.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index a714ec2d..39f1f9d9 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -99,13 +99,22 @@ [LogProperties] IError error [LoggerMessage( Level = LogLevel.Error, - Message = "Unexpected execution error while executing: {Document} with {Variables}")] + Message = "Unexpected execution error while executing: {Document}\nVariables: {Variables}")] public static partial void UnexpectedExecutionException( this ILogger logger, IOperationDocument? document, string? variables, Exception exception ); + + [LoggerMessage( + Level = LogLevel.Information, + Message = "Executed:\n{Document}\nVariables: {Variables}")] + public static partial void Executed( + this ILogger logger, + string document, + string? variables + ); } // Inspired by https://chillicream.com/blog/2019/03/19/logging-with-hotchocolate @@ -239,6 +248,16 @@ private sealed partial class RequestScope(ILogger 0 } operationResult) { From d0f3231f46a0f407128a35b4227d8bd481fa9c2b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 19:22:32 +0100 Subject: [PATCH 690/878] Silence `rm` even if the glob pattern matches no files --- docker.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker.mk b/docker.mk index 2a0333be..cc58fb6a 100755 --- a/docker.mk +++ b/docker.mk @@ -99,7 +99,8 @@ up : dotenv ## (Re)create and start services down : ## Stop services and remove services and networks created by `up` ${docker_compose} down \ --remove-orphans - -rm ./frontend/queries/*.generated.ts + -rm --force \ + ./frontend/queries/*.generated.ts .PHONY : down restart : SERVICES = From 7384edf2876f5be163f26cf8e9bcaa0059d3c16c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 19:42:20 +0100 Subject: [PATCH 691/878] Fix GNU Make target `sql` --- database.mk | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/database.mk b/database.mk index 947fa361..ae4965d3 100755 --- a/database.mk +++ b/database.mk @@ -74,20 +74,6 @@ dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` sql : CONTAINER_NAME = sql_${NAME}_database sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `./database.mk sql SCRIPT=./my.sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - ${docker_compose} up \ - --remove-orphans \ - --wait \ - database - cat ${SCRIPT} \ - | ${docker_compose} exec \ - --no-tty \ - database \ - psql \ - --echo-all \ - --set=ON_ERROR_STOP=1 \ - --file=- \ - --username="${POSTGRES_USER}" \ - --dbname=${POSTGRES_DATABASE_NAME} ${docker_compose} down \ --remove-orphans \ database @@ -100,7 +86,6 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done cat ${SCRIPT} \ | docker exec \ - --no-tty \ ${CONTAINER_NAME} \ psql \ --echo-all \ From 3455a52ef9aa11792cdfcd8fb1d48155a6927c10 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 19:46:13 +0100 Subject: [PATCH 692/878] Shorten service deployment name --- deploy.mk | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/deploy.mk b/deploy.mk index 13e03977..5390cab2 100755 --- a/deploy.mk +++ b/deploy.mk @@ -28,7 +28,7 @@ help : ## Print this help # redeploy the reverse proxy by running # `cd /app/machine; make deploy;` # backup, migrate, and deploy all services by running -# `./deploy.mk down backup migrate deploy-services DIR=$(pwd)/backup` +# `./deploy.mk down backup migrate services DIR=$(pwd)/backup` # first in `cd /app/staging` and checking that everything works as expected, # and finally in `cd /app/production` and checking that everything works as # expected. Before trying it on staging, I usually play the database from @@ -41,7 +41,7 @@ deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk $(MAKE) fetch-all $(MAKE) checkout-target $(MAKE) migrate - $(MAKE) deploy-services + $(MAKE) services $(MAKE) run-tests $(MAKE) end-maintenance .PHONY : deploy @@ -52,7 +52,7 @@ rollback : ## Rollback deployment attempt (use commit hash stored in `./commit` $(MAKE) begin-maintenance $(MAKE) checkout-target $(MAKE) restore - $(MAKE) deploy-services + $(MAKE) services $(MAKE) run-tests $(MAKE) end-maintenance .PHONY : rollback @@ -106,14 +106,14 @@ checkout-target : ## Fetch and checkout `${TARGET}` # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. -deploy-services : ## Deploy services +services : ## Deploy services $(MAKE) --file=./docker.mk build ${docker_compose} up \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ --wait -.PHONY : deploy-services +.PHONY : services run-tests : COMMAND = true run-tests : execb ## Run tests From 31720a101a5c6164727cba9227c021ea573e10ca Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 19:50:50 +0100 Subject: [PATCH 693/878] Relax depend condition even further --- docker-compose.development.yaml | 4 ++-- docker-compose.production.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index ec591258..33e5e754 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -43,7 +43,7 @@ services: CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?required}" depends_on: database: - condition: "service_healthy" + condition: "service_started" volumes: - ./backend:/app - files:/app/src/files @@ -137,7 +137,7 @@ services: CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" depends_on: backend: - condition: "service_healthy" + condition: "service_started" volumes: - ./frontend:/app # The following items make the engine create anonymous volumes for the diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index b5e1d732..2598691f 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -40,7 +40,7 @@ services: dockerfile: "./Dockerfile.production" depends_on: database: - condition: "service_healthy" + condition: "service_started" volumes: - backend_logs:/app/logs - files:/app/files @@ -100,7 +100,7 @@ services: dockerfile: "./Dockerfile.production" depends_on: backend: - condition: "service_healthy" + condition: "service_started" environment: NODE_ENV: "production" command: [ "yarn", "start" ] From 4645efd33626e42433f943d61785979950db73a3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 20:48:55 +0100 Subject: [PATCH 694/878] Allow big cookies and unrestricted body sizes --- database.mk | 3 +-- deploy.mk | 1 - nginx/templates/development/default.conf.template | 8 +++++--- nginx/templates/production/default.conf.template | 7 +++++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/database.mk b/database.mk index ae4965d3..649d2877 100755 --- a/database.mk +++ b/database.mk @@ -10,7 +10,6 @@ COMPOSE_BAKE=true docker_compose = \ docker compose \ - --file ./docker-compose.yaml \ --env-file ./.env dump_archive_name = postgresql_dumpall.gz @@ -182,7 +181,7 @@ restore : ## Restore database and related data from directory with absolute path --set=ON_ERROR_STOP=1 \ --file=- \ --username="${POSTGRES_USER}" \ - --dbname="${POSTGRES_DATABASE_NAME}" + --dbname=postgres docker container stop ${DATABASE_CONTAINER_NAME} docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} diff --git a/deploy.mk b/deploy.mk index 5390cab2..5208aa4f 100755 --- a/deploy.mk +++ b/deploy.mk @@ -10,7 +10,6 @@ COMPOSE_BAKE=true docker_compose = \ docker compose \ - --file ./docker-compose.yaml \ --env-file ./.env # Taken from https://www.client9.com/self-documenting-makefiles/ diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template index 1289a046..eacf4314 100644 --- a/nginx/templates/development/default.conf.template +++ b/nginx/templates/development/default.conf.template @@ -10,8 +10,6 @@ proxy_set_header X-Forwarded-Proto $scheme; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection keep-alive; # proxy_cache_bypass $http_upgrade; -client_max_body_size 10m; -client_body_buffer_size 128k; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; @@ -19,6 +17,11 @@ proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; +client_max_body_size 0; +client_body_buffer_size 128k; +client_header_buffer_size 4k; +large_client_header_buffers 4 16k; + add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; @@ -112,7 +115,6 @@ server { location /api { proxy_pass http://backend; limit_req zone=api burst=6 nodelay; - client_max_body_size 10240m; } location /api/resources/ { diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/production/default.conf.template index d1d09622..cc6232ad 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/production/default.conf.template @@ -10,8 +10,6 @@ proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection keep-alive; # proxy_cache_bypass $http_upgrade; -client_max_body_size 10m; -client_body_buffer_size 128k; proxy_connect_timeout 90; proxy_send_timeout 90; proxy_read_timeout 90; @@ -19,6 +17,11 @@ proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; +client_max_body_size 0; +client_body_buffer_size 128k; +client_header_buffer_size 4k; +large_client_header_buffers 4 16k; + add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; From 70497c091b0b73a95a0b4cdb0048b50f4a311af7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 6 Feb 2026 22:40:31 +0100 Subject: [PATCH 695/878] Do not compare ports of host and origin or referer (because they may differ) --- backend/src/Authentication/AuthenticationHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Authentication/AuthenticationHelpers.cs b/backend/src/Authentication/AuthenticationHelpers.cs index fe1f81d5..81cdd033 100644 --- a/backend/src/Authentication/AuthenticationHelpers.cs +++ b/backend/src/Authentication/AuthenticationHelpers.cs @@ -29,7 +29,7 @@ internal static bool IsSameOriginOrReferer(HttpRequest request) { return false; } - return request.Host == HostString.FromUriComponent(originOrReferer); + return request.Host.Host == originOrReferer.Host; } internal static bool IsReferredToFromSubpath( From d4af32ce09c0daa259ff2d0a69b94552a94b1bd0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 7 Feb 2026 07:00:17 +0100 Subject: [PATCH 696/878] Use `docker compose` directly --- certificates.mk | 15 +++++++-------- database.mk | 34 +++++++++++++++------------------- deploy.mk | 6 +----- docker.mk | 36 ++++++++++++++++-------------------- tools.mk | 8 ++------ 5 files changed, 41 insertions(+), 58 deletions(-) diff --git a/certificates.mk b/certificates.mk index 30956a02..d81adf72 100755 --- a/certificates.mk +++ b/certificates.mk @@ -15,14 +15,13 @@ help : ## Print this help .DEFAULT_GOAL := help build-bootstrap : ## Build the bootstrap image - DOCKER_BUILDKIT=1 \ - docker build \ - --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - --tag ${NAME}_bootstrap \ - --file ./backend/Dockerfile-bootstrap \ - ./backend + docker build \ + --pull \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + --tag ${NAME}_bootstrap \ + --file ./backend/Dockerfile-bootstrap \ + ./backend .PHONY : build-bootstrap jwt : build-bootstrap ## Create JWT encryption and signing certificates if necessary diff --git a/database.mk b/database.mk index 649d2877..15e48144 100755 --- a/database.mk +++ b/database.mk @@ -8,10 +8,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true -docker_compose = \ - docker compose \ - --env-file ./.env - dump_archive_name = postgresql_dumpall.gz files_archive_name = files.gz @@ -24,11 +20,11 @@ help : ## Print this help .DEFAULT_GOAL := help psql : ## Enter PostgreSQL interactive terminal in the running `database` container - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ database - ${docker_compose} exec \ + docker compose exec \ database \ psql \ --username="${POSTGRES_USER}" \ @@ -39,7 +35,7 @@ createdb : CONTAINER_NAME = create_${NAME}_database createdb : ## Create database with name `${POSTGRES_DATABASE_NAME}` -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${CONTAINER_NAME} \ --detach \ database @@ -57,7 +53,7 @@ dropdb : CONTAINER_NAME = drop_${NAME}_database dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${CONTAINER_NAME} \ --detach \ database @@ -73,12 +69,12 @@ dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` sql : CONTAINER_NAME = sql_${NAME}_database sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `./database.mk sql SCRIPT=./my.sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - ${docker_compose} down \ + docker compose down \ --remove-orphans \ database -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${CONTAINER_NAME} \ --detach \ database @@ -94,7 +90,7 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for --dbname=${POSTGRES_DATABASE_NAME} docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ database @@ -112,12 +108,12 @@ backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database backup : FILES_CONTAINER_NAME = backup_${NAME}_files backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` mkdir --parents ${DIR} - ${docker_compose} down \ + docker compose down \ --remove-orphans \ database -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${DATABASE_CONTAINER_NAME} \ --detach \ database @@ -133,7 +129,7 @@ backup : ## Backup database and related data to directory with absolute path `${ docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} -docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${FILES_CONTAINER_NAME} \ --detach \ backend @@ -151,7 +147,7 @@ backup : ## Backup database and related data to directory with absolute path `${ ./files docker container stop ${FILES_CONTAINER_NAME} docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ database @@ -160,14 +156,14 @@ backup : ## Backup database and related data to directory with absolute path `${ restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database restore : FILES_CONTAINER_NAME = restore_${NAME}_files restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` - ${docker_compose} down \ + docker compose down \ --remove-orphans \ database docker volume rm \ ${NAME}_data -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${DATABASE_CONTAINER_NAME} \ --detach \ database @@ -186,7 +182,7 @@ restore : ## Restore database and related data from directory with absolute path docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} -docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} run \ + docker compose run \ --name ${FILES_CONTAINER_NAME} \ --detach \ backend @@ -211,7 +207,7 @@ restore : ## Restore database and related data from directory with absolute path " docker container stop ${FILES_CONTAINER_NAME} docker container rm --volumes ${FILES_CONTAINER_NAME} - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ database diff --git a/deploy.mk b/deploy.mk index 5208aa4f..f9a8ce02 100755 --- a/deploy.mk +++ b/deploy.mk @@ -8,10 +8,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true -docker_compose = \ - docker compose \ - --env-file ./.env - # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -107,7 +103,7 @@ checkout-target : ## Fetch and checkout `${TARGET}` # one go so the maintenance page is only down very shortly. services : ## Deploy services $(MAKE) --file=./docker.mk build - ${docker_compose} up \ + docker compose up \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ diff --git a/docker.mk b/docker.mk index cc58fb6a..63866ca7 100755 --- a/docker.mk +++ b/docker.mk @@ -8,10 +8,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true -docker_compose = \ - docker compose \ - --env-file ./.env - dotenv_linter = \ docker run \ --rm \ @@ -41,24 +37,24 @@ dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are availa .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format - ${docker_compose} config + docker compose config .PHONY : config check : ## Check build configuration - ${docker_compose} build \ + docker compose build \ --check \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) .PHONY : check pull : ## Pull images - ${docker_compose} pull + docker compose pull .PHONY : pull # To debug errors during build add `--progress plain \` to get additional # output. build : dotenv check pull ## Build images - ${docker_compose} build \ + docker compose build \ --pull \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) @@ -66,7 +62,7 @@ build : dotenv check pull ## Build images .PHONY : build bake : ## Print docker-compose file equivalent bake file - ${docker_compose} build \ + docker compose build \ --print \ --pull .PHONY : bake @@ -81,7 +77,7 @@ build-context : ## Show the build context configured by `./${SERVICE}/.dockerign .PHONY : build-context remove : ## Remove stopped services - ${docker_compose} rm \ + docker compose rm \ --volumes .PHONY : remove @@ -91,13 +87,13 @@ remove-data-volume : ## Remove data volumes .PHONY : remove-data up : dotenv ## (Re)create and start services - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait .PHONY : up down : ## Stop services and remove services and networks created by `up` - ${docker_compose} down \ + docker compose down \ --remove-orphans -rm --force \ ./frontend/queries/*.generated.ts @@ -105,11 +101,11 @@ down : ## Stop services and remove services and networks created by `up` restart : SERVICES = restart : ## Restart all or specific stopped and running services, for example, `make restart` or `make restart SERVICES="database backend"` - ${docker_compose} restart ${SERVICES} + docker compose restart ${SERVICES} .PHONY : restart attach : ## Attach to the `${SERVICE}` service, for example, `make attach SERVICE=backend` (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) - ${docker_compose} attach ${SERVICE} + docker compose attach ${SERVICE} .PHONY : attach prune : ## Remove all unused containers, unused networks, unused and dangling images, and unused anonymous volumes @@ -118,17 +114,17 @@ prune : ## Remove all unused containers, unused networks, unused and dangling im .PHONY : prune logs : ## Follow logs - ${docker_compose} logs \ + docker compose logs \ --since=1h \ --follow .PHONY : logs exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ ${SERVICE} - ${docker_compose} exec \ + docker compose exec \ ${SERVICE} \ ${COMMAND} .PHONY : exec @@ -138,7 +134,7 @@ enter : exec ## Enter shell in the running `${SERVICE}` service, for example, `m .PHONY : enter run : ## Run the one-time command `${COMMAND}` against a fresh `${SERVICE}` service - ${docker_compose} run \ + docker compose run \ --rm \ ${SERVICE} \ ${COMMAND} @@ -149,12 +145,12 @@ shell : run ## Enter shell in a fresh `${SERVICE}` service, for example, `make s .PHONY : shell list : ## List all services with health status - ${docker_compose} ps \ + docker compose ps \ --no-trunc \ --all .PHONY : list list-services : ## List all services specified in the docker-compose file (used by Monit as configured in the `machine` project) - ${docker_compose} config \ + docker compose config \ --services .PHONY : list-services diff --git a/tools.mk b/tools.mk index 3924afa9..8c1c1fb7 100755 --- a/tools.mk +++ b/tools.mk @@ -8,10 +8,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true -docker_compose = \ - docker compose \ - --env-file ./.env - dotenv_linter = \ docker run \ --rm \ @@ -31,11 +27,11 @@ help : ## Print this help # Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md trace-backend : ## Trace the dotnet process `Metabase` within the backend service - ${docker_compose} up \ + docker compose up \ --remove-orphans \ --wait \ backend - ${docker_compose} exec \ + docker compose exec \ --privileged \ backend \ make trace From a8f53224db050de873dda4c3e7c7fb471ac6b0db Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 7 Feb 2026 13:46:38 +0100 Subject: [PATCH 697/878] Make the development environment be as close to the production one as possible (for development clone and up the `machine` project and use it as reverse proxy for the local metabase instance) --- .env.development.sample | 23 +- .gitignore | 2 - .gitmodules | 3 - ...-build-context => Dockerfile.build-context | 0 README.md | 12 - backend/.gitignore | 3 - ...kerfile-bootstrap => Dockerfile.bootstrap} | 0 backend/Dockerfile.development | 163 +----------- backend/Makefile | 1 - backend/dotnet-certificate-tool | 1 - backend/dotnet-tools.json | 7 - .../src/Configuration/AuthConfiguration.cs | 4 +- backend/src/Startup.cs | 5 +- backend/src/appsettings.development.json | 7 +- backend/src/appsettings.production.json | 6 +- backend/src/appsettings.test.json | 8 +- certificates.mk | 241 +----------------- database.mk | 4 + docker-compose.development.yaml | 26 +- docker-compose.production.yaml | 14 +- docker.mk | 36 +-- frontend/.env.local.development.sample | 4 +- frontend/.gitignore | 2 - frontend/Dockerfile.development | 12 - frontend/package.json | 4 +- gpg.mk | 14 +- nginx/nginx.conf | 2 +- .../{production => }/default.conf.template | 4 +- .../development/default.conf.template | 150 ----------- 29 files changed, 96 insertions(+), 662 deletions(-) rename Dockerfile-show-build-context => Dockerfile.build-context (100%) rename backend/{Dockerfile-bootstrap => Dockerfile.bootstrap} (100%) delete mode 160000 backend/dotnet-certificate-tool rename nginx/templates/{production => }/default.conf.template (95%) delete mode 100644 nginx/templates/development/default.conf.template diff --git a/.env.development.sample b/.env.development.sample index dc5c80d9..3ce59916 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -1,13 +1,13 @@ NAME=database ENVIRONMENT=development -HOST=solarbuildingenvelopes.com -SUBDOMAIN=local -HTTP_PORT=5050 -HTTPS_PORT=5051 +HOST=local.solarbuildingenvelopes.com +SUBDOMAIN=www +HTTP_PORT=8500 +HTTPS_PORT=7501 -METABASE_HOST=local.buildingenvelopedata.org -METABASE_HTTPS_PORT=4041 +METABASE_HOST=www.local.buildingenvelopedata.org +METABASE_HTTPS_PORT=7001 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres @@ -20,7 +20,7 @@ OPEN_ID_CONNECT_CLIENT_ID=testlab-solar-facades OPEN_ID_CONNECT_CLIENT_SECRET=secret OPEN_TELEMETRY_HOST=host.docker.internal -OPEN_TELEMETRY_GRPC_PORT=4317 +OPEN_TELEMETRY_GRPC_PORT=4517 VERIFICATION_CODE=secret @@ -29,12 +29,3 @@ GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password - -CERTIFICATE_AUTHORITY_HOST=ca.org -CERTIFICATE_AUTHORITY_BASE_FILE_NAME=ca -CERTIFICATE_AUTHORITY_PASSWORD=ca-password -CERTIFICATE_AUTHORITY_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" - -SSL_CERTIFICATE_BASE_FILE_NAME=database -SSL_CERTIFICATE_PASSWORD=password -SSL_CERTIFICATE_SUBJECT="/C=DE/ST=BW/L=Freiburg/O=Fraunhofer Institute for Solar Energy" diff --git a/.gitignore b/.gitignore index e1b71c74..9e3327be 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,6 @@ /docker-compose.yaml */Dockerfile /examples/*.json -/secrets -/ssl /machine/*.retry /nginx/html/maintenance.html /tags* diff --git a/.gitmodules b/.gitmodules index d77f937e..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "dotnet-certificate-tool"] - path = backend/dotnet-certificate-tool - url = git@github.com:building-envelope-data/dotnet-certificate-tool.git diff --git a/Dockerfile-show-build-context b/Dockerfile.build-context similarity index 100% rename from Dockerfile-show-build-context rename to Dockerfile.build-context diff --git a/README.md b/README.md index ab3f72f3..9fceb5c6 100644 --- a/README.md +++ b/README.md @@ -83,18 +83,6 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. List all GNU Make targets by running `make help`. -1. Generate and trust a self-signed certificate authority and SSL certificates - by running `./certificates.mk ssl`. If you are locally working on the metabase and the - database and if you need them to communicate over HTTPS, then instead of - running `./certificates.mk ssl`, make the `CERTIFICATE_AUTHORITY_*` variable - values in the `.env` file match the ones from the metabase (these variables - match in the `.env.development.sample` files), copy the certificate - authority files from the directories `./ssl`, `./backend/ssl`, and - `./frontend/ssl` of the metabase project into the respective directories in - the database project (if the repository reside alongside each other by - running `mkdir ./ssl ./backend/ssl ./frontend/ssl && cp ../metabase/ssl/ca.* ./ssl && cp ../metabase/backend/ssl/ca.* ./backend/ssl && cp ../metabase/frontend/ssl/ca.* ./frontend/ssl`), and run the command - `./certificates.mk generate-ssl-certificate`. - 1. Generate JSON Web Token (JWT) encryption and signing certificates by running `./certificates.mk jwt`. diff --git a/backend/.gitignore b/backend/.gitignore index 3c605139..8a7ea17d 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -8,9 +8,6 @@ /.secrets /.mono -/secrets/ -/ssl/ - /test/**/__snapshots__/__mismatch__/ # Created by https://www.gitignore.io/api/csharp diff --git a/backend/Dockerfile-bootstrap b/backend/Dockerfile.bootstrap similarity index 100% rename from backend/Dockerfile-bootstrap rename to backend/Dockerfile.bootstrap diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index 1620d00e..ff08fdb6 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -13,14 +13,10 @@ SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] ARG GROUP_ID ARG USER_ID -ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME -ARG CERTIFICATE_AUTHORITY_PASSWORD RUN \ if [ -z "$GROUP_ID" ]; then echo "required 'GROUP_ID'"; exit 1; fi && \ - if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi && \ - if [ -z "$CERTIFICATE_AUTHORITY_BASE_FILE_NAME" ]; then echo "required 'CERTIFICATE_AUTHORITY_BASE_FILE_NAME'"; exit 1; fi && \ - if [ -z "$CERTIFICATE_AUTHORITY_PASSWORD" ]; then echo "required 'CERTIFICATE_AUTHORITY_PASSWORD'"; exit 1; fi + if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) # id --user --name "${USER_ID}" 2>/dev/null @@ -66,7 +62,6 @@ RUN \ # # `lldb` is needed by dotnet tool `dotnet-sos`, see # https://github.com/dotnet/diagnostics#net-core-diagnostics-repo -# `openssl` is needed to debug SSL certificates # # Tracing cannot only be done with `dotnet-trace`, which is connected to # `dotnet-sos`, but also with LTTng, see @@ -89,8 +84,6 @@ RUN apt-get update \ gnupg2 \ lldb \ make \ - # npm \ - openssl \ procps \ tini \ && rm \ @@ -112,12 +105,6 @@ RUN apt-get update \ --force \ /var/lib/apt/lists/* -# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 -# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -COPY "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" /usr/local/share/ca-certificates -RUN update-ca-certificates - # Install up-to-date GnuPG from the official GnuPG repository. # For available versions and the signing-key fingerprint see # https://repos.gnupg.org/deb/gnupg/noble/ @@ -191,163 +178,19 @@ WORKDIR /app # * https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-tool-install # * https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool COPY --chown=me:us ./dotnet-tools.json ./ -COPY --chown=me:us ./dotnet-certificate-tool ./dotnet-certificate-tool -RUN \ - # `dotnet pack` is run automatically by `dotnet build` because `GeneratePackageOnBuild` is set to `True`. - dotnet build \ - ./dotnet-certificate-tool/src/GSoft.CertificateTool.csproj \ - && dotnet tool restore \ - --tool-manifest ./dotnet-tools.json \ - --add-source ./dotnet-certificate-tool/src/nupkg \ - && rm --recursive --force \ - ./dotnet-certificate-tool +RUN dotnet tool restore \ + --tool-manifest ./dotnet-tools.json RUN mkdir ./src \ && mkdir ./test COPY --chown=me:us ./src/Database.csproj ./src/ COPY --chown=me:us ./test/Database.Tests.csproj ./test/ -# https://github.com/dotnet/dotnet-docker/blob/master/samples/aspnetapp/Dockerfile.alpine-x64 -# https://github.com/dotnet/dotnet-docker/blob/master/samples/run-aspnetcore-https-development.md -# https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets?view=aspnetcore-2.2&tabs=linux -# -# https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541138051 -# https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# -# `dotnet dev-certs` does not generate certificates that work in Linux -# distributions -# RUN \ -# dotnet dev-certs https --export-path ./.secrets/https/icon.pfx --password crypticpassword && \ - COPY --chown=me:us ./Database.sln ./ RUN dotnet clean \ && dotnet restore \ && dotnet tool run dotnet-sos install # https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-instructions.md -# Add the certificate authority PKCS#12 file to the X.509 certificate store for -# trusted root certificate authorities named `Root`, see -# https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.storename?view=netcore-3.1#fields -# for the current user, see -# https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.storelocation?view=netcore-3.1#fields -# essentially using `X509Store.Add`, see -# https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store.add?view=netcore-3.1 -# -# In C# code with .NET Core and hard-coded file name and password this reads -# ``` -# using (var store = new System.Security.Cryptography.X509Certificates.X509Store(System.Security.Cryptography.X509Certificates.StoreName.Root, System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser, System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite)) -# { -# var cert = new System.Security.Cryptography.X509Certificates.X509Certificate2("/app/ssl/ca.pfx", "ca-password", System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.DefaultKeySet); -# store.Add(cert); -# } -# ``` -# The above code is used in the dotnet tool `certificate-tool`, see -# https://github.com/gsoft-inc/dotnet-certificate-tool -# which we use below to add the certificate. -# -# What I don't know is why the certificate authority -# `${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt` added above using -# `update-ca-certificates` is not contained in the `Root` store of the -# `LocalMachine` used by .NET Core. Where does this store get its certificates -# from if not from `/etc/ssl`? Using `update-ca-certificates` was enough to -# make the command-line tool `cURL` trust -# `${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt` but not .NET Core `HttpClient`. -# -# To debug SSL errors when using `HttpClient` that only say something vague -# like `the remote certificate is invalid`, you can hook into the callback -# `HttpClientHandler#ServerCertificateCustomValidationCallback` to get detailed -# information about the problem: -# ``` -# var content = new System.Net.Http.StringContent("{ \"query\": \"\" }"); -# content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); -# var handler = new System.Net.Http.HttpClientHandler(); -# handler.ServerCertificateCustomValidationCallback += -# (sender, cert, chain, sslPolicyErrors) => { -# Console.WriteLine(sender); -# Console.WriteLine(cert); -# Console.WriteLine(chain); -# foreach (var s in chain.ChainStatus) -# { -# Console.WriteLine(s.Status); -# Console.WriteLine(s.StatusInformation); -# } -# Console.WriteLine(sslPolicyErrors); -# return sslPolicyErrors == System.Net.Security.SslPolicyErrors.None; -# }; -# var client = new System.Net.Http.HttpClient(handler); -# var response = -# await client -# .PostAsync( -# "https://local.solarbuildingenvelopes.com:5051/graphql/", -# content -# ); -# ``` -# -# To print existing and non-existing stores and their certificates use -# ``` -# using System.Security.Cryptography; -# using System.Security.Cryptography.X509Certificates; -# foreach (var storeLocation in (StoreLocation[]) Enum.GetValues(typeof(StoreLocation))) -# { -# foreach (var storeName in (StoreName[]) Enum.GetValues(typeof(StoreName))) -# { -# using (var store = new X509Store(storeName, storeLocation)) -# { -# try -# { -# store.Open(OpenFlags.OpenExistingOnly); -# Console.WriteLine($"# Store {store.Name}, {store.Location}, exists and has {store.Certificates.Count} certificate(s)"); -# if (store.Certificates.Count >= 1) -# { -# foreach (var x509 in store.Certificates) -# { -# Console.WriteLine("----------------------------------------"); -# Console.WriteLine(x509); -# try -# { -# byte[] rawdata = x509.RawData; -# Console.WriteLine("Content Type: {0}", X509Certificate2.GetCertContentType(rawdata)); -# Console.WriteLine("Friendly Name: {0}", x509.FriendlyName); -# Console.WriteLine("Certificate Verified?: {0}", x509.Verify()); -# Console.WriteLine("Simple Name: {0}", x509.GetNameInfo(X509NameType.SimpleName, true),Environment.NewLine); -# Console.WriteLine("Signature Algorithm: {0}", x509.SignatureAlgorithm.FriendlyName); -# if (x509.SignatureAlgorithm.FriendlyName.EndsWith("ECDSA")) -# { -# Console.WriteLine("Private Key: {0}", x509.GetECDsaPrivateKey()?.ToString()); -# Console.WriteLine("Public Key: {0}", x509.GetECDsaPublicKey()?.ToString()); -# } -# else -# { -# Console.WriteLine("Private Key: {0}", x509.PrivateKey?.ToXmlString(false)); -# Console.WriteLine("Public Key: {0}", x509.PublicKey?.Key?.ToXmlString(false)); -# } -# Console.WriteLine("Certificate Archived?: {0}", x509.Archived); -# Console.WriteLine("Length of Raw Data: {0}", x509.RawData.Length); -# x509.Reset(); -# } -# catch (CryptographicException) -# { -# Console.WriteLine("Information could not be written out for this certificate."); -# } -# } -# } -# } -# catch (CryptographicException) -# { -# Console.WriteLine($"Store {store.Name}, {store.Location}, does not exist"); -# } -# } -# Console.WriteLine("========================================"); -# } -# } -# ``` -RUN mkdir ./ssl -COPY --chown=me:us "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" ./ssl/ -RUN dotnet tool run \ - certificate-tool add \ - --file "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx" \ - --store-name Root \ - --password "${CERTIFICATE_AUTHORITY_PASSWORD}" - # bash parameter completion for the dotnet CLI # see https://learn.microsoft.com/en-us/dotnet/core/tools/enable-tab-autocomplete#bash # for the here-document syntax used to append to the ~/.bashrc file diff --git a/backend/Makefile b/backend/Makefile index c358bdf5..f44a8fb0 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -139,7 +139,6 @@ update-tools : ## Update tools dotnet tool list | \ tail -n +3 | \ awk '{print $$1}' | \ - awk '!/dotnet-certificate-tool/' | \ xargs -n 1 dotnet tool update dotnet tool run dotnet-sos install .PHONY : update-tools diff --git a/backend/dotnet-certificate-tool b/backend/dotnet-certificate-tool deleted file mode 160000 index 1b8cd879..00000000 --- a/backend/dotnet-certificate-tool +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1b8cd879ce448dd9a7d8150e8fa5136fec748672 diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 3f04612e..7b915a57 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -79,13 +79,6 @@ ], "rollForward": false }, - "dotnet-certificate-tool": { - "version": "3.2.0", - "commands": [ - "certificate-tool" - ], - "rollForward": false - }, "dotnet-retest": { "version": "1.0.0", "commands": [ diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 9a3efcdc..db9ac38b 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -229,8 +229,8 @@ X509Certificate2 signingCertificate // redirection endpoint URI per provider, unless all the registered // providers support returning a special "iss" parameter containing their // URL as part of authorization responses. For more information, see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4. - RedirectUri = new Uri($"connect/callback/login/{OpenIdConnectConstants.MetabaseClientId}", UriKind.Relative), - PostLogoutRedirectUri = new Uri($"connect/callback/logout/{OpenIdConnectConstants.MetabaseClientId}", UriKind.Relative) + RedirectUri = new UriBuilder(appSettings.Uri) { Path = $"connect/callback/login/{OpenIdConnectConstants.MetabaseClientId}" }.Uri, + PostLogoutRedirectUri = new UriBuilder(appSettings.Uri) { Path = $"connect/callback/logout/{OpenIdConnectConstants.MetabaseClientId}" }.Uri }; clientRegistration.Scopes.UnionWith([ OpenIddictConstants.Scopes.OfflineAccess, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 490b5e3b..2e15dbe2 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -81,7 +81,10 @@ private static void ConfigureRequestResponseServices(IServiceCollection services services.Configure(_ => { // TODO _.AllowedHosts = ... - _.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + _.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | + ForwardedHeaders.XForwardedProto | + ForwardedHeaders.XForwardedHost; // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forward-the-scheme-for-linux-and-non-iis-reverse-proxies _.KnownIPNetworks.Clear(); _.KnownProxies.Clear(); diff --git a/backend/src/appsettings.development.json b/backend/src/appsettings.development.json index 6b673de1..e5bcf976 100644 --- a/backend/src/appsettings.development.json +++ b/backend/src/appsettings.development.json @@ -4,7 +4,10 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Verbose" + "Default": "Verbose", + "Override": { + "Microsoft.AspNetCore.Diagnostics.HealthChecks": "Warning" + } } } -} +} \ No newline at end of file diff --git a/backend/src/appsettings.production.json b/backend/src/appsettings.production.json index 07899b05..ab4459f9 100644 --- a/backend/src/appsettings.production.json +++ b/backend/src/appsettings.production.json @@ -6,10 +6,8 @@ "MinimumLevel": { "Default": "Information", "Override": { - "Microsoft.Hosting.Lifetime": "Information", - "Microsoft.AspNetCore": "Warning", - "Microsoft.AspNetCore.DataProtection": "Information", - "System": "Error" + "System": "Warning", + "Microsoft": "Warning" } } } diff --git a/backend/src/appsettings.test.json b/backend/src/appsettings.test.json index 1defef61..424acc40 100644 --- a/backend/src/appsettings.test.json +++ b/backend/src/appsettings.test.json @@ -4,13 +4,7 @@ }, "Serilog": { "MinimumLevel": { - "Default": "Error", - "Override": { - "Microsoft": "Error", - "Microsoft.AspNetCore": "Warning", - "Microsoft.AspNetCore.DataProtection": "Warning", - "System": "Error" - } + "Default": "Error" } } } diff --git a/certificates.mk b/certificates.mk index d81adf72..50e2bf76 100755 --- a/certificates.mk +++ b/certificates.mk @@ -14,22 +14,16 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -build-bootstrap : ## Build the bootstrap image - docker build \ - --pull \ +jwt : ## Create JWT encryption and signing certificates if necessary + docker compose pull \ + bootstrap + docker compose build \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) \ - --tag ${NAME}_bootstrap \ - --file ./backend/Dockerfile-bootstrap \ - ./backend -.PHONY : build-bootstrap - -jwt : build-bootstrap ## Create JWT encryption and signing certificates if necessary - docker run \ + bootstrap + docker compose run \ --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ + bootstrap \ bash -ceux -o pipefail " \ dotnet-script \ /app/create-certificates.csx \ @@ -38,224 +32,3 @@ jwt : build-bootstrap ## Create JWT encryption and signing certificates if neces ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ " .PHONY : jwt - -# For an introduction to how HTTPS works see https://howhttps.works -ssl : ## Generate and trust certificate authority, and generate SSL certificates - $(MAKE) generate-certificate-authority - $(MAKE) generate-ssl-certificate - $(MAKE) trust-certificate-authority -.PHONY : ssl - -# Creating Self-Signed ECDSA SSL Certificate using OpenSSL: http://www.guyrutenberg.com/2013/12/28/creating-self-signed-ecdsa-ssl-certificate-using-openssl/ -# See also https://gist.github.com/Soarez/9688998 -# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm -# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 -# Which file extensions are meant for which file layout? https://serverfault.com/questions/9708/what-is-a-pem-file-and-how-does-it-differ-from-other-openssl-generated-key-file/9717#9717 -generate-certificate-authority : ## Generate certificate authority ECDSA private key and self-signed certificate - mkdir --parents ./ssl/ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.29-trixie-otel \ - bash -ceux -o pipefail " \ - echo \"# Generate the elliptic curve (EC) private key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ - openssl ecparam \ - -genkey \ - -name secp384r1 \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key && \ - echo \"# Check and print the private key's elliptic curve parameters\" && \ - openssl ecparam \ - -check \ - -text \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -noout && \ - echo \"# Generate the PKCS#10 certificate request '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req' with common name '${CERTIFICATE_AUTHORITY_HOST}' from the private key\" && \ - openssl req \ - -new \ - -subj \"${CERTIFICATE_AUTHORITY_SUBJECT}/CN=${CERTIFICATE_AUTHORITY_HOST}\" \ - -key /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req && \ - echo \"# Verify and print the request\" && \ - openssl req \ - -verify \ - -text \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ - -noout && \ - echo \"# Convert the request into the self-signed certificate '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt'\" && \ - openssl x509 \ - -req \ - -trustout \ - -days 365 \ - -extfile <(printf ' \ - basicConstraints = critical, CA:TRUE, pathlen:0\n \ - subjectKeyIdentifier = hash\n \ - authorityKeyIdentifier = keyid:always, issuer:always\n \ - subjectAltName = DNS:${CERTIFICATE_AUTHORITY_HOST}\n \ - issuerAltName = issuer:copy\n \ - keyUsage = critical, cRLSign, digitalSignature, keyCertSign\n \ - ') \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.req \ - -signkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ - echo \"# Print and Verify the self-signed certificate\" && \ - openssl x509 \ - -text \ - -purpose \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -noout && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt && \ - echo \"# Create the PKCS#12 file '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx' from the self-signed certificate\" && \ - openssl pkcs12 \ - -export \ - -passout pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -inkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -out /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx && \ - echo \"# Verify the PKCS#12 file\" && \ - ( \ - openssl pkcs12 \ - -info \ - -passin pass:${CERTIFICATE_AUTHORITY_PASSWORD} \ - -in /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pfx \ - -noout && \ - echo \"PKCS#12 file is valid\" && \ - exit 0 \ - ) || echo \"PFX file is invalid\" \ - " - mkdir --parents ./backend/ssl - cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./backend/ssl - mkdir --parents ./frontend/ssl - cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.* ./frontend/ssl -.PHONY : generate-certificate-authority - -# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 -# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -# and https://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu/719047#719047 -# For debugging purposes, the following commands can be helpful -# cat /etc/ssl/certs/ca-certificates.crt -# cat ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -# sudo cat /etc/ssl/certs/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.pem -# Note that Firefox and Google Chrome use their own certificate stores: -# * Firefox: https://www.cyberciti.biz/faq/firefox-adding-trusted-ca/ -# * Google Chrome: https://rshankar.com/blog/2010/07/08/how-to-import-a-certificate-in-google-chrome/ -trust-certificate-authority : ## Trust the authority's SSL certificate - sudo cp ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt /usr/local/share/ca-certificates - sudo update-ca-certificates - openssl verify ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -.PHONY : trust-certificate-authority - -# Inspired by https://stackoverflow.com/questions/55485511/how-to-run-dotnet-dev-certs-https-trust/59702094#59702094 -# and https://superuser.com/questions/226192/avoid-password-prompt-for-keys-and-prompts-for-dn-information/226229#226229 -# See also https://github.com/dotnet/aspnetcore/issues/7246#issuecomment-541201757 -# and https://github.com/dotnet/runtime/issues/31237#issuecomment-544929504 -# For an explanation of the distinction between `cert` and `pfx` files, see -# https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 -# OpenSSL Quick Reference: https://www.digicert.com/kb/ssl-support/openssl-quick-reference-guide.htm -# X509v3 Extensions: See `man x509v3_config` and https://superuser.com/questions/738612/openssl-ca-keyusage-extension/1248085#1248085 and https://access.redhat.com/solutions/28965 -# What are PKCS#12 files? https://security.stackexchange.com/questions/29425/difference-between-pfx-and-cert-certificates/29428#29428 -# Process substitution `<( ... )`: https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html -# Note that extensions are not transferred to certificate requests and vice versa as said on https://www.openssl.org/docs/man1.1.0/man1/x509.html#BUGS -generate-ssl-certificate : ## Generate ECDSA private key and SSL certificate signed by our certificate authority - mkdir --parents ./ssl/ - docker run \ - --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/ssl",target=/ssl \ - nginx:1.29-trixie-otel \ - bash -ceux -o pipefail " \ - echo \"# Generate the elliptic curve (EC) private key '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key' with parameters 'secp384r1', that is, a NIST/SECG curve over a 384 bit prime field as said in the output of the command 'openssl ecparam -list_curves'\" && \ - openssl ecparam \ - -genkey \ - -name secp384r1 \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key && \ - echo \"# Check and print the private key's elliptic curve parameters\" && \ - openssl ecparam \ - -check \ - -text \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -noout && \ - echo \"# Generate the PKCS#10 certificate request '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req' with common name '${HOST}' from the private key\" && \ - openssl req \ - -new \ - -subj \"${SSL_CERTIFICATE_SUBJECT}/CN=${HOST}\" \ - -key /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req && \ - echo \"# Verify and print the request\" && \ - openssl req \ - -verify \ - -text \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ - -noout && \ - echo \"# Sign the request with certificate authority '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt' and key '/ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key' resulting in the signed certificate '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt'\" && \ - openssl x509 \ - -req \ - -days 365 \ - -CA /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -CAkey /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.key \ - -CAcreateserial \ - -extfile <(printf ' \ - basicConstraints = critical, CA:FALSE\n \ - subjectKeyIdentifier = hash\n \ - authorityKeyIdentifier = keyid:always, issuer:always\n \ - subjectAltName = DNS:${HOST}\n \ - issuerAltName = issuer:copy\n \ - keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment, keyAgreement\n \ - extendedKeyUsage = critical, clientAuth, serverAuth\n \ - ') \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.req \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ - echo \"# Print and Verify the signed certificate\" && \ - openssl x509 \ - -text \ - -purpose \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - -noout && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt && \ - echo \"# Chain the certificate and the certificate authority's certificate in that order, see http://nginx.org/en/docs/http/configuring_https_servers.html#chains\" && \ - cat \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - > /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ - echo \"# Verify the chained certificate\" && \ - openssl verify \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt && \ - echo \"# Create the PKCS#12 file chain '/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx' from the un-chained signed certificate\" && \ - openssl pkcs12 \ - -export \ - -chain \ - -CAfile /ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt \ - -passout pass:${SSL_CERTIFICATE_PASSWORD} \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt \ - -inkey /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key \ - -out /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx && \ - echo \"# Verify the PKCS#12 file\" && \ - ( \ - openssl pkcs12 \ - -info \ - -passin pass:${SSL_CERTIFICATE_PASSWORD} \ - -in /ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.pfx \ - -noout && \ - echo \"PKCS#12 file is valid\" && \ - exit 0 \ - ) || echo \"PFX file is invalid\" \ - " -.PHONY : generate-ssl-certificate - -fetch-ssl-certificate : ## Fetch the SSL certificate from the server - openssl s_client ${HOST}:${HTTPS_PORT} -.PHONY : fetch-ssl-certificate - -ssl-certificate : ## Print the SSL certificate - openssl x509 -text -noout -in ./ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.crt -.PHONY : ssl-certificate - -certificate-authority : ## View the certificate authority - openssl x509 -text -noout -in ./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt -.PHONY : certificate-authority diff --git a/database.mk b/database.mk index 15e48144..790bb4a4 100755 --- a/database.mk +++ b/database.mk @@ -81,6 +81,7 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done cat ${SCRIPT} \ | docker exec \ + --interactive \ ${CONTAINER_NAME} \ psql \ --echo-all \ @@ -122,6 +123,7 @@ backup : ## Backup database and related data to directory with absolute path `${ ${DATABASE_CONTAINER_NAME} \ pg_dumpall \ --clean \ + --if-exists \ --username="${POSTGRES_USER}" \ | gzip \ > ${DIR}/${dump_archive_name} @@ -169,6 +171,8 @@ restore : ## Restore database and related data from directory with absolute path database while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done gunzip --stdout ${DIR}/${dump_archive_name} \ + | grep --invert-match --extended-regexp \ + "^CREATE ROLE ${POSTGRES_USER};|^ALTER ROLE ${POSTGRES_USER}" \ | docker exec \ --interactive \ ${DATABASE_CONTAINER_NAME} \ diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 33e5e754..37bfc58d 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -9,7 +9,7 @@ services: environment: POSTGRES_USER: "${POSTGRES_USER}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" - POSTGRES_DB: "postgres" # default database + POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` # ports: # - "0.0.0.0:5432:5432" # For possible command-line arguments see @@ -38,9 +38,6 @@ services: build: context: "./backend" dockerfile: "./Dockerfile.development" - args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" - CERTIFICATE_AUTHORITY_PASSWORD: "${CERTIFICATE_AUTHORITY_PASSWORD:?required}" depends_on: database: condition: "service_started" @@ -102,7 +99,7 @@ services: - "host.docker.internal:host-gateway" - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" - "${METABASE_HOST:?required}:host-gateway" - # user: "1000:1000" + # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line command: [ "dotnet", @@ -133,8 +130,6 @@ services: build: context: "./frontend" dockerfile: "./Dockerfile.development" - args: - CERTIFICATE_AUTHORITY_BASE_FILE_NAME: "${CERTIFICATE_AUTHORITY_BASE_FILE_NAME:?required}" depends_on: backend: condition: "service_started" @@ -155,6 +150,7 @@ services: # ports: # - "0.0.0.0:8081:8080" command: [ "yarn", "dev" ] + restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] interval: "5s" @@ -171,20 +167,15 @@ services: frontend: condition: "service_started" volumes: - - ./ssl:/etc/nginx/ssl:ro - - ./nginx/templates/development:/etc/nginx/templates:ro + - ./nginx/templates:/etc/nginx/templates:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/html:/etc/nginx/html:ro - files:/data/files:ro # - /etc/nginx/conf.d # run in read-only mode ... # - /var/cache/nginx # ... # - /var/run # ... - environment: - SERVER_NAME: "${SUBDOMAIN:?required}.${HOST:?required}" - SSL_CERTIFICATE_BASE_FILE_NAME: "${SSL_CERTIFICATE_BASE_FILE_NAME:?required}" ports: - "0.0.0.0:${HTTP_PORT:?required}:80" - - "0.0.0.0:${HTTPS_PORT:?required}:443" command: [ "nginx-debug", "-g", "daemon off;" ] restart: "unless-stopped" healthcheck: @@ -195,6 +186,15 @@ services: labels: autoheal: "true" + bootstrap: + build: + context: "./backend" + dockerfile: "./Dockerfile.bootstrap" + volumes: + - ./backend:/app + profiles: + - "manual" + volumes: data: files: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 2598691f..15c6b489 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -9,7 +9,7 @@ services: environment: POSTGRES_USER: "${POSTGRES_USER}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" - POSTGRES_DB: "postgres" # default database + POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` # ports: # - "0.0.0.0:5432:5432" # For possible command-line arguments see @@ -48,6 +48,7 @@ services: # Create volume for GnuPG - gnupg:/root/.gnupg environment: + # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" ASPNETCORE_ENVIRONMENT: "production" XBASE_Host: "${HOST:?required}" @@ -121,7 +122,7 @@ services: frontend: condition: "service_started" volumes: - - ./nginx/templates/production:/etc/nginx/templates:ro + - ./nginx/templates:/etc/nginx/templates:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/html:/etc/nginx/html:ro - files:/data/files:ro @@ -137,6 +138,15 @@ services: labels: autoheal: "true" + bootstrap: + build: + context: "./backend" + dockerfile: "./Dockerfile.bootstrap" + volumes: + - ./backend:/app + profiles: + - "manual" + volumes: data: files: diff --git a/docker.mk b/docker.mk index 63866ca7..857ee206 100755 --- a/docker.mk +++ b/docker.mk @@ -7,6 +7,7 @@ SHELL := /usr/bin/env bash MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true +SERVICE= dotenv_linter = \ docker run \ @@ -37,18 +38,18 @@ dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are availa .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format - docker compose config + docker compose config ${SERVICE} .PHONY : config check : ## Check build configuration docker compose build \ --check \ --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + --build-arg USER_ID=$(shell id --user) ${SERVICE} .PHONY : check pull : ## Pull images - docker compose pull + docker compose pull ${SERVICE} .PHONY : pull # To debug errors during build add `--progress plain \` to get additional @@ -57,14 +58,14 @@ build : dotenv check pull ## Build images docker compose build \ --pull \ --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) + --build-arg USER_ID=$(shell id --user) ${SERVICE} # --no-cache .PHONY : build bake : ## Print docker-compose file equivalent bake file docker compose build \ --print \ - --pull + --pull ${SERVICE} .PHONY : bake build-context : ## Show the build context configured by `./${SERVICE}/.dockerignore`, for example, `make build-context SERVICE=backend` @@ -72,36 +73,37 @@ build-context : ## Show the build context configured by `./${SERVICE}/.dockerign --pull \ --no-cache \ --progress plain \ - --file ./Dockerfile-show-build-context \ + --file ./Dockerfile.build-context \ ./${SERVICE} .PHONY : build-context remove : ## Remove stopped services docker compose rm \ - --volumes + --volumes ${SERVICE} .PHONY : remove -remove-data-volume : ## Remove data volumes +remove-data-and-files-volumes : ## Remove data and files volume docker volume rm \ ${NAME}_data -.PHONY : remove-data + docker volume rm \ + ${NAME}_files +.PHONY : remove-data-and-files-volumes up : dotenv ## (Re)create and start services docker compose up \ --remove-orphans \ - --wait + --wait ${SERVICE} .PHONY : up down : ## Stop services and remove services and networks created by `up` docker compose down \ - --remove-orphans + --remove-orphans ${SERVICE} -rm --force \ ./frontend/queries/*.generated.ts .PHONY : down -restart : SERVICES = -restart : ## Restart all or specific stopped and running services, for example, `make restart` or `make restart SERVICES="database backend"` - docker compose restart ${SERVICES} +restart : ## Restart services, for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` + docker compose restart ${SERVICE} .PHONY : restart attach : ## Attach to the `${SERVICE}` service, for example, `make attach SERVICE=backend` (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) @@ -116,7 +118,7 @@ prune : ## Remove all unused containers, unused networks, unused and dangling im logs : ## Follow logs docker compose logs \ --since=1h \ - --follow + --follow ${SERVICE} .PHONY : logs exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service @@ -144,10 +146,10 @@ shell : COMMAND = bash shell : run ## Enter shell in a fresh `${SERVICE}` service, for example, `make shell SERVICE=backend` or `make shell SERVICE=frontend` .PHONY : shell -list : ## List all services with health status +list : ## List services with health status docker compose ps \ --no-trunc \ - --all + --all ${SERVICE} .PHONY : list list-services : ## List all services specified in the docker-compose file (used by Monit as configured in the `machine` project) diff --git a/frontend/.env.local.development.sample b/frontend/.env.local.development.sample index 21c3ff38..03cea398 100644 --- a/frontend/.env.local.development.sample +++ b/frontend/.env.local.development.sample @@ -1,3 +1,3 @@ -NEXT_PUBLIC_DATABASE_URL=https://local.solarbuildingenvelopes.com:5051 -NEXT_PUBLIC_METABASE_URL=https://local.buildingenvelopedata.org:4041 +NEXT_PUBLIC_DATABASE_URL=https://www.local.solarbuildingenvelopes.com:7501 +NEXT_PUBLIC_METABASE_URL=https://www.local.buildingenvelopedata.org:7001 NEXT_WEBPACK_USEPOLLING=false diff --git a/frontend/.gitignore b/frontend/.gitignore index 1d070c64..67a986f0 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -1,6 +1,4 @@ /Dockerfile -/secrets -/ssl # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. diff --git a/frontend/Dockerfile.development b/frontend/Dockerfile.development index cbc81b06..cfd96224 100644 --- a/frontend/Dockerfile.development +++ b/frontend/Dockerfile.development @@ -10,11 +10,6 @@ FROM node:24.11-trixie-slim SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -ARG CERTIFICATE_AUTHORITY_BASE_FILE_NAME - -RUN \ - if [ -z "$CERTIFICATE_AUTHORITY_BASE_FILE_NAME" ]; then echo "required 'CERTIFICATE_AUTHORITY_BASE_FILE_NAME'"; exit 1; fi - # TODO directory node_modules is created on host machine when container is run # and does not belong to current user. Avoid it being created at all or at # least make it belong to the current user @@ -23,17 +18,13 @@ RUN \ # As `root` # ############# -# `ca-certificates` is needed to run `update-ca-certificates` -# `openssl` is needed to debug SSL certificates # For best practices on using `apt-get` see # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run RUN apt-get update \ && apt-get install \ --assume-yes \ --no-install-recommends \ - ca-certificates \ make \ - openssl \ tini \ wget \ && rm \ @@ -41,9 +32,6 @@ RUN apt-get update \ --force \ /var/lib/apt/lists/* -COPY "./ssl/${CERTIFICATE_AUTHORITY_BASE_FILE_NAME}.crt" /usr/local/share/ca-certificates -RUN update-ca-certificates - ENV HOME=/home/node RUN mkdir --parents "${HOME}/app" \ && chown \ diff --git a/frontend/package.json b/frontend/package.json index edf791f1..b0491b42 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,9 +7,9 @@ "scripts": { "codegen": "graphql-codegen --config ./codegen.ts", "codegen:watch": "graphql-codegen --config ./codegen.ts --watch", - "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_OPTIONS=\"--use-openssl-ca\" next dev --port 8080'", + "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_TLS_REJECT_UNAUTHORIZED=0 next dev --port 8080'", "build": "yarn refresh-baseline-browser-mapping && yarn codegen && NODE_ENV=production next build", - "test": "yarn codegen && NODE_OPTIONS='--use-openssl-ca' jest", + "test": "yarn codegen && NODE_TLS_REJECT_UNAUTHORIZED=0 jest", "start": "next start --port 8080", "refresh-baseline-browser-mapping": "yarn install --dev baseline-browser-mapping@latest" }, diff --git a/gpg.mk b/gpg.mk index a5473b30..091a4621 100755 --- a/gpg.mk +++ b/gpg.mk @@ -16,12 +16,16 @@ help : ## Print this help # Keep file name .gpg in sync with the one in `AppSettings.cs` key : COMMENT = -key : build-bootstrap ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make gpg PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` - docker run \ +key : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make gpg PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` + docker compose pull \ + bootstrap + docker compose build \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + bootstrap + docker compose run \ --rm \ - --user $(shell id --user):$(shell id --group) \ - --mount type=bind,source="$(shell pwd)/backend",target=/app \ - ${NAME}_bootstrap \ + bootstrap \ bash -ceuxo pipefail " \ gpg \ --quick-generate-key \ diff --git a/nginx/nginx.conf b/nginx/nginx.conf index d27b74d8..4dcef0b9 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -12,7 +12,7 @@ worker_processes 1; # Logs written to `/var/log/nginx/error.log` and `/var/log/nginx/access.log` # are written to `/dev/stdout` and `/dev/stderr` respectively as said on # https://docs.docker.com/config/containers/logging/ -error_log /var/log/nginx/error.log warn; +error_log /var/log/nginx/error.log info; pid /var/run/nginx.pid; events { diff --git a/nginx/templates/production/default.conf.template b/nginx/templates/default.conf.template similarity index 95% rename from nginx/templates/production/default.conf.template rename to nginx/templates/default.conf.template index cc6232ad..61c27f79 100644 --- a/nginx/templates/production/default.conf.template +++ b/nginx/templates/default.conf.template @@ -2,10 +2,12 @@ # and https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx proxy_redirect off; -proxy_set_header Host $host; +proxy_set_header Host $http_x_forwarded_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $http_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; +proxy_set_header X-Forwarded-Port $http_x_forwarded_port; +proxy_set_header X-Forwarded-Host $http_x_forwarded_host; # proxy_http_version 1.1; # proxy_set_header Upgrade $http_upgrade; # proxy_set_header Connection keep-alive; diff --git a/nginx/templates/development/default.conf.template b/nginx/templates/development/default.conf.template deleted file mode 100644 index eacf4314..00000000 --- a/nginx/templates/development/default.conf.template +++ /dev/null @@ -1,150 +0,0 @@ -# Inspired by https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#https-configuration -# and https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx - -proxy_redirect off; -proxy_set_header Host $http_host; # Use the HTTP request header field HOST through the variable `$http_host` instead of the host name `$host` because the former includes the port whereas the latter does not. This header is then used by ASP.NET Core for make relative paths absolute, for example, in the OpenId Configuration `/.well-known/openid-configuration`. The NGINX variables `$http_` are described in http://nginx.org/en/docs/http/ngx_http_core_module.html#var_http_ -proxy_set_header X-Real-IP $remote_addr; -proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; -proxy_set_header X-Forwarded-Proto $scheme; -# proxy_http_version 1.1; -# proxy_set_header Upgrade $http_upgrade; -# proxy_set_header Connection keep-alive; -# proxy_cache_bypass $http_upgrade; -proxy_connect_timeout 90; -proxy_send_timeout 90; -proxy_read_timeout 90; -proxy_buffer_size 128k; -proxy_buffers 4 256k; -proxy_busy_buffers_size 256k; - -client_max_body_size 0; -client_body_buffer_size 128k; -client_header_buffer_size 4k; -large_client_header_buffers 4 16k; - -add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking - -limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=graphql:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=connect:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; - -server_tokens off; - -sendfile on; -# sendfile_max_chunk 1m; -keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case. -client_body_timeout 10; -client_header_timeout 10; -send_timeout 10; - -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} - -upstream backend { - server backend:8080; -} - -upstream frontend { - server frontend:8080; -} - -server { - listen *:80; - add_header Strict-Transport-Security max-age=15768000; - return 301 https://$host$request_uri; -} - -server { - listen *:443 ssl; - server_name ${SERVER_NAME}; - ssl_certificate /etc/nginx/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.chained.crt; - ssl_certificate_key /etc/nginx/ssl/${SSL_CERTIFICATE_BASE_FILE_NAME}.key; - ssl_protocols TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; - ssl_ecdh_curve secp384r1; - ssl_session_cache shared:SSL:10m; - ssl_session_tickets off; - ssl_stapling off; # Stapling does not work for self-signed certificates - - add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always; - add_header X-Frame-Options DENY; - add_header X-Content-Type-Options nosniff; # Secure from MIME-type sniffing: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#mime-type-sniffing - - set $maintenance 0; - if (-f $document_root/maintenance.html) { - set $maintenance 1; - } - if ($internal != 1) { - set $maintenance "${maintenance}1"; - } - if ($maintenance = 11) { - return 503; - } - - location / { - proxy_pass http://frontend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_cache_bypass $http_upgrade; - limit_req zone=frontend burst=100 nodelay; - } - - location /graphql { - proxy_pass http://backend; - limit_req zone=graphql burst=12 delay=6; - } - - location /connect { - proxy_pass http://backend; - limit_req zone=connect burst=6 nodelay; - } - - location /antiforgery { - proxy_pass http://backend; - limit_req zone=antiforgery burst=2 nodelay; - } - - location /api { - proxy_pass http://backend; - limit_req zone=api burst=6 nodelay; - } - - location /api/resources/ { - # Inspired by https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/ - alias /data/files/; # https://nginx.org/en/docs/http/ngx_http_core_module.html#alias - sendfile on; - sendfile_max_chunk 1m; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65; - } - - location /openapi { - proxy_pass http://backend; - limit_req zone=openapi burst=10 nodelay; - } - - location /health { - proxy_pass http://backend; - limit_req zone=health burst=2 nodelay; - } - - error_page 503 @maintenance; - location @maintenance { - internal; - add_header Retry-After 120 always; - default_type application/json; - if ($http_accept ~ application/json) { - return 503 "{\"errors\": [{\"code\": \"MAINTENANCE\", \"message\": \"We are under maintenance and will be back soon!\", \"path\": []}]}"; - } - rewrite ^(.*)$ /maintenance.html break; - } -} From 02523e33f5e75ab1d51ecb454109f79bef10b1bf Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 7 Feb 2026 14:30:41 +0100 Subject: [PATCH 698/878] Upgrade dotnet tools --- backend/dotnet-tools.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 7b915a57..5421952e 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -3,28 +3,28 @@ "isRoot": true, "tools": { "dotnet-aspnet-codegenerator": { - "version": "9.0.0", + "version": "10.0.2", "commands": [ "dotnet-aspnet-codegenerator" ], "rollForward": false }, "dotnet-counters": { - "version": "9.0.652701", + "version": "9.0.661903", "commands": [ "dotnet-counters" ], "rollForward": false }, "dotnet-dump": { - "version": "9.0.652701", + "version": "9.0.661903", "commands": [ "dotnet-dump" ], "rollForward": false }, "dotnet-ef": { - "version": "10.0.0", + "version": "10.0.2", "commands": [ "dotnet-ef" ], @@ -38,35 +38,35 @@ "rollForward": false }, "dotnet-trace": { - "version": "9.0.652701", + "version": "9.0.661903", "commands": [ "dotnet-trace" ], "rollForward": false }, "dotnet-gcdump": { - "version": "9.0.652701", + "version": "9.0.661903", "commands": [ "dotnet-gcdump" ], "rollForward": false }, "dotnet-sos": { - "version": "9.0.652701", + "version": "9.0.661903", "commands": [ "dotnet-sos" ], "rollForward": false }, "roslynator.dotnet.cli": { - "version": "0.11.0", + "version": "0.12.0", "commands": [ "roslynator" ], "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.3.0.3", + "version": "2025.3.2", "commands": [ "jb" ], From 571527418d182feb6665a7d118ed0d3c8b498dfb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 10:54:14 +0100 Subject: [PATCH 699/878] Do not bake absolute URLs into the frontend; Include and exclude content and resource items explicitly (otherwise nonsense content or resource item errors are abound); Forge images locally or on a build server; Create and rotate JWT signing and encryption certificates using a Quartz Job; Include files in Docker build context rather than excluding them --- .env.development.sample | 3 - .env.production.sample | 4 +- .env.staging.sample | 4 +- .gitmodules | 0 Dockerfile.build-context | 2 - README.md | 21 +- backend/.dockerignore | 376 +----------------- backend/Dockerfile.bootstrap | 2 - backend/Dockerfile.development | 8 +- backend/Dockerfile.production | 6 +- backend/src/AppSettings.cs | 7 - .../Authentication/OpenIdConnectConstants.cs | 13 +- .../src/Configuration/AuthConfiguration.cs | 138 +++++-- backend/src/Database.csproj | 36 +- ...ningAndEncryptionCertificateRotationJob.cs | 228 +++++++++++ backend/src/Program.cs | 16 +- backend/src/Startup.cs | 34 +- backend/src/appsettings.json | 2 +- certificates.mk | 34 -- database.mk | 61 +-- deploy.mk | 123 +++--- docker-compose.development.yaml | 31 +- docker-compose.production.yaml | 55 +-- docker.mk | 37 +- forge.mk | 55 +++ frontend/.dockerignore | 57 +-- frontend/.env.local.development.sample | 3 - frontend/.env.local.production.sample | 2 - frontend/.env.local.staging.sample | 2 - frontend/Dockerfile.development | 4 +- frontend/Dockerfile.production | 40 +- frontend/environment.d.ts | 3 - frontend/lib/apollo.ts | 2 +- frontend/next.config.ts | 5 +- frontend/package.json | 2 +- frontend/paths.ts | 43 +- gpg.mk | 1 + tools.mk | 1 + 38 files changed, 712 insertions(+), 749 deletions(-) delete mode 100644 .gitmodules create mode 100644 backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs delete mode 100755 certificates.mk create mode 100755 forge.mk delete mode 100644 frontend/.env.local.development.sample delete mode 100644 frontend/.env.local.production.sample delete mode 100644 frontend/.env.local.staging.sample diff --git a/.env.development.sample b/.env.development.sample index 3ce59916..03af4e23 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -26,6 +26,3 @@ VERIFICATION_CODE=secret GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase - -JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD=encryption-password -JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD=signing-password diff --git a/.env.production.sample b/.env.production.sample index b1ca6662..6ecb54e7 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,5 +1,6 @@ NAME=database_production ENVIRONMENT=production +TARGET=v1.0.0 HOST=solarbuildingenvelopes.com SUBDOMAIN=www @@ -24,6 +25,3 @@ VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= - -JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= -JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.env.staging.sample b/.env.staging.sample index db809a15..c426c63a 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -1,5 +1,6 @@ NAME=database_staging ENVIRONMENT=staging +TARGET=26156e1f3d8c27095f9770c645d06d548537a5d2 HOST=solarbuildingenvelopes.com SUBDOMAIN=staging @@ -24,6 +25,3 @@ VERIFICATION_CODE= GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= - -JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD= -JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD= diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29b..00000000 diff --git a/Dockerfile.build-context b/Dockerfile.build-context index ffe73e3f..85baee60 100644 --- a/Dockerfile.build-context +++ b/Dockerfile.build-context @@ -1,7 +1,5 @@ # syntax=docker/dockerfile:1.21 # check=error=true -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile FROM busybox:1.37 diff --git a/README.md b/README.md index 9fceb5c6..e3553aac 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,7 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF `git submodule update --init --recursive`. An alternative would have been passing `--recurse-submodules` to `git clone` above. -1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env`, - `cp ./frontend/.env.local.development.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, +1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env` and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your `/etc/hosts` file. @@ -83,9 +82,6 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. List all GNU Make targets by running `make help`. -1. Generate JSON Web Token (JWT) encryption and signing certificates by running - `./certificates.mk jwt`. - 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the file `./backend/src/gpg-keys/.gpg` by running @@ -118,7 +114,7 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF the `./.env` file to the remembered fingerprint in your favorite editor. 1. Create the PostgreSQL database and schema by running - `./database.mk createdb migrate`. + `./database.mk create migrate`. 1. Build and start all services and follow their logs by running `make build up logs`. @@ -359,8 +355,7 @@ and the pages following it. ``` 1. Prepare the environment by running - `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env`, - `cp ./frontend/.env.local.${environment}.sample ./frontend/.env.local && chmod 600 ./frontend/.env.local`, + `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env` and by adjusting variable values in the copies to your needs, in particular, by setting passwords to newly generated ones, where random passwords may be generated by running `openssl rand -base64 32`. Here is @@ -391,19 +386,13 @@ and the pages following it. - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` and `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase of the exported GnuPG secret key for signing in the file - `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`; - - `JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD` and - `JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD` are passwords used to - encrypt and sign JSON web tokens (JWT) used by OpenId Connect; + `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`. 1. Prepare your remote controls GNU Make and Docker Compose by running - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.production.yaml ./docker-compose.yaml`. - 1. Generate JSON Web Token (JWT) encryption and signing certificates by running - `./certificates.mk jwt`. - 1. Generate and export a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information @@ -433,7 +422,7 @@ and the pages following it. 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in the `./.env` file to the remembered fingerprint in your favorite editor. - 1. Create the PostgreSQL database by running `./database.mk createdb`. + 1. Create the PostgreSQL database by running `./database.mk create`. ### Creating a release diff --git a/backend/.dockerignore b/backend/.dockerignore index 6a90a764..893fad98 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -1,368 +1,16 @@ # https://docs.docker.com/engine/reference/builder/#dockerignore-file +# The inverse dockerignore file: .dockerinclude -**/.* -**/*.md -**/*.bak -Dockerfile* +* -**/core -**/TestResult*/ +!**/*.cs +!**/*.cshtml +!**/*.csproj +!**/*.sln +!**/*.graphql +!**/*.json -!./ - -### Csharp ### -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -**/*.rsuser -**/*.suo -**/*.user -**/*.userosscache -**/*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -**/*.userprefs - -# Mono auto generated files -**/mono_crash.* - -# Build results -**/debug/ -**/debugPublic/ -**/release/ -**/releases/ -**/x64/ -**/x86/ -**/arm/ -**/arm64/ -**/bld/ -**/bin/ -**/obj/ -**/log/ -**/logs/ - -# Visual Studio 2015/2017 cache/options directory -**/.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -# wwwroot/ - -# Visual Studio 2017 auto generated files -**/Generated\ Files/ - -# MSTest test Results -**/testresult*/ -**/buildlog.* - -# NUnit -**/*.VisualState.xml -**/TestResult.xml -**/nunit-*.xml - -# Build Results of an ATL Project -**/debugPS/ -**/releasePS/ -**/dlldata.c - -# Benchmark Results -**/BenchmarkDotNet.Artifacts/ - -# .NET Core -**/project.lock.json -**/project.fragment.lock.json -**/artifacts/ - -# StyleCop -**/StyleCopReport.xml - -# Files built by Visual Studio -**/*_i.c -**/*_p.c -**/*_h.h -**/*.ilk -**/*.meta -**/*.obj -**/*.iobj -**/*.pch -**/*.pdb -**/*.ipdb -**/*.pgc -**/*.pgd -**/*.rsp -**/*.sbr -**/*.tlb -**/*.tli -**/*.tlh -**/*.tmp -**/*.tmp_proj -**/*_wpftmp.csproj -**/*.log -**/*.vspscc -**/*.vssscc -**/.builds -**/*.pidb -**/*.svclog -**/*.scc - -# Chutzpah Test files -**/_Chutzpah* - -# Visual C++ cache files -**/ipch/ -**/*.aps -**/*.ncb -**/*.opendb -**/*.opensdf -**/*.sdf -**/*.cachefile -**/*.VC.db -**/*.VC.VC.opendb - -# Visual Studio profiler -**/*.psess -**/*.vsp -**/*.vspx -**/*.sap - -# Visual Studio Trace Files -**/*.e2e - -# TFS 2012 Local Workspace -**/$tf/ - -# Guidance Automation Toolkit -**/*.gpState - -# ReSharper is a .NET coding add-in -**/_ReSharper*/ -**/*.resharper -**/*.DotSettings.user - -# TeamCity is a build add-in -**/_TeamCity* - -# DotCover is a Code Coverage Tool -**/*.dotCover - -# AxoCover is a Code Coverage Tool -**/.axoCover/* -**/!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -**/coverage*.* - -# Visual Studio code coverage results -**/*.coverage -**/*.coveragexml - -# NCrunch -**/_NCrunch_* -**/.*crunch*.local.xml -**/nCrunchTemp_* - -# MightyMoose -**/*.mm.* -**/AutoTest.Net/ - -# Web workbench (sass) -**/.sass-cache/ - -# Installshield output folder -**/express/ - -# DocProject is a documentation generator add-in -**/DocProject/buildhelp/ -**/DocProject/Help/*.HxT -**/DocProject/Help/*.HxC -**/DocProject/Help/*.hhc -**/DocProject/Help/*.hhk -**/DocProject/Help/*.hhp -**/DocProject/Help/Html2 -**/DocProject/Help/html - -# Click-Once directory -**/publish/ - -# Publish Web Output -**/*.publish.xml -**/*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -**/*.pubxml -**/*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -**/PublishScripts/ - -# NuGet Packages -**/*.nupkg -# NuGet Symbol Packages -**/*.snupkg -# The packages folder can be ignored because of Package Restore -**/**/packages/* -# except build/, which is used as an MSBuild target. -**/!**/packages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config -# NuGet v3's project.json files produces more ignorable files -**/*.nuget.props -**/*.nuget.targets - -# Microsoft Azure Build Output -**/csx/ -**/*.build.csdef - -# Microsoft Azure Emulator -**/ecf/ -**/rcf/ - -# Windows Store app package directories and files -**/AppPackages/ -**/BundleArtifacts/ -**/Package.StoreAssociation.xml -**/_pkginfo.txt -**/*.appx -**/*.appxbundle -**/*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -**/*.cache -# but keep track of directories ending in .cache -**/!?*.cache/ - -# Others -**/ClientBin/ -**/~$* -**/*~ -**/*.dbmdl -**/*.dbproj.schemaview -**/*.jfm -# **/*.pfx -**/*.publishsettings -**/orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -**/Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -**/_UpgradeReport_Files/ -**/Backup*/ -**/UpgradeLog*.XML -**/UpgradeLog*.htm -**/ServiceFabricBackup/ -**/*.rptproj.bak - -# SQL Server files -**/*.mdf -**/*.ldf -**/*.ndf - -# Business Intelligence projects -**/*.rdl.data -**/*.bim.layout -**/*.bim_*.settings -**/*.rptproj.rsuser -**/*- backup.rdl -**/*- backup (*).rdl -**/*- backup (*).rdl - -# Microsoft Fakes -**/FakesAssemblies/ - -# GhostDoc plugin setting file -**/*.GhostDoc.xml - -# Node.js Tools for Visual Studio -**/.ntvs_analysis.dat -**/node_modules/ - -# Visual Studio 6 build log -**/*.plg - -# Visual Studio 6 workspace options file -**/*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -**/*.vbw - -# Visual Studio LightSwitch build output -**/**/*.HTMLClient/GeneratedArtifacts -**/**/*.DesktopClient/GeneratedArtifacts -**/**/*.DesktopClient/ModelManifest.xml -**/**/*.Server/GeneratedArtifacts -**/**/*.Server/ModelManifest.xml -**/_Pvt_Extensions - -# Paket dependency manager -**/.paket/paket.exe -**/paket-files/ - -# FAKE - F# Make -**/.fake/ - -# CodeRush personal settings -**/.cr/personal - -# Python Tools for Visual Studio (PTVS) -**/__pycache__/ -**/*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -**/*.tss - -# Telerik's JustMock configuration file -**/*.jmconfig - -# BizTalk build output -**/*.btp.cs -**/*.btm.cs -**/*.odx.cs -**/*.xsd.cs - -# OpenCover UI analysis results -**/OpenCover/ - -# Azure Stream Analytics local run output -**/ASALocalRun/ - -# MSBuild Binary and Structured Log -**/*.binlog - -# NVidia Nsight GPU debugger configuration file -**/*.nvuser - -# MFractors (Xamarin productivity tool) working folder -**/.mfractor/ - -# Local History for Visual Studio -**/.localhistory/ - -# BeatPulse healthcheck temp database -**/healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -**/MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -**/.ionide/ - -# End of https://www.toptal.com/developers/gitignore/api/csharp +src/bin/** +src/obj/** +src/bin\\Debug/** +src/obj\\Debug/** diff --git a/backend/Dockerfile.bootstrap b/backend/Dockerfile.bootstrap index 97f54f3d..42439a59 100644 --- a/backend/Dockerfile.bootstrap +++ b/backend/Dockerfile.bootstrap @@ -1,7 +1,5 @@ # syntax=docker/dockerfile:1.21 # check=error=true -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # https://hub.docker.com/_/microsoft-dotnet-sdk diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index ff08fdb6..7714fef1 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -1,7 +1,5 @@ # syntax=docker/dockerfile:1.21 # check=error=true;skip=SecretsUsedInArgOrEnv -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://github.com/dotnet/dotnet-docker/tree/master/samples/aspnetapp @@ -226,12 +224,14 @@ RUN mkdir --parents \ && mkdir --parents \ ./test/bin \ ./test/obj \ + && mkdir --parents \ + "${HOME}/.nuget" \ + "${HOME}/.dotnet" \ && mkdir --parents \ "${HOME}/.vscode-server" \ "${HOME}/.vscode-server-insiders" \ && mkdir --parents \ - "${HOME}/.nuget" \ - "${HOME}/.dotnet" \ + ${HOME}/.dotnet/corefx/cryptography/x509stores \ && mkdir --parents \ "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index d1f82d94..c493bfb1 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -1,7 +1,5 @@ # syntax=docker/dockerfile:1.21 # check=error=true -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by @@ -120,7 +118,9 @@ WORKDIR /app COPY --from=build /app/src/bin/Release/net10.0/publish ./ RUN mkdir --parents \ - /root/.gnupg \ + /root/.dotnet/corefx/cryptography/x509stores \ + && mkdir --parents \ + /root/.gnupg \ && chmod u=rwx,g=,o= \ /root/.gnupg \ && echo "allow-loopback-pinentry" \ diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 35a69293..7d5859d2 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -26,7 +26,6 @@ public sealed record AppSettings public OpenIdConnectClientSettings OpenIdConnectClient { get; init; } = new(); public GnupgSecretSigningKeySettings GnupgSecretSigningKey { get; init; } = new(); - public JsonWebTokenSettings JsonWebToken { get; init; } = new(); public DatabaseSettings Database { get; init; } = new(); public OpenTelemetrySettings OpenTelemetry { get; init; } = new(); @@ -45,12 +44,6 @@ public sealed record GnupgSecretSigningKeySettings public string Fingerprint { get; init; } = ""; }; - public sealed record JsonWebTokenSettings - { - public string EncryptionCertificatePassword { get; init; } = ""; - public string SigningCertificatePassword { get; init; } = ""; - }; - public sealed record DatabaseSettings { public string ConnectionString { get; set; } = ""; diff --git a/backend/src/Authentication/OpenIdConnectConstants.cs b/backend/src/Authentication/OpenIdConnectConstants.cs index 8214e1cd..f54e7697 100644 --- a/backend/src/Authentication/OpenIdConnectConstants.cs +++ b/backend/src/Authentication/OpenIdConnectConstants.cs @@ -1,13 +1,22 @@ using System; +using System.Security.Cryptography.X509Certificates; namespace Database.Authentication; public static class OpenIdConnectConstants { public static readonly TimeSpan AccessTokenLifetime = TimeSpan.FromHours(1); + public static readonly TimeSpan RefreshTokenLifetime = TimeSpan.FromDays(1); + + public const string AuthorizationHeaderBearer = "Bearer"; + public const string DatabaseQuartzSchedulerId = "database"; public const string MetabaseRegistrationId = "metabase"; public const string MetabaseClientId = "metabase"; - public const string DatabaseQuartzSchedulerId = "database"; - public const string AuthorizationHeaderBearer = "Bearer"; + + public const StoreName CertificateStoreName = StoreName.My; + public const StoreLocation CertificateStoreLocation = StoreLocation.CurrentUser; + + public const string SigningSubjectDistinguishedName = $"CN=Metabase OpenId Connect Client Signing Certificate"; + public const string EncryptionSubjectDistinguishedName = $"CN=Metabase OpenId Connect Client Encryption Certificate"; } \ No newline at end of file diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index db9ac38b..51f7100c 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,36 +1,107 @@ using System; -using System.IO; +using System.Collections.Generic; using System.Net.Http; using System.Reflection; using System.Security.Cryptography.X509Certificates; using Database.Authentication; using Database.Authorization; using Database.Data; +using Database.Jobs; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenIddict.Abstractions; using OpenIddict.Client; using Quartz; +using Quartz.AspNetCore; namespace Database.Configuration; public static class AuthConfiguration { private static readonly TimeSpan s_cookieExpirationTimeSpan = TimeSpan.FromDays(1); + + private static void BootstrapCertificates() + { + using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); + try + { + store.Open(OpenFlags.ReadWrite); + { + var distinguishedName = OpenIdConnectConstants.SigningSubjectDistinguishedName; + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + validOnly: true + ); + if (certificates.Count == 0) + { + store.Add( + JwtSigningAndEncryptionCertificateRotationJob.CreateSigningCertificate( + distinguishedName + ) + ); + } + } + { + var distinguishedName = OpenIdConnectConstants.EncryptionSubjectDistinguishedName; + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + validOnly: true + ); + if (certificates.Count == 0) + { + store.Add( + JwtSigningAndEncryptionCertificateRotationJob.CreateEncryptionCertificate( + distinguishedName + ) + ); + } + } + } + finally + { + store.Close(); + } + } + + private static IEnumerable FindCertificates(string distinguishedName) + { + using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); + try + { + store.Open(OpenFlags.ReadOnly); + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + // OpenIddict automatically prioritizes the cert with the latest expiration + // Expired keys are needed for tokens signed and encrypted before it expired + validOnly: false + ); + foreach (var certificate in certificates) + { + yield return certificate; + } + } + finally + { + store.Close(); + } + } + public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment, AppSettings appSettings ) { - var encryptionCertificate = LoadCertificate("jwt-encryption-certificate.pfx", appSettings.JsonWebToken.EncryptionCertificatePassword); - var signingCertificate = LoadCertificate("jwt-signing-certificate.pfx", appSettings.JsonWebToken.SigningCertificatePassword); + BootstrapCertificates(); services.AddScoped(); services.AddScoped(); ConfigureAuthenticationAndAuthorizationServices(services); ConfigureTaskScheduling(services, environment); - ConfigureOpenIddictServices(services, environment, appSettings, encryptionCertificate, signingCertificate); + ConfigureOpenIddictServices(services, environment, appSettings); AddAuthorizationServices(services); } @@ -41,27 +112,6 @@ IServiceCollection services services.AddScoped(); } - private static X509Certificate2 LoadCertificate( - string fileName, - string password - ) - { - if (string.IsNullOrEmpty(password)) - { - throw new ArgumentException($"Empty password for certificate {fileName}."); - } - - var stream = - Assembly.GetExecutingAssembly().GetManifestResourceStream($"Database.{fileName}") - ?? throw new ArgumentException($"Missing certificate {fileName}."); - using var buffer = new MemoryStream(); - stream.CopyTo(buffer); - return X509CertificateLoader.LoadPkcs12( - buffer.ToArray(), - password, - X509KeyStorageFlags.EphemeralKeySet - ); - } private static void ConfigureAuthenticationAndAuthorizationServices( IServiceCollection services @@ -115,20 +165,32 @@ IWebHostEnvironment environment { _.SchedulerId = OpenIdConnectConstants.DatabaseQuartzSchedulerId; _.SchedulerName = "Database"; - _.UseSimpleTypeLoader(); - _.UseInMemoryStore(); - _.UseDefaultThreadPool(_ => - _.MaxConcurrency = 10 - ); if (environment.IsEnvironment(Program.TestEnvironment)) { var probablyUniqueId = Guid.NewGuid().ToString(); _.SchedulerId = $"{OpenIdConnectConstants.DatabaseQuartzSchedulerId}-{probablyUniqueId}"; _.SchedulerName = $"Metabase-{probablyUniqueId}"; } + _.UseSimpleTypeLoader(); + _.UseInMemoryStore(); + _.UseDefaultThreadPool(_ => + _.MaxConcurrency = 10 + ); + var jwtSigningAndEncryptionKeyRotationJobKey = new JobKey(JwtSigningAndEncryptionCertificateRotationJob.KeyName); + _.AddJob(_ => _.WithIdentity(jwtSigningAndEncryptionKeyRotationJobKey)); + _.AddTrigger(_ => _ + .ForJob(jwtSigningAndEncryptionKeyRotationJobKey) + .WithIdentity(JwtSigningAndEncryptionCertificateRotationJob.TriggerIdentityName) + .StartNow() + .WithSimpleSchedule(_ => _ + .WithIntervalInHours(24) + .RepeatForever() + .WithMisfireHandlingInstructionFireNow() + ) + ); }); // Register the Quartz.NET service and configure it to block shutdown until jobs are complete. - services.AddQuartzHostedService(_ => + services.AddQuartzServer(_ => _.WaitForJobsToComplete = true ); } @@ -136,9 +198,7 @@ IWebHostEnvironment environment private static void ConfigureOpenIddictServices( IServiceCollection services, IWebHostEnvironment environment, - AppSettings appSettings, - X509Certificate2 encryptionCertificate, - X509Certificate2 signingCertificate + AppSettings appSettings ) { services.AddOpenIddict() @@ -189,8 +249,14 @@ X509Certificate2 signingCertificate .AllowRefreshTokenFlow(); // Register the signing and encryption credentials. See https://stackoverflow.com/questions/50862755/signing-keys-certificates-and-client-secrets-confusion/50932120#50932120 - _.AddEncryptionCertificate(encryptionCertificate) - .AddSigningCertificate(signingCertificate); + foreach (var encryptionCertificate in FindCertificates(OpenIdConnectConstants.EncryptionSubjectDistinguishedName)) + { + _.AddEncryptionCertificate(encryptionCertificate); + } + foreach (var signingCertificate in FindCertificates(OpenIdConnectConstants.SigningSubjectDistinguishedName)) + { + _.AddSigningCertificate(signingCertificate); + } // Register the ASP.NET Core host and configure the ASP.NET Core-specific options. _.UseAspNetCore() diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index b42c3c21..5ebc820b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -3,16 +3,19 @@ net10.0 14.0 - 31666837686487542159 - enable + + 31666837686487542159 + true + + + true true true latest-Recommended true - true @@ -63,7 +66,7 @@ - + @@ -75,26 +78,33 @@ + + false + + $(DefaultItemExcludes);**/obj/**;**/bin/**;**/obj%5CDebug/**;**/bin%5CDebug/**;**/logs/**;**/files/** + + - - + - + PreserveNewest - - - - + + PreserveNewest + + PreserveNewest - - + + + + diff --git a/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs new file mode 100644 index 00000000..580efe5f --- /dev/null +++ b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs @@ -0,0 +1,228 @@ +using System; +using System.Runtime.ConstrainedExecution; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Database.Authentication; +using Microsoft.Extensions.Logging; +using Quartz; + +namespace Database.Jobs; + +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to create certificate. Distinguished Name: '{DistinguishedName}'")] + internal static partial void FailedToCreateCertificate( + this ILogger logger, + string distinguishedName, + Exception exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to store certificates.")] + internal static partial void FailedToStoreCertificates( + this ILogger logger, + Exception exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to cleanup long expired certificates.")] + internal static partial void FailedToCleanupLongExpiredCertificates( + this ILogger logger, + Exception exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to remove certificate. Thumbprint: '{Thumbprint}'. Distinguished Name: '{DistinguishedName}'.")] + internal static partial void FailedToRemoveCertificate( + this ILogger logger, + string thumbprint, + string distinguishedName, + Exception exception + ); +} + +public sealed class JwtSigningAndEncryptionCertificateRotationJob( + ILogger logger +) +: IJob +{ + public const string KeyName = "JwtSigningAndEncryptionKeyRotationJob"; + public const string TriggerIdentityName = $"{KeyName}.Trigger"; + + private static readonly TimeSpan s_notBeforeOffset = TimeSpan.FromMinutes(-5); + private static readonly TimeSpan s_notAfterOffset = TimeSpan.FromDays(3); + + public async Task Execute(IJobExecutionContext context) + { + StoreCertificatesWithErrorHandling( + CreateSigningCertificateWithErrorHandling(OpenIdConnectConstants.SigningSubjectDistinguishedName), + CreateEncryptionCertificateWithErrorHandling(OpenIdConnectConstants.EncryptionSubjectDistinguishedName) + ); + CleanupLongExpiredCertificatesWithErrorHandling( + OpenIdConnectConstants.SigningSubjectDistinguishedName, + OpenIdConnectConstants.EncryptionSubjectDistinguishedName + ); + // TODO: Trigger OpenIddict reload. Currently done dialy with a cron job that restart all services. + } + + public static X509Certificate2 CreateSigningCertificate(string distinguishedName) + { + // In the future use ECDSA. + // using var algorithm = ECDsa.Create(ECCurve.NamedCurves.nistP256); + // var request = new CertificateRequest( + // new X500DistinguishedName(distinguishedName), + // algorithm, + // HashAlgorithmName.SHA256 + // ); + using var algorithm = RSA.Create(keySizeInBits: 3072); + var request = new CertificateRequest( + new X500DistinguishedName(distinguishedName), + algorithm, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1 + ); + request.CertificateExtensions.Add( + new X509KeyUsageExtension( + X509KeyUsageFlags.DigitalSignature, + critical: true + ) + ); + var now = TimeProvider.System.GetUtcNow(); + var ephemeralCertificate = request.CreateSelfSigned( + notBefore: now.Add(s_notBeforeOffset), + notAfter: now.Add(s_notAfterOffset) + ); + // Roundtrip through PFX to ensure the private key is persisted with the certificate + return X509CertificateLoader.LoadPkcs12( + ephemeralCertificate.Export(X509ContentType.Pfx), + null, + X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet + ); + } + + private X509Certificate2? CreateSigningCertificateWithErrorHandling(string distinguishedName) + { + try + { + return CreateSigningCertificate(distinguishedName); + } + catch (Exception exception) + { + logger.FailedToCreateCertificate(distinguishedName, exception); + return null; + } + } + + public static X509Certificate2 CreateEncryptionCertificate(string distinguishedName) + { + // In the furture use `ML-KEM`. + using var algorithm = RSA.Create(keySizeInBits: 3072); + var request = new CertificateRequest( + new X500DistinguishedName(distinguishedName), + algorithm, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1 + ); + request.CertificateExtensions.Add( + new X509KeyUsageExtension( + X509KeyUsageFlags.KeyEncipherment, + critical: true + ) + ); + var now = TimeProvider.System.GetUtcNow(); + var ephemeralCertificate = request.CreateSelfSigned( + notBefore: now.Add(s_notBeforeOffset), + notAfter: now.Add(s_notAfterOffset) + ); + // Roundtrip through PFX to ensure the private key is persisted with the certificate + return X509CertificateLoader.LoadPkcs12( + ephemeralCertificate.Export(X509ContentType.Pfx), + null, + X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet + ); + } + + private X509Certificate2? CreateEncryptionCertificateWithErrorHandling(string distinguishedName) + { + try + { + return CreateEncryptionCertificate(distinguishedName); + } + catch (Exception exception) + { + logger.FailedToCreateCertificate(distinguishedName, exception); + return null; + } + } + + private void StoreCertificatesWithErrorHandling(params X509Certificate2?[] certificates) + { + using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); + try + { + store.Open(OpenFlags.ReadWrite); + foreach (var certificate in certificates) + { + if (certificate is not null) + { + store.Add(certificate); + } + } + } + catch (Exception exception) + { + logger.FailedToStoreCertificates(exception); + } + finally + { + store.Close(); + } + } + + public void CleanupLongExpiredCertificatesWithErrorHandling(params string[] distinguishedNames) + { + using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); + try + { + store.Open(OpenFlags.ReadWrite | OpenFlags.IncludeArchived); + foreach (var distinguishedName in distinguishedNames) + { + var certificates = store.Certificates.Find( + X509FindType.FindBySubjectDistinguishedName, + distinguishedName, + validOnly: false + ); + var now = TimeProvider.System.GetUtcNow(); + foreach (var certificate in certificates) + { + // Use `NotAfterDaysOffset` as overlap period. + if (certificate.NotAfter.Add(OpenIdConnectConstants.RefreshTokenLifetime) < now) + { + try + { + store.Remove(certificate); + } + catch (Exception exception) + { + logger.FailedToRemoveCertificate(certificate.Thumbprint, distinguishedName, exception); + } + } + } + } + } + catch (Exception exception) + { + logger.FailedToCleanupLongExpiredCertificates(exception); + } + finally + { + store.Close(); + } + } +} \ No newline at end of file diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 7be7f24c..2054904a 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -130,10 +131,17 @@ Uri openTelemetryHost .Enrich.WithSpan() // add trace context .WriteTo.Console(formatProvider: CultureInfo.InvariantCulture) // inspired by https://last9.io/blog/serilog-and-opentelemetry/ - .WriteTo.OpenTelemetry( - endpoint: openTelemetryHost.AbsoluteUri, - protocol: OtlpProtocol.Grpc - ) + .WriteTo.OpenTelemetry(_ => + { + _.Endpoint = openTelemetryHost.AbsoluteUri; + _.Protocol = OtlpProtocol.Grpc; + _.OnBeginSuppressInstrumentation = + OpenTelemetry.SuppressInstrumentationScope.Begin; + _.ResourceAttributes = new Dictionary + { + ["service.name"] = "backend", + }; + }) .WriteTo.File( new CompactJsonFormatter(), LogsPath, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 2e15dbe2..1c41d955 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net.Http; +using System.Net.Mime; using System.Text; using System.Text.Json; using System.Threading.Tasks; @@ -363,16 +365,38 @@ public void Configure(WebApplication app) ResponseWriter = WriteJsonResponse } ) - .WithName("Health") - .WithDescription("Check the webserver health.") - .WithTags("Health") - .DisableHttpMetrics(); + .DisableHttpMetrics() + .AddOpenApiOperationTransformer((operation, context, cancellationToken) => + { + operation.Description = "Checks whether the webserver is healthy."; + var responseContent = new Dictionary + { + [MediaTypeNames.Application.Json] = new OpenApiMediaType + { + Schema = new OpenApiSchema { Type = JsonSchemaType.Object } + } + }; + operation.Responses = new OpenApiResponses + { + ["200"] = new OpenApiResponse + { + Description = "Healthy", + Content = responseContent + }, + ["503"] = new OpenApiResponse + { + Description = "Unhealthy", + Content = responseContent + } + }; + return Task.CompletedTask; + }); } // Inspired by https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-7.0#customize-output private static Task WriteJsonResponse(HttpContext context, HealthReport healthReport) { - context.Response.ContentType = "application/json; charset=utf-8"; + context.Response.ContentType = MediaTypeNames.Application.Json; var options = new JsonWriterOptions { Indented = true }; using var memoryStream = new MemoryStream(); using (var jsonWriter = new Utf8JsonWriter(memoryStream, options)) diff --git a/backend/src/appsettings.json b/backend/src/appsettings.json index 4faf77ac..6b859078 100644 --- a/backend/src/appsettings.json +++ b/backend/src/appsettings.json @@ -1,3 +1,3 @@ { "AllowedHosts": "*" -} +} \ No newline at end of file diff --git a/certificates.mk b/certificates.mk deleted file mode 100755 index 50e2bf76..00000000 --- a/certificates.mk +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env -S make --file - -include ./.env - -SHELL := /usr/bin/env bash -.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c -MAKEFLAGS += --warn-undefined-variables - -# Taken from https://www.client9.com/self-documenting-makefiles/ -help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ - printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ - }' $(MAKEFILE_LIST) -.PHONY : help -.DEFAULT_GOAL := help - -jwt : ## Create JWT encryption and signing certificates if necessary - docker compose pull \ - bootstrap - docker compose build \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - bootstrap - docker compose run \ - --rm \ - bootstrap \ - bash -ceux -o pipefail " \ - dotnet-script \ - /app/create-certificates.csx \ - -- \ - ${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD} \ - ${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD} \ - " -.PHONY : jwt diff --git a/database.mk b/database.mk index 790bb4a4..d90debc5 100755 --- a/database.mk +++ b/database.mk @@ -1,4 +1,5 @@ #!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) include ./.env @@ -9,7 +10,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true dump_archive_name = postgresql_dumpall.gz -files_archive_name = files.gz # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -31,8 +31,15 @@ psql : ## Enter PostgreSQL interactive terminal in the running `database` contai --dbname="${POSTGRES_DATABASE_NAME}" .PHONY : psql -createdb : CONTAINER_NAME = create_${NAME}_database -createdb : ## Create database with name `${POSTGRES_DATABASE_NAME}` +remove-volume : ## Remove data and files volumes + docker volume rm \ + "${NAME}_${ENVIRONMENT}_data" + docker volume rm \ + "${NAME}_${ENVIRONMENT}_files" +.PHONY : remove-volume + +create : CONTAINER_NAME = create_${NAME}_database +create : ## Create database with name `${POSTGRES_DATABASE_NAME}` -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} docker compose run \ @@ -44,13 +51,13 @@ createdb : ## Create database with name `${POSTGRES_DATABASE_NAME}` ${CONTAINER_NAME} \ createdb \ --username="${POSTGRES_USER}" \ - ${POSTGRES_DATABASE_NAME} + "${POSTGRES_DATABASE_NAME}" docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} -.PHONY : createdb +.PHONY : create -dropdb : CONTAINER_NAME = drop_${NAME}_database -dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` +drop : CONTAINER_NAME = drop_${NAME}_database +drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` -docker container stop ${CONTAINER_NAME} -docker container rm --volumes ${CONTAINER_NAME} docker compose run \ @@ -62,13 +69,13 @@ dropdb : ## Drop database with name `${POSTGRES_DATABASE_NAME}` ${CONTAINER_NAME} \ dropdb \ --username="${POSTGRES_USER}" \ - ${POSTGRES_DATABASE_NAME} + "${POSTGRES_DATABASE_NAME}" docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} -.PHONY : dropdb +.PHONY : drop sql : CONTAINER_NAME = sql_${NAME}_database -sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `./database.mk sql SCRIPT=./my.sql` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) docker compose down \ --remove-orphans \ database @@ -85,10 +92,11 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for ${CONTAINER_NAME} \ psql \ --echo-all \ - --set=ON_ERROR_STOP=1 \ + --no-psqlrc \ + --set=ON_ERROR_STOP=on \ --file=- \ --username="${POSTGRES_USER}" \ - --dbname=${POSTGRES_DATABASE_NAME} + --dbname="${POSTGRES_DATABASE_NAME}" docker container stop ${CONTAINER_NAME} docker container rm --volumes ${CONTAINER_NAME} docker compose up \ @@ -101,8 +109,8 @@ migrate : SCRIPT = ./backend/src/Migrations/migrate.sql migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) .PHONY : migrate -# Backup with `pg_dumpall`: https://www.postgresql.org/docs/13/backup-dump.html#BACKUP-DUMP-ALL -# Command `pg_dumpall`: https://www.postgresql.org/docs/13/app-pg-dumpall.html +# Backup with `pg_dump`: https://www.postgresql.org/docs/current/backup-dump.html +# Command `pg_dump`: https://www.postgresql.org/docs/current/app-pgdump.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database @@ -120,8 +128,8 @@ backup : ## Backup database and related data to directory with absolute path `${ database while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done docker exec \ - ${DATABASE_CONTAINER_NAME} \ - pg_dumpall \ + ${CONTAINER_NAME} \ + pg_dump \ --clean \ --if-exists \ --username="${POSTGRES_USER}" \ @@ -157,12 +165,10 @@ backup : ## Backup database and related data to directory with absolute path `${ restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` +restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` docker compose down \ --remove-orphans \ database - docker volume rm \ - ${NAME}_data -docker container stop ${DATABASE_CONTAINER_NAME} -docker container rm --volumes ${DATABASE_CONTAINER_NAME} docker compose run \ @@ -170,18 +176,27 @@ restore : ## Restore database and related data from directory with absolute path --detach \ database while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + -docker exec \ + ${CONTAINER_NAME} \ + dropdb \ + --username="${POSTGRES_USER}" \ + "${POSTGRES_DATABASE_NAME}" + docker exec \ + ${CONTAINER_NAME} \ + createdb \ + --username="${POSTGRES_USER}" \ + "${POSTGRES_DATABASE_NAME}" gunzip --stdout ${DIR}/${dump_archive_name} \ - | grep --invert-match --extended-regexp \ - "^CREATE ROLE ${POSTGRES_USER};|^ALTER ROLE ${POSTGRES_USER}" \ | docker exec \ --interactive \ ${DATABASE_CONTAINER_NAME} \ psql \ --echo-all \ - --set=ON_ERROR_STOP=1 \ + --no-psqlrc \ + --set=ON_ERROR_STOP=on \ --file=- \ --username="${POSTGRES_USER}" \ - --dbname=postgres + --dbname="${POSTGRES_DATABASE_NAME}" docker container stop ${DATABASE_CONTAINER_NAME} docker container rm --volumes ${DATABASE_CONTAINER_NAME} -docker container stop ${FILES_CONTAINER_NAME} diff --git a/deploy.mk b/deploy.mk index f9a8ce02..1ca36adb 100755 --- a/deploy.mk +++ b/deploy.mk @@ -1,4 +1,5 @@ #!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) include ./.env @@ -8,6 +9,15 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true +dotenv_linter = \ + docker run \ + --rm \ + --user $(shell id --user):$(shell id --group) \ + --volume "$(shell pwd):/mnt:ro" \ + --pull "always" \ + --quiet \ + dotenvlinter/dotenv-linter:latest + # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ @@ -28,28 +38,31 @@ help : ## Print this help # and finally in `cd /app/production` and checking that everything works as # expected. Before trying it on staging, I usually play the database from # production into staging. -deploy : DIR = $(shell pwd)/backup -deploy : ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk TARGET=v1.0.0 deploy` - $(MAKE) begin-maintenance - $(MAKE) store-commit - $(MAKE) backup - $(MAKE) fetch-all - $(MAKE) checkout-target - $(MAKE) migrate - $(MAKE) services - $(MAKE) run-tests - $(MAKE) end-maintenance +deploy : DIR = "$(shell pwd)/backup" +deploy : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk deploy TARGET=v1.0.0` + $(MAKE) --file="${SELF}" begin-maintenance + $(MAKE) --file="${SELF}" store-target set-target TARGET="${TARGET}" + $(MAKE) --file="${SELF}" backup DIR="${DIR}" + $(MAKE) --file="${SELF}" fetch-all + $(MAKE) --file="${SELF}" checkout TARGET="${TARGET}" + $(MAKE) --file="${SELF}" dotenv + $(MAKE) --file="${SELF}" migrate + $(MAKE) --file="${SELF}" services + $(MAKE) --file="${SELF}" run-tests + $(MAKE) --file="${SELF}" end-maintenance .PHONY : deploy -rollback : TARGET = $(shell cat ./commit) -rollback : DIR = $(shell pwd)/backup -rollback : ## Rollback deployment attempt (use commit hash stored in `./commit` and database backup stored in `./backup/`) - $(MAKE) begin-maintenance - $(MAKE) checkout-target - $(MAKE) restore - $(MAKE) services - $(MAKE) run-tests - $(MAKE) end-maintenance +rollback : TARGET = "$(shell cat ./.stored-target)" +rollback : DIR = "$(shell pwd)/backup" +rollback : symlink ## Rollback deployment attempt (uses target stored in `./.stored-target` and database backup stored in `./backup/`) + $(MAKE) --file="${SELF}" begin-maintenance + $(MAKE) --file="${SELF}" set-target TARGET="${TARGET}" + $(MAKE) --file="${SELF}" checkout TARGET="${TARGET}" + $(MAKE) --file="${SELF}" dotenv + $(MAKE) --file="${SELF}" restore DIR="${DIR}" + $(MAKE) --file="${SELF}" services + $(MAKE) --file="${SELF}" run-tests + $(MAKE) --file="${SELF}" end-maintenance .PHONY : rollback begin-maintenance : ## Begin maintenance @@ -62,47 +75,51 @@ end-maintenance : ## End maintenance rm ./nginx/html/maintenance.html .PHONY : end-maintenance -backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./deploy.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` - $(MAKE) \ - --file="$(shell pwd)/database.mk" \ - DIR=${DIR} \ - backup +store-target : ## Store the value `${TARGET}` + echo "${TARGET}" \ + > ./.stored-target +.PHONY : store-target + +set-target : ## Set variable `TARGET` in ./.env to `${TARGET}` + sed --in-place --regexp-extended \ + 's#^TARGET=(.*)$$#TARGET=${TARGET}#' \ + ./.env +.PHONY : set-target + +backup : ## Backup database and related data to the directory with the absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./deploy.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` + $(MAKE) --file=./database.mk \ + backup \ + DIR="${DIR}" .PHONY : backup restore : CONTAINER_NAME = restore_${NAME}_database -restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./deploy.mk restore DIR=/app/data/backups/2021-04-22_15_43_35/` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` - $(MAKE) \ - --file="$(shell pwd)/database.mk" \ - DIR=${DIR} \ - backup +restore : ## Restore database and related data from the directory with the absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./deploy.mk restore DIR=/app/data/backups/2021-04-22_15_43_35/` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `./docker.mk restart`)` + $(MAKE) --file=./database.mk \ + backup \ + DIR="${DIR}" .PHONY : restore migrate : ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - $(MAKE) \ - --file="$(shell pwd)/database.mk" \ + $(MAKE) --file=./database.mk \ migrate .PHONY : migrate -store-commit : ## Store current commit - git rev-parse \ - --verify \ - HEAD \ - > ./commit -.PHONY : store-commit - fetch-all : ## Fetch all git fetch --all .PHONY : fetch-all # Inspired by https://grimoire.ca/git/stop-using-git-pull-to-deploy/ -checkout-target : ## Fetch and checkout `${TARGET}` +checkout : ## Fetch and checkout `${TARGET}` git checkout --force "${TARGET}" -.PHONY : checkout-target +.PHONY : checkout + +dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are available in ./.env + ${dotenv_linter} diff /mnt/.env "/mnt/.env.${ENVIRONMENT}.sample" +.PHONY : dotenv # Note that NGINX is because of its dependencies taken down and up last and in # one go so the maintenance page is only down very shortly. -services : ## Deploy services - $(MAKE) --file=./docker.mk build +services : ## Recreate services docker compose up \ --force-recreate \ --renew-anon-volumes \ @@ -110,6 +127,22 @@ services : ## Deploy services --wait .PHONY : services -run-tests : COMMAND = true -run-tests : execb ## Run tests +run-tests : ## TODO Run tests .PHONY : run-tests + +restart : ## Restart service `${SERVICE}` and await its health + $(MAKE) --file=./docker.mk \ + restart \ + SERVICE="${SERVICE}" +.PHONY : restart + +symlink : ## Confirm that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml + if [[ ${ENVIRONMENT} == "staging" ]]; then \ + file="./docker-compose.production.yaml" ; \ + else \ + file="./docker-compose.${ENVIRONMENT}.yaml" ; \ + fi && \ + if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef $${file} ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" ; \ + fi +.PHONY : symlink diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 37bfc58d..11d10fee 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -1,5 +1,5 @@ # Keep setup in sync with the one in ./docker-compose.production.yaml -name: "${NAME}" +name: "${NAME:?required}_${ENVIRONMENT:?required}" services: database: @@ -7,8 +7,8 @@ services: volumes: - data:/var/lib/postgresql/data environment: - POSTGRES_USER: "${POSTGRES_USER}" - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` # ports: # - "0.0.0.0:5432:5432" @@ -20,14 +20,15 @@ services: # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: [ - "-c", "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" + "-c", + "max_connections=100" + # "-c", "log_statement=all", + # "-c", "log_duration=1" ] restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] + test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] interval: "5s" timeout: "5s" retries: "5" @@ -46,8 +47,8 @@ services: - files:/app/src/files - backend_vscode_server:/home/me/.vscode-server - backend_vscode_server_insiders:/home/me/.vscode-server-insiders - # Create volume for GnuPG - gnupg:/home/me/.gnupg + - certificates:/home/me/.dotnet/corefx/cryptography/x509stores # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -73,12 +74,10 @@ services: XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "database" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME:?required}'; User Id='${POSTGRES_USER:?required}'; Password='${POSTGRES_PASSWORD:?required}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" - XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" - XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" @@ -186,18 +185,10 @@ services: labels: autoheal: "true" - bootstrap: - build: - context: "./backend" - dockerfile: "./Dockerfile.bootstrap" - volumes: - - ./backend:/app - profiles: - - "manual" - volumes: data: files: gnupg: + certificates: backend_vscode_server: backend_vscode_server_insiders: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 15c6b489..aa404058 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -1,5 +1,5 @@ # Keep setup in sync with the one in ./docker-compose.development.yaml -name: "${NAME}" +name: "${NAME:?required}_${ENVIRONMENT:?required}" services: database: @@ -7,8 +7,8 @@ services: volumes: - data:/var/lib/postgresql/data environment: - POSTGRES_USER: "${POSTGRES_USER}" - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}" + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` # ports: # - "0.0.0.0:5432:5432" @@ -20,14 +20,15 @@ services: # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: [ - "-c", "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" + "-c", + "max_connections=100" + # "-c", "log_statement=all", + # "-c", "log_duration=1" ] restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER}'" ] + test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] interval: "5s" timeout: "5s" retries: "5" @@ -35,18 +36,16 @@ services: autoheal: "true" backend: - build: - context: "./backend" - dockerfile: "./Dockerfile.production" + image: "${NAME:?required}-backend:${TARGET:?required}" depends_on: database: condition: "service_started" volumes: - backend_logs:/app/logs - files:/app/files - - ./secrets:/run/secrets:ro - # Create volume for GnuPG - gnupg:/root/.gnupg + # persist certificates in the [`X509Store`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store) + - certificates:/root/.dotnet/corefx/cryptography/x509stores environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" @@ -57,12 +56,10 @@ services: XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" XBASE_Database__SchemaName: "database" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME}'; User Id='${POSTGRES_USER}'; Password='${POSTGRES_PASSWORD}'; Maximum Pool Size=90;" + XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME:?required}'; User Id='${POSTGRES_USER:?required}'; Password='${POSTGRES_PASSWORD:?required}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" - XBASE_JsonWebToken__EncryptionCertificatePassword: "${JSON_WEB_TOKEN_ENCRYPTION_CERTIFICATE_PASSWORD:?required}" - XBASE_JsonWebToken__SigningCertificatePassword: "${JSON_WEB_TOKEN_SIGNING_CERTIFICATE_PASSWORD:?required}" XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" @@ -77,12 +74,12 @@ services: [ "dotnet", "/app/App.dll" - # "/Serilog:MinimumLevel:Default=Debug", - # "/Serilog:MinimumLevel:Microsoft=Debug", - # "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", - # "/Serilog:MinimumLevel:Override:Microsoft=Debug", - # "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", - # "/Serilog:MinimumLevel:Override:System=Debug" + # "/Serilog:MinimumLevel:Default=Debug", + # "/Serilog:MinimumLevel:Microsoft=Debug", + # "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", + # "/Serilog:MinimumLevel:Override:Microsoft=Debug", + # "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", + # "/Serilog:MinimumLevel:Override:System=Debug" ] restart: "unless-stopped" healthcheck: @@ -93,12 +90,11 @@ services: retries: "20" labels: autoheal: "true" + pull_policy: "never" stdin_open: true frontend: - build: - context: "./frontend" - dockerfile: "./Dockerfile.production" + image: "${NAME:?required}-frontend:${TARGET:?required}" depends_on: backend: condition: "service_started" @@ -113,6 +109,7 @@ services: retries: "20" labels: autoheal: "true" + pull_policy: "never" nginx: image: "nginx:1.29-trixie-otel" @@ -138,17 +135,9 @@ services: labels: autoheal: "true" - bootstrap: - build: - context: "./backend" - dockerfile: "./Dockerfile.bootstrap" - volumes: - - ./backend:/app - profiles: - - "manual" - volumes: data: files: gnupg: + certificates: backend_logs: diff --git a/docker.mk b/docker.mk index 857ee206..e2295599 100755 --- a/docker.mk +++ b/docker.mk @@ -1,4 +1,5 @@ #!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) include ./.env @@ -26,15 +27,24 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -name : ## Print value of variable `NAME` - @echo ${NAME} -.PHONY : name +environment : ## Print value of variable `ENVIRONMENT` + @echo ${ENVIRONMENT} +.PHONY : environment + +symlink : ## Confirm that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml + if [[ ${ENVIRONMENT} == "staging" ]]; then \ + file="./docker-compose.production.yaml" ; \ + else \ + file="./docker-compose.${ENVIRONMENT}.yaml" ; \ + fi && \ + if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef $${file} ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" ; \ + fi +.PHONY : symlink dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are available in ./.env ${dotenv_linter} diff /mnt/.env "/mnt/.env.${ENVIRONMENT}.sample" - ${dotenv_linter} diff /mnt/frontend/.env.local "/mnt/frontend/.env.local.${ENVIRONMENT}.sample" ${dotenv_linter} diff /mnt/.env.staging.sample /mnt/.env.production.sample - ${dotenv_linter} diff /mnt/frontend/.env.local.staging.sample /mnt/frontend/.env.local.production.sample .PHONY : dotenv config : ## Parse, resolve and render compose file in canonical format @@ -52,14 +62,11 @@ pull : ## Pull images docker compose pull ${SERVICE} .PHONY : pull -# To debug errors during build add `--progress plain \` to get additional -# output. -build : dotenv check pull ## Build images +build : symlink dotenv check pull ## Build images docker compose build \ --pull \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) ${SERVICE} - # --no-cache .PHONY : build bake : ## Print docker-compose file equivalent bake file @@ -82,15 +89,9 @@ remove : ## Remove stopped services --volumes ${SERVICE} .PHONY : remove -remove-data-and-files-volumes : ## Remove data and files volume - docker volume rm \ - ${NAME}_data - docker volume rm \ - ${NAME}_files -.PHONY : remove-data-and-files-volumes - up : dotenv ## (Re)create and start services docker compose up \ + --no-build \ --remove-orphans \ --wait ${SERVICE} .PHONY : up @@ -102,8 +103,10 @@ down : ## Stop services and remove services and networks created by `up` ./frontend/queries/*.generated.ts .PHONY : down -restart : ## Restart services, for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` +restart : ## Restart services (and await their health), for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` docker compose restart ${SERVICE} + docker compose up \ + --wait ${SERVICE} .PHONY : restart attach : ## Attach to the `${SERVICE}` service, for example, `make attach SERVICE=backend` (to detach without stopping use `CTRL-p` followed by `CTRL-q` and otherwise `CTRL-c`) diff --git a/forge.mk b/forge.mk new file mode 100755 index 00000000..98cf5034 --- /dev/null +++ b/forge.mk @@ -0,0 +1,55 @@ +#!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) + +SHELL := /usr/bin/env bash +.SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c +MAKEFLAGS += --warn-undefined-variables + +NAME = metabase +TARGET = $(shell git rev-parse --verify HEAD) + +# Taken from https://www.client9.com/self-documenting-makefiles/ +help : ## Print this help + @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ + }' $(MAKEFILE_LIST) +.PHONY : help +.DEFAULT_GOAL := help + +target : ## Print value of variable `TARGET` + @echo ${TARGET} +.PHONY : target + +build : ## Build image, for example, `./forge.mk build SERVICE=backend` + docker build \ + --pull \ + --file "./${SERVICE}/Dockerfile.production" \ + --tag "${NAME}-${SERVICE}:${TARGET}" \ + "./${SERVICE}" +.PHONY : build + +push : ## Push image, for example, `./forge.mk push SERVICE=backend USER=cloud HOST=192.102.163.92` + docker save \ + --platform "linux/amd64" \ + "${NAME}-${SERVICE}:${TARGET}" \ + | bzip2 \ + | ssh "${USER}@${HOST}" "docker load" +.PHONY : push + +all : ## Build and push all images + $(MAKE) --file="${SELF}" \ + build \ + push \ + SERVICE=backend + $(MAKE) --file="${SELF}" \ + build \ + push \ + SERVICE=frontend +.PHONY : all + +# deploy : all ## Deploy in the cloud, for example, `./forge.mk deploy ENVIRONMENT=staging USER=cloud HOST=192.102.163.92` +# ssh "${USER}@${HOST}" " \ +# cd '/app/${ENVIRONMENT}' \ +# && ./deploy.mk deploy TARGET='${TARGET}' \ +# " +# .PHONY : deploy diff --git a/frontend/.dockerignore b/frontend/.dockerignore index c4de8a51..8f6486e9 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -1,47 +1,20 @@ # https://docs.docker.com/engine/reference/builder/#dockerignore-file +# The inverse dockerignore file: .dockerinclude -**/.* -!.env.local -**/*.md -**/*.bak -Dockerfile* +* -!./ +!. +!**/*.ts +!**/*.tsx +!**/*.graphql +!**/*.json +!yarn.lock -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +__generated__/** +**/*.generated.ts -# dependencies -node_modules -.pnp -.pnp.js - -# testing -coverage - -# next.js -.next/ -next-env.d.ts -out/ - -# production -build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# local env files -.env.development.local -.env.test.local - -# vercel -.vercel - -# graphql-codegen -__generated__ -*.generated.ts +.next/** +next-env.d.ts/** +.vercel/** +coverage/** +node_modules/** diff --git a/frontend/.env.local.development.sample b/frontend/.env.local.development.sample deleted file mode 100644 index 03cea398..00000000 --- a/frontend/.env.local.development.sample +++ /dev/null @@ -1,3 +0,0 @@ -NEXT_PUBLIC_DATABASE_URL=https://www.local.solarbuildingenvelopes.com:7501 -NEXT_PUBLIC_METABASE_URL=https://www.local.buildingenvelopedata.org:7001 -NEXT_WEBPACK_USEPOLLING=false diff --git a/frontend/.env.local.production.sample b/frontend/.env.local.production.sample deleted file mode 100644 index 0232bdb6..00000000 --- a/frontend/.env.local.production.sample +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_DATABASE_URL=https://www.solarbuildingenvelopes.com -NEXT_PUBLIC_METABASE_URL=https://www.buildingenvelopedata.org diff --git a/frontend/.env.local.staging.sample b/frontend/.env.local.staging.sample deleted file mode 100644 index 9dc491da..00000000 --- a/frontend/.env.local.staging.sample +++ /dev/null @@ -1,2 +0,0 @@ -NEXT_PUBLIC_DATABASE_URL=https://staging.solarbuildingenvelopes.com -NEXT_PUBLIC_METABASE_URL=https://staging.buildingenvelopedata.org diff --git a/frontend/Dockerfile.development b/frontend/Dockerfile.development index cfd96224..27779b0b 100644 --- a/frontend/Dockerfile.development +++ b/frontend/Dockerfile.development @@ -1,7 +1,5 @@ # syntax=docker/dockerfile:1.21 # check=error=true -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ @@ -49,7 +47,7 @@ ENV PATH=/app/node_modules/.bin:$PATH COPY --chown=node:node ./package.json ./ COPY --chown=node:node ./yarn.lock ./ RUN yarn install \ - --pure-lockfile \ + --frozen-lockfile \ && yarn cache clean # Create empty directories for user `me` and group `us` to make the respective diff --git a/frontend/Dockerfile.production b/frontend/Dockerfile.production index 7f851b83..2e3bcd9f 100644 --- a/frontend/Dockerfile.production +++ b/frontend/Dockerfile.production @@ -1,46 +1,30 @@ # syntax=docker/dockerfile:1.21 # check=error=true -# The first line fixes the Dockerfile frontend used by BuildKit. For details -# see https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://nextjs.org/docs/deployment#docker-image -FROM node:24.11-trixie-slim AS dependencies +FROM node:24.11-trixie-slim AS base + +FROM base AS dependencies SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -RUN apt-get update \ - && apt-get upgrade --assume-yes \ - && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* WORKDIR /app COPY ./package.json ./yarn.lock ./ -RUN yarn install --pure-lockfile +RUN yarn install --frozen-lockfile -FROM node:24.11-trixie-slim AS build +FROM base AS build SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] -RUN apt-get update \ - && apt-get upgrade --assume-yes \ - && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* WORKDIR /app COPY ./ ./ COPY --from=dependencies /app/node_modules ./node_modules RUN yarn build -FROM node:24.11-trixie-slim +FROM base AS runtime SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] WORKDIR /app ENV NODE_ENV=production -COPY --from=build /app/.env.local ./ -COPY --from=build /app/.next ./.next -COPY --from=build /app/next.config.ts ./ -COPY --from=build /app/node_modules ./node_modules -COPY --from=build /app/package.json ./ -COPY --from=build /app/public ./public +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 --ingroup nodejs nextjs RUN apt-get update \ && apt-get upgrade --assume-yes \ && apt-get install \ @@ -51,9 +35,11 @@ RUN apt-get update \ --recursive \ --force \ /var/lib/apt/lists/* +COPY --from=build --chown=nextjs:nodejs /app/.next ./.next +COPY --from=build /app/next.config.ts ./ +COPY --from=build /app/node_modules ./node_modules +COPY --from=build /app/package.json ./ +COPY --from=build /app/public ./public RUN npx --no-install next telemetry disable # hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. -RUN addgroup --system --gid 1001 nodejs \ - && adduser --system --uid 1001 --ingroup nodejs nextjs \ - && chown --recursive nextjs:nodejs /app/.next USER nextjs diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index 55c71f8b..28ab6cc3 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -1,9 +1,6 @@ declare global { namespace NodeJS { interface ProcessEnv { - NEXT_PUBLIC_DATABASE_URL: string; - NEXT_PUBLIC_METABASE_URL: string; - NEXT_WEBPACK_USEPOLLING: string; NODE_ENV: "development" | "production"; } } diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index f09f111f..281c7f53 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -54,7 +54,7 @@ const persistedQueryLink = new PersistedQueryLink({ }); const httpLink = new HttpLink({ - uri: new URL(GraphQlEndpointPath, process.env.NEXT_PUBLIC_DATABASE_URL).href, + uri: GraphQlEndpointPath, useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie headers: { "accept": "application/json" }, diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 11f31308..2f041c81 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -3,10 +3,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { reactStrictMode: true, typedRoutes: true, - allowedDevOrigins: - process.env.NEXT_PUBLIC_DATABASE_URL == null - ? [] - : [new URL(process.env.NEXT_PUBLIC_DATABASE_URL).hostname], + allowedDevOrigins: [], turbopack: { rules: { "/\.(yml|yaml$)/": ["yaml-loader"], diff --git a/frontend/package.json b/frontend/package.json index b0491b42..0383ebf1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,7 @@ "scripts": { "codegen": "graphql-codegen --config ./codegen.ts", "codegen:watch": "graphql-codegen --config ./codegen.ts --watch", - "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_TLS_REJECT_UNAUTHORIZED=0 next dev --port 8080'", + "dev": "yarn refresh-baseline-browser-mapping && concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_TLS_REJECT_UNAUTHORIZED=0 NEXT_WEBPACK_USEPOLLING=false next dev --port 8080'", "build": "yarn refresh-baseline-browser-mapping && yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_TLS_REJECT_UNAUTHORIZED=0 jest", "start": "next start --port 8080", diff --git a/frontend/paths.ts b/frontend/paths.ts index fa296d69..f32b8723 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -1,5 +1,7 @@ import { Route } from "next"; +const metabaseUrl = "https://www.buildingenvelopedata.org" + export default { home: "/" as Route, legalNotice: "/legal-notice" as Route, @@ -31,79 +33,78 @@ export default { uploadFile: "/upload-file" as Route, login: "/connect/login" as Route, logout: "/connect/logout" as Route, - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_DATABASE_URL) - .href as Route, + graphQl: "/graphql/" as Route, metabase: { - home: process.env.NEXT_PUBLIC_METABASE_URL, - components: new URL("/components", process.env.NEXT_PUBLIC_METABASE_URL) + home: metabaseUrl, + components: new URL("/components", metabaseUrl) .href as Route, component(uuid: string) { return new URL( `/components/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, - databases: new URL("/databases", process.env.NEXT_PUBLIC_METABASE_URL) + databases: new URL("/databases", metabaseUrl) .href as Route, database(uuid: string) { return new URL( `/databases/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, - dataFormats: new URL(`/data-formats`, process.env.NEXT_PUBLIC_METABASE_URL) + dataFormats: new URL(`/data-formats`, metabaseUrl) .href as Route, dataFormat(uuid: string) { return new URL( `/data-formats/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, - institutions: new URL("/institutions", process.env.NEXT_PUBLIC_METABASE_URL) + institutions: new URL("/institutions", metabaseUrl) .href as Route, institution(uuid: string) { return new URL( `/institutions/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, - methods: new URL("/methods", process.env.NEXT_PUBLIC_METABASE_URL) + methods: new URL("/methods", metabaseUrl) .href as Route, method(uuid: string) { return new URL( `/methods/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, - users: new URL("/users", process.env.NEXT_PUBLIC_METABASE_URL) + users: new URL("/users", metabaseUrl) .href as Route, user(uuid: string) { return new URL( `/users/${encodeURIComponent(uuid)}`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route; }, allCalorimetricData: new URL( `/data/calorimetric`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route, allGeometricData: new URL( `/data/geometric`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route, allHygrothermalData: new URL( `/data/hygrothermal`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route, allOpticalData: new URL( `/data/optical`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route, allPhotovoltaicData: new URL( `/data/photovoltaic`, - process.env.NEXT_PUBLIC_METABASE_URL, + metabaseUrl, ).href as Route, - graphQl: new URL("/graphql/", process.env.NEXT_PUBLIC_METABASE_URL) + graphQl: new URL("/graphql/", metabaseUrl) .href as Route, }, }; diff --git a/gpg.mk b/gpg.mk index 091a4621..9b0f2e25 100755 --- a/gpg.mk +++ b/gpg.mk @@ -1,4 +1,5 @@ #!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) include ./.env diff --git a/tools.mk b/tools.mk index 8c1c1fb7..e1870627 100755 --- a/tools.mk +++ b/tools.mk @@ -1,4 +1,5 @@ #!/usr/bin/env -S make --file +SELF := $(lastword $(MAKEFILE_LIST)) include ./.env From 80c2ff52a027aa5dfeee05441ebaf05660a7553c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 11:32:29 +0100 Subject: [PATCH 700/878] Make Next.JS produce minimal standalone output for production --- docker-compose.production.yaml | 2 +- frontend/Dockerfile.production | 15 +++++++-------- frontend/next.config.ts | 1 + 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index aa404058..79cb7e01 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -100,7 +100,7 @@ services: condition: "service_started" environment: NODE_ENV: "production" - command: [ "yarn", "start" ] + command: [ "node", "/app/server.js" ] restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] diff --git a/frontend/Dockerfile.production b/frontend/Dockerfile.production index 2e3bcd9f..056def37 100644 --- a/frontend/Dockerfile.production +++ b/frontend/Dockerfile.production @@ -2,7 +2,8 @@ # check=error=true # Available versions are listed on https://hub.docker.com/r/docker/dockerfile -# Inspired by https://nextjs.org/docs/deployment#docker-image +# Inspired by https://github.com/vercel/next.js/blob/canary/examples/with-docker-multi-env/docker/production/Dockerfile +# linked to in https://nextjs.org/docs/deployment FROM node:24.11-trixie-slim AS base @@ -35,11 +36,9 @@ RUN apt-get update \ --recursive \ --force \ /var/lib/apt/lists/* -COPY --from=build --chown=nextjs:nodejs /app/.next ./.next -COPY --from=build /app/next.config.ts ./ -COPY --from=build /app/node_modules ./node_modules -COPY --from=build /app/package.json ./ -COPY --from=build /app/public ./public -RUN npx --no-install next telemetry disable -# hadolint ignore=DL3059 # Multiple consecutive `RUN` instructions. +COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=build --chown=nextjs:nodejs /app/public ./public +COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs +ENV PORT=8080 +ENV HOSTNAME="0.0.0.0" diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 2f041c81..c56a73d7 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + output: 'standalone', // https://nextjs.org/docs/app/api-reference/config/next-config-js/output#automatically-copying-traced-files reactStrictMode: true, typedRoutes: true, allowedDevOrigins: [], From 24e4a063a26a146b831c71ea81cc48f0ed41cc54 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 12:01:18 +0100 Subject: [PATCH 701/878] Allow production build to copy GraphQL schema, images, and stylesheets --- frontend/.dockerignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 8f6486e9..f587dcee 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -3,11 +3,16 @@ * + !. !**/*.ts !**/*.tsx !**/*.graphql +!**/*.graphqls !**/*.json +!**/*.css +!**/*.png +!public/** !yarn.lock __generated__/** From 6c2f4b373db35b8dd81f05454e500880a0c684ef Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 15:29:41 +0100 Subject: [PATCH 702/878] Do not store GnuPG keys in files: In development use a volume (and manage keys in there from within the shell `make bootstrap`) and in production map the host gpg-agent into the backend service (you can then just use ./gpg.mk on the host to manage keys) --- .env.production.sample | 1 + .env.staging.sample | 1 + ...ckerfile.bootstrap => Dockerfile.bootstrap | 41 ++++--- README.md | 101 ++++++------------ backend/Dockerfile.development | 2 + backend/Dockerfile.production | 2 + backend/src/AppSettings.cs | 1 - backend/src/Program.cs | 6 +- backend/src/Services/SigningService.cs | 15 ++- docker-compose.development.yaml | 14 ++- docker-compose.production.yaml | 4 +- docker.mk | 13 +++ gpg.mk | 64 +++++------ 13 files changed, 127 insertions(+), 138 deletions(-) rename backend/Dockerfile.bootstrap => Dockerfile.bootstrap (84%) diff --git a/.env.production.sample b/.env.production.sample index 6ecb54e7..43f0616d 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -23,5 +23,6 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= +GNUPG_AGENT_SOCKET=/run/user/1000/gnupg/S.gpg-agent GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.env.staging.sample b/.env.staging.sample index c426c63a..f1e76049 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -23,5 +23,6 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= +GNUPG_AGENT_SOCKET=/run/user/1000/gnupg/S.gpg-agent GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/backend/Dockerfile.bootstrap b/Dockerfile.bootstrap similarity index 84% rename from backend/Dockerfile.bootstrap rename to Dockerfile.bootstrap index 42439a59..538ffcfb 100644 --- a/backend/Dockerfile.bootstrap +++ b/Dockerfile.bootstrap @@ -2,9 +2,7 @@ # check=error=true # Available versions are listed on https://hub.docker.com/r/docker/dockerfile -# https://hub.docker.com/_/microsoft-dotnet-sdk -# https://mcr.microsoft.com/artifact/mar/dotnet/sdk/tags -FROM mcr.microsoft.com/dotnet/sdk:10.0-noble-amd64 +FROM debian:bookworm SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] ARG GROUP_ID @@ -15,7 +13,7 @@ RUN \ if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi # Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) -# id --user --name ${USER_ID} 2>/dev/null +# id --user --name "${USER_ID}" 2>/dev/null RUN \ apt-get update && \ apt-get install \ @@ -40,9 +38,6 @@ RUN \ --uid "${USER_ID}" \ --ingroup us \ me && \ - apt-get remove \ - --assume-yes \ - adduser && \ rm \ --recursive \ --force \ @@ -63,6 +58,7 @@ RUN \ apt-get install \ --assume-yes \ --no-install-recommends \ + ca-certificates \ curl \ gnupg2 && \ curl \ @@ -94,17 +90,34 @@ RUN \ --force \ /var/lib/apt/lists/* +RUN apt-get update \ + && apt-get install \ + --assume-yes \ + --no-install-recommends \ + make \ + && rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + ENV HOME=/home/me -RUN mkdir --parents "${HOME}" \ +RUN mkdir --parents "${HOME}/app" \ && chown \ me:us \ - "${HOME}" + "${HOME}" \ + && chown \ + me:us \ + "${HOME}/app" \ + && ln --symbolic "${HOME}/app" /app USER me WORKDIR /app -RUN mkdir --parents "${HOME}/.dotnet" - -ENV PATH=$PATH:/home/me/.dotnet/tools -RUN dotnet tool install --global \ - dotnet-script +RUN mkdir --parents \ + "${HOME}/.gnupg" \ + && chmod u=rwx,g=,o= \ + "${HOME}/.gnupg" \ + && echo "no-use-keyboxd" \ + >"${HOME}/.gnupg/common.conf" \ + && echo "allow-loopback-pinentry" \ + >"${HOME}/.gnupg/gpg-agent.conf" diff --git a/README.md b/README.md index e3553aac..6644c86a 100644 --- a/README.md +++ b/README.md @@ -64,13 +64,11 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF `git clone git@github.com:building-envelope-data/database.git` and navigate into the new directory `database` by running `cd ./database`. -1. Initialize, fetch, and checkout possibly-nested submodules by running - `git submodule update --init --recursive`. An alternative would have been - passing `--recurse-submodules` to `git clone` above. - 1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env` - and adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your - `/etc/hosts` file. + and by adjusting variable values to your needs, in particular, by setting + the `GNUPG_AGENT_SOCKET` to the value reported by `gpgconf --list-dirs + agent-socket`. And add the line `127.0.0.1 local.solarbuildingenvelopes.com` + to your `/etc/hosts` file. 1. Prepare your remote controls GNU Make and Docker Compose by running @@ -82,36 +80,15 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF 1. List all GNU Make targets by running `make help`. -1. Generate and export a GnuPG key with the passphrase - `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running - `./gpg.mk key PERSON=${name} COMMENT=${comment} EMAIL=${email}` with your information - filled in, for example, - `./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de`. Then copy the key's fingerprint which - is output by the command and set it as the value of the - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. - - Instead of using the GNU Make target `key`, you may - - 1. install [GnuPG](https://gnupg.org) as described on - [Download GnuPG](https://gnupg.org/download/index.html) or - [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), - 1. generate a GnuPG key by running `gpg --full-generate-key` and answering - the prompts, - 1. list the keys for which you have both a public and secret key by running - `gpg --list-secret-keys --keyid-format=long`, - 1. identify and copy the long form of the fingerprint of the key you have - just created, - 1. remember the key in the variable `fingerprint` by running - `fingerprint=` with `` replaced by the - copied fingerprint, - 1. create the directory to save the secret key to if it does not exist yet - by running `mkdir --parents ./backend/src/gpg-keys`, - 1. export the armored secret key to the file - `./backend/src/gpg-keys/${fingerprint}.gpg` by running - `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, - 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in - the `./.env` file to the remembered fingerprint in your favorite editor. +1. Generate a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file + by running + `make bootstrap COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` + with your information filled in, for example, + `make bootstrap COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. + Then copy the key's fingerprint which is output by the command and set it as + the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the + `./.env` file. 1. Create the PostgreSQL database and schema by running `./database.mk create migrate`. @@ -159,7 +136,7 @@ In another shell After changing the domain model in `./backend/src/data`, you need to migrate the database by dropping into `make shell SERVICE=backend`, adding a migration -with `make migration NAME=${MIGRATION_NAME}`, verifying and if necessary +with `make migration PERSON=${MIGRATION_NAME}`, verifying and if necessary adapting the new migration C# code and SQL scripts, exiting the container with `exit`, and applying the new migration to the PostgreSQL database with `./database.mk migrate`. See @@ -356,10 +333,10 @@ and the pages following it. 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env` - and by adjusting variable values in the copies to your needs, in - particular, by setting passwords to newly generated ones, where random - passwords may be generated by running `openssl rand -base64 32`. Here is - some information on what the variables mean + and by adjusting variable values to your needs, in particular, by setting + passwords to newly generated ones, where random passwords may be + generated by running `openssl rand -base64 32`. Here is some information + on what the variables mean - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; @@ -383,44 +360,26 @@ and the pages following it. database as a client of the metabase acting as identity provider (the client secret is given when registering an OpenId Connect client at the metabase); + - `GNUPG_AGENT_SOCKET` is the value reported by running + `gpgconf --list-dirs agent-socket`. - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` and `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase - of the exported GnuPG secret key for signing in the file - `./backend/src/gpg-keys/${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}.gpg`. + of the GnuPG secret key for signing response and data approvals. 1. Prepare your remote controls GNU Make and Docker Compose by running - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.production.yaml ./docker-compose.yaml`. - 1. Generate and export a GnuPG key with the passphrase - `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file to the - file `./backend/src/gpg-keys/.gpg` by running `./gpg.mk PERSON=${name} COMMENT=${comment} EMAIL=${email} key` with your information - filled in, for example, `make gpg NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de`. Then copy the key's fingerprint which - is output by the command and set it as the value of the - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. - - Instead of using the GNU Make target `gpg`, you may - - 1. install [GnuPG](https://gnupg.org) as described on - [Download GnuPG](https://gnupg.org/download/index.html) or - [GnuPG Package Repositories](https://www.gnupg.org/blog/20250827-new-repository.html), - 1. generate a GnuPG key by running `gpg --full-generate-key` and answering - the prompts, - 1. list the keys for which you have both a public and secret key by running - `gpg --list-secret-keys --keyid-format=long`, - 1. identify and copy the long form of the fingerprint of the key you have - just created, - 1. remember the key in the variable `fingerprint` by running - `fingerprint=` with `` replaced by the - copied fingerprint, - 1. create the directory to save the secret key to if it does not exist yet - by running `mkdir --parents ./backend/src/gpg-keys`, - 1. export the armored secret key to the file - `./backend/src/gpg-keys/${fingerprint}.gpg` by running - `gpg --armor --export-secret-keys ${fingerprint} > ./backend/src/gpg-keys/${fingerprint}.gpg`, - 1. Set the value of the variable `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in - the `./.env` file to the remembered fingerprint in your favorite editor. + 1. Generate a GnuPG key with the passphrase + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file + by running + `./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}" key` + with your information filled in, for example, + `./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"`. + Then copy the key's fingerprint which is output by the command and set it + as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in + the `./.env` file. 1. Create the PostgreSQL database by running `./database.mk create`. diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index 7714fef1..02a43faa 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -236,6 +236,8 @@ RUN mkdir --parents \ "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ "${HOME}/.gnupg" \ + && echo "no-use-keyboxd" \ + >"${HOME}/.gnupg/common.conf" \ && echo "allow-loopback-pinentry" \ >"${HOME}/.gnupg/gpg-agent.conf" diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index c493bfb1..8737d9b6 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -123,6 +123,8 @@ RUN mkdir --parents \ /root/.gnupg \ && chmod u=rwx,g=,o= \ /root/.gnupg \ + && echo "no-use-keyboxd" \ + >/root/.gnupg/common.conf \ && echo "allow-loopback-pinentry" \ >/root/.gnupg/gpg-agent.conf diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 7d5859d2..48eef9ae 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -39,7 +39,6 @@ public sealed record GnupgSecretSigningKeySettings { // Keep file name {FINGERPRINT}.gpg in sync with the one in the GNU // Make target `gpg` in the `Makefile` - public string FileName => $"{Fingerprint}.gpg"; public string Passphrase { get; init; } = ""; public string Fingerprint { get; init; } = ""; }; diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 2054904a..85764a8e 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -90,7 +90,7 @@ string[] commandLineArguments await CreateDatabase(scope.ServiceProvider); } await SeedDatabase(scope.ServiceProvider); - await InitializeSigningService(scope.ServiceProvider); + await AssertGnuPgKeyExistence(scope.ServiceProvider); } await application.RunAsync(); @@ -203,13 +203,13 @@ IServiceProvider services } } - private static async Task InitializeSigningService( + private static async Task AssertGnuPgKeyExistence( IServiceProvider services ) { await services .GetRequiredService() - .Initialize(); + .AssertGnuPgKeyExistence(); } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 2f64272a..299411a2 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -64,14 +64,14 @@ public sealed class SigningService( { public static readonly Uri KeyServerUrl = new("hkps://keys.openpgp.org/", UriKind.Absolute); - public async Task Initialize() + public async Task AssertGnuPgKeyExistence() { var (success, output, diagnostics) = await ExecuteCommand( - $"gpg --batch --passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' --import './gpg-keys/{appSettings.GnupgSecretSigningKey.FileName}'" + $"gpg --batch --with-colons --list-keys | grep '{appSettings.GnupgSecretSigningKey.Fingerprint}'" ); if (!success) { - throw new InvalidOperationException($"Failed to import GnuPG secret key for signing with passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' and file name '{appSettings.GnupgSecretSigningKey.FileName}'. The command gave the standard output '{output}' and the diagnostic information '{diagnostics}'."); + throw new InvalidOperationException($"There is no GnuPG key with the fingerprint '{appSettings.GnupgSecretSigningKey.Fingerprint}'. The command gave the standard output '{output}' and the diagnostic information '{diagnostics}'."); } } @@ -140,12 +140,17 @@ private async Task ReceiveKey(string fingerprint) ) { logger.ExecuteCommand(command); - var escapedCommand = command.Replace("\"", "\\\""); var process = Process.Start( new ProcessStartInfo() { FileName = "bash", - Arguments = $"-c \"{escapedCommand}\"", + ArgumentList = { + "-o", "errexit", + "-o", "errtrace", + "-o", "nounset", + "-o", "pipefail", + "-c", command + }, ErrorDialog = false, UseShellExecute = false, CreateNoWindow = true, diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 11d10fee..27ac1f94 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -47,8 +47,8 @@ services: - files:/app/src/files - backend_vscode_server:/home/me/.vscode-server - backend_vscode_server_insiders:/home/me/.vscode-server-insiders - - gnupg:/home/me/.gnupg - certificates:/home/me/.dotnet/corefx/cryptography/x509stores + - gnupg:/home/me/.gnupg # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -185,10 +185,20 @@ services: labels: autoheal: "true" + bootstrap: + build: + context: "." + dockerfile: "./Dockerfile.bootstrap" + volumes: + - .:/app + - gnupg:/home/me/.gnupg + profiles: + - "manual" + volumes: data: files: - gnupg: certificates: + gnupg: backend_vscode_server: backend_vscode_server_insiders: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 79cb7e01..05a01546 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -43,9 +43,10 @@ services: volumes: - backend_logs:/app/logs - files:/app/files - - gnupg:/root/.gnupg # persist certificates in the [`X509Store`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store) - certificates:/root/.dotnet/corefx/cryptography/x509stores + - /home/cloud/.gnupg:/home/me/.gnupg + - ${GNUPG_AGENT_SOCKET}:/home/me/.gnupg/S.gpg-agent environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" @@ -138,6 +139,5 @@ services: volumes: data: files: - gnupg: certificates: backend_logs: diff --git a/docker.mk b/docker.mk index e2295599..8f27c92c 100755 --- a/docker.mk +++ b/docker.mk @@ -159,3 +159,16 @@ list-services : ## List all services specified in the docker-compose file (used docker compose config \ --services .PHONY : list-services + +bootstrap : COMMAND = bash +bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a shell within, for example, `make bootstrap COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` or simply `make bootstrap` + docker compose pull \ + bootstrap + docker compose build \ + --build-arg GROUP_ID=$(shell id --group) \ + --build-arg USER_ID=$(shell id --user) \ + bootstrap + docker compose run \ + --rm \ + bootstrap \ + ${COMMAND} diff --git a/gpg.mk b/gpg.mk index 9b0f2e25..4e57c489 100755 --- a/gpg.mk +++ b/gpg.mk @@ -15,45 +15,29 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -# Keep file name .gpg in sync with the one in `AppSettings.cs` +socket : ## Print socket path + gpgconf --list-dirs agent-socket +.PHONY : socket + key : COMMENT = key : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make gpg PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` - docker compose pull \ - bootstrap - docker compose build \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ - bootstrap - docker compose run \ - --rm \ - bootstrap \ - bash -ceuxo pipefail " \ - gpg \ - --quick-generate-key \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - ed25519 \ - sign \ - never && \ - fingerprint=\$$(gpg \ - --list-secret-keys \ - --with-colons \ - --keyid-format=long \ - ${EMAIL} \ - | grep \ - --before=3 \ - '${PERSON} (${COMMENT}) <${EMAIL}>' \ - | awk -F: '\$$1==\"fpr\" {printf \$$10; exit}') && \ - echo fingerprint=\$${fingerprint} && \ - mkdir --parents \ - ./backend/src/gpg-keys && \ - gpg \ - --batch \ - --pinentry-mode loopback \ - --passphrase ${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE} \ - --armor \ - --export-secret-keys \$${fingerprint} \ - > /app/src/gpg-keys/\$${fingerprint}.gpg \ - " + gpg \ + --quick-generate-key \ + --batch \ + --pinentry-mode loopback \ + --passphrase "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}" \ + "${PERSON} (${COMMENT}) <${EMAIL}>" \ + ed25519 \ + sign \ + never + fingerprint=$$(gpg \ + --list-secret-keys \ + --with-colons \ + --keyid-format=long \ + ${EMAIL} \ + | grep \ + --before=3 \ + "${PERSON} (${COMMENT}) <${EMAIL}>" \ + | awk -F: '$$1=="fpr" {printf $$10; exit}') && \ + echo fingerprint=$${fingerprint} +.PHONY : key From b3334635dd1d1b244772f147ab31006144b75988 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 15:54:56 +0100 Subject: [PATCH 703/878] Fix building production images locally --- Dockerfile.bootstrap | 2 +- backend/.dockerignore | 1 + backend/Dockerfile.development | 2 +- backend/Dockerfile.production | 2 +- forge.mk | 2 +- frontend/.dockerignore | 1 - 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile.bootstrap b/Dockerfile.bootstrap index 538ffcfb..114a1873 100644 --- a/Dockerfile.bootstrap +++ b/Dockerfile.bootstrap @@ -114,7 +114,7 @@ USER me WORKDIR /app RUN mkdir --parents \ - "${HOME}/.gnupg" \ + "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ "${HOME}/.gnupg" \ && echo "no-use-keyboxd" \ diff --git a/backend/.dockerignore b/backend/.dockerignore index 893fad98..9187b428 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -3,6 +3,7 @@ * +!. !**/*.cs !**/*.cshtml !**/*.csproj diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index 02a43faa..8b1471a5 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -177,7 +177,7 @@ WORKDIR /app # * https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools#install-a-local-tool COPY --chown=me:us ./dotnet-tools.json ./ RUN dotnet tool restore \ - --tool-manifest ./dotnet-tools.json + --tool-manifest ./dotnet-tools.json RUN mkdir ./src \ && mkdir ./test diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index 8737d9b6..7875b59b 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -118,7 +118,7 @@ WORKDIR /app COPY --from=build /app/src/bin/Release/net10.0/publish ./ RUN mkdir --parents \ - /root/.dotnet/corefx/cryptography/x509stores \ + /root/.dotnet/corefx/cryptography/x509stores \ && mkdir --parents \ /root/.gnupg \ && chmod u=rwx,g=,o= \ diff --git a/forge.mk b/forge.mk index 98cf5034..0bd3d3a2 100755 --- a/forge.mk +++ b/forge.mk @@ -5,7 +5,7 @@ SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c MAKEFLAGS += --warn-undefined-variables -NAME = metabase +NAME = database TARGET = $(shell git rev-parse --verify HEAD) # Taken from https://www.client9.com/self-documenting-makefiles/ diff --git a/frontend/.dockerignore b/frontend/.dockerignore index f587dcee..662452b7 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -3,7 +3,6 @@ * - !. !**/*.ts !**/*.tsx From 0d9f47da8ef4ac16d659f1b47543141499cce917 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 16:10:40 +0100 Subject: [PATCH 704/878] Add missing files archive name --- database.mk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/database.mk b/database.mk index d90debc5..a05bfc96 100755 --- a/database.mk +++ b/database.mk @@ -10,6 +10,7 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true dump_archive_name = postgresql_dumpall.gz +files_archive_name = files.gz # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -128,7 +129,7 @@ backup : ## Backup database and related data to directory with absolute path `${ database while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done docker exec \ - ${CONTAINER_NAME} \ + ${DATABASE_CONTAINER_NAME} \ pg_dump \ --clean \ --if-exists \ @@ -152,7 +153,7 @@ backup : ## Backup database and related data to directory with absolute path `${ --verbose \ --create \ --gzip \ - --file=/backup/${files_archive_name} \ + --file="/backup/${files_archive_name}" \ --directory=/app \ ./files docker container stop ${FILES_CONTAINER_NAME} @@ -222,7 +223,7 @@ restore : ## Restore database and related data from directory with absolute path --extract \ --gunzip \ --strip-components=2 \ - --file=/backup/${files_archive_name} \ + --file='/backup/${files_archive_name}' \ " docker container stop ${FILES_CONTAINER_NAME} docker container rm --volumes ${FILES_CONTAINER_NAME} From 077bdeb0f6fa137da6d0d5f44683cb5070676af4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Feb 2026 16:57:41 +0100 Subject: [PATCH 705/878] Correct GnuPG mount points --- backend/Dockerfile.development | 2 +- backend/Dockerfile.production | 6 +----- docker-compose.production.yaml | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index 8b1471a5..ca1084d0 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -236,7 +236,7 @@ RUN mkdir --parents \ "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ "${HOME}/.gnupg" \ - && echo "no-use-keyboxd" \ + && echo "# use-keyboxd" \ >"${HOME}/.gnupg/common.conf" \ && echo "allow-loopback-pinentry" \ >"${HOME}/.gnupg/gpg-agent.conf" diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index 7875b59b..b49e5d1b 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -122,11 +122,7 @@ RUN mkdir --parents \ && mkdir --parents \ /root/.gnupg \ && chmod u=rwx,g=,o= \ - /root/.gnupg \ - && echo "no-use-keyboxd" \ - >/root/.gnupg/common.conf \ - && echo "allow-loopback-pinentry" \ - >/root/.gnupg/gpg-agent.conf + /root/.gnupg RUN ln --symbolic ./Database.dll ./App.dll diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 05a01546..b3ea663e 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -45,8 +45,8 @@ services: - files:/app/files # persist certificates in the [`X509Store`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store) - certificates:/root/.dotnet/corefx/cryptography/x509stores - - /home/cloud/.gnupg:/home/me/.gnupg - - ${GNUPG_AGENT_SOCKET}:/home/me/.gnupg/S.gpg-agent + - /home/cloud/.gnupg:/root/.gnupg + - ${GNUPG_AGENT_SOCKET}:/root/.gnupg/S.gpg-agent environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" From 5dfee8d74ede948fb5c5811874814bf63c253d02 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Feb 2026 09:09:51 +0100 Subject: [PATCH 706/878] Do not use GnuPG agent of host machine in production (GnuPG versions could be different) --- .env.production.sample | 1 - .env.staging.sample | 1 - Dockerfile.bootstrap | 9 ++------- README.md | 18 ++++++++---------- backend/Dockerfile.development | 9 ++------- backend/Dockerfile.production | 9 ++------- docker-compose.development.yaml | 4 ++++ docker-compose.production.yaml | 5 +++-- gnupg.conf | 1 + gpg-agent.conf | 1 + 10 files changed, 23 insertions(+), 35 deletions(-) create mode 100644 gnupg.conf create mode 100644 gpg-agent.conf diff --git a/.env.production.sample b/.env.production.sample index 43f0616d..6ecb54e7 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -23,6 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_AGENT_SOCKET=/run/user/1000/gnupg/S.gpg-agent GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.env.staging.sample b/.env.staging.sample index f1e76049..c426c63a 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -23,6 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_AGENT_SOCKET=/run/user/1000/gnupg/S.gpg-agent GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/Dockerfile.bootstrap b/Dockerfile.bootstrap index 114a1873..9c0ce1c2 100644 --- a/Dockerfile.bootstrap +++ b/Dockerfile.bootstrap @@ -44,12 +44,7 @@ RUN \ /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions and the signing-key fingerprint see -# https://repos.gnupg.org/deb/gnupg/noble/ -# Keep the version in sync with the one in the other Dockerfiles of this -# repository and with the one of the `machine` repository specified in the -# `local.yaml` Ansible script. -ENV GNUPG_VERSION=2.5.17-1 +# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. RUN \ @@ -84,7 +79,7 @@ RUN \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2="${GNUPG_VERSION}" && \ + gnupg2 && \ rm \ --recursive \ --force \ diff --git a/README.md b/README.md index 6644c86a..c21f8a11 100644 --- a/README.md +++ b/README.md @@ -65,10 +65,9 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF into the new directory `database` by running `cd ./database`. 1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env` - and by adjusting variable values to your needs, in particular, by setting - the `GNUPG_AGENT_SOCKET` to the value reported by `gpgconf --list-dirs - agent-socket`. And add the line `127.0.0.1 local.solarbuildingenvelopes.com` - to your `/etc/hosts` file. + and by adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your + `/etc/hosts` file. The value of `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` will + be set later. 1. Prepare your remote controls GNU Make and Docker Compose by running @@ -335,8 +334,9 @@ and the pages following it. `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env` and by adjusting variable values to your needs, in particular, by setting passwords to newly generated ones, where random passwords may be - generated by running `openssl rand -base64 32`. Here is some information - on what the variables mean + generated by running `openssl rand -base64 32`. The value of + `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` will be set later. Here is some + information on what the variables mean - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; @@ -360,8 +360,6 @@ and the pages following it. database as a client of the metabase acting as identity provider (the client secret is given when registering an OpenId Connect client at the metabase); - - `GNUPG_AGENT_SOCKET` is the value reported by running - `gpgconf --list-dirs agent-socket`. - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` and `GNUPG_SECRET_SIGNING_KEY_PASSPHRASE` are fingerprint and passphrase of the GnuPG secret key for signing response and data approvals. @@ -374,9 +372,9 @@ and the pages following it. 1. Generate a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file by running - `./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}" key` + `make bootstrap COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` with your information filled in, for example, - `./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"`. + `make bootstrap COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index ca1084d0..e6a3c6e5 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -104,12 +104,7 @@ RUN apt-get update \ /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions and the signing-key fingerprint see -# https://repos.gnupg.org/deb/gnupg/noble/ -# Keep the version in sync with the one in the other Dockerfiles of this -# repository and with the one of the `machine` repository specified in the -# `local.yaml` Ansible script. -ENV GNUPG_VERSION=2.5.17-1 +# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. RUN \ @@ -143,7 +138,7 @@ RUN \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2="${GNUPG_VERSION}" && \ + gnupg2 && \ rm \ --recursive \ --force \ diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index b49e5d1b..97e8c326 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -68,12 +68,7 @@ RUN apt-get update \ /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. -# For available versions and the signing-key fingerprint see -# https://repos.gnupg.org/deb/gnupg/noble/ -# Keep the version in sync with the one in the other Dockerfiles of this -# repository and with the one of the `machine` repository specified in the -# `local.yaml` Ansible script. -ENV GNUPG_VERSION=2.5.17-1 +# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 # hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. RUN \ @@ -107,7 +102,7 @@ RUN \ --assume-yes \ --no-install-recommends \ dirmngr \ - gnupg2="${GNUPG_VERSION}" && \ + gnupg2 && \ rm \ --recursive \ --force \ diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 27ac1f94..5c5c9074 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -49,6 +49,8 @@ services: - backend_vscode_server_insiders:/home/me/.vscode-server-insiders - certificates:/home/me/.dotnet/corefx/cryptography/x509stores - gnupg:/home/me/.gnupg + - ./gnupg.conf:/home/me/.gnupg/common.conf + - ./gpg-agent.conf:/home/me/.gnupg/gpg-agent.conf # The following items make the engine create anonymous volumes for the # specified path with the effect that the service does not write # temporary files into the corresponding directory on the host file @@ -192,6 +194,8 @@ services: volumes: - .:/app - gnupg:/home/me/.gnupg + - ./gnupg.conf:/home/me/.gnupg/common.conf + - ./gpg-agent.conf:/home/me/.gnupg/gpg-agent.conf profiles: - "manual" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index b3ea663e..8e1b0e60 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -45,8 +45,9 @@ services: - files:/app/files # persist certificates in the [`X509Store`](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509store) - certificates:/root/.dotnet/corefx/cryptography/x509stores - - /home/cloud/.gnupg:/root/.gnupg - - ${GNUPG_AGENT_SOCKET}:/root/.gnupg/S.gpg-agent + - gnupg:/root/.gnupg + - ./gnupg.conf:/root/.gnupg/common.conf + - ./gpg-agent.conf:/root/.gnupg/gpg-agent.conf environment: # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables ASPNETCORE_URLS: "http://0.0.0.0:8080" diff --git a/gnupg.conf b/gnupg.conf new file mode 100644 index 00000000..f054b759 --- /dev/null +++ b/gnupg.conf @@ -0,0 +1 @@ +# use-keyboxd diff --git a/gpg-agent.conf b/gpg-agent.conf new file mode 100644 index 00000000..d1b6ae31 --- /dev/null +++ b/gpg-agent.conf @@ -0,0 +1 @@ +allow-loopback-pinentry From bca7b43bca21cf3237b244d4798842acd0093539 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Feb 2026 10:04:45 +0100 Subject: [PATCH 707/878] Load antiforgery token before rendering the page --- frontend/components/Layout.tsx | 11 ----------- frontend/components/NavBar.tsx | 4 ++-- frontend/lib/apollo.ts | 8 ++++---- frontend/pages/_app.tsx | 26 +++++++++++++++++++++++--- 4 files changed, 29 insertions(+), 20 deletions(-) diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index cc2917be..82b8f22c 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -50,7 +50,6 @@ export default function Layout({ children }: LayoutProps) { const [cookies, setCookie] = useCookies([cookieConsentName]); const shouldShowCookieConsent = cookies[cookieConsentName] != cookieConsentValue; - const [loadedAntiforgeryToken, setLoadedAntiforgeryToken] = useState(false); useEffect(() => { if (shouldShowCookieConsent) { @@ -71,16 +70,6 @@ export default function Layout({ children }: LayoutProps) { } }, [shouldShowCookieConsent, setCookie]); - useEffect(() => { - fetch(paths.antiforgeryToken).then((_) => { - setLoadedAntiforgeryToken(true); - }); - }, []); - - if (!loadedAntiforgeryToken) { - return null; - } - return ( diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index b3516e3f..5fcdba78 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -5,7 +5,7 @@ import { Button, Menu } from "antd"; import { UserOutlined } from "@ant-design/icons"; import paths from "../paths"; import { CurrentUserDocument } from "../queries/currentUser.generated"; -import { extractXsrfTokenFromCookie } from "../lib/apollo"; +import { extractAntiforgeryTokenFromCookie } from "../lib/apollo"; import { Route } from "next"; type NavItemProps = { @@ -65,7 +65,7 @@ export default function NavBar({ items }: NavBarProps) { type="hidden" value={ typeof window !== "undefined" - ? (extractXsrfTokenFromCookie() ?? "") + ? (extractAntiforgeryTokenFromCookie() ?? "") : "" } /> diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 281c7f53..83dcd147 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -30,13 +30,13 @@ function extractCookie(name: string, cookies: string): string | null { ); } -export function extractXsrfTokenFromCookie(): string | null { +export function extractAntiforgeryTokenFromCookie(): string | null { return extractCookie(AntiforgeryCookieKey, document.cookie); } // inspired by https://www.apollographql.com/docs/react/networking/authentication#header -const xsrfLink = new SetContextLink(({ headers }) => { - const antiforgeryToken = extractXsrfTokenFromCookie(); +const antiforgeryLink = new SetContextLink(({ headers }) => { + const antiforgeryToken = extractAntiforgeryTokenFromCookie(); if (antiforgeryToken) { return { headers: { @@ -60,7 +60,7 @@ const httpLink = new HttpLink({ headers: { "accept": "application/json" }, }); -const apolloLink = xsrfLink.concat(persistedQueryLink).concat(httpLink); +const apolloLink = antiforgeryLink.concat(persistedQueryLink).concat(httpLink); export const apolloClient = new ApolloClient({ ssrMode: false, diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index d254cc0a..1a28e7b4 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -3,16 +3,36 @@ import { apolloClient } from "../lib/apollo"; import { ApolloProvider } from "@apollo/client/react"; import { CookiesProvider } from "react-cookie"; import { App, ConfigProvider } from "antd"; +import { ReactNode, useEffect, useState } from 'react'; +import paths from "../paths"; import "../styles/global.css"; +function AntiforgeryProvider({ children }: { children: ReactNode }) { + const [loaded, setLoaded] = useState(false); + + useEffect(() => { + fetch(paths.antiforgeryToken).then((_) => { + setLoaded(true); + }); + }, []); + + if (!loaded) { + return null; + } + + return children; +} + export default function NextApp({ Component, pageProps }: AppProps) { return ( - - - + + + + + From 93df6e6a83b0922e88c0c03f2b6fb2fa772a3ed2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Feb 2026 13:58:39 +0100 Subject: [PATCH 708/878] Remove unused forking connection --- backend/src/GraphQl/ForkingConnection.cs | 71 ------------------------ 1 file changed, 71 deletions(-) delete mode 100644 backend/src/GraphQl/ForkingConnection.cs diff --git a/backend/src/GraphQl/ForkingConnection.cs b/backend/src/GraphQl/ForkingConnection.cs deleted file mode 100644 index 4007eee7..00000000 --- a/backend/src/GraphQl/ForkingConnection.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using GreenDonut; -using GreenDonut.Data; - -namespace Database.GraphQl; - -public abstract class ForkingConnection( - TSubject subject, - bool useFirstDataLoader, - Func createEdge, - QueryContext? queryContext - ) - where TSubject : IEntity - where TSomeAssociationsByAssociateIdDataLoader : IDataLoader - where TOtherAssociationsByAssociateIdDataLoader : IDataLoader -{ - private readonly Func _createEdge = createEdge; - private readonly QueryContext? _queryContext = queryContext; - private readonly bool _useFirstDataLoader = useFirstDataLoader; - - protected TSubject Subject { get; } = subject; - - public Task GetTotalCountAsync( - TSomeAssociationsByAssociateIdDataLoader someDataLoader, - TOtherAssociationsByAssociateIdDataLoader otherDataLoader, - CancellationToken cancellationToken - ) - { - return _useFirstDataLoader - ? GetTotalCountAsync(someDataLoader, cancellationToken) - : GetTotalCountAsync(otherDataLoader, cancellationToken); - } - - public IAsyncEnumerable GetEdgesAsync( - TSomeAssociationsByAssociateIdDataLoader someDataLoader, - TOtherAssociationsByAssociateIdDataLoader otherDataLoader, - CancellationToken cancellationToken - ) - { - return _useFirstDataLoader - ? GetEdgesAsync(someDataLoader, cancellationToken) - : GetEdgesAsync(otherDataLoader, cancellationToken); - } - - private async Task GetTotalCountAsync( - TDataLoader dataLoader, - CancellationToken cancellationToken - ) - where TDataLoader : IDataLoader - { - return (uint)(await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)).Length; - } - - private async IAsyncEnumerable GetEdgesAsync( - TDataLoader dataLoader, - [EnumeratorCancellation] CancellationToken cancellationToken - ) - where TDataLoader : IDataLoader - { - foreach (var association in await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)) - { - yield return _createEdge(association); - } - } -} \ No newline at end of file From 558cae3bca25516ec2d5283298f67ea7070a21a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Feb 2026 16:01:39 +0100 Subject: [PATCH 709/878] Use `modal` from `useApp` --- frontend/components/Layout.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 82b8f22c..4975055c 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -2,7 +2,7 @@ import Head from "next/head"; import { ReactNode, useEffect, useState } from "react"; import Footer from "./Footer"; import NavBar from "./NavBar"; -import { Modal, Layout as AntLayout, Typography } from "antd"; +import { App, Layout as AntLayout, Typography } from "antd"; import paths from "../paths"; import { useCookies } from "react-cookie"; @@ -50,10 +50,11 @@ export default function Layout({ children }: LayoutProps) { const [cookies, setCookie] = useCookies([cookieConsentName]); const shouldShowCookieConsent = cookies[cookieConsentName] != cookieConsentValue; + const { modal } = App.useApp(); useEffect(() => { if (shouldShowCookieConsent) { - Modal.info({ + modal.info({ title: "Cookie Consent", content: ( From 05e0ba0fbe465ed30dde6b768dc120a16072cce8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Feb 2026 16:06:54 +0100 Subject: [PATCH 710/878] Reduce error information shown to user --- frontend/lib/apollo.ts | 45 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 83dcd147..8fe43051 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -74,29 +74,28 @@ export const apolloClient = new ApolloClient({ // See https://www.apollographql.com/docs/react/data/error-handling export function stringifyApolloError(error: ErrorLike) { - if (CombinedGraphQLErrors.is(error)) { - return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions - }); Stack(${error.stack}); GraphQL Errors(${error.errors - .map( - (e) => - `[Message(${e.message}); Path(${e.path?.join( - ".", - )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions - })]`, - ) - .join(", ")})`; - // } if (CombinedProtocolErrors.is(error)) { - // Handle multipart subscription protocol errors - // } if (LocalStateError.is(error)) { - // Handle errors thrown by the `LocalState` class - // } if (ServerError.is(error)) { - // Handle server HTTP errors - // } if (ServerParseError.is(error)) { - // Handle JSON parse errors - // } if (UnconventionalError.is(error)) { - // Handle errors thrown by irregular types - } - return `Name(${error.name}); Message(${error.message}); Stack(${error.stack})`; + // if (CombinedGraphQLErrors.is(error)) { + // return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions + // }); Stack(${error.stack}); GraphQL Errors(${error.errors + // .map( + // (e) => + // `[Message(${e.message}); Path(${e.path?.join( + // ".", + // )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions + // })]`, + // ) + // .join(", ")})`; + // } if (CombinedProtocolErrors.is(error)) { + // Handle multipart subscription protocol errors + // } if (LocalStateError.is(error)) { + // Handle errors thrown by the `LocalState` class + // } if (ServerError.is(error)) { + // Handle server HTTP errors + // } if (ServerParseError.is(error)) { + // Handle JSON parse errors + // } if (UnconventionalError.is(error)) { + // Handle errors thrown by irregular types + return `${error.name}: ${error.message}`; } /* From 178e0171599fd654f73d2df2276f3d1d742861d5 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 08:41:26 +0100 Subject: [PATCH 711/878] Use " around bash variables and use bash strict mode --- database.mk | 120 ++++++++++++++++---------------- docker-compose.development.yaml | 18 ++--- docker-compose.production.yaml | 2 +- 3 files changed, 70 insertions(+), 70 deletions(-) diff --git a/database.mk b/database.mk index a05bfc96..4481a6ba 100755 --- a/database.mk +++ b/database.mk @@ -41,38 +41,38 @@ remove-volume : ## Remove data and files volumes create : CONTAINER_NAME = create_${NAME}_database create : ## Create database with name `${POSTGRES_DATABASE_NAME}` - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} + -docker container stop "${CONTAINER_NAME}" + -docker container rm --volumes "${CONTAINER_NAME}" docker compose run \ - --name ${CONTAINER_NAME} \ + --name "${CONTAINER_NAME}" \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done docker exec \ - ${CONTAINER_NAME} \ + "${CONTAINER_NAME}" \ createdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} + docker container stop "${CONTAINER_NAME}" + docker container rm --volumes "${CONTAINER_NAME}" .PHONY : create drop : CONTAINER_NAME = drop_${NAME}_database drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} + -docker container stop "${CONTAINER_NAME}" + -docker container rm --volumes "${CONTAINER_NAME}" docker compose run \ - --name ${CONTAINER_NAME} \ + --name "${CONTAINER_NAME}" \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done docker exec \ - ${CONTAINER_NAME} \ + "${CONTAINER_NAME}" \ dropdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} + docker container stop "${CONTAINER_NAME}" + docker container rm --volumes "${CONTAINER_NAME}" .PHONY : drop sql : CONTAINER_NAME = sql_${NAME}_database @@ -80,17 +80,17 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for docker compose down \ --remove-orphans \ database - -docker container stop ${CONTAINER_NAME} - -docker container rm --volumes ${CONTAINER_NAME} + -docker container stop "${CONTAINER_NAME}" + -docker container rm --volumes "${CONTAINER_NAME}" docker compose run \ - --name ${CONTAINER_NAME} \ + --name "${CONTAINER_NAME}" \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} ${CONTAINER_NAME}) != "healthy" ]; do sleep 1; done - cat ${SCRIPT} \ + while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done + cat "${SCRIPT}" \ | docker exec \ --interactive \ - ${CONTAINER_NAME} \ + "${CONTAINER_NAME}" \ psql \ --echo-all \ --no-psqlrc \ @@ -98,8 +98,8 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for --file=- \ --username="${POSTGRES_USER}" \ --dbname="${POSTGRES_DATABASE_NAME}" - docker container stop ${CONTAINER_NAME} - docker container rm --volumes ${CONTAINER_NAME} + docker container stop "${CONTAINER_NAME}" + docker container rm --volumes "${CONTAINER_NAME}" docker compose up \ --remove-orphans \ --wait \ @@ -117,37 +117,37 @@ migrate : sql ## Migrate database by running the idempotent SQL script ./backen backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database backup : FILES_CONTAINER_NAME = backup_${NAME}_files backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` - mkdir --parents ${DIR} + mkdir --parents "${DIR}" docker compose down \ --remove-orphans \ database - -docker container stop ${DATABASE_CONTAINER_NAME} - -docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop "${DATABASE_CONTAINER_NAME}" + -docker container rm --volumes "${DATABASE_CONTAINER_NAME}" docker compose run \ - --name ${DATABASE_CONTAINER_NAME} \ + --name "${DATABASE_CONTAINER_NAME}" \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + while [[ "$$(docker inspect -f {{.State.Health.Status}} '${DATABASE_CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done docker exec \ - ${DATABASE_CONTAINER_NAME} \ + "${DATABASE_CONTAINER_NAME}" \ pg_dump \ --clean \ --if-exists \ --username="${POSTGRES_USER}" \ | gzip \ - > ${DIR}/${dump_archive_name} - docker container stop ${DATABASE_CONTAINER_NAME} - docker container rm --volumes ${DATABASE_CONTAINER_NAME} - -docker container stop ${FILES_CONTAINER_NAME} - -docker container rm --volumes ${FILES_CONTAINER_NAME} + > "${DIR}/${dump_archive_name}" + docker container stop "${DATABASE_CONTAINER_NAME}" + docker container rm --volumes "${DATABASE_CONTAINER_NAME}" + -docker container stop "${FILES_CONTAINER_NAME}" + -docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose run \ - --name ${FILES_CONTAINER_NAME} \ + --name "${FILES_CONTAINER_NAME}" \ --detach \ backend docker run \ --rm \ - --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${DIR}:/backup \ + --volumes-from "${FILES_CONTAINER_NAME}" \ + --volume "${DIR}":/backup \ debian:bullseye-slim \ tar \ --verbose \ @@ -156,8 +156,8 @@ backup : ## Backup database and related data to directory with absolute path `${ --file="/backup/${files_archive_name}" \ --directory=/app \ ./files - docker container stop ${FILES_CONTAINER_NAME} - docker container rm --volumes ${FILES_CONTAINER_NAME} + docker container stop "${FILES_CONTAINER_NAME}" + docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose up \ --remove-orphans \ --wait \ @@ -170,27 +170,27 @@ restore : ## Restore database and related data from directory with absolute path docker compose down \ --remove-orphans \ database - -docker container stop ${DATABASE_CONTAINER_NAME} - -docker container rm --volumes ${DATABASE_CONTAINER_NAME} + -docker container stop "${DATABASE_CONTAINER_NAME}" + -docker container rm --volumes "${DATABASE_CONTAINER_NAME}" docker compose run \ - --name ${DATABASE_CONTAINER_NAME} \ + --name "${DATABASE_CONTAINER_NAME}" \ --detach \ database - while [ $$(docker inspect -f {{.State.Health.Status}} ${DATABASE_CONTAINER_NAME}) != "healthy" ]; do sleep 1; done + while [[ "$$(docker inspect -f {{.State.Health.Status}} '${DATABASE_CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done -docker exec \ - ${CONTAINER_NAME} \ + "${CONTAINER_NAME}" \ dropdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" docker exec \ - ${CONTAINER_NAME} \ + "${CONTAINER_NAME}" \ createdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - gunzip --stdout ${DIR}/${dump_archive_name} \ + gunzip --stdout "${DIR}/${dump_archive_name}" \ | docker exec \ --interactive \ - ${DATABASE_CONTAINER_NAME} \ + "${DATABASE_CONTAINER_NAME}" \ psql \ --echo-all \ --no-psqlrc \ @@ -198,35 +198,35 @@ restore : ## Restore database and related data from directory with absolute path --file=- \ --username="${POSTGRES_USER}" \ --dbname="${POSTGRES_DATABASE_NAME}" - docker container stop ${DATABASE_CONTAINER_NAME} - docker container rm --volumes ${DATABASE_CONTAINER_NAME} - -docker container stop ${FILES_CONTAINER_NAME} - -docker container rm --volumes ${FILES_CONTAINER_NAME} + docker container stop "${DATABASE_CONTAINER_NAME}" + docker container rm --volumes "${DATABASE_CONTAINER_NAME}" + -docker container stop "${FILES_CONTAINER_NAME}" + -docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose run \ - --name ${FILES_CONTAINER_NAME} \ + --name "${FILES_CONTAINER_NAME}" \ --detach \ backend docker run \ --rm \ - --volumes-from ${FILES_CONTAINER_NAME} \ - --volume ${DIR}:/backup \ + --volumes-from "${FILES_CONTAINER_NAME}" \ + --volume "${DIR}":/backup \ debian:bullseye-slim \ - bash -cx " \ - cd /app/files && \ - rm \ + bash -o errexit -o errtrace -o nounset -o pipefail -c " \ + cd /app/files \ + && rm \ --recursive \ --force \ --dir \ - * && \ - tar \ + * \ + && tar \ --verbose \ --extract \ --gunzip \ --strip-components=2 \ --file='/backup/${files_archive_name}' \ " - docker container stop ${FILES_CONTAINER_NAME} - docker container rm --volumes ${FILES_CONTAINER_NAME} + docker container stop "${FILES_CONTAINER_NAME}" + docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose up \ --remove-orphans \ --wait \ diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 5c5c9074..948cd8af 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -9,7 +9,7 @@ services: environment: POSTGRES_USER: "${POSTGRES_USER:?required}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" - POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` + POSTGRES_DB: "postgres" # default database used to run `./database.mk drop` # ports: # - "0.0.0.0:5432:5432" # For possible command-line arguments see @@ -87,7 +87,7 @@ services: # https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-watch#environment-variables DOTNET_USE_POLLING_FILE_WATCHER: "true" # https://github.com/dotnet/sdk/issues/50965#issuecomment-3649670760 - DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "10" + # DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS: "10" DOTNET_WATCH_SUPPRESS_LAUNCH_BROWSER: "true" DOTNET_WATCH_SUPPRESS_BROWSER_REFRESH: "true" # https://docs.microsoft.com/en-us/aspnet/core/security/docker-compose-https#starting-a-container-with-https-support-using-docker-compose @@ -104,13 +104,13 @@ services: command: [ "dotnet", - "watch", - "--verbose", - "--non-interactive", - "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx - "--project", - "/app/src/Database.csproj", - "--", + # "watch", + # "--verbose", + # # "--non-interactive", + # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx + # "--project", + # "/app/src/Database.csproj", + # "--", "run", "--no-launch-profile", "--project", diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 8e1b0e60..dc549e2d 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -9,7 +9,7 @@ services: environment: POSTGRES_USER: "${POSTGRES_USER:?required}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" - POSTGRES_DB: "postgres" # default database used to run `./database.mk dropdb` + POSTGRES_DB: "postgres" # default database used to run `./database.mk drop` # ports: # - "0.0.0.0:5432:5432" # For possible command-line arguments see From f9070df7fb9e2989b16e6dd8eada3c641863c75c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 09:30:26 +0100 Subject: [PATCH 712/878] Declare gnupg volume --- database.mk | 1 + docker-compose.production.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/database.mk b/database.mk index 4481a6ba..b1d75201 100755 --- a/database.mk +++ b/database.mk @@ -134,6 +134,7 @@ backup : ## Backup database and related data to directory with absolute path `${ --clean \ --if-exists \ --username="${POSTGRES_USER}" \ + --dbname="${POSTGRES_DATABASE_NAME}" \ | gzip \ > "${DIR}/${dump_archive_name}" docker container stop "${DATABASE_CONTAINER_NAME}" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index dc549e2d..72d24db9 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -141,4 +141,5 @@ volumes: data: files: certificates: + gnupg: backend_logs: From 666b2002c43e0dc9e81c8a4f293730a1a386be2a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 09:32:25 +0100 Subject: [PATCH 713/878] Remove unused imports --- frontend/components/Layout.tsx | 2 +- frontend/lib/apollo.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 4975055c..dec18595 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -1,5 +1,5 @@ import Head from "next/head"; -import { ReactNode, useEffect, useState } from "react"; +import { ReactNode, useEffect } from "react"; import Footer from "./Footer"; import NavBar from "./NavBar"; import { App, Layout as AntLayout, Typography } from "antd"; diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 8fe43051..7e2be1fd 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -6,7 +6,6 @@ import { } from "@apollo/client"; import { Defer20220824Handler } from "@apollo/client/incremental"; import { LocalState } from "@apollo/client/local-state"; -import { CombinedGraphQLErrors } from "@apollo/client/errors"; import { PersistedQueryLink } from "@apollo/client/link/persisted-queries"; import { SetContextLink } from "@apollo/client/link/context"; import { sha256 } from "crypto-hash"; From 1df88cf928f3b0f7271505e0a2d1397f29feba20 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 09:36:43 +0100 Subject: [PATCH 714/878] Add bootstrap service to production environment --- docker-compose.production.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 72d24db9..5e30991a 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -137,6 +137,18 @@ services: labels: autoheal: "true" + bootstrap: + build: + context: "." + dockerfile: "./Dockerfile.bootstrap" + volumes: + - .:/app + - gnupg:/root/.gnupg + - ./gnupg.conf:/root/.gnupg/common.conf + - ./gpg-agent.conf:/root/.gnupg/gpg-agent.conf + profiles: + - "manual" + volumes: data: files: From bc5018fbab8f515f34c3e28ce508d69b353dbf4d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 09:47:43 +0100 Subject: [PATCH 715/878] Do not populate GnuPG *.conf files --- Dockerfile.bootstrap | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Dockerfile.bootstrap b/Dockerfile.bootstrap index 9c0ce1c2..de62378e 100644 --- a/Dockerfile.bootstrap +++ b/Dockerfile.bootstrap @@ -111,8 +111,4 @@ WORKDIR /app RUN mkdir --parents \ "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ - "${HOME}/.gnupg" \ - && echo "no-use-keyboxd" \ - >"${HOME}/.gnupg/common.conf" \ - && echo "allow-loopback-pinentry" \ - >"${HOME}/.gnupg/gpg-agent.conf" + "${HOME}/.gnupg" From cb13ef3fd5221ac10227eecac1bb80115a1bdf8a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 10:02:14 +0100 Subject: [PATCH 716/878] Stay root in production for bootstrapping GPG keys --- ...tstrap => Dockerfile.bootstrap.development | 0 Dockerfile.bootstrap.production | 63 +++++++++++++++++++ docker-compose.development.yaml | 6 +- docker-compose.production.yaml | 8 +-- 4 files changed, 70 insertions(+), 7 deletions(-) rename Dockerfile.bootstrap => Dockerfile.bootstrap.development (100%) create mode 100644 Dockerfile.bootstrap.production diff --git a/Dockerfile.bootstrap b/Dockerfile.bootstrap.development similarity index 100% rename from Dockerfile.bootstrap rename to Dockerfile.bootstrap.development diff --git a/Dockerfile.bootstrap.production b/Dockerfile.bootstrap.production new file mode 100644 index 00000000..7840c18b --- /dev/null +++ b/Dockerfile.bootstrap.production @@ -0,0 +1,63 @@ +# syntax=docker/dockerfile:1.21 +# check=error=true +# Available versions are listed on https://hub.docker.com/r/docker/dockerfile + +FROM debian:bookworm +SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] + +# Install up-to-date GnuPG from the official GnuPG repository. +# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ +ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 +# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. +RUN \ + version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + ca-certificates \ + curl \ + gnupg2 && \ + curl \ + --silent \ + --show-error \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ + fingerprint=$(gpg \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ + if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ + fi && \ + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ + > /etc/apt/sources.list.d/gnupg.list && \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2 && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + +RUN apt-get update \ + && apt-get install \ + --assume-yes \ + --no-install-recommends \ + make \ + && rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + +RUN mkdir --parents \ + "${HOME}/.gnupg" \ + && chmod u=rwx,g=,o= \ + "${HOME}/.gnupg" diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 948cd8af..fe222578 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -38,7 +38,7 @@ services: backend: build: context: "./backend" - dockerfile: "./Dockerfile.development" + dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" depends_on: database: condition: "service_started" @@ -130,7 +130,7 @@ services: frontend: build: context: "./frontend" - dockerfile: "./Dockerfile.development" + dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" depends_on: backend: condition: "service_started" @@ -190,7 +190,7 @@ services: bootstrap: build: context: "." - dockerfile: "./Dockerfile.bootstrap" + dockerfile: "./Dockerfile.bootstrap.${ENVIRONMENT:?required}" volumes: - .:/app - gnupg:/home/me/.gnupg diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 5e30991a..9cb1d5c2 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -140,12 +140,12 @@ services: bootstrap: build: context: "." - dockerfile: "./Dockerfile.bootstrap" + dockerfile: "./Dockerfile.bootstrap.${ENVIRONMENT:?required}" volumes: - .:/app - - gnupg:/root/.gnupg - - ./gnupg.conf:/root/.gnupg/common.conf - - ./gpg-agent.conf:/root/.gnupg/gpg-agent.conf + - gnupg:/home/me/.gnupg + - ./gnupg.conf:/home/me/.gnupg/common.conf + - ./gpg-agent.conf:/home/me/.gnupg/gpg-agent.conf profiles: - "manual" From 535c38d76d7353cd9728152b50edb8b2e23f3319 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 10:04:06 +0100 Subject: [PATCH 717/878] Use the standard backend image for bootstrapping --- Dockerfile.bootstrap.development | 114 ------------------------------- Dockerfile.bootstrap.production | 63 ----------------- docker-compose.development.yaml | 4 +- docker-compose.production.yaml | 10 ++- 4 files changed, 6 insertions(+), 185 deletions(-) delete mode 100644 Dockerfile.bootstrap.development delete mode 100644 Dockerfile.bootstrap.production diff --git a/Dockerfile.bootstrap.development b/Dockerfile.bootstrap.development deleted file mode 100644 index de62378e..00000000 --- a/Dockerfile.bootstrap.development +++ /dev/null @@ -1,114 +0,0 @@ -# syntax=docker/dockerfile:1.21 -# check=error=true -# Available versions are listed on https://hub.docker.com/r/docker/dockerfile - -FROM debian:bookworm -SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] - -ARG GROUP_ID -ARG USER_ID - -RUN \ - if [ -z "$GROUP_ID" ]; then echo "required 'GROUP_ID'"; exit 1; fi && \ - if [ -z "$USER_ID" ]; then echo "required 'USER_ID'"; exit 1; fi - -# Create non-root user to run commands in (see https://medium.com/@mccode/processes-in-containers-should-not-run-as-root-2feae3f0df3b) -# id --user --name "${USER_ID}" 2>/dev/null -RUN \ - apt-get update && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - adduser && \ - existing_user_name="$( (getent passwd ${USER_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_user_name}"; then \ - deluser "${existing_user_name}"; \ - fi && \ - existing_group_name="$( (getent group ${GROUP_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ - if test -n "${existing_group_name}"; then \ - delgroup "${existing_group_name}"; \ - fi && \ - addgroup \ - --system \ - --gid "${GROUP_ID}" \ - us && \ - adduser \ - --system \ - --home /home/me \ - --uid "${USER_ID}" \ - --ingroup us \ - me && \ - rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -# Install up-to-date GnuPG from the official GnuPG repository. -# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ -ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 -# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. -RUN \ - version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ - apt-get update && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - ca-certificates \ - curl \ - gnupg2 && \ - curl \ - --silent \ - --show-error \ - "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ - | gpg --dearmor \ - > /usr/share/keyrings/gnupg-keyring.gpg && \ - fingerprint=$(gpg \ - --batch \ - --with-colons \ - --import-options show-only \ - --import /usr/share/keyrings/gnupg-keyring.gpg \ - | awk -F':' '$1=="fpr"{print $10}') && \ - if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ - echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ - exit 1; \ - fi && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ - > /etc/apt/sources.list.d/gnupg.list && \ - apt-get update && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - dirmngr \ - gnupg2 && \ - rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -RUN apt-get update \ - && apt-get install \ - --assume-yes \ - --no-install-recommends \ - make \ - && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -ENV HOME=/home/me -RUN mkdir --parents "${HOME}/app" \ - && chown \ - me:us \ - "${HOME}" \ - && chown \ - me:us \ - "${HOME}/app" \ - && ln --symbolic "${HOME}/app" /app - -USER me -WORKDIR /app - -RUN mkdir --parents \ - "${HOME}/.gnupg" \ - && chmod u=rwx,g=,o= \ - "${HOME}/.gnupg" diff --git a/Dockerfile.bootstrap.production b/Dockerfile.bootstrap.production deleted file mode 100644 index 7840c18b..00000000 --- a/Dockerfile.bootstrap.production +++ /dev/null @@ -1,63 +0,0 @@ -# syntax=docker/dockerfile:1.21 -# check=error=true -# Available versions are listed on https://hub.docker.com/r/docker/dockerfile - -FROM debian:bookworm -SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] - -# Install up-to-date GnuPG from the official GnuPG repository. -# For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ -ENV GNUPG_FINGERPRINT=32097B719B3745D6E61DDA1B85C45AE3E1A2B355 -# hadolint ignore=SC2016 # Ignore "Expressions don't expand in single quotes, use double quotes for that" because we do not want `$VERSION_CODENAME` to expand. -RUN \ - version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ - apt-get update && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - ca-certificates \ - curl \ - gnupg2 && \ - curl \ - --silent \ - --show-error \ - "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ - | gpg --dearmor \ - > /usr/share/keyrings/gnupg-keyring.gpg && \ - fingerprint=$(gpg \ - --batch \ - --with-colons \ - --import-options show-only \ - --import /usr/share/keyrings/gnupg-keyring.gpg \ - | awk -F':' '$1=="fpr"{print $10}') && \ - if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ - echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ - exit 1; \ - fi && \ - echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ - > /etc/apt/sources.list.d/gnupg.list && \ - apt-get update && \ - apt-get install \ - --assume-yes \ - --no-install-recommends \ - dirmngr \ - gnupg2 && \ - rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -RUN apt-get update \ - && apt-get install \ - --assume-yes \ - --no-install-recommends \ - make \ - && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* - -RUN mkdir --parents \ - "${HOME}/.gnupg" \ - && chmod u=rwx,g=,o= \ - "${HOME}/.gnupg" diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index fe222578..5901d4ba 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -189,8 +189,8 @@ services: bootstrap: build: - context: "." - dockerfile: "./Dockerfile.bootstrap.${ENVIRONMENT:?required}" + context: "./backend" + dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" volumes: - .:/app - gnupg:/home/me/.gnupg diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 9cb1d5c2..550a3fe2 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -138,14 +138,12 @@ services: autoheal: "true" bootstrap: - build: - context: "." - dockerfile: "./Dockerfile.bootstrap.${ENVIRONMENT:?required}" + image: "${NAME:?required}-backend:${TARGET:?required}" volumes: - .:/app - - gnupg:/home/me/.gnupg - - ./gnupg.conf:/home/me/.gnupg/common.conf - - ./gpg-agent.conf:/home/me/.gnupg/gpg-agent.conf + - gnupg:/root/.gnupg + - ./gnupg.conf:/root/.gnupg/common.conf + - ./gpg-agent.conf:/root/.gnupg/gpg-agent.conf profiles: - "manual" From 5cf5e6b57c5cde56bfc229ae34eba0c4f181b51c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 15:08:53 +0100 Subject: [PATCH 718/878] Simplify database management commands and use the backend service for bootstrapping --- database.mk | 142 ++++++++------------------------ docker-compose.development.yaml | 12 --- docker-compose.production.yaml | 10 --- docker.mk | 6 +- 4 files changed, 36 insertions(+), 134 deletions(-) diff --git a/database.mk b/database.mk index b1d75201..468f55d8 100755 --- a/database.mk +++ b/database.mk @@ -20,7 +20,7 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -psql : ## Enter PostgreSQL interactive terminal in the running `database` container +psql : ## Enter PostgreSQL interactive terminal in the `database` container docker compose up \ --remove-orphans \ --wait \ @@ -39,58 +39,36 @@ remove-volume : ## Remove data and files volumes "${NAME}_${ENVIRONMENT}_files" .PHONY : remove-volume -create : CONTAINER_NAME = create_${NAME}_database create : ## Create database with name `${POSTGRES_DATABASE_NAME}` - -docker container stop "${CONTAINER_NAME}" - -docker container rm --volumes "${CONTAINER_NAME}" - docker compose run \ - --name "${CONTAINER_NAME}" \ - --detach \ + docker compose up \ + --wait \ database - while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done - docker exec \ - "${CONTAINER_NAME}" \ + docker compose exec \ + database \ createdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - docker container stop "${CONTAINER_NAME}" - docker container rm --volumes "${CONTAINER_NAME}" .PHONY : create -drop : CONTAINER_NAME = drop_${NAME}_database drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` - -docker container stop "${CONTAINER_NAME}" - -docker container rm --volumes "${CONTAINER_NAME}" - docker compose run \ - --name "${CONTAINER_NAME}" \ - --detach \ + docker compose up \ + --wait \ database - while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done - docker exec \ - "${CONTAINER_NAME}" \ + docker compose exec \ + database \ dropdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - docker container stop "${CONTAINER_NAME}" - docker container rm --volumes "${CONTAINER_NAME}" .PHONY : drop -sql : CONTAINER_NAME = sql_${NAME}_database -sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - docker compose down \ - --remove-orphans \ - database - -docker container stop "${CONTAINER_NAME}" - -docker container rm --volumes "${CONTAINER_NAME}" - docker compose run \ - --name "${CONTAINER_NAME}" \ - --detach \ +sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after schema changes the backend service needs to be restarted with `make restart SERVICE=backend`) + docker compose up \ + --wait \ database - while [[ "$$(docker inspect -f {{.State.Health.Status}} '${CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done cat "${SCRIPT}" \ - | docker exec \ - --interactive \ - "${CONTAINER_NAME}" \ + | docker compose exec \ + --no-tty \ + database \ psql \ --echo-all \ --no-psqlrc \ @@ -98,38 +76,23 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for --file=- \ --username="${POSTGRES_USER}" \ --dbname="${POSTGRES_DATABASE_NAME}" - docker container stop "${CONTAINER_NAME}" - docker container rm --volumes "${CONTAINER_NAME}" - docker compose up \ - --remove-orphans \ - --wait \ - database .PHONY : sql migrate : SCRIPT = ./backend/src/Migrations/migrate.sql -migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) +migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (note that after schema changes the backend service needs to be restarted with `make restart SERVICE=backend`) .PHONY : migrate # Backup with `pg_dump`: https://www.postgresql.org/docs/current/backup-dump.html # Command `pg_dump`: https://www.postgresql.org/docs/current/app-pgdump.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ -backup : DATABASE_CONTAINER_NAME = backup_${NAME}_database -backup : FILES_CONTAINER_NAME = backup_${NAME}_files -backup : ## Backup database and related data to directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` +backup : ## Backup database and related data to directory with absolute path `${DIR}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` mkdir --parents "${DIR}" - docker compose down \ - --remove-orphans \ - database - -docker container stop "${DATABASE_CONTAINER_NAME}" - -docker container rm --volumes "${DATABASE_CONTAINER_NAME}" - docker compose run \ - --name "${DATABASE_CONTAINER_NAME}" \ - --detach \ + docker compose up \ + --wait \ database - while [[ "$$(docker inspect -f {{.State.Health.Status}} '${DATABASE_CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done - docker exec \ - "${DATABASE_CONTAINER_NAME}" \ + docker compose exec \ + database \ pg_dump \ --clean \ --if-exists \ @@ -137,19 +100,10 @@ backup : ## Backup database and related data to directory with absolute path `${ --dbname="${POSTGRES_DATABASE_NAME}" \ | gzip \ > "${DIR}/${dump_archive_name}" - docker container stop "${DATABASE_CONTAINER_NAME}" - docker container rm --volumes "${DATABASE_CONTAINER_NAME}" - -docker container stop "${FILES_CONTAINER_NAME}" - -docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose run \ - --name "${FILES_CONTAINER_NAME}" \ - --detach \ - backend - docker run \ --rm \ - --volumes-from "${FILES_CONTAINER_NAME}" \ --volume "${DIR}":/backup \ - debian:bullseye-slim \ + backend \ tar \ --verbose \ --create \ @@ -157,41 +111,26 @@ backup : ## Backup database and related data to directory with absolute path `${ --file="/backup/${files_archive_name}" \ --directory=/app \ ./files - docker container stop "${FILES_CONTAINER_NAME}" - docker container rm --volumes "${FILES_CONTAINER_NAME}" - docker compose up \ - --remove-orphans \ - --wait \ - database .PHONY : backup -restore : DATABASE_CONTAINER_NAME = restore_${NAME}_database -restore : FILES_CONTAINER_NAME = restore_${NAME}_files -restore : ## Restore database and related data from directory with absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `make restart`)` - docker compose down \ - --remove-orphans \ - database - -docker container stop "${DATABASE_CONTAINER_NAME}" - -docker container rm --volumes "${DATABASE_CONTAINER_NAME}" - docker compose run \ - --name "${DATABASE_CONTAINER_NAME}" \ - --detach \ +restore : ## Restore database and related data from directory with absolute path `${DIR}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`)` + docker compose up \ + --wait \ database - while [[ "$$(docker inspect -f {{.State.Health.Status}} '${DATABASE_CONTAINER_NAME}')" != "healthy" ]]; do sleep 1; done - -docker exec \ - "${CONTAINER_NAME}" \ + -docker compose exec \ + database \ dropdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" - docker exec \ - "${CONTAINER_NAME}" \ + docker compose exec \ + database \ createdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" gunzip --stdout "${DIR}/${dump_archive_name}" \ - | docker exec \ - --interactive \ - "${DATABASE_CONTAINER_NAME}" \ + | docker compose exec \ + --no-tty \ + database \ psql \ --echo-all \ --no-psqlrc \ @@ -199,19 +138,10 @@ restore : ## Restore database and related data from directory with absolute path --file=- \ --username="${POSTGRES_USER}" \ --dbname="${POSTGRES_DATABASE_NAME}" - docker container stop "${DATABASE_CONTAINER_NAME}" - docker container rm --volumes "${DATABASE_CONTAINER_NAME}" - -docker container stop "${FILES_CONTAINER_NAME}" - -docker container rm --volumes "${FILES_CONTAINER_NAME}" docker compose run \ - --name "${FILES_CONTAINER_NAME}" \ - --detach \ - backend - docker run \ --rm \ - --volumes-from "${FILES_CONTAINER_NAME}" \ --volume "${DIR}":/backup \ - debian:bullseye-slim \ + backend \ bash -o errexit -o errtrace -o nounset -o pipefail -c " \ cd /app/files \ && rm \ @@ -226,10 +156,4 @@ restore : ## Restore database and related data from directory with absolute path --strip-components=2 \ --file='/backup/${files_archive_name}' \ " - docker container stop "${FILES_CONTAINER_NAME}" - docker container rm --volumes "${FILES_CONTAINER_NAME}" - docker compose up \ - --remove-orphans \ - --wait \ - database .PHONY : restore diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 5901d4ba..8ad32c3e 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -187,18 +187,6 @@ services: labels: autoheal: "true" - bootstrap: - build: - context: "./backend" - dockerfile: "./Dockerfile.${ENVIRONMENT:?required}" - volumes: - - .:/app - - gnupg:/home/me/.gnupg - - ./gnupg.conf:/home/me/.gnupg/common.conf - - ./gpg-agent.conf:/home/me/.gnupg/gpg-agent.conf - profiles: - - "manual" - volumes: data: files: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 550a3fe2..72d24db9 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -137,16 +137,6 @@ services: labels: autoheal: "true" - bootstrap: - image: "${NAME:?required}-backend:${TARGET:?required}" - volumes: - - .:/app - - gnupg:/root/.gnupg - - ./gnupg.conf:/root/.gnupg/common.conf - - ./gpg-agent.conf:/root/.gnupg/gpg-agent.conf - profiles: - - "manual" - volumes: data: files: diff --git a/docker.mk b/docker.mk index 8f27c92c..202f17b8 100755 --- a/docker.mk +++ b/docker.mk @@ -163,12 +163,12 @@ list-services : ## List all services specified in the docker-compose file (used bootstrap : COMMAND = bash bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a shell within, for example, `make bootstrap COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` or simply `make bootstrap` docker compose pull \ - bootstrap + backend docker compose build \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) \ - bootstrap + backend docker compose run \ --rm \ - bootstrap \ + backend \ ${COMMAND} From 3938db3a8cbb786386cccb0110aa26e20c872166 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 14 Feb 2026 15:11:40 +0100 Subject: [PATCH 719/878] Ignore the stored target file --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9e3327be..d5a8ad8a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,7 @@ /machine/*.retry /nginx/html/maintenance.html /tags* -/commit - +/.stored-target /.env # Compiled output From d754bedb013c49947742bcbca195d0fac6f4b99d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 12:56:00 +0100 Subject: [PATCH 720/878] Start and stop the backend service when needed due to database changes --- database.mk | 14 +++++++++++--- deploy.mk | 5 +++-- docker.mk | 3 ++- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/database.mk b/database.mk index 468f55d8..dd513965 100755 --- a/database.mk +++ b/database.mk @@ -61,7 +61,7 @@ drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` "${POSTGRES_DATABASE_NAME}" .PHONY : drop -sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after schema changes the backend service needs to be restarted with `make restart SERVICE=backend`) +sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after database schema changes it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`) docker compose up \ --wait \ database @@ -79,7 +79,11 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for .PHONY : sql migrate : SCRIPT = ./backend/src/Migrations/migrate.sql -migrate : sql ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql (note that after schema changes the backend service needs to be restarted with `make restart SERVICE=backend`) +migrate : ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql + $(MAKE) --file="${SELF}" sql SCRIPT="${SCRIPT}" + docker compose restart \ + --do-deps \ + backend .PHONY : migrate # Backup with `pg_dump`: https://www.postgresql.org/docs/current/backup-dump.html @@ -113,7 +117,9 @@ backup : ## Backup database and related data to directory with absolute path `${ ./files .PHONY : backup -restore : ## Restore database and related data from directory with absolute path `${DIR}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` (note that after restoring a database it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`)` +restore : ## Restore database and related data from directory with absolute path `${DIR}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` + docker compose stop \ + backend docker compose up \ --wait \ database @@ -156,4 +162,6 @@ restore : ## Restore database and related data from directory with absolute path --strip-components=2 \ --file='/backup/${files_archive_name}' \ " + docker compose start \ + backend .PHONY : restore diff --git a/deploy.mk b/deploy.mk index 1ca36adb..356a3d89 100755 --- a/deploy.mk +++ b/deploy.mk @@ -66,13 +66,14 @@ rollback : symlink ## Rollback deployment attempt (uses target stored in `./.sto .PHONY : rollback begin-maintenance : ## Begin maintenance - cp \ + cp --update=none \ ./nginx/html/maintenance.off.html \ ./nginx/html/maintenance.html .PHONY : begin-maintenance end-maintenance : ## End maintenance - rm ./nginx/html/maintenance.html + rm --force \ + ./nginx/html/maintenance.html .PHONY : end-maintenance store-target : ## Store the value `${TARGET}` diff --git a/docker.mk b/docker.mk index 202f17b8..1267fae3 100755 --- a/docker.mk +++ b/docker.mk @@ -104,7 +104,8 @@ down : ## Stop services and remove services and networks created by `up` .PHONY : down restart : ## Restart services (and await their health), for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` - docker compose restart ${SERVICE} + docker compose restart \ + --do-deps ${SERVICE} docker compose up \ --wait ${SERVICE} .PHONY : restart From 3b8c04599abffe76b024ac99efba4ba319f7e236 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 13:33:21 +0100 Subject: [PATCH 721/878] Impose CPU limits --- docker-compose.development.yaml | 16 ++++++++++------ docker-compose.production.yaml | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 8ad32c3e..d6617fd0 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -105,12 +105,12 @@ services: [ "dotnet", # "watch", - # "--verbose", - # # "--non-interactive", - # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx - # "--project", - # "/app/src/Database.csproj", - # "--", + # "--verbose", + # # "--non-interactive", + # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx + # "--project", + # "/app/src/Database.csproj", + # "--", "run", "--no-launch-profile", "--project", @@ -125,6 +125,10 @@ services: retries: "20" labels: autoheal: "true" + deploy: + resources: + limits: + cpus: "0.8" stdin_open: true frontend: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 72d24db9..7c98f4d6 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -92,6 +92,10 @@ services: retries: "20" labels: autoheal: "true" + deploy: + resources: + limits: + cpus: "0.8" pull_policy: "never" stdin_open: true From 98944f081fe4fde7bcf4d136ac443dd9ef122a61 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 16:26:13 +0100 Subject: [PATCH 722/878] Remove `--update=non` from copy because it is not yet supported in debian bookworm --- deploy.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.mk b/deploy.mk index 356a3d89..843a9a7d 100755 --- a/deploy.mk +++ b/deploy.mk @@ -66,7 +66,7 @@ rollback : symlink ## Rollback deployment attempt (uses target stored in `./.sto .PHONY : rollback begin-maintenance : ## Begin maintenance - cp --update=none \ + cp \ ./nginx/html/maintenance.off.html \ ./nginx/html/maintenance.html .PHONY : begin-maintenance From 05e4073c5082e4e3bbef54c842c19aa7575682a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 20:08:09 +0100 Subject: [PATCH 723/878] Use `-` for lists instead of `[ .. ]` in yaml because it has less confusing semantics --- docker-compose.development.yaml | 50 ++++++++++++++++++--------------- docker-compose.production.yaml | 39 +++++++++++++------------ 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index d6617fd0..61d0e78d 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -19,12 +19,12 @@ services: # For an introduction to logging see # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: - [ - "-c", - "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" - ] + - "-c" + - "max_connections=100" + # - "-c" + # - "log_statement=all" + # - "-c" + # - "log_duration=1" restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html @@ -102,20 +102,21 @@ services: - "${METABASE_HOST:?required}:host-gateway" # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line command: - [ - "dotnet", - # "watch", - # "--verbose", - # # "--non-interactive", - # "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2xx - # "--project", - # "/app/src/Database.csproj", - # "--", - "run", - "--no-launch-profile", - "--project", - "/app/src/Database.csproj" - ] + - "dotnet" + # - "watch" + # - "--verbose" + # - # "--non-interactive" + # - "--no-hot-reload", # As of this writing, HotChocolate does not work with hot reload, see https://github.com/ChilliCream/graphql-platform/issues/5803#issuecomment-2689242812 However, `--no-hot-reload` fails to release the port when the clean-up timeout is 0, thus I set it to 5ms below via `DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS`, which should be the default as of https://github.com/dotnet/sdk/issues/50965#issuecomment-3656670112 in version 10.0.2x + # - "--project" + # - "/app/src/Database.csproj" + # - "--" + - "run" + - "--no-launch-profile" + - "--no-restore" + - "--verbosity" + - "minimal" + - "--project" + - "/app/src/Database.csproj" restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example @@ -154,7 +155,9 @@ services: # user: "1000:1000" # ports: # - "0.0.0.0:8081:8080" - command: [ "yarn", "dev" ] + command: + - "yarn" + - "dev" restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] @@ -181,7 +184,10 @@ services: # - /var/run # ... ports: - "0.0.0.0:${HTTP_PORT:?required}:80" - command: [ "nginx-debug", "-g", "daemon off;" ] + command: + - "nginx-debug" + - "-g" + - "daemon off;" restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 7c98f4d6..ca56fbd4 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -19,12 +19,12 @@ services: # For an introduction to logging see # https://betterstack.com/community/guides/logging/how-to-start-logging-with-postgresql/ command: - [ - "-c", - "max_connections=100" - # "-c", "log_statement=all", - # "-c", "log_duration=1" - ] + - "-c" + - "max_connections=100" + # - "-c" + # - "log_statement=all" + # - "-c" + # - "log_duration=1" restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html @@ -73,16 +73,14 @@ services: - "host.docker.internal:host-gateway" # https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0#command-line command: - [ - "dotnet", - "/app/App.dll" - # "/Serilog:MinimumLevel:Default=Debug", - # "/Serilog:MinimumLevel:Microsoft=Debug", - # "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug", - # "/Serilog:MinimumLevel:Override:Microsoft=Debug", - # "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug", - # "/Serilog:MinimumLevel:Override:System=Debug" - ] + - "dotnet" + - "/app/App.dll" + # - "/Serilog:MinimumLevel:Default=Debug" + # - "/Serilog:MinimumLevel:Microsoft=Debug" + # - "/Serilog:MinimumLevel:Microsoft.Hosting.Lifetime=Debug" + # - "/Serilog:MinimumLevel:Override:Microsoft=Debug" + # - "/Serilog:MinimumLevel:Override:Microsoft.AspNetCore=Debug" + # - "/Serilog:MinimumLevel:Override:System=Debug" restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example @@ -106,7 +104,9 @@ services: condition: "service_started" environment: NODE_ENV: "production" - command: [ "node", "/app/server.js" ] + command: + - "node" + - "/app/server.js" restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] @@ -131,7 +131,10 @@ services: - files:/data/files:ro ports: - "0.0.0.0:${HTTP_PORT:?required}:80" - command: [ "nginx", "-g", "daemon off;" ] + command: + - "nginx" + - "-g" + - "daemon off;" restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] From 6ce0e454a9db050c32b22b898945704d895b94cd Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 21:35:24 +0100 Subject: [PATCH 724/878] Customize filter convention naming scheme to align filter names with the ones of the official specification --- .../src/Configuration/GraphQlConfiguration.cs | 366 ++++++++---- .../src/Controllers/FileUploadController.cs | 2 + .../CalorimetricDataFilterType.cs | 1 + backend/src/GraphQl/DataX/DataFilterType.cs | 2 +- ...ttpsResourceTreeNonRootVertexFilterType.cs | 2 +- .../src/GraphQl/Entities/EntityFilterType.cs | 45 ++ .../ErrorLoggingDiagnosticEventListener.cs | 2 + .../Filters/AdditionalFilterOperations.cs | 7 +- .../GraphQl/Filters/ClosedIntervalInput.cs | 6 + backend/src/GraphQl/Filters/NotField.cs | 3 +- .../QueryableFloatInClosedIntervalHandler.cs | 48 ++ .../GeometricDataX/GeometricDataFilterType.cs | 1 + .../GetHttpsResourceFilterType.cs | 1 + backend/src/GraphQl/GraphQlConstants.cs | 2 +- .../HygrothermalDataFilterType.cs | 1 + .../GraphQl/NamedMethodArgumentFilterType.cs | 1 + .../OpticalDataX/OpticalDataFilterType.cs | 1 + .../PhotovoltaicDataFilterType.cs | 1 + .../Publications/PublicationFilterType.cs | 1 + .../GraphQl/References/ReferenceFilterType.cs | 2 + .../ResponseApprovalFilterType.cs | 1 + .../GraphQlSchemaTests.IsUnchanged.snap | 562 ++++++++++-------- frontend/type-defs.graphqls | 562 ++++++++++-------- 23 files changed, 980 insertions(+), 640 deletions(-) create mode 100644 backend/src/GraphQl/Filters/ClosedIntervalInput.cs create mode 100644 backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 69b551f1..67ef6674 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -4,14 +4,16 @@ using Database.Data; using Database.GraphQl; using Database.GraphQl.DataX; -using HotChocolate.AspNetCore; +using Database.GraphQl.Filters; using HotChocolate.Configuration; using HotChocolate.Data; using HotChocolate.Data.Filters; +using HotChocolate.Data.Filters.Expressions; using HotChocolate.Data.Sorting; using HotChocolate.Execution; using HotChocolate.Language; using HotChocolate.Types; +using HotChocolate.Types.Descriptors; using HotChocolate.Types.NodaTime; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -49,6 +51,7 @@ IWebHostEnvironment environment .AddProjections() .AddFiltering() .AddSorting() + .AddConvention() .AddQueryContext() .AddAuthorization() .AddGlobalObjectIdentification() @@ -159,9 +162,20 @@ await httpContext.RequestServices } } +public sealed class CustomNamingConventions +: DefaultNamingConventions +{ +} + // See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions public partial class CustomFilterConvention : FilterConvention { + + private const string InputPostFix = "FilterInput"; + private const string InputTypePostFix = "FilterInputType"; + + private readonly CustomNamingConventions _namingConventions = new(); + protected override void Configure(IFilterConventionDescriptor descriptor) { descriptor.AddDefaults(); @@ -170,73 +184,82 @@ protected override void Configure(IFilterConventionDescriptor descriptor) // Allow conjunction and disjunction descriptor.AllowAnd(); descriptor.AllowOr(); + // TODO negation "AllowNot" and "UseNot". See `NotField` and `EntityFilterType.OnCompleteFields` + // Add in-closed-interval operation + descriptor + .Operation(AdditionalFilterOperations.InClosedInterval) + .Name("inClosedInterval"); + descriptor.Configure(_ => _ + .Operation(AdditionalFilterOperations.InClosedInterval) + .Type>() + ); + descriptor.Provider( + new QueryableFilterProvider(_ => _ + .AddDefaultFieldHandlers() + .AddFieldHandler() + ) + ); } - // TODO Overriding and changing type names in this way is _super_ error-prone. However, using `descriptor.Configure<...FilterInputType>(x => x.Name(name))` does not work. Why? // For the base implementation see https://github.com/ChilliCream/hotchocolate/blob/f0dff93a14cb7ddecc7b3a0530a687a5bc4bad71/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 public override string GetTypeName(Type runtimeType) { - var nameString = base.GetTypeName(runtimeType); - return - IDataRegex().Replace( - DoubleRegex().Replace( - FloatRegex().Replace( - ListStringRegex().Replace( - ListFloatRegex().Replace( - GuidRegex().Replace( - ComparableRegex().Replace( - FilterInputRegex().Replace( - OperationFilterInputRegex().Replace( - nameString, - "PropositionInput" - ), - "PropositionInput" - ), - "" - ), - "Uuid" - ), - "Floats" - ), - "Strings" - ), - "Float" - ), - "Float" - ), - "Data" - ); + // return base.GetTypeName(runtimeType); + return GetTypeName(runtimeType, plural: false); } - [GeneratedRegex(@"IData")] - private static partial Regex IDataRegex(); - - [GeneratedRegex(@"Double")] - private static partial Regex DoubleRegex(); - - [GeneratedRegex(@"Float")] - private static partial Regex FloatRegex(); - - [GeneratedRegex(@"ListString")] - private static partial Regex ListStringRegex(); - - [GeneratedRegex(@"ListFloat")] - private static partial Regex ListFloatRegex(); - - [GeneratedRegex(@"Guid")] - private static partial Regex GuidRegex(); - - [GeneratedRegex(@"^Comparable")] - private static partial Regex ComparableRegex(); - - [GeneratedRegex(@"FilterInput$")] - private static partial Regex FilterInputRegex(); - - [GeneratedRegex(@"OperationFilterInput")] - private static partial Regex OperationFilterInputRegex(); + public string GetTypeName(Type runtimeType, bool plural) + { + ArgumentNullException.ThrowIfNull(runtimeType); + var pluralSuffix = plural ? "s" : ""; + if (typeof(IEnumOperationFilterInputType).IsAssignableFrom(runtimeType) + && runtimeType.GenericTypeArguments.Length == 1 + && runtimeType.GetGenericTypeDefinition() == typeof(EnumOperationFilterInputType<>)) + { + var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); + return $"{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; ; + // return genericName + "OperationFilterInput"; + } + if (typeof(IComparableOperationFilterInputType).IsAssignableFrom(runtimeType) + && runtimeType.GenericTypeArguments.Length == 1 + && runtimeType.GetGenericTypeDefinition() + == typeof(ComparableOperationFilterInputType<>)) + { + var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); + return $"Comparable{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + // return $"Comparable{genericName}OperationFilterInput"; + } + if (typeof(IListFilterInputType).IsAssignableFrom(runtimeType) + && runtimeType.GenericTypeArguments.Length == 1) + { + var genericType = runtimeType.GenericTypeArguments[0]; + var genericName = typeof(FilterInputType).IsAssignableFrom(genericType) + ? GetTypeName(genericType, plural: true) + : "List" + _namingConventions.GetTypeName(genericType); + return $"{genericName}"; + // return "List" + genericName; + } + var name = _namingConventions.GetTypeName(runtimeType); + var isInputObjectType = typeof(FilterInputType).IsAssignableFrom(runtimeType); + var isEndingInput = name.EndsWith(InputPostFix, StringComparison.Ordinal); + var isEndingInputType = name.EndsWith(InputTypePostFix, StringComparison.Ordinal); + if (isInputObjectType && isEndingInputType) + { + return $"{name[..^"FilterInputType".Length]}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + } + if (isInputObjectType && !isEndingInput && !isEndingInputType) + { + return $"{name}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + } + if (!isInputObjectType && !isEndingInput) + { + return $"{name}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + } + return name; + } } -public static class FilterConventionDescriptorExtensions +public static class CustomFilterConventionExtensions { // Inspired by FilterConventionDescriptorExtensions#AddDefaults // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L16 @@ -250,10 +273,12 @@ public static IFilterConventionDescriptor AddDefaults( } // Inspired by FilterConventionDescriptorExtensions#AddDefaultOperations - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L28 + // https://github.com/ChilliCream/graphql-platform/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L28 public static IFilterConventionDescriptor AddDefaultOperations( - this IFilterConventionDescriptor descriptor) + this IFilterConventionDescriptor descriptor + ) { + // Use speaking names for operations descriptor.Operation(DefaultFilterOperations.Equals).Name("equalTo"); descriptor.Operation(DefaultFilterOperations.NotEquals).Name("notEqualTo"); descriptor.Operation(DefaultFilterOperations.Contains).Name("contains"); @@ -280,62 +305,189 @@ public static IFilterConventionDescriptor AddDefaultOperations( descriptor.Operation(DefaultFilterOperations.Any).Name("any"); descriptor.Operation(DefaultFilterOperations.Like).Name("like"); descriptor.Operation(DefaultFilterOperations.Data).Name("data"); - // TODO `descriptor.Operation(AdditionalFilterOperations.Not).Name("not");` as in the project `database` - // `inClosedInterval` return descriptor; } // Inspired by FilterConventionDescriptorExtensions#BindDefaultTypes // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L73 public static IFilterConventionDescriptor BindDefaultTypes( - this IFilterConventionDescriptor descriptor) + this IFilterConventionDescriptor descriptor + ) { - descriptor - .BindRuntimeType() - .BindRuntimeType() - .BindRuntimeType() - .BindComparableType("BytePropositionInput") - .BindComparableType("ShortPropositionInput") - .BindComparableType("IntPropositionInput") - .BindComparableType("LongPropositionInput") - .BindComparableType("FloatXPropositionInput") - .BindComparableType("FloatPropositionInput") - .BindComparableType("DecimalPropositionInput") - .BindComparableType("SignedBytePropositionInput") - .BindComparableType("UnsignedShortPropositionInput") - .BindComparableType("UnsignedIntPropositionInput") - .BindComparableType("UnsigendLongPropositionInput") - .BindComparableType("UuidPropositionInput") - .BindComparableType("DateTimePropositionInput") - .BindComparableType("DateTimeOffsetPropositionInput") - .BindComparableType("TimeSpanPropositionInput"); - // TODO Why does this not work? - // descriptor - // .Configure(x => x.Name("StringPropositionInput")) - // .Configure(x => x.Name("BooleanPropositionInput")); - return descriptor; + return descriptor + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + // .BindRuntimeType() + // .BindRuntimeType() + // .BindRuntimeType() + // .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType(); } +} - // Inspired by FilterConventionDescriptorExtensions#FilterConventionDescriptorExtensions - // https://github.com/ChilliCream/hotchocolate/blob/ee5813646fdfea81035c681989793514f33b5d94/src/HotChocolate/Data/src/Data/Filters/Convention/Extensions/FilterConventionDescriptorExtensions.cs#L102 - private static IFilterConventionDescriptor BindComparableType( - this IFilterConventionDescriptor descriptor, - string? name = null) - where T : struct +public sealed class StringFilterInputType + : StringOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) { - descriptor - .BindRuntimeType>() - .BindRuntimeType>(); - // .BindRuntimeType>() - // .BindRuntimeType>(); - // TODO Why does this not work? - // if (name is not null) - // { - // descriptor - // .Configure>(x => x.Name(name)) - // .Configure>(x => x.Name($"Maybe{name}")); - // } - return descriptor; + descriptor.Name($"String{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class BooleanFilterInputType + : BooleanOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Boolean{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class ShortFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Short{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class DateTimeFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"DateTime{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class ByteFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Byte{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class UuidFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Uuid{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +// public sealed class LocalDateFilterInputType +// : ComparableOperationFilterInputType +// { +// protected override void Configure(IFilterInputTypeDescriptor descriptor) +// { +// descriptor.Name($"LocalDate{GraphQlConstants.FilterInputSuffix}"); +// base.Configure(descriptor); +// } +// } + +public sealed class LongFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Long{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +// public sealed class LocalTimeFilterInputType +// : ComparableOperationFilterInputType +// { +// protected override void Configure(IFilterInputTypeDescriptor descriptor) +// { +// descriptor.Name($"LocalTime{GraphQlConstants.FilterInputSuffix}"); +// base.Configure(descriptor); +// } +// } + +public sealed class FloatFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Float{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class TimeSpanFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"TimeSpan{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class IntFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Int{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class DecimalFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Decimal{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class UrlFilterInputType + : ComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Url{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); } } diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 9aadba7a..f16f4fcd 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -168,10 +168,12 @@ CancellationToken cancellationToken await targetStream.WriteAsync(streamedFileContent, cancellationToken); // Don't trust the file name sent by the client. To display the // file name, HTML-encode the value. +#pragma warning disable CA1873 // Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873) logger.SavedUploadedFile( WebUtility.HtmlEncode(contentDisposition.FileName.Value), getHttpsResource.FilePath ); +#pragma warning restore CA1873 break; } // Drain any remaining section body that hasn't been consumed and diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index 207d1755..90a22c50 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -12,6 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(CalorimetricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.GValues); descriptor.Field(x => x.UValues); } diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index 96cced53..fcf30ee9 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -11,6 +11,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Name(nameof(DataFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + descriptor.Name(nameof(DataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index d6f3d3dd..fd8400c7 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -12,7 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.CalorimetricData).Ignore(); descriptor.Field(x => x.GeometricData).Ignore(); descriptor.Field(x => x.HygrothermalData).Ignore(); diff --git a/backend/src/GraphQl/Entities/EntityFilterType.cs b/backend/src/GraphQl/Entities/EntityFilterType.cs index 2b283f62..c3cc4a9e 100644 --- a/backend/src/GraphQl/Entities/EntityFilterType.cs +++ b/backend/src/GraphQl/Entities/EntityFilterType.cs @@ -1,5 +1,10 @@ +using System; +using System.Linq; using Database.Data; +using HotChocolate.Configuration; using HotChocolate.Data.Filters; +using HotChocolate.Types; +using HotChocolate.Types.Descriptors.Definitions; namespace Database.GraphQl.Entities; @@ -15,4 +20,44 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.Id); // TODO Do we want to filter by: descriptor.Field(x => x.Version); } + + // Inspired by https://github.com/ChilliCream/graphql-platform/blob/bc5a190019d8d0fbe46a557a59feacd7b30dd0c3/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs#L78 + // protected override FieldCollection OnCompleteFields( + // ITypeCompletionContext context, + // InputObjectTypeDefinition definition + // ) + // { + // var fields = new InputField[definition.Fields.Count + 2]; + // var index = 0; + // if (definition is FilterInputTypeDefinition { UseAnd: true, } def) + // { + // fields[index] = new AndField(context.DescriptorContext, index, def.Scope); + // index++; + // } + // if (definition is FilterInputTypeDefinition { UseOr: true, } defOr) + // { + // fields[index] = new OrField(context.DescriptorContext, index, defOr.Scope); + // index++; + // } + // foreach (var fieldDefinition in + // definition.Fields.Where(t => !t.Ignore)) + // { + // switch (fieldDefinition) + // { + // case FilterOperationFieldDefinition operation: + // fields[index] = new FilterOperationField(operation, index); + // index++; + // break; + // case FilterFieldDefinition field: + // fields[index] = new FilterField(field, index); + // index++; + // break; + // } + // } + // if (fields.Length > index) + // { + // Array.Resize(ref fields, index); + // } + // return CompleteFields(context, this, fields); + // } } \ No newline at end of file diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index 39f1f9d9..033a67a7 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -252,10 +252,12 @@ public void Dispose() { if (context.Document is not null) { +#pragma warning disable CA1873 // Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873) logger.Executed( context.Document.ToString(true), StringifyVariables() ); +#pragma warning restore CA1873 } } // when the request is finished it will dispose the activity scope diff --git a/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs b/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs index c93ef708..4fa742ad 100644 --- a/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs +++ b/backend/src/GraphQl/Filters/AdditionalFilterOperations.cs @@ -1,8 +1,9 @@ -// Inspired by `HotChocolate.Data.Filters.DefaultFilterOperations`. - namespace Database.GraphQl.Filters; public static class AdditionalFilterOperations { - public const int Not = 100; + // Identifiers must be greater than 1024 according to + // https://chillicream.com/docs/hotchocolate/v15/api-reference/extending-filtering + public const int Not = 1025; + public const int InClosedInterval = 1026; } \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/ClosedIntervalInput.cs b/backend/src/GraphQl/Filters/ClosedIntervalInput.cs new file mode 100644 index 00000000..3e7e7f38 --- /dev/null +++ b/backend/src/GraphQl/Filters/ClosedIntervalInput.cs @@ -0,0 +1,6 @@ +namespace Database.GraphQl.Filters; + +public sealed record ClosedIntervalInput( + double LowerBound, + double UpperBound +); \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/NotField.cs b/backend/src/GraphQl/Filters/NotField.cs index 928fed99..05d14b3d 100644 --- a/backend/src/GraphQl/Filters/NotField.cs +++ b/backend/src/GraphQl/Filters/NotField.cs @@ -7,8 +7,9 @@ namespace Database.GraphQl.Filters; public sealed class NotField + // : FilterOperationField : InputField - , INotField + , INotField { internal NotField(IDescriptorContext context, int index, string? scope) : base(CreateDefinition(context, scope), index) diff --git a/backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs b/backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs new file mode 100644 index 00000000..b66620cd --- /dev/null +++ b/backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using HotChocolate.Data.Filters; +using HotChocolate.Data.Filters.Expressions; +using HotChocolate.Language; +using HotChocolate.Types; +using HotChocolate.Utilities; + +namespace Database.GraphQl.Filters; + +public sealed class QueryableFloatInClosedIntervalHandler +: QueryableComparableOperationHandler +{ + public QueryableFloatInClosedIntervalHandler( + ITypeConverter typeConverter, + InputParser inputParser) + : base(typeConverter, inputParser) + { + // CanBeNull = false; + } + + // This is used to match the handler to all `inClosedInterval` fields + protected override int Operation => AdditionalFilterOperations.InClosedInterval; + + public override Expression HandleOperation( + QueryableFilterContext context, + IFilterOperationField field, + IValueNode value, + object? parsedValue + ) + { + // The context's instance is the expression path to the property + // e.g. ~> y.gValue + var propertyPath = context.GetInstance(); + // The parsed value is what was specified in the query + // e.g. ~> { lowerBound: 0.1, upperBound: 0.8 } + var typedValue = (ClosedIntervalInput?)parsedValue + ?? throw new InvalidOperationException(); + // Creates and returns the operation + // e.g. ~> y.gValue >= 0.1 && y.gValue <= 0.8 + return Expression.AndAlso( + FilterExpressionBuilder.GreaterThanOrEqual(propertyPath, typedValue.LowerBound), + FilterExpressionBuilder.LowerThanOrEqual(propertyPath, typedValue.UpperBound) + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs index a762b17b..8f168aab 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -12,6 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(GeometricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Thicknesses); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 2d367a62..34498285 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -12,6 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(GetHttpsResourceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Description); descriptor.Field(x => x.HashValue); descriptor.Field(x => x.DataFormatId); diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs index 77d482f7..5854e8a3 100644 --- a/backend/src/GraphQl/GraphQlConstants.cs +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -5,7 +5,7 @@ internal static class GraphQlConstants internal const string EndpointPath = "/graphql"; internal const string CorsPolicy = "GraphQlCorsPolicy"; internal const string TypeDiscriminatorPropertyName = "__typename"; - internal const string FilterInputSuffix = "FilterInput"; + internal const string FilterInputSuffix = "PropositionInput"; internal const string SortInputSuffix = "SortInput"; internal const string UuidFieldName = "uuid"; internal const string VersionFieldName = "version"; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index c1e2965d..016404ee 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -12,5 +12,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(HygrothermalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs index bb56cb58..7c9e7d3b 100644 --- a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs @@ -11,6 +11,7 @@ IFilterInputTypeDescriptor descriptor ) { descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(NamedMethodArgumentFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Name); // TODO descriptor.Field(x => x.Value); } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 4aa03e45..580ec31e 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -12,6 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(OpticalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Type); descriptor.Field(x => x.Subtype); descriptor.Field(x => x.CoatedSide); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index 5a81a4cc..abd8c2d9 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -12,5 +12,6 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(PhotovoltaicDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationFilterType.cs b/backend/src/GraphQl/Publications/PublicationFilterType.cs index 3829100f..974e7a90 100644 --- a/backend/src/GraphQl/Publications/PublicationFilterType.cs +++ b/backend/src/GraphQl/Publications/PublicationFilterType.cs @@ -11,5 +11,6 @@ IFilterInputTypeDescriptor descriptor ) { descriptor.Field(x => x.Exists).Ignore(); + descriptor.Name(nameof(PublicationFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); } } \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceFilterType.cs b/backend/src/GraphQl/References/ReferenceFilterType.cs index f047a823..7116b7cc 100644 --- a/backend/src/GraphQl/References/ReferenceFilterType.cs +++ b/backend/src/GraphQl/References/ReferenceFilterType.cs @@ -10,7 +10,9 @@ protected override void Configure( IFilterInputTypeDescriptor descriptor ) { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(ReferenceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Standard); descriptor.Field(x => x.Publication); } diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index 75c79c90..9f2fc9c8 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -12,6 +12,7 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor.Name(nameof(ResponseApprovalFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.UserId); descriptor.Field(x => x.Locale); descriptor.Field(x => x.Name); diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 032bd601..1b794b4f 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -396,7 +396,7 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -485,11 +485,11 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsPropositionInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") @@ -498,7 +498,7 @@ type Mutation { updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsPropositionInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") } @@ -906,8 +906,8 @@ input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] or: [AppliedMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput - sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput + arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + sources: FilterInputTypeOfNamedMethodSourcesPropositionInput } input AppliedMethodSortInput { @@ -919,21 +919,36 @@ input BooleanPropositionInput { notEqualTo: Boolean @cost(weight: "10") } +input BytePropositionInput { + equalTo: Byte @cost(weight: "10") + notEqualTo: Byte @cost(weight: "10") + in: [Byte] @cost(weight: "10") + notIn: [Byte] @cost(weight: "10") + greaterThan: Byte @cost(weight: "10") + notGreaterThan: Byte @cost(weight: "10") + greaterThanOrEqualTo: Byte @cost(weight: "10") + notGreaterThanOrEqualTo: Byte @cost(weight: "10") + lessThan: Byte @cost(weight: "10") + notLessThan: Byte @cost(weight: "10") + lessThanOrEqualTo: Byte @cost(weight: "10") + notLessThanOrEqualTo: Byte @cost(weight: "10") +} + input CalendarSystemPropositionInput { and: [CalendarSystemPropositionInput!] or: [CalendarSystemPropositionInput!] id: StringPropositionInput name: StringPropositionInput - minYear: Int32PropositionInput - maxYear: Int32PropositionInput - eras: ListFilterInputTypeOfEraPropositionInput + minYear: IntPropositionInput + maxYear: IntPropositionInput + eras: FilterInputTypeOfErasPropositionInput } input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -941,8 +956,8 @@ input CalorimetricDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput gValues: FloatsPropositionInput uValues: FloatsPropositionInput @@ -977,6 +992,11 @@ input CielabColorPropositionInput { illuminant: NullableOfIlluminantPropositionInput } +input ClosedIntervalInput { + lowerBound: Float! + upperBound: Float! +} + input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! componentId: Uuid! @@ -1062,11 +1082,11 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } -input CreateResponseApprovalsFilterInput { - and: [CreateResponseApprovalsFilterInput!] - or: [CreateResponseApprovalsFilterInput!] +input CreateResponseApprovalsPropositionInput { + and: [CreateResponseApprovalsPropositionInput!] + or: [CreateResponseApprovalsPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1074,8 +1094,8 @@ input CreateResponseApprovalsFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1108,11 +1128,18 @@ input DataApprovalPropositionInput { statement: ReferencePropositionInput } -input DataFilterInput { - and: [DataFilterInput!] - or: [DataFilterInput!] +input DataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + in: [DataKind!] @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") +} + +input DataPropositionInput { + and: [DataPropositionInput!] + or: [DataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1120,18 +1147,11 @@ input DataFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } -input DataKindPropositionInput { - equalTo: DataKind @cost(weight: "10") - notEqualTo: DataKind @cost(weight: "10") - in: [DataKind!] @cost(weight: "10") - notIn: [DataKind!] @cost(weight: "10") -} - input DataSortInput { id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") @@ -1143,6 +1163,36 @@ input DataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } +input DateTimePropositionInput { + equalTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") + notIn: [DateTime] @cost(weight: "10") + greaterThan: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + greaterThanOrEqualTo: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + lessThan: DateTime @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") + lessThanOrEqualTo: DateTime @cost(weight: "10") + notLessThanOrEqualTo: DateTime @cost(weight: "10") +} + +input DecimalPropositionInput { + equalTo: Decimal @cost(weight: "10") + notEqualTo: Decimal @cost(weight: "10") + in: [Decimal] @cost(weight: "10") + notIn: [Decimal] @cost(weight: "10") + greaterThan: Decimal @cost(weight: "10") + notGreaterThan: Decimal @cost(weight: "10") + greaterThanOrEqualTo: Decimal @cost(weight: "10") + notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + lessThan: Decimal @cost(weight: "10") + notLessThan: Decimal @cost(weight: "10") + lessThanOrEqualTo: Decimal @cost(weight: "10") + notLessThanOrEqualTo: Decimal @cost(weight: "10") +} + input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1170,11 +1220,60 @@ input FileMetaInformationPropositionInput { dataFormatId: UuidPropositionInput } +input FilterInputTypeOfCielabColorsPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfDataApprovalsPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfErasPropositionInput { + all: EraPropositionInput @cost(weight: "10") + none: EraPropositionInput @cost(weight: "10") + some: EraPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfFileMetaInformationsPropositionInput { + all: FileMetaInformationPropositionInput @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfGetHttpsResourcesPropositionInput { + all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfNamedMethodArgumentsPropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfNamedMethodSourcesPropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input FloatPropositionInput { equalTo: Float @cost(weight: "10") notEqualTo: Float @cost(weight: "10") - in: [Float!] @cost(weight: "10") - notIn: [Float!] @cost(weight: "10") + in: [Float] @cost(weight: "10") + notIn: [Float] @cost(weight: "10") greaterThan: Float @cost(weight: "10") notGreaterThan: Float @cost(weight: "10") greaterThanOrEqualTo: Float @cost(weight: "10") @@ -1183,6 +1282,7 @@ input FloatPropositionInput { notLessThan: Float @cost(weight: "10") lessThanOrEqualTo: Float @cost(weight: "10") notLessThanOrEqualTo: Float @cost(weight: "10") + inClosedInterval: ClosedIntervalInput @cost(weight: "10") } input FloatsPropositionInput { @@ -1196,7 +1296,7 @@ input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] or: [GeometricDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1204,8 +1304,8 @@ input GeometricDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput thicknesses: FloatsPropositionInput } @@ -1229,8 +1329,8 @@ input GetHttpsResourcePropositionInput { hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput @@ -1256,23 +1356,23 @@ input GetHttpsResourceTreeNonRootVertSortInput { parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } -input GetHttpsResourceTreeNonRootVertexFilterInput { - and: [GetHttpsResourceTreeNonRootVertexFilterInput!] - or: [GetHttpsResourceTreeNonRootVertexFilterInput!] +input GetHttpsResourceTreeNonRootVertexPropositionInput { + and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] + or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] id: UuidPropositionInput description: StringPropositionInput hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput } input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1280,8 +1380,8 @@ input HygrothermalDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1296,11 +1396,11 @@ input HygrothermalDataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } -input Int32PropositionInput { +input IntPropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") - in: [Int!] @cost(weight: "10") - notIn: [Int!] @cost(weight: "10") + in: [Int] @cost(weight: "10") + notIn: [Int] @cost(weight: "10") greaterThan: Int @cost(weight: "10") notGreaterThan: Int @cost(weight: "10") greaterThanOrEqualTo: Int @cost(weight: "10") @@ -1311,21 +1411,6 @@ input Int32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } -input Int64PropositionInput { - equalTo: Long @cost(weight: "10") - notEqualTo: Long @cost(weight: "10") - in: [Long!] @cost(weight: "10") - notIn: [Long!] @cost(weight: "10") - greaterThan: Long @cost(weight: "10") - notGreaterThan: Long @cost(weight: "10") - greaterThanOrEqualTo: Long @cost(weight: "10") - notGreaterThanOrEqualTo: Long @cost(weight: "10") - lessThan: Long @cost(weight: "10") - notLessThan: Long @cost(weight: "10") - lessThanOrEqualTo: Long @cost(weight: "10") - notLessThanOrEqualTo: Long @cost(weight: "10") -} - input IsoDayOfWeekPropositionInput { equalTo: IsoDayOfWeek @cost(weight: "10") notEqualTo: IsoDayOfWeek @cost(weight: "10") @@ -1351,95 +1436,39 @@ input KeyValuePairOfGuidAndNullableOfUInt32Input { value: NonNegativeInt } -input ListFilterInputTypeOfCielabColorPropositionInput { - all: CielabColorPropositionInput @cost(weight: "10") - none: CielabColorPropositionInput @cost(weight: "10") - some: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfDataApprovalPropositionInput { - all: DataApprovalPropositionInput @cost(weight: "10") - none: DataApprovalPropositionInput @cost(weight: "10") - some: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfEraPropositionInput { - all: EraPropositionInput @cost(weight: "10") - none: EraPropositionInput @cost(weight: "10") - some: EraPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfFileMetaInformationPropositionInput { - all: FileMetaInformationPropositionInput @cost(weight: "10") - none: FileMetaInformationPropositionInput @cost(weight: "10") - some: FileMetaInformationPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfGetHttpsResourcePropositionInput { - all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { - all: NamedMethodArgumentPropositionInput @cost(weight: "10") - none: NamedMethodArgumentPropositionInput @cost(weight: "10") - some: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListStandardizerPropositionInput { - all: StandardizerPropositionInput @cost(weight: "10") - none: StandardizerPropositionInput @cost(weight: "10") - some: StandardizerPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - input LocalDatePropositionInput { and: [LocalDatePropositionInput!] or: [LocalDatePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - month: Int32PropositionInput - day: Int32PropositionInput + year: IntPropositionInput + month: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: Int32PropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - dayOfYear: Int32PropositionInput + dayOfYear: IntPropositionInput } input LocalDateTimePropositionInput { and: [LocalDateTimePropositionInput!] or: [LocalDateTimePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - yearOfEra: Int32PropositionInput + year: IntPropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - month: Int32PropositionInput - dayOfYear: Int32PropositionInput - day: Int32PropositionInput + month: IntPropositionInput + dayOfYear: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput timeOfDay: LocalTimePropositionInput date: LocalDatePropositionInput } @@ -1447,15 +1476,30 @@ input LocalDateTimePropositionInput { input LocalTimePropositionInput { and: [LocalTimePropositionInput!] or: [LocalTimePropositionInput!] - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput +} + +input LongPropositionInput { + equalTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") + notIn: [Long] @cost(weight: "10") + greaterThan: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + greaterThanOrEqualTo: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + lessThan: Long @cost(weight: "10") + notLessThan: Long @cost(weight: "10") + lessThanOrEqualTo: Long @cost(weight: "10") + notLessThanOrEqualTo: Long @cost(weight: "10") } input NamedMethodArgumentInput { @@ -1506,21 +1550,6 @@ input NullableOfIlluminantPropositionInput { notIn: [Illuminant] @cost(weight: "10") } -input NullableOfInt32PropositionInput { - equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - notIn: [Int] @cost(weight: "10") - greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") - greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") - lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") - lessThanOrEqualTo: Int @cost(weight: "10") - notLessThanOrEqualTo: Int @cost(weight: "10") -} - input NullableOfOpticalComponentSubtypePropositionInput { equalTo: OpticalComponentSubtype @cost(weight: "10") notEqualTo: OpticalComponentSubtype @cost(weight: "10") @@ -1535,21 +1564,6 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } -input NullableOfUuidPropositionInput { - equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid] @cost(weight: "10") - notIn: [Uuid] @cost(weight: "10") - greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") - greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") - lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") - lessThanOrEqualTo: Uuid @cost(weight: "10") - notLessThanOrEqualTo: Uuid @cost(weight: "10") -} - input NumerationInput { mainNumber: String! prefix: String @@ -1574,22 +1588,22 @@ input OffsetDateTimePropositionInput { and: [OffsetDateTimePropositionInput!] or: [OffsetDateTimePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - month: Int32PropositionInput - day: Int32PropositionInput + year: IntPropositionInput + month: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: Int32PropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - dayOfYear: Int32PropositionInput - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + dayOfYear: IntPropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput localDateTime: LocalDateTimePropositionInput date: LocalDatePropositionInput timeOfDay: LocalTimePropositionInput @@ -1599,17 +1613,17 @@ input OffsetDateTimePropositionInput { input OffsetPropositionInput { and: [OffsetPropositionInput!] or: [OffsetPropositionInput!] - seconds: Int32PropositionInput - milliseconds: Int32PropositionInput - ticks: Int64PropositionInput - nanoseconds: Int64PropositionInput + seconds: IntPropositionInput + milliseconds: IntPropositionInput + ticks: LongPropositionInput + nanoseconds: LongPropositionInput } input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1617,8 +1631,8 @@ input OpticalDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput type: NullableOfOpticalComponentTypePropositionInput subtype: NullableOfOpticalComponentSubtypePropositionInput @@ -1629,7 +1643,7 @@ input OpticalDataPropositionInput { nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput infraredEmittances: FloatsPropositionInput colorRenderingIndices: FloatsPropositionInput - cielabColors: ListFilterInputTypeOfCielabColorPropositionInput + cielabColors: FilterInputTypeOfCielabColorsPropositionInput } input OpticalDataSortInput { @@ -1647,7 +1661,7 @@ input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] or: [PhotovoltaicDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1655,8 +1669,8 @@ input PhotovoltaicDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1693,7 +1707,7 @@ input PublicationPropositionInput { "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: StringPropositionInput "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: UriPropositionInput + webAddress: UrlPropositionInput title: StringPropositionInput abstract: StringPropositionInput "Referenced section" @@ -1720,16 +1734,16 @@ input PublishDataInput { dataKind: DataKind! } -input RecomputeGetHttpsResourceHashValuesFilterInput { - and: [RecomputeGetHttpsResourceHashValuesFilterInput!] - or: [RecomputeGetHttpsResourceHashValuesFilterInput!] +input RecomputeGetHttpsResourceHashValuesPropositionInput { + and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] id: UuidPropositionInput description: StringPropositionInput hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput @@ -1777,6 +1791,21 @@ input SetGetHttpsResourceParentInput { parentId: Uuid! } +input ShortPropositionInput { + equalTo: Short @cost(weight: "10") + notEqualTo: Short @cost(weight: "10") + in: [Short] @cost(weight: "10") + notIn: [Short] @cost(weight: "10") + greaterThan: Short @cost(weight: "10") + notGreaterThan: Short @cost(weight: "10") + greaterThanOrEqualTo: Short @cost(weight: "10") + notGreaterThanOrEqualTo: Short @cost(weight: "10") + lessThan: Short @cost(weight: "10") + notLessThan: Short @cost(weight: "10") + lessThanOrEqualTo: Short @cost(weight: "10") + notLessThanOrEqualTo: Short @cost(weight: "10") +} + input StandardInput { abstract: String locator: Url @@ -1792,10 +1821,10 @@ input StandardPropositionInput { and: [StandardPropositionInput!] or: [StandardPropositionInput!] "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: NullableOfInt32PropositionInput + year: IntPropositionInput numeration: NumerationPropositionInput - standardizers: ListStandardizerPropositionInput - locator: UriPropositionInput + standardizers: StandardizersPropositionInput + locator: UrlPropositionInput title: StringPropositionInput abstract: StringPropositionInput "The section of the standard to which the reference refers to." @@ -1821,6 +1850,13 @@ input StandardizerPropositionInput { notIn: [Standardizer!] @cost(weight: "10") } +input StandardizersPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input StringPropositionInput { and: [StringPropositionInput!] or: [StringPropositionInput!] @@ -1843,6 +1879,21 @@ input StringsPropositionInput { any: Boolean @cost(weight: "10") } +input TimeSpanPropositionInput { + equalTo: TimeSpan @cost(weight: "10") + notEqualTo: TimeSpan @cost(weight: "10") + in: [TimeSpan] @cost(weight: "10") + notIn: [TimeSpan] @cost(weight: "10") + greaterThan: TimeSpan @cost(weight: "10") + notGreaterThan: TimeSpan @cost(weight: "10") + greaterThanOrEqualTo: TimeSpan @cost(weight: "10") + notGreaterThanOrEqualTo: TimeSpan @cost(weight: "10") + lessThan: TimeSpan @cost(weight: "10") + notLessThan: TimeSpan @cost(weight: "10") + lessThanOrEqualTo: TimeSpan @cost(weight: "10") + notLessThanOrEqualTo: TimeSpan @cost(weight: "10") +} + input ToTreeVertexAppliedConversionMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -1853,7 +1904,7 @@ input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] or: [ToTreeVertexAppliedConversionMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput + arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput sourceName: StringPropositionInput } @@ -1904,11 +1955,11 @@ input UpdateInstitutionAccessRightsInput { periodInDays: Int! } -input UpdateResponseApprovalsFilterInput { - and: [UpdateResponseApprovalsFilterInput!] - or: [UpdateResponseApprovalsFilterInput!] +input UpdateResponseApprovalsPropositionInput { + and: [UpdateResponseApprovalsPropositionInput!] + or: [UpdateResponseApprovalsPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1916,8 +1967,8 @@ input UpdateResponseApprovalsFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1928,40 +1979,6 @@ input UpdateRootGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input UriHostNameTypePropositionInput { - equalTo: UriHostNameType @cost(weight: "10") - notEqualTo: UriHostNameType @cost(weight: "10") - in: [UriHostNameType!] @cost(weight: "10") - notIn: [UriHostNameType!] @cost(weight: "10") -} - -input UriPropositionInput { - and: [UriPropositionInput!] - or: [UriPropositionInput!] - absolutePath: StringPropositionInput - absoluteUri: StringPropositionInput - localPath: StringPropositionInput - authority: StringPropositionInput - hostNameType: UriHostNameTypePropositionInput - isDefaultPort: BooleanPropositionInput - isFile: BooleanPropositionInput - isLoopback: BooleanPropositionInput - pathAndQuery: StringPropositionInput - segments: StringsPropositionInput - isUnc: BooleanPropositionInput - host: StringPropositionInput - port: Int32PropositionInput - query: StringPropositionInput - fragment: StringPropositionInput - scheme: StringPropositionInput - originalString: StringPropositionInput - dnsSafeHost: StringPropositionInput - idnHost: StringPropositionInput - isAbsoluteUri: BooleanPropositionInput - userEscaped: BooleanPropositionInput - userInfo: StringPropositionInput -} - input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -1986,11 +2003,26 @@ input UriSortInput { userInfo: SortEnumType @cost(weight: "10") } +input UrlPropositionInput { + equalTo: Url @cost(weight: "10") + notEqualTo: Url @cost(weight: "10") + in: [Url] @cost(weight: "10") + notIn: [Url] @cost(weight: "10") + greaterThan: Url @cost(weight: "10") + notGreaterThan: Url @cost(weight: "10") + greaterThanOrEqualTo: Url @cost(weight: "10") + notGreaterThanOrEqualTo: Url @cost(weight: "10") + lessThan: Url @cost(weight: "10") + notLessThan: Url @cost(weight: "10") + lessThanOrEqualTo: Url @cost(weight: "10") + notLessThanOrEqualTo: Url @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") notEqualTo: Uuid @cost(weight: "10") - in: [Uuid!] @cost(weight: "10") - notIn: [Uuid!] @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") notGreaterThan: Uuid @cost(weight: "10") greaterThanOrEqualTo: Uuid @cost(weight: "10") @@ -2366,14 +2398,6 @@ enum UpdateRootGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UriHostNameType { - UNKNOWN - BASIC - DNS - I_PV4 - I_PV6 -} - "The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION @@ -2385,6 +2409,9 @@ directive @specifiedBy("The specifiedBy URL points to a human-readable specifica scalar Any +"The `Byte` scalar type represents non-fractional whole numeric values. Byte can represent values between 0 and 255." +scalar Byte + "The `DateTime` scalar represents an ISO-8601 compliant date time type." scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") @@ -2396,6 +2423,9 @@ Example: `Europe/Zurich` """ scalar DateTimeZone +"The `Decimal` scalar type represents a decimal floating-point number." +scalar Decimal + """ Represents a fixed (and calendar-independent) length of time. @@ -2416,6 +2446,12 @@ scalar Long "The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." scalar NonNegativeInt +"The `Short` scalar type represents non-fractional signed whole 16-bit numeric values. Short can represent values between -(2^15) and 2^15 - 1." +scalar Short + +"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." +scalar TimeSpan + "The `Upload` scalar type represents a file upload." scalar Upload diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 032bd601..1b794b4f 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -396,7 +396,7 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -485,11 +485,11 @@ type Mutation { createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals(where: CreateResponseApprovalsFilterInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") + createResponseApprovals(where: CreateResponseApprovalsPropositionInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesFilterInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") @@ -498,7 +498,7 @@ type Mutation { updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals(where: UpdateResponseApprovalsFilterInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateResponseApprovals(where: UpdateResponseApprovalsPropositionInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") } @@ -906,8 +906,8 @@ input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] or: [AppliedMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput - sources: ListFilterInputTypeOfNamedMethodSourcePropositionInput + arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + sources: FilterInputTypeOfNamedMethodSourcesPropositionInput } input AppliedMethodSortInput { @@ -919,21 +919,36 @@ input BooleanPropositionInput { notEqualTo: Boolean @cost(weight: "10") } +input BytePropositionInput { + equalTo: Byte @cost(weight: "10") + notEqualTo: Byte @cost(weight: "10") + in: [Byte] @cost(weight: "10") + notIn: [Byte] @cost(weight: "10") + greaterThan: Byte @cost(weight: "10") + notGreaterThan: Byte @cost(weight: "10") + greaterThanOrEqualTo: Byte @cost(weight: "10") + notGreaterThanOrEqualTo: Byte @cost(weight: "10") + lessThan: Byte @cost(weight: "10") + notLessThan: Byte @cost(weight: "10") + lessThanOrEqualTo: Byte @cost(weight: "10") + notLessThanOrEqualTo: Byte @cost(weight: "10") +} + input CalendarSystemPropositionInput { and: [CalendarSystemPropositionInput!] or: [CalendarSystemPropositionInput!] id: StringPropositionInput name: StringPropositionInput - minYear: Int32PropositionInput - maxYear: Int32PropositionInput - eras: ListFilterInputTypeOfEraPropositionInput + minYear: IntPropositionInput + maxYear: IntPropositionInput + eras: FilterInputTypeOfErasPropositionInput } input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] or: [CalorimetricDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -941,8 +956,8 @@ input CalorimetricDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput gValues: FloatsPropositionInput uValues: FloatsPropositionInput @@ -977,6 +992,11 @@ input CielabColorPropositionInput { illuminant: NullableOfIlluminantPropositionInput } +input ClosedIntervalInput { + lowerBound: Float! + upperBound: Float! +} + input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! componentId: Uuid! @@ -1062,11 +1082,11 @@ input CreatePhotovoltaicDataInput { warnings: [String!]! } -input CreateResponseApprovalsFilterInput { - and: [CreateResponseApprovalsFilterInput!] - or: [CreateResponseApprovalsFilterInput!] +input CreateResponseApprovalsPropositionInput { + and: [CreateResponseApprovalsPropositionInput!] + or: [CreateResponseApprovalsPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1074,8 +1094,8 @@ input CreateResponseApprovalsFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1108,11 +1128,18 @@ input DataApprovalPropositionInput { statement: ReferencePropositionInput } -input DataFilterInput { - and: [DataFilterInput!] - or: [DataFilterInput!] +input DataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + in: [DataKind!] @cost(weight: "10") + notIn: [DataKind!] @cost(weight: "10") +} + +input DataPropositionInput { + and: [DataPropositionInput!] + or: [DataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1120,18 +1147,11 @@ input DataFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } -input DataKindPropositionInput { - equalTo: DataKind @cost(weight: "10") - notEqualTo: DataKind @cost(weight: "10") - in: [DataKind!] @cost(weight: "10") - notIn: [DataKind!] @cost(weight: "10") -} - input DataSortInput { id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") @@ -1143,6 +1163,36 @@ input DataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } +input DateTimePropositionInput { + equalTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") + notIn: [DateTime] @cost(weight: "10") + greaterThan: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + greaterThanOrEqualTo: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + lessThan: DateTime @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") + lessThanOrEqualTo: DateTime @cost(weight: "10") + notLessThanOrEqualTo: DateTime @cost(weight: "10") +} + +input DecimalPropositionInput { + equalTo: Decimal @cost(weight: "10") + notEqualTo: Decimal @cost(weight: "10") + in: [Decimal] @cost(weight: "10") + notIn: [Decimal] @cost(weight: "10") + greaterThan: Decimal @cost(weight: "10") + notGreaterThan: Decimal @cost(weight: "10") + greaterThanOrEqualTo: Decimal @cost(weight: "10") + notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + lessThan: Decimal @cost(weight: "10") + notLessThan: Decimal @cost(weight: "10") + lessThanOrEqualTo: Decimal @cost(weight: "10") + notLessThanOrEqualTo: Decimal @cost(weight: "10") +} + input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1170,11 +1220,60 @@ input FileMetaInformationPropositionInput { dataFormatId: UuidPropositionInput } +input FilterInputTypeOfCielabColorsPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfDataApprovalsPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfErasPropositionInput { + all: EraPropositionInput @cost(weight: "10") + none: EraPropositionInput @cost(weight: "10") + some: EraPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfFileMetaInformationsPropositionInput { + all: FileMetaInformationPropositionInput @cost(weight: "10") + none: FileMetaInformationPropositionInput @cost(weight: "10") + some: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfGetHttpsResourcesPropositionInput { + all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfNamedMethodArgumentsPropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + +input FilterInputTypeOfNamedMethodSourcesPropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input FloatPropositionInput { equalTo: Float @cost(weight: "10") notEqualTo: Float @cost(weight: "10") - in: [Float!] @cost(weight: "10") - notIn: [Float!] @cost(weight: "10") + in: [Float] @cost(weight: "10") + notIn: [Float] @cost(weight: "10") greaterThan: Float @cost(weight: "10") notGreaterThan: Float @cost(weight: "10") greaterThanOrEqualTo: Float @cost(weight: "10") @@ -1183,6 +1282,7 @@ input FloatPropositionInput { notLessThan: Float @cost(weight: "10") lessThanOrEqualTo: Float @cost(weight: "10") notLessThanOrEqualTo: Float @cost(weight: "10") + inClosedInterval: ClosedIntervalInput @cost(weight: "10") } input FloatsPropositionInput { @@ -1196,7 +1296,7 @@ input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] or: [GeometricDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1204,8 +1304,8 @@ input GeometricDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput thicknesses: FloatsPropositionInput } @@ -1229,8 +1329,8 @@ input GetHttpsResourcePropositionInput { hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput @@ -1256,23 +1356,23 @@ input GetHttpsResourceTreeNonRootVertSortInput { parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") } -input GetHttpsResourceTreeNonRootVertexFilterInput { - and: [GetHttpsResourceTreeNonRootVertexFilterInput!] - or: [GetHttpsResourceTreeNonRootVertexFilterInput!] +input GetHttpsResourceTreeNonRootVertexPropositionInput { + and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] + or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] id: UuidPropositionInput description: StringPropositionInput hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput } input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] or: [HygrothermalDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1280,8 +1380,8 @@ input HygrothermalDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1296,11 +1396,11 @@ input HygrothermalDataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") } -input Int32PropositionInput { +input IntPropositionInput { equalTo: Int @cost(weight: "10") notEqualTo: Int @cost(weight: "10") - in: [Int!] @cost(weight: "10") - notIn: [Int!] @cost(weight: "10") + in: [Int] @cost(weight: "10") + notIn: [Int] @cost(weight: "10") greaterThan: Int @cost(weight: "10") notGreaterThan: Int @cost(weight: "10") greaterThanOrEqualTo: Int @cost(weight: "10") @@ -1311,21 +1411,6 @@ input Int32PropositionInput { notLessThanOrEqualTo: Int @cost(weight: "10") } -input Int64PropositionInput { - equalTo: Long @cost(weight: "10") - notEqualTo: Long @cost(weight: "10") - in: [Long!] @cost(weight: "10") - notIn: [Long!] @cost(weight: "10") - greaterThan: Long @cost(weight: "10") - notGreaterThan: Long @cost(weight: "10") - greaterThanOrEqualTo: Long @cost(weight: "10") - notGreaterThanOrEqualTo: Long @cost(weight: "10") - lessThan: Long @cost(weight: "10") - notLessThan: Long @cost(weight: "10") - lessThanOrEqualTo: Long @cost(weight: "10") - notLessThanOrEqualTo: Long @cost(weight: "10") -} - input IsoDayOfWeekPropositionInput { equalTo: IsoDayOfWeek @cost(weight: "10") notEqualTo: IsoDayOfWeek @cost(weight: "10") @@ -1351,95 +1436,39 @@ input KeyValuePairOfGuidAndNullableOfUInt32Input { value: NonNegativeInt } -input ListFilterInputTypeOfCielabColorPropositionInput { - all: CielabColorPropositionInput @cost(weight: "10") - none: CielabColorPropositionInput @cost(weight: "10") - some: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfDataApprovalPropositionInput { - all: DataApprovalPropositionInput @cost(weight: "10") - none: DataApprovalPropositionInput @cost(weight: "10") - some: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfEraPropositionInput { - all: EraPropositionInput @cost(weight: "10") - none: EraPropositionInput @cost(weight: "10") - some: EraPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfFileMetaInformationPropositionInput { - all: FileMetaInformationPropositionInput @cost(weight: "10") - none: FileMetaInformationPropositionInput @cost(weight: "10") - some: FileMetaInformationPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfGetHttpsResourcePropositionInput { - all: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - none: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - some: GetHttpsResourceTreeNonRootVertexFilterInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodArgumentPropositionInput { - all: NamedMethodArgumentPropositionInput @cost(weight: "10") - none: NamedMethodArgumentPropositionInput @cost(weight: "10") - some: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListFilterInputTypeOfNamedMethodSourcePropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input ListStandardizerPropositionInput { - all: StandardizerPropositionInput @cost(weight: "10") - none: StandardizerPropositionInput @cost(weight: "10") - some: StandardizerPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - input LocalDatePropositionInput { and: [LocalDatePropositionInput!] or: [LocalDatePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - month: Int32PropositionInput - day: Int32PropositionInput + year: IntPropositionInput + month: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: Int32PropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - dayOfYear: Int32PropositionInput + dayOfYear: IntPropositionInput } input LocalDateTimePropositionInput { and: [LocalDateTimePropositionInput!] or: [LocalDateTimePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - yearOfEra: Int32PropositionInput + year: IntPropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - month: Int32PropositionInput - dayOfYear: Int32PropositionInput - day: Int32PropositionInput + month: IntPropositionInput + dayOfYear: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput timeOfDay: LocalTimePropositionInput date: LocalDatePropositionInput } @@ -1447,15 +1476,30 @@ input LocalDateTimePropositionInput { input LocalTimePropositionInput { and: [LocalTimePropositionInput!] or: [LocalTimePropositionInput!] - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput +} + +input LongPropositionInput { + equalTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") + notIn: [Long] @cost(weight: "10") + greaterThan: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + greaterThanOrEqualTo: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + lessThan: Long @cost(weight: "10") + notLessThan: Long @cost(weight: "10") + lessThanOrEqualTo: Long @cost(weight: "10") + notLessThanOrEqualTo: Long @cost(weight: "10") } input NamedMethodArgumentInput { @@ -1506,21 +1550,6 @@ input NullableOfIlluminantPropositionInput { notIn: [Illuminant] @cost(weight: "10") } -input NullableOfInt32PropositionInput { - equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - notIn: [Int] @cost(weight: "10") - greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") - greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") - lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") - lessThanOrEqualTo: Int @cost(weight: "10") - notLessThanOrEqualTo: Int @cost(weight: "10") -} - input NullableOfOpticalComponentSubtypePropositionInput { equalTo: OpticalComponentSubtype @cost(weight: "10") notEqualTo: OpticalComponentSubtype @cost(weight: "10") @@ -1535,21 +1564,6 @@ input NullableOfOpticalComponentTypePropositionInput { notIn: [OpticalComponentType] @cost(weight: "10") } -input NullableOfUuidPropositionInput { - equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid] @cost(weight: "10") - notIn: [Uuid] @cost(weight: "10") - greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") - greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") - lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") - lessThanOrEqualTo: Uuid @cost(weight: "10") - notLessThanOrEqualTo: Uuid @cost(weight: "10") -} - input NumerationInput { mainNumber: String! prefix: String @@ -1574,22 +1588,22 @@ input OffsetDateTimePropositionInput { and: [OffsetDateTimePropositionInput!] or: [OffsetDateTimePropositionInput!] calendar: CalendarSystemPropositionInput - year: Int32PropositionInput - month: Int32PropositionInput - day: Int32PropositionInput + year: IntPropositionInput + month: IntPropositionInput + day: IntPropositionInput dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: Int32PropositionInput + yearOfEra: IntPropositionInput era: EraPropositionInput - dayOfYear: Int32PropositionInput - hour: Int32PropositionInput - clockHourOfHalfDay: Int32PropositionInput - minute: Int32PropositionInput - second: Int32PropositionInput - millisecond: Int32PropositionInput - tickOfSecond: Int32PropositionInput - tickOfDay: Int64PropositionInput - nanosecondOfSecond: Int32PropositionInput - nanosecondOfDay: Int64PropositionInput + dayOfYear: IntPropositionInput + hour: IntPropositionInput + clockHourOfHalfDay: IntPropositionInput + minute: IntPropositionInput + second: IntPropositionInput + millisecond: IntPropositionInput + tickOfSecond: IntPropositionInput + tickOfDay: LongPropositionInput + nanosecondOfSecond: IntPropositionInput + nanosecondOfDay: LongPropositionInput localDateTime: LocalDateTimePropositionInput date: LocalDatePropositionInput timeOfDay: LocalTimePropositionInput @@ -1599,17 +1613,17 @@ input OffsetDateTimePropositionInput { input OffsetPropositionInput { and: [OffsetPropositionInput!] or: [OffsetPropositionInput!] - seconds: Int32PropositionInput - milliseconds: Int32PropositionInput - ticks: Int64PropositionInput - nanoseconds: Int64PropositionInput + seconds: IntPropositionInput + milliseconds: IntPropositionInput + ticks: LongPropositionInput + nanoseconds: LongPropositionInput } input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] or: [OpticalDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1617,8 +1631,8 @@ input OpticalDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput type: NullableOfOpticalComponentTypePropositionInput subtype: NullableOfOpticalComponentSubtypePropositionInput @@ -1629,7 +1643,7 @@ input OpticalDataPropositionInput { nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput infraredEmittances: FloatsPropositionInput colorRenderingIndices: FloatsPropositionInput - cielabColors: ListFilterInputTypeOfCielabColorPropositionInput + cielabColors: FilterInputTypeOfCielabColorsPropositionInput } input OpticalDataSortInput { @@ -1647,7 +1661,7 @@ input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] or: [PhotovoltaicDataPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1655,8 +1669,8 @@ input PhotovoltaicDataPropositionInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1693,7 +1707,7 @@ input PublicationPropositionInput { "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: StringPropositionInput "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: UriPropositionInput + webAddress: UrlPropositionInput title: StringPropositionInput abstract: StringPropositionInput "Referenced section" @@ -1720,16 +1734,16 @@ input PublishDataInput { dataKind: DataKind! } -input RecomputeGetHttpsResourceHashValuesFilterInput { - and: [RecomputeGetHttpsResourceHashValuesFilterInput!] - or: [RecomputeGetHttpsResourceHashValuesFilterInput!] +input RecomputeGetHttpsResourceHashValuesPropositionInput { + and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] id: UuidPropositionInput description: StringPropositionInput hashValue: StringPropositionInput dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: ListFilterInputTypeOfFileMetaInformationPropositionInput - parent: GetHttpsResourceTreeNonRootVertexFilterInput + archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput @@ -1777,6 +1791,21 @@ input SetGetHttpsResourceParentInput { parentId: Uuid! } +input ShortPropositionInput { + equalTo: Short @cost(weight: "10") + notEqualTo: Short @cost(weight: "10") + in: [Short] @cost(weight: "10") + notIn: [Short] @cost(weight: "10") + greaterThan: Short @cost(weight: "10") + notGreaterThan: Short @cost(weight: "10") + greaterThanOrEqualTo: Short @cost(weight: "10") + notGreaterThanOrEqualTo: Short @cost(weight: "10") + lessThan: Short @cost(weight: "10") + notLessThan: Short @cost(weight: "10") + lessThanOrEqualTo: Short @cost(weight: "10") + notLessThanOrEqualTo: Short @cost(weight: "10") +} + input StandardInput { abstract: String locator: Url @@ -1792,10 +1821,10 @@ input StandardPropositionInput { and: [StandardPropositionInput!] or: [StandardPropositionInput!] "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: NullableOfInt32PropositionInput + year: IntPropositionInput numeration: NumerationPropositionInput - standardizers: ListStandardizerPropositionInput - locator: UriPropositionInput + standardizers: StandardizersPropositionInput + locator: UrlPropositionInput title: StringPropositionInput abstract: StringPropositionInput "The section of the standard to which the reference refers to." @@ -1821,6 +1850,13 @@ input StandardizerPropositionInput { notIn: [Standardizer!] @cost(weight: "10") } +input StandardizersPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") +} + input StringPropositionInput { and: [StringPropositionInput!] or: [StringPropositionInput!] @@ -1843,6 +1879,21 @@ input StringsPropositionInput { any: Boolean @cost(weight: "10") } +input TimeSpanPropositionInput { + equalTo: TimeSpan @cost(weight: "10") + notEqualTo: TimeSpan @cost(weight: "10") + in: [TimeSpan] @cost(weight: "10") + notIn: [TimeSpan] @cost(weight: "10") + greaterThan: TimeSpan @cost(weight: "10") + notGreaterThan: TimeSpan @cost(weight: "10") + greaterThanOrEqualTo: TimeSpan @cost(weight: "10") + notGreaterThanOrEqualTo: TimeSpan @cost(weight: "10") + lessThan: TimeSpan @cost(weight: "10") + notLessThan: TimeSpan @cost(weight: "10") + lessThanOrEqualTo: TimeSpan @cost(weight: "10") + notLessThanOrEqualTo: TimeSpan @cost(weight: "10") +} + input ToTreeVertexAppliedConversionMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -1853,7 +1904,7 @@ input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] or: [ToTreeVertexAppliedConversionMethodPropositionInput!] methodId: UuidPropositionInput - arguments: ListFilterInputTypeOfNamedMethodArgumentPropositionInput + arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput sourceName: StringPropositionInput } @@ -1904,11 +1955,11 @@ input UpdateInstitutionAccessRightsInput { periodInDays: Int! } -input UpdateResponseApprovalsFilterInput { - and: [UpdateResponseApprovalsFilterInput!] - or: [UpdateResponseApprovalsFilterInput!] +input UpdateResponseApprovalsPropositionInput { + and: [UpdateResponseApprovalsPropositionInput!] + or: [UpdateResponseApprovalsPropositionInput!] id: UuidPropositionInput - userId: NullableOfUuidPropositionInput + userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput description: StringPropositionInput @@ -1916,8 +1967,8 @@ input UpdateResponseApprovalsFilterInput { creatorId: UuidPropositionInput createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput - approvals: ListFilterInputTypeOfDataApprovalPropositionInput - resources: ListFilterInputTypeOfGetHttpsResourcePropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput warnings: StringsPropositionInput } @@ -1928,40 +1979,6 @@ input UpdateRootGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input UriHostNameTypePropositionInput { - equalTo: UriHostNameType @cost(weight: "10") - notEqualTo: UriHostNameType @cost(weight: "10") - in: [UriHostNameType!] @cost(weight: "10") - notIn: [UriHostNameType!] @cost(weight: "10") -} - -input UriPropositionInput { - and: [UriPropositionInput!] - or: [UriPropositionInput!] - absolutePath: StringPropositionInput - absoluteUri: StringPropositionInput - localPath: StringPropositionInput - authority: StringPropositionInput - hostNameType: UriHostNameTypePropositionInput - isDefaultPort: BooleanPropositionInput - isFile: BooleanPropositionInput - isLoopback: BooleanPropositionInput - pathAndQuery: StringPropositionInput - segments: StringsPropositionInput - isUnc: BooleanPropositionInput - host: StringPropositionInput - port: Int32PropositionInput - query: StringPropositionInput - fragment: StringPropositionInput - scheme: StringPropositionInput - originalString: StringPropositionInput - dnsSafeHost: StringPropositionInput - idnHost: StringPropositionInput - isAbsoluteUri: BooleanPropositionInput - userEscaped: BooleanPropositionInput - userInfo: StringPropositionInput -} - input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -1986,11 +2003,26 @@ input UriSortInput { userInfo: SortEnumType @cost(weight: "10") } +input UrlPropositionInput { + equalTo: Url @cost(weight: "10") + notEqualTo: Url @cost(weight: "10") + in: [Url] @cost(weight: "10") + notIn: [Url] @cost(weight: "10") + greaterThan: Url @cost(weight: "10") + notGreaterThan: Url @cost(weight: "10") + greaterThanOrEqualTo: Url @cost(weight: "10") + notGreaterThanOrEqualTo: Url @cost(weight: "10") + lessThan: Url @cost(weight: "10") + notLessThan: Url @cost(weight: "10") + lessThanOrEqualTo: Url @cost(weight: "10") + notLessThanOrEqualTo: Url @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") notEqualTo: Uuid @cost(weight: "10") - in: [Uuid!] @cost(weight: "10") - notIn: [Uuid!] @cost(weight: "10") + in: [Uuid] @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") notGreaterThan: Uuid @cost(weight: "10") greaterThanOrEqualTo: Uuid @cost(weight: "10") @@ -2366,14 +2398,6 @@ enum UpdateRootGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UriHostNameType { - UNKNOWN - BASIC - DNS - I_PV4 - I_PV6 -} - "The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION @@ -2385,6 +2409,9 @@ directive @specifiedBy("The specifiedBy URL points to a human-readable specifica scalar Any +"The `Byte` scalar type represents non-fractional whole numeric values. Byte can represent values between 0 and 255." +scalar Byte + "The `DateTime` scalar represents an ISO-8601 compliant date time type." scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") @@ -2396,6 +2423,9 @@ Example: `Europe/Zurich` """ scalar DateTimeZone +"The `Decimal` scalar type represents a decimal floating-point number." +scalar Decimal + """ Represents a fixed (and calendar-independent) length of time. @@ -2416,6 +2446,12 @@ scalar Long "The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." scalar NonNegativeInt +"The `Short` scalar type represents non-fractional signed whole 16-bit numeric values. Short can represent values between -(2^15) and 2^15 - 1." +scalar Short + +"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." +scalar TimeSpan + "The `Upload` scalar type represents a file upload." scalar Upload From db60ebf3fd641d03ca2fafdce2552a3d2c378c05 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 15 Feb 2026 22:32:05 +0100 Subject: [PATCH 725/878] Correct flag name and add scalars --- database.mk | 2 +- docker.mk | 2 +- frontend/codegen.ts | 7 +++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/database.mk b/database.mk index dd513965..be2c41dc 100755 --- a/database.mk +++ b/database.mk @@ -82,7 +82,7 @@ migrate : SCRIPT = ./backend/src/Migrations/migrate.sql migrate : ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql $(MAKE) --file="${SELF}" sql SCRIPT="${SCRIPT}" docker compose restart \ - --do-deps \ + --no-deps \ backend .PHONY : migrate diff --git a/docker.mk b/docker.mk index 1267fae3..b68e732a 100755 --- a/docker.mk +++ b/docker.mk @@ -105,7 +105,7 @@ down : ## Stop services and remove services and networks created by `up` restart : ## Restart services (and await their health), for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` docker compose restart \ - --do-deps ${SERVICE} + --no-deps ${SERVICE} docker compose up \ --wait ${SERVICE} .PHONY : restart diff --git a/frontend/codegen.ts b/frontend/codegen.ts index 5965ba2a..9e23c42d 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -44,13 +44,16 @@ const config: CodegenConfig = { Byte: "number", DateTime: "string", DateTimeZone: "string", + Decimal: "string", Duration: "string", Locale: "string", - NonNegativeInt: "number", Long: "number", + NonNegativeInt: "number", + Short: "number", + TimeSpan: "string", + Upload: "unknown", Url: "string", Uuid: "string", - Upload: "unknown", }, useTypeImports: true, // immutableTypes: true, From c91cb0fb59b24f035f7ce71f16b17d5abad00f0a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Feb 2026 16:50:42 +0100 Subject: [PATCH 726/878] Mention changes to JWT certificates and GnuPG keys management and to forging images locally and deploying them on the server --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46d52d5e..38c7cdcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,8 +41,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) - Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) - Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) -- -- +- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make bootstrap` and stored in the volume `gnupg`. +- Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. - - - From 2a5c8f894ff66aedde000a132ab46018329529e2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Feb 2026 18:22:54 +0100 Subject: [PATCH 727/878] Use the built-in Index method (instead of the custom WithIndex extension) --- backend/src/Extensions/EnumerableExtensions.cs | 7 ------- backend/src/GraphQl/DataMutationBase.cs | 4 ++-- backend/src/GraphQl/DataX/CreateDataMutationBase.cs | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/backend/src/Extensions/EnumerableExtensions.cs b/backend/src/Extensions/EnumerableExtensions.cs index 423b17df..c7143af6 100644 --- a/backend/src/Extensions/EnumerableExtensions.cs +++ b/backend/src/Extensions/EnumerableExtensions.cs @@ -6,13 +6,6 @@ namespace Database.Extensions; public static class EnumerableExtensions { - // An alternative would be `Index` from https://github.com/morelinq/MoreLINQ - [Pure] - public static IEnumerable<(T item, int index)> WithIndex(this IEnumerable enumerable) - { - return enumerable.Select((item, index) => (item, index)); - } - [Pure] public static IEnumerable NotNull(this IEnumerable enumerable) where T : class { diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index e1382891..8b01d7a1 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -90,7 +90,7 @@ CancellationToken cancellationToken var validateDataFormats = new Task[1 + input.ArchivedFilesMetaInformation.Count]; // First eagerly execute all `LoadAsync`s on data loaders to make it know all keys. validateDataFormats[0] = dataFormatByIdDataLoader.LoadAsync(input.DataFormatId, cancellationToken); - foreach (var (archivedFileMetaInformation, index) in input.ArchivedFilesMetaInformation.WithIndex()) + foreach (var (index, archivedFileMetaInformation) in input.ArchivedFilesMetaInformation.Index()) { validateDataFormats[index + 1] = dataFormatByIdDataLoader.LoadAsync(archivedFileMetaInformation.DataFormatId, cancellationToken); } @@ -109,7 +109,7 @@ CancellationToken cancellationToken ) ); } - foreach (var (archivedFileDataFormat, index) in dataFormats[1..].WithIndex()) + foreach (var (index, archivedFileDataFormat) in dataFormats[1..].Index()) { if (archivedFileDataFormat is null) { diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 07d6edcd..378f9927 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -96,7 +96,7 @@ CancellationToken cancellationToken ) .ToImmutableArray() // Execute eagerly to have the data loader collect all keys first. ); - foreach (var (database, index) in databases.WithIndex()) + foreach (var (index, database) in databases.Index()) { string[] valuePath = [ nameof(input), From d9f5c719225635013e0ad286f9f2e7497c41de56 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Feb 2026 19:03:50 +0100 Subject: [PATCH 728/878] Improve implemention of `InClosedInterval` --- .../src/Configuration/GraphQlConfiguration.cs | 40 ++++++++++++------- ...yableComparableInClosedIntervalHandler.cs} | 30 +++++++------- 2 files changed, 42 insertions(+), 28 deletions(-) rename backend/src/GraphQl/Filters/{QueryableFloatInClosedIntervalHandler.cs => QueryableComparableInClosedIntervalHandler.cs} (55%) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 67ef6674..4c06d47b 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,5 +1,4 @@ using System; -using System.Text.RegularExpressions; using Database.Authentication; using Database.Data; using Database.GraphQl; @@ -196,7 +195,7 @@ protected override void Configure(IFilterConventionDescriptor descriptor) descriptor.Provider( new QueryableFilterProvider(_ => _ .AddDefaultFieldHandlers() - .AddFieldHandler() + .AddFieldHandler() ) ); } @@ -351,6 +350,19 @@ this IFilterConventionDescriptor descriptor } } +public abstract class ExtendedComparableOperationFilterInputType + : ComparableOperationFilterInputType +where T : notnull +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + base.Configure(descriptor); + // descriptor + // .Operation(AdditionalFilterOperations.InClosedInterval) + // .Type>>(); + } +} + public sealed class StringFilterInputType : StringOperationFilterInputType { @@ -372,7 +384,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class ShortFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -382,7 +394,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class DateTimeFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -392,7 +404,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class ByteFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -402,7 +414,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class UuidFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -412,7 +424,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } // public sealed class LocalDateFilterInputType -// : ComparableOperationFilterInputType +// : ExtendedComparableOperationFilterInputType // { // protected override void Configure(IFilterInputTypeDescriptor descriptor) // { @@ -422,7 +434,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) // } public sealed class LongFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -432,7 +444,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } // public sealed class LocalTimeFilterInputType -// : ComparableOperationFilterInputType +// : ExtendedComparableOperationFilterInputType // { // protected override void Configure(IFilterInputTypeDescriptor descriptor) // { @@ -442,7 +454,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) // } public sealed class FloatFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -452,7 +464,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class TimeSpanFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -462,7 +474,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class IntFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -472,7 +484,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class DecimalFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { @@ -482,7 +494,7 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } public sealed class UrlFilterInputType - : ComparableOperationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { diff --git a/backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs b/backend/src/GraphQl/Filters/QueryableComparableInClosedIntervalHandler.cs similarity index 55% rename from backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs rename to backend/src/GraphQl/Filters/QueryableComparableInClosedIntervalHandler.cs index b66620cd..33d5523a 100644 --- a/backend/src/GraphQl/Filters/QueryableFloatInClosedIntervalHandler.cs +++ b/backend/src/GraphQl/Filters/QueryableComparableInClosedIntervalHandler.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using System.Linq.Expressions; -using System.Reflection; using HotChocolate.Data.Filters; using HotChocolate.Data.Filters.Expressions; using HotChocolate.Language; @@ -10,15 +8,15 @@ namespace Database.GraphQl.Filters; -public sealed class QueryableFloatInClosedIntervalHandler +public sealed class QueryableComparableInClosedIntervalHandler : QueryableComparableOperationHandler { - public QueryableFloatInClosedIntervalHandler( + public QueryableComparableInClosedIntervalHandler( ITypeConverter typeConverter, InputParser inputParser) : base(typeConverter, inputParser) { - // CanBeNull = false; + CanBeNull = false; } // This is used to match the handler to all `inClosedInterval` fields @@ -35,14 +33,18 @@ public override Expression HandleOperation( // e.g. ~> y.gValue var propertyPath = context.GetInstance(); // The parsed value is what was specified in the query - // e.g. ~> { lowerBound: 0.1, upperBound: 0.8 } - var typedValue = (ClosedIntervalInput?)parsedValue - ?? throw new InvalidOperationException(); - // Creates and returns the operation - // e.g. ~> y.gValue >= 0.1 && y.gValue <= 0.8 - return Expression.AndAlso( - FilterExpressionBuilder.GreaterThanOrEqual(propertyPath, typedValue.LowerBound), - FilterExpressionBuilder.LowerThanOrEqual(propertyPath, typedValue.UpperBound) - ); + // e.g. ~> { lowerBound: 0.0, upperBound: 1.0 } + ArgumentNullException.ThrowIfNull(parsedValue); + if (parsedValue is ClosedIntervalInput closedIntervalInput) + { + // Creates and returns the LINQ operation + // e.g. ~> 0.0 >= y.gValue && y.gValue <= 1.0 + return Expression.AndAlso( + FilterExpressionBuilder.GreaterThanOrEqual(propertyPath, closedIntervalInput.LowerBound), + FilterExpressionBuilder.LowerThanOrEqual(propertyPath, closedIntervalInput.UpperBound) + ); + } + // Something went wrong + throw new InvalidOperationException(); } } \ No newline at end of file From 86e1ab1cd014537d7d15bbc500c7c8dc6a511660 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Feb 2026 19:33:14 +0100 Subject: [PATCH 729/878] Extract scalar filter types --- .../src/Configuration/GraphQlConfiguration.cs | 153 ----------------- .../GraphQl/Filters/ScalarFilterInputTypes.cs | 157 ++++++++++++++++++ 2 files changed, 157 insertions(+), 153 deletions(-) create mode 100644 backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 4c06d47b..4afb4161 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -350,159 +350,6 @@ this IFilterConventionDescriptor descriptor } } -public abstract class ExtendedComparableOperationFilterInputType - : ComparableOperationFilterInputType -where T : notnull -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - base.Configure(descriptor); - // descriptor - // .Operation(AdditionalFilterOperations.InClosedInterval) - // .Type>>(); - } -} - -public sealed class StringFilterInputType - : StringOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"String{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class BooleanFilterInputType - : BooleanOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Boolean{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class ShortFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Short{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class DateTimeFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"DateTime{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class ByteFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Byte{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class UuidFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Uuid{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -// public sealed class LocalDateFilterInputType -// : ExtendedComparableOperationFilterInputType -// { -// protected override void Configure(IFilterInputTypeDescriptor descriptor) -// { -// descriptor.Name($"LocalDate{GraphQlConstants.FilterInputSuffix}"); -// base.Configure(descriptor); -// } -// } - -public sealed class LongFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Long{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -// public sealed class LocalTimeFilterInputType -// : ExtendedComparableOperationFilterInputType -// { -// protected override void Configure(IFilterInputTypeDescriptor descriptor) -// { -// descriptor.Name($"LocalTime{GraphQlConstants.FilterInputSuffix}"); -// base.Configure(descriptor); -// } -// } - -public sealed class FloatFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Float{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class TimeSpanFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"TimeSpan{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class IntFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Int{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class DecimalFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Decimal{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - -public sealed class UrlFilterInputType - : ExtendedComparableOperationFilterInputType -{ - protected override void Configure(IFilterInputTypeDescriptor descriptor) - { - descriptor.Name($"Url{GraphQlConstants.FilterInputSuffix}"); - base.Configure(descriptor); - } -} - // See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sorting-conventions public partial class CustomSortConvention : SortConvention { diff --git a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs new file mode 100644 index 00000000..a43f2f96 --- /dev/null +++ b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs @@ -0,0 +1,157 @@ +using HotChocolate.Data.Filters; +using HotChocolate.Types; + +namespace Database.GraphQl.Filters; + +public abstract class ExtendedComparableOperationFilterInputType + : ComparableOperationFilterInputType +where T : notnull +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + base.Configure(descriptor); + // descriptor + // .Operation(AdditionalFilterOperations.InClosedInterval) + // .Type>>(); + } +} + +public sealed class StringFilterInputType + : StringOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"String{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class BooleanFilterInputType + : BooleanOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Boolean{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class ShortFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Short{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class DateTimeFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"DateTime{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class ByteFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Byte{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class UuidFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Uuid{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +// public sealed class LocalDateFilterInputType +// : ExtendedComparableOperationFilterInputType +// { +// protected override void Configure(IFilterInputTypeDescriptor descriptor) +// { +// descriptor.Name($"LocalDate{GraphQlConstants.FilterInputSuffix}"); +// base.Configure(descriptor); +// } +// } + +public sealed class LongFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Long{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +// public sealed class LocalTimeFilterInputType +// : ExtendedComparableOperationFilterInputType +// { +// protected override void Configure(IFilterInputTypeDescriptor descriptor) +// { +// descriptor.Name($"LocalTime{GraphQlConstants.FilterInputSuffix}"); +// base.Configure(descriptor); +// } +// } + +public sealed class FloatFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Float{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class TimeSpanFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"TimeSpan{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class IntFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Int{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class DecimalFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Decimal{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class UrlFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"Url{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} \ No newline at end of file From c3d6556b2b6a235e28a682248baa67b8a21d6085 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Feb 2026 19:52:22 +0100 Subject: [PATCH 730/878] Upgrade baseline browser mapping properly --- frontend/package.json | 4 ++-- frontend/yarn.lock | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 0383ebf1..8a348232 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,7 +11,7 @@ "build": "yarn refresh-baseline-browser-mapping && yarn codegen && NODE_ENV=production next build", "test": "yarn codegen && NODE_TLS_REJECT_UNAUTHORIZED=0 jest", "start": "next start --port 8080", - "refresh-baseline-browser-mapping": "yarn install --dev baseline-browser-mapping@latest" + "refresh-baseline-browser-mapping": "yarn upgrade baseline-browser-mapping@latest" }, "dependencies": { "@ant-design/pro-layout": "^7.10.3", @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.9.3", + "baseline-browser-mapping": "^2.9.19", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index d2016358..4abc7aa7 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2832,11 +2832,16 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.3: +baseline-browser-mapping@^2.9.0: version "2.9.5" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz#47f9549e0be1a84cd16651ac4c3b7d87a71408e6" integrity sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA== +baseline-browser-mapping@^2.9.19: + version "2.9.19" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" + integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" From 75b77c50c9d1f5bc61a427b9d94c9c9325f85a9a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 10:47:51 +0100 Subject: [PATCH 731/878] Do not wait for container to become healthy when restarting due to race conditions --- deploy.mk | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/deploy.mk b/deploy.mk index 843a9a7d..9e1846a5 100755 --- a/deploy.mk +++ b/deploy.mk @@ -131,10 +131,12 @@ services : ## Recreate services run-tests : ## TODO Run tests .PHONY : run-tests +# $(MAKE) --file=./docker.mk \ +# restart \ +# SERVICE="${SERVICE}" restart : ## Restart service `${SERVICE}` and await its health - $(MAKE) --file=./docker.mk \ - restart \ - SERVICE="${SERVICE}" + docker compose restart \ + ${SERVICE} .PHONY : restart symlink : ## Confirm that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml From 231fe7e8f83f1678b18c17902cfac088a9be9de6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 12:06:45 +0100 Subject: [PATCH 732/878] Do not recreate or start dependencies for management commands --- database.mk | 13 ++++++++++++- deploy.mk | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/database.mk b/database.mk index be2c41dc..5f267961 100755 --- a/database.mk +++ b/database.mk @@ -22,7 +22,8 @@ help : ## Print this help psql : ## Enter PostgreSQL interactive terminal in the `database` container docker compose up \ - --remove-orphans \ + --no-deps \ + --no-recreate \ --wait \ database docker compose exec \ @@ -41,6 +42,8 @@ remove-volume : ## Remove data and files volumes create : ## Create database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ + --no-deps \ + --no-recreate \ --wait \ database docker compose exec \ @@ -52,6 +55,8 @@ create : ## Create database with name `${POSTGRES_DATABASE_NAME}` drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ + --no-deps \ + --no-recreate \ --wait \ database docker compose exec \ @@ -63,6 +68,8 @@ drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after database schema changes it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`) docker compose up \ + --no-deps \ + --no-recreate \ --wait \ database cat "${SCRIPT}" \ @@ -93,6 +100,8 @@ migrate : ## Migrate database by running the idempotent SQL script ./backend/sr backup : ## Backup database and related data to directory with absolute path `${DIR}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` mkdir --parents "${DIR}" docker compose up \ + --no-deps \ + --no-recreate \ --wait \ database docker compose exec \ @@ -121,6 +130,8 @@ restore : ## Restore database and related data from directory with absolute path docker compose stop \ backend docker compose up \ + --no-deps \ + --no-recreate \ --wait \ database -docker compose exec \ diff --git a/deploy.mk b/deploy.mk index 9e1846a5..d7557f20 100755 --- a/deploy.mk +++ b/deploy.mk @@ -136,6 +136,7 @@ run-tests : ## TODO Run tests # SERVICE="${SERVICE}" restart : ## Restart service `${SERVICE}` and await its health docker compose restart \ + --no-deps \ ${SERVICE} .PHONY : restart From e368ffd54ce0f8574ca49f617849a0ffae461428 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 19:54:25 +0100 Subject: [PATCH 733/878] Fine-tune docker healthcheck timing --- docker-compose.development.yaml | 24 ++++++++++++++++-------- docker-compose.production.yaml | 24 ++++++++++++++++-------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 61d0e78d..9302b7a7 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -29,9 +29,11 @@ services: healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "5" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" @@ -121,9 +123,11 @@ services: healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" deploy: @@ -161,9 +165,11 @@ services: restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" @@ -191,9 +197,11 @@ services: restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index ca56fbd4..0afb4ef4 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -29,9 +29,11 @@ services: healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "5" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" @@ -85,9 +87,11 @@ services: healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" deploy: @@ -110,9 +114,11 @@ services: restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" pull_policy: "never" @@ -138,9 +144,11 @@ services: restart: "unless-stopped" healthcheck: test: [ "CMD-SHELL", "service nginx status || exit 1" ] - interval: "5s" + interval: "30s" timeout: "5s" - retries: "20" + retries: 3 + start_period: "60s" + start_interval: "5s" labels: autoheal: "true" From 3714662e97353e67ba3b82ada2328581f9198245 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 20:31:48 +0100 Subject: [PATCH 734/878] Tune docker compose up and exec usage --- never build, run dependencies, or recreate during up and do not unnecessarily allocate a TTY during exec --- database.mk | 11 +++++++++++ deploy.mk | 2 ++ docker.mk | 9 ++++++++- tools.mk | 4 +++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/database.mk b/database.mk index 5f267961..7bd61cf4 100755 --- a/database.mk +++ b/database.mk @@ -22,6 +22,7 @@ help : ## Print this help psql : ## Enter PostgreSQL interactive terminal in the `database` container docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ @@ -42,11 +43,13 @@ remove-volume : ## Remove data and files volumes create : ## Create database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ database docker compose exec \ + --no-tty \ database \ createdb \ --username="${POSTGRES_USER}" \ @@ -55,11 +58,13 @@ create : ## Create database with name `${POSTGRES_DATABASE_NAME}` drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ database docker compose exec \ + --no-tty \ database \ dropdb \ --username="${POSTGRES_USER}" \ @@ -68,6 +73,7 @@ drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after database schema changes it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`) docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ @@ -100,11 +106,13 @@ migrate : ## Migrate database by running the idempotent SQL script ./backend/sr backup : ## Backup database and related data to directory with absolute path `${DIR}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` mkdir --parents "${DIR}" docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ database docker compose exec \ + --no-tty \ database \ pg_dump \ --clean \ @@ -130,16 +138,19 @@ restore : ## Restore database and related data from directory with absolute path docker compose stop \ backend docker compose up \ + --no-build \ --no-deps \ --no-recreate \ --wait \ database -docker compose exec \ + --no-tty \ database \ dropdb \ --username="${POSTGRES_USER}" \ "${POSTGRES_DATABASE_NAME}" docker compose exec \ + --no-tty \ database \ createdb \ --username="${POSTGRES_USER}" \ diff --git a/deploy.mk b/deploy.mk index d7557f20..06be0bae 100755 --- a/deploy.mk +++ b/deploy.mk @@ -122,6 +122,8 @@ dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are availa # one go so the maintenance page is only down very shortly. services : ## Recreate services docker compose up \ + --no-build \ + --no-deps \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ diff --git a/docker.mk b/docker.mk index b68e732a..547247f9 100755 --- a/docker.mk +++ b/docker.mk @@ -92,6 +92,8 @@ remove : ## Remove stopped services up : dotenv ## (Re)create and start services docker compose up \ --no-build \ + --no-deps \ + --no-recreate \ --remove-orphans \ --wait ${SERVICE} .PHONY : up @@ -107,6 +109,9 @@ restart : ## Restart services (and await their health), for example, `make resta docker compose restart \ --no-deps ${SERVICE} docker compose up \ + --no-build \ + --no-deps \ + --no-recreate \ --wait ${SERVICE} .PHONY : restart @@ -127,7 +132,9 @@ logs : ## Follow logs exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service docker compose up \ - --remove-orphans \ + --no-build \ + --no-deps \ + --no-recreate \ --wait \ ${SERVICE} docker compose exec \ diff --git a/tools.mk b/tools.mk index e1870627..e969cfd6 100755 --- a/tools.mk +++ b/tools.mk @@ -29,7 +29,9 @@ help : ## Print this help # Executing with `--privileged` is necessary according to https://github.com/dotnet/diagnostics/blob/master/documentation/FAQ.md trace-backend : ## Trace the dotnet process `Metabase` within the backend service docker compose up \ - --remove-orphans \ + --no-build \ + --no-deps \ + --no-recreate \ --wait \ backend docker compose exec \ From fa71cf7ff98b09bdc83c35a47904f589bb740d3b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 20:51:14 +0100 Subject: [PATCH 735/878] Never mark an upstream server as down (because we only have one option and it should always be retried) --- nginx/templates/default.conf.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 61c27f79..1a017759 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -49,11 +49,11 @@ map $http_upgrade $connection_upgrade { } upstream backend { - server backend:8080; + server backend:8080 max_fails=0; } upstream frontend { - server frontend:8080; + server frontend:8080 max_fails=0; } server { From 9cf576b00f395f2e22ada15e65c037b4e40c6052 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 21:37:31 +0100 Subject: [PATCH 736/878] Do not start dependencies to access the files volume --- database.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/database.mk b/database.mk index 7bd61cf4..51608ca2 100755 --- a/database.mk +++ b/database.mk @@ -123,6 +123,8 @@ backup : ## Backup database and related data to directory with absolute path `${ > "${DIR}/${dump_archive_name}" docker compose run \ --rm \ + --no-deps \ + --no-TTY \ --volume "${DIR}":/backup \ backend \ tar \ @@ -168,6 +170,8 @@ restore : ## Restore database and related data from directory with absolute path --dbname="${POSTGRES_DATABASE_NAME}" docker compose run \ --rm \ + --no-deps \ + --no-TTY \ --volume "${DIR}":/backup \ backend \ bash -o errexit -o errtrace -o nounset -o pipefail -c " \ From d5b3e081dbda241effb673a4699b5ef9f5b5e070 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Feb 2026 21:38:08 +0100 Subject: [PATCH 737/878] Resolve IPs dynamically every 5 seconds to avoid stale IPs that may happen when an upstream service is restarted --- nginx/templates/default.conf.template | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 1a017759..f6c2f2ad 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -1,6 +1,13 @@ # Inspired by https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#https-configuration # and https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx +# `127.0.0.11` is the default IP address for Docker's embedded DNS server. You can verify it by running `cat /etc/resolv.conf` inside a container. +# `valid=30s` forces NGINX to re-resolve hostnames every 30 seconds. This is critical in Docker because container IPs can change during a docker-compose restart. +# `ipv6=off` makes Docker use IPv4. Docker's internal DNS often has issues with IPv6 resolution for container names. +# `resolver_timeout 5s` Prevents NGINX from waiting indefinitely if the internal DNS server is momentarily unreachable. +resolver 127.0.0.11 valid=5s ipv6=off; +resolver_timeout 5s; + proxy_redirect off; proxy_set_header Host $http_x_forwarded_host; proxy_set_header X-Real-IP $remote_addr; @@ -49,11 +56,13 @@ map $http_upgrade $connection_upgrade { } upstream backend { - server backend:8080 max_fails=0; + zone upstream_dynamic 64k; + server backend:8080 max_fails=0 resolve; } upstream frontend { - server frontend:8080 max_fails=0; + zone upstream_dynamic 64k; + server frontend:8080 max_fails=0 resolve; } server { From 6b0d65b0f83a8f3a0491c6a8d15dc1be3f6c7b8e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 2 Mar 2026 20:03:00 +0100 Subject: [PATCH 738/878] Clean-up anonymous volumes with `make down` --- docker.mk | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docker.mk b/docker.mk index 547247f9..b314a99a 100755 --- a/docker.mk +++ b/docker.mk @@ -98,9 +98,12 @@ up : dotenv ## (Re)create and start services --wait ${SERVICE} .PHONY : up -down : ## Stop services and remove services and networks created by `up` +down : ## Stop services and remove services, networks, and anonymous volumes created by `up` docker compose down \ --remove-orphans ${SERVICE} + docker volume prune \ + --force \ + --filter "label=com.docker.compose.project=${NAME}_${ENVIRONMENT}" -rm --force \ ./frontend/queries/*.generated.ts .PHONY : down From f27f551aa435cd6ca34b0fc5a35b7a5460e8f3b2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 10 Mar 2026 19:09:16 +0100 Subject: [PATCH 739/878] Include response message when HTTP request fails --- backend/src/Services/ApiRequestService.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 6a9c0fe6..027897cd 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -1,6 +1,4 @@ -using System; -using System.IO; -using System.Linq; +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -183,7 +181,7 @@ CancellationToken cancellationToken if (httpResponseMessage.StatusCode is not HttpStatusCode.OK) { throw new HttpRequestException( - $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode}.", + $"The status code is not {HttpStatusCode.OK} but {httpResponseMessage.StatusCode} and the response is: {httpResponseMessage.Content.ReadAsStringAsync(cancellationToken)}", null, httpResponseMessage.StatusCode ); From 17e77dfb28d670395c5a4cd250f928174b938db3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Mar 2026 11:37:33 +0100 Subject: [PATCH 740/878] Correct development setup guide --- README.md | 204 +++++++++++++++++++++++++++--------------------------- deploy.mk | 20 +++--- docker.mk | 3 +- forge.mk | 6 +- tools.mk | 9 +-- 5 files changed, 120 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index c21f8a11..08bc295c 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,82 @@ # Database -This repository presents the reference implementation of a product data server -as part of the product data network -[buildingenvelopedata.org](https://www.buildingenvelopedata.org/). Before -deploying this repository, -[machine](https://github.com/building-envelope-data/machine) can be used to set -up the machine. - -The [API specification of the product data -servers](https://github.com/building-envelope-data/api/blob/develop/apis/database.graphql) -is available in the repository -[api](https://github.com/building-envelope-data/api). There is also -a [visualization of the API of a product data -server](https://graphql-kit.com/graphql-voyager/?url=https://www.solarbuildingenvelopes.com/graphql/). - -This repository is deployed as the [product data server of TestLab Solar -Facades of Fraunhofer ISE](https://www.solarbuildingenvelopes.com). - -If you have a question for which you don't find the answer in this repository, -please raise a [new -issue](https://github.com/building-envelope-data/database/issues/new) and add -the tag `question`! All ways to contribute are presented by +The network of databases +[buildingenvelopedata.org](https://www.buildingenvelopedata.org) +consists of one meta-data database, aka, metabase, and various product-data +databases. This project is the reference implementation of a product-data +database. It is deployed as the product-data server of the +[TestLab Solar Façades of Fraunhofer ISE](https://www.ise.fraunhofer.de/en/rd-infrastructure/accredited-labs/testlab-solar-facades.html) +at +[solarbuildingenvelopes.com](https://www.solarbuildingenvelopes.com) +You can interact with it through its +[user interface](https://www.solarbuildingenvelopes.com) +or +[GraphQL endpoint](https://www.solarbuildingenvelopes.com/graphql/) +whose API is a super-set of the +[API specification of product-data databases](https://github.com/building-envelope-data/api/blob/develop/apis/database.graphql) +standardized in the repository +[api](https://github.com/building-envelope-data/api). +Try for example the +[sample queries](https://github.com/building-envelope-data/api/blob/develop/requests/database/tutorial.graphql). + +If you have a question for which you cannot find an answer +[raise an issue](https://github.com/building-envelope-data/database/issues/new) +with the tag `question`. Feel free to contribute in any way mentioned in [CONTRIBUTING.md](https://github.com/building-envelope-data/database/blob/develop/CONTRIBUTING.md). -The basis for our collaboration is decribed by our [Code of -Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). +When doing so, please adhere to our +[Code of Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF_CONDUCT.md). ## Contents -[Getting started](#getting-started) +[Development](#development) -- [On your Linux machine](#on-your-linux-machine) +- [Getting Started](#getting-started) - [Migrating the Database](#migrating-the-database) - [Developing with Visual Studio Code](#developing-with-visual-studio-code) -- [Troubleshooting](#troubleshooting) +- [Troubleshooting](#troubleshooting-0) [Deployment](#deployment) -- [Setting up a Debian production machine](#setting-up-a-debian-production-machine) +- [Setting up a production machine](#setting-up-a-production-machine) - [Creating a release](#creating-a-release) - [Deploying a release](#deploying-a-release) - [Troubleshooting](#troubleshooting-1) -## Getting started +## Development -### On your Linux machine +### Getting Started -1. Open your favorite shell, for example, good old - [Bourne Again SHell, aka, `bash`](https://www.gnu.org/software/bash/), - the somewhat newer - [Z shell, aka, `zsh`](https://www.zsh.org/), - or shiny new - [`fish`](https://fishshell.com/). +1. Use the sibling project + [machine](https://github.com/building-envelope-data/machine?tab=readme-ov-file#getting-started) + and its instructions for the first stage of the set-up. -1. Install [Git](https://git-scm.com/) by running - `sudo apt install git-all` on [Debian](https://www.debian.org/)-based - distributions like [Ubuntu](https://ubuntu.com/), or - `sudo dnf install git` on [Fedora](https://getfedora.org/) and closely-related - [RPM-Package-Manager](https://rpm.org/)-based distributions like - [CentOS](https://www.centos.org/). For further information see - [Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). +1. Change to the parent directory of `./machine` by running `cd ..`. -1. Clone the source code by running - `git clone git@github.com:building-envelope-data/database.git` and navigate - into the new directory `database` by running `cd ./database`. +1. Clone the source code into `./development` by running + `git clone git@github.com:building-envelope-data/database.git ./development` + and navigate into the new directory `development` by running `cd ./development`. -1. Prepare your environment by running `cp ./.env.development.sample ./.env && chmod 600 ./.env` - and by adding the line `127.0.0.1 local.solarbuildingenvelopes.com` to your - `/etc/hosts` file. The value of `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` will - be set later. +1. Prepare your environment by running + `cp ./.env.development.sample ./.env && chmod 600 ./.env` + and adding the line + `127.0.0.1 local.solarbuildingenvelopes.org www.local.solarbuildingenvelopes.org staging.local.solarbuildingenvelopes.org telemetry.local.solarbuildingenvelopes.org` + to your + `/etc/hosts` file. Note that the value of + `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` in `./.env` will be set later. 1. Prepare your remote controls GNU Make and Docker Compose by running - - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.development.yaml ./docker-compose.yaml`. -1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop), and - [GNU Make](https://www.gnu.org/software/make/). - 1. List all GNU Make targets by running `make help`. 1. Generate a GnuPG key with the passphrase - `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file - by running + `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in `./.env` by running `make bootstrap COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` with your information filled in, for example, `make bootstrap COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. Then copy the key's fingerprint which is output by the command and set it as - the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the - `./.env` file. + the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in `./.env`. 1. Create the PostgreSQL database and schema by running `./database.mk create migrate`. @@ -96,36 +85,38 @@ Conduct](https://github.com/building-envelope-data/database/blob/develop/CODE_OF `make build up logs`. 1. In your web browser, navigate to the - - - web frontend at `https://local.solarbuildingenvelopes.com:5051`, - - GraphQL API at `https://local.solarbuildingenvelopes.com:5051/graphql/`, - - REST API `https://local.solarbuildingenvelopes.com:5051/openapi/docs//`, - - dummy email server at `https://local.solarbuildingenvelopes.com:5051/email/` + - web frontend at `https://www.local.solarbuildingenvelopes.com:${HTTPS_PORT}`, + - GraphQL playground at `https://www.local.solarbuildingenvelopes.com:${HTTPS_PORT}/graphql/`, + - OpenAPI reference at `https://www.local.solarbuildingenvelopes.com:${HTTPS_PORT}/openapi/docs/`, + - dummy email server at `https://www.local.solarbuildingenvelopes.com:${HTTPS_PORT}/email/` (to view for example the confirmation email sent during registration), - - OpenId Connect configuration navigate to - `https://local.solarbuildingenvelopes.com:5051/.well-known/openid-configuration` + - OpenId Connect configuration at + `https://www.local.solarbuildingenvelopes.com:${HTTPS_PORT}/.well-known/openid-configuration`, + - telemetry web frontend at `https://telemetry.local.solarbuildingenvelopes.com:${HTTPS_PORT}`, + - staging web frontend at `https://staging.local.solarbuildingenvelopes.com:${HTTPS_PORT}`, + - test the redirect to `www` at `https://local.solarbuildingenvelopes.com:${HTTPS_PORT}`, - Note that the port is `5051` by default. If you set the variable - `HTTPS_PORT` within the `./.env` to some other value though, you need to use - that value instead within the URLs. + where `${HTTPS_PORT}` is the value set in `../machine/.env` and defaults to + `7001` for the general sample environment, `7001` for + `buildingenvelopedata.org`, and `7501` for `solarbuildingenvelopes.com`. In another shell 1. Drop into `bash` with the working directory `/app`, which is mounted to the host's `./backend` directory, inside a fresh Docker container based on - `./backend/Dockerfile` by running `make shell SERVICE=backend`. If + `./backend/Dockerfile.development` by running `make shell SERVICE=backend`. If necessary, the Docker image is (re)built automatically, which takes a while - the first time. Note that the Docker image and containers try to use the - same user and group IDs as the ones on the host machine. This has the upside - that files created within containers in mounted directories are owned by the - host user. It has the downside that the Docker image may fail to build - because the IDs may already be taken by other users and groups in the base - image. This happens for example if you are `root` on the host machine with - the user and group IDs 0. If there is an ID collision, then you can either - change the user and group ID on the host machine (for example by logging in - as another user) or you can replace all occurrences of `shell id --group` - and `shell id --user` in `Makefile` by fixed non-colliding IDs like 1000. If - you know a better way, please + the first time. Note that the Docker image and containers try to use the same + user and group IDs as the ones on the host machine. This has the upside that + files created within containers in mounted directories are owned by the host + user. It has the downside that the Docker image may fail to build because the + IDs may already be taken by other users and groups in the base image. This + happens for example if you are `root` on the host machine with the user and + group IDs 0. If there is an ID collision, then you can either change the user + and group ID on the host machine (for example by logging in as another user) or + you can replace all occurrences of `shell id --group` and `shell id --user` in + `Makefile` by fixed non-colliding IDs like 1000. If you know a better way, + please [let use know on GitHub](https://github.com/building-envelope-data/database/issues/new). 1. List all backend GNU Make targets by running `make help`. 1. For example, update packages and tools by running `make update`. @@ -135,7 +126,7 @@ In another shell After changing the domain model in `./backend/src/data`, you need to migrate the database by dropping into `make shell SERVICE=backend`, adding a migration -with `make migration PERSON=${MIGRATION_NAME}`, verifying and if necessary +with `make migration NAME=${MIGRATION_NAME}`, verifying and if necessary adapting the new migration C# code and SQL scripts, exiting the container with `exit`, and applying the new migration to the PostgreSQL database with `./database.mk migrate`. See @@ -198,8 +189,8 @@ and choose the process `/app/src/bin/Debug/net10.0/Database run` titled (`Ctrl+Shift+D`), select the launch profile ".NET Core Attach", press the "Start Debugging" icon (`F5`), and select the same process as above. Then, for example, open some source files to set breakpoints, navigate through the -website https://local.solarbuildingenvelopes.com:5051, which will stop at -breakpoints, and inspect the information provided by the debugger at the +website , which will stop +at breakpoints, and inspect the information provided by the debugger at the breakpoints. For details on debugging C# in Visual Studio Code, see [Debugging](https://code.visualstudio.com/docs/csharp/debugging). @@ -226,9 +217,9 @@ manually or upgrading Npgsql, the service `backend` may throw exceptions regarding the object-relational mapping (Npgsql or EF Core). In that case it may be necessary to restart the service `backend`, for example, by running `make down up` and it may even be necessary recreate the database from scratch -by running `make down remove-data-volume up`. Note that the latter will remove -all data from PostgreSQL, recreate the database and its schema, and seed it -freshly. +by running `make down && ./database.mk remove-volume create migrate && make +up`. Note that the latter will remove all data from PostgreSQL, recreate the +database and its schema, and seed it freshly. When your hard-disk starts to grow full, it may be the case that Docker does not clean-up anonymous volumes properly. You can do so manually by running @@ -260,18 +251,20 @@ For information on using Docker in production see [Configure and troubleshoot the Docker daemon](https://docs.docker.com/config/daemon/) and the pages following it. -### Setting up a Debian production machine +### Setting up a production machine 1. Use the sibling project [machine](https://github.com/building-envelope-data/machine) and its instructions for the first stage of the set-up. 1. Enter a shell on the production machine using `ssh`. 1. Change into the directory `/app` by running `cd /app`. 1. Clone the repository twice by running + ``` for environment in staging production ; do git clone git@github.com:building-envelope-data/database.git ./${environment} done ``` + 1. For each of the two environments staging and production referred to by `${environment}` below: 1. Set the variable `environment` by running `environment=staging` or @@ -283,7 +276,6 @@ and the pages following it. log into your account, navigate to the institution operating this database (which you should be a representative of), add an OpenId Connect Application with - - client ID and display name of your choice; - consent type: explicit; - endpoints: authorization, pushed authorization, introspection, @@ -298,7 +290,7 @@ and the pages following it. where `${HOST}` is the domain name with sub-domain of the deployment, for example, `staging.solarbuildingenvelopes.com` or `www.solarbuildingenvelopes.com` for the product-data database of the - TestLab Solar Facades. + TestLab Solar Façades. Alternatively, after logging in, open `https://www.buildingenvelopedata.org/graphql/` and run the following @@ -332,19 +324,18 @@ and the pages following it. 1. Prepare the environment by running `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env` - and by adjusting variable values to your needs, in particular, by setting - passwords to newly generated ones, where random passwords may be - generated by running `openssl rand -base64 32`. The value of - `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` will be set later. Here is some + and by adjusting variable values in the copies to your needs, in + particular, by setting passwords to newly generated ones, where random + passwords may be generated by running `openssl rand -base64 32`. The value + of `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` will be set later. Here is some information on what the variables mean - - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; - `HOST` is the domain name with sub-domain of the deployment, in particular, it is used to make resource locators absolute; - `HTTP_PORT` is the HTTP port to which the reverse proxy NGINX forwards for HTTPS requests (see `PRODUCTION_HTTP_PORT` and - `STAGING_HTTP_PORT` in the `.env` file of your clone of + `STAGING_HTTP_PORT` in `./.env` of your clone of [machine](https://github.com/building-envelope-data/machine)); - `METABASE_HOST` is the domain name with sub-domain of the metabase, in particular, to use it as OpenId Connect provider and to ask it for @@ -365,7 +356,6 @@ and the pages following it. of the GnuPG secret key for signing response and data approvals. 1. Prepare your remote controls GNU Make and Docker Compose by running - - `ln --symbolic ./docker.mk ./Makefile` and - `ln --symbolic ./docker-compose.production.yaml ./docker-compose.yaml`. @@ -394,9 +384,11 @@ and the pages following it. where `*.*.*` is the version. Note that this is **not** the same as "Draft a new release" on [Releases](https://github.com/building-envelope-data/database/releases). -1. Fetch the release branch by running `git fetch` and check it out by running - `git checkout release/v*.*.*`, where `*.*.*` is the version. -1. Apply pending migrations with `./database.mk migrate`. +1. Fetch the release branch by running `git fetch` and switch to it by running + `git switch --detach release/v*.*.*`, where `*.*.*` is the version. +1. If the databases have not diverged, then apply pending migrations with + `./database.mk migrate`. Otherwise, recreate the database by running + `./database.mk drop create migrate`. 1. Make sure that all tests succeed and try out any new features manually. 1. [Publish the new release](https://github.com/building-envelope-data/database/actions/workflows/publish-new-release.yaml) by merging the release branch into `main` whereby a new pull request from @@ -404,6 +396,12 @@ and the pages following it. ### Deploying a release +1. Fetch the release branch by running `git fetch` and switch to it by running + `git switch --detach release/${TAG}`, where `${TAG}` is the release tag to + be deployed, for example, `v1.0.0`. +1. Build and push the Docker images for the services `frontend` and `backend` + services by running `./forge.mk all USER=cloud HOST=${IP}`, where `${IP}` is + the server's IP address. 1. Enter a shell on the production machine using `ssh`. 1. Navigate into `/app/production` by running `cd /app/production`. 1. Back up the production database by running @@ -414,7 +412,7 @@ and the pages following it. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.staging.sample` file of the release to be deployed. 1. Deploy the new release in the staging environment by running - `./deploy.mk TARGET=${TAG} deploy`, where `${TAG}` is + `./deploy.mk do TARGET=${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running @@ -422,18 +420,18 @@ and the pages following it. figure out what went wrong, apply the necessary fixes to the codebase, create a new release, and try to deploy that release instead. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by - running `cd /app/machine && make deploy` and test whether everything works + running `cd /app/machine && ./deploy.mk do` and test whether everything works as expected and if that is the case, continue. Note that in the staging environment sent emails can be viewed in the web browser under `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses in - the variable `RELAY_ALLOWED_EMAILS` in the `.env` file are delivered to the + the variable `RELAY_ALLOWED_EMAILS` in `./.env` are delivered to the respective inboxes (the variable's value is a comma separated list of email addresses). 1. Change to the production environment by running `cd /app/production`. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.production.sample` file of the release to be deployed. 1. Deploy the new release in the production environment by running - `./deploy.mk TARGET=${TAG} deploy`, where `${TAG}` is + `./deploy.mk do TARGET=${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. If it fails _after_ the database backup was made, rollback to the previous state by running @@ -486,7 +484,7 @@ where the `X`s need to be replaced by proper values. Another solution is to reset the transaction log by entering the database container with ``` -docker compose run database bash +make shell SERVICE=database ``` and dry-running diff --git a/deploy.mk b/deploy.mk index 06be0bae..4f414304 100755 --- a/deploy.mk +++ b/deploy.mk @@ -38,26 +38,26 @@ help : ## Print this help # and finally in `cd /app/production` and checking that everything works as # expected. Before trying it on staging, I usually play the database from # production into staging. -deploy : DIR = "$(shell pwd)/backup" -deploy : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk deploy TARGET=v1.0.0` +do : DIR = "$(shell pwd)/backup" +do : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk do TARGET=v1.0.0` $(MAKE) --file="${SELF}" begin-maintenance $(MAKE) --file="${SELF}" store-target set-target TARGET="${TARGET}" $(MAKE) --file="${SELF}" backup DIR="${DIR}" $(MAKE) --file="${SELF}" fetch-all - $(MAKE) --file="${SELF}" checkout TARGET="${TARGET}" + $(MAKE) --file="${SELF}" switch TARGET="${TARGET}" $(MAKE) --file="${SELF}" dotenv $(MAKE) --file="${SELF}" migrate $(MAKE) --file="${SELF}" services $(MAKE) --file="${SELF}" run-tests $(MAKE) --file="${SELF}" end-maintenance -.PHONY : deploy +.PHONY : do rollback : TARGET = "$(shell cat ./.stored-target)" rollback : DIR = "$(shell pwd)/backup" rollback : symlink ## Rollback deployment attempt (uses target stored in `./.stored-target` and database backup stored in `./backup/`) $(MAKE) --file="${SELF}" begin-maintenance $(MAKE) --file="${SELF}" set-target TARGET="${TARGET}" - $(MAKE) --file="${SELF}" checkout TARGET="${TARGET}" + $(MAKE) --file="${SELF}" switch TARGET="${TARGET}" $(MAKE) --file="${SELF}" dotenv $(MAKE) --file="${SELF}" restore DIR="${DIR}" $(MAKE) --file="${SELF}" services @@ -110,9 +110,12 @@ fetch-all : ## Fetch all .PHONY : fetch-all # Inspired by https://grimoire.ca/git/stop-using-git-pull-to-deploy/ -checkout : ## Fetch and checkout `${TARGET}` - git checkout --force "${TARGET}" -.PHONY : checkout +switch : ## Switch to `${TARGET}` + git switch \ + --discard-changes \ + --detach \ + "${TARGET}" +.PHONY : switch dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are available in ./.env ${dotenv_linter} diff /mnt/.env "/mnt/.env.${ENVIRONMENT}.sample" @@ -124,6 +127,7 @@ services : ## Recreate services docker compose up \ --no-build \ --no-deps \ + --pull "always" \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ diff --git a/docker.mk b/docker.mk index b314a99a..0e9f5641 100755 --- a/docker.mk +++ b/docker.mk @@ -15,9 +15,8 @@ dotenv_linter = \ --rm \ --user $(shell id --user):$(shell id --group) \ --volume "$(shell pwd):/mnt:ro" \ - --pull "always" \ --quiet \ - dotenvlinter/dotenv-linter:latest + dotenvlinter/dotenv-linter:4.0.0 # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help diff --git a/forge.mk b/forge.mk index 0bd3d3a2..886ac0ed 100755 --- a/forge.mk +++ b/forge.mk @@ -28,7 +28,7 @@ build : ## Build image, for example, `./forge.mk build SERVICE=backend` "./${SERVICE}" .PHONY : build -push : ## Push image, for example, `./forge.mk push SERVICE=backend USER=cloud HOST=192.102.163.92` +push : ## Push image, for example, `./forge.mk push SERVICE=backend USER=cloud HOST=192.102.162.39` docker save \ --platform "linux/amd64" \ "${NAME}-${SERVICE}:${TARGET}" \ @@ -36,7 +36,7 @@ push : ## Push image, for example, `./forge.mk push SERVICE=backend USER=cloud H | ssh "${USER}@${HOST}" "docker load" .PHONY : push -all : ## Build and push all images +all : ## Build and push all images, for example, `./forge.mk all USER=cloud HOST=192.102.162.39` $(MAKE) --file="${SELF}" \ build \ push \ @@ -47,7 +47,7 @@ all : ## Build and push all images SERVICE=frontend .PHONY : all -# deploy : all ## Deploy in the cloud, for example, `./forge.mk deploy ENVIRONMENT=staging USER=cloud HOST=192.102.163.92` +# deploy : all ## Deploy in the cloud, for example, `./forge.mk deploy ENVIRONMENT=staging USER=cloud HOST=192.102.162.39` # ssh "${USER}@${HOST}" " \ # cd '/app/${ENVIRONMENT}' \ # && ./deploy.mk deploy TARGET='${TARGET}' \ diff --git a/tools.mk b/tools.mk index e969cfd6..b8c25fbd 100755 --- a/tools.mk +++ b/tools.mk @@ -14,9 +14,8 @@ dotenv_linter = \ --rm \ --user $(shell id --user):$(shell id --group) \ --volume "$(shell pwd):/mnt:ro" \ - --pull "always" \ --quiet \ - dotenvlinter/dotenv-linter:latest + dotenvlinter/dotenv-linter:4.0.0 # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -46,9 +45,8 @@ dclint = \ --tty \ --user $(shell id --user):$(shell id --group) \ --volume "$(shell pwd):/app" \ - --pull "always" \ --quiet \ - zavoloklom/dclint:latest \ + zavoloklom/dclint:3.1.0 \ --config /app/.dclintrc hadolint = \ @@ -57,9 +55,8 @@ hadolint = \ --interactive \ --user $(shell id --user):$(shell id --group) \ --volume ./.hadolint.yaml:/.config/.hadolint.yaml \ - --pull "always" \ --quiet \ - hadolint/hadolint:latest \ + hadolint/hadolint:v2.14.0-debian \ hadolint \ --config /.config/.hadolint.yaml From 2de4112eac226dab1be1983c5f3e139e61c99065 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Mar 2026 11:38:53 +0100 Subject: [PATCH 741/878] Add configs for prettier, sqlfluff, and biome dev tools --- .prettierrc | 0 .sqlfluff | 0 biome.json | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .prettierrc create mode 100644 .sqlfluff create mode 100644 biome.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..e69de29b diff --git a/.sqlfluff b/.sqlfluff new file mode 100644 index 00000000..e69de29b diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..e69de29b From c55907761bafd59c267f21410d97f87453f75731 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Mar 2026 11:49:16 +0100 Subject: [PATCH 742/878] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 08bc295c..a10928eb 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ In another shell you can replace all occurrences of `shell id --group` and `shell id --user` in `Makefile` by fixed non-colliding IDs like 1000. If you know a better way, please - [let use know on GitHub](https://github.com/building-envelope-data/database/issues/new). + [let us know on GitHub](https://github.com/building-envelope-data/database/issues/new). 1. List all backend GNU Make targets by running `make help`. 1. For example, update packages and tools by running `make update`. 1. Drop out of the container by running `exit` or pressing `Ctrl-D`. From 4cb551c763ef022e6dcf053a57b4ebc78613a0fc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 11 Mar 2026 12:51:01 +0100 Subject: [PATCH 743/878] Update browser mapping --- frontend/package.json | 2 +- frontend/yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 8a348232..c594339e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.9.19", + "baseline-browser-mapping": "^2.10.0", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4abc7aa7..e189b9da 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2832,16 +2832,16 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +baseline-browser-mapping@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz#5b09935025bf8a80e29130251e337c6a7fc8cbb9" + integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== + baseline-browser-mapping@^2.9.0: version "2.9.5" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz#47f9549e0be1a84cd16651ac4c3b7d87a71408e6" integrity sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA== -baseline-browser-mapping@^2.9.19: - version "2.9.19" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz#3e508c43c46d961eb4d7d2e5b8d1dd0f9ee4f488" - integrity sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg== - big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" From 2bc02077bbdc646fb8b92dd660b0a3b73e69df70 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 12 Mar 2026 09:45:23 +0100 Subject: [PATCH 744/878] Assert that symlinks are set up correctly --- deploy.mk | 13 +++++++++---- docker.mk | 15 ++++++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/deploy.mk b/deploy.mk index 4f414304..aaf24269 100755 --- a/deploy.mk +++ b/deploy.mk @@ -146,13 +146,18 @@ restart : ## Restart service `${SERVICE}` and await its health ${SERVICE} .PHONY : restart -symlink : ## Confirm that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml - if [[ ${ENVIRONMENT} == "staging" ]]; then \ +symlink : ## Confirm that ./Makefile links to ./docker.mk and that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml + if [[ ! -L "./Makefile" ]] || [[ ! "./Makefile" -ef "./docker.mk" ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + exit 1 ; \ + fi + if [[ "${ENVIRONMENT}" == "staging" ]]; then \ file="./docker-compose.production.yaml" ; \ else \ file="./docker-compose.${ENVIRONMENT}.yaml" ; \ fi && \ - if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef $${file} ]]; then \ - echo "./docker-compose.yaml does not link to $${file}" ; \ + if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef "$${file}" ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + exit 2 ; \ fi .PHONY : symlink diff --git a/docker.mk b/docker.mk index 0e9f5641..9b5c9892 100755 --- a/docker.mk +++ b/docker.mk @@ -30,14 +30,19 @@ environment : ## Print value of variable `ENVIRONMENT` @echo ${ENVIRONMENT} .PHONY : environment -symlink : ## Confirm that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml - if [[ ${ENVIRONMENT} == "staging" ]]; then \ +symlink : ## Confirm that ./Makefile links to ./docker.mk and that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml + if [[ ! -L "./Makefile" ]] || [[ ! "./Makefile" -ef "./docker.mk" ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + exit 1 ; \ + fi + if [[ "${ENVIRONMENT}" == "staging" ]]; then \ file="./docker-compose.production.yaml" ; \ else \ file="./docker-compose.${ENVIRONMENT}.yaml" ; \ fi && \ - if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef $${file} ]]; then \ - echo "./docker-compose.yaml does not link to $${file}" ; \ + if [[ ! -L "./docker-compose.yaml" ]] || [[ ! "./docker-compose.yaml" -ef "$${file}" ]]; then \ + echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + exit 2 ; \ fi .PHONY : symlink @@ -88,7 +93,7 @@ remove : ## Remove stopped services --volumes ${SERVICE} .PHONY : remove -up : dotenv ## (Re)create and start services +up : symlink dotenv ## (Re)create and start services docker compose up \ --no-build \ --no-deps \ From e2c88152d656ad14fac552787069df1d53fb3a8d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Mar 2026 16:19:40 +0100 Subject: [PATCH 745/878] Increase retries to wait longer for services to become healthy --- docker-compose.development.yaml | 20 ++++++++++++++------ docker-compose.production.yaml | 20 ++++++++++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 9302b7a7..f7112851 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -28,7 +28,7 @@ services: restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] + test: ["CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'"] interval: "30s" timeout: "5s" retries: 3 @@ -122,10 +122,14 @@ services: restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example - test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] + test: + [ + "CMD-SHELL", + "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1", + ] interval: "30s" timeout: "5s" - retries: 3 + retries: 6 start_period: "60s" start_interval: "5s" labels: @@ -164,10 +168,14 @@ services: - "dev" restart: "unless-stopped" healthcheck: - test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] + test: + [ + "CMD-SHELL", + "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1", + ] interval: "30s" timeout: "5s" - retries: 3 + retries: 6 start_period: "60s" start_interval: "5s" labels: @@ -196,7 +204,7 @@ services: - "daemon off;" restart: "unless-stopped" healthcheck: - test: [ "CMD-SHELL", "service nginx status || exit 1" ] + test: ["CMD-SHELL", "service nginx status || exit 1"] interval: "30s" timeout: "5s" retries: 3 diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 0afb4ef4..99406bb5 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -28,7 +28,7 @@ services: restart: "unless-stopped" healthcheck: # pg_isready: https://www.postgresql.org/docs/13/app-pg-isready.html - test: [ "CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'" ] + test: ["CMD-SHELL", "pg_isready --username='${POSTGRES_USER:?required}'"] interval: "30s" timeout: "5s" retries: 3 @@ -86,10 +86,14 @@ services: restart: "unless-stopped" healthcheck: # https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-5.0#docker-example - test: [ "CMD-SHELL", "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1" ] + test: + [ + "CMD-SHELL", + "curl --silent --fail --output /dev/null http://backend:8080/health || exit 1", + ] interval: "30s" timeout: "5s" - retries: 3 + retries: 6 start_period: "60s" start_interval: "5s" labels: @@ -113,10 +117,14 @@ services: - "/app/server.js" restart: "unless-stopped" healthcheck: - test: [ "CMD-SHELL", "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1" ] + test: + [ + "CMD-SHELL", + "wget --quiet --output-document /dev/null http://frontend:8080 || exit 1", + ] interval: "30s" timeout: "5s" - retries: 3 + retries: 6 start_period: "60s" start_interval: "5s" labels: @@ -143,7 +151,7 @@ services: - "daemon off;" restart: "unless-stopped" healthcheck: - test: [ "CMD-SHELL", "service nginx status || exit 1" ] + test: ["CMD-SHELL", "service nginx status || exit 1"] interval: "30s" timeout: "5s" retries: 3 From f8af891f2dfb503732192fa78f836846d32605d1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Mar 2026 19:58:47 +0100 Subject: [PATCH 746/878] Add domain life-cycle data --- backend/.gitignore | 1 + backend/Makefile | 5 + .../ApiRequests/Queries/LifeCycleData.graphql | 5 + .../Queries/LifeCycleDataFields.graphql | 4 + backend/src/Data/ApplicationDbContext.cs | 36 + backend/src/Data/GetHttpsResource.cs | 34 +- backend/src/Data/IData.cs | 3 +- backend/src/Data/LifeCycleData.cs | 72 + backend/src/Enumerations/DataKind.cs | 1 + .../src/GraphQl/DataX/DeleteDataMutation.cs | 8 +- ...ttpsResourceTreeNonRootVertexFilterType.cs | 3 +- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 14 +- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 14 +- .../GetHttpsResourcesByDataIdDataLoader.cs | 13 +- .../CreateGetHttpsResourceMutation.cs | 1 + .../GetHttpsResourceFilterType.cs | 3 +- .../GetHttpsResourceQueries.cs | 4 +- .../GetHttpsResourceResolvers.cs | 11 +- .../GetHttpsResources/GetHttpsResourceType.cs | 22 +- .../CreateLifeCycleDataMutation.cs | 166 + .../LifeCycleDataByIdDataLoader.cs | 20 + .../LifeCycleDataX/LifeCycleDataFilterType.cs | 17 + .../LifeCycleDataX/LifeCycleDataQueries.cs | 103 + .../LifeCycleDataX/LifeCycleDataSortType.cs | 17 + .../LifeCycleDataX/LifeCycleDataType.cs | 16 + ...0260316165622_AddLifeCycleData.Designer.cs | 3127 +++++++++++++++++ .../20260316165622_AddLifeCycleData.cs | 255 ++ .../ApplicationDbContextModelSnapshot.cs | 430 ++- backend/src/Migrations/migrate.sql | 218 ++ ...ata_to_20260316165622_AddLifeCycleData.sql | 218 ++ backend/src/Services/ApiRequestService.cs | 2 +- .../src/Services/ResponseApprovalService.cs | 10 +- .../GraphQlSchemaTests.IsUnchanged.snap | 116 + frontend/components/Layout.tsx | 144 +- .../data/lifeCycle/LifeCycleData.tsx | 57 + frontend/package.json | 2 +- frontend/pages/data/life-cycle/[uuid].tsx | 22 + frontend/pages/data/life-cycle/index.tsx | 256 ++ frontend/paths.ts | 184 +- frontend/queries/data.graphql | 380 +- frontend/type-defs.graphqls | 116 + frontend/yarn.lock | 8 +- 42 files changed, 5733 insertions(+), 405 deletions(-) create mode 100644 backend/src/ApiRequests/Queries/LifeCycleData.graphql create mode 100644 backend/src/ApiRequests/Queries/LifeCycleDataFields.graphql create mode 100644 backend/src/Data/LifeCycleData.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs create mode 100644 backend/src/Migrations/20260316165622_AddLifeCycleData.Designer.cs create mode 100644 backend/src/Migrations/20260316165622_AddLifeCycleData.cs create mode 100644 backend/src/Migrations/migrate_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20260316165622_AddLifeCycleData.sql create mode 100644 frontend/components/data/lifeCycle/LifeCycleData.tsx create mode 100644 frontend/pages/data/life-cycle/[uuid].tsx create mode 100644 frontend/pages/data/life-cycle/index.tsx diff --git a/backend/.gitignore b/backend/.gitignore index 8a7ea17d..decf6a4f 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,5 +1,6 @@ /Dockerfile /src/core +/src/bin\\Debug /src/tempkey.rsa /src/wwwroot/assets/ /src/files/ diff --git a/backend/Makefile b/backend/Makefile index f44a8fb0..c4006112 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -181,6 +181,7 @@ schema : ## Fetch GraphQL schema using `hasura/graphqurl` has-pending-model-changes : ## Check whether there have been any model changes made since the last migration was added dotnet ef \ --project ./src/Database.csproj \ + --startup-project ./src/Database.csproj \ migrations \ has-pending-model-changes .PHONY : has-pending-model-changes @@ -191,6 +192,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot echo old=$$old && \ dotnet ef \ --project ./src/Database.csproj \ + --startup-project ./src/Database.csproj \ migrations \ add \ ${NAME} && \ @@ -200,6 +202,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot rollback_path=./src/Migrations/rollback_from_$${new}_to_$${old}.sql && \ dotnet ef \ --project ./src/Database.csproj \ + --startup-project ./src/Database.csproj \ migrations \ script \ --idempotent \ @@ -208,6 +211,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot --output $${migrate_path} && \ dotnet ef \ --project ./src/Database.csproj \ + --startup-project ./src/Database.csproj \ migrations \ script \ --idempotent \ @@ -224,6 +228,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot # update-database : ## Update the database to the latest database schema # dotnet ef \ # --project ./src/Database.csproj \ +# --startup-project ./src/Database.csproj \ # database \ # update # .PHONY : update-database diff --git a/backend/src/ApiRequests/Queries/LifeCycleData.graphql b/backend/src/ApiRequests/Queries/LifeCycleData.graphql new file mode 100644 index 00000000..ac4cd973 --- /dev/null +++ b/backend/src/ApiRequests/Queries/LifeCycleData.graphql @@ -0,0 +1,5 @@ +query LifeCycleData($id: Uuid!) { + lifeCycleData(id: $id) { + ...LifeCycleDataFields + } +} diff --git a/backend/src/ApiRequests/Queries/LifeCycleDataFields.graphql b/backend/src/ApiRequests/Queries/LifeCycleDataFields.graphql new file mode 100644 index 00000000..657c82ee --- /dev/null +++ b/backend/src/ApiRequests/Queries/LifeCycleDataFields.graphql @@ -0,0 +1,4 @@ +fragment LifeCycleDataFields on LifeCycleData { + id + ...DataFields +} diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index a8d49fca..34bfce41 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -56,12 +56,14 @@ DbContextOptions options .Include(_ => _.CalorimetricData) .Include(_ => _.GeometricData) .Include(_ => _.HygrothermalData) + .Include(_ => _.LifeCycleData) .Include(_ => _.OpticalData) .Include(_ => _.PhotovoltaicData); public DbSet CalorimetricData { get; private set; } = default!; public DbSet GeometricData { get; private set; } = default!; public DbSet HygrothermalData { get; private set; } = default!; + public DbSet LifeCycleData { get; private set; } = default!; public DbSet OpticalData { get; private set; } = default!; public DbSet PhotovoltaicData { get; private set; } = default!; @@ -72,6 +74,7 @@ public IQueryable Data(DataKind dataKind) DataKind.CALORIMETRIC_DATA => CalorimetricData, DataKind.GEOMETRIC_DATA => GeometricData, DataKind.HYGROTHERMAL_DATA => HygrothermalData, + DataKind.LIFE_CYCLE_DATA => LifeCycleData, DataKind.OPTICAL_DATA => OpticalData, DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, _ => throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}."), @@ -106,6 +109,12 @@ QueryContext queryContext .Where(where) .ToAsyncEnumerable() ) + .Union( + LifeCycleData.AsQueryable() + .With(queryContext, sort => sort.StabilizeOrder()) + .Where(where) + .ToAsyncEnumerable() + ) .Union( OpticalData.AsQueryable() .With(queryContext, sort => sort.StabilizeOrder()) @@ -210,6 +219,14 @@ EntityTypeBuilder builder """ ) .IsUnique(true); + builder + .HasIndex(_ => new { _.LifeCycleDataId }) + .HasFilter( + $""" + {nameof(GetHttpsResource.LifeCycleDataId).Enquote()} IS NOT NULL AND {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + """ + ) + .IsUnique(true); builder .HasIndex(_ => new { _.OpticalDataId }) .HasFilter( @@ -306,6 +323,20 @@ EntityTypeBuilder builder return builder; } + private static + EntityTypeBuilder + ConfigureLifeCycleData( + EntityTypeBuilder builder + ) + { + builder + .HasMany(_ => _.Resources) + .WithOne(_ => _.LifeCycleData) + .HasForeignKey(_ => _.LifeCycleDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + private static EntityTypeBuilder ConfigureOpticalData( @@ -397,6 +428,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureEntity(modelBuilder.Entity()) ).ToTable("hygrothermal_data") ); + ConfigureLifeCycleData( + ConfigureData( + ConfigureEntity(modelBuilder.Entity()) + ).ToTable("lifeCycle_data") + ); ConfigureOpticalData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index cec3f128..033bc908 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Database.Enumerations; using Database.Extensions; -using Database.GraphQl; using Database.Utilities; using EntityFrameworkCore.Projectables; @@ -31,6 +30,7 @@ public sealed class GetHttpsResource nameof(CalorimetricDataId), nameof(GeometricDataId), nameof(HygrothermalDataId), + nameof(LifeCycleDataId), nameof(OpticalDataId), nameof(PhotovoltaicDataId) ]; @@ -83,6 +83,7 @@ public GetHttpsResource( Guid? calorimetricDataId, Guid? geometricDataId, Guid? hygrothermalDataId, + Guid? lifeCycleDataId, Guid? opticalDataId, Guid? photovoltaicDataId, Guid parentId, @@ -97,10 +98,11 @@ ToTreeVertexAppliedConversionMethod appliedConversionMethod ) { CalorimetricDataId = calorimetricDataId; + GeometricDataId = geometricDataId; HygrothermalDataId = hygrothermalDataId; + LifeCycleDataId = lifeCycleDataId; OpticalDataId = opticalDataId; PhotovoltaicDataId = photovoltaicDataId; - GeometricDataId = geometricDataId; ParentId = parentId; ArchivedFilesMetaInformation = archivedFilesMetaInformation; AppliedConversionMethod = appliedConversionMethod; @@ -111,10 +113,11 @@ private void AssertThatExactlyOneDataIdIsNonNull() { var nonNullDataIdCount = new Guid?[] { CalorimetricDataId, + GeometricDataId, HygrothermalDataId, + LifeCycleDataId, OpticalDataId, - PhotovoltaicDataId, - GeometricDataId + PhotovoltaicDataId } .NotNull() .Count(); @@ -144,10 +147,10 @@ private void AssertThatExactlyOneDataIdIsNonNull() // Note that at least one data ID is always present. So `Guid.Empty` will never be used. [NotMapped] - public Guid DataId => CalorimetricDataId ?? HygrothermalDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? GeometricDataId ?? Guid.Empty; + public Guid DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; [NotMapped] - public IData? Data => CalorimetricData ?? HygrothermalData ?? OpticalData ?? GeometricData ?? PhotovoltaicData as IData; + public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; [Projectable] public Guid? GetDataId(DataKind dataKind) => @@ -156,6 +159,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() DataKind.CALORIMETRIC_DATA => CalorimetricDataId, DataKind.GEOMETRIC_DATA => GeometricDataId, DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, DataKind.OPTICAL_DATA => OpticalDataId, DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), @@ -168,6 +172,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() DataKind.CALORIMETRIC_DATA => CalorimetricData, DataKind.GEOMETRIC_DATA => GeometricData, DataKind.HYGROTHERMAL_DATA => HygrothermalData, + DataKind.LIFE_CYCLE_DATA => LifeCycleData, DataKind.OPTICAL_DATA => OpticalData, DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), @@ -178,11 +183,21 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(CalorimetricData.Resources))] public CalorimetricData? CalorimetricData { get; set; } + public Guid? GeometricDataId { get; private set; } + + [InverseProperty(nameof(GeometricData.Resources))] + public GeometricData? GeometricData { get; set; } + public Guid? HygrothermalDataId { get; private set; } [InverseProperty(nameof(HygrothermalData.Resources))] public HygrothermalData? HygrothermalData { get; set; } + public Guid? LifeCycleDataId { get; private set; } + + [InverseProperty(nameof(LifeCycleData.Resources))] + public LifeCycleData? LifeCycleData { get; set; } + public Guid? OpticalDataId { get; private set; } [InverseProperty(nameof(OpticalData.Resources))] @@ -193,11 +208,6 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(PhotovoltaicData.Resources))] public PhotovoltaicData? PhotovoltaicData { get; set; } - public Guid? GeometricDataId { get; private set; } - - [InverseProperty(nameof(GeometricData.Resources))] - public GeometricData? GeometricData { get; set; } - public Guid? ParentId { get; private set; } public ToTreeVertexAppliedConversionMethod? AppliedConversionMethod { get; private set; } diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index d9989a55..4202dcbc 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using System.Threading.Tasks; @@ -12,6 +12,7 @@ namespace Database.Data; [JsonDerivedType(typeof(CalorimetricData), typeDiscriminator: nameof(CalorimetricData))] [JsonDerivedType(typeof(GeometricData), typeDiscriminator: nameof(GeometricData))] [JsonDerivedType(typeof(HygrothermalData), typeDiscriminator: nameof(HygrothermalData))] +[JsonDerivedType(typeof(LifeCycleData), typeDiscriminator: nameof(LifeCycleData))] [JsonDerivedType(typeof(OpticalData), typeDiscriminator: nameof(OpticalData))] [JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] public interface IData : IEntity diff --git a/backend/src/Data/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs new file mode 100644 index 00000000..4290aafc --- /dev/null +++ b/backend/src/Data/LifeCycleData.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.Threading.Tasks; +using NodaTime; + +namespace Database.Data; + +public sealed class LifeCycleData + : DataX +{ + public LifeCycleData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + OffsetDateTime createdAt, + AppliedMethod appliedMethod + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt, + appliedMethod + ) + { + } + + // `DbContext` needs this constructor without owned entities. + public LifeCycleData( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + OffsetDateTime createdAt + ) : base( + userId, + locale, + componentId, + name, + description, + warnings, + creatorId, + createdAt + ) + { + } + + [InverseProperty(nameof(GetHttpsResource.LifeCycleData))] + public override ICollection Resources { get; } = []; + + public override Task ExtractAndSetValuesFromFile( + string filePath, + Guid dataFormatId + ) + { + // if (dataFormatId == IData.BedJsonDataFormatId) + // { + // } + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/backend/src/Enumerations/DataKind.cs b/backend/src/Enumerations/DataKind.cs index 3f626f64..840c5668 100644 --- a/backend/src/Enumerations/DataKind.cs +++ b/backend/src/Enumerations/DataKind.cs @@ -8,6 +8,7 @@ public enum DataKind CALORIMETRIC_DATA, GEOMETRIC_DATA, HYGROTHERMAL_DATA, + LIFE_CYCLE_DATA, OPTICAL_DATA, PHOTOVOLTAIC_DATA } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 6eb50647..2bf59bb5 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -1,8 +1,6 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -10,7 +8,6 @@ using Database.Enumerations; using Database.Extensions; using HotChocolate.Types; -using Microsoft.AspNetCore.Mvc; namespace Database.GraphQl.DataX; @@ -120,6 +117,9 @@ await context.Entry(data) case HygrothermalData hygrothermalData: context.HygrothermalData.Remove(hygrothermalData); break; + case LifeCycleData lifeCycleData: + context.LifeCycleData.Remove(lifeCycleData); + break; case PhotovoltaicData photovoltaicData: context.PhotovoltaicData.Remove(photovoltaicData); break; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index fd8400c7..071e3544 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; @@ -16,6 +16,7 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.CalorimetricData).Ignore(); descriptor.Field(x => x.GeometricData).Ignore(); descriptor.Field(x => x.HygrothermalData).Ignore(); + descriptor.Field(x => x.LifeCycleData).Ignore(); descriptor.Field(x => x.OpticalData).Ignore(); descriptor.Field(x => x.PhotovoltaicData).Ignore(); } diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index ca52b426..4cf9f4a9 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; @@ -18,9 +18,15 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId != null && ids.Contains(x.CalorimetricDataId ?? - x.HygrothermalDataId ?? x.OpticalDataId ?? - x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) + x.ParentId != null && ids.Contains( + x.CalorimetricDataId + ?? x.GeometricDataId + ?? x.HygrothermalDataId + ?? x.LifeCycleDataId + ?? x.OpticalDataId + ?? x.PhotovoltaicDataId + ?? Guid.Empty + ) ), x => x.DataId ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index 389c282c..b16a9dce 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; @@ -18,9 +18,15 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId == null && ids.Contains(x.CalorimetricDataId ?? - x.HygrothermalDataId ?? x.OpticalDataId ?? - x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) + x.ParentId == null && ids.Contains( + x.CalorimetricDataId + ?? x.GeometricDataId + ?? x.HygrothermalDataId + ?? x.LifeCycleDataId + ?? x.OpticalDataId + ?? x.PhotovoltaicDataId + ?? Guid.Empty + ) ), x => x.DataId ) diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index 1d9780df..83d11dfe 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; @@ -18,8 +18,15 @@ IDbContextFactory dbContextFactory dbContextFactory, (dbContext, ids) => dbContext.GetHttpsResources.AsNoTracking().Where(x => - ids.Contains(x.CalorimetricDataId ?? - x.HygrothermalDataId ?? x.OpticalDataId ?? x.PhotovoltaicDataId ?? x.GeometricDataId ?? Guid.Empty) + ids.Contains( + x.CalorimetricDataId + ?? x.GeometricDataId + ?? x.HygrothermalDataId + ?? x.LifeCycleDataId + ?? x.OpticalDataId + ?? x.PhotovoltaicDataId + ?? Guid.Empty + ) ), x => x.DataId ) diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 52e9aeeb..dc2ef628 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -38,6 +38,7 @@ public GetHttpsResource ToDomainModel(string? fileExtension) DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, + DataKind == DataKind.LIFE_CYCLE_DATA ? DataId : null, DataKind == DataKind.OPTICAL_DATA ? DataId : null, DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, ParentId, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 34498285..162c1e39 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using Database.GraphQl.Entities; using HotChocolate.Data.Filters; @@ -23,6 +23,7 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.CalorimetricData); descriptor.Field(x => x.GeometricData); descriptor.Field(x => x.HygrothermalData); + descriptor.Field(x => x.LifeCycleData); descriptor.Field(x => x.OpticalData); descriptor.Field(x => x.PhotovoltaicData); } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index bbf8910b..6302b7a9 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -30,6 +30,7 @@ ISortingContext sorting .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState != PublishingState.PENDING) .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState != PublishingState.PENDING) .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState != PublishingState.PENDING) + .Where(_ => _.LifeCycleData == null || _.LifeCycleData.PublishingState != PublishingState.PENDING) .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState != PublishingState.PENDING) .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING); } @@ -54,6 +55,7 @@ CancellationToken cancellationToken .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState == PublishingState.PENDING) .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState == PublishingState.PENDING) .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState == PublishingState.PENDING) + .Where(_ => _.LifeCycleData == null || _.LifeCycleData.PublishingState == PublishingState.PENDING) .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState == PublishingState.PENDING) .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState == PublishingState.PENDING); } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index b6b3b251..9f674838 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.GeometricDataX; using Database.GraphQl.HygrothermalDataX; +using Database.GraphQl.LifeCycleDataX; using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; using HotChocolate; @@ -17,6 +18,7 @@ public sealed class GetHttpsResourceResolvers [Parent] GetHttpsResource getHttpsResource, CalorimetricDataByIdDataLoader calorimetricDataById, HygrothermalDataByIdDataLoader hygrothermalDataById, + LifeCycleDataByIdDataLoader lifeCycleDataById, OpticalDataByIdDataLoader opticalDataById, PhotovoltaicDataByIdDataLoader photovoltaicDataById, GeometricDataByIdDataLoader geometricDataById, @@ -44,6 +46,13 @@ CancellationToken cancellationToken cancellationToken ); } + if (getHttpsResource.LifeCycleDataId is not null) + { + return await lifeCycleDataById.LoadAsync( + getHttpsResource.LifeCycleDataId ?? Guid.Empty, + cancellationToken + ); + } if (getHttpsResource.OpticalDataId is not null) { return await opticalDataById.LoadAsync( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index e1b4b0bf..0a10606e 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using HotChocolate.Types; namespace Database.GraphQl.GetHttpsResources; @@ -41,6 +41,12 @@ IObjectTypeDescriptor descriptor descriptor .Field(x => x.CalorimetricData) .Ignore(); + descriptor + .Field(x => x.GeometricDataId) + .Ignore(); + descriptor + .Field(x => x.GeometricData) + .Ignore(); descriptor .Field(x => x.HygrothermalDataId) .Ignore(); @@ -48,26 +54,26 @@ IObjectTypeDescriptor descriptor .Field(x => x.HygrothermalData) .Ignore(); descriptor - .Field(x => x.OpticalDataId) + .Field(x => x.LifeCycleDataId) .Ignore(); descriptor - .Field(x => x.OpticalData) + .Field(x => x.LifeCycleData) .Ignore(); descriptor - .Field(x => x.PhotovoltaicDataId) + .Field(x => x.OpticalDataId) .Ignore(); descriptor - .Field(x => x.PhotovoltaicData) + .Field(x => x.OpticalData) .Ignore(); descriptor - .Field(x => x.GeometricDataId) + .Field(x => x.PhotovoltaicDataId) .Ignore(); descriptor - .Field(x => x.GeometricData) + .Field(x => x.PhotovoltaicData) .Ignore(); descriptor .Field(x => x.Data) .ResolveWith(t => - t.GetData(default!, default!, default!, default!, default!, default!, default!)); + t.GetData(default!, default!, default!, default!, default!, default!, default!, default!)); } } \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs new file mode 100644 index 00000000..77b639a0 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Extensions; +using Database.GraphQl.DataX; +using Database.Services; +using HotChocolate; +using HotChocolate.Types; +using NodaTime; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed record CreateLifeCycleDataInput( + [property: GraphQLType>] string Locale, + Guid ComponentId, + string? Name, + string? Description, + string[] Warnings, + OffsetDateTime CreatedAt, + Guid CreatorId, + AppliedMethodInput AppliedMethod, + RootGetHttpsResourceInput RootResource +) : IValidateCreateInput +{ + public LifeCycleData ToDomainModel( + Guid? userId, + string? fileExtension + ) + { + var data = new LifeCycleData( + userId, + Locale, + ComponentId, + Name, + Description, + Warnings, + CreatorId, + CreatedAt, + AppliedMethod.ToDomainModel() + ); + data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + return data; + } +}; + +[SuppressMessage("Naming", "CA1707")] +public enum CreateLifeCycleDataErrorCode +{ + UNKNOWN, + UNAUTHORIZED, + UNAUTHENTICATED, + UNKNOWN_COMPONENT, + ILLEGAL_CREATED_AT, + UNKNOWN_CREATOR, + UNKNOWN_APPLIED_METHOD, + UNKNOWN_DATABASE, + UNKNOWN_DATA, + UNKNOWN_DATA_FORMAT, + CREATING_RESPONSE_APPROVAL_FAILED +} + +public sealed record CreateLifeCycleDataError( + CreateLifeCycleDataErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record CreateLifeCycleDataPayload( + LifeCycleData? LifeCycleData, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class CreateLifeCycleDataMutation +: CreateDataMutationBase +{ + // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + protected override CreateLifeCycleDataPayload NewPayload( + LifeCycleData? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override CreateLifeCycleDataError NewError( + CreateLifeCycleDataErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task CreateLifeCycleDataAsync( + CreateLifeCycleDataInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IComponentByIdDataLoader componentByIdDataLoader, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IMethodByIdDataLoader methodByIdDataLoader, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, + IDataFormatByIdDataLoader dataFormatByIdDataLoader, + ResponseApprovalService responseApprovalService, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + CreateLifeCycleDataErrorCode.UNAUTHENTICATED, + CreateLifeCycleDataErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await ValidateAsync( + input, + CreateLifeCycleDataErrorCode.ILLEGAL_CREATED_AT, + componentByIdDataLoader, + CreateLifeCycleDataErrorCode.UNKNOWN_COMPONENT, + institutionByIdDataLoader, + CreateLifeCycleDataErrorCode.UNKNOWN_CREATOR, + methodByIdDataLoader, + CreateLifeCycleDataErrorCode.UNKNOWN_APPLIED_METHOD, + dataByDatabaseAndIdAndKindDataLoader, + CreateLifeCycleDataErrorCode.UNKNOWN_DATABASE, + CreateLifeCycleDataErrorCode.UNKNOWN_DATA, + dataFormatByIdDataLoader, + CreateLifeCycleDataErrorCode.UNKNOWN_DATA_FORMAT, + cancellationToken + ) + ).Failed(out var dataFormat, out var validateErrorPayload) + ) + { + return validateErrorPayload; + } + + var lifeCycleData = input.ToDomainModel( + currentUserOrApplication.CurrentUser?.Uuid, + dataFormat.Extension + ); + context.LifeCycleData.Add(lifeCycleData); + await context.SaveChangesAsync(cancellationToken); + + if ((await CreateResponseApprovalAsync( + lifeCycleData, + CreateLifeCycleDataErrorCode.CREATING_RESPONSE_APPROVAL_FAILED, + responseApprovalService, + context, + cancellationToken + ) + ).Failed(out var createResponseApprovalErrorPayload) + ) + { + context.Remove(lifeCycleData); + await context.SaveChangesAsync(cancellationToken); + return createResponseApprovalErrorPayload; + } + + return NewPayload(lifeCycleData, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs new file mode 100644 index 00000000..c3bd0833 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs @@ -0,0 +1,20 @@ +using Database.Data; +using Database.GraphQl.Entities; +using GreenDonut; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed class LifeCycleDataByIdDataLoader( + IBatchScheduler batchScheduler, + DataLoaderOptions options, + IDbContextFactory dbContextFactory + ) + : EntityByIdDataLoader( + batchScheduler, + options, + dbContextFactory, + dbContext => dbContext.LifeCycleData + ) +{ +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs new file mode 100644 index 00000000..a61b41eb --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed class LifeCycleDataFilterType + : DataFilterTypeBase +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(LifeCycleDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs new file mode 100644 index 00000000..06b02dd5 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -0,0 +1,103 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.GraphQl.DataX; +using Database.Services; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace Database.GraphQl.LifeCycleDataX; + +[ExtendObjectType(nameof(Query))] +public sealed class LifeCycleDataQueries +: DataQueriesBase +{ + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public Task> GetAllLifeCycleDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + AccessRightsService accessRightsService, + IResolverContext resolverContext, + ISortingContext sorting, + CancellationToken cancellationToken + ) + { + return GetAllDataAsync( + context.LifeCycleData, + locale, + accessRightsService, + sorting, + resolverContext, + cancellationToken + ); + } + + [UsePaging] + // [UseProjection] // We disabled projections because when requesting `id` all results had the + // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. + [UseFiltering] + [UseSorting] + public Task> GetAllPendingLifeCycleDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + AccessRightsService accessRightsService, + IResolverContext resolverContext, + ISortingContext sorting, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return GetAllPendingDataAsync( + context.LifeCycleData, + locale, + accessRightsService, + sorting, + resolverContext, + authorization, + cancellationToken + ); + } + + [UseFiltering] + public Task HasLifeCycleDataAsync( + [GraphQLType] string? locale, + ApplicationDbContext context, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return HasDataAsync( + context.LifeCycleData, + locale, + resolverContext, + cancellationToken + ); + } + + public Task GetLifeCycleDataAsync( + Guid id, + [GraphQLType] string? locale, + LifeCycleDataByIdDataLoader byId, + AccessRightsService accessRightsService, + CancellationToken cancellationToken + ) + { + return GetDataAsync( + id, + locale, + byId, + accessRightsService, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs new file mode 100644 index 00000000..5dee0d7a --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs @@ -0,0 +1,17 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed class LifeCycleDataSortType + : DataSortTypeBase +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Field(x => x.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs new file mode 100644 index 00000000..3498cec9 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs @@ -0,0 +1,16 @@ +using Database.Data; +using Database.GraphQl.DataX; +using HotChocolate.Types; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed class LifeCycleDataType + : DataTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20260316165622_AddLifeCycleData.Designer.cs b/backend/src/Migrations/20260316165622_AddLifeCycleData.Designer.cs new file mode 100644 index 00000000..98d79fca --- /dev/null +++ b/backend/src/Migrations/20260316165622_AddLifeCycleData.Designer.cs @@ -0,0 +1,3127 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260316165622_AddLifeCycleData")] + partial class AddLifeCycleData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("lifeCycle_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260316165622_AddLifeCycleData.cs b/backend/src/Migrations/20260316165622_AddLifeCycleData.cs new file mode 100644 index 00000000..78f7675a --- /dev/null +++ b/backend/src/Migrations/20260316165622_AddLifeCycleData.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddLifeCycleData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() CASCADE;"); + + migrationBuilder.DropCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,life_cycle_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data"); + + migrationBuilder.AddColumn( + name: "LifeCycleDataId", + schema: "database", + table: "get_https_resource", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "lifeCycle_data", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + UserId = table.Column(type: "uuid", nullable: true), + Locale = table.Column(type: "text", nullable: false), + ComponentId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: true), + Description = table.Column(type: "text", nullable: true), + Warnings = table.Column(type: "text[]", nullable: false), + CreatorId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + AppliedMethod_MethodId = table.Column(type: "uuid", nullable: false), + Approval_Timestamp = table.Column(type: "timestamp with time zone", nullable: true), + Approval_Signature = table.Column(type: "text", nullable: true), + Approval_KeyFingerprint = table.Column(type: "text", nullable: true), + Approval_Query = table.Column(type: "text", nullable: true), + Approval_Variables = table.Column(type: "jsonb", nullable: true, defaultValueSql: "'{}'"), + Approval_Message = table.Column(type: "text", nullable: true), + Approval_ApproverId = table.Column(type: "uuid", nullable: true), + DataAccessRights_AllowedUserAndQuantity = table.Column>(type: "jsonb", nullable: true), + DataAccessRights_AllowedInstitutions = table.Column(type: "uuid[]", nullable: true), + DataAccessRights_AllowedApplications = table.Column(type: "text[]", nullable: true), + PublishingState = table.Column(type: "database.publishing_state", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_lifeCycle_data", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "lifeCycle_data_Approvals", + schema: "database", + columns: table => new + { + LifeCycleDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + ApproverId = table.Column(type: "uuid", nullable: false), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false), + Signature = table.Column(type: "text", nullable: false), + KeyFingerprint = table.Column(type: "text", nullable: false), + Query = table.Column(type: "text", nullable: false), + Variables = table.Column(type: "jsonb", nullable: false, defaultValueSql: "'{}'"), + Message = table.Column(type: "text", nullable: false), + Statement_Standard_Year = table.Column(type: "integer", nullable: true), + Statement_Standard_Numeration_Prefix = table.Column(type: "text", nullable: true), + Statement_Standard_Numeration_MainNumber = table.Column(type: "text", nullable: true), + Statement_Standard_Numeration_Suffix = table.Column(type: "text", nullable: true), + Statement_Standard_Standardizers = table.Column(type: "database.standardizer[]", nullable: true), + Statement_Standard_Locator = table.Column(type: "text", nullable: true), + Statement_Standard_Title = table.Column(type: "text", nullable: true), + Statement_Standard_Abstract = table.Column(type: "text", nullable: true), + Statement_Standard_Section = table.Column(type: "text", nullable: true), + Statement_Publication_Authors = table.Column(type: "text[]", nullable: true), + Statement_Publication_Doi = table.Column(type: "text", nullable: true), + Statement_Publication_ArXiv = table.Column(type: "text", nullable: true), + Statement_Publication_Urn = table.Column(type: "text", nullable: true), + Statement_Publication_WebAddress = table.Column(type: "text", nullable: true), + Statement_Publication_Title = table.Column(type: "text", nullable: true), + Statement_Publication_Abstract = table.Column(type: "text", nullable: true), + Statement_Publication_Section = table.Column(type: "text", nullable: true), + Statement_Publication_Exists = table.Column(type: "boolean", nullable: true), + Statement_Exists = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_lifeCycle_data_Approvals", x => new { x.LifeCycleDataId, x.Id }); + table.ForeignKey( + name: "FK_lifeCycle_data_Approvals_lifeCycle_data_LifeCycleDataId", + column: x => x.LifeCycleDataId, + principalSchema: "database", + principalTable: "lifeCycle_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "lifeCycle_data_Arguments", + schema: "database", + columns: table => new + { + AppliedMethodLifeCycleDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_lifeCycle_data_Arguments", x => new { x.AppliedMethodLifeCycleDataId, x.Id }); + table.ForeignKey( + name: "FK_lifeCycle_data_Arguments_lifeCycle_data_AppliedMethodLifeCy~", + column: x => x.AppliedMethodLifeCycleDataId, + principalSchema: "database", + principalTable: "lifeCycle_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "lifeCycle_data_Sources", + schema: "database", + columns: table => new + { + AppliedMethodLifeCycleDataId = table.Column(type: "uuid", nullable: false), + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Value_DataId = table.Column(type: "uuid", nullable: false), + Value_DataTimestamp = table.Column(type: "timestamp with time zone", nullable: false), + Value_DataKind = table.Column(type: "database.data_kind", nullable: false), + Value_DatabaseId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_lifeCycle_data_Sources", x => new { x.AppliedMethodLifeCycleDataId, x.Id }); + table.ForeignKey( + name: "FK_lifeCycle_data_Sources_lifeCycle_data_AppliedMethodLifeCycl~", + column: x => x.AppliedMethodLifeCycleDataId, + principalSchema: "database", + principalTable: "lifeCycle_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_LifeCycleDataId", + schema: "database", + table: "get_https_resource", + column: "LifeCycleDataId", + unique: true, + filter: "\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource", + sql: "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + migrationBuilder.AddForeignKey( + name: "FK_get_https_resource_lifeCycle_data_LifeCycleDataId", + schema: "database", + table: "get_https_resource", + column: "LifeCycleDataId", + principalSchema: "database", + principalTable: "lifeCycle_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() CASCADE;"); + + migrationBuilder.DropForeignKey( + name: "FK_get_https_resource_lifeCycle_data_LifeCycleDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropTable( + name: "lifeCycle_data_Approvals", + schema: "database"); + + migrationBuilder.DropTable( + name: "lifeCycle_data_Arguments", + schema: "database"); + + migrationBuilder.DropTable( + name: "lifeCycle_data_Sources", + schema: "database"); + + migrationBuilder.DropTable( + name: "lifeCycle_data", + schema: "database"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_LifeCycleDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropColumn( + name: "LifeCycleDataId", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,optical_data,photovoltaic_data") + .OldAnnotation("Npgsql:Enum:database.data_kind", "calorimetric_data,geometric_data,hygrothermal_data,life_cycle_data,optical_data,photovoltaic_data"); + + migrationBuilder.AddCheckConstraint( + name: "CK_GetHttpsResource_Exactly_One_Data_Set", + schema: "database", + table: "get_https_resource", + sql: "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 9acae4c4..ef9d6da4 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -22,12 +22,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("ProductVersion", "10.0.2") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); @@ -179,6 +179,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("HygrothermalDataId") .HasColumnType("uuid"); + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + b.Property("OpticalDataId") .HasColumnType("uuid"); @@ -208,6 +211,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsUnique() .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + b.HasIndex("OpticalDataId") .IsUnique() .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); @@ -228,14 +235,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) t.HasTrigger("data_ids_must_match"); - t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); }); b - .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") - .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => @@ -322,6 +329,53 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("institution_access_rights", "database"); }); + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.ToTable("lifeCycle_data", "database"); + }); + modelBuilder.Entity("Database.Data.OpticalData", b => { b.Property("Id") @@ -1427,6 +1481,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasForeignKey("HygrothermalDataId") .OnDelete(DeleteBehavior.Cascade); + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + b.HasOne("Database.Data.OpticalData", "OpticalData") .WithMany("Resources") .HasForeignKey("OpticalDataId") @@ -1525,6 +1584,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("HygrothermalData"); + b.Navigation("LifeCycleData"); + b.Navigation("OpticalData"); b.Navigation("Parent"); @@ -1886,6 +1947,360 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property>("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + modelBuilder.Entity("Database.Data.OpticalData", b => { b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => @@ -2677,6 +3092,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Resources"); }); + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("Resources"); + }); + modelBuilder.Entity("Database.Data.OpticalData", b => { b.Navigation("Resources"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index c83e85dc..afc66795 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1829,3 +1829,221 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TYPE database.data_kind ADD VALUE 'life_cycle_data' AFTER 'hygrothermal_data'; + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE EXTENSION IF NOT EXISTS pgcrypto; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD "LifeCycleDataId" uuid; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data" ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "UserId" uuid, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text, + "Description" text, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + "Approval_Timestamp" timestamp with time zone, + "Approval_Signature" text, + "Approval_KeyFingerprint" text, + "Approval_Query" text, + "Approval_Variables" jsonb DEFAULT ('{}'), + "Approval_Message" text, + "Approval_ApproverId" uuid, + "DataAccessRights_AllowedUserAndQuantity" jsonb, + "DataAccessRights_AllowedInstitutions" uuid[], + "DataAccessRights_AllowedApplications" text[], + "PublishingState" database.publishing_state NOT NULL, + CONSTRAINT "PK_lifeCycle_data" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Approvals" ( + "LifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "ApproverId" uuid NOT NULL, + "Timestamp" timestamp with time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Variables" jsonb NOT NULL DEFAULT ('{}'), + "Message" text NOT NULL, + "Statement_Standard_Year" integer, + "Statement_Standard_Numeration_Prefix" text, + "Statement_Standard_Numeration_MainNumber" text, + "Statement_Standard_Numeration_Suffix" text, + "Statement_Standard_Standardizers" database.standardizer[], + "Statement_Standard_Locator" text, + "Statement_Standard_Title" text, + "Statement_Standard_Abstract" text, + "Statement_Standard_Section" text, + "Statement_Publication_Authors" text[], + "Statement_Publication_Doi" text, + "Statement_Publication_ArXiv" text, + "Statement_Publication_Urn" text, + "Statement_Publication_WebAddress" text, + "Statement_Publication_Title" text, + "Statement_Publication_Abstract" text, + "Statement_Publication_Section" text, + "Statement_Publication_Exists" boolean, + "Statement_Exists" boolean NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Approvals" PRIMARY KEY ("LifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Approvals_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Arguments" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Arguments" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Arguments_lifeCycle_data_AppliedMethodLifeCy~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Sources" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp with time zone NOT NULL, + "Value_DataKind" database.data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Sources" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Sources_lifeCycle_data_AppliedMethodLifeCycl~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_LifeCycleDataId" ON database.get_https_resource ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = 1); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260316165622_AddLifeCycleData', '10.0.2'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20260316165622_AddLifeCycleData.sql b/backend/src/Migrations/migrate_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20260316165622_AddLifeCycleData.sql new file mode 100644 index 00000000..0fe528fb --- /dev/null +++ b/backend/src/Migrations/migrate_from_20251125181102_MirrorWidthAndHeightOfGeometricData_to_20260316165622_AddLifeCycleData.sql @@ -0,0 +1,218 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TYPE database.data_kind ADD VALUE 'life_cycle_data' AFTER 'hygrothermal_data'; + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE EXTENSION IF NOT EXISTS pgcrypto; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD "LifeCycleDataId" uuid; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data" ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "UserId" uuid, + "Locale" text NOT NULL, + "ComponentId" uuid NOT NULL, + "Name" text, + "Description" text, + "Warnings" text[] NOT NULL, + "CreatorId" uuid NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL, + "AppliedMethod_MethodId" uuid NOT NULL, + "Approval_Timestamp" timestamp with time zone, + "Approval_Signature" text, + "Approval_KeyFingerprint" text, + "Approval_Query" text, + "Approval_Variables" jsonb DEFAULT ('{}'), + "Approval_Message" text, + "Approval_ApproverId" uuid, + "DataAccessRights_AllowedUserAndQuantity" jsonb, + "DataAccessRights_AllowedInstitutions" uuid[], + "DataAccessRights_AllowedApplications" text[], + "PublishingState" database.publishing_state NOT NULL, + CONSTRAINT "PK_lifeCycle_data" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Approvals" ( + "LifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "ApproverId" uuid NOT NULL, + "Timestamp" timestamp with time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Variables" jsonb NOT NULL DEFAULT ('{}'), + "Message" text NOT NULL, + "Statement_Standard_Year" integer, + "Statement_Standard_Numeration_Prefix" text, + "Statement_Standard_Numeration_MainNumber" text, + "Statement_Standard_Numeration_Suffix" text, + "Statement_Standard_Standardizers" database.standardizer[], + "Statement_Standard_Locator" text, + "Statement_Standard_Title" text, + "Statement_Standard_Abstract" text, + "Statement_Standard_Section" text, + "Statement_Publication_Authors" text[], + "Statement_Publication_Doi" text, + "Statement_Publication_ArXiv" text, + "Statement_Publication_Urn" text, + "Statement_Publication_WebAddress" text, + "Statement_Publication_Title" text, + "Statement_Publication_Abstract" text, + "Statement_Publication_Section" text, + "Statement_Publication_Exists" boolean, + "Statement_Exists" boolean NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Approvals" PRIMARY KEY ("LifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Approvals_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Arguments" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Arguments" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Arguments_lifeCycle_data_AppliedMethodLifeCy~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE TABLE database."lifeCycle_data_Sources" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp with time zone NOT NULL, + "Value_DataKind" database.data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_lifeCycle_data_Sources" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_lifeCycle_data_Sources_lifeCycle_data_AppliedMethodLifeCycl~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_LifeCycleDataId" ON database.get_https_resource ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL AND "ParentId" IS NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "CK_GetHttpsResource_Exactly_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = 1); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260316165622_AddLifeCycleData') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260316165622_AddLifeCycleData', '10.0.2'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index 027897cd..f9d2398a 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index e529aa1c..c47a308a 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; @@ -57,6 +57,13 @@ ILogger logger "HygrothermalData.graphql" ]; + private static readonly string[] s_lifeCycleDataQueryFileNames = + [ + "DataFields.graphql", + "LifeCycleDataFields.graphql", + "LifeCycleData.graphql" + ]; + private static readonly string[] s_photovoltaicDataQueryFileNames = [ "DataFields.graphql", @@ -108,6 +115,7 @@ public async Task CreateResponseApproval(IData dataObject, Can CalorimetricData data => await Query(data.Id, s_calorimetricDataQueryFileNames, cancellationToken), GeometricData data => await Query(data.Id, s_geometricDataQueryFileNames, cancellationToken), HygrothermalData data => await Query(data.Id, s_hygrothermalDataQueryFileNames, cancellationToken), + LifeCycleData data => await Query(data.Id, s_lifeCycleDataQueryFileNames, cancellationToken), OpticalData data => await Query(data.Id, s_opticalDataQueryFileNames, cancellationToken), PhotovoltaicData data => await Query(data.Id, s_photovoltaicDataQueryFileNames, cancellationToken), _ => throw new ArgumentOutOfRangeException($"Unsupported data type {dataObject.GetType()}"), diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 1b794b4f..37a3c68f 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -196,6 +196,18 @@ type CreateHygrothermalDataPayload { query: Query! } +type CreateLifeCycleDataError implements UserError { + code: CreateLifeCycleDataErrorCode! + message: String! + path: [String!]! +} + +type CreateLifeCycleDataPayload { + errors: [CreateLifeCycleDataError!] + lifeCycleData: LifeCycleData + query: Query! +} + type CreateOpticalDataError implements UserError { code: CreateOpticalDataErrorCode! message: String! @@ -476,6 +488,48 @@ type KeyValuePairOfGuidAndNullableOfUInt32 { value: NonNegativeInt } +type LifeCycleData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type LifeCycleDataConnection { + "A list of edges." + edges: [LifeCycleDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type LifeCycleDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: LifeCycleData! +} + type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") @@ -483,6 +537,7 @@ type Mutation { createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createLifeCycleData(input: CreateLifeCycleDataInput!): CreateLifeCycleDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsPropositionInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") @@ -661,10 +716,12 @@ type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") @@ -681,9 +738,11 @@ type Query { hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasLifeCycleData(locale: Locale where: LifeCycleDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + lifeCycleData(id: Uuid! locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." @@ -1048,6 +1107,18 @@ input CreateHygrothermalDataInput { warnings: [String!]! } +input CreateLifeCycleDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! cielabColors: [CielabColorInput!]! @@ -1334,6 +1405,7 @@ input GetHttpsResourcePropositionInput { calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -1436,6 +1508,34 @@ input KeyValuePairOfGuidAndNullableOfUInt32Input { value: NonNegativeInt } +input LifeCycleDataPropositionInput { + and: [LifeCycleDataPropositionInput!] + or: [LifeCycleDataPropositionInput!] + id: UuidPropositionInput + userId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + warnings: StringsPropositionInput +} + +input LifeCycleDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + input LocalDatePropositionInput { and: [LocalDatePropositionInput!] or: [LocalDatePropositionInput!] @@ -1747,6 +1847,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -2142,6 +2243,20 @@ enum CreateHygrothermalDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum CreateLifeCycleDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED @@ -2182,6 +2297,7 @@ enum DataKind { CALORIMETRIC_DATA GEOMETRIC_DATA HYGROTHERMAL_DATA + LIFE_CYCLE_DATA OPTICAL_DATA PHOTOVOLTAIC_DATA } diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index dec18595..1f107067 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -7,86 +7,90 @@ import paths from "../paths"; import { useCookies } from "react-cookie"; const navItems = [ - { - path: paths.home, - label: "Home", - }, - { - path: paths.calorimetricData, - label: "Calorimetric Data", - }, - { - path: paths.geometricData, - label: "Geometric Data", - }, - { - path: paths.hygrothermalData, - label: "Hygrothermal Data", - }, - { - path: paths.opticalData, - label: "Optical Data", - }, - { - path: paths.photovoltaicData, - label: "Photovoltaic Data", - }, - { - path: paths.database, - label: "Database", - }, + { + path: paths.home, + label: "Home", + }, + { + path: paths.calorimetricData, + label: "Calorimetric Data", + }, + { + path: paths.geometricData, + label: "Geometric Data", + }, + { + path: paths.hygrothermalData, + label: "Hygrothermal Data", + }, + { + path: paths.lifeCycleData, + label: "Life-Cycle Data", + }, + { + path: paths.opticalData, + label: "Optical Data", + }, + { + path: paths.photovoltaicData, + label: "Photovoltaic Data", + }, + { + path: paths.database, + label: "Database", + }, ]; export type LayoutProps = { - children?: ReactNode; + children?: ReactNode; }; const cookieConsentName = "consent"; const cookieConsentValue = "yes"; export default function Layout({ children }: LayoutProps) { - const appTitle = "TestLab Solar Façades"; + const appTitle = "TestLab Solar Façades"; - const [cookies, setCookie] = useCookies([cookieConsentName]); - const shouldShowCookieConsent = - cookies[cookieConsentName] != cookieConsentValue; - const { modal } = App.useApp(); + const [cookies, setCookie] = useCookies([cookieConsentName]); + const shouldShowCookieConsent = + cookies[cookieConsentName] != cookieConsentValue; + const { modal } = App.useApp(); - useEffect(() => { - if (shouldShowCookieConsent) { - modal.info({ - title: "Cookie Consent", - content: ( - - This website employs cookies to make it work securely. As these - cookies are essential you need to agree to their usage to use this - website. - - ), - okText: "I agree", - onOk: () => { - setCookie(cookieConsentName, cookieConsentValue); - }, - }); - } - }, [shouldShowCookieConsent, setCookie]); + useEffect(() => { + if (shouldShowCookieConsent) { + modal.info({ + title: "Cookie Consent", + content: ( + + This website employs cookies to make it work securely. As these + cookies are essential you need to agree to their usage to use this + website. + + ), + okText: "I agree", + onOk: () => { + setCookie(cookieConsentName, cookieConsentValue); + }, + }); + } + }, [shouldShowCookieConsent, setCookie]); - return ( - - - {appTitle} - - - - - - - - {children} - - -
- - - ); + return ( + + + {appTitle} + + + + + + + + {children} + + +
+ + + ); } diff --git a/frontend/components/data/lifeCycle/LifeCycleData.tsx b/frontend/components/data/lifeCycle/LifeCycleData.tsx new file mode 100644 index 00000000..e4ad6cfd --- /dev/null +++ b/frontend/components/data/lifeCycle/LifeCycleData.tsx @@ -0,0 +1,57 @@ +import { Scalars } from "../../../__generated__/graphql"; +import { LifeCycleDataDocument } from "../../../queries/data.generated"; +import { Skeleton, Result, App } from "antd"; +import { useEffect } from "react"; +import { stringifyApolloError } from "../../../lib/apollo"; +import DataPageHeader from "../DataPageHeader"; +import { useQuery } from "@apollo/client/react"; + +export type LifeCycleDataProps = { + lifeCycleDataId: Scalars["Uuid"]["input"]; +}; + +export default function LifeCycleData({ + lifeCycleDataId, +}: LifeCycleDataProps) { + const { loading, error, data } = useQuery(LifeCycleDataDocument, { + variables: { + uuid: lifeCycleDataId, + }, + }); + const lifeCycleData = data?.lifeCycleData; + const { message } = App.useApp(); + + useEffect(() => { + if (error) { + message.error(stringifyApolloError(error)); + } + }, [error]); + + if (loading) { + return ; + } + + if (!lifeCycleData) { + return ( + + ); + } + + return ( + <> + , + // ]} + > + + ); +} diff --git a/frontend/package.json b/frontend/package.json index c594339e..6f8c3c03 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.10.0", + "baseline-browser-mapping": "^2.10.8", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^9.5.0", diff --git a/frontend/pages/data/life-cycle/[uuid].tsx b/frontend/pages/data/life-cycle/[uuid].tsx new file mode 100644 index 00000000..ea309fb4 --- /dev/null +++ b/frontend/pages/data/life-cycle/[uuid].tsx @@ -0,0 +1,22 @@ +import { useRouter } from "next/router"; +import LifeCycleData from "../../../components/data/lifeCycle/LifeCycleData"; +import Layout from "../../../components/Layout"; + +function Page() { + const router = useRouter(); + + if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 + return null; + } + + const { uuid } = router.query; + + return ( + + + + ); +} + +export default Page; diff --git a/frontend/pages/data/life-cycle/index.tsx b/frontend/pages/data/life-cycle/index.tsx new file mode 100644 index 00000000..4471dda2 --- /dev/null +++ b/frontend/pages/data/life-cycle/index.tsx @@ -0,0 +1,256 @@ +import Layout from "../../../components/Layout"; +import { Table, App, Form, Button, Alert, Typography } from "antd"; +import { AllLifeCycleDataDocument } from "../../../queries/data.generated"; +import { + LifeCycleData, + Scalars, + LifeCycleDataPropositionInput, +} from "../../../__generated__/graphql"; +import { useState } from "react"; +import { setMapValue } from "../../../lib/freeTextFilter"; +import { + getAppliedMethodColumnProps, + getComponentUuidColumnProps, + getDescriptionColumnProps, + getNameColumnProps, + getResourceTreeColumnProps, + getTimestampColumnProps, + getUuidColumnProps, +} from "../../../lib/table"; +import { + UuidPropositionComparator, + UuidPropositionFormList, +} from "../../../components/UuidPropositionFormList"; +import paths from "../../../paths"; +import { useQuery } from "@apollo/client/react"; + +const layout = { + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, +}; +const tailLayout = { + wrapperCol: { offset: 8, span: 16 }, +}; + +enum Negator { + Is = "is", + IsNot = "isNot", +} + +const negateIfNecessary = ( + negator: Negator, + proposition: LifeCycleDataPropositionInput, +): LifeCycleDataPropositionInput => { + switch (negator) { + case Negator.Is: + return proposition; + case Negator.IsNot: + // return { not: proposition }; + return proposition; // TODO Support `not` in filter! + } +}; + +const conjunct = ( + propositions: LifeCycleDataPropositionInput[], +): LifeCycleDataPropositionInput => { + if (propositions.length == 0) { + return {}; + } + if (propositions.length == 1) { + return propositions[0]; + } + return { and: propositions }; +}; + +// const disjunct = ( +// propositions: LifeCycleDataPropositionInput[] +// ): LifeCycleDataPropositionInput => { +// if (propositions.length == 0) { +// return {}; +// } +// if (propositions.length == 1) { +// return propositions[0]; +// } +// return { or: propositions }; +// }; + +function Page() { + const [form] = Form.useForm(); + const [filtering, setFiltering] = useState(false); + const [globalErrorMessages, setGlobalErrorMessages] = useState( + new Array(), + ); + const { message } = App.useApp(); + const [data, setData] = useState([]); + // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 + // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 + // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. + // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies + const allLifeCycleDataQuery = useQuery(AllLifeCycleDataDocument, { + skip: true, + errorPolicy: "all", + }); + + const [filterText, setFilterText] = useState(() => new Map()); + const onFilterTextChange = setMapValue(filterText, setFilterText); + + const onFinish = ({ + componentIds, + dataFormatIds, + }: { + componentIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; + dataFormatIds: + | { + negator: Negator; + comparator: UuidPropositionComparator; + value: Scalars["Uuid"]["input"] | undefined; + }[] + | undefined; + }) => { + const filter = async () => { + try { + setFiltering(true); + // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout + const propositions: LifeCycleDataPropositionInput[] = []; + if (componentIds) { + for (let { negator, comparator, value } of componentIds) { + propositions.push( + negateIfNecessary(negator, { + componentId: { [comparator]: value }, + }), + ); + } + } + if (dataFormatIds) { + for (let { negator, comparator, value } of dataFormatIds) { + propositions.push( + negateIfNecessary(negator, { + resources: { + some: { + dataFormatId: { [comparator]: value }, + }, + }, + }), + ); + } + } + const { error, data } = await allLifeCycleDataQuery.refetch( + propositions.length == 0 + ? {} + : { + where: conjunct(propositions), + }, + ); + if (error) { + // TODO Handle properly. + console.log(error); + message.error(error.message); + } + // TODO Casting to `LifeCycleData` is wrong and error prone! + setData( + (data?.allLifeCycleData?.edges?.map((x) => x.node) || + []) as LifeCycleData[], + ); + } catch (error) { + // TODO Handle properly. + console.log("Failed:", error); + } finally { + setFiltering(false); + } + }; + filter(); + }; + + const onFinishFailed = () => { + setGlobalErrorMessages(["Fix the errors below."]); + }; + + return ( + + Life-Cycle Data + {/* TODO Display error messages in a list? */} + {globalErrorMessages.length > 0 && ( + + )} +
+ + + + + + + +
( + onFilterTextChange, + (x) => filterText.get(x), + paths.lifeCycleDatum, + ), + }, + { + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => + filterText.get(x), + ), + }, + { + ...getDescriptionColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getTimestampColumnProps<(typeof data)[0]>(), + }, + { + ...getComponentUuidColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + // { + // ...getInternallyLinkedFilterableStringColumnProps( + // "Database UUID", + // "databaseId", + // (x) => x.databaseId, + // onFilterTextChange, + // (x) => filterText.get(x), + // (x) => paths.database(x.databaseId) + // ), + // }, + { + ...getAppliedMethodColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getResourceTreeColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + ]} + dataSource={data} + /> + + ); +} + +export default Page; diff --git a/frontend/paths.ts b/frontend/paths.ts index f32b8723..1db58062 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -1,110 +1,84 @@ import { Route } from "next"; -const metabaseUrl = "https://www.buildingenvelopedata.org" +const metabaseUrl = "https://www.buildingenvelopedata.org"; export default { - home: "/" as Route, - legalNotice: "/legal-notice" as Route, - dataProtectionInformation: "/data-protection-information" as Route, - antiforgeryToken: "/antiforgery/token" as Route, - userInfo: "/user-info" as Route, - database: "/database" as Route, - calorimetricData: "/data/calorimetric" as Route, - calorimetricDatum(uuid: string) { - return `/data/calorimetric/${encodeURIComponent(uuid)}` as Route; - }, - hygrothermalData: "/data/hygrothermal" as Route, - hygrothermalDatum(uuid: string) { - return `/data/hygrothermal/${encodeURIComponent(uuid)}` as Route; - }, - opticalData: "/data/optical" as Route, - opticalDatum(uuid: string) { - return `/data/optical/${encodeURIComponent(uuid)}` as Route; - }, - photovoltaicData: "/data/photovoltaic" as Route, - photovoltaicDatum(uuid: string) { - return `/data/photovoltaic/${encodeURIComponent(uuid)}` as Route; - }, - geometricData: "/data/geometric" as Route, - geometricDatum(uuid: string) { - return `/data/geometric/${encodeURIComponent(uuid)}` as Route; - }, - createData: "/data/create" as Route, - uploadFile: "/upload-file" as Route, - login: "/connect/login" as Route, - logout: "/connect/logout" as Route, - graphQl: "/graphql/" as Route, - metabase: { - home: metabaseUrl, - components: new URL("/components", metabaseUrl) - .href as Route, - component(uuid: string) { - return new URL( - `/components/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - databases: new URL("/databases", metabaseUrl) - .href as Route, - database(uuid: string) { - return new URL( - `/databases/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - dataFormats: new URL(`/data-formats`, metabaseUrl) - .href as Route, - dataFormat(uuid: string) { - return new URL( - `/data-formats/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - institutions: new URL("/institutions", metabaseUrl) - .href as Route, - institution(uuid: string) { - return new URL( - `/institutions/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - methods: new URL("/methods", metabaseUrl) - .href as Route, - method(uuid: string) { - return new URL( - `/methods/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - users: new URL("/users", metabaseUrl) - .href as Route, - user(uuid: string) { - return new URL( - `/users/${encodeURIComponent(uuid)}`, - metabaseUrl, - ).href as Route; - }, - allCalorimetricData: new URL( - `/data/calorimetric`, - metabaseUrl, - ).href as Route, - allGeometricData: new URL( - `/data/geometric`, - metabaseUrl, - ).href as Route, - allHygrothermalData: new URL( - `/data/hygrothermal`, - metabaseUrl, - ).href as Route, - allOpticalData: new URL( - `/data/optical`, - metabaseUrl, - ).href as Route, - allPhotovoltaicData: new URL( - `/data/photovoltaic`, - metabaseUrl, - ).href as Route, - graphQl: new URL("/graphql/", metabaseUrl) - .href as Route, - }, + home: "/" as Route, + legalNotice: "/legal-notice" as Route, + dataProtectionInformation: "/data-protection-information" as Route, + antiforgeryToken: "/antiforgery/token" as Route, + userInfo: "/user-info" as Route, + database: "/database" as Route, + calorimetricData: "/data/calorimetric" as Route, + calorimetricDatum(uuid: string) { + return `/data/calorimetric/${encodeURIComponent(uuid)}` as Route; + }, + hygrothermalData: "/data/hygrothermal" as Route, + hygrothermalDatum(uuid: string) { + return `/data/hygrothermal/${encodeURIComponent(uuid)}` as Route; + }, + lifeCycleData: "/data/life-cycle" as Route, + lifeCycleDatum(uuid: string) { + return `/data/life-cycle/${encodeURIComponent(uuid)}` as Route; + }, + opticalData: "/data/optical" as Route, + opticalDatum(uuid: string) { + return `/data/optical/${encodeURIComponent(uuid)}` as Route; + }, + photovoltaicData: "/data/photovoltaic" as Route, + photovoltaicDatum(uuid: string) { + return `/data/photovoltaic/${encodeURIComponent(uuid)}` as Route; + }, + geometricData: "/data/geometric" as Route, + geometricDatum(uuid: string) { + return `/data/geometric/${encodeURIComponent(uuid)}` as Route; + }, + createData: "/data/create" as Route, + uploadFile: "/upload-file" as Route, + login: "/connect/login" as Route, + logout: "/connect/logout" as Route, + graphQl: "/graphql/" as Route, + metabase: { + home: metabaseUrl, + components: new URL("/components", metabaseUrl).href as Route, + component(uuid: string) { + return new URL(`/components/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + databases: new URL("/databases", metabaseUrl).href as Route, + database(uuid: string) { + return new URL(`/databases/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + dataFormats: new URL(`/data-formats`, metabaseUrl).href as Route, + dataFormat(uuid: string) { + return new URL(`/data-formats/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + institutions: new URL("/institutions", metabaseUrl).href as Route, + institution(uuid: string) { + return new URL(`/institutions/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + methods: new URL("/methods", metabaseUrl).href as Route, + method(uuid: string) { + return new URL(`/methods/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + users: new URL("/users", metabaseUrl).href as Route, + user(uuid: string) { + return new URL(`/users/${encodeURIComponent(uuid)}`, metabaseUrl) + .href as Route; + }, + allCalorimetricData: new URL(`/data/calorimetric`, metabaseUrl) + .href as Route, + allGeometricData: new URL(`/data/geometric`, metabaseUrl).href as Route, + allHygrothermalData: new URL(`/data/hygrothermal`, metabaseUrl) + .href as Route, + alllifeCycleData: new URL(`/data/life-cycle`, metabaseUrl).href as Route, + allOpticalData: new URL(`/data/optical`, metabaseUrl).href as Route, + allPhotovoltaicData: new URL(`/data/photovoltaic`, metabaseUrl) + .href as Route, + graphQl: new URL("/graphql/", metabaseUrl).href as Route, + }, }; diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 5d228e4e..e8c73639 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -1,229 +1,253 @@ fragment DataPartial on Data { - uuid - timestamp - componentId - name - description - appliedMethod { - methodId - arguments { - name - value - } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } - } - } - resourceTree { - root { - value { - uuid - description - hashValue - locator - dataFormatId - } - } - nonRootVertices { - appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName - } - parentId - value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - timestamp - signature - keyFingerprint - query - variables - message - statement { - __typename - ... on Publication { - abstract - arXiv - authors - doi - section - title - urn - webAddress - } - ... on Standard { - abstract - locator - section - standardizers - title - year - numeration { - mainNumber - prefix - suffix - } - } - } - approverId - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId - } - warnings - userId + uuid + timestamp + componentId + name + description + appliedMethod { + methodId + arguments { + name + value + } + sources { + name + value { + dataId + dataKind + dataTimestamp + databaseId + } + } + } + resourceTree { + root { + value { + uuid + description + hashValue + locator + dataFormatId + } + } + nonRootVertices { + appliedConversionMethod { + arguments { + name + value + } + methodId + sourceName + } + parentId + value { + archivedFilesMetaInformation { + dataFormatId + path + } + description + dataFormatId + hashValue + id + locator + uuid + } + vertexId + } + } + approvals { + timestamp + signature + keyFingerprint + query + variables + message + statement { + __typename + ... on Publication { + abstract + arXiv + authors + doi + section + title + urn + webAddress + } + ... on Standard { + abstract + locator + section + standardizers + title + year + numeration { + mainNumber + prefix + suffix + } + } + } + approverId + } + createdAt + creatorId + locale + resources { + description + hashValue + locator + dataFormatId + } + warnings + userId } fragment CalorimetricDataPartial on CalorimetricData { - ...DataPartial - gValues - uValues + ...DataPartial + gValues + uValues } query CalorimetricData($uuid: Uuid!) { - calorimetricData(id: $uuid) { - ...CalorimetricDataPartial - } + calorimetricData(id: $uuid) { + ...CalorimetricDataPartial + } } query AllCalorimetricData($where: CalorimetricDataPropositionInput) { - allCalorimetricData(where: $where) { - edges { - node { - ...CalorimetricDataPartial - } - } - } + allCalorimetricData(where: $where) { + edges { + node { + ...CalorimetricDataPartial + } + } + } } -# query HasCalorimetricData($where: DataPropositionInput!) { -# hasCalorimetricData(where: $where) -# } +query HasCalorimetricData($where: CalorimetricDataPropositionInput!) { + hasCalorimetricData(where: $where) +} fragment HygrothermalDataPartial on HygrothermalData { - ...DataPartial + ...DataPartial } query HygrothermalData($uuid: Uuid!) { - hygrothermalData(id: $uuid) { - ...HygrothermalDataPartial - } + hygrothermalData(id: $uuid) { + ...HygrothermalDataPartial + } } query AllHygrothermalData($where: HygrothermalDataPropositionInput) { - allHygrothermalData(where: $where) { - edges { - node { - ...HygrothermalDataPartial - } - } - } + allHygrothermalData(where: $where) { + edges { + node { + ...HygrothermalDataPartial + } + } + } +} + +query HasHygrothermalData($where: HygrothermalDataPropositionInput!) { + hasHygrothermalData(where: $where) +} + +fragment LifeCycleDataPartial on LifeCycleData { + ...DataPartial +} + +query LifeCycleData($uuid: Uuid!) { + lifeCycleData(id: $uuid) { + ...LifeCycleDataPartial + } +} + +query AllLifeCycleData($where: LifeCycleDataPropositionInput) { + allLifeCycleData(where: $where) { + edges { + node { + ...LifeCycleDataPartial + } + } + } } -# query HasHygrothermalData($where: DataPropositionInput!) { -# hasHygrothermalData(where: $where) -# } +query HasLifeCycleData($where: LifeCycleDataPropositionInput!) { + hasLifeCycleData(where: $where) +} fragment OpticalDataPartial on OpticalData { - ...DataPartial - infraredEmittances - nearnormalHemisphericalSolarReflectances - nearnormalHemisphericalSolarTransmittances - nearnormalHemisphericalVisibleReflectances - nearnormalHemisphericalVisibleTransmittances - colorRenderingIndices + ...DataPartial + infraredEmittances + nearnormalHemisphericalSolarReflectances + nearnormalHemisphericalSolarTransmittances + nearnormalHemisphericalVisibleReflectances + nearnormalHemisphericalVisibleTransmittances + colorRenderingIndices } query OpticalData($uuid: Uuid!) { - opticalData(id: $uuid) { - ...OpticalDataPartial - } + opticalData(id: $uuid) { + ...OpticalDataPartial + } } query AllOpticalData($where: OpticalDataPropositionInput) { - allOpticalData(where: $where) { - edges { - node { - ...OpticalDataPartial - } - } - } + allOpticalData(where: $where) { + edges { + node { + ...OpticalDataPartial + } + } + } } -# query HasOpticalData($where: DataPropositionInput!) { -# hasOpticalData(where: $where) -# } +query HasOpticalData($where: OpticalDataPropositionInput!) { + hasOpticalData(where: $where) +} fragment PhotovoltaicDataPartial on PhotovoltaicData { - ...DataPartial + ...DataPartial } query PhotovoltaicData($uuid: Uuid!) { - photovoltaicData(id: $uuid) { - ...PhotovoltaicDataPartial - } + photovoltaicData(id: $uuid) { + ...PhotovoltaicDataPartial + } } query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { - allPhotovoltaicData(where: $where) { - edges { - node { - ...PhotovoltaicDataPartial - } - } - } + allPhotovoltaicData(where: $where) { + edges { + node { + ...PhotovoltaicDataPartial + } + } + } } -# query HasPhotovoltaicData($where: DataPropositionInput!) { -# hasPhotovoltaicData(where: $where) -# } +query HasPhotovoltaicData($where: PhotovoltaicDataPropositionInput!) { + hasPhotovoltaicData(where: $where) +} fragment GeometricDataPartial on GeometricData { - ...DataPartial - thicknesses + ...DataPartial + thicknesses } query GeometricData($uuid: Uuid!) { - geometricData(id: $uuid) { - ...GeometricDataPartial - } + geometricData(id: $uuid) { + ...GeometricDataPartial + } } query AllGeometricData($where: GeometricDataPropositionInput) { - allGeometricData(where: $where) { - edges { - node { - ...GeometricDataPartial - } - } - } + allGeometricData(where: $where) { + edges { + node { + ...GeometricDataPartial + } + } + } } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 1b794b4f..37a3c68f 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -196,6 +196,18 @@ type CreateHygrothermalDataPayload { query: Query! } +type CreateLifeCycleDataError implements UserError { + code: CreateLifeCycleDataErrorCode! + message: String! + path: [String!]! +} + +type CreateLifeCycleDataPayload { + errors: [CreateLifeCycleDataError!] + lifeCycleData: LifeCycleData + query: Query! +} + type CreateOpticalDataError implements UserError { code: CreateOpticalDataErrorCode! message: String! @@ -476,6 +488,48 @@ type KeyValuePairOfGuidAndNullableOfUInt32 { value: NonNegativeInt } +type LifeCycleData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + dataAccessRights: DataAccessRights! + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! @cost(weight: "10") + warnings: [String!]! +} + +"A connection to a list of items." +type LifeCycleDataConnection { + "A list of edges." + edges: [LifeCycleDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type LifeCycleDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: LifeCycleData! +} + type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") @@ -483,6 +537,7 @@ type Mutation { createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") + createLifeCycleData(input: CreateLifeCycleDataInput!): CreateLifeCycleDataPayload! @cost(weight: "10") createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") createResponseApprovals(where: CreateResponseApprovalsPropositionInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") @@ -661,10 +716,12 @@ type Query { allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") + allPendingLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") @@ -681,9 +738,11 @@ type Query { hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") + hasLifeCycleData(locale: Locale where: LifeCycleDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") + lifeCycleData(id: Uuid! locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." @@ -1048,6 +1107,18 @@ input CreateHygrothermalDataInput { warnings: [String!]! } +input CreateLifeCycleDataInput { + appliedMethod: AppliedMethodInput! + componentId: Uuid! + createdAt: DateTime! + creatorId: Uuid! + description: String + locale: Locale! + name: String + rootResource: RootGetHttpsResourceInput! + warnings: [String!]! +} + input CreateOpticalDataInput { appliedMethod: AppliedMethodInput! cielabColors: [CielabColorInput!]! @@ -1334,6 +1405,7 @@ input GetHttpsResourcePropositionInput { calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -1436,6 +1508,34 @@ input KeyValuePairOfGuidAndNullableOfUInt32Input { value: NonNegativeInt } +input LifeCycleDataPropositionInput { + and: [LifeCycleDataPropositionInput!] + or: [LifeCycleDataPropositionInput!] + id: UuidPropositionInput + userId: UuidPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + description: StringPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + createdAt: OffsetDateTimePropositionInput + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + warnings: StringsPropositionInput +} + +input LifeCycleDataSortInput { + id: SortEnumType @cost(weight: "10") + locale: SortEnumType @cost(weight: "10") + name: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + appliedMethod: AppliedMethodSortInput @cost(weight: "10") +} + input LocalDatePropositionInput { and: [LocalDatePropositionInput!] or: [LocalDatePropositionInput!] @@ -1747,6 +1847,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { calorimetricData: CalorimetricDataPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -2142,6 +2243,20 @@ enum CreateHygrothermalDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum CreateLifeCycleDataErrorCode { + UNKNOWN + UNAUTHORIZED + UNAUTHENTICATED + UNKNOWN_COMPONENT + ILLEGAL_CREATED_AT + UNKNOWN_CREATOR + UNKNOWN_APPLIED_METHOD + UNKNOWN_DATABASE + UNKNOWN_DATA + UNKNOWN_DATA_FORMAT + CREATING_RESPONSE_APPROVAL_FAILED +} + enum CreateOpticalDataErrorCode { UNKNOWN UNAUTHORIZED @@ -2182,6 +2297,7 @@ enum DataKind { CALORIMETRIC_DATA GEOMETRIC_DATA HYGROTHERMAL_DATA + LIFE_CYCLE_DATA OPTICAL_DATA PHOTOVOLTAIC_DATA } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e189b9da..7d4cbc08 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2832,10 +2832,10 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz#5b09935025bf8a80e29130251e337c6a7fc8cbb9" - integrity sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA== +baseline-browser-mapping@^2.10.8: + version "2.10.8" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz#23d1cea1a85b181c2b8660b6cfe626dc2fb15630" + integrity sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ== baseline-browser-mapping@^2.9.0: version "2.9.5" From 9da4f0b50c46c14f3c7dc5a195b95789a0fe8905 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Mar 2026 20:12:46 +0100 Subject: [PATCH 747/878] Use the dotnet tool dotnet-outdated-tool to update packages --- backend/Makefile | 26 ++++++++++-------- backend/dotnet-tools.json | 11 ++++++-- backend/src/Database.csproj | 44 +++++++++++++++--------------- backend/test/Database.Tests.csproj | 18 ++++++------ 4 files changed, 55 insertions(+), 44 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index c4006112..d18bc2a3 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -126,24 +126,28 @@ format : ## Format code $(MAKE) dos2unix .PHONY : format +outdated-packages : ## List outdated packages + dotnet tool run \ + dotnet-outdated +.PHONY : outdated-packages + update-packages : ## Update packages - for subproject in ./src ./test ; do ( \ - cd ./$${subproject} && \ - dotnet list package | \ - awk '/>/ {print $$2}' | \ - xargs -n 1 dotnet add package \ - ) ; done + dotnet tool run \ + dotnet-outdated \ + --upgrade .PHONY : update-packages update-tools : ## Update tools - dotnet tool list | \ - tail -n +3 | \ - awk '{print $$1}' | \ - xargs -n 1 dotnet tool update + dotnet tool list \ + | tail --lines=+3 \ + | awk '{print $$1}' \ + | xargs --max-args=1 dotnet tool update dotnet tool run dotnet-sos install .PHONY : update-tools -update : update-packages update-tools ## Update packages and tools +update : ## Update packages and tools + -$(MAKE) update-packages + -$(MAKE) update-tools .PHONY : update restore : VERBOSITY = normal diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 5421952e..6761c304 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "10.0.2", + "version": "10.0.5", "commands": [ "dotnet-ef" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.3.2", + "version": "2025.3.3", "commands": [ "jb" ], @@ -85,6 +85,13 @@ "dotnet-retest" ], "rollForward": false + }, + "dotnet-outdated-tool": { + "version": "4.7.1", + "commands": [ + "dotnet-outdated" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 5ebc820b..af7ffa69 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -25,7 +25,7 @@ - + @@ -37,38 +37,38 @@ - - - - - - - + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - - - + + + + + + - - - + + + - + - - - - + + + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 368a0129..3ac9482d 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -16,16 +16,16 @@ - + - - - - - - - - + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all From 504bcfdbdc452f46aa94ab14f19a0a09a1d0d5a3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 16 Mar 2026 20:15:08 +0100 Subject: [PATCH 748/878] Make gpg.mk available to the bootstrap service --- docker.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/docker.mk b/docker.mk index 9b5c9892..355bd89b 100755 --- a/docker.mk +++ b/docker.mk @@ -184,6 +184,7 @@ bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a sh --build-arg USER_ID=$(shell id --user) \ backend docker compose run \ + --volume .:/app \ --rm \ backend \ ${COMMAND} From 2a117751373e21677a7eab98a0cef6d70fc2aca1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 14:14:26 +0100 Subject: [PATCH 749/878] Add default directories for backup and restore --- database.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/database.mk b/database.mk index 51608ca2..ae3632e1 100755 --- a/database.mk +++ b/database.mk @@ -103,6 +103,7 @@ migrate : ## Migrate database by running the idempotent SQL script ./backend/sr # Command `pg_dump`: https://www.postgresql.org/docs/current/app-pgdump.html # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ +backup : DIR = ./backup backup : ## Backup database and related data to directory with absolute path `${DIR}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` mkdir --parents "${DIR}" docker compose up \ @@ -136,6 +137,7 @@ backup : ## Backup database and related data to directory with absolute path `${ ./files .PHONY : backup +restore : DIR = ./backup restore : ## Restore database and related data from directory with absolute path `${DIR}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` docker compose stop \ backend From f4c7df9e30de0a87dfe15e2b21faa400481d8b67 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 14:49:25 +0100 Subject: [PATCH 750/878] Only ask for current institution from metabase (instead of current application which is not accessible in general) --- .../Queries/CurrentUserOrApplication.graphql | 107 ------------------ .../Queries/CurrentUserOrInstitution.graphql | 81 +++++++++++++ ...on.cs => QueryCurrentUserOrInstitution.cs} | 42 +++---- .../src/Authorization/CommonAuthorization.cs | 35 +++--- backend/src/Data/DataAccessRights.cs | 2 +- backend/src/Data/DataX.cs | 2 +- .../UpdateDataAccessRightsMutation.cs | 2 +- .../CreateCalorimetricDataMutation.cs | 6 +- .../CreateGeometricDataMutation.cs | 6 +- .../CreateHygrothermalDataMutation.cs | 6 +- .../Institutions/InstitutionQueries.cs | 23 ++++ .../CreateLifeCycleDataMutation.cs | 6 +- backend/src/GraphQl/MutationBase.cs | 14 +-- .../OpticalDataX/CreateOpticalDataMutation.cs | 6 +- .../CreatePhotovoltaicDataMutation.cs | 6 +- backend/src/Services/AccessRightsService.cs | 22 ++-- backend/src/Services/CacheService.cs | 14 +-- backend/src/Services/UserService.cs | 37 +++--- 18 files changed, 205 insertions(+), 212 deletions(-) delete mode 100644 backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql create mode 100644 backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql rename backend/src/ApiRequests/{QueryCurrentUserOrApplication.cs => QueryCurrentUserOrInstitution.cs} (74%) create mode 100644 backend/src/GraphQl/Institutions/InstitutionQueries.cs diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql deleted file mode 100644 index 73baee45..00000000 --- a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql +++ /dev/null @@ -1,107 +0,0 @@ -query CurrentUserOrApplication( - $databaseId: Uuid! -) { - currentUser { - uuid - name - representedInstitutions { - edges { - node { - uuid - name - managedInstitutions { - edges { - node { - uuid - name - } - } - totalCount - } - } - role - } - totalCount - } - databaseOperatingRepresentedInstitutions: representedInstitutions( - where: { - or: [ - { - and: [ - { role: { in: [ASSISTANT, OWNER] } } - { - institution: { - operatedDatabases: { - some: { - id: { equalTo: $databaseId } - } - } - } - } - ] - } - { - and: [ - { role: { in: [ASSISTANT, OWNER] } } - { - institution: { - managedInstitutions: { - some: { - operatedDatabases: { - some: { - id: { - equalTo: $databaseId - } - } - } - } - } - } - } - ] - } - ] - } - ) { - edges { - node { - uuid - name - } - role - } - totalCount - } - } - currentApplication: currentOpenIdConnectApplication { - uuid - clientId - owner { - node { - uuid - name - databaseOperatingDatabases: operatedDatabases( - where: { - id: { equalTo: $databaseId } - } - ) { - totalCount - } - databaseOperatingManagedInstitutions: managedInstitutions( - where: { - operatedDatabases: { - some: { - id: { - equalTo: $databaseId - } - } - } - } - ) - { - totalCount - } - } - } - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql new file mode 100644 index 00000000..82aa1122 --- /dev/null +++ b/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql @@ -0,0 +1,81 @@ +query CurrentUserOrInstitution($databaseId: Uuid!) { + currentUser { + uuid + name + representedInstitutions { + edges { + node { + uuid + name + managedInstitutions { + edges { + node { + uuid + name + } + } + totalCount + } + } + role + } + totalCount + } + databaseOperatingRepresentedInstitutions: representedInstitutions( + where: { + or: [ + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + operatedDatabases: { some: { id: { equalTo: $databaseId } } } + } + } + ] + } + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + managedInstitutions: { + some: { + operatedDatabases: { + some: { id: { equalTo: $databaseId } } + } + } + } + } + } + ] + } + ] + } + ) { + edges { + node { + uuid + name + } + role + } + totalCount + } + } + currentInstitution { + uuid + name + databaseOperatingDatabases: operatedDatabases( + where: { id: { equalTo: $databaseId } } + ) { + totalCount + } + databaseOperatingManagedInstitutions: managedInstitutions( + where: { operatedDatabases: { some: { id: { equalTo: $databaseId } } } } + ) { + totalCount + } + } +} + diff --git a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs similarity index 74% rename from backend/src/ApiRequests/QueryCurrentUserOrApplication.cs rename to backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs index 7509d3b6..3c18ea61 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -10,11 +10,11 @@ namespace Database.ApiRequests; /// /// Class to request user info from Metabase API. /// -public sealed class QueryCurrentUserOrApplication +public sealed class QueryCurrentUserOrInstitution { - private const string QueryFileName = "CurrentUserOrApplication.graphql"; + private const string QueryFileName = "CurrentUserOrInstitution.graphql"; - public static readonly CurrentUserOrApplication Empty = new(null, null); + public static readonly CurrentUserOrInstitution Empty = new(null, null); public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; @@ -68,30 +68,20 @@ public sealed record InstitutionManagedInstitutionNode( string Name ); - public sealed record CurrentOpenIdConnectApplication( + public sealed record CurrentInstitution( Guid Uuid, - string ClientId, - OpenIdConnectApplicationOwnerEdge Owner + string Name, + DatabaseOperatingDatabaseConnection DatabaseOperatingDatabases, + DatabaseOperatingManagedInstitutionConnection DatabaseOperatingManagedInstitutions ) { - public bool IsOwnedByDatabaseOperator() + public bool IsDatabaseOperator() { - return Owner.Node.DatabaseOperatingDatabases.TotalCount >= 1 - || Owner.Node.DatabaseOperatingManagedInstitutions.TotalCount >= 1; + return DatabaseOperatingDatabases.TotalCount >= 1 + || DatabaseOperatingManagedInstitutions.TotalCount >= 1; } }; - public sealed record OpenIdConnectApplicationOwnerEdge( - OpenIdConnectApplicationOwnerNode Node - ); - - public sealed record OpenIdConnectApplicationOwnerNode( - Guid Uuid, - string Name, - DatabaseOperatingDatabaseConnection DatabaseOperatingDatabases, - DatabaseOperatingManagedInstitutionConnection DatabaseOperatingManagedInstitutions - ); - public sealed record DatabaseOperatingDatabaseConnection( uint TotalCount ); @@ -100,21 +90,21 @@ public sealed record DatabaseOperatingManagedInstitutionConnection( uint TotalCount ); - public sealed record CurrentUserOrApplication( + public sealed record CurrentUserOrInstitution( CurrentUser? CurrentUser, - CurrentOpenIdConnectApplication? CurrentApplication + CurrentInstitution? CurrentInstitution ); /// /// Request current user from Metabase. /// - public static async Task Do( + public static async Task Do( AppSettings appSettings, ApiRequestService apiRequestService, CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( + return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), new GraphQLRequest( await GraphQlQueryHelpers.Construct(QueryFileName), @@ -122,7 +112,7 @@ await GraphQlQueryHelpers.Construct(QueryFileName), { databaseId = appSettings.DatabaseId }, - "CurrentUserOrApplication" + "CurrentUserOrInstitution" ), cancellationToken )) diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index a929f0ed..aa94df09 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,9 +1,9 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Services; -using static Database.ApiRequests.QueryCurrentUserOrApplication; +using Database.ApiRequests; namespace Database.Authorization; @@ -11,44 +11,53 @@ public sealed class CommonAuthorization( UserService userService ) { - public Task GetCurrentUserOrApplicationAsync(CancellationToken cancellationToken) + public Task SwitchUserOrInstitutionAsync( + Func> handleUser, + Func> handleInstitution, + CancellationToken cancellationToken + ) + { + return userService.SwitchUserOrInstitutionAsync(handleUser, handleInstitution, cancellationToken); + } + + public Task FetchCurrentUserOrInstitutionAsync(CancellationToken cancellationToken) { - return userService.FetchCurrentUserOrApplicationAsync(cancellationToken); + return userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); } public Task IsAuthenticated(CancellationToken cancellationToken) { - return userService.SwitchUserOrApplicationAsync( + return userService.SwitchUserOrInstitutionAsync( user => Task.FromResult(user is not null), - application => Task.FromResult(application is not null), + institution => Task.FromResult(institution is not null), cancellationToken ); } public Task IsDatabaseOperator(CancellationToken cancellationToken) { - return userService.SwitchUserOrApplicationAsync( + return userService.SwitchUserOrInstitutionAsync( user => Task.FromResult( user is not null && user.IsAtLeastAssistantManagerOfDatabaseOperator() ), - application => Task.FromResult( - application is not null - && application.IsOwnedByDatabaseOperator() + institution => Task.FromResult( + institution is not null + && institution.IsDatabaseOperator() ), cancellationToken ); } public bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - CurrentUser currentUser, + QueryCurrentUserOrInstitution.CurrentUser currentUser, Guid institutionId ) { return currentUser.RepresentedInstitutions.Edges.Any(t => ( - t.Role is InstitutionRepresentativeRole.ASSISTANT - || t.Role is InstitutionRepresentativeRole.OWNER + t.Role is QueryCurrentUserOrInstitution.InstitutionRepresentativeRole.ASSISTANT + || t.Role is QueryCurrentUserOrInstitution.InstitutionRepresentativeRole.OWNER ) && ( diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index e4544c5a..aac47497 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using EntityFrameworkCore.Projectables; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index eddc6d40..d5bde541 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index b085b483..21508185 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 93001f41..5fa7bbbb 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -115,7 +115,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -144,7 +144,7 @@ CancellationToken cancellationToken } var calorimetricData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.CalorimetricData.Add(calorimetricData); @@ -167,4 +167,4 @@ CancellationToken cancellationToken return NewPayload(calorimetricData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 685795f7..b11f44cc 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -120,7 +120,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -149,7 +149,7 @@ CancellationToken cancellationToken } var geometricData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.GeometricData.Add(geometricData); @@ -172,4 +172,4 @@ CancellationToken cancellationToken return NewPayload(geometricData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 34e75a48..c182836a 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -111,7 +111,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -140,7 +140,7 @@ CancellationToken cancellationToken } var hygrothermalData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.HygrothermalData.Add(hygrothermalData); @@ -163,4 +163,4 @@ CancellationToken cancellationToken return NewPayload(hygrothermalData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs new file mode 100644 index 00000000..0b8a389d --- /dev/null +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using HotChocolate.Types; +using Database.Authorization; +using Database.ApiRequests; + +namespace Database.GraphQl.Institutions; + +[ExtendObjectType(nameof(Query))] +public sealed class InstitutionQueries +{ + public Task GetCurrentInstitutionAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrInstitutionAsync( + user => Task.FromResult(null), + institution => Task.FromResult(institution), + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs index 77b639a0..0880b08d 100644 --- a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -111,7 +111,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -140,7 +140,7 @@ CancellationToken cancellationToken } var lifeCycleData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.LifeCycleData.Add(lifeCycleData); @@ -163,4 +163,4 @@ CancellationToken cancellationToken return NewPayload(lifeCycleData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs index 6500b287..95a6b40e 100644 --- a/backend/src/GraphQl/MutationBase.cs +++ b/backend/src/GraphQl/MutationBase.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; -using static Database.ApiRequests.QueryCurrentUserOrApplication; +using static Database.ApiRequests.QueryCurrentUserOrInstitution; namespace Database.GraphQl; @@ -13,7 +13,7 @@ public abstract class MutationBase protected abstract TPayload NewPayload(TData? data, IReadOnlyCollection? errors); protected abstract TError NewError(TErrorCode errorCode, string message, IReadOnlyList path); - protected async Task> AuthorizeAsync( + protected async Task> AuthorizeAsync( TErrorCode unauthenticatedErrorCode, TErrorCode unauthorizedErrorCode, CommonAuthorization authorization, @@ -22,7 +22,7 @@ CancellationToken cancellationToken { if (!await authorization.IsAuthenticated(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthenticatedErrorCode, @@ -34,7 +34,7 @@ CancellationToken cancellationToken } if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthorizedErrorCode, @@ -44,8 +44,8 @@ CancellationToken cancellationToken ]) ); } - return new Result.Data( - await authorization.GetCurrentUserOrApplicationAsync(cancellationToken) + return new Result.Data( + await authorization.FetchCurrentUserOrInstitutionAsync(cancellationToken) ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index a1e9af41..8f71758c 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -133,7 +133,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -162,7 +162,7 @@ CancellationToken cancellationToken } var opticalData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.OpticalData.Add(opticalData); @@ -185,4 +185,4 @@ CancellationToken cancellationToken return NewPayload(opticalData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 7b97bdaf..42014c7b 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -113,7 +113,7 @@ CancellationToken cancellationToken authorization, cancellationToken ) - ).Failed(out var currentUserOrApplication, out var authorizeErrorPayload) + ).Failed(out var currentUserOrInstitution, out var authorizeErrorPayload) ) { return authorizeErrorPayload; @@ -142,7 +142,7 @@ CancellationToken cancellationToken } var photovoltaicData = input.ToDomainModel( - currentUserOrApplication.CurrentUser?.Uuid, + currentUserOrInstitution.CurrentUser?.Uuid, dataFormat.Extension ); context.PhotovoltaicData.Add(photovoltaicData); @@ -165,4 +165,4 @@ CancellationToken cancellationToken return NewPayload(photovoltaicData, null); } -} \ No newline at end of file +} diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index d78bd35b..8cf05a4c 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -6,7 +6,7 @@ using Database.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using static Database.ApiRequests.QueryCurrentUserOrApplication; +using static Database.ApiRequests.QueryCurrentUserOrInstitution; namespace Database.Services; @@ -14,8 +14,8 @@ public static partial class Log { [LoggerMessage( Level = LogLevel.Information, - Message = "Cannot apply access rights because the user or application is unknown. The fetched user ID is {UserId} and the application ID is {ApplicationId}.")] - public static partial void UnknownUserOrApplication(this ILogger logger, Guid? userId, string? applicationId); + Message = "Cannot apply access rights because the user or institution is unknown. The fetched user ID is {UserId} and the institution ID is {InstitutionId}.")] + public static partial void UnknownUserOrInstitution(this ILogger logger, Guid? userId, string? institutionId); [LoggerMessage( Level = LogLevel.Debug, @@ -48,24 +48,24 @@ CancellationToken cancellationToken IReadOnlyList? institutionIds = null; IReadOnlyList? institutionAccessRights = null; - var currentUserOrApplication = await userService.FetchCurrentUserOrApplicationAsync(cancellationToken); + var currentUserOrInstitution = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectClientId(); - if (currentUserOrApplication.CurrentUser is not null) + if (currentUserOrInstitution.CurrentUser is not null) { context = dbContextFactory.CreateDbContext(); - alreadyAccessedByUserCount = cacheService.GetAccessCountForUser(currentUserOrApplication.CurrentUser.Uuid); - institutionIds = currentUserOrApplication.CurrentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); + alreadyAccessedByUserCount = cacheService.GetAccessCountForUser(currentUserOrInstitution.CurrentUser.Uuid); + institutionIds = currentUserOrInstitution.CurrentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); } - else if (currentUserOrApplication.CurrentApplication is not null) + else if (currentUserOrInstitution.CurrentInstitution is not null) { context = dbContextFactory.CreateDbContext(); - institutionIds = [currentUserOrApplication.CurrentApplication.Owner.Node.Uuid]; + institutionIds = [currentUserOrInstitution.CurrentInstitution.Uuid]; institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); } - var result = ProcessData(data, currentUserOrApplication.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount, cacheService); + var result = ProcessData(data, currentUserOrInstitution.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount, cacheService); if (context is not null) { diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index a1881725..77dca2e3 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -3,26 +3,26 @@ using Database.Extensions; using Microsoft.Extensions.Caching.Memory; using NodaTime; -using static Database.ApiRequests.QueryCurrentUserOrApplication; +using static Database.ApiRequests.QueryCurrentUserOrInstitution; namespace Database.Services; public sealed class CacheService( - IMemoryCache currentUserOrApplicationCache, + IMemoryCache currentUserOrInstitutionCache, IMemoryCache accessCountCache, IMemoryCache timePeriodCountCache) { - public CurrentUserOrApplication? SetCurrentUserOrApplication(string token, CurrentUserOrApplication cachedUserOrApplication) + public CurrentUserOrInstitution? SetCurrentUserOrInstitution(string token, CurrentUserOrInstitution cachedUserOrInstitution) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - return currentUserOrApplicationCache.Set(token, cachedUserOrApplication, cacheEntryOptions); + return currentUserOrInstitutionCache.Set(token, cachedUserOrInstitution, cacheEntryOptions); } - public bool TryGetCurrentUserOrApplication(string token, [NotNullWhen(true)] out CurrentUserOrApplication? cachedUserOrApplication) + public bool TryGetCurrentUserOrInstitution(string token, [NotNullWhen(true)] out CurrentUserOrInstitution? cachedUserOrInstitution) { - return currentUserOrApplicationCache.TryGetValue(token, out cachedUserOrApplication); + return currentUserOrInstitutionCache.TryGetValue(token, out cachedUserOrInstitution); } public uint GetAccessCountForUser(Guid userId) @@ -59,4 +59,4 @@ public uint SetAccessCountForUser(Guid userId, uint count) { return timePeriodCountCache.Set(institutionId, (OffsetDateTime.UtcNow, (uint)0)); } -} \ No newline at end of file +} diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 7674d803..01d18460 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -9,7 +9,7 @@ namespace Database.Services; /// -/// Service to fetch current user or application from the metabase +/// Service to fetch current user or institution from the metabase or the local cache /// public sealed class UserService( AppSettings appSettings, @@ -26,45 +26,42 @@ CacheService cacheService return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); } - public async Task SwitchUserOrApplicationAsync( - Func> handleUser, - Func> handleApplication, + public async Task SwitchUserOrInstitutionAsync( + Func> handleUser, + Func> handleInstitution, CancellationToken cancellationToken ) { - var userOrApplication = await FetchCurrentUserOrApplicationAsync(cancellationToken); - if (userOrApplication.CurrentApplication is not null) + var userOrInstitution = await FetchCurrentUserOrInstitutionAsync(cancellationToken); + if (userOrInstitution.CurrentInstitution is not null) { - return await handleApplication(userOrApplication.CurrentApplication); + return await handleInstitution(userOrInstitution.CurrentInstitution); } else { - return await handleUser(userOrApplication.CurrentUser); + return await handleUser(userOrInstitution.CurrentUser); } } - /// - /// Get current user or OpenId Connect application from Metabase (or the local cache). - /// - public async Task FetchCurrentUserOrApplicationAsync( + public async Task FetchCurrentUserOrInstitutionAsync( CancellationToken cancellationToken ) { var httpContext = httpContextAccessor.HttpContext; if (httpContext is null) { - return QueryCurrentUserOrApplication.Empty; + return QueryCurrentUserOrInstitution.Empty; } var token = httpContext.ExtractBearerToken(); if (token is null) { - return QueryCurrentUserOrApplication.Empty; + return QueryCurrentUserOrInstitution.Empty; } // If there is no authenticated user, then the bearer token may be // invalid or expired, so we cannot trust a possibly-cached result. if (httpContext.User is null) { - return await QueryCurrentUserOrApplication.Do( + return await QueryCurrentUserOrInstitution.Do( appSettings, apiRequestService, cancellationToken @@ -72,17 +69,17 @@ CancellationToken cancellationToken } // If there is an authenticated user, then the bearer token is valid and // we try to get the cached user or application. - if (!cacheService.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) + if (!cacheService.TryGetCurrentUserOrInstitution(token, out var cachedUserOrInstitution)) { // If it is not cached, fetch it ... - cachedUserOrApplication = await QueryCurrentUserOrApplication.Do( + cachedUserOrInstitution = await QueryCurrentUserOrInstitution.Do( appSettings, apiRequestService, cancellationToken ); // ... and store it in the cache. - cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); + cacheService.SetCurrentUserOrInstitution(token, cachedUserOrInstitution); } - return cachedUserOrApplication; + return cachedUserOrInstitution; } } \ No newline at end of file From 72db15371e0de76bb3958aeedaa68e1fbf7cfe4d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 14:57:38 +0100 Subject: [PATCH 751/878] Level-up the deploy and rollback scripts to handle errors more gracefully and aid recovery --- deploy.mk | 76 ++---- deploy.sh | 701 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 724 insertions(+), 53 deletions(-) create mode 100755 deploy.sh diff --git a/deploy.mk b/deploy.mk index aaf24269..008ee2f0 100755 --- a/deploy.mk +++ b/deploy.mk @@ -26,45 +26,33 @@ help : ## Print this help .PHONY : help .DEFAULT_GOAL := help -# To deploy `develop`, I usually just take all docker services down by running -# `cd /app/machine; make down; cd /app/production; make down; cd /app/staging; make down;` -# pull the latest code by running -# `cd /app/machine; git pull -p; cd /app/production; git pull -p; cd /app/staging; git pull -p;` -# redeploy the reverse proxy by running -# `cd /app/machine; make deploy;` -# backup, migrate, and deploy all services by running -# `./deploy.mk down backup migrate services DIR=$(pwd)/backup` -# first in `cd /app/staging` and checking that everything works as expected, -# and finally in `cd /app/production` and checking that everything works as -# expected. Before trying it on staging, I usually play the database from -# production into staging. -do : DIR = "$(shell pwd)/backup" -do : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk do TARGET=v1.0.0` - $(MAKE) --file="${SELF}" begin-maintenance - $(MAKE) --file="${SELF}" store-target set-target TARGET="${TARGET}" - $(MAKE) --file="${SELF}" backup DIR="${DIR}" - $(MAKE) --file="${SELF}" fetch-all - $(MAKE) --file="${SELF}" switch TARGET="${TARGET}" - $(MAKE) --file="${SELF}" dotenv - $(MAKE) --file="${SELF}" migrate - $(MAKE) --file="${SELF}" services - $(MAKE) --file="${SELF}" run-tests - $(MAKE) --file="${SELF}" end-maintenance +do : ON_ERROR = pause +do : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk do TARGET=v1.0.0 ON_ERROR=pause` + ./deploy.sh target "${TARGET}" --on-error "${ON_ERROR}" .PHONY : do -rollback : TARGET = "$(shell cat ./.stored-target)" -rollback : DIR = "$(shell pwd)/backup" -rollback : symlink ## Rollback deployment attempt (uses target stored in `./.stored-target` and database backup stored in `./backup/`) - $(MAKE) --file="${SELF}" begin-maintenance - $(MAKE) --file="${SELF}" set-target TARGET="${TARGET}" - $(MAKE) --file="${SELF}" switch TARGET="${TARGET}" - $(MAKE) --file="${SELF}" dotenv - $(MAKE) --file="${SELF}" restore DIR="${DIR}" - $(MAKE) --file="${SELF}" services - $(MAKE) --file="${SELF}" run-tests - $(MAKE) --file="${SELF}" end-maintenance +restore : ON_ERROR = pause +restore : symlink ## Restore deployment `${TIMESTAMP}`, for example, `./deploy.mk restore TIMESTAMP="2026-03-12T21:43:11+01:00" ON_ERROR=pause` + ./deploy.sh restore "${TIMESTAMP}" --on-error "${ON_ERROR}" +.PHONY : restore + +resume : ON_ERROR = pause +resume : ## Resume a paused deployment attempt + ./deploy.sh resume --on-error "${ON_ERROR}" +.PHONY : resume + +rollback : ## Rollback deployment attempt + ./deploy.sh rollback .PHONY : rollback +state : ## Show deployment state + ./deploy.sh state +.PHONY : state + +list : ## List deployments + ./deploy.sh list +.PHONY : list + begin-maintenance : ## Begin maintenance cp \ ./nginx/html/maintenance.off.html \ @@ -87,24 +75,6 @@ set-target : ## Set variable `TARGET` in ./.env to `${TARGET}` ./.env .PHONY : set-target -backup : ## Backup database and related data to the directory with the absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions), for example, `./deploy.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` - $(MAKE) --file=./database.mk \ - backup \ - DIR="${DIR}" -.PHONY : backup - -restore : CONTAINER_NAME = restore_${NAME}_database -restore : ## Restore database and related data from the directory with the absolute path `${DIR}` (down-ing and up-ing the database service before and after to prevent race conditions and removing and recreating the data volume before to start cleanly), for example, `./deploy.mk restore DIR=/app/data/backups/2021-04-22_15_43_35/` (note that after restoring a database it is usually necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting all services with `./docker.mk restart`)` - $(MAKE) --file=./database.mk \ - backup \ - DIR="${DIR}" -.PHONY : restore - -migrate : ## Migrate database (down-ing and up-ing the database service before and after to prevent race conditions. In general, note that other PostgreSQL instances using the same data volume must not be used while migrating and need to be restarted afterwards to make migration results visible) - $(MAKE) --file=./database.mk \ - migrate -.PHONY : migrate - fetch-all : ## Fetch all git fetch --all .PHONY : fetch-all diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 00000000..dbafe26b --- /dev/null +++ b/deploy.sh @@ -0,0 +1,701 @@ +#!/usr/bin/env bash +# [Bash Strict Mode](https://github.com/olivergondza/bash-strict-mode) +set -o errexit +set -o errtrace +set -o nounset +set -o pipefail +# set -o functrace # ensures DEBUG trap works inside functions +# shellcheck disable=SC2154 # warning: s is referenced but not assigned. +trap 'status_code=$?; echo "$0: Error on line "${LINENO}": ${BASH_COMMAND}" >&2; exit ${status_code}' ERR +# capture last line number (needed when script is exited via `exit ...` instead of an error) +trap 'LAST_LINENO=${LINENO}' ERR # DEBUG +LAST_LINENO=-1 + +INVOCATION=$(printf "%q " "$0" "$@") + +#----------------------------------------------- +# COLORS & FORMATTING + +RED=$(tput setaf 1) +GREEN=$(tput setaf 2) +YELLOW=$(tput setaf 3) +BLUE=$(tput setaf 4) +CYAN=$(tput setaf 6) +BOLD=$(tput bold) +DIM=$(tput setaf 244) # `tput dim` is considered a legacy feature by some, for example, kitty does not support it +RESET=$(tput sgr0) + +#----------------------------------------------- +# ICONS & SYMBOLS +CHECK="✓" +CROSS="✗" +# ARROW="➜" +WARN="▲" # ⚠ +BULLET="•" + +#----------------------------------------------- +# GLOBAL VARIABLES + +COMMAND= +TARGET= +RESTORE_TIMESTAMP= +ON_ERROR=pause +DRY_RUN=false +STEP=initialize +HISTORY_PATH=./.deploy-history +touch "${HISTORY_PATH}" + +#----------------------------------------------- +# OUTPUT HELPERS + +print_header() { + local title="$1" + echo "${BOLD}${CYAN}···························································${RESET}" >&2 + echo "${BOLD}${CYAN}${title}${RESET}" >&2 + echo "${BOLD}${CYAN}···························································${RESET}" >&2 +} + +print_section() { + local title="$1" + echo >&2 + echo "${BOLD}${BLUE}${title}${RESET}" >&2 + echo "${BLUE}· · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·${RESET}" >&2 +} + +print_success() { + local message="$1" + echo "${GREEN}${CHECK}${RESET} ${message}" >&2 +} + +print_error() { + local message="$1" + echo "${RED}${CROSS}${RESET} ${RED}${BOLD}${message}${RESET}" >&2 +} + +print_warning() { + local message="$1" + echo "${YELLOW}${WARN}${RESET} ${YELLOW}${message}${RESET}" >&2 +} + +print_info() { + local message="$1" + echo "${CYAN}${BULLET}${RESET} ${message}" >&2 +} + +print_step() { + local step_num="$1" + local total_steps="$2" + local step_name="$3" + echo "${GREEN}[${step_num}/${total_steps}]${RESET} ${step_name}" >&2 +} + +#----------------------------------------------- +# USAGE + +usage() { + local script_name + script_name=$(basename "$0") + cat < [options] + ${CYAN}${script_name} restore${RESET} [options] + ${CYAN}${script_name} resume${RESET} [options] + ${CYAN}${script_name} rollback${RESET} [options] + ${CYAN}${script_name} state${RESET} + ${CYAN}${script_name} list${RESET} + ${CYAN}${script_name} --help${RESET} + +${BOLD}OPTIONS:${RESET} + ${GREEN}-e, --on-error${RESET} When an error occurs then: ${DIM}pause${RESET}, ${DIM}rollback${RESET}, or ${DIM}ask${RESET} (for user input). ${DIM}(default: pause)${RESET} + ${GREEN}-d, --dry-run${RESET} Print commands instead of running them. + ${GREEN}-h, --help${RESET} Display this help message. + +${BOLD}EXAMPLES:${RESET} + ${DIM}${script_name} target v1.0.0 --on-error ask${RESET} + ${DIM}${script_name} restore 2026-03-12T21:43:11+01:00${RESET} + ${DIM}${script_name} resume${RESET} + ${DIM}${script_name} rollback --dry-run${RESET} + ${DIM}${script_name} state${RESET} + ${DIM}${script_name} list | less${RESET} + +EOF + exit 1 +} + +#----------------------------------------------- +# ARGUMENTS & OPTIONS + +[[ $# -eq 0 ]] && usage + +COMMAND="$1" + +case "${COMMAND}" in +target) + COMMAND=deploy + if [[ -z "${2-}" ]]; then + print_error "Git target is missing" + exit 1 + fi + TARGET="$2" + shift 2 # skip command and target + ;; +restore) + if [[ -z "${2-}" ]]; then + print_error "Timestamp is missing" + exit 1 + fi + RESTORE_TIMESTAMP="$2" + shift 2 # skip command and timestamp + ;; +resume | rollback | state | list) + shift 1 # skip command + ;; +--help) + usage + ;; +*) + print_error "Unsupported command '${COMMAND}'." + usage + ;; +esac + +while [[ $# -gt 0 ]]; do + case "$1" in + -e | --on-error) + if [[ -z "$2" ]]; then + print_error "--on-error requires a value" + exit 1 + fi + ON_ERROR="$2" + shift 2 # skip name and value + ;; + -d | --dry-run) + DRY_RUN=true + shift # skip to the next option + ;; + -h | --help) + usage + ;; + *) + print_error "Unknown option '$1'" + usage + ;; + esac +done + +case "${ON_ERROR}" in +pause | restore | ask) ;; +*) + print_error "--on-error is neither 'pause' nor 'restore' nor 'ask' but '${ON_ERROR}'." + usage + ;; +esac + +#----------------------------------------------- +# DRY-RUN + +run() { + if ${DRY_RUN}; then + echo "${YELLOW}[DRY-RUN]${RESET} Would execute: ${BOLD}$*${RESET}" >&2 + else + "$@" + fi +} + +fetch_target_from_dotenv() { + grep --only-matching --perl-regexp '(?<=TARGET=).*' ./.env +} + +#----------------------------------------------- +# ATTEMPT + +declare -A attempt=() # associative array + +read_last_attempt_matching() { + local pattern="$1" + print_info "Reading last attempt matching '${pattern}' from history '${HISTORY_PATH}'" + local line + line=$(tac "${HISTORY_PATH}" 2>/dev/null | grep --max-count=1 "${pattern}" || true) + if [[ -n "${line}" ]]; then + IFS=',' read -ra pairs <<<"${line}" + for pair in "${pairs[@]}"; do + key="${pair%%=*}" + value="${pair#*=}" + attempt["${key}"]="${value}" + done + fi +} + +append_or_overwrite_attempt() { + print_info "Writing attempt '${attempt["timestamp"]}' to history '${HISTORY_PATH}'" + entries=("timestamp=${attempt["timestamp"]}") + unset "attempt[timestamp]" + for key in "${!attempt[@]}"; do + entries+=("$key=${attempt[$key]}") + done + line="$( + IFS=, + echo "${entries[*]}" + )" + if [[ "${COMMAND}" == "resume" ]]; then + run sed --in-place "\$c ${line}" "${HISTORY_PATH}" + else + run echo "${line}" >>"${HISTORY_PATH}" + fi +} + +remove_attempt() { + print_info "Removing attempt '${attempt["timestamp"]}' from history" + run sed --in-place "/timestamp=${attempt["timestamp"]}/d" "${HISTORY_PATH}" +} + +prepare_attempt() { + local now + local default_backup_dir + now="$(date --iso-8601=seconds)" + default_backup_dir="/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")" + case "${COMMAND}" in + deploy) + attempt["command"]="${COMMAND}" + attempt["timestamp"]="${now}" + attempt["backup_dir"]="${default_backup_dir}" + attempt["target"]="${TARGET}" + ;; + restore) + read_last_attempt_matching "^timestamp=${RESTORE_TIMESTAMP}," + if [[ ${#attempt[@]} -eq 0 ]]; then + print_error "There is no deployment with timestamp '${RESTORE_TIMESTAMP}' to restore." + print_info "To print all available deployments run \`${CYAN}./deploy.sh list${RESET}\`" + exit 1 + fi + if [[ -n "${attempt["until"]-}" ]]; then + print_error "The deployment with timestamp '${RESTORE_TIMESTAMP}' did not succeed but failed at step '${attempt["until"]}'." + print_info "You cannot restore a failed deployment. To print all available deployments run \`${CYAN}./deploy.sh list${RESET}\`" + exit 1 + fi + attempt["command"]="${COMMAND}" + attempt["timestamp"]="${now}" + attempt["restore_dir"]="${attempt["backup_dir"]-}" + attempt["backup_dir"]="${default_backup_dir}" + attempt["restore_timestamp"]="${RESTORE_TIMESTAMP}" + ;; + resume | rollback) + read_last_attempt_matching "." # read last attempt + # note that `"${COMMAND}"` and `attempt["command"]` differ in these cases + if [[ ${#attempt[@]} -eq 0 ]] || [[ -z "${attempt["until"]-}" ]]; then + print_error "There is no paused deploy or restore attempt to ${COMMAND}." + exit 1 + fi + ;; + state) + read_last_attempt_matching "." # read last attempt + # note that `"${COMMAND}"` and `attempt["command"]` differ in this case + ;; + list) ;; + *) + print_error "Unsupported command '${COMMAND}'." + usage + ;; + esac +} + +prepare_attempt + +#=============================================== +# ACT + +case "${COMMAND}" in + +list) + print_header "HISTORY" + # IFS= prevents whitespace trimming and -r backslash escaping + tac "${HISTORY_PATH}" | while IFS= read -r line; do + attempt=() + if [[ -n "${line}" ]]; then + IFS=',' read -ra pairs <<<"${line}" + for pair in "${pairs[@]}"; do + key="${pair%%=*}" + value="${pair#*=}" + attempt["${key}"]="${value}" + done + fi + case "${attempt["command"]-}" in + deploy) + if [[ -v attempt["until"] ]]; then + echo "${YELLOW}⏸${RESET} ${attempt["timestamp"]} ${BOLD}[deploy ${attempt["target"]}]${RESET} paused at ${YELLOW}${attempt["until"]}${RESET}" >&2 + echo " ${DIM}backup: ${attempt["backup_dir"]}, previous: ${attempt["previous_target"]}${RESET}" >&2 + else + echo "${GREEN}✓${RESET} ${attempt["timestamp"]} ${BOLD}[deploy ${attempt["target"]}]${RESET} ${GREEN}succeeded${RESET}" >&2 + echo " ${DIM}backup: ${attempt["backup_dir"]}, previous: ${attempt["previous_target"]}${RESET}" >&2 + fi + ;; + restore) + if [[ -v attempt["until"] ]]; then + echo "${YELLOW}⏸${RESET} ${attempt["timestamp"]} ${BOLD}[restore ${attempt["restore_timestamp"]}]${RESET} paused at ${YELLOW}${attempt["until"]}${RESET}" >&2 + echo " ${DIM}restore: ${attempt["restore_dir"]}, backup: ${attempt["backup_dir"]}, target: ${attempt["target"]}, previous: ${attempt["previous_target"]}${RESET}" >&2 + else + echo "${GREEN}✓${RESET} ${attempt["timestamp"]} ${BOLD}[restore ${attempt["restore_timestamp"]}]${RESET} ${GREEN}succeeded${RESET}" >&2 + echo " ${DIM}restore: ${attempt["restore_dir"]}, backup: ${attempt["backup_dir"]}, target: ${attempt["target"]}, previous: ${attempt["previous_target"]}${RESET}" >&2 + fi + ;; + *) + print_error "Unsupported command '${attempt["command"]-}' in line ${line}" + ;; + esac + done + ;; + +state) + print_header "STATE" + dotenv_target="$(fetch_target_from_dotenv)" + if [[ -z "${dotenv_target}" ]]; then + print_warning "Nothing is deployed according to ./.env" + else + print_success "Currently deployed target is '${CYAN}${dotenv_target}${RESET}' according to ./.env" + fi + echo >&2 + if [[ ${#attempt[@]} -eq 0 ]]; then + print_info "No deployment attempt has been made yet." + else + if [[ ! -v attempt["until"] ]]; then + #----------------------------------------------- + # SUCCESS + case "${attempt["command"]-}" in + deploy) + print_success "Deploy of '${CYAN}${attempt["target"]-}${RESET}' succeeded." + ;; + restore) + print_success "Restore of '${CYAN}${attempt["restore_timestamp"]-}${RESET}' succeeded." + ;; + *) + print_error "Unsupported command '${attempt["command"]-}'" + exit 1 + ;; + esac + else + #----------------------------------------------- + # PAUSED + case "${attempt["command"]-}" in + deploy) + print_warning "Paused deploy of '${CYAN}${attempt["target"]-}${RESET}' at step '${YELLOW}${attempt["until"]-}${RESET}'." + print_info "Resume with \`${CYAN}./deploy.sh resume${RESET}\`" + print_info "Rollback with \`${CYAN}./deploy.sh rollback${RESET}\`" + ;; + restore) + print_warning "Paused restore of '${CYAN}${attempt["restore_timestamp"]-}${RESET}' at step '${YELLOW}${attempt["until"]-}${RESET}'." + print_info "Resume with \`${CYAN}./deploy.sh resume${RESET}\`" + print_info "Rollback with \`${CYAN}./deploy.sh rollback${RESET}\`" + ;; + *) + print_error "Unsupported command '${attempt["command"]-}'" + exit 1 + ;; + esac + fi + fi + ;; + +deploy | restore | resume) + + #=============================================== + # DEPLOY OR RESTORE OR RESUME + + #----------------------------------------------- + # CLEANUP + + cleanup_deploy_or_restore_or_resume() { + local exit_code="$1" + local line_number="$2" + local bash_command="$3" + [ "${exit_code}" -eq 0 ] && exit 0 # exit normally if no error + + echo >&2 + print_error "Failed during step '${STEP-unknown}'" + echo "${DIM}Line '${line_number}'${RESET}" >&2 + echo "${DIM}Command '${bash_command}'${RESET}" >&2 + echo "${DIM}Exit code '${exit_code}'${RESET}" >&2 + + append_or_overwrite_attempt + + pause() { + echo >&2 + print_warning "Deployment paused. Fix the issue. Then either" + print_info "resume with \`${CYAN}./deploy.sh resume${RESET}\` or" + print_info "rollback with \`${CYAN}./deploy.sh rollback${RESET}\`." + exit "${exit_code}" + } + + rollback() { + echo >&2 + print_info "Rolling back. Afterwards fix the issue. Then retry with \`${INVOCATION}\`" + run ./deploy.sh rollback + exit "${exit_code}" + } + + case "${ON_ERROR}" in + pause) + pause + ;; + rollback) + rollback + ;; + ask | *) + echo >&2 + while true; do + read -rp "$(echo -e "${YELLOW}Do you want to [p]ause or [r]ollback?${RESET} ")" action + case "${action,,}" in + p | pause) pause ;; + r | rollback) rollback ;; + *) print_warning "Invalid choice. Please type 'p' for pause or 'r' for rollback." ;; + esac + done + ;; + esac + } + + # Trap all exits (errors or manual cancels) + trap 'cleanup_deploy_or_restore_or_resume "$?" "${LAST_LINENO}" "${BASH_COMMAND}"' EXIT + + #----------------------------------------------- + # DO + + case "${COMMAND}" in + deploy) + print_header "DEPLOYING TARGET '${CYAN}${attempt["target"]-}${RESET}'" + ;; + restore) + print_header "RESTORING DEPLOYMENT '${CYAN}${attempt["restore_timestamp"]-}${RESET}'" + ;; + resume) + case "${attempt["command"]-}" in + deploy) + print_header "RESUMING DEPLOY of '${CYAN}${attempt["target"]-}${RESET}' at step '${YELLOW}${attempt["until"]-}${RESET})'" + ;; + restore) + print_header "RESUMING RESTORE of '${CYAN}${attempt["restore_timestamp"]-}${RESET}' at step: '${YELLOW}${attempt["until"]-}${RESET})'" + ;; + *) + print_error "Unsupported command '${attempt["command"]-}'" + exit 1 + ;; + esac + ;; + *) + print_error "Unsupported command '${COMMAND}'" + exit 1 + ;; + esac + + case "${attempt["until"]-"begin-maintenance"}" in + *) # run always + STEP="begin-maintenance" + attempt["until"]=${STEP} + print_step "1" "9" "Beginning maintenance mode" + run ./deploy.mk begin-maintenance || exit 1 + ;;& # continue with a proper match below + begin-maintenance) ;& # fall through + set-target) + STEP="set-target" + attempt["until"]=${STEP} + print_step "2" "9" "Setting target in ./.env to '${CYAN}${attempt["target"]-}${RESET}'" + attempt["previous_target"]="$(fetch_target_from_dotenv)" + if [[ -z "${attempt[previous_target]}" ]]; then + print_error "Previous target is missing in ./.env" + exit 1 + fi + if [[ -z "${attempt["target"]-}" ]]; then + print_error "Target is unknown" + exit 1 + fi + run ./deploy.mk set-target TARGET="${attempt["target"]-}" || exit 1 + ;& # fall through + backup) + STEP="backup" + attempt["until"]=${STEP} + print_step "3" "9" "Backing up data into '${CYAN}${attempt["backup_dir"]-}${RESET}'" + if [[ -z "${attempt["backup_dir"]-}" ]]; then + print_error "Backup directory is unknown" + exit 1 + fi + run ./database.mk backup DIR="${attempt["backup_dir"]-}" || exit 1 + ;& # fall through + switch) + STEP="switch" + attempt["until"]=${STEP} + print_step "4" "9" "Fetching code from Git remote and switching to Git target '${CYAN}${attempt["target"]-}${RESET}'" + if [[ -z "${attempt["target"]-}" ]]; then + print_error "Target is unknown" + exit 1 + fi + run ./deploy.mk fetch-all || exit 1 + run ./deploy.mk switch TARGET="${attempt["target"]-}" || exit 1 + ;& # fall through + dotenv) + STEP="dotenv" + attempt["until"]=${STEP} + print_step "5" "9" "Checking dotenv file ./.env for compatibility with ./.env.production.yaml" + run ./deploy.mk dotenv || exit 1 + ;& # fall through + migrate-or-restore) + STEP="migrate-or-restore" + attempt["until"]=${STEP} + case "${attempt["command"]-}" in + deploy) + print_step "6" "9" "Migrating PostgreSQL database" + run ./database.mk migrate || exit 1 + ;; + restore) + print_step "6" "9" "Restoring data from '${CYAN}${attempt["restore_dir"]-}${RESET}'" + if [[ -z "${attempt["restore_dir"]-}" ]]; then + print_error "Restore directory is unknown" + exit 1 + fi + run ./database.mk restore DIR="${attempt["restore_dir"]-}" || exit 1 + ;; + *) + print_error "Unsupported command '${attempt["command"]-}'" + exit 1 + ;; + esac + ;& # fall through + services) + STEP="services" + attempt["until"]=${STEP} + print_step "7" "9" "Recreating Docker Compose services" + run ./deploy.mk services || exit 1 + ;& # fall through + run-tests) + STEP="run-tests" + attempt["until"]=${STEP} + print_step "8" "9" "Running tests" + run ./deploy.mk run-tests || exit 1 + ;& # fall through + end-maintenance) + STEP="end-maintenance" + attempt["until"]=${STEP} + print_step "9" "9" "Ending maintenance mode" + run ./deploy.mk end-maintenance || exit 1 + ;; + esac + + unset "attempt[until]" + append_or_overwrite_attempt + + echo >&2 + print_success "Completed successfully!" + echo >&2 + + ;; + +rollback) + + #=============================================== + # ROLLBACK + + #----------------------------------------------- + # CLEANUP + cleanup_rollback() { + local exit_code="$1" + local line_number="$2" + local bash_command="$3" + [ "${exit_code}" -eq 0 ] && exit 0 # exit normally if no error + + echo >&2 + print_error "Failed rolling back step '${STEP-unknown}'" + echo "${DIM}Line '${line_number}'${RESET}" >&2 + echo "${DIM}Command '${bash_command}'${RESET}" >&2 + echo "${DIM}Exit code '${exit_code}'${RESET}" >&2 + print_warning "Rollback aborted. Fix the issue. Then retry with \`${INVOCATION}\`" + exit "${exit_code}" + } + # Trap all exits (errors or manual cancels) + trap 'cleanup_rollback "$?" "${LAST_LINENO}" "${BASH_COMMAND}"' EXIT + + #----------------------------------------------- + # DO + print_header "ROLLING BACK TO TARGET '${CYAN}${attempt[previous_target]}${RESET}'" + + rollback_steps() { + STEP="begin-maintenance" + [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + print_step "1" "9" "Beginning maintenance mode" + run ./deploy.mk begin-maintenance || exit 1 + + STEP="set-target" + print_step "2" "9" "Setting target in ./.env to '${CYAN}${attempt[previous_target]-}${RESET}'" + if [[ -z "${attempt[previous_target]-}" ]]; then + print_error "Previous target is unknown" + exit 1 + fi + run ./deploy.mk set-target TARGET="${attempt[previous_target]-}" || exit 1 + [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + + STEP="backup" + # keep the backup for the step `migrate-or-restore` + [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + + STEP="switch" + print_step "3" "9" "Fetching code from Git remote and switching to Git target '${CYAN}${attempt[previous_target]-}${RESET}'" + if [[ -z "${attempt[previous_target]-}" ]]; then + print_error "Previous target is unknown" + exit 1 + fi + run ./deploy.mk fetch-all || exit 1 + run ./deploy.mk switch TARGET="${attempt[previous_target]-}" || exit 1 + [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + + STEP="dotenv" + print_step "4" "9" "Checking dotenv file ./.env for compatibility with ./.env.production.yaml" + run ./deploy.mk dotenv || exit 1 + [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + + STEP="migrate-or-restore" + print_step "5" "9" "Restoring data from '${CYAN}${attempt["backup_dir"]-}${RESET}'" + if [[ -z "${attempt["backup_dir"]-}" ]]; then + print_error "Backup directory is unknown" + exit 1 + fi + run ./database.mk restore DIR="${attempt["backup_dir"]-}" || exit 1 + # re-create services just to make sure instead of `[[ "${attempt["until"]}" == "${STEP}" ]] && return 0` + + STEP="services" + print_step "6" "9" "Recreating Docker Compose services" + run ./deploy.mk services || exit 1 + # run tests just to make sure instead of `[[ "${attempt["until"]}" == "${STEP}" ]] && return 0` + + STEP="run-tests" + print_step "7" "9" "Running tests" + run ./deploy.mk run-tests || exit 1 + # [[ "${attempt["until"]}" == "${STEP}" ]] && return 0 + + return 0 + } + rollback_steps + + STEP="backup" + print_step "8" "9" "Removing data backup '${CYAN}${attempt["backup_dir"]-}${RESET}'" + if [[ -n "${attempt["backup_dir"]-}" ]]; then + run rm --recursive --force "${attempt["backup_dir"]-}" || exit 1 + else + print_warning "Skipping because backup directory is unknown" + fi + + STEP="end-maintenance" + echo >&2 + print_step "9" "9" "Ending maintenance mode" + run ./deploy.mk end-maintenance || exit 1 + + remove_attempt + + echo >&2 + print_success "Rollback completed successfully!" + + ;; + +esac From 309e61557217a4679e4f35581f2e7fb4dca119f3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 15:47:14 +0100 Subject: [PATCH 752/878] Add pull policies to each individual service --- deploy.mk | 1 - docker-compose.development.yaml | 4 ++++ docker-compose.production.yaml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/deploy.mk b/deploy.mk index 008ee2f0..6de556a6 100755 --- a/deploy.mk +++ b/deploy.mk @@ -97,7 +97,6 @@ services : ## Recreate services docker compose up \ --no-build \ --no-deps \ - --pull "always" \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index f7112851..fd691411 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -36,6 +36,7 @@ services: start_interval: "5s" labels: autoheal: "true" + pull_policy: "always" backend: build: @@ -138,6 +139,7 @@ services: resources: limits: cpus: "0.8" + pull_policy: "never" stdin_open: true frontend: @@ -180,6 +182,7 @@ services: start_interval: "5s" labels: autoheal: "true" + pull_policy: "never" nginx: image: "nginx:1.29-trixie-otel" @@ -212,6 +215,7 @@ services: start_interval: "5s" labels: autoheal: "true" + pull_policy: "always" volumes: data: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 99406bb5..b6db33c1 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -36,6 +36,7 @@ services: start_interval: "5s" labels: autoheal: "true" + pull_policy: "always" backend: image: "${NAME:?required}-backend:${TARGET:?required}" @@ -159,6 +160,7 @@ services: start_interval: "5s" labels: autoheal: "true" + pull_policy: "always" volumes: data: From 9c86adb3806a333b3a64dc1c9f9aa05626a49794 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 17:13:58 +0100 Subject: [PATCH 753/878] Log errors in GraphQL responses made to other GraphQL APIs --- backend/src/ApiRequests/GetUserInfo.cs | 17 ++++---- .../ApiRequests/IsGnuPgFingerprintValid.cs | 41 +++++++++++++----- .../QueryCurrentUserOrInstitution.cs | 43 +++++++++++++------ backend/src/ApiRequests/QueryData.cs | 31 ++++++++++--- backend/src/ApiRequests/QueryDatabase.cs | 39 +++++++++++++---- backend/src/ApiRequests/UpdateDatabase.cs | 41 +++++++++++++----- .../DataApprovals/AddDataApprovalMutation.cs | 8 ++-- .../src/GraphQl/Databases/DatabaseQueries.cs | 11 ++--- .../Databases/UpdateDatabaseMutation.cs | 12 ++---- .../ErrorLoggingDiagnosticEventListener.cs | 33 +++++++------- backend/src/GraphQl/Methods/MethodQueries.cs | 13 +++--- backend/src/GraphQl/Users/UserQueries.cs | 12 ++---- .../src/Logging/GraphQlErrorTagProvider.cs | 35 +++++++++++++++ .../Logging/HotChocolateErrorTagProvider.cs | 40 +++++++++++++++++ backend/src/Services/DataApprovalService.cs | 29 ------------- backend/src/Services/UserService.cs | 11 ++--- backend/src/Startup.cs | 14 +++++- 17 files changed, 284 insertions(+), 146 deletions(-) create mode 100644 backend/src/Logging/GraphQlErrorTagProvider.cs create mode 100644 backend/src/Logging/HotChocolateErrorTagProvider.cs delete mode 100644 backend/src/Services/DataApprovalService.cs diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index 68488032..fa1ba28a 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -1,16 +1,17 @@ -using System; +using System; using System.Collections.Generic; -using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Database.Services; -using Microsoft.AspNetCore.Http; namespace Database.ApiRequests; -public sealed class GetUserInfo +public sealed class GetUserInfo( + AppSettings appSettings, + ApiRequestService apiRequestService +) { - public static Uri GetEndpoint(AppSettings appSettings) => + public Uri GetEndpoint => new UriBuilder(appSettings.MetabaseHostUri) { Path = "/connect/userinfo" }.Uri; public sealed record UserInfo( @@ -32,13 +33,11 @@ string Formatted /// /// Request user info from Metabase. /// - public static Task Do( - AppSettings appSettings, - ApiRequestService apiRequestService, + public Task Do( CancellationToken cancellationToken) { return apiRequestService.PerformHttpGetRequest( - GetEndpoint(appSettings), + GetEndpoint, cancellationToken ); } diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index caf4eb9d..c12c57d3 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -1,19 +1,35 @@ -using System; -using System.Net.Http; +using System; using System.Threading; using System.Threading.Tasks; +using Database.Logging; using Database.Services; using GraphQL; -using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; using NodaTime; namespace Database.ApiRequests; -public sealed class IsGnuPgFingerprintValid +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + +public sealed class IsGnuPgFingerprintValid( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) { private const string QueryFileName = "IsGnuPgFingerprintValid.graphql"; - public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; public sealed record Institution( @@ -22,17 +38,15 @@ bool HasGnuPgKeyFingerprint private sealed record Data(Institution? Institution); - public static async Task Do( + public async Task Do( string fingerprint, Guid institutionId, OffsetDateTime createdAt, - AppSettings appSettings, - ApiRequestService apiRequestService, CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( - GetGraphQlEndpoint(appSettings), + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, new GraphQLRequest( await GraphQlQueryHelpers.Construct(QueryFileName), new @@ -44,6 +58,11 @@ await GraphQlQueryHelpers.Construct(QueryFileName), "IsGnuPgFingerprintValid" ), cancellationToken - )).Data.Institution?.HasGnuPgKeyFingerprint ?? false; + )); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response.Data.Institution?.HasGnuPgKeyFingerprint ?? false; ; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs index 3c18ea61..90123cec 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs @@ -2,21 +2,39 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Database.Logging; using Database.Services; using GraphQL; +using Microsoft.Extensions.Logging; namespace Database.ApiRequests; +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + /// -/// Class to request user info from Metabase API. +/// Request current user or institution from metabase. /// -public sealed class QueryCurrentUserOrInstitution +public sealed class QueryCurrentUserOrInstitution( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) { private const string QueryFileName = "CurrentUserOrInstitution.graphql"; public static readonly CurrentUserOrInstitution Empty = new(null, null); - public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; public sealed record CurrentUser( @@ -95,17 +113,12 @@ public sealed record CurrentUserOrInstitution( CurrentInstitution? CurrentInstitution ); - /// - /// Request current user from Metabase. - /// - public static async Task Do( - AppSettings appSettings, - ApiRequestService apiRequestService, + public async Task Do( CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( - GetGraphQlEndpoint(appSettings), + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, new GraphQLRequest( await GraphQlQueryHelpers.Construct(QueryFileName), new @@ -115,7 +128,11 @@ await GraphQlQueryHelpers.Construct(QueryFileName), "CurrentUserOrInstitution" ), cancellationToken - )) - .Data; + )); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response.Data; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryData.cs b/backend/src/ApiRequests/QueryData.cs index 7f3032c2..fa9d887f 100644 --- a/backend/src/ApiRequests/QueryData.cs +++ b/backend/src/ApiRequests/QueryData.cs @@ -1,23 +1,39 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; +using Database.Logging; using Database.Services; using GraphQL; +using Microsoft.Extensions.Logging; namespace Database.ApiRequests; -public sealed class QueryData +public static partial class Log { - public static async Task> Do( + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + +public sealed class QueryData( + ApiRequestService apiRequestService, + ILogger logger +) +{ + public async Task> Do( Uri databaseUrl, Guid dataId, string query, - ApiRequestService apiRequestService, CancellationToken cancellationToken) where TGraphQlData : class { - return await apiRequestService.QueryGraphQl( + var response = await apiRequestService.QueryGraphQl( databaseUrl, new GraphQLRequest( query, @@ -25,5 +41,10 @@ public static async Task> Do( ), cancellationToken ); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index a6c11448..00f805f1 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -1,16 +1,34 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; +using Database.Logging; using Database.Services; using GraphQL; +using Microsoft.Extensions.Logging; namespace Database.ApiRequests; -public sealed class QueryDatabase +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + +public sealed class QueryDatabase( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) { private const string QueryFileName = "Database.graphql"; - public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; public sealed record Database( @@ -41,15 +59,13 @@ Guid Uuid private sealed record DatabaseData(Database? Database); - public static async Task Do( + public async Task Do( Guid databaseId, - AppSettings appSettings, - ApiRequestService apiRequestService, CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( - GetGraphQlEndpoint(appSettings), + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, new GraphQLRequest( await GraphQlQueryHelpers.Construct(QueryFileName), new @@ -59,6 +75,11 @@ await GraphQlQueryHelpers.Construct(QueryFileName), "Database" ), cancellationToken - )).Data.Database; + )); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response.Data.Database; ; } } \ No newline at end of file diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index 1382f6b3..e9ed5964 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -1,18 +1,36 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; +using Database.Logging; using Database.Services; using GraphQL; +using Microsoft.Extensions.Logging; namespace Database.ApiRequests; -public sealed class UpdateDatabase +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + +public sealed class UpdateDatabase( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) { private const string UpdateDatabaseFileName = "UpdateDatabase.graphql"; - public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; public sealed record UpdateDatabaseInput( @@ -43,15 +61,13 @@ IReadOnlyList Path private sealed record UpdateDatabaseData(UpdateDatabasePayload? UpdateDatabase); - public static async Task Do( + public async Task Do( UpdateDatabaseInput updateDatabaseInput, - AppSettings appSettings, - ApiRequestService apiRequestService, CancellationToken cancellationToken ) { - return (await apiRequestService.QueryGraphQl( - GetGraphQlEndpoint(appSettings), + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, new GraphQLRequest( await GraphQlQueryHelpers.Construct( UpdateDatabaseFileName @@ -63,8 +79,11 @@ await GraphQlQueryHelpers.Construct( "UpdateDatabase" ), cancellationToken - )) - .Data - .UpdateDatabase; + )); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response.Data.UpdateDatabase; } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 2ab5953e..56d98173 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -1,11 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Authorization; using Database.Data; using Database.Enumerations; @@ -102,7 +102,7 @@ public async Task AddDataApprovalAsync( AddDataApprovalInput input, ApplicationDbContext context, CommonAuthorization authorization, - DataApprovalService dataApprovalService, + IsGnuPgFingerprintValid isGnuPgFingerprintValid, ResponseApprovalService responseApprovalService, SigningService signingService, ApiRequestService apiRequestService, @@ -123,7 +123,7 @@ CancellationToken cancellationToken return authorizeErrorPayload; } var errors = new List(); - if (!await dataApprovalService.IsGnuPgFingerprintValid( + if (!await isGnuPgFingerprintValid.Do( input.KeyFingerprint, input.ApproverId, input.Timestamp, diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 3f473cf3..81ed299a 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,7 +1,6 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services; using HotChocolate; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -13,20 +12,18 @@ public sealed class DatabaseQueries { public async Task GetDatabaseAsync( AppSettings appSettings, - ApiRequestService apiRequestService, + QueryDatabase queryDatabase, IResolverContext resolverContext, CancellationToken cancellationToken ) { var database = await GraphQlRequestHelper.TransformExceptionsAsync( - () => QueryDatabase.Do( + () => queryDatabase.Do( appSettings.DatabaseId, - appSettings, - apiRequestService, cancellationToken ), resolverContext, - QueryDatabase.GetGraphQlEndpoint(appSettings) + queryDatabase.GetGraphQlEndpoint ); if (database is null) { diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index 93246e56..264d63c4 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -1,7 +1,6 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Services; using HotChocolate.Resolvers; using HotChocolate.Types; using static Database.ApiRequests.UpdateDatabase; @@ -13,21 +12,18 @@ public sealed class UpdateDatabaseMutation { public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, - AppSettings appSettings, - ApiRequestService apiRequestService, + UpdateDatabase updateDatabase, IResolverContext resolverContext, CancellationToken cancellationToken ) { var databasePayload = await GraphQlRequestHelper.TransformExceptionsAsync( - () => UpdateDatabase.Do( + () => updateDatabase.Do( input, - appSettings, - apiRequestService, cancellationToken ), resolverContext, - UpdateDatabase.GetGraphQlEndpoint(appSettings) + updateDatabase.GetGraphQlEndpoint ); if (databasePayload is null) { diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index 033a67a7..da08fe4d 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.RegularExpressions; +using Database.Logging; using HotChocolate; using HotChocolate.Execution; using HotChocolate.Execution.Instrumentation; @@ -26,32 +27,34 @@ public static partial void RequestError( [LoggerMessage( Level = LogLevel.Error, - Message = "Resolver error. Field: {FieldName}" + Message = "Resolver error. Field: '{FieldName}'. Operation: '{Operation}'." )] public static partial void ResolverError( this ILogger logger, Exception? exception, string fieldName, - [LogProperties] IOperation operation, - [LogProperties] IError error + IOperation operation, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( - Level = LogLevel.Error + Level = LogLevel.Error, + Message = "Subscription event error. Operation: {Operation}" )] public static partial void SubscriptionEventError( this ILogger logger, Exception exception, - [LogProperties] IOperation operation + IOperation operation ); [LoggerMessage( - Level = LogLevel.Error + Level = LogLevel.Error, + Message = "Subscription event error. Operation: {Operation}" )] public static partial void SubscriptionTransportError( this ILogger logger, Exception exception, - [LogProperties] IOperation operation + IOperation operation ); [LoggerMessage( @@ -62,19 +65,19 @@ public static partial void SyntaxError( this ILogger logger, Exception? exception, IOperationDocument? document, - [LogProperties] IError error + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( Level = LogLevel.Error, - Message = "Task error. Kind: {Kind}. Status: {Status}." + Message = "Task error. Kind: '{Kind}'. Status: '{Status}'." )] public static partial void TaskError( this ILogger logger, Exception? exception, ExecutionTaskKind kind, ExecutionTaskStatus status, - [LogProperties] IError error + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( @@ -84,22 +87,22 @@ public static partial void ValidationError( this ILogger logger, Exception? exception, IOperationDocument? document, - [LogProperties] IError error + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( Level = LogLevel.Error, - Message = "Execution returned an error: {Document} with {Variables}")] + Message = "Operation error. Document: {Document}\nVariables: {Variables}")] public static partial void OperationError( this ILogger logger, IOperationDocument? document, string? variables, - [LogProperties] IError error + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( Level = LogLevel.Error, - Message = "Unexpected execution error while executing: {Document}\nVariables: {Variables}")] + Message = "Unexpected execution error. Document: {Document}\nVariables: {Variables}")] public static partial void UnexpectedExecutionException( this ILogger logger, IOperationDocument? document, diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index f21cc328..1d762017 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json; @@ -96,6 +96,8 @@ public async Task CalculateMethodAsync( AppSettings appSettings, MethodFactory methodFactory, ApiRequestService apiRequestService, + QueryDatabase queryDatabase, + QueryData queryData, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -113,14 +115,12 @@ [new CalculateMethodError( ); } var database = await GraphQlRequestHelper.TransformExceptionsAsync( - () => QueryDatabase.Do( + () => queryDatabase.Do( dataReference.DatabaseId, - appSettings, - apiRequestService, cancellationToken ), resolverContext, - QueryDatabase.GetGraphQlEndpoint(appSettings) + queryDatabase.GetGraphQlEndpoint ); if (database is null) { @@ -136,11 +136,10 @@ [new CalculateMethodError( var query = ConstructQuery(dataReference.DataKind); GraphQLResponse? response; response = await GraphQlRequestHelper.TransformExceptionsAsync( - () => QueryData.Do( + () => queryData.Do( database.Locator, dataReference.DataId, query, - apiRequestService, cancellationToken ), resolverContext, diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 2eeca3e3..d95ed292 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,10 +1,9 @@ -using System.Linq; +using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using Database.Data; -using Database.Services; using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -35,20 +34,17 @@ await context.Users.AsNoTracking() } public Task GetCurrentUserInfoAsync( - AppSettings appSettings, - ApiRequestService apiRequestService, + GetUserInfo getUserInfo, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GraphQlRequestHelper.TransformExceptionsAsync( - () => GetUserInfo.Do( - appSettings, - apiRequestService, + () => getUserInfo.Do( cancellationToken ), resolverContext, - GetUserInfo.GetEndpoint(appSettings) + getUserInfo.GetEndpoint ); } } \ No newline at end of file diff --git a/backend/src/Logging/GraphQlErrorTagProvider.cs b/backend/src/Logging/GraphQlErrorTagProvider.cs new file mode 100644 index 00000000..9de36220 --- /dev/null +++ b/backend/src/Logging/GraphQlErrorTagProvider.cs @@ -0,0 +1,35 @@ +using System.Linq; +using GraphQL; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static class GraphQlErrorsTagProvider +{ + public static void RecordTags(ITagCollector collector, GraphQLError[] errors) + { + foreach (var (i, error) in errors.Index()) + { + collector.Add($"[{i}].{nameof(error.Message)}", error.Message); + if (error.Path is not null) + { + collector.Add($"[{i}].{nameof(error.Path)}", string.Join(".", error.Path)); + } + if (error.Locations is not null) + { + foreach (var (j, location) in error.Locations.Index()) + { + collector.Add($"[{i}].{nameof(error.Locations)}.[{j}]", $"{location.Line}:{location.Column}"); + } + } + if (error.Extensions is not null) + { + foreach (var (j, extension) in error.Extensions.Index()) + { + collector.Add($"[{i}].{nameof(error.Extensions)}.[{j}].Key", extension.Key); + collector.Add($"[{i}].{nameof(error.Extensions)}.[{j}].Value", extension.Value); + } + } + } + } +} diff --git a/backend/src/Logging/HotChocolateErrorTagProvider.cs b/backend/src/Logging/HotChocolateErrorTagProvider.cs new file mode 100644 index 00000000..5b021643 --- /dev/null +++ b/backend/src/Logging/HotChocolateErrorTagProvider.cs @@ -0,0 +1,40 @@ +using System.Linq; +using HotChocolate; +using Microsoft.Extensions.Logging; + +namespace Database.Logging; + +public static class HotChocolateIErrorTagProvider +{ + public static void RecordTags(ITagCollector collector, IError error) + { + collector.Add($"{nameof(error.Message)}", error.Message); + if (error.Exception is not null) + { + collector.Add($"{nameof(error.Exception)}", error.Exception); + } + if (error.Code is not null) + { + collector.Add($"{nameof(error.Code)}", error.Code); + } + if (error.Path is not null) + { + collector.Add($"{nameof(error.Path)}", string.Join(".", error.Path)); + } + if (error.Locations is not null) + { + foreach (var (j, location) in error.Locations.Index()) + { + collector.Add($"{nameof(error.Locations)}.[{j}]", $"{location.Line}:{location.Column}"); + } + } + if (error.Extensions is not null) + { + foreach (var (j, extension) in error.Extensions.Index()) + { + collector.Add($"{nameof(error.Extensions)}.[{j}].Key", extension.Key); + collector.Add($"{nameof(error.Extensions)}.[{j}].Value", extension.Value); + } + } + } +} diff --git a/backend/src/Services/DataApprovalService.cs b/backend/src/Services/DataApprovalService.cs deleted file mode 100644 index 94fb43b6..00000000 --- a/backend/src/Services/DataApprovalService.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using NodaTime; - -namespace Database.Services; - -public sealed class DataApprovalService( - AppSettings appSettings, - ApiRequestService apiRequestService -) -{ - public Task IsGnuPgFingerprintValid( - string fingerprint, - Guid institutionId, - OffsetDateTime createdAt, - CancellationToken cancellationToken - ) - { - return ApiRequests.IsGnuPgFingerprintValid.Do( - fingerprint, - institutionId, - createdAt, - appSettings, - apiRequestService, - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 01d18460..dc9b2d90 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -12,8 +12,7 @@ namespace Database.Services; /// Service to fetch current user or institution from the metabase or the local cache /// public sealed class UserService( - AppSettings appSettings, - ApiRequestService apiRequestService, + QueryCurrentUserOrInstitution queryCurrentUserOrInstitution, IHttpContextAccessor httpContextAccessor, CacheService cacheService ) @@ -61,9 +60,7 @@ CancellationToken cancellationToken // invalid or expired, so we cannot trust a possibly-cached result. if (httpContext.User is null) { - return await QueryCurrentUserOrInstitution.Do( - appSettings, - apiRequestService, + return await queryCurrentUserOrInstitution.Do( cancellationToken ); } @@ -72,9 +69,7 @@ CancellationToken cancellationToken if (!cacheService.TryGetCurrentUserOrInstitution(token, out var cachedUserOrInstitution)) { // If it is not cached, fetch it ... - cachedUserOrInstitution = await QueryCurrentUserOrInstitution.Do( - appSettings, - apiRequestService, + cachedUserOrInstitution = await queryCurrentUserOrInstitution.Do( cancellationToken ); // ... and store it in the cache. diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 1c41d955..84ceb89b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net.Http; @@ -6,6 +6,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Configuration; using Database.Data; using Database.Data.Extensions; @@ -75,6 +76,7 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(environment); // services.AddDatabaseDeveloperPageExceptionFilter(); ConfigureCustomServices(services); + ConfigureApiRequests(services); } private static void ConfigureRequestResponseServices(IServiceCollection services) @@ -274,7 +276,6 @@ public static void ConfigureCustomServices(IServiceCollection services) { services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddSingleton(); @@ -282,6 +283,15 @@ public static void ConfigureCustomServices(IServiceCollection services) services.AddSingleton(); } + public static void ConfigureApiRequests(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + } + public void Configure(WebApplication app) { // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/ From 36c0d2b0137f74b290e647b3b0e4d78f773d68b0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 17 Mar 2026 21:08:15 +0100 Subject: [PATCH 754/878] Register the `GetUserInfo` service and aid debugging of GraphQL server initialization --- .../src/Configuration/GraphQlConfiguration.cs | 33 +++++++++++++++++-- backend/src/Startup.cs | 1 + 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 4afb4161..9cadedf4 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.Authentication; using Database.Data; using Database.GraphQl; @@ -13,6 +13,7 @@ using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Types.Descriptors; +using HotChocolate.Types.Descriptors.Definitions; using HotChocolate.Types.NodaTime; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -34,8 +35,14 @@ IWebHostEnvironment environment .AddMemoryCache() .AddSha256DocumentHashProvider(HashFormat.Hex); // https://chillicream.com/docs/hotchocolate/v15/security/#fips-compliance // GraphQL Server - services - .AddGraphQLServer() + var serverBuilder = services + .AddGraphQLServer(); + if (environment.IsDevelopment()) + { + // to debug exceptions like `System.InvalidOperationException: The name becomes immutable once it was assigned.` + serverBuilder.TryAddTypeInterceptor(); + } + serverBuilder // TODO add warmup task once we upgrade to version 16: https://chillicream.com/docs/hotchocolate/v16/server/warmup // .AddWarmupTask(async (executor, cancellationToken) => // { @@ -161,6 +168,26 @@ await httpContext.RequestServices } } +// https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs +public sealed class LoggingTypeInterceptor +: TypeInterceptor +{ + public override void OnBeforeInitialize(ITypeDiscoveryContext context) + { + Console.WriteLine($"[INIT] Discovered type '{context.Type.GetType().Name}'"); + } + + public override void OnBeforeCompleteName(ITypeCompletionContext context, DefinitionBase definition) + { + Console.WriteLine($"[NAME] Finalizing name '{definition.Name}' for type '{context.Type.GetType().Name}'"); + } + + public override void OnAfterCompleteType(ITypeCompletionContext context, DefinitionBase definition) + { + Console.WriteLine($"[DONE] Completed type '{context.Type.GetType().Name}' with name '{definition.Name}'"); + } +} + public sealed class CustomNamingConventions : DefaultNamingConventions { diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 84ceb89b..7a8b4c2b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -285,6 +285,7 @@ public static void ConfigureCustomServices(IServiceCollection services) public static void ConfigureApiRequests(IServiceCollection services) { + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); From 68e4c74e2b015b134a5f629491e540112af81d51 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 14:56:32 +0100 Subject: [PATCH 755/878] Use variable size collections for Entity Framework Core properties --- backend/src/Data/GetHttpsResource.cs | 2 +- .../GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs | 4 ++-- .../GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 033bc908..1bada226 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -259,7 +259,7 @@ internal void UpdateRoot( string description, Guid dataFormatId, string? fileExtension, - FileMetaInformation[] archivedFilesMetaInformation + ICollection archivedFilesMetaInformation ) { if (ParentId is not null) diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index 5d2430c4..f1ab2e43 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -133,7 +133,7 @@ CancellationToken cancellationToken input.Description, input.DataFormatId, dataFormat.Extension, - input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToArray(), + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToList(), input.AppliedConversionMethod.ToDomainModel() ); await context.SaveChangesAsync(cancellationToken); diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index 40eebbdf..75e0448c 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -121,7 +121,7 @@ CancellationToken cancellationToken input.Description, input.DataFormatId, dataFormat.Extension, - input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToArray() + input.ArchivedFilesMetaInformation.Select(_ => _.ToDomainModel()).ToList() ); await context.SaveChangesAsync(cancellationToken); From 5187c3d04e330289ed72e767f47163648443cb2d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 16:42:35 +0100 Subject: [PATCH 756/878] Correct spelling errors --- backend/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index d18bc2a3..d7218a21 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -225,7 +225,7 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot sed -i -e "$(shell printf '1s/^\357\273\277//')" -e "s/\r//" $${migrate_path} $${rollback_path} && \ cat $${migrate_path} >> ./src/Migrations/migrate.sql && \ $(MAKE) dos2unix - echo "Review the generated C# and SQL files and make necessary adjusments consistenty across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." + echo "Review the generated C# and SQL files and make necessary adjustments consistently across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." .PHONY : migration # https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#command-line-tools From 29b315c84299aeeadce2f48e15536ff1299ccb80 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 16:57:15 +0100 Subject: [PATCH 757/878] Configure psql --- .psqlrc | 4 ++++ docker-compose.development.yaml | 1 + docker-compose.production.yaml | 1 + 3 files changed, 6 insertions(+) create mode 100644 .psqlrc diff --git a/.psqlrc b/.psqlrc new file mode 100644 index 00000000..3dbd26ee --- /dev/null +++ b/.psqlrc @@ -0,0 +1,4 @@ +\x auto +\pset linestyle unicode +\pset null '∅' +\set QUIET 1 diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index fd691411..3c49877a 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -6,6 +6,7 @@ services: image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data + - ./.psqlrc:/etc/postgresql-common/psqlrc:ro environment: POSTGRES_USER: "${POSTGRES_USER:?required}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index b6db33c1..1ce15ae2 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -6,6 +6,7 @@ services: image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data + - ./.psqlrc:/etc/postgresql-common/psqlrc:ro environment: POSTGRES_USER: "${POSTGRES_USER:?required}" POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" From 4937822944e7411439388ded3b5234aa6ee11e34 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 17:02:07 +0100 Subject: [PATCH 758/878] Correct BOMs --- backend/src/ApiRequests/GetUserInfo.cs | 2 +- backend/src/ApiRequests/IsGnuPgFingerprintValid.cs | 2 +- backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs | 2 +- backend/src/ApiRequests/QueryData.cs | 2 +- backend/src/ApiRequests/QueryDatabase.cs | 2 +- backend/src/ApiRequests/UpdateDatabase.cs | 2 +- backend/src/Authorization/CommonAuthorization.cs | 2 +- backend/src/Configuration/GraphQlConfiguration.cs | 2 +- backend/src/Data/DataAccessRights.cs | 2 +- backend/src/Data/DataX.cs | 2 +- backend/src/Data/GetHttpsResource.cs | 2 +- backend/src/Data/IData.cs | 2 +- .../src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs | 2 +- backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs | 2 +- backend/src/GraphQl/DataX/DeleteDataMutation.cs | 2 +- .../DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs | 2 +- .../GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs | 2 +- .../GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs | 2 +- .../src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs | 2 +- backend/src/GraphQl/Databases/DatabaseQueries.cs | 2 +- backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs | 2 +- backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs | 2 +- backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs | 2 +- .../GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs | 2 +- .../GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs | 2 +- backend/src/GraphQl/Institutions/InstitutionQueries.cs | 2 +- backend/src/GraphQl/Methods/MethodQueries.cs | 2 +- backend/src/GraphQl/MutationBase.cs | 2 +- backend/src/GraphQl/Users/UserQueries.cs | 2 +- backend/src/Logging/GraphQlErrorTagProvider.cs | 2 +- backend/src/Logging/HotChocolateErrorTagProvider.cs | 2 +- backend/src/Migrations/20260316165622_AddLifeCycleData.cs | 2 +- backend/src/Services/AccessRightsService.cs | 2 +- backend/src/Services/ResponseApprovalService.cs | 2 +- backend/src/Services/UserService.cs | 2 +- backend/src/Startup.cs | 2 +- 39 files changed, 39 insertions(+), 39 deletions(-) diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index fa1ba28a..5e1e5b17 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs index c12c57d3..2fe76f3a 100644 --- a/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs +++ b/backend/src/ApiRequests/IsGnuPgFingerprintValid.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Logging; diff --git a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs index 90123cec..1c394c56 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/ApiRequests/QueryData.cs b/backend/src/ApiRequests/QueryData.cs index fa9d887f..992dd72a 100644 --- a/backend/src/ApiRequests/QueryData.cs +++ b/backend/src/ApiRequests/QueryData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Logging; diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs index 00f805f1..0a4953d8 100644 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ b/backend/src/ApiRequests/QueryDatabase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Logging; diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index e9ed5964..e876b2a2 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index aa94df09..d012b51e 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 9cadedf4..90389083 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,4 +1,4 @@ -using System; +using System; using Database.Authentication; using Database.Data; using Database.GraphQl; diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs index aac47497..e4544c5a 100644 --- a/backend/src/Data/DataAccessRights.cs +++ b/backend/src/Data/DataAccessRights.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using EntityFrameworkCore.Projectables; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index d5bde541..eddc6d40 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 1bada226..bdaa1252 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 4202dcbc..79a242e4 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index 21508185..b085b483 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 56d98173..627a2fbc 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Net.Http; diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 2bf59bb5..2086c80f 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Threading; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index 071e3544..b1d9a664 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs index 4cf9f4a9..51446c32 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs index b16a9dce..07fe5492 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs index 83d11dfe..0882dc0c 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Database.Data; using Database.GraphQl.Entities; diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 81ed299a..75142f1b 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using HotChocolate; diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index 264d63c4..7b0172bb 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; using HotChocolate.Resolvers; diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index da08fe4d..b91859a6 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Text; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 162c1e39..6e1a5f92 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using Database.GraphQl.Entities; using HotChocolate.Data.Filters; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 6302b7a9..95850511 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 9f674838..d6801662 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.Data; diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 0a10606e..f93cac99 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,4 +1,4 @@ -using Database.Data; +using Database.Data; using HotChocolate.Types; namespace Database.GraphQl.GetHttpsResources; diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index f1ab2e43..25325d6a 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index 75e0448c..d91a3f4b 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; diff --git a/backend/src/GraphQl/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs index 0b8a389d..e81852dc 100644 --- a/backend/src/GraphQl/Institutions/InstitutionQueries.cs +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.cs @@ -1,4 +1,4 @@ -using System.Threading; +using System.Threading; using System.Threading.Tasks; using HotChocolate.Types; using Database.Authorization; diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 1d762017..764fc95a 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text.Json; diff --git a/backend/src/GraphQl/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs index 95a6b40e..18ab1b8b 100644 --- a/backend/src/GraphQl/MutationBase.cs +++ b/backend/src/GraphQl/MutationBase.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index d95ed292..240341b9 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Logging/GraphQlErrorTagProvider.cs b/backend/src/Logging/GraphQlErrorTagProvider.cs index 9de36220..f64529a9 100644 --- a/backend/src/Logging/GraphQlErrorTagProvider.cs +++ b/backend/src/Logging/GraphQlErrorTagProvider.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using GraphQL; using Microsoft.Extensions.Logging; diff --git a/backend/src/Logging/HotChocolateErrorTagProvider.cs b/backend/src/Logging/HotChocolateErrorTagProvider.cs index 5b021643..9398a1f9 100644 --- a/backend/src/Logging/HotChocolateErrorTagProvider.cs +++ b/backend/src/Logging/HotChocolateErrorTagProvider.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using HotChocolate; using Microsoft.Extensions.Logging; diff --git a/backend/src/Migrations/20260316165622_AddLifeCycleData.cs b/backend/src/Migrations/20260316165622_AddLifeCycleData.cs index 78f7675a..f9012a16 100644 --- a/backend/src/Migrations/20260316165622_AddLifeCycleData.cs +++ b/backend/src/Migrations/20260316165622_AddLifeCycleData.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text.Json; using Database.Enumerations; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 8cf05a4c..42ebabf6 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index c47a308a..7d78b304 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index dc9b2d90..60337b8c 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7a8b4c2b..d8a83a0b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net.Http; From 4717de0883ea711b343ae5a3c2326a2ec019160e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 17:12:05 +0100 Subject: [PATCH 759/878] No Byte-Order Mark please --- backend/.editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/.editorconfig b/backend/.editorconfig index 3eb2414c..5dcf14c1 100644 --- a/backend/.editorconfig +++ b/backend/.editorconfig @@ -13,7 +13,7 @@ end_of_line = lf [*.{cs,csx,vb,vbx}] indent_size = 4 insert_final_newline = false -charset = utf-8-bom +charset = utf-8 # XML project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] From 204c25204804c4c979784bd910ab884ca88816aa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 18 Mar 2026 23:25:46 +0100 Subject: [PATCH 760/878] Put .env into /app/data --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a10928eb..5a10fbef 100644 --- a/README.md +++ b/README.md @@ -323,7 +323,7 @@ and the pages following it. ``` 1. Prepare the environment by running - `cp ./.env.${environment}.sample ./.env && chmod 600 ./.env` + `cp ./.env.${environment}.sample /app/data/.env.${environment} && chmod 600 /app/data/.env.${environment} && ln /app/data/.env.${environment} ./.env` and by adjusting variable values in the copies to your needs, in particular, by setting passwords to newly generated ones, where random passwords may be generated by running `openssl rand -base64 32`. The value From 5b21054a1bc119c656eabc6e88b4aa37be8712f6 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Mar 2026 20:47:02 +0100 Subject: [PATCH 761/878] Add `--check` to `build` target (because `--check` is not a dry-run check but checks while building) --- docker.mk | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docker.mk b/docker.mk index 355bd89b..e42846b7 100755 --- a/docker.mk +++ b/docker.mk @@ -55,20 +55,14 @@ config : ## Parse, resolve and render compose file in canonical format docker compose config ${SERVICE} .PHONY : config -check : ## Check build configuration - docker compose build \ - --check \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) ${SERVICE} -.PHONY : check - pull : ## Pull images docker compose pull ${SERVICE} .PHONY : pull -build : symlink dotenv check pull ## Build images +build : symlink dotenv pull ## Build images docker compose build \ --pull \ + --check \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) ${SERVICE} .PHONY : build From 078c5a3d91d61cd9538f6ef35fc1a90fea4b1c51 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Mar 2026 20:53:01 +0100 Subject: [PATCH 762/878] Log Apollo Client errors and filter "AUTH_NOT_AUTHENTICATED" errors --- frontend/lib/apollo.ts | 195 ++++++++++++++++++++++++++++------------- 1 file changed, 133 insertions(+), 62 deletions(-) diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index 7e2be1fd..e26a9883 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -1,100 +1,171 @@ import { - ApolloClient, - InMemoryCache, - HttpLink, - ErrorLike, + ApolloClient, + InMemoryCache, + HttpLink, + ErrorLike, + CombinedGraphQLErrors, + CombinedProtocolErrors, + ApolloLink, + FetchResult, + Observable, } from "@apollo/client"; +import { ErrorLink } from "@apollo/client/link/error"; import { Defer20220824Handler } from "@apollo/client/incremental"; import { LocalState } from "@apollo/client/local-state"; import { PersistedQueryLink } from "@apollo/client/link/persisted-queries"; import { SetContextLink } from "@apollo/client/link/context"; import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin +import { GraphQLError, GraphQLFormattedError } from "graphql"; +import { map } from "rxjs"; const AntiforgeryCookieKey = "XSRF-TOKEN"; const AntiforgeryHeaderName = "X-XSRF-TOKEN"; const GraphQlEndpointPath = "/graphql/"; function extractCookie(name: string, cookies: string): string | null { - return ( - cookies - .split(";") - .map((cookie) => cookie.trim()) - .filter((cookie) => { - return cookie.substring(0, name.length + 1) === `${name}=`; - }) - .map((cookie) => { - return decodeURIComponent(cookie.substring(name.length + 1)); - })[0] || null - ); + return ( + cookies + .split(";") + .map((cookie) => cookie.trim()) + .filter((cookie) => { + return cookie.substring(0, name.length + 1) === `${name}=`; + }) + .map((cookie) => { + return decodeURIComponent(cookie.substring(name.length + 1)); + })[0] || null + ); } +// Log any GraphQL errors, protocol errors, or network error that occurred +// inspired by https://www.apollographql.com/docs/react/api/link/apollo-link-error +const logErrorsLink = new ErrorLink(({ error }) => { + if (CombinedGraphQLErrors.is(error)) { + error.errors.forEach(({ message, locations, path }) => + console.log( + `[GraphQL Error]: Message: ${message}, Location: ${locations}, Path: ${path}`, + ), + ); + } else if (CombinedProtocolErrors.is(error)) { + error.errors.forEach(({ message, extensions }) => + console.error( + `[Protocol Error]: Message: ${message}, Extensions: ${JSON.stringify( + extensions, + )}`, + ), + ); + } else { + console.error(`[Network Error]: ${error}`); + } +}); + +type AuthErrorCode = "AUTH_NOT_AUTHENTICATED"; + +type AuthGraphQlError = GraphQLFormattedError & { + extensions: { + code: AuthErrorCode; + }; +}; + +function isAuthError(error: GraphQLFormattedError): error is AuthGraphQlError { + return error.extensions?.code === "AUTH_NOT_AUTHENTICATED"; +} + +const filterAuthErrorsLink = new ApolloLink((operation, forward) => + forward(operation).pipe( + map((response) => { + if (response.errors) { + response.errors = response.errors.filter( + (error): error is Exclude => + !isAuthError(error), + ); + } + return response; + }), + ), +); + export function extractAntiforgeryTokenFromCookie(): string | null { - return extractCookie(AntiforgeryCookieKey, document.cookie); + return extractCookie(AntiforgeryCookieKey, document.cookie); } // inspired by https://www.apollographql.com/docs/react/networking/authentication#header const antiforgeryLink = new SetContextLink(({ headers }) => { - const antiforgeryToken = extractAntiforgeryTokenFromCookie(); - if (antiforgeryToken) { - return { - headers: { - ...headers, - [AntiforgeryHeaderName]: antiforgeryToken, - }, - }; - } - return { headers: headers }; + const antiforgeryToken = extractAntiforgeryTokenFromCookie(); + if (antiforgeryToken) { + return { + headers: { + ...headers, + [AntiforgeryHeaderName]: antiforgeryToken, + }, + }; + } + return { headers: headers }; }); const persistedQueryLink = new PersistedQueryLink({ - sha256, - useGETForHashedQueries: false, + sha256, + useGETForHashedQueries: false, }); const httpLink = new HttpLink({ - uri: GraphQlEndpointPath, - useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors - credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie - headers: { "accept": "application/json" }, + uri: GraphQlEndpointPath, + useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors + credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie + headers: { accept: "application/json" }, }); -const apolloLink = antiforgeryLink.concat(persistedQueryLink).concat(httpLink); +const apolloLink = filterAuthErrorsLink + .concat(logErrorsLink) + .concat(antiforgeryLink) + .concat(persistedQueryLink) + .concat(httpLink); export const apolloClient = new ApolloClient({ - ssrMode: false, - link: apolloLink, - cache: new InMemoryCache({ - possibleTypes: fragmentMatcherTypes.possibleTypes, - }), - localState: new LocalState({}), - incrementalHandler: new Defer20220824Handler(), + ssrMode: false, + link: apolloLink, + cache: new InMemoryCache({ + possibleTypes: fragmentMatcherTypes.possibleTypes, + }), + localState: new LocalState({}), + incrementalHandler: new Defer20220824Handler(), + defaultOptions: { + watchQuery: { + errorPolicy: "all" as const, + }, + query: { + errorPolicy: "all" as const, + }, + mutate: { + errorPolicy: "all" as const, + }, + }, }); // See https://www.apollographql.com/docs/react/data/error-handling export function stringifyApolloError(error: ErrorLike) { - // if (CombinedGraphQLErrors.is(error)) { - // return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions - // }); Stack(${error.stack}); GraphQL Errors(${error.errors - // .map( - // (e) => - // `[Message(${e.message}); Path(${e.path?.join( - // ".", - // )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions - // })]`, - // ) - // .join(", ")})`; - // } if (CombinedProtocolErrors.is(error)) { - // Handle multipart subscription protocol errors - // } if (LocalStateError.is(error)) { - // Handle errors thrown by the `LocalState` class - // } if (ServerError.is(error)) { - // Handle server HTTP errors - // } if (ServerParseError.is(error)) { - // Handle JSON parse errors - // } if (UnconventionalError.is(error)) { - // Handle errors thrown by irregular types - return `${error.name}: ${error.message}`; + // if (CombinedGraphQLErrors.is(error)) { + // return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions + // }); Stack(${error.stack}); GraphQL Errors(${error.errors + // .map( + // (e) => + // `[Message(${e.message}); Path(${e.path?.join( + // ".", + // )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions + // })]`, + // ) + // .join(", ")})`; + // } if (CombinedProtocolErrors.is(error)) { + // Handle multipart subscription protocol errors + // } if (LocalStateError.is(error)) { + // Handle errors thrown by the `LocalState` class + // } if (ServerError.is(error)) { + // Handle server HTTP errors + // } if (ServerParseError.is(error)) { + // Handle JSON parse errors + // } if (UnconventionalError.is(error)) { + // Handle errors thrown by irregular types + return `${error.name}: ${error.message}`; } /* From 319a9261a119c69e059dad1818db8ec47ef170b2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Mar 2026 21:08:11 +0100 Subject: [PATCH 763/878] Align names of params with then ones of the overridden methods --- backend/src/Configuration/GraphQlConfiguration.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 90389083..69fbdf38 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -172,19 +172,19 @@ await httpContext.RequestServices public sealed class LoggingTypeInterceptor : TypeInterceptor { - public override void OnBeforeInitialize(ITypeDiscoveryContext context) + public override void OnBeforeInitialize(ITypeDiscoveryContext discoveryContext) { - Console.WriteLine($"[INIT] Discovered type '{context.Type.GetType().Name}'"); + Console.WriteLine($"[INIT] Discovered type '{discoveryContext.Type.GetType().Name}'"); } - public override void OnBeforeCompleteName(ITypeCompletionContext context, DefinitionBase definition) + public override void OnBeforeCompleteName(ITypeCompletionContext completionContext, DefinitionBase definition) { - Console.WriteLine($"[NAME] Finalizing name '{definition.Name}' for type '{context.Type.GetType().Name}'"); + Console.WriteLine($"[NAME] Finalizing name '{definition.Name}' for type '{completionContext.Type.GetType().Name}'"); } - public override void OnAfterCompleteType(ITypeCompletionContext context, DefinitionBase definition) + public override void OnAfterCompleteType(ITypeCompletionContext completionContext, DefinitionBase definition) { - Console.WriteLine($"[DONE] Completed type '{context.Type.GetType().Name}' with name '{definition.Name}'"); + Console.WriteLine($"[DONE] Completed type '{completionContext.Type.GetType().Name}' with name '{definition.Name}'"); } } From 5ae799b702f5f4db16793e820315f2ff747a4889 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 20 Mar 2026 21:25:48 +0100 Subject: [PATCH 764/878] Add explicit excludes to have views compile properly (I have no idea why the default exclude does not work anymore) --- backend/src/Database.csproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index af7ffa69..e92324c3 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -85,17 +85,17 @@ - + - + PreserveNewest - + PreserveNewest - + PreserveNewest From 3858a77a697a36d0929769e37f47c3b8e07cac5f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 22 Mar 2026 21:38:10 +0100 Subject: [PATCH 765/878] Fine-tune nginx config --- nginx/templates/default.conf.template | 127 ++++++++++++++++---------- 1 file changed, 80 insertions(+), 47 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index f6c2f2ad..e06df341 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -2,12 +2,17 @@ # and https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx # `127.0.0.11` is the default IP address for Docker's embedded DNS server. You can verify it by running `cat /etc/resolv.conf` inside a container. -# `valid=30s` forces NGINX to re-resolve hostnames every 30 seconds. This is critical in Docker because container IPs can change during a docker-compose restart. +# `valid=5s` forces NGINX to re-resolve hostnames every 30 seconds. This is critical in Docker because container IPs can change during a docker-compose restart. # `ipv6=off` makes Docker use IPv4. Docker's internal DNS often has issues with IPv6 resolution for container names. # `resolver_timeout 5s` Prevents NGINX from waiting indefinitely if the internal DNS server is momentarily unreachable. resolver 127.0.0.11 valid=5s ipv6=off; resolver_timeout 5s; +map $http_upgrade $connection_upgrade { + default upgrade; + '' keep-alive; +} + proxy_redirect off; proxy_set_header Host $http_x_forwarded_host; proxy_set_header X-Real-IP $remote_addr; @@ -15,44 +20,42 @@ proxy_set_header X-Forwarded-For $http_x_forwarded_for; proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto; proxy_set_header X-Forwarded-Port $http_x_forwarded_port; proxy_set_header X-Forwarded-Host $http_x_forwarded_host; -# proxy_http_version 1.1; -# proxy_set_header Upgrade $http_upgrade; -# proxy_set_header Connection keep-alive; -# proxy_cache_bypass $http_upgrade; -proxy_connect_timeout 90; -proxy_send_timeout 90; -proxy_read_timeout 90; + +proxy_http_version 1.1; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection $connection_upgrade; +proxy_cache_bypass $http_upgrade; + +proxy_connect_timeout 30s; +proxy_send_timeout 30s; +proxy_read_timeout 60s; + +# Use big buffers for the OpenId Connect cookies proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; -client_max_body_size 0; +client_max_body_size 1m; client_body_buffer_size 128k; client_header_buffer_size 4k; large_client_header_buffers 4 16k; -add_header X-Frame-Options "SAMEORIGIN"; # Secure from clickjacking: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#secure-nginx-from-clickjacking - -limit_req_zone $binary_remote_addr zone=frontend:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=graphql:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=connect:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=antiforgery:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=api:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=openapi:10m rate=30r/s; -limit_req_zone $binary_remote_addr zone=health:10m rate=30r/s; +limit_req_status 429; +limit_req_zone $binary_remote_addr zone=general:2m rate=30r/s; +limit_req_zone $binary_remote_addr zone=sensitive:2m rate=10r/s; server_tokens off; sendfile on; # sendfile_max_chunk 1m; -keepalive_timeout 29; # Adjust to the lowest possible value that makes sense for your use case. -client_body_timeout 10; -client_header_timeout 10; -send_timeout 10; - -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; +keepalive_timeout 30s; # Adjust to the lowest possible value that makes sense for your use case. +client_body_timeout 10s; +client_header_timeout 10s; +send_timeout 10s; + +map $http_accept $error_type { + default "text/html"; + ~application/json "application/json"; } upstream backend { @@ -68,9 +71,6 @@ upstream frontend { server { listen *:80; - add_header X-Frame-Options DENY; - add_header X-Content-Type-Options nosniff; # Secure from MIME-type sniffing: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#mime-type-sniffing - set $maintenance 0; if (-f $document_root/maintenance.html) { set $maintenance 1; @@ -82,34 +82,46 @@ server { return 503; } + limit_req zone=general burst=10 nodelay; + location / { proxy_pass http://frontend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_cache_bypass $http_upgrade; - limit_req zone=frontend burst=100 nodelay; + limit_req zone=general burst=100 nodelay; + } + + location /_next/webpack-hmr { + proxy_pass http://frontend; + proxy_read_timeout 86400s; + limit_req zone=general burst=100 nodelay; } location /graphql { proxy_pass http://backend; - limit_req zone=graphql burst=12 delay=6; + limit_req zone=sensitive burst=12 delay=6; } location /connect { proxy_pass http://backend; - limit_req zone=connect burst=6 nodelay; + limit_req zone=sensitive burst=6 nodelay; } location /antiforgery { proxy_pass http://backend; - limit_req zone=antiforgery burst=2 nodelay; + limit_req zone=sensitive burst=2 nodelay; } location /api { proxy_pass http://backend; - limit_req zone=api burst=6 nodelay; - client_max_body_size 10240m; + limit_req zone=sensitive burst=6 nodelay; + } + + location /api/upload-file { + proxy_pass http://backend; + limit_req zone=sensitive burst=2 nodelay; + client_max_body_size 10G; + client_body_timeout 300s; + proxy_read_timeout 300s; + proxy_send_timeout 300s; } location /api/resources/ { @@ -119,27 +131,48 @@ server { sendfile_max_chunk 1m; tcp_nopush on; tcp_nodelay on; - keepalive_timeout 65; + keepalive_timeout 65s; } location /openapi { proxy_pass http://backend; - limit_req zone=openapi burst=10 nodelay; + limit_req zone=general burst=10 nodelay; } location /health { proxy_pass http://backend; - limit_req zone=health burst=2 nodelay; + limit_req zone=general burst=2 nodelay; + } + + error_page 413 /413.json; + location = /413.json { + add_header Content-Type application/json always; + return 413 '{"errors": [{"code": "CONTENT_TOO_LARGE", "message": "The uploaded file exceeds the maximum allowed size.", "path": []}]}'; } - error_page 503 @maintenance; + error_page 404 /404.json; + location = /404.json { + add_header Content-Type application/json always; + return 429 '{"errors": [{"code": "NOT_FOUND", "message": "The requested resource does not exist.", "path": []}]}'; + } + + error_page 429 /429.json; + location = /429.json { + add_header Retry-After 1 always; + add_header Content-Type application/json always; + return 429 '{"errors": [{"code": "TOO_MANY_REQUESTS", "message": "Too many requests: Please slow down and try again later.", "path": []}]}'; + } + + error_page 502 503 504 @maintenance; location @maintenance { internal; - add_header Retry-After 120 always; - default_type application/json; - if ($http_accept ~ application/json) { - return 503 "{\"errors\": [{\"code\": \"MAINTENANCE\", \"message\": \"We are under maintenance and will be back soon!\", \"path\": []}]}"; + if ($error_type = "application/json") { + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 503 '{"errors": [{"code": "MAINTENANCE", "message": "We are under maintenance and will be back soon!", "path": []}]}'; } + add_header Retry-After 120 always; + root /etc/nginx/html; rewrite ^(.*)$ /maintenance.html break; } } From 1cd930a50be26bc14a52b18fc8e4c8c34731c406 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 22 Mar 2026 21:38:45 +0100 Subject: [PATCH 766/878] Upgrade to Ant.Design 6 --- frontend/lib/freeTextFilter.tsx | 2 +- frontend/package.json | 8 +- frontend/yarn.lock | 3710 +++++++++++++++---------------- 3 files changed, 1756 insertions(+), 1964 deletions(-) diff --git a/frontend/lib/freeTextFilter.tsx b/frontend/lib/freeTextFilter.tsx index d5d405ae..5a90355a 100644 --- a/frontend/lib/freeTextFilter.tsx +++ b/frontend/lib/freeTextFilter.tsx @@ -64,7 +64,7 @@ export function getFreeTextFilterProps( searchInput = node; }} placeholder={"Filter"} - value={selectedKeys[0]} + value={selectedKeys?.[0]?.toString()} onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : []) } diff --git a/frontend/package.json b/frontend/package.json index 6f8c3c03..adca86e3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -17,7 +17,7 @@ "@ant-design/pro-layout": "^7.10.3", "@apollo/client": "^4.0.9", "@parcel/watcher": "^2.5.1", - "antd": "^5.0", + "antd": "^6.3.3", "crypto-hash": "^4.0.0", "dayjs": "^1.0", "graphql": "^16.12.0", @@ -33,7 +33,7 @@ "@babel/core": "^7.0", "@graphql-codegen/cli": "^6.0.2", "@graphql-codegen/fragment-matcher": "^6.0.0", - "@graphql-codegen/near-operation-file-preset": "^4.0.0", + "@graphql-codegen/near-operation-file-preset": "^5.0.0", "@graphql-codegen/typescript": "^5.0.4", "@graphql-codegen/typescript-operations": "^5.0.4", "@types/node": "^24.10.1", @@ -45,7 +45,7 @@ "baseline-browser-mapping": "^2.10.8", "concurrently": "^9.2.1", "deepmerge": "^4.0", - "eslint": "^9.5.0", + "eslint": "^10.1.0", "eslint-config-next": "^16.0.8", "graphql-tag": "^2.0", "jest": "^30.2.0", @@ -54,7 +54,7 @@ "react-test-renderer": "^19.2.0", "ts-node": "^10.9.1", "typescript": "^5.0.4", - "yaml-loader": "^0.8" + "yaml-loader": "^0.9.0" }, "browserslist": { "production": [ diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7d4cbc08..2484ea33 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2,23 +2,30 @@ # yarn lockfile v1 -"@ant-design/colors@^7.0.0", "@ant-design/colors@^7.2.1": +"@ant-design/colors@^7.0.0": version "7.2.1" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-7.2.1.tgz#3bbc1c6c18550020d1622a0067ff03492318df98" integrity sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ== dependencies: "@ant-design/fast-color" "^2.0.6" -"@ant-design/cssinjs-utils@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz#5dd79126057920a6992d57b38dd84e2c0b707977" - integrity sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg== +"@ant-design/colors@^8.0.0", "@ant-design/colors@^8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-8.0.1.tgz#6b5444f2ab4061c7b1aa4bc776adb023b0253161" + integrity sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ== + dependencies: + "@ant-design/fast-color" "^3.0.0" + +"@ant-design/cssinjs-utils@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz#a4a57e02dd7e7c3732ab7f1df406df98b5542d12" + integrity sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA== dependencies: - "@ant-design/cssinjs" "^1.21.0" + "@ant-design/cssinjs" "^2.1.2" "@babel/runtime" "^7.23.2" - rc-util "^5.38.0" + "@rc-component/util" "^1.4.0" -"@ant-design/cssinjs@^1.21.0", "@ant-design/cssinjs@^1.21.1", "@ant-design/cssinjs@^1.23.0": +"@ant-design/cssinjs@^1.21.1": version "1.24.0" resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz#7db091f03f189abc77a13cbd27a2293802cd7285" integrity sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg== @@ -31,6 +38,19 @@ rc-util "^5.35.0" stylis "^4.3.4" +"@ant-design/cssinjs@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz#0219e37afdd957248b10da366febae1e4001c952" + integrity sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ== + dependencies: + "@babel/runtime" "^7.11.1" + "@emotion/hash" "^0.8.0" + "@emotion/unitless" "^0.7.5" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" + csstype "^3.1.3" + stylis "^4.3.4" + "@ant-design/fast-color@^2.0.6": version "2.0.6" resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz#ab4d4455c1542c9017d367c2fa8ca3e4215d0ba2" @@ -38,12 +58,17 @@ dependencies: "@babel/runtime" "^7.24.7" +"@ant-design/fast-color@^3.0.0", "@ant-design/fast-color@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-3.0.1.tgz#fee56b95427c0b55b216c93d9a7f3473f31615b5" + integrity sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw== + "@ant-design/icons-svg@^4.4.0": version "4.4.2" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== -"@ant-design/icons@^5.0.0", "@ant-design/icons@^5.6.1": +"@ant-design/icons@^5.0.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-5.6.1.tgz#7290fcdc3d96ff3fca793ed399053cd29ad5dbd3" integrity sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg== @@ -54,6 +79,16 @@ classnames "^2.2.6" rc-util "^5.31.1" +"@ant-design/icons@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.1.0.tgz#97cc14a3c0528b8e2b37f41f232b019f2ca38c2c" + integrity sha512-KrWMu1fIg3w/1F2zfn+JlfNDU8dDqILfA5Tg85iqs1lf8ooyGlbkA+TkwfOKKgqpUmAiRY1PTFpuOU2DAIgSUg== + dependencies: + "@ant-design/colors" "^8.0.0" + "@ant-design/icons-svg" "^4.4.0" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + "@ant-design/pro-layout@^7.10.3": version "7.22.7" resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-7.22.7.tgz#7002b1838e1cdf879bbec813677b478c26fbbb77" @@ -103,21 +138,20 @@ safe-stable-stringify "^2.4.3" swr "^2.0.0" -"@ant-design/react-slick@~1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz#f84ce3e4d0dc941f02b16f1d1d6d7a371ffbb4f1" - integrity sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA== +"@ant-design/react-slick@~2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-2.0.0.tgz#2d1dac45e7fc94060355f7cc233e92ca247133dc" + integrity sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg== dependencies: - "@babel/runtime" "^7.10.4" - classnames "^2.2.5" + "@babel/runtime" "^7.28.4" + clsx "^2.1.1" json2mq "^0.2.0" - resize-observer-polyfill "^1.5.1" throttle-debounce "^5.0.0" "@apollo/client@^4.0.9": - version "4.0.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.0.9.tgz#fdc0388f73a1640f3da30a8791e448b96ad0b49a" - integrity sha512-Lh2drMzFE9x5jVS8RKmlGL5SORkvpyUJIT+wTErxDUR2HpWePiBfhhcHHRSlZFiCR866ewCv4euTc4IDF0GWxw== + version "4.1.6" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.1.6.tgz#6eff1cfc09b1a2ef15f4f46471e6767958e109f7" + integrity sha512-ak8uzqmKeX3u9BziGf83RRyODAJKFkPG72hTNvEj4WjMWFmuKW2gGN1i3OfajKT6yuGjvo+n23ES2zqWDKFCZg== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -127,50 +161,43 @@ optimism "^0.18.0" tslib "^2.3.0" -"@ardatan/relay-compiler@^12.0.3": - version "12.0.3" - resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-12.0.3.tgz#a60824672da7f7cef2a3879ed833b120d292fa08" - integrity sha512-mBDFOGvAoVlWaWqs3hm1AciGHSQE1rqFc/liZTyYz/Oek9yZdT5H26pH2zAFuEiTiBVPPyMuqf5VjOFPI2DGsQ== +"@ardatan/relay-compiler@^13.0.0": + version "13.0.0" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-13.0.0.tgz#20038e20b0ce751947d8e142b6b45e322d90f2bd" + integrity sha512-ite4+xng5McO8MflWCi0un0YmnorTujsDnfPfhzYzAgoJ+jkI1pZj6jtmTl8Jptyi1H+Pa0zlatJIsxDD++ETA== dependencies: - "@babel/generator" "^7.26.10" - "@babel/parser" "^7.26.10" "@babel/runtime" "^7.26.10" - chalk "^4.0.0" - fb-watchman "^2.0.0" - immutable "~3.7.6" + immutable "^5.1.5" invariant "^2.2.4" - nullthrows "^1.1.1" - relay-runtime "12.0.0" - signedsource "^1.0.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" - integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== dependencies: - "@babel/helper-validator-identifier" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.27.2": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.5.tgz#a8a4962e1567121ac0b3b487f52107443b455c7f" - integrity sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA== +"@babel/compat-data@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" + integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== "@babel/core@^7.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.5.tgz#4c81b35e51e1b734f510c99b07dfbc7bbbb48f7e" - integrity sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" - "@babel/helper-compilation-targets" "^7.27.2" - "@babel/helper-module-transforms" "^7.28.3" - "@babel/helpers" "^7.28.4" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/traverse" "^7.28.5" - "@babel/types" "^7.28.5" + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" + integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-compilation-targets" "^7.28.6" + "@babel/helper-module-transforms" "^7.28.6" + "@babel/helpers" "^7.28.6" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/traverse" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" @@ -178,23 +205,23 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.18.13", "@babel/generator@^7.26.10", "@babel/generator@^7.27.5", "@babel/generator@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.5.tgz#712722d5e50f44d07bc7ac9fe84438742dd61298" - integrity sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ== +"@babel/generator@^7.18.13", "@babel/generator@^7.27.5", "@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== dependencies: - "@babel/parser" "^7.28.5" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz#46a0f6efab808d51d29ce96858dd10ce8732733d" - integrity sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ== +"@babel/helper-compilation-targets@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" + integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== dependencies: - "@babel/compat-data" "^7.27.2" + "@babel/compat-data" "^7.28.6" "@babel/helper-validator-option" "^7.27.1" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -205,34 +232,34 @@ resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== -"@babel/helper-module-imports@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" - integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== +"@babel/helper-module-imports@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.27.1" - "@babel/types" "^7.27.1" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/helper-module-transforms@^7.28.3": - version "7.28.3" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz#a2b37d3da3b2344fe085dab234426f2b9a2fa5f6" - integrity sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw== +"@babel/helper-module-transforms@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" + integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== dependencies: - "@babel/helper-module-imports" "^7.27.1" - "@babel/helper-validator-identifier" "^7.27.1" - "@babel/traverse" "^7.28.3" + "@babel/helper-module-imports" "^7.28.6" + "@babel/helper-validator-identifier" "^7.28.5" + "@babel/traverse" "^7.28.6" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" - integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.28.6", "@babel/helper-plugin-utils@^7.8.0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" + integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== "@babel/helper-string-parser@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.27.1", "@babel/helper-validator-identifier@^7.28.5": +"@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== @@ -242,20 +269,20 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@babel/helpers@^7.28.4": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.4.tgz#fe07274742e95bdf7cf1443593eeb8926ab63827" - integrity sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w== +"@babel/helpers@^7.28.6": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" + integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== dependencies: - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.4" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.27.2", "@babel/parser@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.5.tgz#0b0225ee90362f030efd644e8034c99468893b08" - integrity sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== dependencies: - "@babel/types" "^7.28.5" + "@babel/types" "^7.29.0" "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -286,18 +313,18 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-import-assertions@^7.26.0": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" - integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz#ae9bc1923a6ba527b70104dd2191b0cd872c8507" + integrity sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-import-attributes@^7.24.7": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" - integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503" + integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-import-meta@^7.10.4": version "7.10.4" @@ -314,11 +341,11 @@ "@babel/helper-plugin-utils" "^7.8.0" "@babel/plugin-syntax-jsx@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz#2f9beb5eff30fa507c5532d107daac7b888fa34c" - integrity sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee" + integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" "@babel/plugin-syntax-logical-assignment-operators@^7.10.4": version "7.10.4" @@ -377,43 +404,43 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz#5147d29066a793450f220c63fa3a9431b7e6dd18" - integrity sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ== + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" + integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== dependencies: - "@babel/helper-plugin-utils" "^7.27.1" + "@babel/helper-plugin-utils" "^7.28.6" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.16.7", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.5", "@babel/runtime@^7.23.2", "@babel/runtime@^7.23.6", "@babel/runtime@^7.23.9", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.25.7", "@babel/runtime@^7.26.0", "@babel/runtime@^7.26.10": - version "7.28.4" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.4.tgz#a70226016fabe25c5783b2f22d3e1c9bc5ca3326" - integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ== +"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.26.10", "@babel/runtime@^7.28.4": + version "7.29.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" + integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.27.2": - version "7.27.2" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d" - integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw== +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/parser" "^7.27.2" - "@babel/types" "^7.27.1" + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" -"@babel/traverse@^7.26.10", "@babel/traverse@^7.27.1", "@babel/traverse@^7.28.3", "@babel/traverse@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.5.tgz#450cab9135d21a7a2ca9d2d35aa05c20e68c360b" - integrity sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ== +"@babel/traverse@^7.26.10", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== dependencies: - "@babel/code-frame" "^7.27.1" - "@babel/generator" "^7.28.5" + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.28.5" - "@babel/template" "^7.27.2" - "@babel/types" "^7.28.5" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.4", "@babel/types@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.5.tgz#10fc405f60897c35f07e85493c932c7b5ca0592b" - integrity sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA== +"@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== dependencies: "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" @@ -436,24 +463,24 @@ integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== "@emnapi/core@^1.4.3": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.7.1.tgz#3a79a02dbc84f45884a1806ebb98e5746bdfaac4" - integrity sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg== + version "1.9.1" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.9.1.tgz#2143069c744ca2442074f8078462e51edd63c7bd" + integrity sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA== dependencies: - "@emnapi/wasi-threads" "1.1.0" + "@emnapi/wasi-threads" "1.2.0" tslib "^2.4.0" "@emnapi/runtime@^1.4.3", "@emnapi/runtime@^1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.7.1.tgz#a73784e23f5d57287369c808197288b52276b791" - integrity sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA== + version "1.9.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.9.1.tgz#115ff2a0d589865be6bd8e9d701e499c473f2a8d" + integrity sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz#60b2102fddc9ccb78607e4a3cf8403ea69be41bf" - integrity sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ== +"@emnapi/wasi-threads@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz#a19d9772cc3d195370bf6e2a805eec40aa75e18e" + integrity sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg== dependencies: tslib "^2.4.0" @@ -467,10 +494,10 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@envelop/core@^5.2.3", "@envelop/core@^5.3.0", "@envelop/core@^5.4.0": - version "5.4.0" - resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.4.0.tgz#e79b8da5ab4f05eccd9758435df3dc50b11e607d" - integrity sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ== +"@envelop/core@^5.4.0": + version "5.5.1" + resolved "https://registry.yarnpkg.com/@envelop/core/-/core-5.5.1.tgz#ca30c927b3a7d7f118d36111e17b355eedae9ff4" + integrity sha512-3DQg8sFskDo386TkL5j12jyRAdip/8yzK3x7YGbZBgobZ4aKXrvDU0GppU0SnmrpQnNaiTUsxBs9LKkwQ/eyvw== dependencies: "@envelop/instrumentation" "^1.0.0" "@envelop/types" "^5.2.1" @@ -493,72 +520,52 @@ "@whatwg-node/promise-helpers" "^1.0.0" tslib "^2.5.0" -"@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": - version "4.9.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" - integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== +"@eslint-community/eslint-utils@^4.8.0", "@eslint-community/eslint-utils@^4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz#4e90af67bc51ddee6cdef5284edf572ec376b595" + integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ== dependencies: eslint-visitor-keys "^3.4.3" -"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1": +"@eslint-community/regexpp@^4.12.2": version "4.12.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== -"@eslint/config-array@^0.21.1": - version "0.21.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" - integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== +"@eslint/config-array@^0.23.3": + version "0.23.3" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.3.tgz#3f4a93dd546169c09130cbd10f2415b13a20a219" + integrity sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw== dependencies: - "@eslint/object-schema" "^2.1.7" + "@eslint/object-schema" "^3.0.3" debug "^4.3.1" - minimatch "^3.1.2" + minimatch "^10.2.4" -"@eslint/config-helpers@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" - integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== +"@eslint/config-helpers@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz#721fe6bbb90d74b0c80d6ff2428e5bbcb002becb" + integrity sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw== dependencies: - "@eslint/core" "^0.17.0" + "@eslint/core" "^1.1.1" -"@eslint/core@^0.17.0": - version "0.17.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" - integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== +"@eslint/core@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.1.tgz#450f3d2be2d463ccd51119544092256b4e88df32" + integrity sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ== dependencies: "@types/json-schema" "^7.0.15" -"@eslint/eslintrc@^3.3.1": - version "3.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.3.tgz#26393a0806501b5e2b6a43aa588a4d8df67880ac" - integrity sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^10.0.1" - globals "^14.0.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.1" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@9.39.1": - version "9.39.1" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.1.tgz#0dd59c3a9f40e3f1882975c321470969243e0164" - integrity sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw== - -"@eslint/object-schema@^2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" - integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== +"@eslint/object-schema@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.3.tgz#5bf671e52e382e4adc47a9906f2699374637db6b" + integrity sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ== -"@eslint/plugin-kit@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" - integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== +"@eslint/plugin-kit@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz#eb9e6689b56ce8bc1855bb33090e63f3fc115e8e" + integrity sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ== dependencies: - "@eslint/core" "^0.17.0" + "@eslint/core" "^1.1.1" levn "^0.4.1" "@fastify/busboy@^3.1.1": @@ -575,32 +582,33 @@ tslib "~2.6.0" "@graphql-codegen/cli@^6.0.2": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.1.0.tgz#3fa4c16a85863ffe8ee170d47655b4fe4b69de7b" - integrity sha512-7w3Zq5IFONVOBcyOiP01Nv9WRxGS/TEaBCAb/ALYA3xHq95dqKCpoGnxt/Ut9R18jiS+aMgT0gc8Tr8sHy44jA== + version "6.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.2.1.tgz#c9f4e6ee180bafa433cd635d5e30d27ed9c0c900" + integrity sha512-E1B+5nBda2l89Pci5M0HcEj2Hmx2yhORFX+1T3rmwpQjdOiulo+h9JifWxKomUpjfbmU1YkBSd47CCGLFPU10A== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/client-preset" "^5.2.0" - "@graphql-codegen/core" "^5.0.0" - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-tools/apollo-engine-loader" "^8.0.0" - "@graphql-tools/code-file-loader" "^8.0.0" - "@graphql-tools/git-loader" "^8.0.0" - "@graphql-tools/github-loader" "^9.0.0" - "@graphql-tools/graphql-file-loader" "^8.0.0" - "@graphql-tools/json-file-loader" "^8.0.0" - "@graphql-tools/load" "^8.1.0" - "@graphql-tools/url-loader" "^9.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-codegen/client-preset" "^5.2.4" + "@graphql-codegen/core" "^5.0.1" + "@graphql-codegen/plugin-helpers" "^6.2.0" + "@graphql-tools/apollo-engine-loader" "^8.0.28" + "@graphql-tools/code-file-loader" "^8.1.28" + "@graphql-tools/git-loader" "^8.0.32" + "@graphql-tools/github-loader" "^9.0.6" + "@graphql-tools/graphql-file-loader" "^8.1.11" + "@graphql-tools/json-file-loader" "^8.0.26" + "@graphql-tools/load" "^8.1.8" + "@graphql-tools/merge" "^9.0.6" + "@graphql-tools/url-loader" "^9.0.6" + "@graphql-tools/utils" "^11.0.0" "@inquirer/prompts" "^7.8.2" "@whatwg-node/fetch" "^0.10.0" chalk "^4.1.0" cosmiconfig "^9.0.0" debounce "^2.0.0" detect-indent "^6.0.0" - graphql-config "^5.1.1" + graphql-config "^5.1.6" is-glob "^4.0.1" jiti "^2.3.0" json-to-pretty-yaml "^1.2.2" @@ -614,33 +622,33 @@ yaml "^2.3.1" yargs "^17.0.0" -"@graphql-codegen/client-preset@^5.2.0": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.2.1.tgz#0a5c13c61da21a30e4d60af05c881be45d8f574b" - integrity sha512-6qFjHQQUWrEH+MVvWs5sPUgme8X+Ivg3WfzaCESooRBQZ4/EnSFlXkPWUTbOKYLRUoMv4g6iTRcZQf6u1wtHZA== +"@graphql-codegen/client-preset@^5.2.4": + version "5.2.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.2.4.tgz#f5096cfd19d8726a6282ec07640c7e5411196755" + integrity sha512-k4f9CoepkVznXRReCHBVnG/FeQVQgIOhgtkaJ6I9FcQRzUkrm9ASmQjOdNdMlZt0DHTU4nbVxIBGZW7gk1RavA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/gql-tag-operations" "5.1.1" - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/typed-document-node" "^6.1.4" - "@graphql-codegen/typescript" "^5.0.6" - "@graphql-codegen/typescript-operations" "^5.0.6" - "@graphql-codegen/visitor-plugin-common" "^6.2.1" + "@graphql-codegen/gql-tag-operations" "5.1.4" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/typed-document-node" "^6.1.7" + "@graphql-codegen/typescript" "^5.0.9" + "@graphql-codegen/typescript-operations" "^5.0.9" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" "@graphql-tools/documents" "^1.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/utils" "^11.0.0" "@graphql-typed-document-node/core" "3.2.0" tslib "~2.6.0" -"@graphql-codegen/core@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.0.tgz#c3fd07226a3cd2f75c80896c127adc191f42e179" - integrity sha512-vLTEW0m8LbE4xgRwbFwCdYxVkJ1dBlVJbQyLb9Q7bHnVFgHAP982Xo8Uv7FuPBmON+2IbTjkCqhFLHVZbqpvjQ== +"@graphql-codegen/core@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.1.tgz#0b34d222e30a2dd97b42c2ffbec4087800a883dc" + integrity sha512-eQD7aXpKkKvaydMv5Bu0FnKCPnNMAhZ3vZW+K4Rl9IAC2w5PDv9lJhs3YTWM9W58zNOZpGQGT2F0ekS3QNIiKw== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.1" "@graphql-tools/schema" "^10.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/utils" "^11.0.0" tslib "~2.6.0" "@graphql-codegen/fragment-matcher@^6.0.0": @@ -651,92 +659,92 @@ "@graphql-codegen/plugin-helpers" "^6.0.0" tslib "~2.6.0" -"@graphql-codegen/gql-tag-operations@5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.1.1.tgz#4be470728b03fc616c3422068b1fb6138b9af92c" - integrity sha512-XewD0XxN2sgKieEIFeGWV5yT5X2aNy+eg+K8bHlUD7QfyrN2bi67rv/O5Edu7LVDOJR69uqVBp++18d742mn3Q== +"@graphql-codegen/gql-tag-operations@5.1.4": + version "5.1.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.1.4.tgz#4714f2475868b43015420aba05a9dc04868a7d5e" + integrity sha512-tDj/0a1U7rDH3PQgLeA+PlgBNb593MIJ43oAOKMRgJPwIQ9T7p2oqBRLxwfFZFTDLwnwsGZ7xIKqIcGgyAIj5Q== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/visitor-plugin-common" "6.2.1" - "@graphql-tools/utils" "^10.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-tools/utils" "^11.0.0" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/near-operation-file-preset@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-4.0.0.tgz#79fa9d0a587a54751fa4075d0f85473412c5372a" - integrity sha512-InHE3tN0dZKCZivrQ950y6Tnxtt/DJbvqqm5iCOmExJeIzhLoMOB0Rq2ex4KsRdSY9YtpYbYNCmO2xSU3MbRcg== +"@graphql-codegen/near-operation-file-preset@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-5.0.0.tgz#00cf43ce61eba094f82751f3b1709b7a9b9857b9" + integrity sha512-Ck5jMu2izbKFwODYCM/sGWNQKN4BN4fpxr8g99fs6ESH29yfWMjA1dbUlTbtQtR7iXy39jT8451BLTG4/vOIxQ== dependencies: "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/visitor-plugin-common" "^6.2.1" - "@graphql-tools/utils" "^10.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-tools/utils" "^11.0.0" parse-filepath "^1.0.2" tslib "^2.8.1" -"@graphql-codegen/plugin-helpers@^6.0.0", "@graphql-codegen/plugin-helpers@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.1.0.tgz#251d76dac2801a4baaef5613b9a22698c1218c47" - integrity sha512-JJypehWTcty9kxKiqH7TQOetkGdOYjY78RHlI+23qB59cV2wxjFFVf8l7kmuXS4cpGVUNfIjFhVr7A1W7JMtdA== +"@graphql-codegen/plugin-helpers@^6.0.0", "@graphql-codegen/plugin-helpers@^6.1.1", "@graphql-codegen/plugin-helpers@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.2.0.tgz#d10a56939c9341522e30c23aa2a83b10eafcd0c0" + integrity sha512-TKm0Q0+wRlg354Qt3PyXc+sy6dCKxmNofBsgmHoFZNVHtzMQSSgNT+rUWdwBwObQ9bFHiUVsDIv8QqxKMiKmpw== dependencies: - "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/utils" "^11.0.0" change-case-all "1.0.15" common-tags "1.8.2" import-from "4.0.0" lodash "~4.17.0" tslib "~2.6.0" -"@graphql-codegen/schema-ast@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.0.tgz#9708d7484a01bb3a502685126708d7b139fd0721" - integrity sha512-jn7Q3PKQc0FxXjbpo9trxzlz/GSFQWxL042l0iC8iSbM/Ar+M7uyBwMtXPsev/3Razk+osQyreghIz0d2+6F7Q== +"@graphql-codegen/schema-ast@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.1.tgz#01034d85460179f552ac495853539eb318d93732" + integrity sha512-svLffXddnXxq1qFXQqqh+zYrxdiMnIKm+CXCUv0MYhLh0R4L5vpnaTzIUCk3icHNNXhKRm2uBD70+K8VY0xiCg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-tools/utils" "^11.0.0" tslib "~2.6.0" -"@graphql-codegen/typed-document-node@^6.1.4": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.4.tgz#a7ea8b556e140dfdc969718d8019af6e103b8fe0" - integrity sha512-ITWsA+qvT7R64z7KmYHXfgyD5ff069FAGq/hpR0EWVfzXT4RW1Xn/3Biw7/jvwMGsS1BTjo8ZLSIMNM8KjE3GA== +"@graphql-codegen/typed-document-node@^6.1.7": + version "6.1.7" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.7.tgz#cdb3bbc6a5c430d16fd23be60ce2734cfb73a901" + integrity sha512-VLL9hB+YPigc/W2QYCkSNMZrkKv42nTchb9mJ0h5VY98YmW/zWb6NeYM80iHSpk8ZvHsuUT5geA53/s1phO2NQ== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/visitor-plugin-common" "6.2.1" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" auto-bind "~4.0.0" change-case-all "1.0.15" tslib "~2.6.0" -"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.0.6": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.6.tgz#4ef74afa125f26f7c2f757d8f11fda66aadea6dc" - integrity sha512-pkR/82qWO50OHWeV3BiDuVxNFxiJerpmNjFep71VlabADXiU3GIeSaDd6G9a1/SCniVTXZQk2ivCb0ZJiuwo1A== +"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.9.tgz#95388b2c80434118d5dd102ef5eff7f0de4ca29c" + integrity sha512-jJFdJKMS5Cqisb5QMi7xXHPsJH9yHBMYOxBc8laFkFjHk/AOqJK90qCKbO9lwwTMPZUDe6N/HslmA0ax4J0zsg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/typescript" "^5.0.6" - "@graphql-codegen/visitor-plugin-common" "6.2.1" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/typescript" "^5.0.9" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/typescript@^5.0.4", "@graphql-codegen/typescript@^5.0.6": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.6.tgz#6085b57bd643e0c5f77412018a1784eecedb0146" - integrity sha512-rKW3wYInAnmO/DmKjhW3/KLMxUauUCZuMEPQmuoHChnwIuMjn5kVXCdArGyQqv+vVtFj55aS+sJLN4MPNNjSNg== +"@graphql-codegen/typescript@^5.0.4", "@graphql-codegen/typescript@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.9.tgz#967c3fdd11c79f7adbd612c994367f3b38272435" + integrity sha512-YlIZ4nqdFdzr5vxuNtQtZnnMYuZ5cLYB2HaGhGI2zvqHxCmkBjIRpu/5sfccawKy23wetV+aoWvoNqxGKIryig== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.0" - "@graphql-codegen/schema-ast" "^5.0.0" - "@graphql-codegen/visitor-plugin-common" "6.2.1" + "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/schema-ast" "^5.0.1" + "@graphql-codegen/visitor-plugin-common" "^6.2.4" auto-bind "~4.0.0" tslib "~2.6.0" -"@graphql-codegen/visitor-plugin-common@6.2.1", "@graphql-codegen/visitor-plugin-common@^6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.2.1.tgz#76b02057bdbde00eeb852d72bc814d941ba46550" - integrity sha512-5QT1hCV3286mrmoIC7vlFXsTlwELMexhuFIkjh+oVGGL1E8hxkIPAU0kfH/lsPbQHKi8zKmic2pl3tAdyYxNyg== +"@graphql-codegen/visitor-plugin-common@^6.2.4": + version "6.2.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.2.4.tgz#73906f34e8baaaadf5bfe5fefece4d86f835aebc" + integrity sha512-iwiVCc7Mv8/XAa3K35AdFQ9chJSDv/gYEnBeQFF/Sq/W8EyJoHypOGOTTLk7OSrWO4xea65ggv0e7fGt7rPJjQ== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.0" + "@graphql-codegen/plugin-helpers" "^6.1.1" "@graphql-tools/optimize" "^2.0.0" - "@graphql-tools/relay-operation-optimizer" "^7.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/relay-operation-optimizer" "^7.1.1" + "@graphql-tools/utils" "^11.0.0" auto-bind "~4.0.0" change-case-all "1.0.15" dependency-graph "^1.0.0" @@ -744,81 +752,51 @@ parse-filepath "^1.0.2" tslib "~2.6.0" -"@graphql-hive/signal@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-1.0.0.tgz#6e2193660a47c925abadbe72293dfc9430e24f8f" - integrity sha512-RiwLMc89lTjvyLEivZ/qxAC5nBHoS2CtsWFSOsN35sxG9zoo5Z+JsFHM8MlvmO9yt+MJNIyC5MLE1rsbOphlag== - "@graphql-hive/signal@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-2.0.0.tgz#205805328b118e1ae556417ed364257234800464" integrity sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ== -"@graphql-tools/apollo-engine-loader@^8.0.0": - version "8.0.27" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.27.tgz#cd1216e1375415a7b57caa8e39d58d02b96ef98e" - integrity sha512-XT4BvqmRXkVaT8GgNb9/pr8u4M4vTcvGuI2GlvK+albrJNIV8VxTpsdVYma3kw+VtSIYrxEvLixlfDA/KdmDpg== +"@graphql-tools/apollo-engine-loader@^8.0.28": + version "8.0.28" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.28.tgz#b69334ea21540faee316f0026a53b3889dd90a01" + integrity sha512-MzgDrUuoxp6dZeo54zLBL3cEJKJtM3N/2RqK0rbPxPq5X2z6TUA7EGg8vIFTUkt5xelAsUrm8/4ai41ZDdxOng== dependencies: - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" "@whatwg-node/fetch" "^0.10.13" - sync-fetch "0.6.0-2" + sync-fetch "0.6.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@^10.0.4": - version "10.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.4.tgz#1f61115b650ee1734338ed9ff5d0e57dc1cb9b8d" - integrity sha512-t8E0ILelbaIju0aNujMkKetUmbv3/07nxGSv0kEGLBk9GNtEmQ/Bjj8ZTo2WN35/Fy70zCHz2F/48Nx/Ec48cA== +"@graphql-tools/batch-execute@^10.0.7": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.7.tgz#20a5b2c9cec281e38d6ed13e8d8f0163aa88d947" + integrity sha512-vKo9XUiy2sc5tzMupXoxZbu5afVY/9yJ0+yLrM5Dhh38yHYULf3z9VC1eAwW0kj8pWpOo8d8CV3jpleGwv83PA== dependencies: - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" dataloader "^2.2.3" tslib "^2.8.1" -"@graphql-tools/batch-execute@^9.0.19": - version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-9.0.19.tgz#b724f3542e2cee8a3a71262a711469afa39cae88" - integrity sha512-VGamgY4PLzSx48IHPoblRw0oTaBa7S26RpZXt0Y4NN90ytoE0LutlpB2484RbkfcTjv9wa64QD474+YP1kEgGA== - dependencies: - "@graphql-tools/utils" "^10.9.1" - "@whatwg-node/promise-helpers" "^1.3.0" - dataloader "^2.2.3" - tslib "^2.8.1" - -"@graphql-tools/code-file-loader@^8.0.0": - version "8.1.27" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.27.tgz#bbeec7c97c03564b238b7466baea67274cac8f11" - integrity sha512-q3GDbm+7m3DiAnqxa+lYMgYZd49+ez6iGFfXHmzP6qAnf5WlBxRNKNjNVuxOgoV30DCr+vOJfoXeU7VN1qqGWQ== +"@graphql-tools/code-file-loader@^8.1.28": + version "8.1.28" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.28.tgz#c90655d86210d7ad8489f452867754b49c89f57a" + integrity sha512-BL3Ft/PFlXDE5nNuqA36hYci7Cx+8bDrPDc8X3VSpZy9iKFBY+oQ+IwqnEHCkt8OSp2n2V0gqTg4u3fcQP1Kwg== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.26" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/graphql-tag-pluck" "8.3.27" + "@graphql-tools/utils" "^11.0.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^10.2.23": - version "10.2.23" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-10.2.23.tgz#fcd42e7aa70149269ad644e03098c8d28810bd87" - integrity sha512-xrPtl7f1LxS+B6o+W7ueuQh67CwRkfl+UKJncaslnqYdkxKmNBB4wnzVcW8ZsRdwbsla/v43PtwAvSlzxCzq2w== +"@graphql-tools/delegate@^12.0.12": + version "12.0.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.12.tgz#19d669b3cb7ebe360848ae55f42f4b9769d5bd71" + integrity sha512-/vgLWhIwm+Mgo5VUOJQj6EOpaxXRQmA7mk8j6/8vBbPi56LoYA/UPRygcpEnm9EuXTspFKCTBil+xqThU3EmqQ== dependencies: - "@graphql-tools/batch-execute" "^9.0.19" - "@graphql-tools/executor" "^1.4.9" - "@graphql-tools/schema" "^10.0.25" - "@graphql-tools/utils" "^10.9.1" - "@repeaterjs/repeater" "^3.0.6" - "@whatwg-node/promise-helpers" "^1.3.0" - dataloader "^2.2.3" - dset "^3.1.2" - tslib "^2.8.1" - -"@graphql-tools/delegate@^12.0.2": - version "12.0.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.2.tgz#9f609918bc41b4e36a3a88cf2f9a21f71220bf72" - integrity sha512-1X93onxNgOzRvnZ8Xulwi6gNuBeuDxvGYOjUHEZyesPCsaWsyiVj1Wk6Pw/DTPGLy70sOFUKQGcaZbWnDORM2w== - dependencies: - "@graphql-tools/batch-execute" "^10.0.4" + "@graphql-tools/batch-execute" "^10.0.7" "@graphql-tools/executor" "^1.4.13" "@graphql-tools/schema" "^10.0.29" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^11.0.0" "@repeaterjs/repeater" "^3.0.6" "@whatwg-node/promise-helpers" "^1.3.2" dataloader "^2.2.3" @@ -832,79 +810,35 @@ lodash.sortby "^4.7.0" tslib "^2.4.0" -"@graphql-tools/executor-common@^0.0.4": - version "0.0.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.4.tgz#763603a6d7a22bb09d67ce682e84a0d730ff2bf9" - integrity sha512-SEH/OWR+sHbknqZyROCFHcRrbZeUAyjCsgpVWCRjqjqRbiJiXq6TxNIIOmpXgkrXWW/2Ev4Wms6YSGJXjdCs6Q== - dependencies: - "@envelop/core" "^5.2.3" - "@graphql-tools/utils" "^10.8.1" - -"@graphql-tools/executor-common@^0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-0.0.6.tgz#f681de443d7e90e1768c44bd64a4cd418dc356e3" - integrity sha512-JAH/R1zf77CSkpYATIJw+eOJwsbWocdDjY+avY7G+P5HCXxwQjAjWVkJI1QJBQYjPQDVxwf1fmTZlIN3VOadow== - dependencies: - "@envelop/core" "^5.3.0" - "@graphql-tools/utils" "^10.9.1" - -"@graphql-tools/executor-common@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-1.0.5.tgz#cecd704fb9d84d40af80ee8fc7e6962f96f9fcb9" - integrity sha512-gsBRxP4ui8s7/ppKGCJUQ9xxTNoFpNYmEirgM52EHo74hL5hrpS5o4zOmBH33+9t2ZasBziIfupYtLNa0DgK0g== +"@graphql-tools/executor-common@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-common/-/executor-common-1.0.6.tgz#01f0e11a90c031afe3608b2435e3bfd37ada2692" + integrity sha512-23/K5C+LSlHDI0mj2SwCJ33RcELCcyDUgABm1Z8St7u/4Z5+95i925H/NAjUyggRjiaY8vYtNiMOPE49aPX1sg== dependencies: "@envelop/core" "^5.4.0" - "@graphql-tools/utils" "^10.10.3" - -"@graphql-tools/executor-graphql-ws@^2.0.1": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-2.0.7.tgz#e8d7689d266b3ccea962b708d6f5d31a1a246c66" - integrity sha512-J27za7sKF6RjhmvSOwOQFeNhNHyP4f4niqPnerJmq73OtLx9Y2PGOhkXOEB0PjhvPJceuttkD2O1yMgEkTGs3Q== - dependencies: - "@graphql-tools/executor-common" "^0.0.6" - "@graphql-tools/utils" "^10.9.1" - "@whatwg-node/disposablestack" "^0.0.6" - graphql-ws "^6.0.6" - isomorphic-ws "^5.0.0" - tslib "^2.8.1" - ws "^8.18.3" + "@graphql-tools/utils" "^11.0.0" "@graphql-tools/executor-graphql-ws@^3.1.2": - version "3.1.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.3.tgz#aac6605564f0e7c2f72611487c48960bd445bed3" - integrity sha512-q4k8KLoH2U51XdWJRdiW/KIKbBOtJ1mcILv0ALvBkOF99C3vwGj2zr4U0AMGCD3HzML2mPZuajhfYo/xB/pnZQ== + version "3.1.5" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.5.tgz#5d4ecc7a406126ae77489a0d1129ed7ccd9e5331" + integrity sha512-WXRsfwu9AkrORD9nShrd61OwwxeQ5+eXYcABRR3XPONFIS8pWQfDJGGqxql9/227o/s0DV5SIfkBURb5Knzv+A== dependencies: - "@graphql-tools/executor-common" "^1.0.5" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/executor-common" "^1.0.6" + "@graphql-tools/utils" "^11.0.0" "@whatwg-node/disposablestack" "^0.0.6" graphql-ws "^6.0.6" isows "^1.0.7" tslib "^2.8.1" ws "^8.18.3" -"@graphql-tools/executor-http@^1.1.9": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-1.3.3.tgz#057de79ebc90edbd242259cccb1268d37ee6c579" - integrity sha512-LIy+l08/Ivl8f8sMiHW2ebyck59JzyzO/yF9SFS4NH6MJZUezA1xThUXCDIKhHiD56h/gPojbkpcFvM2CbNE7A== - dependencies: - "@graphql-hive/signal" "^1.0.0" - "@graphql-tools/executor-common" "^0.0.4" - "@graphql-tools/utils" "^10.8.1" - "@repeaterjs/repeater" "^3.0.4" - "@whatwg-node/disposablestack" "^0.0.6" - "@whatwg-node/fetch" "^0.10.4" - "@whatwg-node/promise-helpers" "^1.3.0" - meros "^1.2.1" - tslib "^2.8.1" - "@graphql-tools/executor-http@^3.0.6": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.0.7.tgz#aaf1ed4db37a97c447dee1842099d71173977ad9" - integrity sha512-sHjtiUZmRtkjhpSzMhxT2ywAGzHjuB1rHsiaSLAq8U5BQg5WoLakKYD7BajgVHwNbfWEc+NnFiJI7ldyhiciiQ== + version "3.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.1.1.tgz#2952fec496178b4b0c994cc3ff448bc005401b29" + integrity sha512-Le57fMdN7nGBp8XddkpGreCzcPGCZ+uHs1kPw/xMKPJMsn/vZUHbWJ0FY0cb0jfE3OVhbMIjjSe/OHlG3Mm3zw== dependencies: "@graphql-hive/signal" "^2.0.0" - "@graphql-tools/executor-common" "^1.0.5" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/executor-common" "^1.0.6" + "@graphql-tools/utils" "^11.0.0" "@repeaterjs/repeater" "^3.0.4" "@whatwg-node/disposablestack" "^0.0.6" "@whatwg-node/fetch" "^0.10.13" @@ -912,114 +846,113 @@ meros "^1.3.2" tslib "^2.8.1" -"@graphql-tools/executor-legacy-ws@^1.1.19", "@graphql-tools/executor-legacy-ws@^1.1.24": - version "1.1.24" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.24.tgz#b31c13951b393b461bf0e653fb770b5c87aa9dee" - integrity sha512-wfSpOJCxeBcwVXy3JS4TB4oLwVICuVKPlPQhcAjTRPWYwKerE0HosgUzxCX1fEQ4l1B1OMgKWRglGpoXExKqsQ== +"@graphql-tools/executor-legacy-ws@^1.1.25": + version "1.1.25" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.25.tgz#235c334443a4dba79ceff9c20b0a03e766743d4c" + integrity sha512-6uf4AEXO0QMxJ7AWKVPqEZXgYBJaiz5vf29X0boG8QtcqWy8mqkXKWLND2Swdx0SbEx0efoGFcjuKufUcB0ASQ== dependencies: - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" "@types/ws" "^8.0.0" isomorphic-ws "^5.0.0" tslib "^2.4.0" - ws "^8.17.1" + ws "^8.19.0" -"@graphql-tools/executor@^1.4.13", "@graphql-tools/executor@^1.4.9": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.5.0.tgz#5aca9a7af96f7edcb4659306428d7d970fd0e843" - integrity sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA== +"@graphql-tools/executor@^1.4.13": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.5.1.tgz#2171473cc7cd0b7ca468c21d767eec9ac9df00fe" + integrity sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA== dependencies: - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" "@graphql-typed-document-node/core" "^3.2.0" "@repeaterjs/repeater" "^3.0.4" "@whatwg-node/disposablestack" "^0.0.6" "@whatwg-node/promise-helpers" "^1.0.0" tslib "^2.4.0" -"@graphql-tools/git-loader@^8.0.0": - version "8.0.31" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.31.tgz#d6ac1d30316fb3049afbebb2e67f01b3282a2a15" - integrity sha512-xVHM1JecjpU2P0aOj/IaIUc3w6It8sWOdrJElWFZdY9yfWRqXFYwfemtsn/JOrJDIJXYeGpJ304OeqJD5vFIEw== +"@graphql-tools/git-loader@^8.0.32": + version "8.0.32" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.32.tgz#b72f8830aacf26a10044c39c3a3e512d8f23e8c2" + integrity sha512-H5HTp2vevv0rRMEnCJBVmVF8md3LpJI1C1+d6OtzvmuONJ8mOX2mkf9rtoqwiztynVegaDUekvMFsc9k5iE2WA== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.26" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/graphql-tag-pluck" "8.3.27" + "@graphql-tools/utils" "^11.0.0" is-glob "4.0.3" micromatch "^4.0.8" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/github-loader@^9.0.0": - version "9.0.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.5.tgz#b9e05e2819fc716fcc2ffd61a2ae687d2806d0fa" - integrity sha512-89FRDQGMlzL3607BCQtJhKEiQaZtTmdAnyC5Hmi9giTQXVzEXBbMEZOU0qILxj64cr+smNBx5XqxQ1xn0uZeEA== +"@graphql-tools/github-loader@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.6.tgz#75353e71c431cae3ac04751b804b5122c1704556" + integrity sha512-hhlt2MMkRcvDva/qyzqFddXzaMmRnriJ0Ts+/LcNeYnB8hcEqRMpF9RCsHYjo1mFRaiu8i4PSIpXyyFu3To7Ow== dependencies: "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/graphql-tag-pluck" "^8.3.26" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/graphql-tag-pluck" "^8.3.27" + "@graphql-tools/utils" "^11.0.0" "@whatwg-node/fetch" "^0.10.13" "@whatwg-node/promise-helpers" "^1.0.0" - sync-fetch "0.6.0-2" + sync-fetch "0.6.0" tslib "^2.4.0" -"@graphql-tools/graphql-file-loader@^8.0.0": - version "8.1.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.8.tgz#796eb75be354973d4f524f262d827faa0185741a" - integrity sha512-dZi9Cw+NWEzJAqzIUON9qjZfjebjcoT4H6jqLkEoAv6kRtTq52m4BLXgFWjMHU7PNLE9OOHB9St7UeZQL+GYrw== +"@graphql-tools/graphql-file-loader@^8.0.0", "@graphql-tools/graphql-file-loader@^8.1.11": + version "8.1.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.12.tgz#9a0f3f4a27bfd849a90d5b2297f64f60e54a949c" + integrity sha512-Nma7gBgJoUbqXWTmdHjouo36tjzewA8MptVcHoH7widzkciaUVzBhriHzqICFB/dVxig//g9MX8s1XawZo7UAg== dependencies: - "@graphql-tools/import" "7.1.8" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/import" "^7.1.12" + "@graphql-tools/utils" "^11.0.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@8.3.26", "@graphql-tools/graphql-tag-pluck@^8.3.26": - version "8.3.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.26.tgz#79f9add9cd8d3001e87c60795d6df73258afd47a" - integrity sha512-hLsX++KA3YR/PnNJGBq1weSAY8XUUAQFfOSHanLHA2qs5lcNgU6KWbiLiRsJ/B/ZNi2ZO687dhzeZ4h4Yt0V6Q== +"@graphql-tools/graphql-tag-pluck@8.3.27", "@graphql-tools/graphql-tag-pluck@^8.3.27": + version "8.3.27" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.27.tgz#5f138347addfa3df5c169be69dbe41c902c6227f" + integrity sha512-CJ0WVXhGYsfFngpRrAAcjRHyxSDHx4dEz2W15bkwvt9he/AWhuyXm07wuGcoLrl0q0iQp1BiRjU7D8SxWZo3JQ== dependencies: "@babel/core" "^7.26.10" "@babel/parser" "^7.26.10" "@babel/plugin-syntax-import-assertions" "^7.26.0" "@babel/traverse" "^7.26.10" "@babel/types" "^7.26.10" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" tslib "^2.4.0" -"@graphql-tools/import@7.1.8": - version "7.1.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.8.tgz#7a78c6292b08d8bb795dd63657f1075de3a3408c" - integrity sha512-aUKHMbaeHhCkS867mNCk9sJuvd9xE3Ocr+alwdvILkDxHf7Xaumx4mK8tN9FAXeKhQWGGD5QpkIBnUzt2xoX/A== +"@graphql-tools/import@^7.1.12": + version "7.1.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.12.tgz#a6b04c0c53b58cccb132950913e55b1f31f0f30a" + integrity sha512-QSsdPsdJ7yCgQ5XODyKYpC7NlB9R1Koi0R3418PT7GiRm+9O8gYXSs/23dumcOnpiLrnf4qR2aytBn1+JOAhnA== dependencies: - "@graphql-tools/utils" "^10.11.0" - "@theguild/federation-composition" "^0.21.0" + "@graphql-tools/utils" "^11.0.0" resolve-from "5.0.0" tslib "^2.4.0" -"@graphql-tools/json-file-loader@^8.0.0": - version "8.0.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.25.tgz#2688a5db08a08bf009adce9cfd2f1907d243fe0f" - integrity sha512-Dnr9z818Kdn3rfoZO/+/ZQUqWavjV7AhEp4edV1mGsX+J1HFkNC3WMl6MD3W0hth2HWLQpCFJDdOPnchxnFNfA== +"@graphql-tools/json-file-loader@^8.0.0", "@graphql-tools/json-file-loader@^8.0.26": + version "8.0.26" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.26.tgz#82ff8afbecb77082625c7ee76384da7aed0f70ce" + integrity sha512-kwy9IFi5QtXXTLBgWkvA1RqsZeJDn0CxsTbhNlziCzmga9fNo7qtZ18k9FYIq3EIoQQlok+b7W7yeyJATA2xhw== dependencies: - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/load@^8.1.0": - version "8.1.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.7.tgz#43386396c6fc0bb5aa7fc6d259e266bafeb79103" - integrity sha512-RxrHOC4vVI50+Q1mwgpmTVCB/UDDYVEGD/g/hP3tT2BW9F3rJ7Z3Lmt/nGfPQuWPao3w6vgJ9oSAWtism7CU5w== +"@graphql-tools/load@^8.1.0", "@graphql-tools/load@^8.1.8": + version "8.1.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.8.tgz#977fdf0005078283942e0e4a028ff97c78dd6f0b" + integrity sha512-gxO662b64qZSToK3N6XUxWG5E6HOUjlg5jEnmGvD4bMtGJ0HwEe/BaVZbBQemCfLkxYjwRIBiVfOY9o0JyjZJg== dependencies: - "@graphql-tools/schema" "^10.0.30" - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/schema" "^10.0.31" + "@graphql-tools/utils" "^11.0.0" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.1.6": - version "9.1.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.6.tgz#95eb8bb5ae314bc07a62938b7a1b8c2622e1dc32" - integrity sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw== +"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.0.6", "@graphql-tools/merge@^9.1.7": + version "9.1.7" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.7.tgz#8040553c19aeadf6df41060b811e9c5df8716be1" + integrity sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ== dependencies: - "@graphql-tools/utils" "^10.11.0" + "@graphql-tools/utils" "^11.0.0" tslib "^2.4.0" "@graphql-tools/optimize@^2.0.0": @@ -1029,89 +962,60 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/relay-operation-optimizer@^7.0.0": - version "7.0.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.26.tgz#309ffed9e63038db97db15e7adae667571442af9" - integrity sha512-cVdS2Hw4hg/WgPVV2wRIzZM975pW5k4vdih3hR4SvEDQVr6MmozmlTQSqzMyi9yg8LKTq540Oz3bYQa286yGmg== - dependencies: - "@ardatan/relay-compiler" "^12.0.3" - "@graphql-tools/utils" "^10.11.0" - tslib "^2.4.0" - -"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.25", "@graphql-tools/schema@^10.0.29", "@graphql-tools/schema@^10.0.30": - version "10.0.30" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.30.tgz#cdc9583e70a297d53ff0b344935bd81e3bf7d7ac" - integrity sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA== +"@graphql-tools/relay-operation-optimizer@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.1.1.tgz#5294ffc075dcdf0c2ffcd1f02339374cefba51b8" + integrity sha512-va+ZieMlz6Fj18xUbwyQkZ34PsnzIdPT6Ccy1BNOQw1iclQwk52HejLMZeE/4fH+4cu80Q2HXi5+FjCKpmnJCg== dependencies: - "@graphql-tools/merge" "^9.1.6" - "@graphql-tools/utils" "^10.11.0" + "@ardatan/relay-compiler" "^13.0.0" + "@graphql-tools/utils" "^11.0.0" tslib "^2.4.0" -"@graphql-tools/url-loader@^8.0.0": - version "8.0.33" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-8.0.33.tgz#1e5ebfa6a840e3bdc25336c231df2889028f7e0c" - integrity sha512-Fu626qcNHcqAj8uYd7QRarcJn5XZ863kmxsg1sm0fyjyfBJnsvC7ddFt6Hayz5kxVKfsnjxiDfPMXanvsQVBKw== +"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.29", "@graphql-tools/schema@^10.0.31": + version "10.0.31" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.31.tgz#dea275db4d7423f293826cff2aa11e7b3027ddce" + integrity sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ== dependencies: - "@graphql-tools/executor-graphql-ws" "^2.0.1" - "@graphql-tools/executor-http" "^1.1.9" - "@graphql-tools/executor-legacy-ws" "^1.1.19" - "@graphql-tools/utils" "^10.9.1" - "@graphql-tools/wrap" "^10.0.16" - "@types/ws" "^8.0.0" - "@whatwg-node/fetch" "^0.10.0" - "@whatwg-node/promise-helpers" "^1.0.0" - isomorphic-ws "^5.0.0" - sync-fetch "0.6.0-2" + "@graphql-tools/merge" "^9.1.7" + "@graphql-tools/utils" "^11.0.0" tslib "^2.4.0" - ws "^8.17.1" -"@graphql-tools/url-loader@^9.0.0": - version "9.0.5" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.5.tgz#49ac043c4758ee521d6400a5c9f11eb4c9dcf1f7" - integrity sha512-EPNhZBBL48TudLdyenOw1wV9dI7vsinWLLxSTtkx4zUQxmU+p/LxMyf7MUwjmp3yFZhR/9XchsTZX6uvOyXWqA== +"@graphql-tools/url-loader@^9.0.0", "@graphql-tools/url-loader@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.6.tgz#281ad72428170a08167423071e2ee9a06d910f49" + integrity sha512-QdJI3f7ANDMYfYazRgJzzybznjOrQAOuDXweC9xmKgPZoTqNxEAsatiy69zcpTf6092taJLyrqRH6R7xUTzf4A== dependencies: "@graphql-tools/executor-graphql-ws" "^3.1.2" "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/executor-legacy-ws" "^1.1.24" - "@graphql-tools/utils" "^10.11.0" - "@graphql-tools/wrap" "^11.0.0" + "@graphql-tools/executor-legacy-ws" "^1.1.25" + "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/wrap" "^11.1.1" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.10.13" "@whatwg-node/promise-helpers" "^1.0.0" isomorphic-ws "^5.0.0" - sync-fetch "0.6.0-2" + sync-fetch "0.6.0" tslib "^2.4.0" - ws "^8.17.1" + ws "^8.19.0" -"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.10.3", "@graphql-tools/utils@^10.11.0", "@graphql-tools/utils@^10.8.1", "@graphql-tools/utils@^10.9.1": - version "10.11.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.11.0.tgz#2513dca9ca25bab5a2651da2d66c37b1e2616bdf" - integrity sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q== +"@graphql-tools/utils@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-11.0.0.tgz#f54a09372e96c32416fcbd0eb8e9e0f1338900bd" + integrity sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@whatwg-node/promise-helpers" "^1.0.0" cross-inspect "1.0.1" tslib "^2.4.0" -"@graphql-tools/wrap@^10.0.16": - version "10.1.4" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-10.1.4.tgz#46f94b00113693d4d4eccddcf262494164234ba6" - integrity sha512-7pyNKqXProRjlSdqOtrbnFRMQAVamCmEREilOXtZujxY6kYit3tvWWSjUrcIOheltTffoRh7EQSjpy2JDCzasg== - dependencies: - "@graphql-tools/delegate" "^10.2.23" - "@graphql-tools/schema" "^10.0.25" - "@graphql-tools/utils" "^10.9.1" - "@whatwg-node/promise-helpers" "^1.3.0" - tslib "^2.8.1" - -"@graphql-tools/wrap@^11.0.0": - version "11.1.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.2.tgz#abd122104d90cf0e00418edb8fc2093f6d95e9a6" - integrity sha512-TcKZzUzJNmuyMBQ1oMdnxhBUUacN/5VEJu0/1KVce2aIzCwTTaN9JTU3MgjO7l5Ixn4QLkc6XbxYNv0cHDQgtQ== +"@graphql-tools/wrap@^11.1.1": + version "11.1.12" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.12.tgz#2a26247c6d1953b32f5ae08899266ad2529af174" + integrity sha512-PJ0tuiGbEOOZAJk2/pTKyzMEbwBncPBfO7Z84tCPzM/CAR4ZlAXbXjaXOw4fdi0ReUDyOG06Z8DGgEQjr68dKw== dependencies: - "@graphql-tools/delegate" "^12.0.2" + "@graphql-tools/delegate" "^12.0.12" "@graphql-tools/schema" "^10.0.29" - "@graphql-tools/utils" "^10.10.3" + "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" tslib "^2.8.1" @@ -1144,9 +1048,9 @@ integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== "@img/colour@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" - integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.1.0.tgz#b0c2c2fa661adf75effd6b4964497cd80010bb9d" + integrity sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ== "@img/sharp-darwin-arm64@0.34.5": version "0.34.5" @@ -1463,108 +1367,107 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.2.0.tgz#c52fcd5b58fdd2e8eb66b2fd8ae56f2f64d05b28" - integrity sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ== +"@jest/console@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.3.0.tgz#42ccc3f995d400a8fe35b8850cfe10a8d4804cdf" + integrity sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" - jest-message-util "30.2.0" - jest-util "30.2.0" + jest-message-util "30.3.0" + jest-util "30.3.0" slash "^3.0.0" -"@jest/core@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.2.0.tgz#813d59faa5abd5510964a8b3a7b17cc77b775275" - integrity sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ== +"@jest/core@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.3.0.tgz#d06bb8456f35350f6494fd2405bcec4abb97b994" + integrity sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw== dependencies: - "@jest/console" "30.2.0" + "@jest/console" "30.3.0" "@jest/pattern" "30.0.1" - "@jest/reporters" "30.2.0" - "@jest/test-result" "30.2.0" - "@jest/transform" "30.2.0" - "@jest/types" "30.2.0" + "@jest/reporters" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" ansi-escapes "^4.3.2" chalk "^4.1.2" ci-info "^4.2.0" exit-x "^0.2.2" graceful-fs "^4.2.11" - jest-changed-files "30.2.0" - jest-config "30.2.0" - jest-haste-map "30.2.0" - jest-message-util "30.2.0" + jest-changed-files "30.3.0" + jest-config "30.3.0" + jest-haste-map "30.3.0" + jest-message-util "30.3.0" jest-regex-util "30.0.1" - jest-resolve "30.2.0" - jest-resolve-dependencies "30.2.0" - jest-runner "30.2.0" - jest-runtime "30.2.0" - jest-snapshot "30.2.0" - jest-util "30.2.0" - jest-validate "30.2.0" - jest-watcher "30.2.0" - micromatch "^4.0.8" - pretty-format "30.2.0" + jest-resolve "30.3.0" + jest-resolve-dependencies "30.3.0" + jest-runner "30.3.0" + jest-runtime "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" + jest-watcher "30.3.0" + pretty-format "30.3.0" slash "^3.0.0" -"@jest/diff-sequences@30.0.1": - version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz#0ededeae4d071f5c8ffe3678d15f3a1be09156be" - integrity sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw== +"@jest/diff-sequences@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz#25b0818d3d83f00b9c7b04e069b8810f9014b143" + integrity sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA== -"@jest/environment@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.2.0.tgz#1e673cdb8b93ded707cf6631b8353011460831fa" - integrity sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g== +"@jest/environment@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.3.0.tgz#b0657c2944b6ef3352f7b25903cc3a23e6ab70f6" + integrity sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw== dependencies: - "@jest/fake-timers" "30.2.0" - "@jest/types" "30.2.0" + "@jest/fake-timers" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" - jest-mock "30.2.0" + jest-mock "30.3.0" -"@jest/expect-utils@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.2.0.tgz#4f95413d4748454fdb17404bf1141827d15e6011" - integrity sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA== +"@jest/expect-utils@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.3.0.tgz#c45b2da9802ffed33bf43b3e019ddb95e5ad95e8" + integrity sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA== dependencies: "@jest/get-type" "30.1.0" -"@jest/expect@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.2.0.tgz#9a5968499bb8add2bbb09136f69f7df5ddbf3185" - integrity sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA== +"@jest/expect@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.3.0.tgz#08ee7f5b610167b0068743246c0b568f4c40c773" + integrity sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg== dependencies: - expect "30.2.0" - jest-snapshot "30.2.0" + expect "30.3.0" + jest-snapshot "30.3.0" -"@jest/fake-timers@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.2.0.tgz#0941ddc28a339b9819542495b5408622dc9e94ec" - integrity sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw== +"@jest/fake-timers@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.3.0.tgz#2b2868130c1d28233a79566874c42cae1c5a70bc" + integrity sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ== dependencies: - "@jest/types" "30.2.0" - "@sinonjs/fake-timers" "^13.0.0" + "@jest/types" "30.3.0" + "@sinonjs/fake-timers" "^15.0.0" "@types/node" "*" - jest-message-util "30.2.0" - jest-mock "30.2.0" - jest-util "30.2.0" + jest-message-util "30.3.0" + jest-mock "30.3.0" + jest-util "30.3.0" "@jest/get-type@30.1.0": version "30.1.0" resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== -"@jest/globals@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.2.0.tgz#2f4b696d5862664b89c4ee2e49ae24d2bb7e0988" - integrity sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw== +"@jest/globals@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.3.0.tgz#40f4c90e5602629ecda1ca773a8fb21575bb64ea" + integrity sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA== dependencies: - "@jest/environment" "30.2.0" - "@jest/expect" "30.2.0" - "@jest/types" "30.2.0" - jest-mock "30.2.0" + "@jest/environment" "30.3.0" + "@jest/expect" "30.3.0" + "@jest/types" "30.3.0" + jest-mock "30.3.0" "@jest/pattern@30.0.1": version "30.0.1" @@ -1574,31 +1477,31 @@ "@types/node" "*" jest-regex-util "30.0.1" -"@jest/reporters@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.2.0.tgz#a36b28fcbaf0c4595250b108e6f20e363348fd91" - integrity sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ== +"@jest/reporters@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.3.0.tgz#0c1065f6c892665e5a051df22b19df4466ed816b" + integrity sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "30.2.0" - "@jest/test-result" "30.2.0" - "@jest/transform" "30.2.0" - "@jest/types" "30.2.0" + "@jest/console" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" "@jridgewell/trace-mapping" "^0.3.25" "@types/node" "*" chalk "^4.1.2" collect-v8-coverage "^1.0.2" exit-x "^0.2.2" - glob "^10.3.10" + glob "^10.5.0" graceful-fs "^4.2.11" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^5.0.0" istanbul-reports "^3.1.3" - jest-message-util "30.2.0" - jest-util "30.2.0" - jest-worker "30.2.0" + jest-message-util "30.3.0" + jest-util "30.3.0" + jest-worker "30.3.0" slash "^3.0.0" string-length "^4.0.2" v8-to-istanbul "^9.0.1" @@ -1610,12 +1513,12 @@ dependencies: "@sinclair/typebox" "^0.34.0" -"@jest/snapshot-utils@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.2.0.tgz#387858eb90c2f98f67bff327435a532ac5309fbe" - integrity sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug== +"@jest/snapshot-utils@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz#ca003c91a3e1e4e4956dee716a2aaf04b6707f31" + integrity sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" chalk "^4.1.2" graceful-fs "^4.2.11" natural-compare "^1.4.0" @@ -1629,51 +1532,50 @@ callsites "^3.1.0" graceful-fs "^4.2.11" -"@jest/test-result@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.2.0.tgz#9c0124377fb7996cdffb86eda3dbc56eacab363d" - integrity sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg== +"@jest/test-result@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.3.0.tgz#cd8882d683d467fcffb98c09501a65687a76aae9" + integrity sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ== dependencies: - "@jest/console" "30.2.0" - "@jest/types" "30.2.0" + "@jest/console" "30.3.0" + "@jest/types" "30.3.0" "@types/istanbul-lib-coverage" "^2.0.6" collect-v8-coverage "^1.0.2" -"@jest/test-sequencer@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz#bf0066bc72e176d58f5dfa7f212b6e7eee44f221" - integrity sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q== +"@jest/test-sequencer@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz#27002b2093f4e0d9e0e1ebb0bc274a242fdadc14" + integrity sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA== dependencies: - "@jest/test-result" "30.2.0" + "@jest/test-result" "30.3.0" graceful-fs "^4.2.11" - jest-haste-map "30.2.0" + jest-haste-map "30.3.0" slash "^3.0.0" -"@jest/transform@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.2.0.tgz#54bef1a4510dcbd58d5d4de4fe2980a63077ef2a" - integrity sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA== +"@jest/transform@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.3.0.tgz#9e6f78ffa205449bf956e269fd707c160f47ce2f" + integrity sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A== dependencies: "@babel/core" "^7.27.4" - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@jridgewell/trace-mapping" "^0.3.25" babel-plugin-istanbul "^7.0.1" chalk "^4.1.2" convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.11" - jest-haste-map "30.2.0" + jest-haste-map "30.3.0" jest-regex-util "30.0.1" - jest-util "30.2.0" - micromatch "^4.0.8" + jest-util "30.3.0" pirates "^4.0.7" slash "^3.0.0" write-file-atomic "^5.0.1" -"@jest/types@30.2.0": - version "30.2.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.2.0.tgz#1c678a7924b8f59eafd4c77d56b6d0ba976d62b8" - integrity sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg== +"@jest/types@30.3.0": + version "30.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.3.0.tgz#cada800d323cb74945c24ac74615fdb312a6c85f" + integrity sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw== dependencies: "@jest/pattern" "30.0.1" "@jest/schemas" "30.0.5" @@ -1734,57 +1636,57 @@ "@emnapi/runtime" "^1.4.3" "@tybys/wasm-util" "^0.10.0" -"@next/env@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.0.8.tgz#6167b208ffde10fee64230e09daff387f3f451fd" - integrity sha512-xP4WrQZuj9MdmLJy3eWFHepo+R3vznsMSS8Dy3wdA7FKpjCiesQ6DxZvdGziQisj0tEtCgBKJzjcAc4yZOgLEQ== +"@next/env@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.1.tgz#3896715e28c850355b7b1c9c687beb9d7e9cdc40" + integrity sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg== -"@next/eslint-plugin-next@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.0.8.tgz#62018f7f9eb3eda013f72a67f0805bda51360eb0" - integrity sha512-1miV0qXDcLUaOdHridVPCh4i39ElRIAraseVIbb3BEqyZ5ol9sPyjTP/GNTPV5rBxqxjF6/vv5zQTVbhiNaLqA== +"@next/eslint-plugin-next@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz#994717439ae3259268ed5f7d6830966f0fbb3c9d" + integrity sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.8.tgz#0b28316ea33542365d3a7770c548910992b29cae" - integrity sha512-yjVMvTQN21ZHOclQnhSFbjBTEizle+1uo4NV6L4rtS9WO3nfjaeJYw+H91G+nEf3Ef43TaEZvY5mPWfB/De7tA== - -"@next/swc-darwin-x64@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.8.tgz#51139ce4b2fca924c1df48b0a0b33a7ba6641ed2" - integrity sha512-+zu2N3QQ0ZOb6RyqQKfcu/pn0UPGmg+mUDqpAAEviAcEVEYgDckemOpiMRsBP3IsEKpcoKuNzekDcPczEeEIzA== - -"@next/swc-linux-arm64-gnu@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.8.tgz#323625ec532c119495fbea6301c29c00fa25b767" - integrity sha512-LConttk+BeD0e6RG0jGEP9GfvdaBVMYsLJ5aDDweKiJVVCu6sGvo+Ohz9nQhvj7EQDVVRJMCGhl19DmJwGr6bQ== - -"@next/swc-linux-arm64-musl@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.8.tgz#19a988df87f26390233cbe8eb793edde7157c84b" - integrity sha512-JaXFAlqn8fJV+GhhA9lpg6da/NCN/v9ub98n3HoayoUSPOVdoxEEt86iT58jXqQCs/R3dv5ZnxGkW8aF4obMrQ== - -"@next/swc-linux-x64-gnu@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.8.tgz#71dc3516fab7ca793bf0fd424c638280175afcc1" - integrity sha512-O7M9it6HyNhsJp3HNAsJoHk5BUsfj7hRshfptpGcVsPZ1u0KQ/oVy8oxF7tlwxA5tR43VUP0yRmAGm1us514ng== - -"@next/swc-linux-x64-musl@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.8.tgz#6878cbd85eee73dbd18b3e34791e4e3cc9dc5f43" - integrity sha512-8+KClEC/GLI2dLYcrWwHu5JyC5cZYCFnccVIvmxpo6K+XQt4qzqM5L4coofNDZYkct/VCCyJWGbZZDsg6w6LFA== - -"@next/swc-win32-arm64-msvc@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.8.tgz#556fadbd1915060ef821eb0e38b86866194bcbcf" - integrity sha512-rpQ/PgTEgH68SiXmhu/cJ2hk9aZ6YgFvspzQWe2I9HufY6g7V02DXRr/xrVqOaKm2lenBFPNQ+KAaeveywqV+A== - -"@next/swc-win32-x64-msvc@16.0.8": - version "16.0.8" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.8.tgz#9cb3db37d0113ce099877e04b975868e57782204" - integrity sha512-jWpWjWcMQu2iZz4pEK2IktcfR+OA9+cCG8zenyLpcW8rN4rzjfOzH4yj/b1FiEAZHKS+5Vq8+bZyHi+2yqHbFA== +"@next/swc-darwin-arm64@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz#8bd5c16ee04eb5f07d4f3ca71a3d5270093a9de6" + integrity sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q== + +"@next/swc-darwin-x64@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz#4c1a9134cd442e7fcd74bbe85ab283616ece06cb" + integrity sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA== + +"@next/swc-linux-arm64-gnu@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz#ec08722d22551ea649872df907a8fee027ab1828" + integrity sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw== + +"@next/swc-linux-arm64-musl@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz#4d6270f5be7905c1a3e4f1c4f9cf4b8c62331561" + integrity sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ== + +"@next/swc-linux-x64-gnu@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz#2d55519ba822cd27d9d65ed45a0ace3562df1dcc" + integrity sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg== + +"@next/swc-linux-x64-musl@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz#607b03c3a5bade2368beb4f5b4cac9c243333638" + integrity sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg== + +"@next/swc-win32-arm64-msvc@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz#a96e776f37287b39e309e0850a8d8e2c6c749070" + integrity sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA== + +"@next/swc-win32-x64-msvc@16.2.1": + version "16.2.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz#a299bf2b5688029429061d13492c57ccf947c9c5" + integrity sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1812,94 +1714,94 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@parcel/watcher-android-arm64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" - integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== - -"@parcel/watcher-darwin-arm64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67" - integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== - -"@parcel/watcher-darwin-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" - integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== - -"@parcel/watcher-freebsd-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" - integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== - -"@parcel/watcher-linux-arm-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" - integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== - -"@parcel/watcher-linux-arm-musl@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" - integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== - -"@parcel/watcher-linux-arm64-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" - integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== - -"@parcel/watcher-linux-arm64-musl@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" - integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== - -"@parcel/watcher-linux-x64-glibc@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" - integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== - -"@parcel/watcher-linux-x64-musl@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" - integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== - -"@parcel/watcher-win32-arm64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" - integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== - -"@parcel/watcher-win32-ia32@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" - integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== - -"@parcel/watcher-win32-x64@2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" - integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== +"@parcel/watcher-android-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" + integrity sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A== + +"@parcel/watcher-darwin-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz#88d3e720b59b1eceffce98dac46d7c40e8be5e8e" + integrity sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA== + +"@parcel/watcher-darwin-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz#bf05d76a78bc15974f15ec3671848698b0838063" + integrity sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg== + +"@parcel/watcher-freebsd-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz#8bc26e9848e7303ac82922a5ae1b1ef1bdb48a53" + integrity sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng== + +"@parcel/watcher-linux-arm-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz#1328fee1deb0c2d7865079ef53a2ba4cc2f8b40a" + integrity sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ== + +"@parcel/watcher-linux-arm-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz#bad0f45cb3e2157746db8b9d22db6a125711f152" + integrity sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg== + +"@parcel/watcher-linux-arm64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz#b75913fbd501d9523c5f35d420957bf7d0204809" + integrity sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA== + +"@parcel/watcher-linux-arm64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz#da5621a6a576070c8c0de60dea8b46dc9c3827d4" + integrity sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA== + +"@parcel/watcher-linux-x64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz#ce437accdc4b30f93a090b4a221fd95cd9b89639" + integrity sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ== + +"@parcel/watcher-linux-x64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz#02400c54b4a67efcc7e2327b249711920ac969e2" + integrity sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg== + +"@parcel/watcher-win32-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz#caae3d3c7583ca0a7171e6bd142c34d20ea1691e" + integrity sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q== + +"@parcel/watcher-win32-ia32@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz#9ac922550896dfe47bfc5ae3be4f1bcaf8155d6d" + integrity sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g== + +"@parcel/watcher-win32-x64@2.5.6": + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz#73fdafba2e21c448f0e456bbe13178d8fe11739d" + integrity sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw== "@parcel/watcher@^2.5.1": - version "2.5.1" - resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" - integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== + version "2.5.6" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.6.tgz#3f932828c894f06d0ad9cfefade1756ecc6ef1f1" + integrity sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ== dependencies: - detect-libc "^1.0.3" + detect-libc "^2.0.3" is-glob "^4.0.3" - micromatch "^4.0.5" node-addon-api "^7.0.0" + picomatch "^4.0.3" optionalDependencies: - "@parcel/watcher-android-arm64" "2.5.1" - "@parcel/watcher-darwin-arm64" "2.5.1" - "@parcel/watcher-darwin-x64" "2.5.1" - "@parcel/watcher-freebsd-x64" "2.5.1" - "@parcel/watcher-linux-arm-glibc" "2.5.1" - "@parcel/watcher-linux-arm-musl" "2.5.1" - "@parcel/watcher-linux-arm64-glibc" "2.5.1" - "@parcel/watcher-linux-arm64-musl" "2.5.1" - "@parcel/watcher-linux-x64-glibc" "2.5.1" - "@parcel/watcher-linux-x64-musl" "2.5.1" - "@parcel/watcher-win32-arm64" "2.5.1" - "@parcel/watcher-win32-ia32" "2.5.1" - "@parcel/watcher-win32-x64" "2.5.1" + "@parcel/watcher-android-arm64" "2.5.6" + "@parcel/watcher-darwin-arm64" "2.5.6" + "@parcel/watcher-darwin-x64" "2.5.6" + "@parcel/watcher-freebsd-x64" "2.5.6" + "@parcel/watcher-linux-arm-glibc" "2.5.6" + "@parcel/watcher-linux-arm-musl" "2.5.6" + "@parcel/watcher-linux-arm64-glibc" "2.5.6" + "@parcel/watcher-linux-arm64-musl" "2.5.6" + "@parcel/watcher-linux-x64-glibc" "2.5.6" + "@parcel/watcher-linux-x64-musl" "2.5.6" + "@parcel/watcher-win32-arm64" "2.5.6" + "@parcel/watcher-win32-ia32" "2.5.6" + "@parcel/watcher-win32-x64" "2.5.6" "@pkgjs/parseargs@^0.11.0": version "0.11.0" @@ -1911,85 +1813,396 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== -"@rc-component/async-validator@^5.0.3": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz#5291ad92f00a14b6766fc81735c234277f83e948" - integrity sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg== +"@rc-component/async-validator@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.1.0.tgz#e81f31e676d9cadc71e4310bbf1749c7a5882291" + integrity sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA== dependencies: "@babel/runtime" "^7.24.4" -"@rc-component/color-picker@~2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz#6b9b96152466a9d4475cbe72b40b594bfda164be" - integrity sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q== +"@rc-component/cascader@~1.14.0": + version "1.14.0" + resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.14.0.tgz#74e1fca58cb14f8f75f6e4bf1debd90534aaea7c" + integrity sha512-Ip9356xwZUR2nbW5PRVGif4B/bDve4pLa/N+PGbvBaTnjbvmN4PFMBGQSmlDlzKP1ovxaYMvwF/dI9lXNLT4iQ== dependencies: - "@ant-design/fast-color" "^2.0.6" - "@babel/runtime" "^7.23.6" - classnames "^2.2.6" - rc-util "^5.38.1" + "@rc-component/select" "~1.6.0" + "@rc-component/tree" "~1.2.0" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" -"@rc-component/context@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-1.4.0.tgz#dc6fb021d6773546af8f016ae4ce9aea088395e8" - integrity sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w== +"@rc-component/checkbox@~2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/checkbox/-/checkbox-2.0.0.tgz#90e0b30c276e507a5ab9942f626fe4572f5e4ff9" + integrity sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ== + dependencies: + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/collapse@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/collapse/-/collapse-1.2.0.tgz#f4f041c0d0e97298b83aad8491fde9fe75b3ac48" + integrity sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw== dependencies: "@babel/runtime" "^7.10.1" - rc-util "^5.27.0" + "@rc-component/motion" "^1.1.4" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" -"@rc-component/mini-decimal@^1.0.1": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz#7b7a362b14a0a54cb5bc6fd2b82731f29f11d9b0" - integrity sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ== +"@rc-component/color-picker@~3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@rc-component/color-picker/-/color-picker-3.1.1.tgz#0a00411457e697cf9320e945762a4b08f71938f9" + integrity sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg== dependencies: - "@babel/runtime" "^7.18.0" + "@ant-design/fast-color" "^3.0.1" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" -"@rc-component/mutate-observer@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz#ee53cc88b78aade3cd0653609215a44779386fd8" - integrity sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw== +"@rc-component/context@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-2.0.1.tgz#88c7a565ae92c34a7f02f33c34b145e4039deed0" + integrity sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw== dependencies: - "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" + "@rc-component/util" "^1.3.0" + +"@rc-component/dialog@~1.8.4": + version "1.8.4" + resolved "https://registry.yarnpkg.com/@rc-component/dialog/-/dialog-1.8.4.tgz#e1f05f311539852f40a5717bc3874ce0af64c6ff" + integrity sha512-Ay6PM7phkTkquplG8fWfUGFZ2GTLx9diTl4f0d8Eqxd7W1u1KjE9AQooFQHOHnhZf0Ya3z51+5EKCWHmt/dNEw== + dependencies: + "@rc-component/motion" "^1.1.3" + "@rc-component/portal" "^2.1.0" + "@rc-component/util" "^1.9.0" + clsx "^2.1.1" + +"@rc-component/drawer@~1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@rc-component/drawer/-/drawer-1.4.2.tgz#eb13a6556fb67be28407295c89401b01549224e0" + integrity sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q== + dependencies: + "@rc-component/motion" "^1.1.4" + "@rc-component/portal" "^2.1.3" + "@rc-component/util" "^1.9.0" + clsx "^2.1.1" + +"@rc-component/dropdown@~1.0.0", "@rc-component/dropdown@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@rc-component/dropdown/-/dropdown-1.0.2.tgz#c6010dac9e3ce0d7cf305523083d499dc779819e" + integrity sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg== + dependencies: + "@rc-component/trigger" "^3.0.0" + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/form@~1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.7.2.tgz#5e9db42f7fb6a55abff5d4eb0c263ccce6473710" + integrity sha512-5C90rXH7aZvvvxB4M5ew+QxROvimdL/lqhSshR8NsyiR7HKOoGQYSitxdfENnH6/0KNFxEy2ranVe2LrTnHZIw== + dependencies: + "@rc-component/async-validator" "^5.1.0" + "@rc-component/util" "^1.6.2" + clsx "^2.1.1" + +"@rc-component/image@~1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@rc-component/image/-/image-1.6.0.tgz#4c3f66b07a1d7c50de23a7bf24660262161daf8d" + integrity sha512-tSfn2ZE/oP082g4QIOxeehkmgnXB7R+5AFj/lIFr4k7pEuxHBdyGIq9axoCY9qea8NN0DY6p4IB/F07tLqaT5A== + dependencies: + "@rc-component/motion" "^1.0.0" + "@rc-component/portal" "^2.1.2" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" -"@rc-component/portal@^1.0.0-8", "@rc-component/portal@^1.0.0-9", "@rc-component/portal@^1.0.2", "@rc-component/portal@^1.1.0", "@rc-component/portal@^1.1.1": +"@rc-component/input-number@~1.6.2": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@rc-component/input-number/-/input-number-1.6.2.tgz#ae04e1ee69393fc047588c632e7ce6e19faf617f" + integrity sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w== + dependencies: + "@rc-component/mini-decimal" "^1.0.1" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" + +"@rc-component/input@~1.1.0", "@rc-component/input@~1.1.2": version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-1.1.2.tgz#55db1e51d784e034442e9700536faaa6ab63fc71" - integrity sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg== + resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.1.2.tgz#5fdb55741c012a3f8847d7bd24e318ed1d02cc05" + integrity sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg== + dependencies: + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" + +"@rc-component/mentions@~1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@rc-component/mentions/-/mentions-1.6.0.tgz#8ffc86052ac6891b83ecf17beff17c7d5cd20f91" + integrity sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ== + dependencies: + "@rc-component/input" "~1.1.0" + "@rc-component/menu" "~1.2.0" + "@rc-component/textarea" "~1.1.0" + "@rc-component/trigger" "^3.0.0" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/menu@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.2.0.tgz#3ec1afd2030b2f3d7917f54eb9a9d6e5d5d1d84a" + integrity sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg== + dependencies: + "@rc-component/motion" "^1.1.4" + "@rc-component/overflow" "^1.0.0" + "@rc-component/trigger" "^3.0.0" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/mini-decimal@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.3.tgz#461d0cd2a91efd3c92503763d8c00625552d211f" + integrity sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw== dependencies: "@babel/runtime" "^7.18.0" - classnames "^2.3.2" - rc-util "^5.24.4" -"@rc-component/qrcode@~1.1.0": +"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.1.tgz#1e56b06841ee677261251e6e69fedc8d73e65b22" + integrity sha512-Wo1mkd0tCcHtvYvpPOmlYJz546z16qlsiwaygmW7NPJpOZOF9GBjhGzdzZSsC2lEJ1IUkWLF4gMHlRA1aSA+Yw== + dependencies: + "@rc-component/util" "^1.2.0" + clsx "^2.1.1" + +"@rc-component/mutate-observer@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz#78f54a23bff7c62b2137dfb67e063c1be6ac0652" + integrity sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w== + dependencies: + "@rc-component/util" "^1.2.0" + +"@rc-component/notification@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/notification/-/notification-1.2.0.tgz#dd7c7d50f1d3217bfbc75bc46259e212096855c5" + integrity sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA== + dependencies: + "@rc-component/motion" "^1.1.4" + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/overflow@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/overflow/-/overflow-1.0.0.tgz#ade1cc336c66b45eb1631fdfad59ef84c8f90a89" + integrity sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw== + dependencies: + "@babel/runtime" "^7.11.1" + "@rc-component/resize-observer" "^1.0.1" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" + +"@rc-component/pagination@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/pagination/-/pagination-1.2.0.tgz#3a97abda8f1077f514e03a74b3b9c77f9e68499a" + integrity sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw== + dependencies: + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/picker@~1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@rc-component/picker/-/picker-1.9.1.tgz#7ffcb1e4d4655fe2f3d712773e1d3ab9cd5c2a5c" + integrity sha512-9FBYYsvH3HMLICaPDA/1Th5FLaDkFa7qAtangIdlhKb3ZALaR745e9PsOhheJb6asS4QXc12ffiAcjdkZ4C5/g== + dependencies: + "@rc-component/overflow" "^1.0.0" + "@rc-component/resize-observer" "^1.0.0" + "@rc-component/trigger" "^3.6.15" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/portal@^2.1.0", "@rc-component/portal@^2.1.2", "@rc-component/portal@^2.1.3", "@rc-component/portal@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-2.2.0.tgz#ec4c6c3de2cd09fa3ce545f2439a7eb84852a7b9" + integrity sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ== + dependencies: + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/progress@~1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@rc-component/progress/-/progress-1.0.2.tgz#9aba5e24d3ca73a61a451fd041f5d03ca8907c62" + integrity sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ== + dependencies: + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/qrcode@~1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.1.1.tgz#909f181bb9a7469d32a6e96c7f35476d4bd92008" integrity sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA== dependencies: "@babel/runtime" "^7.24.7" -"@rc-component/tour@~1.15.1": - version "1.15.1" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-1.15.1.tgz#9b79808254185fc19e964172d99e25e8c6800ded" - integrity sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ== +"@rc-component/rate@~1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/rate/-/rate-1.0.1.tgz#836c3c0bea69047f4234383e2ce6ab83a02ee26a" + integrity sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw== dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/portal" "^1.0.0-9" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.2" - rc-util "^5.24.4" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" -"@rc-component/trigger@^2.0.0", "@rc-component/trigger@^2.1.1", "@rc-component/trigger@^2.3.0": +"@rc-component/resize-observer@^1.0.0", "@rc-component/resize-observer@^1.0.1", "@rc-component/resize-observer@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@rc-component/resize-observer/-/resize-observer-1.1.1.tgz#216d7edb5259bb7d2a732735f0a103328ac8ad80" + integrity sha512-NfXXMmiR+SmUuKE1NwJESzEUYUFWIDUn2uXpxCTOLwiRUUakd62DRNFjRJArgzyFW8S5rsL4aX5XlyIXyC/vRA== + dependencies: + "@rc-component/util" "^1.2.0" + +"@rc-component/segmented@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@rc-component/segmented/-/segmented-1.3.0.tgz#46f5e3f34630a246e02fe20fd501ed448eb64c17" + integrity sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg== + dependencies: + "@babel/runtime" "^7.11.1" + "@rc-component/motion" "^1.1.4" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/select@~1.6.0", "@rc-component/select@~1.6.14": + version "1.6.15" + resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.6.15.tgz#de2a3c8b020834cabd600b52de573a328c061eef" + integrity sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g== + dependencies: + "@rc-component/overflow" "^1.0.0" + "@rc-component/trigger" "^3.0.0" + "@rc-component/util" "^1.3.0" + "@rc-component/virtual-list" "^1.0.1" + clsx "^2.1.1" + +"@rc-component/slider@~1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/slider/-/slider-1.0.1.tgz#a869eb09be343cfc580b28608edb0b230ceb1f04" + integrity sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g== + dependencies: + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/steps@~1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@rc-component/steps/-/steps-1.2.2.tgz#8440329540e987ccaed252e008972d0b63723d6f" + integrity sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw== + dependencies: + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/switch@~1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rc-component/switch/-/switch-1.0.3.tgz#d6efa8a17ca9c35f0838321c1cfe0b9adb954523" + integrity sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw== + dependencies: + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/table@~1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.9.1.tgz#77a485f871c3a4b1abbb37f8fb9d25fb97efb481" + integrity sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg== + dependencies: + "@rc-component/context" "^2.0.1" + "@rc-component/resize-observer" "^1.0.0" + "@rc-component/util" "^1.1.0" + "@rc-component/virtual-list" "^1.0.1" + clsx "^2.1.1" + +"@rc-component/tabs@~1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.7.0.tgz#8e74ac1ce42c937a64e07fb91a7f7640560df09a" + integrity sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w== + dependencies: + "@rc-component/dropdown" "~1.0.0" + "@rc-component/menu" "~1.2.0" + "@rc-component/motion" "^1.1.3" + "@rc-component/resize-observer" "^1.0.0" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/textarea@~1.1.0", "@rc-component/textarea@~1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/textarea/-/textarea-1.1.2.tgz#2daa5dcb997840040fb8892b0d601ef28d9d1f37" + integrity sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A== + dependencies: + "@rc-component/input" "~1.1.0" + "@rc-component/resize-observer" "^1.0.0" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/tooltip@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/tooltip/-/tooltip-1.4.0.tgz#c8cf15c6773218a5a36271467f06e663f99c28e7" + integrity sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg== + dependencies: + "@rc-component/trigger" "^3.7.1" + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/tour@~2.3.0": version "2.3.0" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-2.3.0.tgz#9499ada078daca9dd99d01f0f0743ee1ab9e398b" - integrity sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg== + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-2.3.0.tgz#3c45ddcab02cd1846f707e8993beecd9dee23a8f" + integrity sha512-K04K9r32kUC+auBSQfr+Fss4SpSIS9JGe56oq/ALAX0p+i2ylYOI1MgR83yBY7v96eO6ZFXcM/igCQmubps0Ow== dependencies: - "@babel/runtime" "^7.23.2" - "@rc-component/portal" "^1.1.0" - classnames "^2.3.2" - rc-motion "^2.0.0" - rc-resize-observer "^1.3.1" - rc-util "^5.44.0" + "@rc-component/portal" "^2.2.0" + "@rc-component/trigger" "^3.0.0" + "@rc-component/util" "^1.7.0" + clsx "^2.1.1" + +"@rc-component/tree-select@~1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.8.0.tgz#480e84221befbd1fa93ab2034423e2b064e41981" + integrity sha512-iYsPq3nuLYvGqdvFAW+l+I9ASRIOVbMXyA8FGZg2lGym/GwkaWeJGzI4eJ7c9IOEhRj0oyfIN4S92Fl3J05mjQ== + dependencies: + "@rc-component/select" "~1.6.0" + "@rc-component/tree" "~1.2.0" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" + +"@rc-component/tree@~1.2.0", "@rc-component/tree@~1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.2.4.tgz#cb4f7d818118b3447763e74d3a82fba6454c7317" + integrity sha512-5Gli43+m4R7NhpYYz3Z61I6LOw9yI6CNChxgVtvrO6xB1qML7iE6QMLVMB3+FTjo2yF6uFdAHtqWPECz/zbX5w== + dependencies: + "@rc-component/motion" "^1.0.0" + "@rc-component/util" "^1.8.1" + "@rc-component/virtual-list" "^1.0.1" + clsx "^2.1.1" + +"@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.0": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-3.9.0.tgz#d4d2df167e9aced1bf17672d9104a3297663f766" + integrity sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg== + dependencies: + "@rc-component/motion" "^1.1.4" + "@rc-component/portal" "^2.2.0" + "@rc-component/resize-observer" "^1.1.1" + "@rc-component/util" "^1.2.1" + clsx "^2.1.1" + +"@rc-component/upload@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@rc-component/upload/-/upload-1.1.0.tgz#cb634587ffdf8a8a4a26a279fac06989fb47f593" + integrity sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw== + dependencies: + "@rc-component/util" "^1.3.0" + clsx "^2.1.1" + +"@rc-component/util@^1.1.0", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.6.2", "@rc-component/util@^1.7.0", "@rc-component/util@^1.8.1", "@rc-component/util@^1.9.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.10.0.tgz#faeebbe0510f998b6d5cee9da9176b6d613cac2c" + integrity sha512-aY9GLBuiUdpyfIUpAWSYer4Tu3mVaZCo5A0q9NtXcazT3MRiI3/WNHCR+DUn5VAtR6iRRf0ynCqQUcHli5UdYw== + dependencies: + is-mobile "^5.0.0" + react-is "^18.2.0" + +"@rc-component/virtual-list@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@rc-component/virtual-list/-/virtual-list-1.0.2.tgz#356c465de522ae3834731827d9fb2311ed09b7e9" + integrity sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ== + dependencies: + "@babel/runtime" "^7.20.0" + "@rc-component/resize-observer" "^1.0.1" + "@rc-component/util" "^1.4.0" + clsx "^2.1.1" "@repeaterjs/repeater@^3.0.4", "@repeaterjs/repeater@^3.0.6": version "3.0.6" @@ -2002,9 +2215,9 @@ integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== "@sinclair/typebox@^0.34.0": - version "0.34.41" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.41.tgz#aa51a6c1946df2c5a11494a2cdb9318e026db16c" - integrity sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g== + version "0.34.48" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.48.tgz#75b0ead87e59e1adbd6dccdc42bad4fddee73b59" + integrity sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA== "@sinonjs/commons@^3.0.1": version "3.0.1" @@ -2013,10 +2226,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@^13.0.0": - version "13.0.5" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz#36b9dbc21ad5546486ea9173d6bea063eb1717d5" - integrity sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw== +"@sinonjs/fake-timers@^15.0.0": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz#e1a6f7171941aadcc31d2cea1744264d58b8b34c" + integrity sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw== dependencies: "@sinonjs/commons" "^3.0.1" @@ -2027,16 +2240,6 @@ dependencies: tslib "^2.8.0" -"@theguild/federation-composition@^0.21.0": - version "0.21.0" - resolved "https://registry.yarnpkg.com/@theguild/federation-composition/-/federation-composition-0.21.0.tgz#e2f3d434ac780255245d3e82e78522cd2e696473" - integrity sha512-cdQ9rDEtBpT553DLLtcsSjtSDIadibIxAD3K5r0eUuDOfxx+es7Uk+aOucfqMlNOM3eybsgJN3T2SQmEsINwmw== - dependencies: - constant-case "^3.0.4" - debug "4.4.3" - json5 "^2.2.3" - lodash.sortby "^4.7.0" - "@tsconfig/node10@^1.0.7": version "1.0.12" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" @@ -2097,7 +2300,12 @@ dependencies: "@babel/types" "^7.28.2" -"@types/estree@^1.0.6": +"@types/esrecurse@^4.3.1": + version "4.3.1" + resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" + integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== + +"@types/estree@^1.0.6", "@types/estree@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== @@ -2138,10 +2346,17 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*", "@types/node@^24.10.1": - version "24.10.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.2.tgz#82a57476a19647d8f2c7750d0924788245e39b26" - integrity sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA== +"@types/node@*": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.5.0.tgz#5c99f37c443d9ccc4985866913f1ed364217da31" + integrity sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw== + dependencies: + undici-types "~7.18.0" + +"@types/node@^24.10.1": + version "24.12.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.0.tgz#6222e028210e5322e4f4f6767f8d88e5ea3b33d2" + integrity sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ== dependencies: undici-types "~7.16.0" @@ -2151,9 +2366,9 @@ integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== "@types/react-highlight-words@^0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.0.tgz#c7e3e8302acb5082980e611ce2d93c6e6d3b4022" - integrity sha512-Qm512TiOakvtNzHJ2+TNVHnLn5cJ2wLQV0+LrhuispVth6dRf5b8ydjq3Kc0thpZ7bz4s6RnG6meboAXHWRK+Q== + version "0.20.1" + resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.1.tgz#f84a47b36d5e571afc7da47f273aaabf366b71a0" + integrity sha512-EgF6RaoibBurIhxk3X3d5xL0uMqjD7KtShvZ8S+d7/o6xZ+mQ4rct1a2E49vWA9r63OzrHCIyghqvQHASb/ERw== dependencies: "@types/react" "*" @@ -2165,9 +2380,9 @@ "@types/react" "*" "@types/react@*", "@types/react@^19.2.7": - version "19.2.7" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.7.tgz#84e62c0f23e8e4e5ac2cadcea1ffeacccae7f62f" - integrity sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg== + version "19.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad" + integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== dependencies: csstype "^3.2.2" @@ -2195,101 +2410,101 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.49.0.tgz#8ed8736b8415a9193989220eadb6031dbcd2260a" - integrity sha512-JXij0vzIaTtCwu6SxTh8qBc66kmf1xs7pI4UOiMDFVct6q86G0Zs7KRcEoJgY3Cav3x5Tq0MF5jwgpgLqgKG3A== - dependencies: - "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.49.0" - "@typescript-eslint/type-utils" "8.49.0" - "@typescript-eslint/utils" "8.49.0" - "@typescript-eslint/visitor-keys" "8.49.0" - ignore "^7.0.0" +"@typescript-eslint/eslint-plugin@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz#ddfdfb30f8b5ccee7f3c21798b377c51370edd55" + integrity sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ== + dependencies: + "@eslint-community/regexpp" "^4.12.2" + "@typescript-eslint/scope-manager" "8.57.1" + "@typescript-eslint/type-utils" "8.57.1" + "@typescript-eslint/utils" "8.57.1" + "@typescript-eslint/visitor-keys" "8.57.1" + ignore "^7.0.5" natural-compare "^1.4.0" - ts-api-utils "^2.1.0" - -"@typescript-eslint/parser@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.49.0.tgz#0ede412d59e99239b770f0f08c76c42fba717fa2" - integrity sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA== - dependencies: - "@typescript-eslint/scope-manager" "8.49.0" - "@typescript-eslint/types" "8.49.0" - "@typescript-eslint/typescript-estree" "8.49.0" - "@typescript-eslint/visitor-keys" "8.49.0" - debug "^4.3.4" - -"@typescript-eslint/project-service@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.49.0.tgz#ce220525c88cb2d23792b391c07e14cb9697651a" - integrity sha512-/wJN0/DKkmRUMXjZUXYZpD1NEQzQAAn9QWfGwo+Ai8gnzqH7tvqS7oNVdTjKqOcPyVIdZdyCMoqN66Ia789e7g== - dependencies: - "@typescript-eslint/tsconfig-utils" "^8.49.0" - "@typescript-eslint/types" "^8.49.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.49.0.tgz#a3496765b57fb48035d671174552e462e5bffa63" - integrity sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg== - dependencies: - "@typescript-eslint/types" "8.49.0" - "@typescript-eslint/visitor-keys" "8.49.0" - -"@typescript-eslint/tsconfig-utils@8.49.0", "@typescript-eslint/tsconfig-utils@^8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.49.0.tgz#857777c8e35dd1e564505833d8043f544442fbf4" - integrity sha512-8prixNi1/6nawsRYxet4YOhnbW+W9FK/bQPxsGB1D3ZrDzbJ5FXw5XmzxZv82X3B+ZccuSxo/X8q9nQ+mFecWA== - -"@typescript-eslint/type-utils@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.49.0.tgz#d8118a0c1896a78a22f01d3c176e9945409b085b" - integrity sha512-KTExJfQ+svY8I10P4HdxKzWsvtVnsuCifU5MvXrRwoP2KOlNZ9ADNEWWsQTJgMxLzS5VLQKDjkCT/YzgsnqmZg== - dependencies: - "@typescript-eslint/types" "8.49.0" - "@typescript-eslint/typescript-estree" "8.49.0" - "@typescript-eslint/utils" "8.49.0" - debug "^4.3.4" - ts-api-utils "^2.1.0" - -"@typescript-eslint/types@8.49.0", "@typescript-eslint/types@^8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.49.0.tgz#c1bd3ebf956d9e5216396349ca23c58d74f06aee" - integrity sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ== - -"@typescript-eslint/typescript-estree@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.49.0.tgz#99c5a53275197ccb4e849786dad68344e9924135" - integrity sha512-jrLdRuAbPfPIdYNppHJ/D0wN+wwNfJ32YTAm10eJVsFmrVpXQnDWBn8niCSMlWjvml8jsce5E/O+86IQtTbJWA== - dependencies: - "@typescript-eslint/project-service" "8.49.0" - "@typescript-eslint/tsconfig-utils" "8.49.0" - "@typescript-eslint/types" "8.49.0" - "@typescript-eslint/visitor-keys" "8.49.0" - debug "^4.3.4" - minimatch "^9.0.4" - semver "^7.6.0" + ts-api-utils "^2.4.0" + +"@typescript-eslint/parser@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.57.1.tgz#d523e559b148264055c0a49a29d5f50c7de659c2" + integrity sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw== + dependencies: + "@typescript-eslint/scope-manager" "8.57.1" + "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/typescript-estree" "8.57.1" + "@typescript-eslint/visitor-keys" "8.57.1" + debug "^4.4.3" + +"@typescript-eslint/project-service@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.57.1.tgz#16af9fe16eedbd7085e4fdc29baa73715c0c55c5" + integrity sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg== + dependencies: + "@typescript-eslint/tsconfig-utils" "^8.57.1" + "@typescript-eslint/types" "^8.57.1" + debug "^4.4.3" + +"@typescript-eslint/scope-manager@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz#4524d7e7b420cb501807499684d435ae129aaf35" + integrity sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg== + dependencies: + "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/visitor-keys" "8.57.1" + +"@typescript-eslint/tsconfig-utils@8.57.1", "@typescript-eslint/tsconfig-utils@^8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz#9233443ec716882a6f9e240fd900a73f0235f3d7" + integrity sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg== + +"@typescript-eslint/type-utils@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz#c49af1347b5869ca85155547a8f34f84ab386fd9" + integrity sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA== + dependencies: + "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/typescript-estree" "8.57.1" + "@typescript-eslint/utils" "8.57.1" + debug "^4.4.3" + ts-api-utils "^2.4.0" + +"@typescript-eslint/types@8.57.1", "@typescript-eslint/types@^8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.57.1.tgz#54b27a8a25a7b45b4f978c3f8e00c4c78f11142c" + integrity sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ== + +"@typescript-eslint/typescript-estree@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz#a9fd28d4a0ec896aa9a9a7e0cead62ea24f99e76" + integrity sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g== + dependencies: + "@typescript-eslint/project-service" "8.57.1" + "@typescript-eslint/tsconfig-utils" "8.57.1" + "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/visitor-keys" "8.57.1" + debug "^4.4.3" + minimatch "^10.2.2" + semver "^7.7.3" tinyglobby "^0.2.15" - ts-api-utils "^2.1.0" + ts-api-utils "^2.4.0" -"@typescript-eslint/utils@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.49.0.tgz#43b3b91d30afd6f6114532cf0b228f1790f43aff" - integrity sha512-N3W7rJw7Rw+z1tRsHZbK395TWSYvufBXumYtEGzypgMUthlg0/hmCImeA8hgO2d2G4pd7ftpxxul2J8OdtdaFA== +"@typescript-eslint/utils@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.57.1.tgz#e40f5a7fcff02fd24092a7b52bd6ec029fb50465" + integrity sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ== dependencies: - "@eslint-community/eslint-utils" "^4.7.0" - "@typescript-eslint/scope-manager" "8.49.0" - "@typescript-eslint/types" "8.49.0" - "@typescript-eslint/typescript-estree" "8.49.0" + "@eslint-community/eslint-utils" "^4.9.1" + "@typescript-eslint/scope-manager" "8.57.1" + "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/typescript-estree" "8.57.1" -"@typescript-eslint/visitor-keys@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.49.0.tgz#8e450cc502c0d285cad9e84d400cf349a85ced6c" - integrity sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA== +"@typescript-eslint/visitor-keys@8.57.1": + version "8.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz#3af4f88118924d3be983d4b8ae84803f11fe4563" + integrity sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A== dependencies: - "@typescript-eslint/types" "8.49.0" - eslint-visitor-keys "^4.2.1" + "@typescript-eslint/types" "8.57.1" + eslint-visitor-keys "^5.0.0" "@umijs/route-utils@^4.0.0": version "4.0.3" @@ -2411,7 +2626,7 @@ "@whatwg-node/promise-helpers" "^1.0.0" tslib "^2.6.3" -"@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.13", "@whatwg-node/fetch@^0.10.4": +"@whatwg-node/fetch@^0.10.0", "@whatwg-node/fetch@^0.10.13": version "0.10.13" resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.10.13.tgz#2d47190c620f134dda31c7f827976a909b2cca7a" integrity sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q== @@ -2420,16 +2635,16 @@ urlpattern-polyfill "^10.0.0" "@whatwg-node/node-fetch@^0.8.3": - version "0.8.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.4.tgz#72df6b3ea9d295bcedbc8f05cf2cfeabe654669f" - integrity sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA== + version "0.8.5" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.5.tgz#5ab2226866ae10a9d0403f9d44bd684178963781" + integrity sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ== dependencies: "@fastify/busboy" "^3.1.1" "@whatwg-node/disposablestack" "^0.0.6" "@whatwg-node/promise-helpers" "^1.3.2" tslib "^2.6.3" -"@whatwg-node/promise-helpers@^1.0.0", "@whatwg-node/promise-helpers@^1.2.1", "@whatwg-node/promise-helpers@^1.2.4", "@whatwg-node/promise-helpers@^1.3.0", "@whatwg-node/promise-helpers@^1.3.2": +"@whatwg-node/promise-helpers@^1.0.0", "@whatwg-node/promise-helpers@^1.2.1", "@whatwg-node/promise-helpers@^1.2.4", "@whatwg-node/promise-helpers@^1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz#3b54987ad6517ef6db5920c66a6f0dada606587d" integrity sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA== @@ -2475,21 +2690,21 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.1.1: - version "8.3.4" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" - integrity sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g== + version "8.3.5" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.5.tgz#8a6b8ca8fc5b34685af15dabb44118663c296496" + integrity sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw== dependencies: acorn "^8.11.0" -acorn@^8.11.0, acorn@^8.15.0, acorn@^8.4.1: - version "8.15.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" - integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +acorn@^8.11.0, acorn@^8.16.0, acorn@^8.4.1: + version "8.16.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== +ajv@^6.14.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" + integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -2504,9 +2719,9 @@ ansi-escapes@^4.3.2: type-fest "^0.21.3" ansi-escapes@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.2.0.tgz#31b25afa3edd3efc09d98c2fee831d460ff06b49" - integrity sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw== + version "7.3.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz#5395bb74b2150a4a1d6e3c2565f4aeca78d28627" + integrity sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg== dependencies: environment "^1.0.0" @@ -2515,7 +2730,7 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: +ansi-regex@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== @@ -2539,63 +2754,62 @@ ansi-styles@^5.2.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.1.0, ansi-styles@^6.2.1: +ansi-styles@^6.1.0, ansi-styles@^6.2.1, ansi-styles@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== -antd@^5.0: - version "5.29.1" - resolved "https://registry.yarnpkg.com/antd/-/antd-5.29.1.tgz#e124b1aaa709a534816c42d558da02a917b995cc" - integrity sha512-TTFVbpKbyL6cPfEoKq6Ya3BIjTUr7uDW9+7Z+1oysRv1gpcN7kQ4luH8r/+rXXwz4n6BIz1iBJ1ezKCdsdNW0w== - dependencies: - "@ant-design/colors" "^7.2.1" - "@ant-design/cssinjs" "^1.23.0" - "@ant-design/cssinjs-utils" "^1.1.3" - "@ant-design/fast-color" "^2.0.6" - "@ant-design/icons" "^5.6.1" - "@ant-design/react-slick" "~1.1.2" - "@babel/runtime" "^7.26.0" - "@rc-component/color-picker" "~2.0.1" - "@rc-component/mutate-observer" "^1.1.0" - "@rc-component/qrcode" "~1.1.0" - "@rc-component/tour" "~1.15.1" - "@rc-component/trigger" "^2.3.0" - classnames "^2.5.1" - copy-to-clipboard "^3.3.3" +antd@^6.3.3: + version "6.3.3" + resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.3.tgz#72f6857640e6d6b6b36da7f75a1d1eed506a39e9" + integrity sha512-T8FAQelw36zS96cZw2U/qEjpYny5yFc7hg+1W7DvVr8xMoSXWvyB8WvmiDVH0nS0LPYV4y2sxetsJoGZt7rhhw== + dependencies: + "@ant-design/colors" "^8.0.1" + "@ant-design/cssinjs" "^2.1.2" + "@ant-design/cssinjs-utils" "^2.1.2" + "@ant-design/fast-color" "^3.0.1" + "@ant-design/icons" "^6.1.0" + "@ant-design/react-slick" "~2.0.0" + "@babel/runtime" "^7.28.4" + "@rc-component/cascader" "~1.14.0" + "@rc-component/checkbox" "~2.0.0" + "@rc-component/collapse" "~1.2.0" + "@rc-component/color-picker" "~3.1.1" + "@rc-component/dialog" "~1.8.4" + "@rc-component/drawer" "~1.4.2" + "@rc-component/dropdown" "~1.0.2" + "@rc-component/form" "~1.7.2" + "@rc-component/image" "~1.6.0" + "@rc-component/input" "~1.1.2" + "@rc-component/input-number" "~1.6.2" + "@rc-component/mentions" "~1.6.0" + "@rc-component/menu" "~1.2.0" + "@rc-component/motion" "^1.3.1" + "@rc-component/mutate-observer" "^2.0.1" + "@rc-component/notification" "~1.2.0" + "@rc-component/pagination" "~1.2.0" + "@rc-component/picker" "~1.9.1" + "@rc-component/progress" "~1.0.2" + "@rc-component/qrcode" "~1.1.1" + "@rc-component/rate" "~1.0.1" + "@rc-component/resize-observer" "^1.1.1" + "@rc-component/segmented" "~1.3.0" + "@rc-component/select" "~1.6.14" + "@rc-component/slider" "~1.0.1" + "@rc-component/steps" "~1.2.2" + "@rc-component/switch" "~1.0.3" + "@rc-component/table" "~1.9.1" + "@rc-component/tabs" "~1.7.0" + "@rc-component/textarea" "~1.1.2" + "@rc-component/tooltip" "~1.4.0" + "@rc-component/tour" "~2.3.0" + "@rc-component/tree" "~1.2.4" + "@rc-component/tree-select" "~1.8.0" + "@rc-component/trigger" "^3.9.0" + "@rc-component/upload" "~1.1.0" + "@rc-component/util" "^1.9.0" + clsx "^2.1.1" dayjs "^1.11.11" - rc-cascader "~3.34.0" - rc-checkbox "~3.5.0" - rc-collapse "~3.9.0" - rc-dialog "~9.6.0" - rc-drawer "~7.3.0" - rc-dropdown "~4.2.1" - rc-field-form "~2.7.1" - rc-image "~7.12.0" - rc-input "~1.8.0" - rc-input-number "~9.5.0" - rc-mentions "~2.20.0" - rc-menu "~9.16.1" - rc-motion "^2.9.5" - rc-notification "~5.6.4" - rc-pagination "~5.1.0" - rc-picker "~4.11.3" - rc-progress "~4.0.0" - rc-rate "~2.13.1" - rc-resize-observer "^1.4.3" - rc-segmented "~2.7.0" - rc-select "~14.16.8" - rc-slider "~11.1.9" - rc-steps "~6.0.1" - rc-switch "~4.1.0" - rc-table "~7.54.0" - rc-tabs "~15.7.0" - rc-textarea "~1.10.2" - rc-tooltip "~6.4.0" - rc-tree "~5.13.1" - rc-tree-select "~5.27.0" - rc-upload "~4.11.0" - rc-util "^5.44.4" scroll-into-view-if-needed "^3.1.0" throttle-debounce "^5.0.2" @@ -2730,7 +2944,7 @@ arraybuffer.prototype.slice@^1.0.4: get-intrinsic "^1.2.6" is-array-buffer "^3.0.4" -asap@^2.0.0, asap@~2.0.3: +asap@^2.0.0: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== @@ -2758,24 +2972,24 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.0.tgz#16f74d6482e343ff263d4f4503829e9ee91a86b6" - integrity sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ== + version "4.11.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.1.tgz#052ff9b2cbf543f5595028b583e4763b40c78ea7" + integrity sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A== axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== -babel-jest@30.2.0, babel-jest@^30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac" - integrity sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw== +babel-jest@30.3.0, babel-jest@^30.2.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.3.0.tgz#3ff5553fa3bcbb8738d2d7335a4dbdc3bd1a0eb5" + integrity sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ== dependencies: - "@jest/transform" "30.2.0" + "@jest/transform" "30.3.0" "@types/babel__core" "^7.20.5" babel-plugin-istanbul "^7.0.1" - babel-preset-jest "30.2.0" + babel-preset-jest "30.3.0" chalk "^4.1.2" graceful-fs "^4.2.11" slash "^3.0.0" @@ -2791,10 +3005,10 @@ babel-plugin-istanbul@^7.0.1: istanbul-lib-instrument "^6.0.2" test-exclude "^6.0.0" -babel-plugin-jest-hoist@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz#94c250d36b43f95900f3a219241e0f4648191ce2" - integrity sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA== +babel-plugin-jest-hoist@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz#235ad714a45c18b12566becf439e1c604e277015" + integrity sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg== dependencies: "@types/babel__core" "^7.20.5" @@ -2819,12 +3033,12 @@ babel-preset-current-node-syntax@^1.2.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-jest@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.2.0.tgz#04717843e561347781d6d7f69c81e6bcc3ed11ce" - integrity sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ== +babel-preset-jest@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz#21cf3d19a6f5e9924426c879ee0b7f092636d043" + integrity sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ== dependencies: - babel-plugin-jest-hoist "30.2.0" + babel-plugin-jest-hoist "30.3.0" babel-preset-current-node-syntax "^1.2.0" balanced-match@^1.0.0: @@ -2832,20 +3046,15 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -baseline-browser-mapping@^2.10.8: - version "2.10.8" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz#23d1cea1a85b181c2b8660b6cfe626dc2fb15630" - integrity sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ== - -baseline-browser-mapping@^2.9.0: - version "2.9.5" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.5.tgz#47f9549e0be1a84cd16651ac4c3b7d87a71408e6" - integrity sha512-D5vIoztZOq1XM54LUdttJVc96ggEsIfju2JBvht06pSzpckp3C7HReun67Bghzrtdsq9XdMGbSSB3v3GhMNmAA== +balanced-match@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" + integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +baseline-browser-mapping@^2.10.8, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19: + version "2.10.10" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz#e74bd066724c1d8d7d8ea75fc3be25389a7a5c56" + integrity sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ== brace-expansion@^1.1.7: version "1.1.12" @@ -2855,13 +3064,20 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: +brace-expansion@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.2: + version "5.0.4" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336" + integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -2942,9 +3158,9 @@ camelcase@^6.3.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: - version "1.0.30001760" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz#bdd1960fafedf8d5f04ff16e81460506ff9b798f" - integrity sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw== + version "1.0.30001780" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz#0e413de292808868a62ed9118822683fa120a110" + integrity sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ== capital-case@^1.0.4: version "1.0.4" @@ -2955,7 +3171,7 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2: +chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3017,16 +3233,16 @@ chardet@^2.1.1: integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== ci-info@^4.2.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.3.1.tgz#355ad571920810b5623e11d40232f443f16f1daa" - integrity sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA== + version "4.4.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" + integrity sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== cjs-module-lexer@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz#bff23b0609cc9afa428bd35f1918f7d03b448562" - integrity sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" + integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== -classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2, classnames@^2.5.1: +classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow== @@ -3039,12 +3255,12 @@ cli-cursor@^5.0.0: restore-cursor "^5.0.0" cli-truncate@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.1.1.tgz#455476face9904d94b7d11e98d9adbca15292ea5" - integrity sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A== + version "5.2.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.2.0.tgz#c8e72aaca8339c773d128c36e0a17c6315b694eb" + integrity sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw== dependencies: - slice-ansi "^7.1.0" - string-width "^8.0.0" + slice-ansi "^8.0.0" + string-width "^8.2.0" cli-width@^4.1.0: version "4.1.0" @@ -3065,6 +3281,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -3148,14 +3369,7 @@ convert-source-map@^2.0.0: cookie@^1.0.2: version "1.1.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" - integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== - -copy-to-clipboard@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" - integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== - dependencies: - toggle-selection "^1.0.6" + integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== cosmiconfig@^8.1.0: version "8.3.6" @@ -3168,9 +3382,9 @@ cosmiconfig@^8.1.0: path-type "^4.0.0" cosmiconfig@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" - integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + version "9.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.1.tgz#df110631a8547b5d1a98915271986f06e3011379" + integrity sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ== dependencies: env-paths "^2.2.1" import-fresh "^3.3.0" @@ -3182,13 +3396,6 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cross-fetch@^3.1.5: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.2.0.tgz#34e9192f53bc757d6614304d9e5e6fb4edb782e3" - integrity sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q== - dependencies: - node-fetch "^2.7.0" - cross-inspect@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" @@ -3206,9 +3413,9 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: which "^2.0.1" crypto-hash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-4.0.0.tgz#c58a8737560980714fa086113de3b8f577b72178" - integrity sha512-KkkFriWj2teXIJOmftIRavBL4q8A7EZV24Nn9sWhIR4hdeS8u+q2A2p0tWZ1m4xVoP1zs3VJjVwwAUnztBb8uw== + version "4.0.1" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-4.0.1.tgz#c453edb056cd73cb9312a7ac84dba88615ee2211" + integrity sha512-4u4yl+8C+75Xedf1uMA0J/TTYAqJ54XdyBc1RV3UjCkU2wQjFMsB5ScjRDh3oo21zAwSprcVEDolaGDT8z5G6g== csstype@^3.1.3, csstype@^3.2.2: version "3.2.3" @@ -3258,22 +3465,15 @@ dataloader@^2.2.3: integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: - version "1.11.19" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.19.tgz#15dc98e854bb43917f12021806af897c58ae2938" - integrity sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw== + version "1.11.20" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.20.tgz#88d919fd639dc991415da5f4cb6f1b6650811938" + integrity sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ== debounce@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5" integrity sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw== -debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0: - version "4.4.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" - integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== - dependencies: - ms "^2.1.3" - debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -3281,15 +3481,22 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== dedent@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" - integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== + version "1.7.2" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.2.tgz#34e2264ab538301e27cf7b07bf2369c19baa8dd9" + integrity sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA== deep-is@^0.1.3: version "0.1.4" @@ -3334,12 +3541,7 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== - -detect-libc@^2.1.2: +detect-libc@^2.0.3, detect-libc@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== @@ -3358,9 +3560,9 @@ dezalgo@^1.0.0: wrappy "1" diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + version "4.0.4" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" + integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== dir-glob@^3.0.1: version "3.0.1" @@ -3384,11 +3586,6 @@ dot-case@^3.0.4: no-case "^3.0.4" tslib "^2.0.3" -dset@^3.1.2: - version "3.1.4" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.4.tgz#f8eaf5f023f068a036d08cd07dc9ffb7d0065248" - integrity sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA== - dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" @@ -3404,9 +3601,9 @@ eastasianwidth@^0.2.0: integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== electron-to-chromium@^1.5.263: - version "1.5.267" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" - integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== + version "1.5.321" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz#57a80554e2e7fd65e3689d320f52a64723472d5d" + integrity sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ== emittery@^0.13.1: version "0.13.1" @@ -3428,11 +3625,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - env-paths@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -3450,10 +3642,10 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0: - version "1.24.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.0.tgz#c44732d2beb0acc1ed60df840869e3106e7af328" - integrity sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg== +es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.1: + version "1.24.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" + integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" @@ -3521,25 +3713,26 @@ es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz#d1dd0f58129054c0ad922e6a9a1e65eef435fe75" - integrity sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w== + version "1.3.1" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz#3be0f4e63438d6c5a1fb5f33b891aaad3f7dae06" + integrity sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ== dependencies: call-bind "^1.0.8" - call-bound "^1.0.3" + call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.23.6" + es-abstract "^1.24.1" es-errors "^1.3.0" - es-set-tostringtag "^2.0.3" + es-set-tostringtag "^2.1.0" function-bind "^1.1.2" - get-intrinsic "^1.2.6" + get-intrinsic "^1.3.0" globalthis "^1.0.4" gopd "^1.2.0" has-property-descriptors "^1.0.2" has-proto "^1.2.0" has-symbols "^1.1.0" internal-slot "^1.1.0" - iterator.prototype "^1.1.4" + iterator.prototype "^1.1.5" + math-intrinsics "^1.1.0" safe-array-concat "^1.1.3" es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: @@ -3549,7 +3742,7 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" -es-set-tostringtag@^2.0.3, es-set-tostringtag@^2.1.0: +es-set-tostringtag@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== @@ -3596,11 +3789,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^16.0.8: - version "16.0.8" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.0.8.tgz#0f5bb2c8c2a88479ef004258963112a31e4cc6a5" - integrity sha512-8J5cOAboXIV3f8OD6BOyj7Fik6n/as7J4MboiUSExWruf/lCu1OPR3ZVSdnta6WhzebrmAATEmNSBZsLWA6kbg== + version "16.2.1" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.1.tgz#d3b6fe5076979285a2b607c70e469dc167b43237" + integrity sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg== dependencies: - "@next/eslint-plugin-next" "16.0.8" + "@next/eslint-plugin-next" "16.2.1" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.32.0" @@ -3720,11 +3913,13 @@ eslint-plugin-react@^7.37.0: string.prototype.matchall "^4.0.12" string.prototype.repeat "^1.0.0" -eslint-scope@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" - integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== +eslint-scope@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-9.1.2.tgz#b9de6ace2fab1cff24d2e58d85b74c8fcea39802" + integrity sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ== dependencies: + "@types/esrecurse" "^4.3.1" + "@types/estree" "^1.0.8" esrecurse "^4.3.0" estraverse "^5.2.0" @@ -3733,37 +3928,34 @@ eslint-visitor-keys@^3.4.3: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" - integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== +eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" + integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== -eslint@^9.5.0: - version "9.39.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.1.tgz#be8bf7c6de77dcc4252b5a8dcb31c2efff74a6e5" - integrity sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g== +eslint@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.1.0.tgz#9ca98e654e642ab2e1af6d1e9d8613857ac341b4" + integrity sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA== dependencies: "@eslint-community/eslint-utils" "^4.8.0" - "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.1" - "@eslint/config-helpers" "^0.4.2" - "@eslint/core" "^0.17.0" - "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.39.1" - "@eslint/plugin-kit" "^0.4.1" + "@eslint-community/regexpp" "^4.12.2" + "@eslint/config-array" "^0.23.3" + "@eslint/config-helpers" "^0.5.3" + "@eslint/core" "^1.1.1" + "@eslint/plugin-kit" "^0.6.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" "@types/estree" "^1.0.6" - ajv "^6.12.4" - chalk "^4.0.0" + ajv "^6.14.0" cross-spawn "^7.0.6" debug "^4.3.2" escape-string-regexp "^4.0.0" - eslint-scope "^8.4.0" - eslint-visitor-keys "^4.2.1" - espree "^10.4.0" - esquery "^1.5.0" + eslint-scope "^9.1.2" + eslint-visitor-keys "^5.0.1" + espree "^11.2.0" + esquery "^1.7.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^8.0.0" @@ -3773,29 +3965,28 @@ eslint@^9.5.0: imurmurhash "^0.1.4" is-glob "^4.0.0" json-stable-stringify-without-jsonify "^1.0.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" + minimatch "^10.2.4" natural-compare "^1.4.0" optionator "^0.9.3" -espree@^10.0.1, espree@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" - integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== +espree@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-11.2.0.tgz#01d5e47dc332aaba3059008362454a8cc34ccaa5" + integrity sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw== dependencies: - acorn "^8.15.0" + acorn "^8.16.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.1" + eslint-visitor-keys "^5.0.1" esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== +esquery@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" + integrity sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g== dependencies: estraverse "^5.1.0" @@ -3817,9 +4008,9 @@ esutils@^2.0.2: integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + version "5.0.4" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" + integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== execa@^5.1.1: version "5.1.1" @@ -3841,17 +4032,17 @@ exit-x@^0.2.2: resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== -expect@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-30.2.0.tgz#d4013bed267013c14bc1199cec8aa57cee9b5869" - integrity sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw== +expect@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-30.3.0.tgz#1b82111517d1ab030f3db0cf1b4061c8aa644f61" + integrity sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q== dependencies: - "@jest/expect-utils" "30.2.0" + "@jest/expect-utils" "30.3.0" "@jest/get-type" "30.1.0" - jest-matcher-utils "30.2.0" - jest-message-util "30.2.0" - jest-mock "30.2.0" - jest-util "30.2.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-mock "30.3.0" + jest-util "30.3.0" fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -3891,37 +4082,19 @@ fast-levenshtein@^2.0.6: integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== fastq@^1.6.0: - version "1.19.1" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.19.1.tgz#d50eaba803c8846a883c16492821ebcd2cda55f5" - integrity sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ== + version "1.20.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== dependencies: reusify "^1.0.4" -fb-watchman@^2.0.0, fb-watchman@^2.0.2: +fb-watchman@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== dependencies: bser "2.1.1" -fbjs-css-vars@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" - integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ== - -fbjs@^3.0.0: - version "3.0.5" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" - integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== - dependencies: - cross-fetch "^3.1.5" - fbjs-css-vars "^1.0.0" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^1.0.35" - fdir@^6.5.0: version "6.5.0" resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" @@ -3974,9 +4147,9 @@ flat-cache@^4.0.0: keyv "^4.5.4" flatted@^3.2.9: - version "3.3.3" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" - integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + version "3.4.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" + integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== for-each@^0.3.3, for-each@^0.3.5: version "0.3.5" @@ -4047,10 +4220,10 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.0, get-east-asian-width@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz#9bc4caa131702b4b61729cb7e42735bc550c9ee6" - integrity sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q== +get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.1, get-east-asian-width@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" + integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" @@ -4096,9 +4269,9 @@ get-symbol-description@^1.1.0: get-intrinsic "^1.2.6" get-tsconfig@^4.10.0: - version "4.13.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.0.tgz#fcdd991e6d22ab9a600f00e91c318707a5d9a0d7" - integrity sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ== + version "4.13.7" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz#b9d8b199b06033ceeea1a93df7ea5765415089bc" + integrity sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q== dependencies: resolve-pkg-maps "^1.0.0" @@ -4116,7 +4289,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.3.10: +glob@^10.5.0: version "10.5.0" resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== @@ -4145,11 +4318,6 @@ globals@16.4.0: resolved "https://registry.yarnpkg.com/globals/-/globals-16.4.0.tgz#574bc7e72993d40cf27cf6c241f324ee77808e51" integrity sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw== -globals@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" - integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== - globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -4180,20 +4348,20 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.11: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphql-config@^5.1.1: - version "5.1.5" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-5.1.5.tgz#34e0bfba88e74b6eefd889716a9378086f595f7f" - integrity sha512-mG2LL1HccpU8qg5ajLROgdsBzx/o2M6kgI3uAmoaXiSH9PCUbtIyLomLqUtCFaAeG2YCFsl0M5cfQ9rKmDoMVA== +graphql-config@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-5.1.6.tgz#bea614428ec0c5c9a6267b8a19d17e09018f8a4f" + integrity sha512-fCkYnm4Kdq3un0YIM4BCZHVR5xl0UeLP6syxxO7KAstdY7QVyVvTHP0kRPDYEP1v08uwtJVgis5sj3IOTLOniQ== dependencies: "@graphql-tools/graphql-file-loader" "^8.0.0" "@graphql-tools/json-file-loader" "^8.0.0" "@graphql-tools/load" "^8.1.0" "@graphql-tools/merge" "^9.0.0" - "@graphql-tools/url-loader" "^8.0.0" - "@graphql-tools/utils" "^10.0.0" + "@graphql-tools/url-loader" "^9.0.0" + "@graphql-tools/utils" "^11.0.0" cosmiconfig "^8.1.0" jiti "^2.0.0" - minimatch "^9.0.5" + minimatch "^10.0.0" string-env-interpolation "^1.0.1" tslib "^2.4.0" @@ -4205,14 +4373,14 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: tslib "^2.1.0" graphql-ws@^6.0.6: - version "6.0.6" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.6.tgz#e9c9ff85f4ddb5bbe6faa2721c0d27e6c11746d2" - integrity sha512-zgfER9s+ftkGKUZgc0xbx8T7/HMO4AV5/YuYiFc+AtgcO5T0v8AxYYNQ+ltzuzDZgNkYJaFspm5MMYLjQzrkmw== + version "6.0.7" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.7.tgz#140307a513f0ba659ecec618d6cf15354a842f96" + integrity sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg== graphql@^16.12.0: - version "16.12.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.12.0.tgz#28cc2462435b1ac3fdc6976d030cef83a0c13ac7" - integrity sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ== + version "16.13.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.13.1.tgz#38ae5c76fbc4a009e0004dca6c76c370a1da7b54" + integrity sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ== has-bigints@^1.0.2: version "1.1.0" @@ -4310,9 +4478,9 @@ human-signals@^2.1.0: integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== iconv-lite@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.0.tgz#c50cd80e6746ca8115eb98743afa81aa0e147a3e" - integrity sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ== + version "0.7.2" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" + integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" @@ -4321,17 +4489,17 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.0: +ignore@^7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9" integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== -immutable@~3.7.6: - version "3.7.6" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" - integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== +immutable@^5.1.5: + version "5.1.5" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.5.tgz#93ee4db5c2a9ab42a4a783069f3c5d8847d40165" + integrity sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A== -import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.3.0: version "3.3.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== @@ -4487,7 +4655,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-fullwidth-code-point@^5.0.0: +is-fullwidth-code-point@^5.0.0, is-fullwidth-code-point@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" integrity sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ== @@ -4529,6 +4697,11 @@ is-map@^2.0.3: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e" integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw== +is-mobile@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-5.0.0.tgz#1e08a0ef2c38a67bff84a52af68d67bcef445333" + integrity sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ== + is-negative-zero@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" @@ -4711,7 +4884,7 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -iterator.prototype@^1.1.4: +iterator.prototype@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" integrity sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g== @@ -4737,96 +4910,95 @@ javascript-stringify@^2.0.1: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.2.0.tgz#602266e478ed554e1e1469944faa7efd37cee61c" - integrity sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ== +jest-changed-files@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.3.0.tgz#055849df695f9a9fcde0ae44024f815bbc627f3a" + integrity sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA== dependencies: execa "^5.1.1" - jest-util "30.2.0" + jest-util "30.3.0" p-limit "^3.1.0" -jest-circus@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.2.0.tgz#98b8198b958748a2f322354311023d1d02e7603f" - integrity sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg== +jest-circus@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.3.0.tgz#153614c11ab35867f371bd93496ecb9690b92077" + integrity sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA== dependencies: - "@jest/environment" "30.2.0" - "@jest/expect" "30.2.0" - "@jest/test-result" "30.2.0" - "@jest/types" "30.2.0" + "@jest/environment" "30.3.0" + "@jest/expect" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" co "^4.6.0" dedent "^1.6.0" is-generator-fn "^2.1.0" - jest-each "30.2.0" - jest-matcher-utils "30.2.0" - jest-message-util "30.2.0" - jest-runtime "30.2.0" - jest-snapshot "30.2.0" - jest-util "30.2.0" + jest-each "30.3.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-runtime "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" p-limit "^3.1.0" - pretty-format "30.2.0" + pretty-format "30.3.0" pure-rand "^7.0.0" slash "^3.0.0" stack-utils "^2.0.6" -jest-cli@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.2.0.tgz#1780f8e9d66bf84a10b369aea60aeda7697dcc67" - integrity sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA== +jest-cli@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.3.0.tgz#5ed75a337f486a1f1c5acbb2de8acddb106ead6c" + integrity sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw== dependencies: - "@jest/core" "30.2.0" - "@jest/test-result" "30.2.0" - "@jest/types" "30.2.0" + "@jest/core" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" chalk "^4.1.2" exit-x "^0.2.2" import-local "^3.2.0" - jest-config "30.2.0" - jest-util "30.2.0" - jest-validate "30.2.0" + jest-config "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" yargs "^17.7.2" -jest-config@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.2.0.tgz#29df8c50e2ad801cc59c406b50176c18c362a90b" - integrity sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA== +jest-config@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.3.0.tgz#b969e0aaaf5964419e62953bb712c16d15972425" + integrity sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w== dependencies: "@babel/core" "^7.27.4" "@jest/get-type" "30.1.0" "@jest/pattern" "30.0.1" - "@jest/test-sequencer" "30.2.0" - "@jest/types" "30.2.0" - babel-jest "30.2.0" + "@jest/test-sequencer" "30.3.0" + "@jest/types" "30.3.0" + babel-jest "30.3.0" chalk "^4.1.2" ci-info "^4.2.0" deepmerge "^4.3.1" - glob "^10.3.10" + glob "^10.5.0" graceful-fs "^4.2.11" - jest-circus "30.2.0" + jest-circus "30.3.0" jest-docblock "30.2.0" - jest-environment-node "30.2.0" + jest-environment-node "30.3.0" jest-regex-util "30.0.1" - jest-resolve "30.2.0" - jest-runner "30.2.0" - jest-util "30.2.0" - jest-validate "30.2.0" - micromatch "^4.0.8" + jest-resolve "30.3.0" + jest-runner "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" parse-json "^5.2.0" - pretty-format "30.2.0" + pretty-format "30.3.0" slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.2.0.tgz#e3ec3a6ea5c5747f605c9e874f83d756cba36825" - integrity sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A== +jest-diff@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.3.0.tgz#e0a4c84ef350ffd790ffd5b0016acabeecf5f759" + integrity sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ== dependencies: - "@jest/diff-sequences" "30.0.1" + "@jest/diff-sequences" "30.3.0" "@jest/get-type" "30.1.0" chalk "^4.1.2" - pretty-format "30.2.0" + pretty-format "30.3.0" jest-docblock@30.2.0: version "30.2.0" @@ -4835,89 +5007,89 @@ jest-docblock@30.2.0: dependencies: detect-newline "^3.1.0" -jest-each@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.2.0.tgz#39e623ae71641c2ac3ee69b3ba3d258fce8e768d" - integrity sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ== +jest-each@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.3.0.tgz#faa7229bf7a9fa6426dc604057a7d2a173493b1e" + integrity sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA== dependencies: "@jest/get-type" "30.1.0" - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" chalk "^4.1.2" - jest-util "30.2.0" - pretty-format "30.2.0" + jest-util "30.3.0" + pretty-format "30.3.0" -jest-environment-node@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.2.0.tgz#3def7980ebd2fd86e74efd4d2e681f55ab38da0f" - integrity sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA== +jest-environment-node@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.3.0.tgz#aa8a57c5d0c4af0f8b1f7403ba737fec6b3aabbe" + integrity sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ== dependencies: - "@jest/environment" "30.2.0" - "@jest/fake-timers" "30.2.0" - "@jest/types" "30.2.0" + "@jest/environment" "30.3.0" + "@jest/fake-timers" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" - jest-mock "30.2.0" - jest-util "30.2.0" - jest-validate "30.2.0" + jest-mock "30.3.0" + jest-util "30.3.0" + jest-validate "30.3.0" -jest-haste-map@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.2.0.tgz#808e3889f288603ac70ff0ac047598345a66022e" - integrity sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw== +jest-haste-map@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.3.0.tgz#1ea6843e6e45c077d91270666a4fcba958c24cd5" + integrity sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/node" "*" anymatch "^3.1.3" fb-watchman "^2.0.2" graceful-fs "^4.2.11" jest-regex-util "30.0.1" - jest-util "30.2.0" - jest-worker "30.2.0" - micromatch "^4.0.8" + jest-util "30.3.0" + jest-worker "30.3.0" + picomatch "^4.0.3" walker "^1.0.8" optionalDependencies: fsevents "^2.3.3" -jest-leak-detector@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz#292fdca7b7c9cf594e1e570ace140b01d8beb736" - integrity sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ== +jest-leak-detector@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz#a695a851e353f517a554a2f5c91c2742fc131c98" + integrity sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ== dependencies: "@jest/get-type" "30.1.0" - pretty-format "30.2.0" + pretty-format "30.3.0" -jest-matcher-utils@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz#69a0d4c271066559ec8b0d8174829adc3f23a783" - integrity sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg== +jest-matcher-utils@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz#d6c739fec1ecd33809f2d2b1348f6ab01d2f2493" + integrity sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA== dependencies: "@jest/get-type" "30.1.0" chalk "^4.1.2" - jest-diff "30.2.0" - pretty-format "30.2.0" + jest-diff "30.3.0" + pretty-format "30.3.0" -jest-message-util@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.2.0.tgz#fc97bf90d11f118b31e6131e2b67fc4f39f92152" - integrity sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw== +jest-message-util@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.3.0.tgz#4d723544d36890ba862ac3961db52db5b0d1ba39" + integrity sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw== dependencies: "@babel/code-frame" "^7.27.1" - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/stack-utils" "^2.0.3" chalk "^4.1.2" graceful-fs "^4.2.11" - micromatch "^4.0.8" - pretty-format "30.2.0" + picomatch "^4.0.3" + pretty-format "30.3.0" slash "^3.0.0" stack-utils "^2.0.6" -jest-mock@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.2.0.tgz#69f991614eeb4060189459d3584f710845bff45e" - integrity sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw== +jest-mock@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.3.0.tgz#e0fa4184a596a6c4fdec53d4f412158418923747" + integrity sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/node" "*" - jest-util "30.2.0" + jest-util "30.3.0" jest-pnp-resolver@^1.2.3: version "1.2.3" @@ -4929,169 +5101,169 @@ jest-regex-util@30.0.1: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== -jest-resolve-dependencies@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.2.0.tgz#3370e2c0b49cc560f6a7e8ec3a59dd99525e1a55" - integrity sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w== +jest-resolve-dependencies@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz#4d638c9f0d93a62a6ed25dec874bfd7e756c8ce5" + integrity sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw== dependencies: jest-regex-util "30.0.1" - jest-snapshot "30.2.0" + jest-snapshot "30.3.0" -jest-resolve@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.2.0.tgz#2e2009cbd61e8f1f003355d5ec87225412cebcd7" - integrity sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A== +jest-resolve@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.3.0.tgz#b7bee9927279805b1b50715d2170a545553b87ff" + integrity sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g== dependencies: chalk "^4.1.2" graceful-fs "^4.2.11" - jest-haste-map "30.2.0" + jest-haste-map "30.3.0" jest-pnp-resolver "^1.2.3" - jest-util "30.2.0" - jest-validate "30.2.0" + jest-util "30.3.0" + jest-validate "30.3.0" slash "^3.0.0" unrs-resolver "^1.7.11" -jest-runner@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.2.0.tgz#c62b4c3130afa661789705e13a07bdbcec26a114" - integrity sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ== - dependencies: - "@jest/console" "30.2.0" - "@jest/environment" "30.2.0" - "@jest/test-result" "30.2.0" - "@jest/transform" "30.2.0" - "@jest/types" "30.2.0" +jest-runner@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.3.0.tgz#fa970fc4e45d418ad7e7d581b24cac7af5944cb7" + integrity sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw== + dependencies: + "@jest/console" "30.3.0" + "@jest/environment" "30.3.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" emittery "^0.13.1" exit-x "^0.2.2" graceful-fs "^4.2.11" jest-docblock "30.2.0" - jest-environment-node "30.2.0" - jest-haste-map "30.2.0" - jest-leak-detector "30.2.0" - jest-message-util "30.2.0" - jest-resolve "30.2.0" - jest-runtime "30.2.0" - jest-util "30.2.0" - jest-watcher "30.2.0" - jest-worker "30.2.0" + jest-environment-node "30.3.0" + jest-haste-map "30.3.0" + jest-leak-detector "30.3.0" + jest-message-util "30.3.0" + jest-resolve "30.3.0" + jest-runtime "30.3.0" + jest-util "30.3.0" + jest-watcher "30.3.0" + jest-worker "30.3.0" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.2.0.tgz#395ea792cde048db1b0cd1a92dc9cb9f1921bf8a" - integrity sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg== +jest-runtime@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.3.0.tgz#1a9bec7a9b68db12dfe4136bbe41ab883ea2c996" + integrity sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng== dependencies: - "@jest/environment" "30.2.0" - "@jest/fake-timers" "30.2.0" - "@jest/globals" "30.2.0" + "@jest/environment" "30.3.0" + "@jest/fake-timers" "30.3.0" + "@jest/globals" "30.3.0" "@jest/source-map" "30.0.1" - "@jest/test-result" "30.2.0" - "@jest/transform" "30.2.0" - "@jest/types" "30.2.0" + "@jest/test-result" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" cjs-module-lexer "^2.1.0" collect-v8-coverage "^1.0.2" - glob "^10.3.10" + glob "^10.5.0" graceful-fs "^4.2.11" - jest-haste-map "30.2.0" - jest-message-util "30.2.0" - jest-mock "30.2.0" + jest-haste-map "30.3.0" + jest-message-util "30.3.0" + jest-mock "30.3.0" jest-regex-util "30.0.1" - jest-resolve "30.2.0" - jest-snapshot "30.2.0" - jest-util "30.2.0" + jest-resolve "30.3.0" + jest-snapshot "30.3.0" + jest-util "30.3.0" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.2.0.tgz#266fbbb4b95fc4665ce6f32f1f38eeb39f4e26d0" - integrity sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA== +jest-snapshot@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.3.0.tgz#6e7ea75069dda86e36311a0f73189e830d4f51ad" + integrity sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ== dependencies: "@babel/core" "^7.27.4" "@babel/generator" "^7.27.5" "@babel/plugin-syntax-jsx" "^7.27.1" "@babel/plugin-syntax-typescript" "^7.27.1" "@babel/types" "^7.27.3" - "@jest/expect-utils" "30.2.0" + "@jest/expect-utils" "30.3.0" "@jest/get-type" "30.1.0" - "@jest/snapshot-utils" "30.2.0" - "@jest/transform" "30.2.0" - "@jest/types" "30.2.0" + "@jest/snapshot-utils" "30.3.0" + "@jest/transform" "30.3.0" + "@jest/types" "30.3.0" babel-preset-current-node-syntax "^1.2.0" chalk "^4.1.2" - expect "30.2.0" + expect "30.3.0" graceful-fs "^4.2.11" - jest-diff "30.2.0" - jest-matcher-utils "30.2.0" - jest-message-util "30.2.0" - jest-util "30.2.0" - pretty-format "30.2.0" + jest-diff "30.3.0" + jest-matcher-utils "30.3.0" + jest-message-util "30.3.0" + jest-util "30.3.0" + pretty-format "30.3.0" semver "^7.7.2" synckit "^0.11.8" -jest-util@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.2.0.tgz#5142adbcad6f4e53c2776c067a4db3c14f913705" - integrity sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA== +jest-util@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.3.0.tgz#95a4fbacf2dac20e768e2f1744b70519f2ba7980" + integrity sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg== dependencies: - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" "@types/node" "*" chalk "^4.1.2" ci-info "^4.2.0" graceful-fs "^4.2.11" - picomatch "^4.0.2" + picomatch "^4.0.3" -jest-validate@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.2.0.tgz#273eaaed4c0963b934b5b31e96289edda6e0a2ef" - integrity sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw== +jest-validate@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.3.0.tgz#215e11b8fcc5e2ca4b99ea5d730a5b4c969e4355" + integrity sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q== dependencies: "@jest/get-type" "30.1.0" - "@jest/types" "30.2.0" + "@jest/types" "30.3.0" camelcase "^6.3.0" chalk "^4.1.2" leven "^3.1.0" - pretty-format "30.2.0" + pretty-format "30.3.0" -jest-watcher@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.2.0.tgz#f9c055de48e18c979e7756a3917e596e2d69b07b" - integrity sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg== +jest-watcher@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.3.0.tgz#3afa1af355b9fe80f0261eb8a23981a315858596" + integrity sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w== dependencies: - "@jest/test-result" "30.2.0" - "@jest/types" "30.2.0" + "@jest/test-result" "30.3.0" + "@jest/types" "30.3.0" "@types/node" "*" ansi-escapes "^4.3.2" chalk "^4.1.2" emittery "^0.13.1" - jest-util "30.2.0" + jest-util "30.3.0" string-length "^4.0.2" -jest-worker@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.2.0.tgz#fd5c2a36ff6058ec8f74366ec89538cc99539d26" - integrity sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g== +jest-worker@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.3.0.tgz#ae4dc1f1d93d0cba1415624fcedaec40ea764f14" + integrity sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ== dependencies: "@types/node" "*" "@ungap/structured-clone" "^1.3.0" - jest-util "30.2.0" + jest-util "30.3.0" merge-stream "^2.0.0" supports-color "^8.1.1" jest@^30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-30.2.0.tgz#9f0a71e734af968f26952b5ae4b724af82681630" - integrity sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A== + version "30.3.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-30.3.0.tgz#6460b889dd805e9677400505f16f1d9b14c285a3" + integrity sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg== dependencies: - "@jest/core" "30.2.0" - "@jest/types" "30.2.0" + "@jest/core" "30.3.0" + "@jest/types" "30.3.0" import-local "^3.2.0" - jest-cli "30.2.0" + jest-cli "30.3.0" jiti@^2.0.0, jiti@^2.3.0: version "2.6.1" @@ -5111,7 +5283,7 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.1.0, js-yaml@^4.1.1: +js-yaml@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== @@ -5165,7 +5337,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.3: +json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -5245,15 +5417,6 @@ listr2@^9.0.0: rfdc "^1.4.1" wrap-ansi "^9.0.0" -loader-utils@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" - integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -5269,14 +5432,9 @@ locate-path@^6.0.0: p-locate "^5.0.0" lodash-es@^4.17.21: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" - integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.23.tgz#58c4360fd1b5d33afc6c0bbd3d1149349b1138e0" + integrity sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== lodash.sortby@^4.7.0: version "4.7.0" @@ -5284,9 +5442,9 @@ lodash.sortby@^4.7.0: integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== lodash@^4.17.21, lodash@~4.17.0: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.0.0: version "4.1.0" @@ -5384,7 +5542,7 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meros@^1.2.1, meros@^1.3.2: +meros@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.2.tgz#4cb0d7f3d22074c6e7ae1d66f72c080bca2e414b" integrity sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A== @@ -5407,19 +5565,26 @@ mimic-function@^5.0.0: resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== +minimatch@^10.0.0, minimatch@^10.2.2, minimatch@^10.2.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" + integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== + dependencies: + brace-expansion "^5.0.2" + minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.4, minimatch@^9.0.5: - version "9.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" - integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== +minimatch@^9.0.4: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== dependencies: - brace-expansion "^2.0.1" + brace-expansion "^2.0.2" minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" @@ -5427,9 +5592,9 @@ minimist@^1.2.0, minimist@^1.2.6: integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" - integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== mkdirp@^0.5.1: version "0.5.6" @@ -5464,25 +5629,26 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^16.0.8: - version "16.0.8" - resolved "https://registry.yarnpkg.com/next/-/next-16.0.8.tgz#10c0289e76e8617a8ad991210ec7402cb9cba87b" - integrity sha512-LmcZzG04JuzNXi48s5P+TnJBsTGPJunViNKV/iE4uM6kstjTQsQhvsAv+xF6MJxU2Pr26tl15eVbp0jQnsv6/g== + version "16.2.1" + resolved "https://registry.yarnpkg.com/next/-/next-16.2.1.tgz#8e3ee1051f900e2a52e5978fc1cc3bbd7fe76cad" + integrity sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q== dependencies: - "@next/env" "16.0.8" + "@next/env" "16.2.1" "@swc/helpers" "0.5.15" + baseline-browser-mapping "^2.9.19" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.0.8" - "@next/swc-darwin-x64" "16.0.8" - "@next/swc-linux-arm64-gnu" "16.0.8" - "@next/swc-linux-arm64-musl" "16.0.8" - "@next/swc-linux-x64-gnu" "16.0.8" - "@next/swc-linux-x64-musl" "16.0.8" - "@next/swc-win32-arm64-msvc" "16.0.8" - "@next/swc-win32-x64-msvc" "16.0.8" - sharp "^0.34.4" + "@next/swc-darwin-arm64" "16.2.1" + "@next/swc-darwin-x64" "16.2.1" + "@next/swc-linux-arm64-gnu" "16.2.1" + "@next/swc-linux-arm64-musl" "16.2.1" + "@next/swc-linux-x64-gnu" "16.2.1" + "@next/swc-linux-x64-musl" "16.2.1" + "@next/swc-win32-arm64-msvc" "16.2.1" + "@next/swc-win32-x64-msvc" "16.2.1" + sharp "^0.34.5" no-case@^3.0.4: version "3.0.4" @@ -5502,12 +5668,15 @@ node-domexception@^1.0.0: resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== -node-fetch@^2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== +node-exports-info@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/node-exports-info/-/node-exports-info-1.6.0.tgz#1aedafb01a966059c9a5e791a94a94d93f5c2a13" + integrity sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw== dependencies: - whatwg-url "^5.0.0" + array.prototype.flatmap "^1.3.3" + es-errors "^1.3.0" + object.entries "^1.1.9" + semver "^6.3.1" node-fetch@^3.3.2: version "3.3.2" @@ -5524,9 +5693,9 @@ node-int64@^0.4.0: integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== node-releases@^2.0.27: - version "2.0.27" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" - integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + version "2.0.36" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.36.tgz#99fd6552aaeda9e17c4713b57a63964a2e325e9d" + integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA== nopt@^4.0.1: version "4.0.3" @@ -5570,12 +5739,7 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -nullthrows@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" - integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== - -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -5859,7 +6023,7 @@ picomatch@^2.0.4, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -picomatch@^4.0.2, picomatch@^4.0.3: +picomatch@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== @@ -5896,26 +6060,19 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.7.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.7.4.tgz#d2f8335d4b1cec47e1c8098645411b0c9dff9c0f" - integrity sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA== + version "3.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== -pretty-format@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.2.0.tgz#2d44fe6134529aed18506f6d11509d8a62775ebe" - integrity sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA== +pretty-format@30.3.0: + version "30.3.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.3.0.tgz#e977eed4bcd1b6195faed418af8eac68b9ea1f29" + integrity sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ== dependencies: "@jest/schemas" "30.0.5" ansi-styles "^5.2.0" react-is "^18.3.1" -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== - dependencies: - asap "~2.0.3" - prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -5940,203 +6097,7 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -rc-cascader@~3.34.0: - version "3.34.0" - resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.34.0.tgz#56f936ab6b1229bab7d558701ce9b9e96536582c" - integrity sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag== - dependencies: - "@babel/runtime" "^7.25.7" - classnames "^2.3.1" - rc-select "~14.16.2" - rc-tree "~5.13.0" - rc-util "^5.43.0" - -rc-checkbox@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz#3ae2441e3a321774d390f76539e706864fcf5ff0" - integrity sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.25.2" - -rc-collapse@~3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.9.0.tgz#972404ce7724e1c9d1d2476543e1175404a36806" - integrity sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.3.4" - rc-util "^5.27.0" - -rc-dialog@~9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-9.6.0.tgz#dc7a255c6ad1cb56021c3a61c7de86ee88c7c371" - integrity sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/portal" "^1.0.0-8" - classnames "^2.2.6" - rc-motion "^2.3.0" - rc-util "^5.21.0" - -rc-drawer@~7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-7.3.0.tgz#1bb5fe5f9da38b6a2b2a7dffc9fcb647252a328f" - integrity sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg== - dependencies: - "@babel/runtime" "^7.23.9" - "@rc-component/portal" "^1.1.1" - classnames "^2.2.6" - rc-motion "^2.6.1" - rc-util "^5.38.1" - -rc-dropdown@~4.2.0, rc-dropdown@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz#44729eb2a4272e0353d31ac060da21e606accb1c" - integrity sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA== - dependencies: - "@babel/runtime" "^7.18.3" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-util "^5.44.1" - -rc-field-form@~2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-2.7.1.tgz#8bb1d7c6bf40e2b99b494816972252c327d9b5a0" - integrity sha512-vKeSifSJ6HoLaAB+B8aq/Qgm8a3dyxROzCtKNCsBQgiverpc4kWDQihoUwzUj+zNWJOykwSY4dNX3QrGwtVb9A== - dependencies: - "@babel/runtime" "^7.18.0" - "@rc-component/async-validator" "^5.0.3" - rc-util "^5.32.2" - -rc-image@~7.12.0: - version "7.12.0" - resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-7.12.0.tgz#95e9314701e668217d113c1f29b4f01ac025cafe" - integrity sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/portal" "^1.0.2" - classnames "^2.2.6" - rc-dialog "~9.6.0" - rc-motion "^2.6.2" - rc-util "^5.34.1" - -rc-input-number@~9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-9.5.0.tgz#b47963d0f2cbd85ab2f1badfdc089a904c073f38" - integrity sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/mini-decimal" "^1.0.1" - classnames "^2.2.5" - rc-input "~1.8.0" - rc-util "^5.40.1" - -rc-input@~1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-1.8.0.tgz#d2f4404befebf2fbdc28390d5494c302f74ae974" - integrity sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.18.1" - -rc-mentions@~2.20.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-2.20.0.tgz#3bbeac0352b02e0ce3e1244adb48701bb6903bf7" - integrity sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ== - dependencies: - "@babel/runtime" "^7.22.5" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.6" - rc-input "~1.8.0" - rc-menu "~9.16.0" - rc-textarea "~1.10.0" - rc-util "^5.34.1" - -rc-menu@~9.16.0, rc-menu@~9.16.1: - version "9.16.1" - resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.16.1.tgz#9df1168e41d87dc7164c582173e1a1d32011899f" - integrity sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.0.0" - classnames "2.x" - rc-motion "^2.4.3" - rc-overflow "^1.3.1" - rc-util "^5.27.0" - -rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1, rc-motion@^2.6.2, rc-motion@^2.9.0, rc-motion@^2.9.5: - version "2.9.5" - resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.9.5.tgz#12c6ead4fd355f94f00de9bb4f15df576d677e0c" - integrity sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-util "^5.44.0" - -rc-notification@~5.6.4: - version "5.6.4" - resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-5.6.4.tgz#ea89c39c13cd517fdfd97fe63f03376fabb78544" - integrity sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.9.0" - rc-util "^5.20.1" - -rc-overflow@^1.3.1, rc-overflow@^1.3.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.5.0.tgz#02e58a15199e392adfcc87e0d6e9e7c8e57f2771" - integrity sha512-Lm/v9h0LymeUYJf0x39OveU52InkdRXqnn2aYXfWmo8WdOonIKB2kfau+GF0fWq6jPgtdO9yMqveGcK6aIhJmg== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-resize-observer "^1.0.0" - rc-util "^5.37.0" - -rc-pagination@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-5.1.0.tgz#a6e63a2c5db29e62f991282eb18a2d3ee725ba8b" - integrity sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.3.2" - rc-util "^5.38.0" - -rc-picker@~4.11.3: - version "4.11.3" - resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-4.11.3.tgz#7e7e3ad83aa461c284b8391c697492d1c34d2cb8" - integrity sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg== - dependencies: - "@babel/runtime" "^7.24.7" - "@rc-component/trigger" "^2.0.0" - classnames "^2.2.1" - rc-overflow "^1.3.2" - rc-resize-observer "^1.4.0" - rc-util "^5.43.0" - -rc-progress@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-4.0.0.tgz#5382147d9add33d3a5fbd264001373df6440e126" - integrity sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.6" - rc-util "^5.16.1" - -rc-rate@~2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.13.1.tgz#29af7a3d4768362e9d4388f955a8b6389526b7fd" - integrity sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.0.1" - -rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-resize-observer@^1.4.0, rc-resize-observer@^1.4.3: +rc-resize-observer@^1.1.0: version "1.4.3" resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz#4fd41fa561ba51362b5155a07c35d7c89a1ea569" integrity sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ== @@ -6146,134 +6107,7 @@ rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.3.1, rc-util "^5.44.1" resize-observer-polyfill "^1.5.1" -rc-segmented@~2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.7.0.tgz#f56c2044abf8f03958b3a9a9d32987f10dcc4fc4" - integrity sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA== - dependencies: - "@babel/runtime" "^7.11.1" - classnames "^2.2.1" - rc-motion "^2.4.4" - rc-util "^5.17.0" - -rc-select@~14.16.2, rc-select@~14.16.8: - version "14.16.8" - resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.16.8.tgz#78e6782f1ccc1f03d9003bc3effa4ed609d29a97" - integrity sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/trigger" "^2.1.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-overflow "^1.3.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.2" - -rc-slider@~11.1.9: - version "11.1.9" - resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-11.1.9.tgz#d872130fbf4ec51f28543d62e90451091d6f5208" - integrity sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.5" - rc-util "^5.36.0" - -rc-steps@~6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-6.0.1.tgz#c2136cd0087733f6d509209a84a5c80dc29a274d" - integrity sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g== - dependencies: - "@babel/runtime" "^7.16.7" - classnames "^2.2.3" - rc-util "^5.16.1" - -rc-switch@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-4.1.0.tgz#f37d81b4e0c5afd1274fd85367b17306bf25e7d7" - integrity sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg== - dependencies: - "@babel/runtime" "^7.21.0" - classnames "^2.2.1" - rc-util "^5.30.0" - -rc-table@~7.54.0: - version "7.54.0" - resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.54.0.tgz#dedd4ea18d1189f2acdf90a80f04d8ca0111e16a" - integrity sha512-/wDTkki6wBTjwylwAGjpLKYklKo9YgjZwAU77+7ME5mBoS32Q4nAwoqhA2lSge6fobLW3Tap6uc5xfwaL2p0Sw== - dependencies: - "@babel/runtime" "^7.10.1" - "@rc-component/context" "^1.4.0" - classnames "^2.2.5" - rc-resize-observer "^1.1.0" - rc-util "^5.44.3" - rc-virtual-list "^3.14.2" - -rc-tabs@~15.7.0: - version "15.7.0" - resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-15.7.0.tgz#14ca2ee6213d00491a8b67ae26e2d35c256bf19a" - integrity sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA== - dependencies: - "@babel/runtime" "^7.11.2" - classnames "2.x" - rc-dropdown "~4.2.0" - rc-menu "~9.16.0" - rc-motion "^2.6.2" - rc-resize-observer "^1.0.0" - rc-util "^5.34.1" - -rc-textarea@~1.10.0, rc-textarea@~1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-1.10.2.tgz#459e3574a95c32939c6793045a1e4db04cb514cc" - integrity sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "^2.2.1" - rc-input "~1.8.0" - rc-resize-observer "^1.0.0" - rc-util "^5.27.0" - -rc-tooltip@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz#e832ed0392872025e59928cfc1ad9045656467fd" - integrity sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g== - dependencies: - "@babel/runtime" "^7.11.2" - "@rc-component/trigger" "^2.0.0" - classnames "^2.3.1" - rc-util "^5.44.3" - -rc-tree-select@~5.27.0: - version "5.27.0" - resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz#3daa62972ae80846dac96bf4776d1a9dc9c7c4c6" - integrity sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww== - dependencies: - "@babel/runtime" "^7.25.7" - classnames "2.x" - rc-select "~14.16.2" - rc-tree "~5.13.0" - rc-util "^5.43.0" - -rc-tree@~5.13.0, rc-tree@~5.13.1: - version "5.13.1" - resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.13.1.tgz#f36a33a94a1282f4b09685216c01487089748910" - integrity sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A== - dependencies: - "@babel/runtime" "^7.10.1" - classnames "2.x" - rc-motion "^2.0.1" - rc-util "^5.16.1" - rc-virtual-list "^3.5.1" - -rc-upload@~4.11.0: - version "4.11.0" - resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.11.0.tgz#c2ced905a8b38b3e5c48493d388ca0e8373de18b" - integrity sha512-ZUyT//2JAehfHzjWowqROcwYJKnZkIUGWaTE/VogVrepSl7AFNbQf4+zGfX4zl9Vrj/Jm8scLO0R6UlPDKK4wA== - dependencies: - "@babel/runtime" "^7.18.3" - classnames "^2.2.5" - rc-util "^5.2.0" - -rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.2.0, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.24.4, rc-util@^5.25.2, rc-util@^5.27.0, rc-util@^5.30.0, rc-util@^5.31.1, rc-util@^5.32.2, rc-util@^5.34.1, rc-util@^5.35.0, rc-util@^5.36.0, rc-util@^5.37.0, rc-util@^5.38.0, rc-util@^5.38.1, rc-util@^5.40.1, rc-util@^5.43.0, rc-util@^5.44.0, rc-util@^5.44.1, rc-util@^5.44.3, rc-util@^5.44.4: +rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.31.1, rc-util@^5.35.0, rc-util@^5.44.1: version "5.44.4" resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.44.4.tgz#89ee9037683cca01cd60f1a6bbda761457dd6ba5" integrity sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w== @@ -6281,16 +6115,6 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18. "@babel/runtime" "^7.18.3" react-is "^18.2.0" -rc-virtual-list@^3.14.2, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2: - version "3.19.2" - resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz#1dd2d782c9a3ccbe537bb873447d73f83af8de0f" - integrity sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA== - dependencies: - "@babel/runtime" "^7.20.0" - classnames "^2.2.6" - rc-resize-observer "^1.0.0" - rc-util "^5.36.0" - react-cookie@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.0.1.tgz#0d7f5112c9003b61c70d3d53f1f0a5b741e9f2e9" @@ -6301,9 +6125,9 @@ react-cookie@^8.0.1: universal-cookie "^8.0.0" react-dom@^19.2.1: - version "19.2.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.1.tgz#ce3527560bda4f997e47d10dab754825b3061f59" - integrity sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg== + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.4.tgz#6fac6bd96f7db477d966c7ec17c1a2b1ad8e6591" + integrity sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ== dependencies: scheduler "^0.27.0" @@ -6325,23 +6149,23 @@ react-is@^18.2.0, react-is@^18.3.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-is@^19.2.1: - version "19.2.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.1.tgz#712fa6ee30c8398dc34260017325dad92b9224c5" - integrity sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA== +react-is@^19.2.4: + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" + integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== react-test-renderer@^19.2.0: - version "19.2.1" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.1.tgz#a2c91e0a47539431f1c7979c1076b1a25a8accdb" - integrity sha512-xsyf515ij+d8Rs/tsDZSJYXn+GdYO/IOw9BVFtjJbrrFR+dL1yZQQN1ChxY6otYAsCeAHhU0XsKyJUzP6omyvQ== + version "19.2.4" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.4.tgz#c26f93fc8da5409472626cd873d005a1974df3ff" + integrity sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw== dependencies: - react-is "^19.2.1" + react-is "^19.2.4" scheduler "^0.27.0" react@^19.2.1: - version "19.2.1" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.1.tgz#8600fa205e58e2e807f6ef431c9f6492591a2700" - integrity sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw== + version "19.2.4" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.4.tgz#438e57baa19b77cb23aab516cf635cd0579ee09a" + integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== read-installed@~4.0.3: version "4.0.3" @@ -6403,15 +6227,6 @@ regexp.prototype.flags@^1.5.3, regexp.prototype.flags@^1.5.4: gopd "^1.2.0" set-function-name "^2.0.2" -relay-runtime@12.0.0: - version "12.0.0" - resolved "https://registry.yarnpkg.com/relay-runtime/-/relay-runtime-12.0.0.tgz#1e039282bdb5e0c1b9a7dc7f6b9a09d4f4ff8237" - integrity sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug== - dependencies: - "@babel/runtime" "^7.0.0" - fbjs "^3.0.0" - invariant "^2.2.4" - remedial@^1.0.7: version "1.0.8" resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" @@ -6469,11 +6284,14 @@ resolve@^1.10.0, resolve@^1.22.4: supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.5: - version "2.0.0-next.5" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" - integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== + version "2.0.0-next.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.6.tgz#b3961812be69ace7b3bc35d5bf259434681294af" + integrity sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA== dependencies: - is-core-module "^2.13.0" + es-errors "^1.3.0" + is-core-module "^2.16.1" + node-exports-info "^1.6.0" + object-keys "^1.1.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6569,10 +6387,10 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: - version "7.7.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" - integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== +semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: + version "7.7.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== sentence-case@^3.0.4: version "3.0.4" @@ -6614,12 +6432,7 @@ set-proto@^1.0.0: es-errors "^1.3.0" es-object-atoms "^1.0.0" -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -sharp@^0.34.4, sharp@^0.34.5: +sharp@^0.34.5: version "0.34.5" resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.5.tgz#b6f148e4b8c61f1797bde11a9d1cfebbae2c57b0" integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg== @@ -6720,11 +6533,6 @@ signal-exit@^4.0.1, signal-exit@^4.1.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -signedsource@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" - integrity sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww== - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -6738,6 +6546,14 @@ slice-ansi@^7.1.0: ansi-styles "^6.2.1" is-fullwidth-code-point "^5.0.0" +slice-ansi@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-8.0.0.tgz#22d0b66d18bc5c57f488bfcf36cbde3bef731537" + integrity sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg== + dependencies: + ansi-styles "^6.2.3" + is-fullwidth-code-point "^5.1.0" + slide@~1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -6800,9 +6616,9 @@ spdx-expression-parse@^3.0.0: spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: - version "3.0.22" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz#abf5a08a6f5d7279559b669f47f0a43e8f3464ef" - integrity sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ== + version "3.0.23" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" + integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== spdx-ranges@^2.0.0: version "2.1.1" @@ -6904,13 +6720,13 @@ string-width@^7.0.0: get-east-asian-width "^1.0.0" strip-ansi "^7.1.0" -string-width@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.1.0.tgz#9e9fb305174947cf45c30529414b5da916e9e8d1" - integrity sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg== +string-width@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.0.tgz#bdb6a9bd6d7800db635adae96cdb0443fec56c42" + integrity sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw== dependencies: - get-east-asian-width "^1.3.0" - strip-ansi "^7.1.0" + get-east-asian-width "^1.5.0" + strip-ansi "^7.1.2" string.prototype.includes@^2.0.1: version "2.0.1" @@ -6994,12 +6810,12 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1, strip-ansi@^7.1.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" - integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== +strip-ansi@^7.0.1, strip-ansi@^7.1.0, strip-ansi@^7.1.2: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== dependencies: - ansi-regex "^6.0.1" + ansi-regex "^6.2.2" strip-bom@^3.0.0: version "3.0.0" @@ -7067,26 +6883,26 @@ swap-case@^2.0.2: tslib "^2.0.3" swr@^2.0.0: - version "2.3.7" - resolved "https://registry.yarnpkg.com/swr/-/swr-2.3.7.tgz#93ca89c9c06a6a8dab72e9d8e85a687123f40356" - integrity sha512-ZEquQ82QvalqTxhBVv/DlAg2mbmUjF4UgpPg9wwk4ufb9rQnZXh1iKyyKBqV6bQGu1Ie7L1QwSYO07qFIa1p+g== + version "2.4.1" + resolved "https://registry.yarnpkg.com/swr/-/swr-2.4.1.tgz#c9e48abff6bf4b04846342e2f1f6be108a078cf6" + integrity sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA== dependencies: dequal "^2.0.3" - use-sync-external-store "^1.4.0" + use-sync-external-store "^1.6.0" -sync-fetch@0.6.0-2: - version "0.6.0-2" - resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.6.0-2.tgz#d82d6dc8efaf2d103a9015e7bd7ba0bfc8e078f2" - integrity sha512-c7AfkZ9udatCuAy9RSfiGPpeOKKUAUK5e1cXadLOGUjasdxqYqAK0jTNkM/FSEyJ3a5Ra27j/tw/PS0qLmaF/A== +sync-fetch@0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/sync-fetch/-/sync-fetch-0.6.0.tgz#5759e775f3d5202e1b3d14821bc152fec32aa180" + integrity sha512-IELLEvzHuCfc1uTsshPK58ViSdNqXxlml1U+fmwJIKLYKOr/rAtBrorE2RYm5IHaMpDNlmC0fr1LAvdXvyheEQ== dependencies: node-fetch "^3.3.2" timeout-signal "^2.0.0" whatwg-mimetype "^4.0.0" synckit@^0.11.8: - version "0.11.11" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.11.tgz#c0b619cf258a97faa209155d9cd1699b5c998cb0" - integrity sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw== + version "0.11.12" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" + integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== dependencies: "@pkgr/core" "^0.2.9" @@ -7136,16 +6952,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - tree-kill@1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -7156,10 +6962,10 @@ treeify@^1.1.0: resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== -ts-api-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91" - integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ== +ts-api-utils@^2.4.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" + integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== ts-log@^2.2.3: version "2.2.7" @@ -7268,25 +7074,20 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.46.0: - version "8.49.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.49.0.tgz#4a8b608ae48c0db876c8fb2a2724839fc5a7147c" - integrity sha512-zRSVH1WXD0uXczCXw+nsdjGPUdx4dfrs5VQoHnUWmv1U3oNlAKv4FUNdLDhVUg+gYn+a5hUESqch//Rv5wVhrg== + version "8.57.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.57.1.tgz#573f97d3e48bbb67290b47dde1b7cb3b5d01dc4f" + integrity sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA== dependencies: - "@typescript-eslint/eslint-plugin" "8.49.0" - "@typescript-eslint/parser" "8.49.0" - "@typescript-eslint/typescript-estree" "8.49.0" - "@typescript-eslint/utils" "8.49.0" + "@typescript-eslint/eslint-plugin" "8.57.1" + "@typescript-eslint/parser" "8.57.1" + "@typescript-eslint/typescript-estree" "8.57.1" + "@typescript-eslint/utils" "8.57.1" typescript@^5.0.4: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== -ua-parser-js@^1.0.35: - version "1.0.41" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.41.tgz#bd04dc9ec830fcf9e4fad35cf22dcedd2e3b4e9c" - integrity sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug== - unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" @@ -7307,6 +7108,11 @@ undici-types@~7.16.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== + universal-cookie@^8.0.0: version "8.0.1" resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.0.1.tgz#19263fcda6889978a081c610a90fc395eeaeec64" @@ -7349,9 +7155,9 @@ unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" update-browserslist-db@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz#cfb4358afa08b3d5731a2ecd95eebf4ddef8033e" - integrity sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA== + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== dependencies: escalade "^3.2.0" picocolors "^1.1.1" @@ -7382,7 +7188,7 @@ urlpattern-polyfill@^10.0.0: resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-10.1.0.tgz#1b2517e614136c73ba32948d5e7a3a063cba8e74" integrity sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw== -use-sync-external-store@^1.4.0: +use-sync-external-store@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w== @@ -7433,24 +7239,11 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - whatwg-mimetype@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" @@ -7492,9 +7285,9 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.19" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.19.tgz#df03842e870b6b88e117524a4b364b6fc689f956" - integrity sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw== + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.8" @@ -7574,10 +7367,10 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@^8.17.1, ws@^8.18.3: - version "8.18.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" - integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== +ws@^8.18.3, ws@^8.19.0: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" + integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== y18n@^5.0.5: version "5.0.8" @@ -7589,19 +7382,18 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml-loader@^0.8: - version "0.8.1" - resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.8.1.tgz#034f901147073cfc307cdcce8bd44c1547e60ba1" - integrity sha512-BCEndnUoi3BaZmePkwGGe93txRxLgMhBa/gE725v1/GHnura8QvNs7c4+4C1yyhhKoj3Dg63M7IqhA++15j6ww== +yaml-loader@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/yaml-loader/-/yaml-loader-0.9.0.tgz#4de1f70dd5fb6965c4c126337729c41fd6ad0aba" + integrity sha512-N0bPfDR42qCruELfmpF6Q8XnDu1elTULGjvVn7u6rsfDa8WhlmJbjXQRh+x5TGmpOqw8R9qfiUgc3aKP+8Hd4w== dependencies: javascript-stringify "^2.0.1" - loader-utils "^2.0.0" yaml "^2.0.0" yaml@^2.0.0, yaml@^2.3.1: - version "2.8.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" - integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== + version "2.8.3" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" + integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== yargs-parser@^21.1.1: version "21.1.1" @@ -7642,6 +7434,6 @@ yoctocolors-cjs@^2.1.3: integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== "zod@^3.25.0 || ^4.0.0": - version "4.1.13" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.1.13.tgz#93699a8afe937ba96badbb0ce8be6033c0a4b6b1" - integrity sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig== + version "4.3.6" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" + integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== From 7e86b0dc2acb3db52c1525d8c90aae3d7f879895 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 22 Mar 2026 22:42:00 +0100 Subject: [PATCH 767/878] Do not use deprecated features and display errors in `ErrorAlert` --- frontend/components/ErrorAlert.tsx | 21 + .../components/FloatPropositionFormList.tsx | 28 +- frontend/components/Layout.tsx | 148 ++--- frontend/components/NavBar.tsx | 8 +- .../components/UuidPropositionFormList.tsx | 42 +- frontend/components/data/DataPageHeader.tsx | 5 +- .../data/lifeCycle/LifeCycleData.tsx | 4 +- .../components/databases/UpdateDatabase.tsx | 9 +- frontend/lib/apollo.ts | 226 ++++---- frontend/lib/table.tsx | 4 +- frontend/next.config.ts | 2 +- frontend/package.json | 2 +- frontend/pages/_app.tsx | 2 +- frontend/pages/data/calorimetric/index.tsx | 30 +- frontend/pages/data/geometric/index.tsx | 23 +- frontend/pages/data/hygrothermal/index.tsx | 8 +- frontend/pages/data/life-cycle/[uuid].tsx | 22 +- frontend/pages/data/life-cycle/index.tsx | 410 +++++++------ frontend/pages/data/optical/index.tsx | 26 +- frontend/pages/data/photovoltaic/index.tsx | 8 +- frontend/paths.ts | 156 ++--- frontend/queries/data.graphql | 364 ++++++------ frontend/type-defs.graphqls | 544 +++++++++++++++--- frontend/yarn.lock | 2 +- 24 files changed, 1250 insertions(+), 844 deletions(-) create mode 100644 frontend/components/ErrorAlert.tsx diff --git a/frontend/components/ErrorAlert.tsx b/frontend/components/ErrorAlert.tsx new file mode 100644 index 00000000..23537461 --- /dev/null +++ b/frontend/components/ErrorAlert.tsx @@ -0,0 +1,21 @@ +import { Alert } from "antd"; + +export default function ErrorAlert({ messages }: { messages: string[] }) { + return messages.length > 0 ? ( + + {messages.map((message, index) => ( +
  • {message}
  • + ))} + + ) + } + /> + ) : null; +} diff --git a/frontend/components/FloatPropositionFormList.tsx b/frontend/components/FloatPropositionFormList.tsx index 1c50a7ad..0453fcc7 100644 --- a/frontend/components/FloatPropositionFormList.tsx +++ b/frontend/components/FloatPropositionFormList.tsx @@ -1,5 +1,5 @@ -import { Form, Input, Select, InputNumber, Button } from "antd"; -import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; +import { Form, Select, InputNumber, Button, Space } from "antd"; +import { PlusOutlined } from "@ant-design/icons"; const tailLayout = { wrapperCol: { offset: 8, span: 16 }, @@ -20,24 +20,27 @@ export enum FloatPropositionComparator { export type FloatPropositionFormListProps = { name: string; label: string; + minimum: number; + maximum: number; }; export function FloatPropositionFormList({ name, label, + minimum, + maximum, }: FloatPropositionFormListProps) { return ( {(fields, { add, remove }, { errors }) => ( <> - {fields.map(({ key, name, fieldKey, ...restField }, index) => ( + {fields.map(({ key, name, ...restField }, index) => ( - + @@ -53,7 +56,6 @@ export function FloatPropositionFormList({ {...restField} key={`comparator${key}`} name={[name, "comparator"]} - fieldKey={[fieldKey ?? -1, "comparator"]} noStyle initialValue={FloatPropositionComparator.EqualTo} > @@ -83,21 +85,19 @@ export function FloatPropositionFormList({ {...restField} key={`value${key}`} name={[name, "value"]} - fieldKey={[fieldKey ?? -1, "value"]} noStyle > - remove(name)} - /> - + + ))} diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 1f107067..71d68fad 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -7,90 +7,90 @@ import paths from "../paths"; import { useCookies } from "react-cookie"; const navItems = [ - { - path: paths.home, - label: "Home", - }, - { - path: paths.calorimetricData, - label: "Calorimetric Data", - }, - { - path: paths.geometricData, - label: "Geometric Data", - }, - { - path: paths.hygrothermalData, - label: "Hygrothermal Data", - }, - { - path: paths.lifeCycleData, - label: "Life-Cycle Data", - }, - { - path: paths.opticalData, - label: "Optical Data", - }, - { - path: paths.photovoltaicData, - label: "Photovoltaic Data", - }, - { - path: paths.database, - label: "Database", - }, + { + path: paths.home, + label: "Home", + }, + { + path: paths.calorimetricData, + label: "Calorimetric Data", + }, + { + path: paths.geometricData, + label: "Geometric Data", + }, + { + path: paths.hygrothermalData, + label: "Hygrothermal Data", + }, + { + path: paths.lifeCycleData, + label: "Life-Cycle Data", + }, + { + path: paths.opticalData, + label: "Optical Data", + }, + { + path: paths.photovoltaicData, + label: "Photovoltaic Data", + }, + { + path: paths.database, + label: "Database", + }, ]; export type LayoutProps = { - children?: ReactNode; + children?: ReactNode; }; const cookieConsentName = "consent"; const cookieConsentValue = "yes"; export default function Layout({ children }: LayoutProps) { - const appTitle = "TestLab Solar Façades"; + const appTitle = "TestLab Solar Façades"; - const [cookies, setCookie] = useCookies([cookieConsentName]); - const shouldShowCookieConsent = - cookies[cookieConsentName] != cookieConsentValue; - const { modal } = App.useApp(); + const [cookies, setCookie] = useCookies([cookieConsentName]); + const shouldShowCookieConsent = + cookies[cookieConsentName] != cookieConsentValue; + const { modal } = App.useApp(); - useEffect(() => { - if (shouldShowCookieConsent) { - modal.info({ - title: "Cookie Consent", - content: ( - - This website employs cookies to make it work securely. As these - cookies are essential you need to agree to their usage to use this - website. - - ), - okText: "I agree", - onOk: () => { - setCookie(cookieConsentName, cookieConsentValue); - }, - }); - } - }, [shouldShowCookieConsent, setCookie]); + useEffect(() => { + if (shouldShowCookieConsent) { + modal.info({ + title: "Cookie Consent", + content: ( + + This website employs cookies to make it work securely. As these + cookies are essential you need to agree to their usage to use this + website. + + ), + okText: "I agree", + onOk: () => { + setCookie(cookieConsentName, cookieConsentValue); + }, + }); + } + }, [shouldShowCookieConsent, setCookie]); - return ( - - - {appTitle} - - - - - - - - {children} - - -
    - - - ); + return ( + + + {appTitle} + + + + + + + + {children} + + +
    + + + ); } diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 5fcdba78..8ac1ff76 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -40,9 +40,7 @@ export default function NavBar({ items }: NavBarProps) { {items.map(({ path, label }) => ( - - {label} - + {label} ))} {currentUser ? ( @@ -53,9 +51,7 @@ export default function NavBar({ items }: NavBarProps) { > {moderatorItems.map(({ path, label }) => ( - - {label} - + {label} ))} diff --git a/frontend/components/UuidPropositionFormList.tsx b/frontend/components/UuidPropositionFormList.tsx index 25b59edc..789e387d 100644 --- a/frontend/components/UuidPropositionFormList.tsx +++ b/frontend/components/UuidPropositionFormList.tsx @@ -1,5 +1,5 @@ -import { Form, Input, Select, Button } from "antd"; -import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; +import { Form, Input, Space, Select, Button } from "antd"; +import { PlusOutlined } from "@ant-design/icons"; const tailLayout = { wrapperCol: { offset: 8, span: 16 }, @@ -10,6 +10,11 @@ enum Negator { IsNot = "isNot", } +const negatorOptions = [ + { value: Negator.Is, label: "Is" }, + { value: Negator.IsNot, label: "Is" }, +]; + export enum UuidPropositionComparator { EqualTo = "equalTo", } @@ -27,27 +32,26 @@ export function UuidPropositionFormList({ {(fields, { add, remove }, { errors }) => ( <> - {fields.map(({ key, name, fieldKey, ...restField }, index) => ( + {fields.map(({ key, name, ...restField }, index) => ( - + - + + - remove(name)} - /> - + + ))} diff --git a/frontend/components/data/DataPageHeader.tsx b/frontend/components/data/DataPageHeader.tsx index 8d5db192..d8d27cd1 100644 --- a/frontend/components/data/DataPageHeader.tsx +++ b/frontend/components/data/DataPageHeader.tsx @@ -38,10 +38,7 @@ export default function DataPageHeader({ {data.createdAt} - + {data.creatorId} diff --git a/frontend/components/data/lifeCycle/LifeCycleData.tsx b/frontend/components/data/lifeCycle/LifeCycleData.tsx index e4ad6cfd..35b4ef17 100644 --- a/frontend/components/data/lifeCycle/LifeCycleData.tsx +++ b/frontend/components/data/lifeCycle/LifeCycleData.tsx @@ -10,9 +10,7 @@ export type LifeCycleDataProps = { lifeCycleDataId: Scalars["Uuid"]["input"]; }; -export default function LifeCycleData({ - lifeCycleDataId, -}: LifeCycleDataProps) { +export default function LifeCycleData({ lifeCycleDataId }: LifeCycleDataProps) { const { loading, error, data } = useQuery(LifeCycleDataDocument, { variables: { uuid: lifeCycleDataId, diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx index f70a0738..babdb4cf 100644 --- a/frontend/components/databases/UpdateDatabase.tsx +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -1,5 +1,5 @@ import { useMutation } from "@apollo/client/react"; -import { Alert, Form, Input, Button, Modal } from "antd"; +import { Form, Input, Button, Modal } from "antd"; import { UpdateDatabaseDocument, DatabaseDocument, @@ -7,6 +7,7 @@ import { import { Scalars } from "../../__generated__/graphql"; import { useState } from "react"; import { handleFormErrors } from "../../lib/form"; +import ErrorAlert from "../ErrorAlert"; const layout = { labelCol: { span: 8 }, @@ -104,11 +105,7 @@ export default function UpdateDatabase({ onCancel={() => setOpen(false)} footer={false} > - {globalErrorMessages.length > 0 ? ( - - ) : ( - <> - )} +
    cookie.trim()) - .filter((cookie) => { - return cookie.substring(0, name.length + 1) === `${name}=`; - }) - .map((cookie) => { - return decodeURIComponent(cookie.substring(name.length + 1)); - })[0] || null - ); + return ( + cookies + .split(";") + .map((cookie) => cookie.trim()) + .filter((cookie) => { + return cookie.substring(0, name.length + 1) === `${name}=`; + }) + .map((cookie) => { + return decodeURIComponent(cookie.substring(name.length + 1)); + })[0] || null + ); } // Log any GraphQL errors, protocol errors, or network error that occurred // inspired by https://www.apollographql.com/docs/react/api/link/apollo-link-error const logErrorsLink = new ErrorLink(({ error }) => { - if (CombinedGraphQLErrors.is(error)) { - error.errors.forEach(({ message, locations, path }) => - console.log( - `[GraphQL Error]: Message: ${message}, Location: ${locations}, Path: ${path}`, - ), - ); - } else if (CombinedProtocolErrors.is(error)) { - error.errors.forEach(({ message, extensions }) => - console.error( - `[Protocol Error]: Message: ${message}, Extensions: ${JSON.stringify( - extensions, - )}`, - ), - ); - } else { - console.error(`[Network Error]: ${error}`); - } + if (CombinedGraphQLErrors.is(error)) { + error.errors.forEach(({ message, locations, path }) => + console.log( + `[GraphQL Error]: Message: ${message}, Location: ${locations}, Path: ${path}`, + ), + ); + } else if (CombinedProtocolErrors.is(error)) { + error.errors.forEach(({ message, extensions }) => + console.error( + `[Protocol Error]: Message: ${message}, Extensions: ${JSON.stringify( + extensions, + )}`, + ), + ); + } else { + console.error(`[Network Error]: ${error}`); + } }); type AuthErrorCode = "AUTH_NOT_AUTHENTICATED"; type AuthGraphQlError = GraphQLFormattedError & { - extensions: { - code: AuthErrorCode; - }; + extensions: { + code: AuthErrorCode; + }; }; function isAuthError(error: GraphQLFormattedError): error is AuthGraphQlError { - return error.extensions?.code === "AUTH_NOT_AUTHENTICATED"; + return error.extensions?.code === "AUTH_NOT_AUTHENTICATED"; } const filterAuthErrorsLink = new ApolloLink((operation, forward) => - forward(operation).pipe( - map((response) => { - if (response.errors) { - response.errors = response.errors.filter( - (error): error is Exclude => - !isAuthError(error), - ); - } - return response; - }), - ), + forward(operation).pipe( + map((response) => { + if (response.errors) { + response.errors = response.errors.filter( + (error): error is Exclude => + !isAuthError(error), + ); + } + return response; + }), + ), ); export function extractAntiforgeryTokenFromCookie(): string | null { - return extractCookie(AntiforgeryCookieKey, document.cookie); + return extractCookie(AntiforgeryCookieKey, document.cookie); } // inspired by https://www.apollographql.com/docs/react/networking/authentication#header const antiforgeryLink = new SetContextLink(({ headers }) => { - const antiforgeryToken = extractAntiforgeryTokenFromCookie(); - if (antiforgeryToken) { - return { - headers: { - ...headers, - [AntiforgeryHeaderName]: antiforgeryToken, - }, - }; - } - return { headers: headers }; + const antiforgeryToken = extractAntiforgeryTokenFromCookie(); + if (antiforgeryToken) { + return { + headers: { + ...headers, + [AntiforgeryHeaderName]: antiforgeryToken, + }, + }; + } + return { headers: headers }; }); const persistedQueryLink = new PersistedQueryLink({ - sha256, - useGETForHashedQueries: false, + sha256, + useGETForHashedQueries: false, }); const httpLink = new HttpLink({ - uri: GraphQlEndpointPath, - useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors - credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie - headers: { accept: "application/json" }, + uri: GraphQlEndpointPath, + useGETForQueries: false, // Use `POST` for queries to avoid "414 Request-URI Too Large" errors + credentials: "same-origin", // https://www.apollographql.com/docs/react/networking/authentication#cookie + headers: { accept: "application/json" }, }); const apolloLink = filterAuthErrorsLink - .concat(logErrorsLink) - .concat(antiforgeryLink) - .concat(persistedQueryLink) - .concat(httpLink); + .concat(logErrorsLink) + .concat(antiforgeryLink) + .concat(persistedQueryLink) + .concat(httpLink); export const apolloClient = new ApolloClient({ - ssrMode: false, - link: apolloLink, - cache: new InMemoryCache({ - possibleTypes: fragmentMatcherTypes.possibleTypes, - }), - localState: new LocalState({}), - incrementalHandler: new Defer20220824Handler(), - defaultOptions: { - watchQuery: { - errorPolicy: "all" as const, - }, - query: { - errorPolicy: "all" as const, - }, - mutate: { - errorPolicy: "all" as const, - }, - }, + ssrMode: false, + link: apolloLink, + cache: new InMemoryCache({ + possibleTypes: fragmentMatcherTypes.possibleTypes, + }), + localState: new LocalState({}), + incrementalHandler: new Defer20220824Handler(), + defaultOptions: { + watchQuery: { + errorPolicy: "all" as const, + }, + query: { + errorPolicy: "all" as const, + }, + mutate: { + errorPolicy: "all" as const, + }, + }, }); // See https://www.apollographql.com/docs/react/data/error-handling export function stringifyApolloError(error: ErrorLike) { - // if (CombinedGraphQLErrors.is(error)) { - // return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions - // }); Stack(${error.stack}); GraphQL Errors(${error.errors - // .map( - // (e) => - // `[Message(${e.message}); Path(${e.path?.join( - // ".", - // )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions - // })]`, - // ) - // .join(", ")})`; - // } if (CombinedProtocolErrors.is(error)) { - // Handle multipart subscription protocol errors - // } if (LocalStateError.is(error)) { - // Handle errors thrown by the `LocalState` class - // } if (ServerError.is(error)) { - // Handle server HTTP errors - // } if (ServerParseError.is(error)) { - // Handle JSON parse errors - // } if (UnconventionalError.is(error)) { - // Handle errors thrown by irregular types - return `${error.name}: ${error.message}`; + // if (CombinedGraphQLErrors.is(error)) { + // return `Name(${error.name}); Message(${error.message}); Extensions(${error.extensions + // }); Stack(${error.stack}); GraphQL Errors(${error.errors + // .map( + // (e) => + // `[Message(${e.message}); Path(${e.path?.join( + // ".", + // )}); Locations(${e.locations?.join(", ")}); Extensions(${e.extensions + // })]`, + // ) + // .join(", ")})`; + // } if (CombinedProtocolErrors.is(error)) { + // Handle multipart subscription protocol errors + // } if (LocalStateError.is(error)) { + // Handle errors thrown by the `LocalState` class + // } if (ServerError.is(error)) { + // Handle server HTTP errors + // } if (ServerParseError.is(error)) { + // Handle JSON parse errors + // } if (UnconventionalError.is(error)) { + // Handle errors thrown by irregular types + return `${error.name}: ${error.message}`; } /* diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx index d53d218a..cad0b63e 100644 --- a/frontend/lib/table.tsx +++ b/frontend/lib/table.tsx @@ -146,9 +146,7 @@ export function getInternallyLinkedFilterableStringColumnProps( (record, _highlightedValue, value) => value ? ( // TODO Why does this not work with `_highlightedValue`? An error is raised saying "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?": https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-function-component or https://reactjs.org/docs/forwarding-refs.html or https://deepscan.io/docs/rules/react-func-component-invalid-ref-prop or https://www.carlrippon.com/react-forwardref-typescript/ - - {value} - + {value} ) : ( <> ), diff --git a/frontend/next.config.ts b/frontend/next.config.ts index c56a73d7..12c94a08 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - output: 'standalone', // https://nextjs.org/docs/app/api-reference/config/next-config-js/output#automatically-copying-traced-files + output: "standalone", // https://nextjs.org/docs/app/api-reference/config/next-config-js/output#automatically-copying-traced-files reactStrictMode: true, typedRoutes: true, allowedDevOrigins: [], diff --git a/frontend/package.json b/frontend/package.json index adca86e3..d5bdeef6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.10.8", + "baseline-browser-mapping": "^2.10.10", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^10.1.0", diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 1a28e7b4..a14175c8 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -3,7 +3,7 @@ import { apolloClient } from "../lib/apollo"; import { ApolloProvider } from "@apollo/client/react"; import { CookiesProvider } from "react-cookie"; import { App, ConfigProvider } from "antd"; -import { ReactNode, useEffect, useState } from 'react'; +import { ReactNode, useEffect, useState } from "react"; import paths from "../paths"; import "../styles/global.css"; diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index e23a22c7..7c95fb94 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -1,13 +1,5 @@ import Layout from "../../../components/Layout"; -import { - Table, - App, - Form, - Button, - Alert, - Typography, - Descriptions, -} from "antd"; +import { Table, App, Form, Button, Typography, Descriptions } from "antd"; import { AllCalorimetricDataDocument } from "../../../queries/data.generated"; import { CalorimetricData, @@ -35,6 +27,7 @@ import { } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; import { useQuery } from "@apollo/client/react"; +import ErrorAlert from "../../../components/ErrorAlert"; const layout = { labelCol: { span: 8 }, @@ -234,10 +227,7 @@ function Page() { return ( Calorimetric Data - {/* TODO Display error messages in a list? */} - {globalErrorMessages.length > 0 && ( - - )} + - - + + - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.lifeCycleDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - ]} - dataSource={data} - /> - - ); + + + + +
    ( + onFilterTextChange, + (x) => filterText.get(x), + paths.lifeCycleDatum, + ), + }, + { + ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => + filterText.get(x), + ), + }, + { + ...getDescriptionColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getTimestampColumnProps<(typeof data)[0]>(), + }, + { + ...getComponentUuidColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + // { + // ...getInternallyLinkedFilterableStringColumnProps( + // "Database UUID", + // "databaseId", + // (x) => x.databaseId, + // onFilterTextChange, + // (x) => filterText.get(x), + // (x) => paths.database(x.databaseId) + // ), + // }, + { + ...getAppliedMethodColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + { + ...getResourceTreeColumnProps<(typeof data)[0]>( + onFilterTextChange, + (x) => filterText.get(x), + ), + }, + ]} + dataSource={data} + /> + + ); } export default Page; diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index b48a66ff..0215eedd 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -1,13 +1,5 @@ import Layout from "../../../components/Layout"; -import { - Table, - App, - Form, - Button, - Alert, - Typography, - Descriptions, -} from "antd"; +import { Table, App, Form, Button, Typography, Descriptions } from "antd"; import { AllOpticalDataDocument } from "../../../queries/data.generated"; import { OpticalData, @@ -35,6 +27,7 @@ import { } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; import { useQuery } from "@apollo/client/react"; +import ErrorAlert from "../../../components/ErrorAlert"; const layout = { labelCol: { span: 8 }, @@ -319,10 +312,7 @@ function Page() { return ( Optical Data - {/* TODO Display error messages in a list? */} - {globalErrorMessages.length > 0 && ( - - )} +
    diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index 66ee80e7..f1ffca90 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -1,5 +1,5 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Alert, Typography } from "antd"; +import { Table, App, Form, Button, Typography } from "antd"; import { AllPhotovoltaicDataDocument } from "../../../queries/data.generated"; import { PhotovoltaicData, @@ -23,6 +23,7 @@ import { } from "../../../components/UuidPropositionFormList"; import paths from "../../../paths"; import { useQuery } from "@apollo/client/react"; +import ErrorAlert from "../../../components/ErrorAlert"; const layout = { labelCol: { span: 8 }, @@ -174,10 +175,7 @@ function Page() { return ( Photovoltaic Data - {/* TODO Display error messages in a list? */} - {globalErrorMessages.length > 0 && ( - - )} + Date: Mon, 23 Mar 2026 16:40:00 +0100 Subject: [PATCH 768/878] Correct type of file meta information list --- backend/src/Data/GetHttpsResource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index bdaa1252..a15ace42 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -279,7 +279,7 @@ internal void UpdateChild( string description, Guid dataFormatId, string? fileExtension, - FileMetaInformation[] archivedFilesMetaInformation, + ICollection archivedFilesMetaInformation, ToTreeVertexAppliedConversionMethod appliedConversionMethod ) { From 60e987ff97d0f5bb89dad1d624ca740ef041a09f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 23 Mar 2026 17:15:40 +0100 Subject: [PATCH 769/878] Correct data format index --- backend/src/GraphQl/DataMutationBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index 8b01d7a1..263b000a 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -98,7 +98,7 @@ CancellationToken cancellationToken var dataFormats = await Task.WhenAll(validateDataFormats); // Finally collect errors. var errors = new List(); - var dataFormat = dataFormats[1]; + var dataFormat = dataFormats[0]; if (dataFormat is null) { errors.Add( From 9b8b32fef8b627359d425290d22d3fe7ccee28fe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 23 Mar 2026 20:42:16 +0100 Subject: [PATCH 770/878] Name the reverse proxy service `reverse_proxy` (in accordance with the reverse proxy of the machine project) --- backend/src/Startup.cs | 8 ++++---- deploy.mk | 4 ++-- docker-compose.development.yaml | 2 +- docker-compose.production.yaml | 2 +- docker.mk | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index d8a83a0b..9cd60738 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -313,11 +313,11 @@ public void Configure(WebApplication app) // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. // ASP.NET advices to not use HSTS for APIs, see the warning on // https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl - // app.UseHsts(); // Done by NGINX, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ + // app.UseHsts(); // Done by the reverse proxy, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ } app.UseStatusCodePages(); // [UseStatusCodePages](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-9.0#usestatuscodepages) - // app.UseHttpsRedirection(); // Done by NGINX + // app.UseHttpsRedirection(); // Done by the reverse proxy app.UseSerilogRequestLogging(); app.UseStaticFiles(); app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) @@ -338,8 +338,8 @@ public void Configure(WebApplication app) app.UseAuthorization(); app.UseAntiforgery(); // app.UseSession(); // Not used - // app.UseResponseCompression(); // Done by Nginx - // app.UseResponseCaching(); // Done by Nginx + // app.UseResponseCompression(); // Done by the reverse proxy + // app.UseResponseCaching(); // Done by the reverse proxy // app.UseWebSockets(); app.MapOpenApi(OpenApiConstants.RoutePattern); app.MapScalarApiReference(OpenApiConstants.DocsRoute, _ => diff --git a/deploy.mk b/deploy.mk index 6de556a6..5644c866 100755 --- a/deploy.mk +++ b/deploy.mk @@ -91,8 +91,8 @@ dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are availa ${dotenv_linter} diff /mnt/.env "/mnt/.env.${ENVIRONMENT}.sample" .PHONY : dotenv -# Note that NGINX is because of its dependencies taken down and up last and in -# one go so the maintenance page is only down very shortly. +# Note that the reverse proxy is, because of its dependencies, taken down and +# up last and in one go so the maintenance page is only down very shortly. services : ## Recreate services docker compose up \ --no-build \ diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 3c49877a..4b5cefab 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -185,7 +185,7 @@ services: autoheal: "true" pull_policy: "never" - nginx: + reverse_proxy: image: "nginx:1.29-trixie-otel" depends_on: backend: diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 1ce15ae2..1fcf357a 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -133,7 +133,7 @@ services: autoheal: "true" pull_policy: "never" - nginx: + reverse_proxy: image: "nginx:1.29-trixie-otel" depends_on: backend: diff --git a/docker.mk b/docker.mk index e42846b7..9b4d59f2 100755 --- a/docker.mk +++ b/docker.mk @@ -106,7 +106,7 @@ down : ## Stop services and remove services, networks, and anonymous volumes cre ./frontend/queries/*.generated.ts .PHONY : down -restart : ## Restart services (and await their health), for example, `make restart` to restart all services or `make restart SERVICE=nginx` or `make restart SERVICE="database backend"` +restart : ## Restart services (and await their health), for example, `make restart` to restart all services or `make restart SERVICE=reverse_proxy` or `make restart SERVICE="database backend"` docker compose restart \ --no-deps ${SERVICE} docker compose up \ @@ -144,7 +144,7 @@ exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` ser .PHONY : exec enter : COMMAND = bash -enter : exec ## Enter shell in the running `${SERVICE}` service, for example, `make enter SERVICE=database` or `make enter SERVICE=nginx` +enter : exec ## Enter shell in the running `${SERVICE}` service, for example, `make enter SERVICE=database` or `make enter SERVICE=reverse_proxy` .PHONY : enter run : ## Run the one-time command `${COMMAND}` against a fresh `${SERVICE}` service From 553ab4be342314cea7fe5e62ad530f2418f83a43 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 23 Mar 2026 22:14:54 +0100 Subject: [PATCH 771/878] Return informative JSON error responses for proxy level errors --- nginx/templates/default.conf.template | 73 +++++++++++++++++++++------ 1 file changed, 58 insertions(+), 15 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index e06df341..1794b4b8 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -29,6 +29,7 @@ proxy_cache_bypass $http_upgrade; proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 60s; +proxy_intercept_errors on; # Use big buffers for the OpenId Connect cookies proxy_buffer_size 128k; @@ -53,6 +54,9 @@ client_body_timeout 10s; client_header_timeout 10s; send_timeout 10s; +# Distniguish through which proxies the request passed +add_header X-Proxy-Layer "inner" always; + map $http_accept $error_type { default "text/html"; ~application/json "application/json"; @@ -144,35 +148,74 @@ server { limit_req zone=general burst=2 nodelay; } - error_page 413 /413.json; - location = /413.json { + error_page 404 @not_found; + location @not_found { + internal; + add_header Content-Type application/json always; + return 404 '{"errors": [{"code": "NOT_FOUND", "message": "The requested resource could not be found.", "path": []}]}'; + } + + error_page 408 @request_timeout; + location @request_timeout { + internal; + add_header Content-Type application/json always; + return 408 '{"errors": [{"code": "REQUEST_TIMEOUT", "message": "The server timed out waiting for the request. Please check your network connection and try again.", "path": []}]}'; + } + + error_page 413 @content_too_large; + location @content_too_large { + internal; add_header Content-Type application/json always; - return 413 '{"errors": [{"code": "CONTENT_TOO_LARGE", "message": "The uploaded file exceeds the maximum allowed size.", "path": []}]}'; + return 413 '{"errors": [{"code": "CONTENT_TOO_LARGE", "message": "The uploaded file is too large. Please reduce the size and try again.", "path": []}]}'; } - error_page 404 /404.json; - location = /404.json { + error_page 429 @too_many_requests; + location @too_many_requests { + internal; + add_header Retry-After 60 always; add_header Content-Type application/json always; - return 429 '{"errors": [{"code": "NOT_FOUND", "message": "The requested resource does not exist.", "path": []}]}'; + return 429 '{"errors": [{"code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded. Please slow down and try again later.", "path": []}]}'; } - error_page 429 /429.json; - location = /429.json { - add_header Retry-After 1 always; + error_page 501 @not_implemented; + location @not_implemented { + internal; add_header Content-Type application/json always; - return 429 '{"errors": [{"code": "TOO_MANY_REQUESTS", "message": "Too many requests: Please slow down and try again later.", "path": []}]}'; + return 501 '{"errors": [{"code": "NOT_IMPLEMENTED", "message": "The HTTP method used is not supported.", "path": []}]}'; } - error_page 502 503 504 @maintenance; + error_page 502 @bad_gateway; + location @bad_gateway { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 502 '{"errors": [{"code": "BAD_GATEWAY", "message": "The upstream server is temporarily unable to handle the request. Please try again in 2 minutes.", "path": []}]}'; + } + + error_page 504 @gateway_timeout; + location @gateway_timeout { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 504 '{"errors": [{"code": "GATEWAY_TIMEOUT", "message": "The upstream server took too long to respond. This may be due to high load. Please try again in 2 minutes.", "path": []}]}'; + } + + error_page 503 @maintenance; location @maintenance { internal; if ($error_type = "application/json") { - add_header Retry-After 120 always; - add_header Content-Type application/json always; - return 503 '{"errors": [{"code": "MAINTENANCE", "message": "We are under maintenance and will be back soon!", "path": []}]}'; + error_page 503 @service_unavailable; + return 503; } add_header Retry-After 120 always; root /etc/nginx/html; - rewrite ^(.*)$ /maintenance.html break; + try_files /maintenance.html @service_unavailable; + } + + location @service_unavailable { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 503 '{"errors": [{"code": "SERVICE_UNAVAILABLE", "message": "The system is under maintenance. Please try again in 2 minutes.", "path": []}]}'; } } From d05d6f4c8d5b442647f3be6aec8a34acac4f92cb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 15:34:24 +0100 Subject: [PATCH 772/878] Remove `--check` from `build` (it makes the `frontend` image not build sometimes for some reason) --- docker.mk | 1 - 1 file changed, 1 deletion(-) diff --git a/docker.mk b/docker.mk index 9b4d59f2..9f5a84e0 100755 --- a/docker.mk +++ b/docker.mk @@ -62,7 +62,6 @@ pull : ## Pull images build : symlink dotenv pull ## Build images docker compose build \ --pull \ - --check \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) ${SERVICE} .PHONY : build From dedf0d53d8b187f8a6ac7b70dde98110b7fa5467 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 16:03:04 +0100 Subject: [PATCH 773/878] Correct example to make help text work --- deploy.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.mk b/deploy.mk index 5644c866..2d7c1bba 100755 --- a/deploy.mk +++ b/deploy.mk @@ -32,7 +32,7 @@ do : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deplo .PHONY : do restore : ON_ERROR = pause -restore : symlink ## Restore deployment `${TIMESTAMP}`, for example, `./deploy.mk restore TIMESTAMP="2026-03-12T21:43:11+01:00" ON_ERROR=pause` +restore : symlink ## Restore deployment `${TIMESTAMP}`, for example, `./deploy.mk restore TIMESTAMP="2026-03-12T21" ON_ERROR=pause` ./deploy.sh restore "${TIMESTAMP}" --on-error "${ON_ERROR}" .PHONY : restore From d7a65d0cbede58e827199a403ae7d42913e61d52 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 16:22:05 +0100 Subject: [PATCH 774/878] Convert date-time to UTC --- backend/src/Data/ApplicationDbContext.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 34bfce41..3311127b 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; namespace Database.Data; @@ -67,6 +68,28 @@ DbContextOptions options public DbSet OpticalData { get; private set; } = default!; public DbSet PhotovoltaicData { get; private set; } = default!; + // Inspired by https://github.com/openiddict/openiddict-core/issues/1376#issuecomment-1151275376 + // It is needed to fix the following error that occurred when trying to redeem OpenId Connect tokens in production: + // `Cannot write DateTime with Kind=Unspecified to PostgreSQL type 'timestamp with time zone', only UTC is supported (...)` + // See also https://github.com/openiddict/openiddict-core/issues/1376 + protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) + { + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + base.ConfigureConventions(configurationBuilder); + } + + private sealed class UtcValueConverter : ValueConverter + { + public UtcValueConverter() + : base( + v => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime(), + v => DateTime.SpecifyKind(v, DateTimeKind.Utc) + ) + { + } + } + public IQueryable Data(DataKind dataKind) { return dataKind switch From dde8902b3cecf1c697eebb1357550104a3c6835d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 16:37:44 +0100 Subject: [PATCH 775/878] Convert date-time with offset to UTC --- backend/src/Data/ApplicationDbContext.cs | 35 +++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 3311127b..b3570272 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -14,6 +14,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; +using NodaTime; namespace Database.Data; @@ -74,14 +75,18 @@ DbContextOptions options // See also https://github.com/openiddict/openiddict-core/issues/1376 protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { - configurationBuilder.Properties().HaveConversion(); - configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); + configurationBuilder.Properties().HaveConversion(); base.ConfigureConventions(configurationBuilder); } - private sealed class UtcValueConverter : ValueConverter + private sealed class DateTimeUtcValueConverter : ValueConverter { - public UtcValueConverter() + public DateTimeUtcValueConverter() : base( v => v.Kind == DateTimeKind.Utc ? v : v.ToUniversalTime(), v => DateTime.SpecifyKind(v, DateTimeKind.Utc) @@ -90,6 +95,28 @@ public UtcValueConverter() } } + private sealed class DateTimeOffsetUtcValueConverter : ValueConverter + { + public DateTimeOffsetUtcValueConverter() + : base( + v => v.ToUniversalTime(), + v => v + ) + { + } + } + + private sealed class OffsetDateTimeUtcValueConverter : ValueConverter + { + public OffsetDateTimeUtcValueConverter() + : base( + v => v.WithOffset(Offset.Zero), + v => v + ) + { + } + } + public IQueryable Data(DataKind dataKind) { return dataKind switch From 7a97e3d661a704f868c8ef4255e1a6bac6227900 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 21:35:16 +0100 Subject: [PATCH 776/878] Make file restore work in development --- database.mk | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/database.mk b/database.mk index ae3632e1..c54c43be 100755 --- a/database.mk +++ b/database.mk @@ -177,7 +177,11 @@ restore : ## Restore database and related data from directory with absolute path --volume "${DIR}":/backup \ backend \ bash -o errexit -o errtrace -o nounset -o pipefail -c " \ - cd /app/files \ + if [[ "${ENVIRONMENT}" == "development" ]]; then \ + cd /app/src/files ; \ + else \ + cd /app/files ; \ + fi \ && rm \ --recursive \ --force \ From 24494fa2c6ba4554fd6bab6b02b1215da4c0327b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 22:29:01 +0100 Subject: [PATCH 777/878] Allow Next.JS to use web sockets in development --- docker-compose.development.yaml | 1 + frontend/environment.d.ts | 1 + frontend/next.config.ts | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 4b5cefab..52cbe88a 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -161,6 +161,7 @@ services: - /app/node_modules environment: NODE_ENV: "development" + HOST: "${HOST:?required}" extra_hosts: - "${SUBDOMAIN:?required}.${HOST:?required}:host-gateway" # user: "1000:1000" diff --git a/frontend/environment.d.ts b/frontend/environment.d.ts index 28ab6cc3..03537938 100644 --- a/frontend/environment.d.ts +++ b/frontend/environment.d.ts @@ -2,6 +2,7 @@ declare global { namespace NodeJS { interface ProcessEnv { NODE_ENV: "development" | "production"; + HOST: string | undefined; } } } diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 12c94a08..9960a61d 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -4,7 +4,7 @@ const nextConfig: NextConfig = { output: "standalone", // https://nextjs.org/docs/app/api-reference/config/next-config-js/output#automatically-copying-traced-files reactStrictMode: true, typedRoutes: true, - allowedDevOrigins: [], + allowedDevOrigins: process.env.HOST == null ? [] : [`*.${process.env.HOST}`], turbopack: { rules: { "/\.(yml|yaml$)/": ["yaml-loader"], From 33c44d893c8367c10612e9b2f3b82f0bf0ac2704 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 24 Mar 2026 23:17:17 +0100 Subject: [PATCH 778/878] Do not buffer file uploads in reverse proxy --- nginx/templates/default.conf.template | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 1794b4b8..0676505c 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -96,6 +96,7 @@ server { location /_next/webpack-hmr { proxy_pass http://frontend; proxy_read_timeout 86400s; + proxy_send_timeout 86400s; limit_req zone=general burst=100 nodelay; } @@ -126,6 +127,7 @@ server { client_body_timeout 300s; proxy_read_timeout 300s; proxy_send_timeout 300s; + proxy_request_buffering off; } location /api/resources/ { From b73ea6244a5c53eba753edc17928cff83c3b509f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 14:13:45 +0100 Subject: [PATCH 779/878] Remove outdated frontend README --- docker-compose.development.yaml | 2 +- docker.mk | 1 + frontend/README.md | 36 --------------------------------- 3 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 frontend/README.md diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 52cbe88a..cd705b0f 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -84,7 +84,7 @@ services: XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" - XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" XBASE_OpenTelemetry__GrpcPort: "${OPEN_TELEMETRY_GRPC_PORT:?required}" diff --git a/docker.mk b/docker.mk index 9f5a84e0..cdd102f0 100755 --- a/docker.mk +++ b/docker.mk @@ -173,6 +173,7 @@ bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a sh docker compose pull \ backend docker compose build \ + --pull \ --build-arg GROUP_ID=$(shell id --group) \ --build-arg USER_ID=$(shell id --user) \ backend diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 5800f313..00000000 --- a/frontend/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# TypeScript and GraphQL Example - -One of the strengths of GraphQL is [enforcing data types on runtime](https://graphql.github.io/graphql-spec/June2018/#sec-Value-Completion). Further, TypeScript and [GraphQL Code Generator](https://graphql-code-generator.com/) (graphql-codegen) make it safer by typing data statically, so you can write truly type-protected code with rich IDE assists. - -This template extends [Apollo Server and Client Example](https://github.com/vercel/next.js/tree/canary/examples/api-routes-apollo-server-and-client#readme) by rewriting in TypeScript and integrating [graphql-let](https://github.com/piglovesyou/graphql-let#readme), which runs [TypeScript React Apollo](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo) in [graphql-codegen](https://github.com/dotansimha/graphql-code-generator#readme) under the hood. It enhances the typed GraphQL use as below: - -```tsx -import { useNewsQuery } from './news.graphql' - -const News = () => { - // Typed already️⚡️ - const { data: { news } } = useNewsQuery() - - return
    {news.map(...)}
    -} -``` - -By default `**/*.graphqls` is recognized as GraphQL schema and `**/*.graphql` as GraphQL documents. If you prefer the other extensions, make sure the settings of the webpack loader in `next.config.js` and `.graphql-let.yaml` are consistent. - -## Deploy your own - -Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-typescript-graphql&project-name=with-typescript-graphql&repository-name=with-typescript-graphql) - -## How to use - -Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init) or [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/) to bootstrap the example: - -```bash -npx create-next-app --example with-typescript-graphql with-typescript-graphql-app -# or -yarn create next-app --example with-typescript-graphql with-typescript-graphql-app -``` - -Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)). From ac3cf2e71005c4fcfc77293091fdaca5c9492b37 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 14:34:24 +0100 Subject: [PATCH 780/878] Set GnuPG fingerprint for bootstrapping the GnuPG key --- docker-compose.development.yaml | 2 +- docker.mk | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index cd705b0f..52cbe88a 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -84,7 +84,7 @@ services: XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" - XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT?required}" + XBASE_GnupgSecretSigningKey__Fingerprint: "${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT:?required}" XBASE_GnupgSecretSigningKey__Passphrase: "${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE:?required}" XBASE_OpenTelemetry__Host: "${OPEN_TELEMETRY_HOST:?required}" XBASE_OpenTelemetry__GrpcPort: "${OPEN_TELEMETRY_GRPC_PORT:?required}" diff --git a/docker.mk b/docker.mk index cdd102f0..2108c4d3 100755 --- a/docker.mk +++ b/docker.mk @@ -13,10 +13,10 @@ SERVICE= dotenv_linter = \ docker run \ --rm \ - --user $(shell id --user):$(shell id --group) \ + --user "$(shell id --user):$(shell id --group)" \ --volume "$(shell pwd):/mnt:ro" \ --quiet \ - dotenvlinter/dotenv-linter:4.0.0 + dotenvlinter/dotenv-linter:4.0.0 # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -62,8 +62,8 @@ pull : ## Pull images build : symlink dotenv pull ## Build images docker compose build \ --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) ${SERVICE} + --build-arg GROUP_ID="$(shell id --group)" \ + --build-arg USER_ID="$(shell id --user)" ${SERVICE} .PHONY : build bake : ## Print docker-compose file equivalent bake file @@ -168,17 +168,22 @@ list-services : ## List all services specified in the docker-compose file (used --services .PHONY : list-services +# During the initial setup, the fingerprint is unknown. However, the Docker +# Compose files require it to be defined and non-empty. So, if it is empty, we +# set it to "unknown". bootstrap : COMMAND = bash +bootstrap : GNUPG_SECRET_SIGNING_KEY_FINGERPRINT = $(or ${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT},unknown) bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a shell within, for example, `make bootstrap COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` or simply `make bootstrap` docker compose pull \ backend docker compose build \ --pull \ - --build-arg GROUP_ID=$(shell id --group) \ - --build-arg USER_ID=$(shell id --user) \ + --build-arg GROUP_ID="$(shell id --group)" \ + --build-arg USER_ID="$(shell id --user)" \ backend docker compose run \ --volume .:/app \ --rm \ backend \ ${COMMAND} +.PHONY : bootstrap From 78a906ff960c08d913efecc281c9dc2270f64c17 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 14:50:38 +0100 Subject: [PATCH 781/878] Separate running tests from listing tests --- backend/Makefile | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index d7218a21..6919de45 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -23,19 +23,18 @@ help : ## Print this help # See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test # Sadly, `--list-tests` ignores the option `--filter` in the test adapter # `NUnit`: https://github.com/dotnet/sdk/issues/8643#issuecomment-1497765835 +list-tests : ARGS = +list-tests : ## List all tests + dotnet test \ + --list-tests \ + ${ARGS} +.PHONY : list-tests + +# See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test # dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal # dotnet retest -- --filter "FullyQualifiedName~${x}" --verbosity minimal test : ARGS = test : ## Run (previously failed) tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make test ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"'` - @echo "--------------------------------------------------" - @echo "Complete test list" - @echo "--------------------------------------------------" - dotnet test \ - --list-tests \ - ${ARGS} - @echo "--------------------------------------------------" - @echo "All test runs" - @echo "--------------------------------------------------" dotnet retest \ --retries 0 \ -- \ From f6db19faaf3f47effb38df428b156792f103d3bc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 16:41:33 +0100 Subject: [PATCH 782/878] Clean-up registration of compile, content, and watch items --- backend/src/Database.csproj | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index e92324c3..60594d3a 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -81,30 +81,30 @@ false - $(DefaultItemExcludes);**/obj/**;**/bin/**;**/obj%5CDebug/**;**/bin%5CDebug/**;**/logs/**;**/files/** + **/obj/**;**/bin/**;**/obj%5CDebug/**;**/bin%5CDebug/**;**/logs/**;**/files/** - + + - + + PreserveNewest - + PreserveNewest - + PreserveNewest - - - - - - - + + + + + From 1d2c24001b769d88e669ab9e8c0d0c62050037ff Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 16:45:48 +0100 Subject: [PATCH 783/878] Build PostgreSQL connection string in C# to allow for replacing only the database name for running tests --- backend/src/AppSettings.cs | 6 ++++- backend/src/Startup.cs | 22 +++++++++++++------ .../CustomWebApplicationFactory.cs | 10 ++++----- docker-compose.development.yaml | 7 +++++- docker-compose.production.yaml | 7 +++++- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 48eef9ae..af155b5f 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -45,7 +45,11 @@ public sealed record GnupgSecretSigningKeySettings public sealed record DatabaseSettings { - public string ConnectionString { get; set; } = ""; + public string Host { get; init; } = ""; + public int Port { get; init; } + public string Name { get; set; } = ""; + public string UserName { get; init; } = ""; + public string Password { get; init; } = ""; public string SchemaName { get; init; } = ""; }; diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 9cd60738..c1f2c3ca 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -29,6 +29,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; +using Npgsql; using OpenTelemetry.Exporter; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; @@ -202,15 +203,22 @@ private void ConfigureDatabaseContext( DbContextOptionsBuilder options ) { - // https://www.npgsql.org/efcore/mapping/enum.html + var connectionStringBuilder = new NpgsqlConnectionStringBuilder(); + connectionStringBuilder.Host = _appSettings.Database.Host; + connectionStringBuilder.Port = _appSettings.Database.Port; + connectionStringBuilder.Database = _appSettings.Database.Name; + connectionStringBuilder.Username = _appSettings.Database.UserName; + connectionStringBuilder.Password = _appSettings.Database.Password; + connectionStringBuilder.MaxPoolSize = 90; options .UseNpgsql( - _appSettings.Database.ConnectionString, + connectionStringBuilder.ConnectionString, _ => _ // Keep version in sync with the one in ./docker-compose.*.yaml .SetPostgresVersion(13, 23) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .UseNodaTime() + // https://www.npgsql.org/efcore/mapping/enum.html .MapEnum(ApplicationDbContext.CalorimetricObserverTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.CoatedSideTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, _appSettings.Database.SchemaName) @@ -230,19 +238,19 @@ DbContextOptionsBuilder options .EnableSensitiveDataLogging() .EnableDetailedErrors(); } - if (environment.IsEnvironment(Program.TestEnvironment)) { - options.ConfigureWarnings(x => - x.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) + options.ConfigureWarnings(_ => + _.Ignore(CoreEventId.ManyServiceProvidersCreatedWarning) ); } } private void ConfigureDatabaseServices(IServiceCollection services) { - // Configure the database-context options only once as suggested in - // https://github.com/npgsql/efcore.pg/issues/3375#issuecomment-2509746639 + // Configure the database-context options only once in + // `AddPooledDbContextFactory` and not a second time in `AddDbContext` + // as suggested in services.AddPooledDbContextFactory(ConfigureDatabaseContext); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index c5034708..653ae7d7 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -66,8 +66,8 @@ public AppSettings AppSettings protected override void ConfigureWebHost(IWebHostBuilder builder) { - var connectionString = $"Host=database; Port=5432; Database=xbase_test_{Guid.NewGuid().ToString().Replace("-", "")}; User Id=postgres; Password=postgres; Maximum Pool Size=90;"; - // var schemaName = $"database_{Guid.NewGuid().ToString().Replace("-", "")}"; + var databaseName = Guid.NewGuid().ToString().Replace("-", ""); + // var schemaName = $"metabase_{Guid.NewGuid().ToString().Replace("-", "")}"; // builder.ConfigureAppConfiguration(_ => _.AddInMemoryCollection([new KeyValuePair("Database__ConnectionString", connectionString)])); // "Database__SchemaName" builder.UseEnvironment(Database.Program.TestEnvironment); builder.ConfigureAppConfiguration((webHostBuilderContext, configurationBuilder) => @@ -85,8 +85,8 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) using var scope = serviceCollection.BuildServiceProvider().CreateScope(); // Configure `AppSettings` var appSettings = scope.ServiceProvider.GetRequiredService(); - appSettings.Database.ConnectionString = connectionString; - // `appSettings.Database.ConnectionString` should readonly. + appSettings.Database.Name = databaseName; + // `appSettings.Database.Name` should readonly. // However, the commented code below together with // `AddInMemoryCollection` above, does not configure the // `ConnectionString` in such a way, that Npgsql knows about it. @@ -97,7 +97,7 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) // { // Database = oldAppSettings.Database with // { - // ConnectionString = connectionString + // Name = databaseName // // SchemaName = schemaName // } // } diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 52cbe88a..429c3584 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -3,6 +3,7 @@ name: "${NAME:?required}_${ENVIRONMENT:?required}" services: database: + # Keep version in sync with the one in `Startup.cs` image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data @@ -79,8 +80,12 @@ services: XBASE_MetabaseHost: "${METABASE_HOST:?required}:${METABASE_HTTPS_PORT:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_Database__Host: "database" + XBASE_Database__Port: 5432 + XBASE_Database__Name: "${POSTGRES_DATABASE_NAME:?required}" + XBASE_Database__UserName: "${POSTGRES_USER:?required}" + XBASE_Database__Password: "${POSTGRES_PASSWORD:?required}" XBASE_Database__SchemaName: "database" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME:?required}'; User Id='${POSTGRES_USER:?required}'; Password='${POSTGRES_PASSWORD:?required}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 1fcf357a..cbb0dd72 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -3,6 +3,7 @@ name: "${NAME:?required}_${ENVIRONMENT:?required}" services: database: + # Keep version in sync with the one in `Startup.cs` image: "postgres:13.23-trixie" volumes: - data:/var/lib/postgresql/data @@ -61,8 +62,12 @@ services: XBASE_MetabaseHost: "${METABASE_HOST:?required}" XBASE_DatabaseId: "${DATABASE_ID:?required}" XBASE_OperatorId: "${OPERATOR_ID:?required}" + XBASE_Database__Host: "database" + XBASE_Database__Port: 5432 + XBASE_Database__Name: "${POSTGRES_DATABASE_NAME:?required}" + XBASE_Database__UserName: "${POSTGRES_USER:?required}" + XBASE_Database__Password: "${POSTGRES_PASSWORD:?required}" XBASE_Database__SchemaName: "database" - XBASE_Database__ConnectionString: "Host=database; Port=5432; Database='${POSTGRES_DATABASE_NAME:?required}'; User Id='${POSTGRES_USER:?required}'; Password='${POSTGRES_PASSWORD:?required}'; Maximum Pool Size=90;" XBASE_OpenIdConnectClient__Id: "${OPEN_ID_CONNECT_CLIENT_ID:?required}" XBASE_OpenIdConnectClient__Secret: "${OPEN_ID_CONNECT_CLIENT_SECRET:?required}" XBASE_VerificationCode: "${VERIFICATION_CODE:?required}" From 56c4e675e7189eb9e02ac8ccd53e55c8706d5339 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 25 Mar 2026 19:19:44 +0100 Subject: [PATCH 784/878] Use the new Microsoft.Testing.Platform as test platform and stay with NUnit as test runner and log to NUnit with its Serilog sink --- backend/Directory.Build.props | 18 ++++ backend/Makefile | 94 +++++++------------ backend/global.json | 5 + backend/src/Database.csproj | 16 +--- backend/test/Database.Tests.csproj | 29 +++--- .../CustomWebApplicationFactory.cs | 16 +++- backend/test/Program.cs | 13 --- 7 files changed, 87 insertions(+), 104 deletions(-) create mode 100644 backend/Directory.Build.props create mode 100644 backend/global.json delete mode 100644 backend/test/Program.cs diff --git a/backend/Directory.Build.props b/backend/Directory.Build.props new file mode 100644 index 00000000..7538c469 --- /dev/null +++ b/backend/Directory.Build.props @@ -0,0 +1,18 @@ + + + + net10.0 + 14.0 + enable + + + + + true + true + true + latest-Recommended + true + + + diff --git a/backend/Makefile b/backend/Makefile index 6919de45..a4169f1e 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -20,38 +20,44 @@ help : ## Print this help # List of dotnet commands # https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet -# See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test -# Sadly, `--list-tests` ignores the option `--filter` in the test adapter -# `NUnit`: https://github.com/dotnet/sdk/issues/8643#issuecomment-1497765835 -list-tests : ARGS = -list-tests : ## List all tests - dotnet test \ +# See https://learn.microsoft.com/en-us/dotnet/core/testing/microsoft-testing-platform-run-and-debug?tabs=dotnetcli +list : ARGS = +list : ## List tests, for example, `make list-tests ARGS='--filter "FullyQualifiedName~LoggedInUser_CreatesComponent"'` + dotnet run \ + --project ./test/*.Tests.csproj \ --list-tests \ ${ARGS} -.PHONY : list-tests +.PHONY : list -# See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test +# See https://learn.microsoft.com/en-us/dotnet/core/testing/microsoft-testing-platform-run-and-debug?tabs=dotnetcli # dotnet test --filter "FullyQualifiedName~${x}" --verbosity minimal # dotnet retest -- --filter "FullyQualifiedName~${x}" --verbosity minimal test : ARGS = test : ## Run (previously failed) tests, possibly with custom arguments `${ARGS}` from `dotnet test --help`, for example, `make test` or `make test ARGS='--filter "FullyQualifiedName~InvalidResetCode_IsUserError"'` - dotnet retest \ - --retries 0 \ - -- \ - --collect:"XPlat Code Coverage" \ + dotnet run \ + --project ./test/*.Tests.csproj \ + --coverage \ ${ARGS} .PHONY : test -assets : ## Build assets - npm run all -.PHONY : assets +play : ## Run tests in `Metabase.Tests.Play` (semi-alternative to the buggy `dotnet script` REPL) + dotnet run \ + --project ./test/*.Tests.csproj \ + --filter "FullyQualifiedName~Database.Tests.Play" +.PHONY : play + +info : ## Displays advanced information about the .NET Test Application such as ... The platform. The environment. Each registered command line provider, such as its name, version, description, and options. Each registered tool, such as its command, name, version, description, and all command-line providers.xxx + dotnet run \ + --project ./test/*.Tests.csproj \ + --info +.PHONY : info # For usage information see https://github.com/tomchavakis/nuget-license#usage # or run `dotnet tool run dotnet-project-licenses -- --help` licenses : ## Print licenses dotnet tool run \ dotnet-project-licenses \ - -i ./Database.sln + -i ./*.sln # --json # --allowed-license-types ... # --forbidden-license-types ... @@ -82,17 +88,13 @@ analyze : ## Analyze with Roslynator # https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html#install-and-use-resharper-command-line-tools-as-net-core-tools inspect : ## Inspect with ReSharper, for example, `make inspect OUTPUT=./inspection.xml` - dotnet tool run jb inspectcode --build ./Database.sln -o ${OUTPUT} + dotnet tool run jb inspectcode --build ./*.sln -o ${OUTPUT} .PHONY : inspect editorconfig : ## Debug editorconfig editorconfig ./.editorconfig .PHONY : editorconfig -play : ## Run tests in `Database.Tests.Play` (semi-alternative to the buggy `dotnet script` REPL) - dotnet test --filter "FullyQualifiedName~Database.Tests.Play" -.PHONY : play - # TODO Apply not only to `src` folder but ignore files and directories within .gitignore. # With git installed there are various options, which all have some # disadvantages, see @@ -113,15 +115,15 @@ dos2unix : ## Strip the byte-order mark, also known as, BOM, and remove carriage # We do not run JetBrains ReSharper cleanupcode because it does wrong things # like removing private setters needed by EntityFramework Core. We used to run # it as follows: For formatting -# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat Code" ./Database.sln` +# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat Code" ./*.sln` # For syntax styling -# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./Database.sln` +# `dotnet tool run jb cleanupcode --profile="Built-in: Reformat & Apply Syntax Style" ./*.sln` # For cleanup -# `dotnet tool run jb cleanupcode ./Database.sln` +# `dotnet tool run jb cleanupcode ./*.sln` # The profile `Built-in: Full Cleanup` does not update file headers. # See https://www.jetbrains.com/help/resharper/Code_Cleanup__Index.html#profiles format : ## Format code - dotnet format ./Database.sln + dotnet format ./*.sln $(MAKE) dos2unix .PHONY : format @@ -183,8 +185,8 @@ schema : ## Fetch GraphQL schema using `hasura/graphqurl` has-pending-model-changes : ## Check whether there have been any model changes made since the last migration was added dotnet ef \ - --project ./src/Database.csproj \ - --startup-project ./src/Database.csproj \ + --project ./src/*.csproj \ + --startup-project ./src/*.csproj \ migrations \ has-pending-model-changes .PHONY : has-pending-model-changes @@ -194,8 +196,8 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot old=$$(find ./src/Migrations/ -type f -regex ".*/[^/.]*_[^/.]*\.cs" -exec basename --suffix=.cs {} \; | sort | tail --lines=1 ) && \ echo old=$$old && \ dotnet ef \ - --project ./src/Database.csproj \ - --startup-project ./src/Database.csproj \ + --project ./src/*.csproj \ + --startup-project ./src/*.csproj \ migrations \ add \ ${NAME} && \ @@ -204,8 +206,8 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot migrate_path=./src/Migrations/migrate_from_$${old}_to_$${new}.sql && \ rollback_path=./src/Migrations/rollback_from_$${new}_to_$${old}.sql && \ dotnet ef \ - --project ./src/Database.csproj \ - --startup-project ./src/Database.csproj \ + --project ./src/*.csproj \ + --startup-project ./src/*.csproj \ migrations \ script \ --idempotent \ @@ -213,8 +215,8 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot $${new} \ --output $${migrate_path} && \ dotnet ef \ - --project ./src/Database.csproj \ - --startup-project ./src/Database.csproj \ + --project ./src/*.csproj \ + --startup-project ./src/*.csproj \ migrations \ script \ --idempotent \ @@ -226,29 +228,3 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot $(MAKE) dos2unix echo "Review the generated C# and SQL files and make necessary adjustments consistently across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." .PHONY : migration - -# https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying?tabs=dotnet-core-cli#command-line-tools -# update-database : ## Update the database to the latest database schema -# dotnet ef \ -# --project ./src/Database.csproj \ -# --startup-project ./src/Database.csproj \ -# database \ -# update -# .PHONY : update-database - -list-keys : ## List GnuPG keys - gpg --list-keys -.PHONY : list-keys - -delete-key : ## Delete GnuPG key `${FINGERPRINT}` - gpg \ - --pinentry-mode loopback \ - --batch \ - --yes \ - --delete-secret-keys ${FINGERPRINT} - gpg \ - --pinentry-mode loopback \ - --batch \ - --yes \ - --delete-key ${FINGERPRINT} -.PHONY : delete-key diff --git a/backend/global.json b/backend/global.json new file mode 100644 index 00000000..3140116d --- /dev/null +++ b/backend/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 60594d3a..a587442b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -1,21 +1,11 @@ + + - net10.0 - 14.0 - enable - 31666837686487542159 true - - - - - true - true - true - latest-Recommended - true + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 3ac9482d..01e4126b 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -1,18 +1,11 @@ - + + + - net10.0 - false - 14.0 - false - enable - - true - true - true - latest-Recommended - true - false + true + true + Exe @@ -22,13 +15,15 @@ - - - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index 653ae7d7..ae7204ea 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -3,13 +3,14 @@ // https://www.thinktecture.com/en/entity-framework-core/isolation-of-integration-tests-in-2-1/ using System; -using System.Linq; using System.Threading.Tasks; +using Serilog; using Database.Data; -using Database.Services; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System.Globalization; namespace Database.Tests.Integration; @@ -64,6 +65,17 @@ public AppSettings AppSettings } } + protected override IHost CreateHost(IHostBuilder builder) + { + builder.UseSerilog((context, services, configuration) => + { + configuration + .ReadFrom.Configuration(context.Configuration) // appsettings.test.json + .WriteTo.NUnitOutput(formatProvider: CultureInfo.InvariantCulture); + }); + return base.CreateHost(builder); + } + protected override void ConfigureWebHost(IWebHostBuilder builder) { var databaseName = Guid.NewGuid().ToString().Replace("-", ""); diff --git a/backend/test/Program.cs b/backend/test/Program.cs deleted file mode 100644 index cc52b131..00000000 --- a/backend/test/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using NUnitLite; - -namespace Database.Tests; - -public static class Program -{ - public static int Main( - string[] commandLineArguments - ) - { - return new AutoRun().Execute(commandLineArguments); - } -} \ No newline at end of file From 112a989327f5f29902b794d3b4b61fd8a85c483e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 26 Mar 2026 09:36:38 +0100 Subject: [PATCH 785/878] Include props file with common csproj settings in the Dockerfile --- backend/.dockerignore | 3 +- backend/Dockerfile.development | 169 +++++++++++++++++---------------- 2 files changed, 87 insertions(+), 85 deletions(-) diff --git a/backend/.dockerignore b/backend/.dockerignore index 9187b428..591bb290 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -7,7 +7,8 @@ !**/*.cs !**/*.cshtml !**/*.csproj -!**/*.sln +!*.sln +!*.props !**/*.graphql !**/*.json diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index e6a3c6e5..4452979b 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -21,34 +21,34 @@ RUN \ RUN \ apt-get update && \ apt-get install \ - --assume-yes \ - --no-install-recommends \ - adduser && \ + --assume-yes \ + --no-install-recommends \ + adduser && \ existing_user_name="$( (getent passwd ${USER_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ if test -n "${existing_user_name}"; then \ - deluser "${existing_user_name}"; \ + deluser "${existing_user_name}"; \ fi && \ existing_group_name="$( (getent group ${GROUP_ID} 2>/dev/null || true) | cut --delimiter=: --fields=1)" && \ if test -n "${existing_group_name}"; then \ - delgroup "${existing_group_name}"; \ + delgroup "${existing_group_name}"; \ fi && \ addgroup \ - --system \ - --gid "${GROUP_ID}" \ - us && \ + --system \ + --gid "${GROUP_ID}" \ + us && \ adduser \ - --system \ - --home /home/me \ - --uid "${USER_ID}" \ - --ingroup us \ - me && \ + --system \ + --home /home/me \ + --uid "${USER_ID}" \ + --ingroup us \ + me && \ apt-get remove \ - --assume-yes \ - adduser && \ + --assume-yes \ + adduser && \ rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* ############# # As `root` # @@ -74,34 +74,34 @@ RUN \ # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run RUN apt-get update \ && apt-get install \ - --assume-yes \ - --no-install-recommends \ - curl \ - editorconfig \ - gawk \ - gnupg2 \ - lldb \ - make \ - procps \ - tini \ + --assume-yes \ + --no-install-recommends \ + curl \ + editorconfig \ + gawk \ + gnupg2 \ + lldb \ + make \ + procps \ + tini \ && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* # Add dependencies for interactive examples in ./examples # `jq` can be used to format and filter JSON data RUN apt-get update \ && apt-get install \ - --assume-yes \ - --no-install-recommends \ - bash \ - jq \ - neovim \ + --assume-yes \ + --no-install-recommends \ + bash \ + jq \ + neovim \ && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. # For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ @@ -111,47 +111,47 @@ RUN \ version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ apt-get update && \ apt-get install \ - --assume-yes \ - --no-install-recommends \ - curl \ - gnupg2 && \ + --assume-yes \ + --no-install-recommends \ + curl \ + gnupg2 && \ curl \ - --silent \ - --show-error \ - "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ - | gpg --dearmor \ - > /usr/share/keyrings/gnupg-keyring.gpg && \ + --silent \ + --show-error \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ fingerprint=$(gpg \ - --batch \ - --with-colons \ - --import-options show-only \ - --import /usr/share/keyrings/gnupg-keyring.gpg \ - | awk -F':' '$1=="fpr"{print $10}') && \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ - echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ - exit 1; \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ fi && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ - > /etc/apt/sources.list.d/gnupg.list && \ + > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ apt-get install \ - --assume-yes \ - --no-install-recommends \ - dirmngr \ - gnupg2 && \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2 && \ rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* ENV HOME=/home/me RUN mkdir --parents "${HOME}/app" \ && chown \ - me:us \ - "${HOME}" \ + me:us \ + "${HOME}" \ && chown \ - me:us \ - "${HOME}/app" \ + me:us \ + "${HOME}/app" \ && ln --symbolic "${HOME}/app" /app ########### @@ -176,10 +176,11 @@ RUN dotnet tool restore \ RUN mkdir ./src \ && mkdir ./test -COPY --chown=me:us ./src/Database.csproj ./src/ -COPY --chown=me:us ./test/Database.Tests.csproj ./test/ +COPY --chown=me:us ./src/*.csproj ./src/ +COPY --chown=me:us ./test/*.Tests.csproj ./test/ -COPY --chown=me:us ./Database.sln ./ +COPY --chown=me:us ./Directory.Build.props ./ +COPY --chown=me:us ./*.sln ./ RUN dotnet clean \ && dotnet restore \ && dotnet tool run dotnet-sos install # https://github.com/dotnet/diagnostics/blob/master/documentation/installing-sos-instructions.md @@ -212,29 +213,29 @@ EOT RUN mkdir --parents \ ./src/files \ && mkdir --parents \ - ./src/logs \ + ./src/logs \ && mkdir --parents \ - ./src/bin \ - ./src/obj \ + ./src/bin \ + ./src/obj \ && mkdir --parents \ - ./test/bin \ - ./test/obj \ + ./test/bin \ + ./test/obj \ && mkdir --parents \ - "${HOME}/.nuget" \ - "${HOME}/.dotnet" \ + "${HOME}/.nuget" \ + "${HOME}/.dotnet" \ && mkdir --parents \ - "${HOME}/.vscode-server" \ - "${HOME}/.vscode-server-insiders" \ + "${HOME}/.vscode-server" \ + "${HOME}/.vscode-server-insiders" \ && mkdir --parents \ - ${HOME}/.dotnet/corefx/cryptography/x509stores \ + ${HOME}/.dotnet/corefx/cryptography/x509stores \ && mkdir --parents \ - "${HOME}/.gnupg" \ + "${HOME}/.gnupg" \ && chmod u=rwx,g=,o= \ - "${HOME}/.gnupg" \ + "${HOME}/.gnupg" \ && echo "# use-keyboxd" \ - >"${HOME}/.gnupg/common.conf" \ + >"${HOME}/.gnupg/common.conf" \ && echo "allow-loopback-pinentry" \ - >"${HOME}/.gnupg/gpg-agent.conf" + >"${HOME}/.gnupg/gpg-agent.conf" ENV SHELL=/bin/bash From 189ca9225b8833966548258a6389c591a44f62a3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 28 Mar 2026 11:54:28 +0100 Subject: [PATCH 786/878] Tell what to do when migrations are pending or missing --- backend/src/Program.cs | 2 +- backend/test/Integration/CustomWebApplicationFactory.cs | 5 +++-- backend/test/Integration/MigrationTests.cs | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 85764a8e..3fe7ba65 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -166,7 +166,7 @@ IServiceProvider services var pendingMigrations = dbContext.Database.GetPendingMigrations(); if (pendingMigrations.Any()) { - throw new InvalidOperationException($"The database is not up to date. The pending migrations are: {string.Join(", ", pendingMigrations)}"); + throw new InvalidOperationException($"The database is not up to date. The pending migrations are: {string.Join(", ", pendingMigrations)}. Apply them by running `./database.mk migrate`."); } } diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index ae7204ea..7a42c80c 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -67,6 +67,7 @@ public AppSettings AppSettings protected override IHost CreateHost(IHostBuilder builder) { + builder.UseEnvironment(Database.Program.TestEnvironment); builder.UseSerilog((context, services, configuration) => { configuration @@ -79,8 +80,8 @@ protected override IHost CreateHost(IHostBuilder builder) protected override void ConfigureWebHost(IWebHostBuilder builder) { var databaseName = Guid.NewGuid().ToString().Replace("-", ""); - // var schemaName = $"metabase_{Guid.NewGuid().ToString().Replace("-", "")}"; - // builder.ConfigureAppConfiguration(_ => _.AddInMemoryCollection([new KeyValuePair("Database__ConnectionString", connectionString)])); // "Database__SchemaName" + // var schemaName = $"database_{Guid.NewGuid().ToString().Replace("-", "")}"; + // builder.ConfigureAppConfiguration(_ => _.AddInMemoryCollection([new KeyValuePair("Database__Name", databaseName)])); // "Database__SchemaName" builder.UseEnvironment(Database.Program.TestEnvironment); builder.ConfigureAppConfiguration((webHostBuilderContext, configurationBuilder) => { diff --git a/backend/test/Integration/MigrationTests.cs b/backend/test/Integration/MigrationTests.cs index 841858d8..26c7852b 100644 --- a/backend/test/Integration/MigrationTests.cs +++ b/backend/test/Integration/MigrationTests.cs @@ -44,7 +44,10 @@ public Task EnsureMigrationsAreUpToDate() source: snapshotModel?.GetRelationalModel(), target: model.GetRelationalModel()); // The differences should be empty if the migrations are up-to-date - modelDifferences.Should().BeEquivalentTo(Enumerable.Empty()); // .BeEmpty(); + modelDifferences.Should().BeEquivalentTo( + Enumerable.Empty(), + because: "you forgot to add a migration with `make migration NAME='...' in the shell `./docker.mk shell SERVICE=backend`." + ); }); } } \ No newline at end of file From f4bc6a73d2d757cf09c3b0e5a2a2d7c4dc8de95b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Apr 2026 19:08:28 +0200 Subject: [PATCH 787/878] Emit compiler generated files to ease debugging --- backend/Directory.Build.props | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/Directory.Build.props b/backend/Directory.Build.props index 7538c469..1de6f3ca 100644 --- a/backend/Directory.Build.props +++ b/backend/Directory.Build.props @@ -13,6 +13,8 @@ true latest-Recommended true + + true From 8a33cf15987a75b39cf28e32adf40becf5dc1e2d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 1 Apr 2026 19:09:04 +0200 Subject: [PATCH 788/878] Use HotChocolate run method to support for example exporting the schema in development --- backend/src/Database.csproj | 1 + backend/src/Program.cs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index a587442b..cb7a4f4c 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -21,6 +21,7 @@ + diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 3fe7ba65..3f99d381 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using HotChocolate.AspNetCore; using Database.Data; using Database.Services; using Microsoft.AspNetCore.Builder; @@ -92,9 +93,8 @@ string[] commandLineArguments await SeedDatabase(scope.ServiceProvider); await AssertGnuPgKeyExistence(scope.ServiceProvider); } - - await application.RunAsync(); - return 0; + // dotnet run -- schema export --output ./schema.graphql + return await application.RunWithGraphQLCommandsAsync(commandLineArguments); } catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { From d0ad9ffb61cc2fca62a558b046c6b50d3440d2c8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 18 Apr 2026 21:05:23 +0200 Subject: [PATCH 789/878] Document environment and target variables --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 5a10fbef..52127fce 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,12 @@ and the pages following it. information on what the variables mean - `NAME` is the name Docker project name, in particular, it is the prefix of the Docker container names listed by `docker ps --all`; + - `ENVIRONMENT` is either `staging` or `production`; + - `TARGET` is the deployed tag or commit. It is set later by running + `./deploy.mk do TARGET=${TAG}`. The corresponding Docker images named + `${NAME}-backend:${TARGET}` and `${NAME}-frontend:${TARGET}` are built + on a build or development machine and pushed to the server later with + GNU Make targets from `./forge.mk`; - `HOST` is the domain name with sub-domain of the deployment, in particular, it is used to make resource locators absolute; - `HTTP_PORT` is the HTTP port to which the reverse proxy NGINX forwards From 03af9cefea0b5d441275558fab096a64a45a4c08 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 18 Apr 2026 21:06:28 +0200 Subject: [PATCH 790/878] Using branch as deploy target is problematic --- deploy.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.mk b/deploy.mk index 2d7c1bba..e4bcb547 100755 --- a/deploy.mk +++ b/deploy.mk @@ -27,7 +27,7 @@ help : ## Print this help .DEFAULT_GOAL := help do : ON_ERROR = pause -do : symlink ## Deploy tag, branch, or commit `${TARGET}`, for example, `./deploy.mk do TARGET=v1.0.0 ON_ERROR=pause` +do : symlink ## Deploy tag or commit `${TARGET}`, for example, `./deploy.mk do TARGET=v1.0.0 ON_ERROR=pause` ./deploy.sh target "${TARGET}" --on-error "${ON_ERROR}" .PHONY : do From af4870b13c5a5e222e8154930e393327b6a504ae Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 24 Apr 2026 12:58:38 +0200 Subject: [PATCH 791/878] Add placeholder for GnuPG fingerprint --- .env.development.sample | 2 +- .env.production.sample | 2 +- .env.staging.sample | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.development.sample b/.env.development.sample index 03af4e23..dd709e85 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -24,5 +24,5 @@ OPEN_TELEMETRY_GRPC_PORT=4517 VERIFICATION_CODE=secret -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase diff --git a/.env.production.sample b/.env.production.sample index 6ecb54e7..b4129d94 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -23,5 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.env.staging.sample b/.env.staging.sample index c426c63a..b4cf4beb 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -23,5 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= From a8a333dc3b2a44b0efba0e5d9fe64d4ecea55fa2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 24 Apr 2026 12:59:31 +0200 Subject: [PATCH 792/878] Proxy Next.JS static assets properly --- nginx/templates/default.conf.template | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 0676505c..5b67632e 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -57,6 +57,10 @@ send_timeout 10s; # Distniguish through which proxies the request passed add_header X-Proxy-Layer "inner" always; +types { + application/javascript js mjs; +} + map $http_accept $error_type { default "text/html"; ~application/json "application/json"; @@ -93,6 +97,13 @@ server { limit_req zone=general burst=100 nodelay; } + location /_next/static { + proxy_pass http://frontend; + expires 365d; + access_log off; + limit_req zone=general burst=100 nodelay; + } + location /_next/webpack-hmr { proxy_pass http://frontend; proxy_read_timeout 86400s; @@ -100,6 +111,13 @@ server { limit_req zone=general burst=100 nodelay; } + location ~ /_next/static/.*/_clientMiddlewareManifest\.js$ { + proxy_pass http://frontend; + proxy_hide_header Content-Type; + add_header Content-Type application/javascript; + limit_req zone=general burst=100 nodelay; + } + location /graphql { proxy_pass http://backend; limit_req zone=sensitive burst=12 delay=6; From 752209a473bf251f14b8e1fc1a7927ae6b2595a3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 24 Apr 2026 13:10:33 +0200 Subject: [PATCH 793/878] Install GNU Make in production image to run `./gpg.mk` GNU Make targets --- backend/Dockerfile.production | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index 97e8c326..ac5d406a 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -62,6 +62,7 @@ RUN apt-get update \ --assume-yes \ --no-install-recommends \ curl \ + make \ && rm \ --recursive \ --force \ From 9c6e4c584b5be6a54bba6331faea323cd7ee3310 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 24 Apr 2026 15:08:51 +0200 Subject: [PATCH 794/878] Rename `./docker.mk bootstrap` to `./docker.mk manage` (it can also be used later on to manage GnuPG keys), forward necessary environment variables (the .env file is not available inside the container if it is a symbolic link), and use `./forge.mk build` instead of `docker compose build` in production (and staging) --- .env.development.sample | 2 +- .env.production.sample | 4 +-- .env.staging.sample | 4 +-- CHANGELOG.md | 3 +- README.md | 47 +++++++++++++++----------- docker.mk | 74 ++++++++++++++++++++++++++++------------- forge.mk | 17 ++++++---- gpg.mk | 7 ++-- 8 files changed, 101 insertions(+), 57 deletions(-) diff --git a/.env.development.sample b/.env.development.sample index dd709e85..03af4e23 100644 --- a/.env.development.sample +++ b/.env.development.sample @@ -24,5 +24,5 @@ OPEN_TELEMETRY_GRPC_PORT=4517 VERIFICATION_CODE=secret -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE=passphrase diff --git a/.env.production.sample b/.env.production.sample index b4129d94..b46b26cd 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -1,6 +1,6 @@ NAME=database_production ENVIRONMENT=production -TARGET=v1.0.0 +TARGET= HOST=solarbuildingenvelopes.com SUBDOMAIN=www @@ -23,5 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/.env.staging.sample b/.env.staging.sample index b4cf4beb..6d2f6658 100644 --- a/.env.staging.sample +++ b/.env.staging.sample @@ -1,6 +1,6 @@ NAME=database_staging ENVIRONMENT=staging -TARGET=26156e1f3d8c27095f9770c645d06d548537a5d2 +TARGET= HOST=solarbuildingenvelopes.com SUBDOMAIN=staging @@ -23,5 +23,5 @@ OPEN_TELEMETRY_GRPC_PORT=4317 VERIFICATION_CODE= -GNUPG_SECRET_SIGNING_KEY_FINGERPRINT=placeholder +GNUPG_SECRET_SIGNING_KEY_FINGERPRINT= GNUPG_SECRET_SIGNING_KEY_PASSPHRASE= diff --git a/CHANGELOG.md b/CHANGELOG.md index 38c7cdcd..eaa6569d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) - Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) - Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) -- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make bootstrap` and stored in the volume `gnupg`. +- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make manage` and stored in the volume `gnupg`. - Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. - - @@ -122,5 +122,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.0.0] - 2022-02-28 [Unreleased]: https://github.com/building-envelope-data/database/compare/v1.0.0...HEAD - [v1.0.0]: https://github.com/building-envelope-data/database/compare/352f44e492ca66d7835970cdf007b2897e686e30...v1.0.0 diff --git a/README.md b/README.md index 52127fce..2c4c0d76 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,17 @@ When doing so, please adhere to our 1. Generate a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in `./.env` by running - `make bootstrap COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` + `make manage COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` with your information filled in, for example, - `make bootstrap COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. + `make manage COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in `./.env`. + Note that the key is stored in the Docker Compose volume + `${NAME}_${ENVIRONMENT}_gnupg`. Here or later on, you can enter a shell + inside the management container by running `make manage` and manage GnuPG + keys with the `gpg` command or the GNU Make targets `./gpg.mk` there. + 1. Create the PostgreSQL database and schema by running `./database.mk create migrate`. @@ -368,13 +373,18 @@ and the pages following it. 1. Generate a GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}` set in the `./.env` file by running - `make bootstrap COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` + `make manage COMMAND='./gpg.mk key PERSON="${name}" COMMENT="${comment}" EMAIL="${email}"'` with your information filled in, for example, - `make bootstrap COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. + `make manage COMMAND='./gpg.mk key PERSON="Anna Smith" COMMENT="first" EMAIL="anna.smith@fraunhofer.de"'`. Then copy the key's fingerprint which is output by the command and set it as the value of the `GNUPG_SECRET_SIGNING_KEY_FINGERPRINT` variable in the `./.env` file. + Note that the key is stored in the Docker Compose volume + `${NAME}_${ENVIRONMENT}_gnupg`. Here or later on, you can enter a shell + inside the management container by running `make manage` and manage GnuPG + keys with the `gpg` command or the GNU Make targets `./gpg.mk` there. + 1. Create the PostgreSQL database by running `./database.mk create`. ### Creating a release @@ -420,29 +430,28 @@ and the pages following it. 1. Deploy the new release in the staging environment by running `./deploy.mk do TARGET=${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. -1. If it fails _after_ the database backup was made, rollback to the previous - state by running - `./deploy.mk rollback`, - figure out what went wrong, apply the necessary fixes to the codebase, - create a new release, and try to deploy that release instead. +1. If it fails, then either + - rollback to the previous state by running `./deploy.mk rollback`, figure + out what went wrong, apply the necessary fixes to the codebase, create a + new release, and try to deploy that release instead, or + - make configuration changes that caused the failure and resume the + deployment attempt by running `./deploy.mk resume`. 1. If it succeeds, deploy the new reverse proxy that handles sub-domains by - running `cd /app/machine && ./deploy.mk do` and test whether everything works - as expected and if that is the case, continue. Note that in the - staging environment sent emails can be viewed in the web browser under + running `cd /app/machine && git pull && ./deploy.mk do` and test whether + everything works as expected and if that is the case, continue. Note that in + the staging environment sent emails can be viewed in the web browser under `https://staging.solarbuildingenvelopes.com/email/` and emails to addresses in - the variable `RELAY_ALLOWED_EMAILS` in `./.env` are delivered to the - respective inboxes (the variable's value is a comma separated list of email - addresses). + the variable `RELAY_ALLOWED_EMAILS` in `./.env` are delivered to the respective + inboxes (the variable's value is a comma separated list of email addresses). 1. Change to the production environment by running `cd /app/production`. 1. Adapt the environment file `./.env` if necessary by comparing it with the `./.env.production.sample` file of the release to be deployed. 1. Deploy the new release in the production environment by running `./deploy.mk do TARGET=${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. -1. If it fails _after_ the database backup was made, rollback to the previous - state by running - `./deploy.mk rollback`, - figure out what went wrong, apply the necessary fixes to the codebase, +1. If it fails, then rollback to the previous state by running + `./deploy.mk rollback`, figure out what went wrong, apply the necessary + fixes the configuration and try again, or apply fixes to the codebase, create a new release, and try to deploy that release instead. ### Troubleshooting diff --git a/docker.mk b/docker.mk index 2108c4d3..4189a24c 100755 --- a/docker.mk +++ b/docker.mk @@ -60,10 +60,21 @@ pull : ## Pull images .PHONY : pull build : symlink dotenv pull ## Build images - docker compose build \ - --pull \ - --build-arg GROUP_ID="$(shell id --group)" \ - --build-arg USER_ID="$(shell id --user)" ${SERVICE} + if [[ "${ENVIRONMENT}" == "development" ]]; then \ + docker compose build \ + --pull \ + --build-arg GROUP_ID="$(shell id --group)" \ + --build-arg USER_ID="$(shell id --user)" ${SERVICE} ; \ + else \ + $(MAKE) --file=./forge.mk \ + build \ + SERVICE=backend \ + OVERWRITE=true && \ + $(MAKE) --file=./forge.mk \ + build \ + SERVICE=frontend \ + OVERWRITE=true ; \ + fi .PHONY : build bake : ## Print docker-compose file equivalent bake file @@ -168,22 +179,39 @@ list-services : ## List all services specified in the docker-compose file (used --services .PHONY : list-services -# During the initial setup, the fingerprint is unknown. However, the Docker -# Compose files require it to be defined and non-empty. So, if it is empty, we -# set it to "unknown". -bootstrap : COMMAND = bash -bootstrap : GNUPG_SECRET_SIGNING_KEY_FINGERPRINT = $(or ${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT},unknown) -bootstrap : ## Run a one-time command in a fresh bootstrap service or enter a shell within, for example, `make bootstrap COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` or simply `make bootstrap` - docker compose pull \ - backend - docker compose build \ - --pull \ - --build-arg GROUP_ID="$(shell id --group)" \ - --build-arg USER_ID="$(shell id --user)" \ - backend - docker compose run \ - --volume .:/app \ - --rm \ - backend \ - ${COMMAND} -.PHONY : bootstrap +# During the initial setup, target and fingerprint are unknown. However, the +# Docker Compose files requires them to be defined and non-empty. So, if they +# are empty, we set them to the branch name (or the commit hash if in detached +# head mode) and to "unknown" respectively. +# Because in production ./.env is a symbolic link (even to a file in another +# file system under /app/data), it is not available within the management +# container. So, we pass the required variables explicitly. +manage : COMMAND = bash +manage : TARGET := $(or ${TARGET},$(shell git symbolic-ref --quiet --short HEAD || git rev-parse --verify HEAD)) +manage : GNUPG_SECRET_SIGNING_KEY_FINGERPRINT := $(or ${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT},unknown) +manage : ## Run a one-time command in a fresh management service or enter a shell within, for example, `make manage COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` to create a GnuPG key or simply `make manage` to enter bash inside the container + if [[ "${ENVIRONMENT}" == "development" ]]; then \ + GNUPG_SECRET_SIGNING_KEY_FINGERPRINT="${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}" \ + docker compose pull \ + backend && \ + GNUPG_SECRET_SIGNING_KEY_FINGERPRINT="${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}" \ + docker compose build \ + --pull \ + --build-arg GROUP_ID="$(shell id --group)" \ + --build-arg USER_ID="$(shell id --user)" \ + backend ; \ + else \ + $(MAKE) --file=./forge.mk \ + build \ + SERVICE=backend \ + TARGET="${TARGET}" ; \ + fi + TARGET="${TARGET}" \ + GNUPG_SECRET_SIGNING_KEY_FINGERPRINT="${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT}" \ + docker compose run \ + --volume .:/app \ + --rm \ + --env GNUPG_SECRET_SIGNING_KEY_PASSPHRASE="${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}" \ + backend \ + ${COMMAND} +.PHONY : manage diff --git a/forge.mk b/forge.mk index 886ac0ed..8f5aa062 100755 --- a/forge.mk +++ b/forge.mk @@ -20,12 +20,17 @@ target : ## Print value of variable `TARGET` @echo ${TARGET} .PHONY : target -build : ## Build image, for example, `./forge.mk build SERVICE=backend` - docker build \ - --pull \ - --file "./${SERVICE}/Dockerfile.production" \ - --tag "${NAME}-${SERVICE}:${TARGET}" \ - "./${SERVICE}" +build : OVERWRITE = false +build : ## Build image if it does not exist yet, for example, `./forge.mk build SERVICE=backend` or even if it does exist `./forge.mk build SERVICE=backend OVERWRITE=true` + if [[ "${OVERWRITE}" == "true" || -z "$(shell docker images --quiet '${NAME}-${SERVICE}:${TARGET}')" ]]; then \ + docker build \ + --pull \ + --file "./${SERVICE}/Dockerfile.production" \ + --tag "${NAME}-${SERVICE}:${TARGET}" \ + "./${SERVICE}" ; \ + else \ + echo "Image already exists, skipping build. Pass `OVERWRITE=true` to build anyway." ; \ + fi .PHONY : build push : ## Push image, for example, `./forge.mk push SERVICE=backend USER=cloud HOST=192.102.162.39` diff --git a/gpg.mk b/gpg.mk index 4e57c489..6afa2452 100755 --- a/gpg.mk +++ b/gpg.mk @@ -1,7 +1,10 @@ #!/usr/bin/env -S make --file SELF := $(lastword $(MAKEFILE_LIST)) -include ./.env +# Because in production ./.env is a symbolic link (even to a file in another +# file system under /app/data), it is not available within the management +# container in which this Makefile is used. So, we need the required variables +# to be available in the environment (instead of including ./.env) SHELL := /usr/bin/env bash .SHELLFLAGS := -o errexit -o errtrace -o nounset -o pipefail -c @@ -20,7 +23,7 @@ socket : ## Print socket path .PHONY : socket key : COMMENT = -key : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `make gpg PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` +key : ## Generate GnuPG key with the passphrase `${GNUPG_SECRET_SIGNING_KEY_PASSPHRASE}`, for example, `./gpg.mk key PERSON="Simon Wacker" COMMENT=solarbuildingenvelopes EMAIL=simon.wacker@ise.fraunhofer.de` gpg \ --quick-generate-key \ --batch \ From 226b487eaa32e995af051689240ce598ba6596c5 Mon Sep 17 00:00:00 2001 From: christoph-maurer Date: Mon, 27 Apr 2026 19:32:18 +0200 Subject: [PATCH 795/878] Update the frontend --- frontend/package.json | 4 +- frontend/tsconfig.json | 18 +- frontend/yarn.lock | 1230 ++++++++++++++++++++-------------------- 3 files changed, 632 insertions(+), 620 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index d5bdeef6..aa378520 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.10.10", + "baseline-browser-mapping": "^2.10.23", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^10.1.0", @@ -53,7 +53,7 @@ "prettier": "^3.3.2", "react-test-renderer": "^19.2.0", "ts-node": "^10.9.1", - "typescript": "^5.0.4", + "typescript": "^6.0.3", "yaml-loader": "^0.9.0" }, "browserslist": { diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 79f39b3d..26572294 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -6,9 +6,12 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "jsx": "react-jsx", - "lib": ["dom", "es2017"], + "lib": [ + "dom", + "es2017" + ], "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "noEmit": true, "noFallthroughCasesInSwitch": true, "noUnusedLocals": true, @@ -20,6 +23,13 @@ "incremental": true // "verbatimModuleSyntax": true }, - "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] } diff --git a/frontend/yarn.lock b/frontend/yarn.lock index c04db8ff..2b5e60d0 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -9,7 +9,7 @@ dependencies: "@ant-design/fast-color" "^2.0.6" -"@ant-design/colors@^8.0.0", "@ant-design/colors@^8.0.1": +"@ant-design/colors@^8.0.1": version "8.0.1" resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-8.0.1.tgz#6b5444f2ab4061c7b1aa4bc776adb023b0253161" integrity sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ== @@ -63,7 +63,7 @@ resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-3.0.1.tgz#fee56b95427c0b55b216c93d9a7f3473f31615b5" integrity sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw== -"@ant-design/icons-svg@^4.4.0": +"@ant-design/icons-svg@^4.4.0", "@ant-design/icons-svg@^4.4.2": version "4.4.2" resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz#ed2be7fb4d82ac7e1d45a54a5b06d6cecf8be6f6" integrity sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA== @@ -79,14 +79,14 @@ classnames "^2.2.6" rc-util "^5.31.1" -"@ant-design/icons@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.1.0.tgz#97cc14a3c0528b8e2b37f41f232b019f2ca38c2c" - integrity sha512-KrWMu1fIg3w/1F2zfn+JlfNDU8dDqILfA5Tg85iqs1lf8ooyGlbkA+TkwfOKKgqpUmAiRY1PTFpuOU2DAIgSUg== +"@ant-design/icons@^6.1.1": + version "6.2.2" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.2.tgz#ed33a140ff35d7bcb6e48f01a6970ea6939cef99" + integrity sha512-zlJtE7AMbG12TeYVPhtBXwNpFInNy8mjLzcIm+0BPw16/b8ODG87YJ1G37VIF5VFscdgfsf6EweAFPTobu/3iQ== dependencies: - "@ant-design/colors" "^8.0.0" - "@ant-design/icons-svg" "^4.4.0" - "@rc-component/util" "^1.3.0" + "@ant-design/colors" "^8.0.1" + "@ant-design/icons-svg" "^4.4.2" + "@rc-component/util" "^1.10.1" clsx "^2.1.1" "@ant-design/pro-layout@^7.10.3": @@ -149,9 +149,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^4.0.9": - version "4.1.6" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.1.6.tgz#6eff1cfc09b1a2ef15f4f46471e6767958e109f7" - integrity sha512-ak8uzqmKeX3u9BziGf83RRyODAJKFkPG72hTNvEj4WjMWFmuKW2gGN1i3OfajKT6yuGjvo+n23ES2zqWDKFCZg== + version "4.1.9" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.1.9.tgz#797186c27ad98f728e40a7e33b7539675d1509b7" + integrity sha512-qfpkQD51tdU/7iAR6aLb4w9o/L7I475DluWHRb61U/3Q0AH29nNOxOBHjBbWDdf16ncPOoQuxne1sEs2NjqBFw== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -161,12 +161,12 @@ optimism "^0.18.0" tslib "^2.3.0" -"@ardatan/relay-compiler@^13.0.0": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-13.0.0.tgz#20038e20b0ce751947d8e142b6b45e322d90f2bd" - integrity sha512-ite4+xng5McO8MflWCi0un0YmnorTujsDnfPfhzYzAgoJ+jkI1pZj6jtmTl8Jptyi1H+Pa0zlatJIsxDD++ETA== +"@ardatan/relay-compiler@^13.0.1": + version "13.0.1" + resolved "https://registry.yarnpkg.com/@ardatan/relay-compiler/-/relay-compiler-13.0.1.tgz#b00a4c1f986156fa2472db8036e9087877d37be3" + integrity sha512-afG3YPwuSA0E5foouZusz5GlXKs74dObv4cuWyLyfKsYFj2r7oGRNB28v18HvwuLSQtQFCi+DpIe0TZkgQDYyg== dependencies: - "@babel/runtime" "^7.26.10" + "@babel/runtime" "^7.29.2" immutable "^5.1.5" invariant "^2.2.4" @@ -184,7 +184,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== -"@babel/core@^7.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.26.10", "@babel/core@^7.27.4": +"@babel/core@^7.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4", "@babel/core@^7.28.6": version "7.29.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== @@ -277,7 +277,7 @@ "@babel/template" "^7.28.6" "@babel/types" "^7.29.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.26.10", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0", "@babel/parser@^7.29.2": version "7.29.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== @@ -410,7 +410,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.28.6" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.26.10", "@babel/runtime@^7.28.4": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.28.4", "@babel/runtime@^7.29.2": version "7.29.2" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== @@ -463,24 +463,24 @@ integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== "@emnapi/core@^1.4.3": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.9.1.tgz#2143069c744ca2442074f8078462e51edd63c7bd" - integrity sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467" + integrity sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw== dependencies: - "@emnapi/wasi-threads" "1.2.0" + "@emnapi/wasi-threads" "1.2.1" tslib "^2.4.0" "@emnapi/runtime@^1.4.3", "@emnapi/runtime@^1.7.0": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.9.1.tgz#115ff2a0d589865be6bd8e9d701e499c473f2a8d" - integrity sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA== + version "1.10.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c" + integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA== dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz#a19d9772cc3d195370bf6e2a805eec40aa75e18e" - integrity sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg== +"@emnapi/wasi-threads@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz#28fed21a1ba1ce797c44a070abc94d42f3ae8548" + integrity sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w== dependencies: tslib "^2.4.0" @@ -532,40 +532,40 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== -"@eslint/config-array@^0.23.3": - version "0.23.3" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.3.tgz#3f4a93dd546169c09130cbd10f2415b13a20a219" - integrity sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw== +"@eslint/config-array@^0.23.5": + version "0.23.5" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.23.5.tgz#56e86d243049195d8acc0c06a1b3dfdc3fa3de95" + integrity sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA== dependencies: - "@eslint/object-schema" "^3.0.3" + "@eslint/object-schema" "^3.0.5" debug "^4.3.1" minimatch "^10.2.4" -"@eslint/config-helpers@^0.5.3": - version "0.5.3" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.3.tgz#721fe6bbb90d74b0c80d6ff2428e5bbcb002becb" - integrity sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw== +"@eslint/config-helpers@^0.5.5": + version "0.5.5" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz#ae16134e4792ac5fbdc533548a24ac1ea9f7f3ae" + integrity sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w== dependencies: - "@eslint/core" "^1.1.1" + "@eslint/core" "^1.2.1" -"@eslint/core@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.1.1.tgz#450f3d2be2d463ccd51119544092256b4e88df32" - integrity sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ== +"@eslint/core@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-1.2.1.tgz#c1da7cd1b82fa8787f98b5629fb811848a1b63ce" + integrity sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ== dependencies: "@types/json-schema" "^7.0.15" -"@eslint/object-schema@^3.0.3": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.3.tgz#5bf671e52e382e4adc47a9906f2699374637db6b" - integrity sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ== +"@eslint/object-schema@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091" + integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw== -"@eslint/plugin-kit@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz#eb9e6689b56ce8bc1855bb33090e63f3fc115e8e" - integrity sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ== +"@eslint/plugin-kit@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz#c4125fd015eceeb09b793109fdbcd4dd0a02d346" + integrity sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ== dependencies: - "@eslint/core" "^1.1.1" + "@eslint/core" "^1.2.1" levn "^0.4.1" "@fastify/busboy@^3.1.1": @@ -573,25 +573,25 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== -"@graphql-codegen/add@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.0.tgz#d195051d7c4bd6d3955a92361cc9eec7784bb541" - integrity sha512-biFdaURX0KTwEJPQ1wkT6BRgNasqgQ5KbCI1a3zwtLtO7XTo7/vKITPylmiU27K5DSOWYnY/1jfSqUAEBuhZrQ== +"@graphql-codegen/add@^6.0.0", "@graphql-codegen/add@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.1.tgz#90f8750e1074975bef1ead05f82523e32766c61a" + integrity sha512-MSylSekjpVWbOBw2A/2ssk1fPY54sYb6Qk2C4AX5u7s2R+2pMQ9ws7DTXo8VU9qwTgWwVp6vGfdQ0AMpAn4Iug== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - tslib "~2.6.0" + "@graphql-codegen/plugin-helpers" "^6.3.0" + tslib "^2.8.0" "@graphql-codegen/cli@^6.0.2": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.2.1.tgz#c9f4e6ee180bafa433cd635d5e30d27ed9c0c900" - integrity sha512-E1B+5nBda2l89Pci5M0HcEj2Hmx2yhORFX+1T3rmwpQjdOiulo+h9JifWxKomUpjfbmU1YkBSd47CCGLFPU10A== + version "6.3.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.3.1.tgz#f64f257cf81ffaf2e6241eff64c6de105e74dab2" + integrity sha512-I5KkyX1SgQZPojMeQTRydB6fml4cysZq/mIdhNW4rmqdoOcTgdMPq1Tl+wtRp1VpBAOrBazJUJh1nAqJMMSPIQ== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/client-preset" "^5.2.4" - "@graphql-codegen/core" "^5.0.1" - "@graphql-codegen/plugin-helpers" "^6.2.0" + "@graphql-codegen/client-preset" "^5.3.0" + "@graphql-codegen/core" "^5.0.2" + "@graphql-codegen/plugin-helpers" "^6.3.0" "@graphql-tools/apollo-engine-loader" "^8.0.28" "@graphql-tools/code-file-loader" "^8.1.28" "@graphql-tools/git-loader" "^8.0.32" @@ -622,126 +622,125 @@ yaml "^2.3.1" yargs "^17.0.0" -"@graphql-codegen/client-preset@^5.2.4": - version "5.2.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.2.4.tgz#f5096cfd19d8726a6282ec07640c7e5411196755" - integrity sha512-k4f9CoepkVznXRReCHBVnG/FeQVQgIOhgtkaJ6I9FcQRzUkrm9ASmQjOdNdMlZt0DHTU4nbVxIBGZW7gk1RavA== +"@graphql-codegen/client-preset@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.3.0.tgz#e479c9d03500ba9e30f699c8644196e9759877df" + integrity sha512-K9FON+j7qyxAUDuSGqI3ofb7lWTBs16oPTYpu14lhdL4DKZQSHLyc8EMYU9e3KcyQ/13gU/d6culOppzAuexLA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" - "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/gql-tag-operations" "5.1.4" - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/typed-document-node" "^6.1.7" - "@graphql-codegen/typescript" "^5.0.9" - "@graphql-codegen/typescript-operations" "^5.0.9" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/add" "^6.0.1" + "@graphql-codegen/gql-tag-operations" "5.2.0" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/typed-document-node" "^6.1.8" + "@graphql-codegen/typescript" "^5.0.10" + "@graphql-codegen/typescript-operations" "^5.1.0" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" "@graphql-tools/documents" "^1.0.0" "@graphql-tools/utils" "^11.0.0" "@graphql-typed-document-node/core" "3.2.0" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/core@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.1.tgz#0b34d222e30a2dd97b42c2ffbec4087800a883dc" - integrity sha512-eQD7aXpKkKvaydMv5Bu0FnKCPnNMAhZ3vZW+K4Rl9IAC2w5PDv9lJhs3YTWM9W58zNOZpGQGT2F0ekS3QNIiKw== +"@graphql-codegen/core@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.2.tgz#6f64fd4ba7bf45a7a03229ed520609c50e048186" + integrity sha512-7RX0wwjoWPlLG/tUmpaTK91ZZqHcACNWpRL0nGnnJaJrORie9pgmX8JPrcwBgYiHSC+3ERo9xY91RFPem/VrpQ== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/plugin-helpers" "^6.3.0" "@graphql-tools/schema" "^10.0.0" "@graphql-tools/utils" "^11.0.0" - tslib "~2.6.0" + tslib "^2.8.0" "@graphql-codegen/fragment-matcher@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.0.tgz#4beb848c0fdb38bbdc7b99250de84ec4456e7a5d" - integrity sha512-lXdFWrUSEbOL2KhmYxdeNPLllcBGZnNunwgV72C12+21v9lyKaOqFnYueyt0pT110MGCwUhKTnXzUtV1D5i9Jw== + version "6.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.1.tgz#4daff16f982810a1ac770d7fe7a8a3200b11254e" + integrity sha512-gM984SjSalbvHbvxqTI8MNy2wkoiuaLLlif4/1xDbFF0E2yIVzWknbSFwVyhjDJ+HjlZvSt+uH7BQv31Szhgsw== dependencies: - "@graphql-codegen/plugin-helpers" "^6.0.0" - tslib "~2.6.0" + "@graphql-codegen/plugin-helpers" "^6.3.0" + tslib "^2.8.0" -"@graphql-codegen/gql-tag-operations@5.1.4": - version "5.1.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.1.4.tgz#4714f2475868b43015420aba05a9dc04868a7d5e" - integrity sha512-tDj/0a1U7rDH3PQgLeA+PlgBNb593MIJ43oAOKMRgJPwIQ9T7p2oqBRLxwfFZFTDLwnwsGZ7xIKqIcGgyAIj5Q== +"@graphql-codegen/gql-tag-operations@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.2.0.tgz#e8f7c8c5adcabb54e97548db8b0973f6602be0be" + integrity sha512-B9gtJ4ziqpIv+7mHqwjtpYLFOuv0GmmRGpNDoWKM2VIx4OQqgI84d6OHKYCVeO7yu3mUr0QPvUgkSyuLVrdukA== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" "@graphql-tools/utils" "^11.0.0" auto-bind "~4.0.0" - tslib "~2.6.0" + tslib "^2.8.0" "@graphql-codegen/near-operation-file-preset@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-5.0.0.tgz#00cf43ce61eba094f82751f3b1709b7a9b9857b9" - integrity sha512-Ck5jMu2izbKFwODYCM/sGWNQKN4BN4fpxr8g99fs6ESH29yfWMjA1dbUlTbtQtR7iXy39jT8451BLTG4/vOIxQ== + version "5.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-5.1.0.tgz#a5ec0555951800da280a292671da17d10f26ba29" + integrity sha512-tME5X7OrrkYUr9CeW4rHrl6lUxrojNNXwN4yibdnmSP1w93Pj/p4kogKmnacd5Sns05Q82cucswuTQTQmVENKA== dependencies: "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" "@graphql-tools/utils" "^11.0.0" parse-filepath "^1.0.2" tslib "^2.8.1" -"@graphql-codegen/plugin-helpers@^6.0.0", "@graphql-codegen/plugin-helpers@^6.1.1", "@graphql-codegen/plugin-helpers@^6.2.0": - version "6.2.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.2.0.tgz#d10a56939c9341522e30c23aa2a83b10eafcd0c0" - integrity sha512-TKm0Q0+wRlg354Qt3PyXc+sy6dCKxmNofBsgmHoFZNVHtzMQSSgNT+rUWdwBwObQ9bFHiUVsDIv8QqxKMiKmpw== +"@graphql-codegen/plugin-helpers@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.3.0.tgz#d4ba4660c68cb703291fbc6616a6510df26dd5f3" + integrity sha512-Auc+/B7okDx9+pVgLVliZtZLYh6iltWXlnzzM+bRE+zh1T4r3hKbnr8xAmtT937ArfSgk5GHcQHr8LfPYnrRBg== dependencies: "@graphql-tools/utils" "^11.0.0" change-case-all "1.0.15" common-tags "1.8.2" import-from "4.0.0" - lodash "~4.17.0" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/schema-ast@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.1.tgz#01034d85460179f552ac495853539eb318d93732" - integrity sha512-svLffXddnXxq1qFXQqqh+zYrxdiMnIKm+CXCUv0MYhLh0R4L5vpnaTzIUCk3icHNNXhKRm2uBD70+K8VY0xiCg== +"@graphql-codegen/schema-ast@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.2.tgz#0d7fd83760fa10fea6804f75c801e933d832ac33" + integrity sha512-jl1F/9IjRkJisEb9B0ayG4QGqYlPldLRy8ojDdmL9NE1NsdB5ROfxQnSqyC3g+wuvBhWX7kZgMRQYn3RU1I5bA== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/plugin-helpers" "^6.3.0" "@graphql-tools/utils" "^11.0.0" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/typed-document-node@^6.1.7": - version "6.1.7" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.7.tgz#cdb3bbc6a5c430d16fd23be60ce2734cfb73a901" - integrity sha512-VLL9hB+YPigc/W2QYCkSNMZrkKv42nTchb9mJ0h5VY98YmW/zWb6NeYM80iHSpk8ZvHsuUT5geA53/s1phO2NQ== +"@graphql-codegen/typed-document-node@^6.1.8": + version "6.1.8" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.8.tgz#a0190940feef2b3b16e0f04e55b64a7d92837a2e" + integrity sha512-+qDdiJSQ7Ol+vpLMAH8ZJok50CvlYxA6seQ7cwEa3emXt8MmH5hh3zdc9unQlPc7bynoJHRCgoKk7E0B7hry0w== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" auto-bind "~4.0.0" change-case-all "1.0.15" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.0.9": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.0.9.tgz#95388b2c80434118d5dd102ef5eff7f0de4ca29c" - integrity sha512-jJFdJKMS5Cqisb5QMi7xXHPsJH9yHBMYOxBc8laFkFjHk/AOqJK90qCKbO9lwwTMPZUDe6N/HslmA0ax4J0zsg== +"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.1.0.tgz#8e30fc79999bf913e53836670d6046b881567e77" + integrity sha512-JlmjbFl0EnsfMDIYvTE1Q0kAOrntVEZ+ZfBqWTP91g4e0F/TzuwJ/V4tiFmeDf5dx/rf9AK4VkPehIdxu7TYhw== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/typescript" "^5.0.9" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/typescript" "^5.0.10" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" auto-bind "~4.0.0" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/typescript@^5.0.4", "@graphql-codegen/typescript@^5.0.9": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.9.tgz#967c3fdd11c79f7adbd612c994367f3b38272435" - integrity sha512-YlIZ4nqdFdzr5vxuNtQtZnnMYuZ5cLYB2HaGhGI2zvqHxCmkBjIRpu/5sfccawKy23wetV+aoWvoNqxGKIryig== +"@graphql-codegen/typescript@^5.0.10", "@graphql-codegen/typescript@^5.0.4": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.10.tgz#5991746408152503069b0c36bfdf5456b68a2e2a" + integrity sha512-Pa8OFmL9TdhEYnLYJLYA9EhP8eEeivP/YDYq4Nb8LQaL7GXm4TGX8zELYaCM9Fu8M3iZb7iQGMt7qc+1lXz8XQ== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" - "@graphql-codegen/schema-ast" "^5.0.1" - "@graphql-codegen/visitor-plugin-common" "^6.2.4" + "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/schema-ast" "^5.0.2" + "@graphql-codegen/visitor-plugin-common" "^6.3.0" auto-bind "~4.0.0" - tslib "~2.6.0" + tslib "^2.8.0" -"@graphql-codegen/visitor-plugin-common@^6.2.4": - version "6.2.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.2.4.tgz#73906f34e8baaaadf5bfe5fefece4d86f835aebc" - integrity sha512-iwiVCc7Mv8/XAa3K35AdFQ9chJSDv/gYEnBeQFF/Sq/W8EyJoHypOGOTTLk7OSrWO4xea65ggv0e7fGt7rPJjQ== +"@graphql-codegen/visitor-plugin-common@^6.3.0": + version "6.3.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.3.0.tgz#5c3d1b6397acd27a552beab989c5d8af155e2aef" + integrity sha512-vGBoE+4huzZyNhyGSAhXAkdROHlwKxxuziZm4XtP1mxe7nuI+VgyOmXebafLijbmuDsptPXQN0C/htL54O8hrg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.1.1" + "@graphql-codegen/plugin-helpers" "^6.3.0" "@graphql-tools/optimize" "^2.0.0" "@graphql-tools/relay-operation-optimizer" "^7.1.1" "@graphql-tools/utils" "^11.0.0" @@ -750,7 +749,7 @@ dependency-graph "^1.0.0" graphql-tag "^2.11.0" parse-filepath "^1.0.2" - tslib "~2.6.0" + tslib "^2.8.0" "@graphql-hive/signal@^2.0.0": version "2.0.0" @@ -758,19 +757,19 @@ integrity sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ== "@graphql-tools/apollo-engine-loader@^8.0.28": - version "8.0.28" - resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.28.tgz#b69334ea21540faee316f0026a53b3889dd90a01" - integrity sha512-MzgDrUuoxp6dZeo54zLBL3cEJKJtM3N/2RqK0rbPxPq5X2z6TUA7EGg8vIFTUkt5xelAsUrm8/4ai41ZDdxOng== + version "8.0.30" + resolved "https://registry.yarnpkg.com/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.30.tgz#f7440938acfb0fa00a7e8ffe9d39d3df24d3724c" + integrity sha512-hUydKGGECrWloERMmfoMzHZi12X99AM9geCGF5XVsv4iMRl/Iyuet24th4kC9bZ8MlAdCwAwtUsCyv9uRfYwSA== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" "@whatwg-node/fetch" "^0.10.13" sync-fetch "0.6.0" tslib "^2.4.0" -"@graphql-tools/batch-execute@^10.0.7": - version "10.0.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.7.tgz#20a5b2c9cec281e38d6ed13e8d8f0163aa88d947" - integrity sha512-vKo9XUiy2sc5tzMupXoxZbu5afVY/9yJ0+yLrM5Dhh38yHYULf3z9VC1eAwW0kj8pWpOo8d8CV3jpleGwv83PA== +"@graphql-tools/batch-execute@^10.0.8": + version "10.0.8" + resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-10.0.8.tgz#11f3967dc2df25c149a5dc7c495fd9363f63388e" + integrity sha512-Kobt37qrVTFhX4HUK5/vPgMXFw/5f97AzmAlfmDBSRh/GnoAmLKCb48FrEI3gdeIwZB2fEhVHJyDqsojldnLQA== dependencies: "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" @@ -778,22 +777,22 @@ tslib "^2.8.1" "@graphql-tools/code-file-loader@^8.1.28": - version "8.1.28" - resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.28.tgz#c90655d86210d7ad8489f452867754b49c89f57a" - integrity sha512-BL3Ft/PFlXDE5nNuqA36hYci7Cx+8bDrPDc8X3VSpZy9iKFBY+oQ+IwqnEHCkt8OSp2n2V0gqTg4u3fcQP1Kwg== + version "8.1.32" + resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-8.1.32.tgz#6bfc8091fb35692174824f1e714586725e2a3bff" + integrity sha512-gR5mNQjn0BugDL8a4A+ovS2KEvU52RNOGnbwiq9oWAEHiSv7iqJu77bpWARTzlE1ZFPK5MSQe9218+1t5PbXmQ== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.27" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/graphql-tag-pluck" "8.3.31" + "@graphql-tools/utils" "^11.1.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^12.0.12": - version "12.0.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.12.tgz#19d669b3cb7ebe360848ae55f42f4b9769d5bd71" - integrity sha512-/vgLWhIwm+Mgo5VUOJQj6EOpaxXRQmA7mk8j6/8vBbPi56LoYA/UPRygcpEnm9EuXTspFKCTBil+xqThU3EmqQ== +"@graphql-tools/delegate@^12.0.14": + version "12.0.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.14.tgz#982f209aa582fd3ed9dfd54fec562812785eeb33" + integrity sha512-/xCDM8zlCk1Lccww9asOIpxna9IFpIlol4yGsBD9Y2+3/Zu5k4/HzDC8LKJtw5MxdG+uJN1l9nRepr4GeBC4kA== dependencies: - "@graphql-tools/batch-execute" "^10.0.7" + "@graphql-tools/batch-execute" "^10.0.8" "@graphql-tools/executor" "^1.4.13" "@graphql-tools/schema" "^10.0.29" "@graphql-tools/utils" "^11.0.0" @@ -818,7 +817,7 @@ "@envelop/core" "^5.4.0" "@graphql-tools/utils" "^11.0.0" -"@graphql-tools/executor-graphql-ws@^3.1.2": +"@graphql-tools/executor-graphql-ws@^3.1.4": version "3.1.5" resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-3.1.5.tgz#5d4ecc7a406126ae77489a0d1129ed7ccd9e5331" integrity sha512-WXRsfwu9AkrORD9nShrd61OwwxeQ5+eXYcABRR3XPONFIS8pWQfDJGGqxql9/227o/s0DV5SIfkBURb5Knzv+A== @@ -831,10 +830,10 @@ tslib "^2.8.1" ws "^8.18.3" -"@graphql-tools/executor-http@^3.0.6": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.1.1.tgz#2952fec496178b4b0c994cc3ff448bc005401b29" - integrity sha512-Le57fMdN7nGBp8XddkpGreCzcPGCZ+uHs1kPw/xMKPJMsn/vZUHbWJ0FY0cb0jfE3OVhbMIjjSe/OHlG3Mm3zw== +"@graphql-tools/executor-http@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.2.1.tgz#28abc8d521b187a13b46b3dae5dba885096350e5" + integrity sha512-53i0TYO0cznIlZDJcnq4gQ6SOZ8efGgCDV33MYh6oqEapcp36tCMEVnVGVxcX5qRRyNHkqTY6hkA+/AyK9kicQ== dependencies: "@graphql-hive/signal" "^2.0.0" "@graphql-tools/executor-common" "^1.0.6" @@ -846,23 +845,23 @@ meros "^1.3.2" tslib "^2.8.1" -"@graphql-tools/executor-legacy-ws@^1.1.25": - version "1.1.25" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.25.tgz#235c334443a4dba79ceff9c20b0a03e766743d4c" - integrity sha512-6uf4AEXO0QMxJ7AWKVPqEZXgYBJaiz5vf29X0boG8QtcqWy8mqkXKWLND2Swdx0SbEx0efoGFcjuKufUcB0ASQ== +"@graphql-tools/executor-legacy-ws@^1.1.28": + version "1.1.28" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.28.tgz#e640359a0c65af59609989510ba47849fa3e6680" + integrity sha512-O4uj93GG9iUb3s32eyhUohvyfA8mLhN8FvGzEdK628hFQPhZN75yurtVFrR08DHex71mQ3wYCCFkErpwdJbDDQ== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" "@types/ws" "^8.0.0" isomorphic-ws "^5.0.0" tslib "^2.4.0" - ws "^8.19.0" + ws "^8.20.0" "@graphql-tools/executor@^1.4.13": - version "1.5.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.5.1.tgz#2171473cc7cd0b7ca468c21d767eec9ac9df00fe" - integrity sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA== + version "1.5.3" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.5.3.tgz#3c567ec4854fe8ed69eb6a890e41db7bdae2b0bc" + integrity sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" "@graphql-typed-document-node/core" "^3.2.0" "@repeaterjs/repeater" "^3.0.4" "@whatwg-node/disposablestack" "^0.0.6" @@ -870,89 +869,89 @@ tslib "^2.4.0" "@graphql-tools/git-loader@^8.0.32": - version "8.0.32" - resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.32.tgz#b72f8830aacf26a10044c39c3a3e512d8f23e8c2" - integrity sha512-H5HTp2vevv0rRMEnCJBVmVF8md3LpJI1C1+d6OtzvmuONJ8mOX2mkf9rtoqwiztynVegaDUekvMFsc9k5iE2WA== + version "8.0.36" + resolved "https://registry.yarnpkg.com/@graphql-tools/git-loader/-/git-loader-8.0.36.tgz#f3e2a8da8c65e086f176146616d24f16e36ce9fe" + integrity sha512-PDDakesRu8FJYHJLf9/gkTweh8M19Bymz9i+vOlk9OTs9XmNcCqKM+1S610KX2AodvuBFz/xbesjTtTJIppLPg== dependencies: - "@graphql-tools/graphql-tag-pluck" "8.3.27" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/graphql-tag-pluck" "8.3.31" + "@graphql-tools/utils" "^11.1.0" is-glob "4.0.3" micromatch "^4.0.8" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/github-loader@^9.0.6": - version "9.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.0.6.tgz#75353e71c431cae3ac04751b804b5122c1704556" - integrity sha512-hhlt2MMkRcvDva/qyzqFddXzaMmRnriJ0Ts+/LcNeYnB8hcEqRMpF9RCsHYjo1mFRaiu8i4PSIpXyyFu3To7Ow== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/github-loader/-/github-loader-9.1.2.tgz#58346bcdf175bf44849a6280e1e9a7e022cb5eb1" + integrity sha512-jhRJncj9Wkr1Cd8Mo3QI2oG6fTw5ILr1/OXcHIqx744NBj8pPwQBXmQzZqh7MXxbekl2EAcum7SJIjq1HpYcPA== dependencies: - "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/graphql-tag-pluck" "^8.3.27" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/executor-http" "^3.2.1" + "@graphql-tools/graphql-tag-pluck" "^8.3.31" + "@graphql-tools/utils" "^11.1.0" "@whatwg-node/fetch" "^0.10.13" "@whatwg-node/promise-helpers" "^1.0.0" sync-fetch "0.6.0" tslib "^2.4.0" "@graphql-tools/graphql-file-loader@^8.0.0", "@graphql-tools/graphql-file-loader@^8.1.11": - version "8.1.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.12.tgz#9a0f3f4a27bfd849a90d5b2297f64f60e54a949c" - integrity sha512-Nma7gBgJoUbqXWTmdHjouo36tjzewA8MptVcHoH7widzkciaUVzBhriHzqICFB/dVxig//g9MX8s1XawZo7UAg== + version "8.1.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.1.14.tgz#cc69e84e1566637052d401a4d3f117c67277bfc5" + integrity sha512-CfAcsSEVkkHfEXLFzrd5rUYpcQEGWNV8lfc1Tb1p5m9HnYICzDDH08I5V33iMrEDza3GuujjjRBYqplBkqwIow== dependencies: - "@graphql-tools/import" "^7.1.12" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/import" "^7.1.14" + "@graphql-tools/utils" "^11.1.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/graphql-tag-pluck@8.3.27", "@graphql-tools/graphql-tag-pluck@^8.3.27": - version "8.3.27" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.27.tgz#5f138347addfa3df5c169be69dbe41c902c6227f" - integrity sha512-CJ0WVXhGYsfFngpRrAAcjRHyxSDHx4dEz2W15bkwvt9he/AWhuyXm07wuGcoLrl0q0iQp1BiRjU7D8SxWZo3JQ== +"@graphql-tools/graphql-tag-pluck@8.3.31", "@graphql-tools/graphql-tag-pluck@^8.3.31": + version "8.3.31" + resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.31.tgz#3ea68b94e62b6c4cc68f612ec10068b07c01cdbd" + integrity sha512-ema2RRPZGj8TKruNElyDBHVCNFMxioGIVfLBuiA+GdfmRGt95b/i7Uksnj4EwItA6MCmhxokxZoa/fl6mJt3tw== dependencies: - "@babel/core" "^7.26.10" - "@babel/parser" "^7.26.10" + "@babel/core" "^7.28.6" + "@babel/parser" "^7.29.2" "@babel/plugin-syntax-import-assertions" "^7.26.0" "@babel/traverse" "^7.26.10" "@babel/types" "^7.26.10" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" tslib "^2.4.0" -"@graphql-tools/import@^7.1.12": - version "7.1.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.12.tgz#a6b04c0c53b58cccb132950913e55b1f31f0f30a" - integrity sha512-QSsdPsdJ7yCgQ5XODyKYpC7NlB9R1Koi0R3418PT7GiRm+9O8gYXSs/23dumcOnpiLrnf4qR2aytBn1+JOAhnA== +"@graphql-tools/import@^7.1.14": + version "7.1.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-7.1.14.tgz#33e435ae5afc0e19ec1baed7e74066e71455dbbf" + integrity sha512-aqLcu04aEidszbXM6M0PWWL8bP17eX9sxXwjYWpglLvIRd4NFqb3C9QzBY8pleqXNMtWqXktlm9BQjevgSrirQ== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" resolve-from "5.0.0" tslib "^2.4.0" "@graphql-tools/json-file-loader@^8.0.0", "@graphql-tools/json-file-loader@^8.0.26": - version "8.0.26" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.26.tgz#82ff8afbecb77082625c7ee76384da7aed0f70ce" - integrity sha512-kwy9IFi5QtXXTLBgWkvA1RqsZeJDn0CxsTbhNlziCzmga9fNo7qtZ18k9FYIq3EIoQQlok+b7W7yeyJATA2xhw== + version "8.0.28" + resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-8.0.28.tgz#6075780add2715c7b0126ecf3b9b4585758aaa5a" + integrity sha512-qgCsSkPArnjlNkcYpgGKiXxCTNkrAT9E+l1LhR+Por2jTlKBBeZ8stortkQ/PNDDjuL0WPrLQmHKhNPHabnB3A== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" globby "^11.0.3" tslib "^2.4.0" unixify "^1.0.0" "@graphql-tools/load@^8.1.0", "@graphql-tools/load@^8.1.8": - version "8.1.8" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.8.tgz#977fdf0005078283942e0e4a028ff97c78dd6f0b" - integrity sha512-gxO662b64qZSToK3N6XUxWG5E6HOUjlg5jEnmGvD4bMtGJ0HwEe/BaVZbBQemCfLkxYjwRIBiVfOY9o0JyjZJg== + version "8.1.10" + resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-8.1.10.tgz#23d14a3028b66090d51102c7e7c6fa72f70901eb" + integrity sha512-hjcvfEFtwtc8vGi46wtpmGWadNzfEhzbjqinyFIZuIZPlR4aYdWQtqWtY/RMM4Ew4t1USkMNm6xrqC2TH1vCSA== dependencies: - "@graphql-tools/schema" "^10.0.31" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/schema" "^10.0.33" + "@graphql-tools/utils" "^11.1.0" p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.0.6", "@graphql-tools/merge@^9.1.7": - version "9.1.7" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.7.tgz#8040553c19aeadf6df41060b811e9c5df8716be1" - integrity sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ== +"@graphql-tools/merge@^9.0.0", "@graphql-tools/merge@^9.0.6", "@graphql-tools/merge@^9.1.9": + version "9.1.9" + resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-9.1.9.tgz#d5b042ef11695ca130a36477e25786100353609b" + integrity sha512-iHUWNjRHeQRYdgIMIuChThOwoKzA9vrzYeslgfBo5eUYEyHGZCoDPjAavssoYXLwstYt1dZj2J22jSzc2DrN0Q== dependencies: - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/utils" "^11.1.0" tslib "^2.4.0" "@graphql-tools/optimize@^2.0.0": @@ -963,32 +962,32 @@ tslib "^2.4.0" "@graphql-tools/relay-operation-optimizer@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.1.1.tgz#5294ffc075dcdf0c2ffcd1f02339374cefba51b8" - integrity sha512-va+ZieMlz6Fj18xUbwyQkZ34PsnzIdPT6Ccy1BNOQw1iclQwk52HejLMZeE/4fH+4cu80Q2HXi5+FjCKpmnJCg== + version "7.1.4" + resolved "https://registry.yarnpkg.com/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.1.4.tgz#6e25416d78d7bc8d92c64df89d39d30644c26042" + integrity sha512-cwOD/GEo/R//1uGCP0/urIxsMFoUgzkJVyMt9BDM2HhQhU6rSgH5l6lFukAFTJyPJVdyeOdYm2i0Jj5vYWbHTw== dependencies: - "@ardatan/relay-compiler" "^13.0.0" - "@graphql-tools/utils" "^11.0.0" + "@ardatan/relay-compiler" "^13.0.1" + "@graphql-tools/utils" "^11.1.0" tslib "^2.4.0" -"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.29", "@graphql-tools/schema@^10.0.31": - version "10.0.31" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.31.tgz#dea275db4d7423f293826cff2aa11e7b3027ddce" - integrity sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ== +"@graphql-tools/schema@^10.0.0", "@graphql-tools/schema@^10.0.29", "@graphql-tools/schema@^10.0.33": + version "10.0.33" + resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-10.0.33.tgz#147d70aa9bb12123ffca2c091be1f3f3ec592911" + integrity sha512-O6P3RIftO0jafnSsFAqpjurUuUxJ43s/AdPVLQsBkI6y4Ic/tKm4C1Qm1KKQsCDTOxXPJClh/v3g7k7yLKCFBQ== dependencies: - "@graphql-tools/merge" "^9.1.7" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/merge" "^9.1.9" + "@graphql-tools/utils" "^11.1.0" tslib "^2.4.0" "@graphql-tools/url-loader@^9.0.0", "@graphql-tools/url-loader@^9.0.6": - version "9.0.6" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.0.6.tgz#281ad72428170a08167423071e2ee9a06d910f49" - integrity sha512-QdJI3f7ANDMYfYazRgJzzybznjOrQAOuDXweC9xmKgPZoTqNxEAsatiy69zcpTf6092taJLyrqRH6R7xUTzf4A== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-9.1.2.tgz#cb299cab94ae694503c6c7cb124a42278452fff5" + integrity sha512-pVSiPrfWQKb3jq23Pl7EjbB2uv3tgZLnWo/axkmg4itAEZ5s/vV/jKa8P1HZzUnSVUTR+8tcEZVeNsUbzFCbkg== dependencies: - "@graphql-tools/executor-graphql-ws" "^3.1.2" - "@graphql-tools/executor-http" "^3.0.6" - "@graphql-tools/executor-legacy-ws" "^1.1.25" - "@graphql-tools/utils" "^11.0.0" + "@graphql-tools/executor-graphql-ws" "^3.1.4" + "@graphql-tools/executor-http" "^3.2.1" + "@graphql-tools/executor-legacy-ws" "^1.1.28" + "@graphql-tools/utils" "^11.1.0" "@graphql-tools/wrap" "^11.1.1" "@types/ws" "^8.0.0" "@whatwg-node/fetch" "^0.10.13" @@ -996,12 +995,12 @@ isomorphic-ws "^5.0.0" sync-fetch "0.6.0" tslib "^2.4.0" - ws "^8.19.0" + ws "^8.20.0" -"@graphql-tools/utils@^11.0.0": - version "11.0.0" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-11.0.0.tgz#f54a09372e96c32416fcbd0eb8e9e0f1338900bd" - integrity sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA== +"@graphql-tools/utils@^11.0.0", "@graphql-tools/utils@^11.1.0": + version "11.1.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-11.1.0.tgz#03593c3ae1177e04a3620c688fd29df912aa094f" + integrity sha512-PtFVG4r8Z2LEBSaPYQMusBiB3o6kjLVJyjCLbnWem/SpSuM21v6LTmgpkXfYU1qpBV2UGsFyuEnSJInl8fR1Ag== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@whatwg-node/promise-helpers" "^1.0.0" @@ -1009,11 +1008,11 @@ tslib "^2.4.0" "@graphql-tools/wrap@^11.1.1": - version "11.1.12" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.12.tgz#2a26247c6d1953b32f5ae08899266ad2529af174" - integrity sha512-PJ0tuiGbEOOZAJk2/pTKyzMEbwBncPBfO7Z84tCPzM/CAR4ZlAXbXjaXOw4fdi0ReUDyOG06Z8DGgEQjr68dKw== + version "11.1.14" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.14.tgz#58d1854ab207676f241e9b405fda02556aec7d8a" + integrity sha512-ebSVT7apxr+88q3Wy0i4AyRmJ42I0SuMqjPIn1fqW14yCTQRZG8YLuIALG1gKR936+GkfMLOrADh6egJvdlN6Q== dependencies: - "@graphql-tools/delegate" "^12.0.12" + "@graphql-tools/delegate" "^12.0.14" "@graphql-tools/schema" "^10.0.29" "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" @@ -1024,19 +1023,27 @@ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== -"@humanfs/core@^0.19.1": - version "0.19.1" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" - integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== +"@humanfs/core@^0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.2.tgz#a8272ca03b2acf492670222b2320b6c421bfde60" + integrity sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA== + dependencies: + "@humanfs/types" "^0.15.0" "@humanfs/node@^0.16.6": - version "0.16.7" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" - integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== + version "0.16.8" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.8.tgz#8f800cccc13f4f8cd3116e2d9c0a94939da3e3ed" + integrity sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ== dependencies: - "@humanfs/core" "^0.19.1" + "@humanfs/core" "^0.19.2" + "@humanfs/types" "^0.15.0" "@humanwhocodes/retry" "^0.4.0" +"@humanfs/types@^0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@humanfs/types/-/types-0.15.0.tgz#f2a09f62012390b2bff3fc6fb248ddec8c09a090" + integrity sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q== + "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" @@ -1363,9 +1370,9 @@ resolve-from "^5.0.0" "@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + version "0.1.6" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.6.tgz#8dc9afa2ac1506cb1a58f89940f1c124446c8df3" + integrity sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw== "@jest/console@30.3.0": version "30.3.0" @@ -1636,57 +1643,57 @@ "@emnapi/runtime" "^1.4.3" "@tybys/wasm-util" "^0.10.0" -"@next/env@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.1.tgz#3896715e28c850355b7b1c9c687beb9d7e9cdc40" - integrity sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg== +"@next/env@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.4.tgz#b95793a9f235744c97fa805c99cb033a6ff51ece" + integrity sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw== -"@next/eslint-plugin-next@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.1.tgz#994717439ae3259268ed5f7d6830966f0fbb3c9d" - integrity sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA== +"@next/eslint-plugin-next@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.4.tgz#69564ea019d91a33c6ddedddc15d7e0385430b3d" + integrity sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.1.tgz#8bd5c16ee04eb5f07d4f3ca71a3d5270093a9de6" - integrity sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q== - -"@next/swc-darwin-x64@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.1.tgz#4c1a9134cd442e7fcd74bbe85ab283616ece06cb" - integrity sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA== - -"@next/swc-linux-arm64-gnu@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.1.tgz#ec08722d22551ea649872df907a8fee027ab1828" - integrity sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw== - -"@next/swc-linux-arm64-musl@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.1.tgz#4d6270f5be7905c1a3e4f1c4f9cf4b8c62331561" - integrity sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ== - -"@next/swc-linux-x64-gnu@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.1.tgz#2d55519ba822cd27d9d65ed45a0ace3562df1dcc" - integrity sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg== - -"@next/swc-linux-x64-musl@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.1.tgz#607b03c3a5bade2368beb4f5b4cac9c243333638" - integrity sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg== - -"@next/swc-win32-arm64-msvc@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.1.tgz#a96e776f37287b39e309e0850a8d8e2c6c749070" - integrity sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA== - -"@next/swc-win32-x64-msvc@16.2.1": - version "16.2.1" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.1.tgz#a299bf2b5688029429061d13492c57ccf947c9c5" - integrity sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg== +"@next/swc-darwin-arm64@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz#c80ad82f707b58e083fad460e9ba32ab3001ded8" + integrity sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A== + +"@next/swc-darwin-x64@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz#59a79f846130b36687ae348bc07b6b2fe4ed3da8" + integrity sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ== + +"@next/swc-linux-arm64-gnu@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz#886bcb7b164596f185724be96a2a753d5538bed8" + integrity sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ== + +"@next/swc-linux-arm64-musl@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz#9a5d1ba1416c059cec3831dcb9a14b79a7f1477e" + integrity sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg== + +"@next/swc-linux-x64-gnu@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz#2d35270e904909b38ec84d594706c2b8acfa19fa" + integrity sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ== + +"@next/swc-linux-x64-musl@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz#31c750a8cc4b350d2e839c95942361c8b35b9ac9" + integrity sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA== + +"@next/swc-win32-arm64-msvc@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz#eeb6e0d1f58b88b40dcdd8283689fa065b645a25" + integrity sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow== + +"@next/swc-win32-x64-msvc@16.2.4": + version "16.2.4" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz#8252d02b505be5025dee62628859761e0bb11597" + integrity sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1893,23 +1900,23 @@ "@rc-component/util" "^1.2.1" clsx "^2.1.1" -"@rc-component/form@~1.7.2": - version "1.7.2" - resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.7.2.tgz#5e9db42f7fb6a55abff5d4eb0c263ccce6473710" - integrity sha512-5C90rXH7aZvvvxB4M5ew+QxROvimdL/lqhSshR8NsyiR7HKOoGQYSitxdfENnH6/0KNFxEy2ranVe2LrTnHZIw== +"@rc-component/form@~1.8.1": + version "1.8.1" + resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.1.tgz#d811fb52df41bf72297938ebfe5cf4a4774588d4" + integrity sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ== dependencies: "@rc-component/async-validator" "^5.1.0" "@rc-component/util" "^1.6.2" clsx "^2.1.1" -"@rc-component/image@~1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@rc-component/image/-/image-1.6.0.tgz#4c3f66b07a1d7c50de23a7bf24660262161daf8d" - integrity sha512-tSfn2ZE/oP082g4QIOxeehkmgnXB7R+5AFj/lIFr4k7pEuxHBdyGIq9axoCY9qea8NN0DY6p4IB/F07tLqaT5A== +"@rc-component/image@~1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@rc-component/image/-/image-1.9.0.tgz#110785d735d20336afcdbac84e8fbfd059a7a44e" + integrity sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ== dependencies: "@rc-component/motion" "^1.0.0" "@rc-component/portal" "^2.1.2" - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.10.1" clsx "^2.1.1" "@rc-component/input-number@~1.6.2": @@ -1959,10 +1966,10 @@ dependencies: "@babel/runtime" "^7.18.0" -"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.1.tgz#1e56b06841ee677261251e6e69fedc8d73e65b22" - integrity sha512-Wo1mkd0tCcHtvYvpPOmlYJz546z16qlsiwaygmW7NPJpOZOF9GBjhGzdzZSsC2lEJ1IUkWLF4gMHlRA1aSA+Yw== +"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.2.tgz#bd96e0fd16ee9d98c1d9be14198f003e367d8feb" + integrity sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ== dependencies: "@rc-component/util" "^1.2.0" clsx "^2.1.1" @@ -1984,9 +1991,9 @@ clsx "^2.1.1" "@rc-component/overflow@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@rc-component/overflow/-/overflow-1.0.0.tgz#ade1cc336c66b45eb1631fdfad59ef84c8f90a89" - integrity sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@rc-component/overflow/-/overflow-1.0.1.tgz#b9f1100b1531555b1d6a6ba2963e116fd2f96779" + integrity sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA== dependencies: "@babel/runtime" "^7.11.1" "@rc-component/resize-observer" "^1.0.1" @@ -2043,10 +2050,10 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/resize-observer@^1.0.0", "@rc-component/resize-observer@^1.0.1", "@rc-component/resize-observer@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@rc-component/resize-observer/-/resize-observer-1.1.1.tgz#216d7edb5259bb7d2a732735f0a103328ac8ad80" - integrity sha512-NfXXMmiR+SmUuKE1NwJESzEUYUFWIDUn2uXpxCTOLwiRUUakd62DRNFjRJArgzyFW8S5rsL4aX5XlyIXyC/vRA== +"@rc-component/resize-observer@^1.0.0", "@rc-component/resize-observer@^1.0.1", "@rc-component/resize-observer@^1.1.1", "@rc-component/resize-observer@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz#5897e65d7fed5c6e768dcfd8bdec181a3309a98f" + integrity sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q== dependencies: "@rc-component/util" "^1.2.0" @@ -2060,7 +2067,7 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/select@~1.6.0", "@rc-component/select@~1.6.14": +"@rc-component/select@~1.6.0", "@rc-component/select@~1.6.15": version "1.6.15" resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.6.15.tgz#de2a3c8b020834cabd600b52de573a328c061eef" integrity sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g== @@ -2186,10 +2193,10 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/util@^1.1.0", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.6.2", "@rc-component/util@^1.7.0", "@rc-component/util@^1.8.1", "@rc-component/util@^1.9.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.10.0.tgz#faeebbe0510f998b6d5cee9da9176b6d613cac2c" - integrity sha512-aY9GLBuiUdpyfIUpAWSYer4Tu3mVaZCo5A0q9NtXcazT3MRiI3/WNHCR+DUn5VAtR6iRRf0ynCqQUcHli5UdYw== +"@rc-component/util@^1.1.0", "@rc-component/util@^1.10.1", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.6.2", "@rc-component/util@^1.7.0", "@rc-component/util@^1.8.1", "@rc-component/util@^1.9.0": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.10.1.tgz#213c84c77e8b2001095530d3b0dc47c49c34ffe3" + integrity sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng== dependencies: is-mobile "^5.0.0" react-is "^18.2.0" @@ -2215,9 +2222,9 @@ integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== "@sinclair/typebox@^0.34.0": - version "0.34.48" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.48.tgz#75b0ead87e59e1adbd6dccdc42bad4fddee73b59" - integrity sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA== + version "0.34.49" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.49.tgz#4f1369234f2ecf693866476c3b2e1b54d2a9d68e" + integrity sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A== "@sinonjs/commons@^3.0.1": version "3.0.1" @@ -2227,9 +2234,9 @@ type-detect "4.0.8" "@sinonjs/fake-timers@^15.0.0": - version "15.1.1" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz#e1a6f7171941aadcc31d2cea1744264d58b8b34c" - integrity sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw== + version "15.3.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz#afecc36681e26aab9e0fe809fd9ad578096a3058" + integrity sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw== dependencies: "@sinonjs/commons" "^3.0.1" @@ -2310,7 +2317,7 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== -"@types/hoist-non-react-statics@^3.3.6": +"@types/hoist-non-react-statics@^3.3.7": version "3.3.7" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz#306e3a3a73828522efa1341159da4846e7573a6c" integrity sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g== @@ -2347,16 +2354,16 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.5.0.tgz#5c99f37c443d9ccc4985866913f1ed364217da31" - integrity sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw== + version "25.6.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.6.0.tgz#4e09bad9b469871f2d0f68140198cbd714f4edca" + integrity sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ== dependencies: - undici-types "~7.18.0" + undici-types "~7.19.0" "@types/node@^24.10.1": - version "24.12.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.0.tgz#6222e028210e5322e4f4f6767f8d88e5ea3b33d2" - integrity sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ== + version "24.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.2.tgz#353cb161dbf1785ea25e8829ba7ec574c5c629ac" + integrity sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g== dependencies: undici-types "~7.16.0" @@ -2410,100 +2417,100 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.1.tgz#ddfdfb30f8b5ccee7f3c21798b377c51370edd55" - integrity sha512-Gn3aqnvNl4NGc6x3/Bqk1AOn0thyTU9bqDRhiRnUWezgvr2OnhYCWCgC8zXXRVqBsIL1pSDt7T9nJUe0oM0kDQ== +"@typescript-eslint/eslint-plugin@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz#fcbe76b693ce2412410cf4d48aefd617d345f2d9" + integrity sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.57.1" - "@typescript-eslint/type-utils" "8.57.1" - "@typescript-eslint/utils" "8.57.1" - "@typescript-eslint/visitor-keys" "8.57.1" + "@typescript-eslint/scope-manager" "8.59.0" + "@typescript-eslint/type-utils" "8.59.0" + "@typescript-eslint/utils" "8.59.0" + "@typescript-eslint/visitor-keys" "8.59.0" ignore "^7.0.5" natural-compare "^1.4.0" - ts-api-utils "^2.4.0" + ts-api-utils "^2.5.0" -"@typescript-eslint/parser@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.57.1.tgz#d523e559b148264055c0a49a29d5f50c7de659c2" - integrity sha512-k4eNDan0EIMTT/dUKc/g+rsJ6wcHYhNPdY19VoX/EOtaAG8DLtKCykhrUnuHPYvinn5jhAPgD2Qw9hXBwrahsw== +"@typescript-eslint/parser@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.0.tgz#57a138280b3ceaf07904fbd62c433d5cc1ee1573" + integrity sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg== dependencies: - "@typescript-eslint/scope-manager" "8.57.1" - "@typescript-eslint/types" "8.57.1" - "@typescript-eslint/typescript-estree" "8.57.1" - "@typescript-eslint/visitor-keys" "8.57.1" + "@typescript-eslint/scope-manager" "8.59.0" + "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/typescript-estree" "8.59.0" + "@typescript-eslint/visitor-keys" "8.59.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.57.1.tgz#16af9fe16eedbd7085e4fdc29baa73715c0c55c5" - integrity sha512-vx1F37BRO1OftsYlmG9xay1TqnjNVlqALymwWVuYTdo18XuKxtBpCj1QlzNIEHlvlB27osvXFWptYiEWsVdYsg== +"@typescript-eslint/project-service@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.0.tgz#914bf62069d870faa0389ffd725774a200f511bf" + integrity sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.57.1" - "@typescript-eslint/types" "^8.57.1" + "@typescript-eslint/tsconfig-utils" "^8.59.0" + "@typescript-eslint/types" "^8.59.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.57.1.tgz#4524d7e7b420cb501807499684d435ae129aaf35" - integrity sha512-hs/QcpCwlwT2L5S+3fT6gp0PabyGk4Q0Rv2doJXA0435/OpnSR3VRgvrp8Xdoc3UAYSg9cyUjTeFXZEPg/3OKg== +"@typescript-eslint/scope-manager@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz#f71be268bd31da1c160815c689e4dde7c9bc9e8e" + integrity sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg== dependencies: - "@typescript-eslint/types" "8.57.1" - "@typescript-eslint/visitor-keys" "8.57.1" + "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/visitor-keys" "8.59.0" -"@typescript-eslint/tsconfig-utils@8.57.1", "@typescript-eslint/tsconfig-utils@^8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.1.tgz#9233443ec716882a6f9e240fd900a73f0235f3d7" - integrity sha512-0lgOZB8cl19fHO4eI46YUx2EceQqhgkPSuCGLlGi79L2jwYY1cxeYc1Nae8Aw1xjgW3PKVDLlr3YJ6Bxx8HkWg== +"@typescript-eslint/tsconfig-utils@8.59.0", "@typescript-eslint/tsconfig-utils@^8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz#1276077f5ad77e384446ea28a2474e8f8be1af41" + integrity sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg== -"@typescript-eslint/type-utils@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.57.1.tgz#c49af1347b5869ca85155547a8f34f84ab386fd9" - integrity sha512-+Bwwm0ScukFdyoJsh2u6pp4S9ktegF98pYUU0hkphOOqdMB+1sNQhIz8y5E9+4pOioZijrkfNO/HUJVAFFfPKA== +"@typescript-eslint/type-utils@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz#2834ea3b179cedfc9244dcd4f74105a27751a439" + integrity sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg== dependencies: - "@typescript-eslint/types" "8.57.1" - "@typescript-eslint/typescript-estree" "8.57.1" - "@typescript-eslint/utils" "8.57.1" + "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/typescript-estree" "8.59.0" + "@typescript-eslint/utils" "8.59.0" debug "^4.4.3" - ts-api-utils "^2.4.0" - -"@typescript-eslint/types@8.57.1", "@typescript-eslint/types@^8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.57.1.tgz#54b27a8a25a7b45b4f978c3f8e00c4c78f11142c" - integrity sha512-S29BOBPJSFUiblEl6RzPPjJt6w25A6XsBqRVDt53tA/tlL8q7ceQNZHTjPeONt/3S7KRI4quk+yP9jK2WjBiPQ== - -"@typescript-eslint/typescript-estree@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.1.tgz#a9fd28d4a0ec896aa9a9a7e0cead62ea24f99e76" - integrity sha512-ybe2hS9G6pXpqGtPli9Gx9quNV0TWLOmh58ADlmZe9DguLq0tiAKVjirSbtM1szG6+QH6rVXyU6GTLQbWnMY+g== - dependencies: - "@typescript-eslint/project-service" "8.57.1" - "@typescript-eslint/tsconfig-utils" "8.57.1" - "@typescript-eslint/types" "8.57.1" - "@typescript-eslint/visitor-keys" "8.57.1" + ts-api-utils "^2.5.0" + +"@typescript-eslint/types@8.59.0", "@typescript-eslint/types@^8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.0.tgz#cfcc643c6e879016479775850d86d84c14492738" + integrity sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A== + +"@typescript-eslint/typescript-estree@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz#feba58a70ab6ea7ac53a2f3ae900db28ce3454c2" + integrity sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw== + dependencies: + "@typescript-eslint/project-service" "8.59.0" + "@typescript-eslint/tsconfig-utils" "8.59.0" + "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/visitor-keys" "8.59.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" - ts-api-utils "^2.4.0" + ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.57.1.tgz#e40f5a7fcff02fd24092a7b52bd6ec029fb50465" - integrity sha512-XUNSJ/lEVFttPMMoDVA2r2bwrl8/oPx8cURtczkSEswY5T3AeLmCy+EKWQNdL4u0MmAHOjcWrqJp2cdvgjn8dQ== +"@typescript-eslint/utils@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.0.tgz#f50df9bd6967881ef64fba62230111153179ead5" + integrity sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.57.1" - "@typescript-eslint/types" "8.57.1" - "@typescript-eslint/typescript-estree" "8.57.1" + "@typescript-eslint/scope-manager" "8.59.0" + "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/typescript-estree" "8.59.0" -"@typescript-eslint/visitor-keys@8.57.1": - version "8.57.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.1.tgz#3af4f88118924d3be983d4b8ae84803f11fe4563" - integrity sha512-YWnmJkXbofiz9KbnbbwuA2rpGkFPLbAIetcCNO6mJ8gdhdZ/v7WDXsoGFAJuM6ikUFKTlSQnjWnVO4ux+UzS6A== +"@typescript-eslint/visitor-keys@8.59.0": + version "8.59.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz#2e80de30e7e944ed4bd47d751e37dcb04db03795" + integrity sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q== dependencies: - "@typescript-eslint/types" "8.57.1" + "@typescript-eslint/types" "8.59.0" eslint-visitor-keys "^5.0.0" "@umijs/route-utils@^4.0.0": @@ -2702,9 +2709,9 @@ acorn@^8.11.0, acorn@^8.16.0, acorn@^8.4.1: integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== ajv@^6.14.0: - version "6.14.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" - integrity sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw== + version "6.15.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492" + integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw== dependencies: fast-deep-equal "^3.1.1" fast-json-stable-stringify "^2.0.0" @@ -2760,15 +2767,15 @@ ansi-styles@^6.1.0, ansi-styles@^6.2.1, ansi-styles@^6.2.3: integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== antd@^6.3.3: - version "6.3.3" - resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.3.tgz#72f6857640e6d6b6b36da7f75a1d1eed506a39e9" - integrity sha512-T8FAQelw36zS96cZw2U/qEjpYny5yFc7hg+1W7DvVr8xMoSXWvyB8WvmiDVH0nS0LPYV4y2sxetsJoGZt7rhhw== + version "6.3.7" + resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.7.tgz#620354ec04135356cbc5ce0a666871ddc73e4117" + integrity sha512-WTHi4bHVNKpYXLHESzU0Tts7rRNQeL84Bph9dfI3Qw7mHbTulExDcYKNHny5CTXcrBBOpraXbU9miBAwUR5vaw== dependencies: "@ant-design/colors" "^8.0.1" "@ant-design/cssinjs" "^2.1.2" "@ant-design/cssinjs-utils" "^2.1.2" "@ant-design/fast-color" "^3.0.1" - "@ant-design/icons" "^6.1.0" + "@ant-design/icons" "^6.1.1" "@ant-design/react-slick" "~2.0.0" "@babel/runtime" "^7.28.4" "@rc-component/cascader" "~1.14.0" @@ -2778,13 +2785,13 @@ antd@^6.3.3: "@rc-component/dialog" "~1.8.4" "@rc-component/drawer" "~1.4.2" "@rc-component/dropdown" "~1.0.2" - "@rc-component/form" "~1.7.2" - "@rc-component/image" "~1.6.0" + "@rc-component/form" "~1.8.1" + "@rc-component/image" "~1.9.0" "@rc-component/input" "~1.1.2" "@rc-component/input-number" "~1.6.2" "@rc-component/mentions" "~1.6.0" "@rc-component/menu" "~1.2.0" - "@rc-component/motion" "^1.3.1" + "@rc-component/motion" "^1.3.2" "@rc-component/mutate-observer" "^2.0.1" "@rc-component/notification" "~1.2.0" "@rc-component/pagination" "~1.2.0" @@ -2792,9 +2799,9 @@ antd@^6.3.3: "@rc-component/progress" "~1.0.2" "@rc-component/qrcode" "~1.1.1" "@rc-component/rate" "~1.0.1" - "@rc-component/resize-observer" "^1.1.1" + "@rc-component/resize-observer" "^1.1.2" "@rc-component/segmented" "~1.3.0" - "@rc-component/select" "~1.6.14" + "@rc-component/select" "~1.6.15" "@rc-component/slider" "~1.0.1" "@rc-component/steps" "~1.2.2" "@rc-component/switch" "~1.0.3" @@ -2807,7 +2814,7 @@ antd@^6.3.3: "@rc-component/tree-select" "~1.8.0" "@rc-component/trigger" "^3.9.0" "@rc-component/upload" "~1.1.0" - "@rc-component/util" "^1.9.0" + "@rc-component/util" "^1.10.1" clsx "^2.1.1" dayjs "^1.11.11" scroll-into-view-if-needed "^3.1.0" @@ -2972,9 +2979,9 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: - version "4.11.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.1.tgz#052ff9b2cbf543f5595028b583e4763b40c78ea7" - integrity sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A== + version "4.11.3" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.3.tgz#d23cf404edaa5f97bdfd9afed6eea8405e5326e7" + integrity sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg== axobject-query@^4.1.0: version "4.1.0" @@ -3051,30 +3058,30 @@ balanced-match@^4.0.2: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== -baseline-browser-mapping@^2.10.10, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19: - version "2.10.10" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz#e74bd066724c1d8d7d8ea75fc3be25389a7a5c56" - integrity sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ== +baseline-browser-mapping@^2.10.12, baseline-browser-mapping@^2.10.23, baseline-browser-mapping@^2.9.19: + version "2.10.23" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz#3a1a55d1a691a8c8d74688af7f1fd17eac23c184" + integrity sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g== brace-expansion@^1.1.7: - version "1.1.12" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" - integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + version "1.1.14" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b" + integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" - integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + version "2.1.0" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.0.tgz#4f41a41190216ee36067ec381526fe9539c4f0ae" + integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w== dependencies: balanced-match "^1.0.0" -brace-expansion@^5.0.2: - version "5.0.4" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.4.tgz#614daaecd0a688f660bbbc909a8748c3d80d4336" - integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== +brace-expansion@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" + integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== dependencies: balanced-match "^4.0.2" @@ -3086,15 +3093,15 @@ braces@^3.0.3: fill-range "^7.1.1" browserslist@^4.24.0: - version "4.28.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" - integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + version "4.28.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" + integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== dependencies: - baseline-browser-mapping "^2.9.0" - caniuse-lite "^1.0.30001759" - electron-to-chromium "^1.5.263" - node-releases "^2.0.27" - update-browserslist-db "^1.2.0" + baseline-browser-mapping "^2.10.12" + caniuse-lite "^1.0.30001782" + electron-to-chromium "^1.5.328" + node-releases "^2.0.36" + update-browserslist-db "^1.2.3" bser@2.1.1: version "2.1.1" @@ -3108,7 +3115,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== @@ -3116,14 +3123,14 @@ call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply- es-errors "^1.3.0" function-bind "^1.1.2" -call-bind@^1.0.7, call-bind@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" - integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== +call-bind@^1.0.7, call-bind@^1.0.8, call-bind@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.9.tgz#39a644700c80bc7d0ca9102fc6d1d43b2fd7eee7" + integrity sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ== dependencies: - call-bind-apply-helpers "^1.0.0" - es-define-property "^1.0.0" - get-intrinsic "^1.2.4" + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + get-intrinsic "^1.3.0" set-function-length "^1.2.2" call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: @@ -3157,10 +3164,10 @@ camelcase@^6.3.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001759: - version "1.0.30001780" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz#0e413de292808868a62ed9118822683fa120a110" - integrity sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ== +caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001782: + version "1.0.30001791" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz#dfb93d85c40ad380c57123e72e10f3c575786b51" + integrity sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ== capital-case@^1.0.4: version "1.0.4" @@ -3366,7 +3373,7 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -cookie@^1.0.2: +cookie@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== @@ -3600,10 +3607,10 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.5.263: - version "1.5.321" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.321.tgz#57a80554e2e7fd65e3689d320f52a64723472d5d" - integrity sha512-L2C7Q279W2D/J4PLZLk7sebOILDSWos7bMsMNN06rK482umHUrh/3lM8G7IlHFOYip2oAg5nha1rCMxr/rs6ZQ== +electron-to-chromium@^1.5.328: + version "1.5.344" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz#6437cc08a7d9b914a98120e182f37793c9eaffd4" + integrity sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg== emittery@^0.13.1: version "0.13.1" @@ -3642,10 +3649,10 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.1: - version "1.24.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" - integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== +es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.2: + version "1.24.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.2.tgz#2dbd38c180735ee983f77585140a2706a963ed9a" + integrity sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg== dependencies: array-buffer-byte-length "^1.0.2" arraybuffer.prototype.slice "^1.0.4" @@ -3713,14 +3720,14 @@ es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz#3be0f4e63438d6c5a1fb5f33b891aaad3f7dae06" - integrity sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ== + version "1.3.2" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz#8f4ff1f3603cbd09fbdb72c747a679779a65cc7f" + integrity sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw== dependencies: - call-bind "^1.0.8" + call-bind "^1.0.9" call-bound "^1.0.4" define-properties "^1.2.1" - es-abstract "^1.24.1" + es-abstract "^1.24.2" es-errors "^1.3.0" es-set-tostringtag "^2.1.0" function-bind "^1.1.2" @@ -3733,7 +3740,6 @@ es-iterator-helpers@^1.2.1: internal-slot "^1.1.0" iterator.prototype "^1.1.5" math-intrinsics "^1.1.0" - safe-array-concat "^1.1.3" es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" @@ -3789,11 +3795,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^16.0.8: - version "16.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.1.tgz#d3b6fe5076979285a2b607c70e469dc167b43237" - integrity sha512-qhabwjQZ1Mk53XzXvmogf8KQ0tG0CQXF0CZ56+2/lVhmObgmaqj7x5A1DSrWdZd3kwI7GTPGUjFne+krRxYmFg== + version "16.2.4" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.4.tgz#ed9f106bb474736e4f7c53c998321fffdedbcde6" + integrity sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg== dependencies: - "@next/eslint-plugin-next" "16.2.1" + "@next/eslint-plugin-next" "16.2.4" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.32.0" @@ -3804,13 +3810,13 @@ eslint-config-next@^16.0.8: typescript-eslint "^8.46.0" eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9: - version "0.3.9" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" - integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + version "0.3.10" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz#84ce3005abfc300588cf23bbac1aabec1fc6e8c1" + integrity sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ== dependencies: debug "^3.2.7" - is-core-module "^2.13.0" - resolve "^1.22.4" + is-core-module "^2.16.1" + resolve "^2.0.0-next.6" eslint-import-resolver-typescript@^3.5.2: version "3.10.1" @@ -3879,9 +3885,9 @@ eslint-plugin-jsx-a11y@^6.10.0: string.prototype.includes "^2.0.1" eslint-plugin-react-hooks@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz#66e258db58ece50723ef20cc159f8aa908219169" - integrity sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA== + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz#e6742cad75d970c0a3f30d7d3fa80a4784f55927" + integrity sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g== dependencies: "@babel/core" "^7.24.4" "@babel/parser" "^7.24.4" @@ -3934,16 +3940,16 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== eslint@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.1.0.tgz#9ca98e654e642ab2e1af6d1e9d8613857ac341b4" - integrity sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA== + version "10.2.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.2.1.tgz#224b2a6caeb34473eddcf918762363e2e063222a" + integrity sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" - "@eslint/config-array" "^0.23.3" - "@eslint/config-helpers" "^0.5.3" - "@eslint/core" "^1.1.1" - "@eslint/plugin-kit" "^0.6.1" + "@eslint/config-array" "^0.23.5" + "@eslint/config-helpers" "^0.5.5" + "@eslint/core" "^1.2.1" + "@eslint/plugin-kit" "^0.7.1" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" @@ -4269,9 +4275,9 @@ get-symbol-description@^1.1.0: get-intrinsic "^1.2.6" get-tsconfig@^4.10.0: - version "4.13.7" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.13.7.tgz#b9d8b199b06033ceeea1a93df7ea5765415089bc" - integrity sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q== + version "4.14.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz#985d85c52a9903864280ccc2448d413fbf1efed8" + integrity sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA== dependencies: resolve-pkg-maps "^1.0.0" @@ -4373,14 +4379,14 @@ graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: tslib "^2.1.0" graphql-ws@^6.0.6: - version "6.0.7" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.7.tgz#140307a513f0ba659ecec618d6cf15354a842f96" - integrity sha512-yoLRW+KRlDmnnROdAu7sX77VNLC0bsFoZyGQJLy1cF+X/SkLg/fWkRGrEEYQK8o2cafJ2wmEaMqMEZB3U3DYDg== + version "6.0.8" + resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.8.tgz#6712bb9da8462937e92343e3146726f89424df68" + integrity sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw== graphql@^16.12.0: - version "16.13.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.13.1.tgz#38ae5c76fbc4a009e0004dca6c76c370a1da7b54" - integrity sha512-gGgrVCoDKlIZ8fIqXBBb0pPKqDgki0Z/FSKNiQzSGj2uEYHr1tq5wmBegGwJx6QB5S5cM0khSBpi/JFHMCvsmQ== + version "16.13.2" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.13.2.tgz#4d2b73df5796b201f1bc2765f5d7067f689cb55f" + integrity sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig== has-bigints@^1.0.2: version "1.1.0" @@ -4424,9 +4430,9 @@ has-tostringtag@^1.0.2: has-symbols "^1.0.3" hasown@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + version "2.0.3" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c" + integrity sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg== dependencies: function-bind "^1.1.2" @@ -4614,7 +4620,7 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.13.0, is-core-module@^2.16.1: +is-core-module@^2.16.1: version "2.16.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -5432,19 +5438,19 @@ locate-path@^6.0.0: p-locate "^5.0.0" lodash-es@^4.17.21: - version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.23.tgz#58c4360fd1b5d33afc6c0bbd3d1149349b1138e0" - integrity sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.18.1.tgz#b962eeb80d9d983a900bf342961fb7418ca10b1d" + integrity sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A== lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== -lodash@^4.17.21, lodash@~4.17.0: - version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" - integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== +lodash@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== log-symbols@^4.0.0: version "4.1.0" @@ -5566,11 +5572,11 @@ mimic-function@^5.0.0: integrity sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA== minimatch@^10.0.0, minimatch@^10.2.2, minimatch@^10.2.4: - version "10.2.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.4.tgz#465b3accbd0218b8281f5301e27cedc697f96fde" - integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== dependencies: - brace-expansion "^5.0.2" + brace-expansion "^5.0.5" minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.5" @@ -5629,25 +5635,25 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^16.0.8: - version "16.2.1" - resolved "https://registry.yarnpkg.com/next/-/next-16.2.1.tgz#8e3ee1051f900e2a52e5978fc1cc3bbd7fe76cad" - integrity sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q== + version "16.2.4" + resolved "https://registry.yarnpkg.com/next/-/next-16.2.4.tgz#b1f708a283052e8ccb86ef16001d69e558e6ef5f" + integrity sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q== dependencies: - "@next/env" "16.2.1" + "@next/env" "16.2.4" "@swc/helpers" "0.5.15" baseline-browser-mapping "^2.9.19" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.2.1" - "@next/swc-darwin-x64" "16.2.1" - "@next/swc-linux-arm64-gnu" "16.2.1" - "@next/swc-linux-arm64-musl" "16.2.1" - "@next/swc-linux-x64-gnu" "16.2.1" - "@next/swc-linux-x64-musl" "16.2.1" - "@next/swc-win32-arm64-msvc" "16.2.1" - "@next/swc-win32-x64-msvc" "16.2.1" + "@next/swc-darwin-arm64" "16.2.4" + "@next/swc-darwin-x64" "16.2.4" + "@next/swc-linux-arm64-gnu" "16.2.4" + "@next/swc-linux-arm64-musl" "16.2.4" + "@next/swc-linux-x64-gnu" "16.2.4" + "@next/swc-linux-x64-musl" "16.2.4" + "@next/swc-win32-arm64-msvc" "16.2.4" + "@next/swc-win32-x64-msvc" "16.2.4" sharp "^0.34.5" no-case@^3.0.4: @@ -5692,10 +5698,10 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.27: - version "2.0.36" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.36.tgz#99fd6552aaeda9e17c4713b57a63964a2e325e9d" - integrity sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA== +node-releases@^2.0.36: + version "2.0.38" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.38.tgz#791569b9e4424a044e12c3abfad418ed83ce9947" + integrity sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw== nopt@^4.0.1: version "4.0.3" @@ -6019,14 +6025,14 @@ picocolors@^1.0.0, picocolors@^1.1.1: integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== -picomatch@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" - integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +picomatch@^4.0.3, picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== pirates@^4.0.7: version "4.0.7" @@ -6060,9 +6066,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.8.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" - integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + version "3.8.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0" + integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw== pretty-format@30.3.0: version "30.3.0" @@ -6116,18 +6122,18 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.31.1, rc-util@^5.35.0, rc-util@^5.44. react-is "^18.2.0" react-cookie@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.0.1.tgz#0d7f5112c9003b61c70d3d53f1f0a5b741e9f2e9" - integrity sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA== + version "8.1.0" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.1.0.tgz#819f32bb4984b3157b44c533cad7883a63ff6e7e" + integrity sha512-Qs+gD3gpQmUXnJUZafhJtNWhhNdi8OYbOAF5YQRAZa/D171ILOIEMfXDz/tmhkE+nOthllmqryHH6I/qmvIYWQ== dependencies: - "@types/hoist-non-react-statics" "^3.3.6" + "@types/hoist-non-react-statics" "^3.3.7" hoist-non-react-statics "^3.3.2" universal-cookie "^8.0.0" react-dom@^19.2.1: - version "19.2.4" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.4.tgz#6fac6bd96f7db477d966c7ec17c1a2b1ad8e6591" - integrity sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ== + version "19.2.5" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.5.tgz#b8768b10837d0b8e9ca5b9e2d58dff3d880ea25e" + integrity sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag== dependencies: scheduler "^0.27.0" @@ -6149,23 +6155,23 @@ react-is@^18.2.0, react-is@^18.3.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-is@^19.2.4: - version "19.2.4" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.4.tgz#a080758243c572ccd4a63386537654298c99d135" - integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA== +react-is@^19.2.5: + version "19.2.5" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.5.tgz#7e7b54143e9313fed787b23fd4295d5a23872ad9" + integrity sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ== react-test-renderer@^19.2.0: - version "19.2.4" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.4.tgz#c26f93fc8da5409472626cd873d005a1974df3ff" - integrity sha512-Ttl5D7Rnmi6JGMUpri4UjB4BAN0FPs4yRDnu2XSsigCWOLm11o8GwRlVsh27ER+4WFqsGtrBuuv5zumUaRCmKw== + version "19.2.5" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.5.tgz#0fbfb2796d09b0c133563e9468a45d77e13ffe00" + integrity sha512-kwViRpdISMTpcpy5B6TSewfJzRjnajihRaj57ZmOWKD+SPN6k9LUM13O0pfOuW8ir6B6OOiAXwCRqOoVxRNykA== dependencies: - react-is "^19.2.4" + react-is "^19.2.5" scheduler "^0.27.0" react@^19.2.1: - version "19.2.4" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.4.tgz#438e57baa19b77cb23aab516cf635cd0579ee09a" - integrity sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ== + version "19.2.5" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.5.tgz#c888ab8b8ef33e2597fae8bdb2d77edbdb42858b" + integrity sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA== read-installed@~4.0.3: version "4.0.3" @@ -6274,16 +6280,17 @@ resolve-pkg-maps@^1.0.0: resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.10.0, resolve@^1.22.4: - version "1.22.11" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" - integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== +resolve@^1.10.0: + version "1.22.12" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.12.tgz#f5b2a680897c69c238a13cd16b15671f8b73549f" + integrity sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA== dependencies: + es-errors "^1.3.0" is-core-module "^2.16.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.5: +resolve@^2.0.0-next.5, resolve@^2.0.0-next.6: version "2.0.0-next.6" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.6.tgz#b3961812be69ace7b3bc35d5bf259434681294af" integrity sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA== @@ -6328,13 +6335,13 @@ rxjs@7.8.2, rxjs@^7.8.2: tslib "^2.1.0" safe-array-concat@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" - integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + version "1.1.4" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz#a54cc9b61a57f33b42abad3cbdda3a2b38cc5719" + integrity sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" - get-intrinsic "^1.2.6" + call-bind "^1.0.9" + call-bound "^1.0.4" + get-intrinsic "^1.3.0" has-symbols "^1.1.0" isarray "^2.0.5" @@ -6484,12 +6491,12 @@ shell-quote@1.8.3, shell-quote@^1.7.3: integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== side-channel-list@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" - integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.1.tgz#c2e0b5a14a540aebee3bbc6c3f8666cc9b509127" + integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w== dependencies: es-errors "^1.3.0" - object-inspect "^1.13.3" + object-inspect "^1.13.4" side-channel-map@^1.0.1: version "1.0.1" @@ -6721,9 +6728,9 @@ string-width@^7.0.0: strip-ansi "^7.1.0" string-width@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.0.tgz#bdb6a9bd6d7800db635adae96cdb0443fec56c42" - integrity sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw== + version "8.2.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-8.2.1.tgz#165089cfa527cc88fbc23dd73313f5e334af1ea1" + integrity sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA== dependencies: get-east-asian-width "^1.5.0" strip-ansi "^7.1.2" @@ -6845,9 +6852,9 @@ styled-jsx@5.1.6: client-only "0.0.1" stylis@^4.3.4: - version "4.3.6" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" - integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.4.0.tgz#c5846c9345f4bfc51bd0cbd7ca35a0744f485a5d" + integrity sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA== supports-color@8.1.1, supports-color@^8.1.1: version "8.1.1" @@ -6926,12 +6933,12 @@ timeout-signal@^2.0.0: integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== tinyglobby@^0.2.13, tinyglobby@^0.2.15: - version "0.2.15" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" - integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + version "0.2.16" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" + integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== dependencies: fdir "^6.5.0" - picomatch "^4.0.3" + picomatch "^4.0.4" title-case@^3.0.3: version "3.0.3" @@ -6962,7 +6969,7 @@ treeify@^1.1.0: resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== -ts-api-utils@^2.4.0: +ts-api-utils@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== @@ -7006,11 +7013,6 @@ tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6 resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tslib@~2.6.0: - version "2.6.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" - integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== - type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -7074,19 +7076,19 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.46.0: - version "8.57.1" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.57.1.tgz#573f97d3e48bbb67290b47dde1b7cb3b5d01dc4f" - integrity sha512-fLvZWf+cAGw3tqMCYzGIU6yR8K+Y9NT2z23RwOjlNFF2HwSB3KhdEFI5lSBv8tNmFkkBShSjsCjzx1vahZfISA== + version "8.59.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.59.0.tgz#d1cc7c63559ce7116aeb66d35ec9dbe0063379fd" + integrity sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw== dependencies: - "@typescript-eslint/eslint-plugin" "8.57.1" - "@typescript-eslint/parser" "8.57.1" - "@typescript-eslint/typescript-estree" "8.57.1" - "@typescript-eslint/utils" "8.57.1" + "@typescript-eslint/eslint-plugin" "8.59.0" + "@typescript-eslint/parser" "8.59.0" + "@typescript-eslint/typescript-estree" "8.59.0" + "@typescript-eslint/utils" "8.59.0" -typescript@^5.0.4: - version "5.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" - integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== +typescript@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" + integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== unbox-primitive@^1.1.0: version "1.1.0" @@ -7108,17 +7110,17 @@ undici-types@~7.16.0: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== -undici-types@~7.18.0: - version "7.18.2" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" - integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== +undici-types@~7.19.0: + version "7.19.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.19.2.tgz#1b67fc26d0f157a0cba3a58a5b5c1e2276b8ba2a" + integrity sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg== universal-cookie@^8.0.0: - version "8.0.1" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.0.1.tgz#19263fcda6889978a081c610a90fc395eeaeec64" - integrity sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg== + version "8.1.0" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.1.0.tgz#0d7b2dd77b94d26d5e4609ce5a8df17054b15dd6" + integrity sha512-65+kikQAWq7gsJbirwB7dk6e8xeug1hx3++x2dQoymdXcV7fYv0yChOgHCg01ZwP3fE3sYeq6EWCSpFv3HLl9g== dependencies: - cookie "^1.0.2" + cookie "^1.1.1" unixify@^1.0.0: version "1.0.0" @@ -7154,7 +7156,7 @@ unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" -update-browserslist-db@^1.2.0: +update-browserslist-db@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== @@ -7367,7 +7369,7 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@^8.18.3, ws@^8.19.0: +ws@^8.18.3, ws@^8.20.0: version "8.20.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== From d0127e9c14f4d57fa4d17dac85d2bcadeb8c5b61 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Apr 2026 13:29:05 +0200 Subject: [PATCH 796/878] Use ' instead of ` (because latter is treated as command substitution) --- forge.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge.mk b/forge.mk index 8f5aa062..0f099f37 100755 --- a/forge.mk +++ b/forge.mk @@ -29,7 +29,7 @@ build : ## Build image if it does not exist yet, for example, `./forge.mk build --tag "${NAME}-${SERVICE}:${TARGET}" \ "./${SERVICE}" ; \ else \ - echo "Image already exists, skipping build. Pass `OVERWRITE=true` to build anyway." ; \ + echo "Image already exists, skipping build. Pass 'OVERWRITE=true' to build anyway." ; \ fi .PHONY : build From 8776880370d9fbf48b6aa811e835ad30fa19a68f Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Apr 2026 13:37:23 +0200 Subject: [PATCH 797/878] Track files named *.large.* with Git LFS --- .gitattributes | 1 + ...49.json => 2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json} | 0 backend/test/Methods/SpectralToIntegralMethodTests.cs | 3 +-- 3 files changed, 2 insertions(+), 2 deletions(-) rename backend/test/Methods/{2d40b285-79d4-4ec6-8d46-767bd9b0f249.json => 2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json} (100%) diff --git a/.gitattributes b/.gitattributes index 93fce6ba..e8260ead 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json filter=lfs diff=lfs merge=lfs -text +*.large.* filter=lfs diff=lfs merge=lfs -text diff --git a/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json b/backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json similarity index 100% rename from backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json rename to backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json diff --git a/backend/test/Methods/SpectralToIntegralMethodTests.cs b/backend/test/Methods/SpectralToIntegralMethodTests.cs index d27eb99c..e122fbb2 100644 --- a/backend/test/Methods/SpectralToIntegralMethodTests.cs +++ b/backend/test/Methods/SpectralToIntegralMethodTests.cs @@ -5,7 +5,6 @@ using System.Text.Json; using System.Threading.Tasks; using Database.Json; -using Database.Methods; using Database.Methods.SpectralToIntegral; using FluentAssertions; using FluentAssertions.Execution; @@ -90,7 +89,7 @@ public void Calculate_Few() public async Task Calculate_Many() { using var fileStream = File.OpenRead( - ConstructFilePath("./Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json") + ConstructFilePath("./Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.large.json") ); using var jsonDocument = await JsonDocument.ParseAsync( fileStream, From 5f83d03b19053ddf6af0ede6a5d29d1390219e84 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Apr 2026 13:43:38 +0200 Subject: [PATCH 798/878] Remove unnecessary full-path Git LFS file tracking --- .gitattributes | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index e8260ead..7860975d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -backend/test/Methods/2d40b285-79d4-4ec6-8d46-767bd9b0f249.json filter=lfs diff=lfs merge=lfs -text *.large.* filter=lfs diff=lfs merge=lfs -text From 1b2148990ff5611200270fbda2c18a8bc130a872 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 28 Apr 2026 17:43:26 +0200 Subject: [PATCH 799/878] Add steps for the very first deployment --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2c4c0d76..c6927384 100644 --- a/README.md +++ b/README.md @@ -385,7 +385,17 @@ and the pages following it. inside the management container by running `make manage` and manage GnuPG keys with the `gpg` command or the GNU Make targets `./gpg.mk` there. - 1. Create the PostgreSQL database by running `./database.mk create`. + 1. Switch to the Git branch, tag, or commit you want to deploy by either running + `git switch ${BRANCH}`, + `git switch --detach release/${TAG}`, or + `git switch --detach ${COMMIT_HASH}`, + where, for example, `${BRANCH}` is `develop` or `${TAG}` is `v1.0.0` or + `${COMMIT_HASH}` is `5e14d7d0858f26c00c82ab9c248cd750606a24b6`. + + 1. Create the PostgreSQL database and schema by running + `./database.mk create migrate`. + + 1. Build and start all services by running `make build up`. ### Creating a release @@ -416,8 +426,8 @@ and the pages following it. `git switch --detach release/${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. Build and push the Docker images for the services `frontend` and `backend` - services by running `./forge.mk all USER=cloud HOST=${IP}`, where `${IP}` is - the server's IP address. + by running `./forge.mk all USER=cloud HOST=${IP}`, where `${IP}` is the + server's IP address. 1. Enter a shell on the production machine using `ssh`. 1. Navigate into `/app/production` by running `cd /app/production`. 1. Back up the production database by running From 7880c3c58a37cbdb024c78c891ed23b35efd99ea Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 12 May 2026 16:26:57 +0200 Subject: [PATCH 800/878] Make authentication for file-uploads via the authentication scheme optional and if that did not succeed, ask the metabase whether an extracted or given bearer token authorizes the client --- .../Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs | 1 - backend/src/Controllers/FileUploadController.cs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs index 4244bb40..166cf511 100644 --- a/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs +++ b/backend/src/Authentication/GraphQlAuthenticationAndAntiforgeryHandler.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Immutable; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index f16f4fcd..469d3345 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -90,6 +90,7 @@ protected override void Dispose(bool disposing) [DisableFormValueModelBinding] // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] + [AllowAnonymous] [Produces(MediaTypeNames.Application.Json)] public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, From ab23b665e7863bd4fac33766475d464a944e0e09 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 18 May 2026 20:02:54 +0200 Subject: [PATCH 801/878] Map `allowedUserAndQuantity` dictionary to `jsonb` column (which in particular fixes the mutation `updateDataAccessRights` with non-null `allowedUserAndQuantity` input) and check whether users, applications, and/or institutions passed to that mutation exist in the metabase --- .../src/ApiRequests/ComponentDataLoader.cs | 7 +- .../src/ApiRequests/DataFormatDataLoader.cs | 7 +- .../src/ApiRequests/InstitutionDataLoader.cs | 7 +- backend/src/ApiRequests/MethodDataLoader.cs | 7 +- .../OpenIdConnectApplicationDataLoader.cs | 42 +++++++++ .../ApiRequests/Queries/Components.graphql | 5 +- .../ApiRequests/Queries/DataFormats.graphql | 5 +- .../ApiRequests/Queries/Institutions.graphql | 5 +- .../src/ApiRequests/Queries/Methods.graphql | 5 +- .../Queries/OpenIdConnectApplications.graphql | 9 ++ backend/src/ApiRequests/Queries/Users.graphql | 9 ++ .../src/ApiRequests/QueryByIdDataLoader.cs | 42 ++++----- backend/src/ApiRequests/UserDataLoader.cs | 42 +++++++++ backend/src/Data/ApplicationDbContext.cs | 14 +++ backend/src/Database.csproj | 1 - .../UpdateDataAccessRightsMutation.cs | 88 ++++++++++++++++++- .../CalorimetricDataQueries.cs | 8 +- backend/src/GraphQl/DataX/DataQueriesBase.cs | 5 +- .../GeometricDataX/GeometricDataQueries.cs | 8 +- .../HygrothermalDataQueries.cs | 6 +- .../LifeCycleDataX/LifeCycleDataQueries.cs | 6 +- .../OpticalDataX/OpticalDataQueries.cs | 6 +- .../PhotovoltaicDataQueries.cs | 8 +- backend/src/Services/AccessRightsService.cs | 14 +-- frontend/package.json | 2 +- frontend/yarn.lock | 7 +- 26 files changed, 286 insertions(+), 79 deletions(-) create mode 100644 backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs create mode 100644 backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql create mode 100644 backend/src/ApiRequests/Queries/Users.graphql create mode 100644 backend/src/ApiRequests/UserDataLoader.cs diff --git a/backend/src/ApiRequests/ComponentDataLoader.cs b/backend/src/ApiRequests/ComponentDataLoader.cs index fd69cf01..edc0ecfe 100644 --- a/backend/src/ApiRequests/ComponentDataLoader.cs +++ b/backend/src/ApiRequests/ComponentDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -15,8 +16,8 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Component( - Guid Uuid - ) : IIdNode; + Guid Id + ) : IIdNode; private sealed record ComponentsData( Connection? Connection @@ -30,7 +31,7 @@ public static Task> GetComponentByIdAsync( CancellationToken cancellationToken ) { - return QueryByIdDataLoader.GetByIdAsync( + return QueryByIdDataLoader.GetByIdAsync( componentIds, [QueryFileName], apiRequestService, diff --git a/backend/src/ApiRequests/DataFormatDataLoader.cs b/backend/src/ApiRequests/DataFormatDataLoader.cs index 1bb2a46e..09350273 100644 --- a/backend/src/ApiRequests/DataFormatDataLoader.cs +++ b/backend/src/ApiRequests/DataFormatDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -15,12 +16,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record DataFormat( - Guid Uuid, + Guid Id, string Name, string? Extension, string MediaType, Uri? SchemaLocator - ) : IIdNode; + ) : IIdNode; private sealed record DataFormatsData( Connection? Connection @@ -34,7 +35,7 @@ public static Task> GetDataFormatByIdAsync( CancellationToken cancellationToken ) { - return QueryByIdDataLoader.GetByIdAsync( + return QueryByIdDataLoader.GetByIdAsync( dataFormatIds, [QueryFileName], apiRequestService, diff --git a/backend/src/ApiRequests/InstitutionDataLoader.cs b/backend/src/ApiRequests/InstitutionDataLoader.cs index 754c844c..9d431466 100644 --- a/backend/src/ApiRequests/InstitutionDataLoader.cs +++ b/backend/src/ApiRequests/InstitutionDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -15,8 +16,8 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Institution( - Guid Uuid - ) : IIdNode; + Guid Id + ) : IIdNode; private sealed record InstitutionsData( Connection? Connection @@ -30,7 +31,7 @@ public static Task> GetInstitutionByIdAsync( CancellationToken cancellationToken ) { - return QueryByIdDataLoader.GetByIdAsync( + return QueryByIdDataLoader.GetByIdAsync( institutionIds, [QueryFileName], apiRequestService, diff --git a/backend/src/ApiRequests/MethodDataLoader.cs b/backend/src/ApiRequests/MethodDataLoader.cs index 32af4d39..20808157 100644 --- a/backend/src/ApiRequests/MethodDataLoader.cs +++ b/backend/src/ApiRequests/MethodDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -15,8 +16,8 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Method( - Guid Uuid - ) : IIdNode; + Guid Id + ) : IIdNode; private sealed record MethodsData( Connection? Connection @@ -30,7 +31,7 @@ public static Task> GetMethodByIdAsync( CancellationToken cancellationToken ) { - return QueryByIdDataLoader.GetByIdAsync( + return QueryByIdDataLoader.GetByIdAsync( methodIds, [QueryFileName], apiRequestService, diff --git a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs new file mode 100644 index 00000000..7d1b4144 --- /dev/null +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; + +namespace Database.ApiRequests; + +public static class OpenIdConnectApplicationDataLoader +{ + private const string QueryFileName = "OpenIdConnectApplications.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record OpenIdConnectApplication( + string Id + ) : IIdNode; + + private sealed record OpenIdConnectApplicationsData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetOpenIdConnectApplicationByClientIdAsync( + IReadOnlyList clientIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + clientIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Components.graphql b/backend/src/ApiRequests/Queries/Components.graphql index b67fefa1..610926ea 100644 --- a/backend/src/ApiRequests/Queries/Components.graphql +++ b/backend/src/ApiRequests/Queries/Components.graphql @@ -2,8 +2,9 @@ query Components($ids: [Uuid!]!) { connection: components(where: { id: { in: $ids } }) { edges { node { - uuid + id: uuid } } } -} \ No newline at end of file +} + diff --git a/backend/src/ApiRequests/Queries/DataFormats.graphql b/backend/src/ApiRequests/Queries/DataFormats.graphql index 7a7f0e27..5cb90fc3 100644 --- a/backend/src/ApiRequests/Queries/DataFormats.graphql +++ b/backend/src/ApiRequests/Queries/DataFormats.graphql @@ -2,7 +2,7 @@ query DataFormats($ids: [Uuid!]!) { connection: dataFormats(where: { id: { in: $ids } }) { edges { node { - uuid + id: uuid name extension mediaType @@ -10,4 +10,5 @@ query DataFormats($ids: [Uuid!]!) { } } } -} \ No newline at end of file +} + diff --git a/backend/src/ApiRequests/Queries/Institutions.graphql b/backend/src/ApiRequests/Queries/Institutions.graphql index 69389195..d81a0594 100644 --- a/backend/src/ApiRequests/Queries/Institutions.graphql +++ b/backend/src/ApiRequests/Queries/Institutions.graphql @@ -2,8 +2,9 @@ query Institutions($ids: [Uuid!]!) { connection: institutions(where: { id: { in: $ids } }) { edges { node { - uuid + id: uuid } } } -} \ No newline at end of file +} + diff --git a/backend/src/ApiRequests/Queries/Methods.graphql b/backend/src/ApiRequests/Queries/Methods.graphql index 1808c325..05b068cd 100644 --- a/backend/src/ApiRequests/Queries/Methods.graphql +++ b/backend/src/ApiRequests/Queries/Methods.graphql @@ -2,8 +2,9 @@ query Methods($ids: [Uuid!]!) { connection: methods(where: { id: { in: $ids } }) { edges { node { - uuid + id: uuid } } } -} \ No newline at end of file +} + diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql new file mode 100644 index 00000000..932ffec3 --- /dev/null +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -0,0 +1,9 @@ +query OpenIdConnectApplications($ids: [String!]!) { + connection: openIdConnectApplications(where: { clientId: { in: $ids } }) { + edges { + node { + id: clientId + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/Users.graphql b/backend/src/ApiRequests/Queries/Users.graphql new file mode 100644 index 00000000..09a0a69b --- /dev/null +++ b/backend/src/ApiRequests/Queries/Users.graphql @@ -0,0 +1,9 @@ +query Users($ids: [Uuid!]!) { + connection: users(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + } + } + } +} diff --git a/backend/src/ApiRequests/QueryByIdDataLoader.cs b/backend/src/ApiRequests/QueryByIdDataLoader.cs index c2f26a4a..1652a2e4 100644 --- a/backend/src/ApiRequests/QueryByIdDataLoader.cs +++ b/backend/src/ApiRequests/QueryByIdDataLoader.cs @@ -8,39 +8,39 @@ namespace Database.ApiRequests; -public interface IIdNode +public static class QueryByIdDataLoader { - Guid Uuid { get; } -} - -public sealed record Edge( - TNode Node -); + public interface IIdNode + { + TId Id { get; } + } -public sealed record Connection( - IReadOnlyCollection>? Edges -); + public sealed record Edge( + TNode Node + ); -public interface IConnectionData -{ - Connection? Connection { get; } -} + public sealed record Connection( + IReadOnlyCollection>? Edges + ); + public interface IConnectionData + { + Connection? Connection { get; } + } -public static class QueryByIdDataLoader -{ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; - public static async Task> GetByIdAsync( - IReadOnlyList ids, + public static async Task> GetByIdAsync( + IReadOnlyList ids, string[] queryFileNames, ApiRequestService apiRequestService, AppSettings appSettings, CancellationToken cancellationToken ) + where TId : notnull where TData : class, IConnectionData - where TNode : IIdNode + where TNode : IIdNode { return (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint(appSettings), @@ -58,8 +58,8 @@ await GraphQlQueryHelpers.Construct(queryFileNames), ?.Connection ?.Edges ?.ToDictionary( - _ => _.Node.Uuid, + _ => _.Node.Id, _ => _.Node - ) ?? Enumerable.Empty().ToDictionary(_ => _.Uuid); + ) ?? Enumerable.Empty().ToDictionary(_ => _.Id); } } \ No newline at end of file diff --git a/backend/src/ApiRequests/UserDataLoader.cs b/backend/src/ApiRequests/UserDataLoader.cs new file mode 100644 index 00000000..be2ff55a --- /dev/null +++ b/backend/src/ApiRequests/UserDataLoader.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; +using static Database.ApiRequests.QueryByIdDataLoader; + +namespace Database.ApiRequests; + +public static class UserDataLoader +{ + private const string QueryFileName = "Users.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record User( + Guid Id + ) : IIdNode; + + private sealed record UsersData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetUserByIdAsync( + IReadOnlyList userIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + userIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index b3570272..61575e2e 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; using NodaTime; +using System.Text.Json; namespace Database.Data; @@ -206,6 +207,19 @@ EntityTypeBuilder builder ) where TData : class, IData { + builder + .OwnsOne( + data => data.DataAccessRights, + dataAccessRights => + { + dataAccessRights + .Property(_ => _.AllowedUserAndQuantity) + .HasConversion( + dictionary => JsonSerializer.Serialize(dictionary), + stringValue => JsonSerializer.Deserialize>(stringValue) ?? new() + ); + } + ); builder .OwnsOne( data => data.AppliedMethod, diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index cb7a4f4c..27942a7b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -30,7 +30,6 @@ - diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs index b085b483..ff70a5a3 100644 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs @@ -1,13 +1,15 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.Extensions; -using Database.Services; +using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; namespace Database.GraphQl.AccessRights; @@ -27,7 +29,10 @@ public enum UpdateDataAccessRightsErrorCode UNKNOWN, UNAUTHENTICATED, UNAUTHORIZED, - UNKNOWN_DATA + UNKNOWN_DATA, + UNKNOWN_INSTITUTION, + UNKNOWN_USER, + UNKNOWN_APPLICATION } public sealed record UpdateDataAccessRightsError( @@ -61,6 +66,9 @@ public async Task UpdateDataAccessRightsAsync( UpdateDataAccessRightsInput input, ApplicationDbContext context, CommonAuthorization authorization, + IInstitutionByIdDataLoader institutionByIdDataLoader, + IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByIdDataLoader, + IUserByIdDataLoader userByIdDataLoader, CancellationToken cancellationToken ) { @@ -88,6 +96,82 @@ CancellationToken cancellationToken return fetchDataErrorPayload; } + List errors = []; + + var unknownInstitutions = + input.AllowedInstitutions is null + ? null + : input.AllowedInstitutions.Zip( + await Task.WhenAll( + input.AllowedInstitutions + .Select(id => institutionByIdDataLoader.LoadAsync(id, cancellationToken)) + ) + ) + .Where(_ => _.Second is null) + .Select(_ => _.First) + .ToList().AsReadOnly(); + if (unknownInstitutions is not null && unknownInstitutions.Count > 0) + { + errors.Add( + NewError( + UpdateDataAccessRightsErrorCode.UNKNOWN_INSTITUTION, + $"The institution(s) '{string.Join("', '", unknownInstitutions)}' do(es) not exist.", + [nameof(input), nameof(input.AllowedInstitutions).ToLowerFirst()] + ) + ); + } + + var unknownOpenIdConnectApplications = + input.AllowedApplications is null + ? null + : input.AllowedApplications.Zip( + await Task.WhenAll( + input.AllowedApplications + .Select(id => openIdConnectApplicationByIdDataLoader.LoadAsync(id, cancellationToken)) + ) + ) + .Where(_ => _.Second is null) + .Select(_ => _.First) + .ToList().AsReadOnly(); + if (unknownOpenIdConnectApplications is not null && unknownOpenIdConnectApplications.Count > 0) + { + errors.Add( + NewError( + UpdateDataAccessRightsErrorCode.UNKNOWN_APPLICATION, + $"The openIdConnectApplication(s) '{string.Join("', '", unknownOpenIdConnectApplications)}' do(es) not exist.", + [nameof(input), nameof(input.AllowedApplications).ToLowerFirst()] + ) + ); + } + + var unknownUsers = + input.AllowedUserAndQuantity is null + ? null + : input.AllowedUserAndQuantity.Zip( + await Task.WhenAll( + input.AllowedUserAndQuantity + .Select(userAndQuantity => userByIdDataLoader.LoadAsync(userAndQuantity.Key, cancellationToken)) + ) + ) + .Where(_ => _.Second is null) + .Select(_ => _.First.Key) + .ToList().AsReadOnly(); + if (unknownUsers is not null && unknownUsers.Count > 0) + { + errors.Add( + NewError( + UpdateDataAccessRightsErrorCode.UNKNOWN_USER, + $"The user(s) '{string.Join("', '", unknownUsers)}' do(es) not exist.", + [nameof(input), nameof(input.AllowedUserAndQuantity).ToLowerFirst()] + ) + ); + } + + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; data.DataAccessRights.AllowedApplications = input.AllowedApplications; data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 1e4e0755..3bd1573b 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -1,18 +1,16 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; -using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.CalorimetricDataX; @@ -25,7 +23,7 @@ public sealed class CalorimetricDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllCalorimetricDataAsync( + public Task> GetAllCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -49,7 +47,7 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingCalorimetricDataAsync( + public Task> GetAllPendingCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 08887c8e..216b0ec6 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -18,7 +19,7 @@ namespace Database.GraphQl.DataX; public abstract class DataQueriesBase where TData : class, IData { - protected async Task> GetAllDataAsync( + protected async Task> GetAllDataAsync( DbSet data, [GraphQLType] string? locale, AccessRightsService accessRightsService, @@ -40,7 +41,7 @@ CancellationToken cancellationToken return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); } - protected async Task> GetAllPendingDataAsync( + protected async Task> GetAllPendingDataAsync( DbSet data, [GraphQLType] string? locale, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 882d0e74..2520920a 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,18 +1,16 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; -using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GeometricDataX; @@ -23,7 +21,7 @@ public sealed class GeometricDataQueries [UsePaging] [UseFiltering] [UseSorting] - public Task> GetAllGeometricDataAsync( + public Task> GetAllGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -45,7 +43,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - public Task> GetAllPendingGeometricDataAsync( + public Task> GetAllPendingGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 1067c670..6f22f619 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -23,7 +23,7 @@ public sealed class HygrothermalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllHygrothermalDataAsync( + public Task> GetAllHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -47,7 +47,7 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingHygrothermalDataAsync( + public Task> GetAllPendingHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index 06b02dd5..01ffe18e 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -23,7 +23,7 @@ public sealed class LifeCycleDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllLifeCycleDataAsync( + public Task> GetAllLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -47,7 +47,7 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingLifeCycleDataAsync( + public Task> GetAllPendingLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 035a71fc..24196d2f 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -23,7 +23,7 @@ public sealed class OpticalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllOpticalDataAsync( + public Task> GetAllOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -47,7 +47,7 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingOpticalDataAsync( + public Task> GetAllPendingOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index acb59d8e..d64b19eb 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -1,18 +1,16 @@ using System; -using System.Linq; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; -using Database.GraphQl.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Data; using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.PhotovoltaicDataX; @@ -25,7 +23,7 @@ public sealed class PhotovoltaicDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPhotovoltaicDataAsync( + public Task> GetAllPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, @@ -49,7 +47,7 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingPhotovoltaicDataAsync( + public Task> GetAllPendingPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 42ebabf6..6c66b454 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -32,13 +32,13 @@ public sealed class AccessRightsService( public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData { - return await ( - await ApplyAccessRightsOnData(new List { data }.AsQueryable(), cancellationToken) - ).SingleOrDefaultAsync(cancellationToken); + return ( + await ApplyAccessRightsOnData([data], cancellationToken) + ).SingleOrDefault(); } - public async Task> ApplyAccessRightsOnData( - IQueryable data, + public async Task> ApplyAccessRightsOnData( + IEnumerable data, CancellationToken cancellationToken ) where T : IData @@ -72,7 +72,7 @@ CancellationToken cancellationToken // Save InstitutionAccessRight changes await context.SaveChangesAsync(cancellationToken); } - return result.AsQueryable(); + return result; } private static async Task> GetInstitutionAccessRightsAsync( @@ -90,7 +90,7 @@ await context.InstitutionAccessRights.AsQueryable() } private IEnumerable ProcessData( - IQueryable data, + IEnumerable data, CurrentUser? currentUser, string? openIdConnectClientId, IReadOnlyList? institutionIds, diff --git a/frontend/package.json b/frontend/package.json index aa378520..8660286c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -42,7 +42,7 @@ "@types/react-highlight-words": "^0.20.0", "@types/react-test-renderer": "^19.1.0", "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.10.23", + "baseline-browser-mapping": "^2.10.30", "concurrently": "^9.2.1", "deepmerge": "^4.0", "eslint": "^10.1.0", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2b5e60d0..2bb05242 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -3058,11 +3058,16 @@ balanced-match@^4.0.2: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== -baseline-browser-mapping@^2.10.12, baseline-browser-mapping@^2.10.23, baseline-browser-mapping@^2.9.19: +baseline-browser-mapping@^2.10.12, baseline-browser-mapping@^2.9.19: version "2.10.23" resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz#3a1a55d1a691a8c8d74688af7f1fd17eac23c184" integrity sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g== +baseline-browser-mapping@^2.10.30: + version "2.10.30" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz#58915c74388b05f3b3504026194ea9fa98f6e6b6" + integrity sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg== + brace-expansion@^1.1.7: version "1.1.14" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b" From d2efe7923e32b1f0d888a964491656472aac5427 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Jun 2026 16:22:09 +0200 Subject: [PATCH 802/878] Disable request size limit imposed by the web server Kestrel (it is already enforced by the reverse proxy NGINX, which allows 10GB) --- backend/src/Controllers/FileUploadController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index 469d3345..bc8c063c 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -88,6 +88,7 @@ protected override void Dispose(bool disposing) [EndpointName("UploadFile")] [EndpointDescription("Upload file for GET HTTP resource")] [DisableFormValueModelBinding] + [DisableRequestSizeLimit] // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [AllowAnonymous] From 52e4dc0fd9c6ca9fa9370bfaea848c2564fade33 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 20 May 2026 16:56:01 +0200 Subject: [PATCH 803/878] Upgrade to HotChocolate 16 --- backend/create-certificates.csx | 44 ---- backend/dotnet-tools.json | 4 +- .../src/Configuration/AuthConfiguration.cs | 19 +- .../src/Configuration/GraphQlConfiguration.cs | 196 +++++++++++------- .../Controllers/AuthenticationController.cs | 16 +- backend/src/Data/ApplicationDbContext.cs | 128 +++++++++++- backend/src/Data/Association.cs | 8 + backend/src/Data/AuditableAssociation.cs | 10 + backend/src/Data/AuditableEntity.cs | 20 ++ backend/src/Data/CalorimetricData.cs | 5 +- backend/src/Data/DataX.cs | 52 +++-- backend/src/Data/Entity.cs | 15 +- backend/src/Data/GeometricData.cs | 5 +- backend/src/Data/GetHttpsResource.cs | 2 +- backend/src/Data/HygrothermalData.cs | 5 +- backend/src/Data/IAssociation.cs | 7 + backend/src/Data/IAuditable.cs | 13 ++ backend/src/Data/IData.cs | 6 +- backend/src/Data/INamed.cs | 6 + backend/src/Data/InstitutionAccessRights.cs | 6 +- backend/src/Data/LifeCycleData.cs | 5 +- backend/src/Data/OpticalData.cs | 5 +- backend/src/Data/PhotovoltaicData.cs | 5 +- backend/src/Data/User.cs | 2 +- backend/src/Database.csproj | 66 +++--- .../src/Extensions/EnumerableExtensions.cs | 20 -- backend/src/Extensions/LinqExtensions.cs | 190 +++++++++++++++++ backend/src/Extensions/NodaTimeExtensions.cs | 15 +- backend/src/Extensions/StringExtensions.cs | 15 ++ .../AuditableAssociationFilterType.cs | 19 ++ .../AuditableAssociationSortType.cs | 19 ++ backend/src/GraphQl/AuthorizedConnection.cs | 40 ++++ .../GraphQl/AuthorizedPaginatedConnection.cs | 47 +++++ .../CalorimetricDataByIdDataLoader.cs | 20 -- .../CalorimetricDataLoaders.cs | 31 +++ .../CalorimetricDataQueries.cs | 13 +- .../CalorimetricDataX/CalorimetricDataType.cs | 2 +- .../CreateCalorimetricDataMutation.cs | 7 +- backend/src/GraphQl/Connection.cs | 31 +-- backend/src/GraphQl/DataLoaders.cs | 126 +++++++++++ .../GraphQl/DataX/CreateDataMutationBase.cs | 5 +- backend/src/GraphQl/DataX/DataConnection.cs | 14 +- .../src/GraphQl/DataX/DataConnectionBase.cs | 5 +- backend/src/GraphQl/DataX/DataDataLoaders.cs | 69 ++++++ .../src/GraphQl/DataX/DataFilterTypeBase.cs | 5 +- backend/src/GraphQl/DataX/DataQueries.cs | 1 + backend/src/GraphQl/DataX/DataQueriesBase.cs | 48 ++--- backend/src/GraphQl/DataX/DataResolvers.cs | 20 +- backend/src/GraphQl/DataX/DataSortTypeBase.cs | 5 +- backend/src/GraphQl/DataX/DataType.cs | 19 +- backend/src/GraphQl/DataX/DataTypeBase.cs | 21 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 7 +- ...ceTreeNonRootVerticesByDataIdDataLoader.cs | 34 --- ...HttpsResourceTreeRootByDataIdDataLoader.cs | 34 --- .../GetHttpsResourcesByDataIdDataLoader.cs | 34 --- .../src/GraphQl/DataX/UpdateDataMutation.cs | 4 +- .../AssociationsByAssociateIdDataLoader.cs | 38 ---- ...erType.cs => AuditableEntityFilterType.cs} | 13 +- ...SortType.cs => AuditableEntitySortType.cs} | 10 +- .../GraphQl/Entities/EntityByIdDataLoader.cs | 39 ---- backend/src/GraphQl/Entities/EntityType.cs | 10 +- .../ErrorLoggingDiagnosticEventListener.cs | 187 +++++++++-------- .../src/GraphQl/Extensions/PageExtensions.cs | 66 ++++++ .../Extensions/ResolverContextExtensions.cs | 14 +- .../Extensions/SortingContextExtensions.cs | 30 --- backend/src/GraphQl/Filters/INotField.cs | 21 +- backend/src/GraphQl/Filters/NotField.cs | 87 ++++---- .../GraphQl/Filters/ScalarFilterInputTypes.cs | 64 ++++-- .../CreateGeometricDataMutation.cs | 9 +- .../GeometricDataByIdDataLoader.cs | 20 -- .../GeometricDataX/GeometricDataLoaders.cs | 31 +++ .../GeometricDataX/GeometricDataQueries.cs | 11 +- .../GeometricDataX/GeometricDataType.cs | 2 +- .../GetHttpsResourceByIdDataLoader.cs | 20 -- ...eChildrenByGetHttpsResourceIdDataLoader.cs | 26 --- .../GetHttpsResourceDataLoaders.cs | 49 +++++ .../GetHttpsResourceFilterType.cs | 2 +- .../GetHttpsResourceQueries.cs | 38 ++-- .../GetHttpsResourceResolvers.cs | 16 +- .../GetHttpsResourceSortType.cs | 2 +- .../GetHttpsResources/GetHttpsResourceType.cs | 3 +- ...mputeGetHttpsResourceHashValuesMutation.cs | 5 +- backend/src/GraphQl/GraphQlConstants.cs | 2 + backend/src/GraphQl/GraphQlThrowHelper.cs | 52 +++++ backend/src/GraphQl/GraphQlTypeResources.cs | 8 + .../CreateHygrothermalDataMutation.cs | 7 +- .../HygrothermalDataByIdDataLoader.cs | 20 -- .../HygrothermalDataLoaders.cs | 31 +++ .../HygrothermalDataQueries.cs | 11 +- .../HygrothermalDataX/HygrothermalDataType.cs | 2 +- .../CreateLifeCycleDataMutation.cs | 7 +- .../LifeCycleDataByIdDataLoader.cs | 20 -- .../LifeCycleDataX/LifeCycleDataLoaders.cs | 31 +++ .../LifeCycleDataX/LifeCycleDataQueries.cs | 11 +- .../LifeCycleDataX/LifeCycleDataType.cs | 2 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 7 +- .../OpticalDataX/OpticalDataByIdDataLoader.cs | 20 -- .../OpticalDataX/OpticalDataLoaders.cs | 31 +++ .../OpticalDataX/OpticalDataQueries.cs | 11 +- .../GraphQl/OpticalDataX/OpticalDataType.cs | 2 +- backend/src/GraphQl/PaginatedConnection.cs | 75 +++++++ backend/src/GraphQl/PaginatedEdge.cs | 31 +++ .../CreatePhotovoltaicDataMutation.cs | 9 +- .../PhotovoltaicDataByIdDataLoader.cs | 20 -- .../PhotovoltaicDataLoaders.cs | 31 +++ .../PhotovoltaicDataQueries.cs | 13 +- .../PhotovoltaicDataX/PhotovoltaicDataType.cs | 2 +- .../CreateResponseApprovalsMutation.cs | 3 +- .../ResponseApprovalFilterType.cs | 3 +- .../UpdateResponseApprovalsMutation.cs | 3 +- .../src/GraphQl/{ => Scalars}/LocaleType.cs | 6 +- backend/src/GraphQl/Scalars/MyUriType.cs | 110 ++++++++++ .../src/GraphQl/Scalars/NonNegativeIntType.cs | 72 +++++++ backend/src/GraphQl/Sorting.cs | 18 ++ .../src/GraphQl/Users/UserByIdDataLoader.cs | 20 -- backend/src/GraphQl/Users/UserDataLoaders.cs | 31 +++ backend/src/GraphQl/Users/UserType.cs | 3 +- ...ningAndEncryptionCertificateRotationJob.cs | 20 +- .../SpectralToIntegralMethod.cs | 2 +- backend/src/Program.cs | 1 - backend/src/Services/AccessRightsService.cs | 12 +- backend/src/Services/CacheService.cs | 10 +- .../src/Services/ResponseApprovalService.cs | 7 +- backend/src/Services/SigningService.cs | 2 +- backend/src/Startup.cs | 37 +--- backend/src/Utilities/FileHelpers.cs | 8 +- backend/test/AuditableTests.cs | 75 +++++++ backend/test/Database.Tests.csproj | 14 +- 128 files changed, 2191 insertions(+), 1105 deletions(-) delete mode 100644 backend/create-certificates.csx create mode 100644 backend/src/Data/Association.cs create mode 100644 backend/src/Data/AuditableAssociation.cs create mode 100644 backend/src/Data/AuditableEntity.cs create mode 100644 backend/src/Data/IAssociation.cs create mode 100644 backend/src/Data/IAuditable.cs create mode 100644 backend/src/Data/INamed.cs delete mode 100644 backend/src/Extensions/EnumerableExtensions.cs create mode 100644 backend/src/Extensions/LinqExtensions.cs create mode 100644 backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs create mode 100644 backend/src/GraphQl/Associations/AuditableAssociationSortType.cs create mode 100644 backend/src/GraphQl/AuthorizedConnection.cs create mode 100644 backend/src/GraphQl/AuthorizedPaginatedConnection.cs delete mode 100644 backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/CalorimetricDataX/CalorimetricDataLoaders.cs create mode 100644 backend/src/GraphQl/DataLoaders.cs create mode 100644 backend/src/GraphQl/DataX/DataDataLoaders.cs delete mode 100644 backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs delete mode 100644 backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs delete mode 100644 backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs delete mode 100644 backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs rename backend/src/GraphQl/Entities/{EntityFilterType.cs => AuditableEntityFilterType.cs} (90%) rename backend/src/GraphQl/Entities/{EntitySortType.cs => AuditableEntitySortType.cs} (58%) delete mode 100644 backend/src/GraphQl/Entities/EntityByIdDataLoader.cs create mode 100644 backend/src/GraphQl/Extensions/PageExtensions.cs delete mode 100644 backend/src/GraphQl/Extensions/SortingContextExtensions.cs delete mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/GeometricDataX/GeometricDataLoaders.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs delete mode 100644 backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs create mode 100644 backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs create mode 100644 backend/src/GraphQl/GraphQlThrowHelper.cs create mode 100644 backend/src/GraphQl/GraphQlTypeResources.cs delete mode 100644 backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/HygrothermalDataX/HygrothermalDataLoaders.cs delete mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/LifeCycleDataX/LifeCycleDataLoaders.cs delete mode 100644 backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/OpticalDataX/OpticalDataLoaders.cs create mode 100644 backend/src/GraphQl/PaginatedConnection.cs create mode 100644 backend/src/GraphQl/PaginatedEdge.cs delete mode 100644 backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs create mode 100644 backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataLoaders.cs rename backend/src/GraphQl/{ => Scalars}/LocaleType.cs (94%) create mode 100644 backend/src/GraphQl/Scalars/MyUriType.cs create mode 100644 backend/src/GraphQl/Scalars/NonNegativeIntType.cs create mode 100644 backend/src/GraphQl/Sorting.cs delete mode 100644 backend/src/GraphQl/Users/UserByIdDataLoader.cs create mode 100644 backend/src/GraphQl/Users/UserDataLoaders.cs create mode 100644 backend/test/AuditableTests.cs diff --git a/backend/create-certificates.csx b/backend/create-certificates.csx deleted file mode 100644 index 1f92e4c0..00000000 --- a/backend/create-certificates.csx +++ /dev/null @@ -1,44 +0,0 @@ -using System.Linq; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - -// TODO Is there a better way to manage these keys? Is there gonna be a problem in two-years time when the keys become invalid? -// Inspired by https://github.com/openiddict/openiddict-core/blob/78901e3e7e3ee47cf7846a71f758dc9ca110b1a2/src/OpenIddict.Server/OpenIddictServerBuilder.cs#L661-L679 -// and https://github.com/openiddict/openiddict-core/blob/78901e3e7e3ee47cf7846a71f758dc9ca110b1a2/src/OpenIddict.Server/OpenIddictServerBuilder.cs#L661-L679 -foreach (var (fileName, name, flags, password) in new[] { - ("jwt-encryption-certificate.pfx", "Encryption", X509KeyUsageFlags.KeyEncipherment, Args[0]), - ("jwt-signing-certificate.pfx", "Signing", X509KeyUsageFlags.DigitalSignature, Args[1]) -}) -{ - var path = Path.Join("src", fileName); - var certificate = - File.Exists(path) - ? X509CertificateLoader.LoadPkcs12FromFile(path, password) - : null; - if (certificate is null || certificate.NotAfter <= DateTime.Now) - { - var subject = new X500DistinguishedName($"CN=Database OpenId Connect Server {name} Certificate"); - // certificates.LastOrDefault(certificate => certificate.NotBefore < DateTime.Now && certificate.NotAfter > DateTime.Now); - // TODO Is RSA sufficiently secure? Or should we use ECDSA? - using (var algorithm = RSA.Create(keySizeInBits: 2048)) - { - var request = new CertificateRequest( - subject, - algorithm, - HashAlgorithmName.SHA256, - RSASignaturePadding.Pkcs1 - ); - request.CertificateExtensions.Add( - new X509KeyUsageExtension(flags, critical: true) - ); - certificate = request.CreateSelfSigned( - notBefore: DateTimeOffset.UtcNow, - notAfter: DateTimeOffset.UtcNow.AddYears(2) - ); - File.WriteAllBytes( - path, - certificate.Export(X509ContentType.Pfx, password) - ); - } - } -} \ No newline at end of file diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 6761c304..972eb2a4 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "10.0.5", + "version": "10.0.8", "commands": [ "dotnet-ef" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2025.3.3", + "version": "2026.1.1", "commands": [ "jb" ], diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 51f7100c..6a7e65ad 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Net.Http; -using System.Reflection; using System.Security.Cryptography.X509Certificates; using Database.Authentication; using Database.Authorization; @@ -10,6 +9,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using NodaTime; using OpenIddict.Abstractions; using OpenIddict.Client; using Quartz; @@ -21,7 +21,7 @@ public static class AuthConfiguration { private static readonly TimeSpan s_cookieExpirationTimeSpan = TimeSpan.FromDays(1); - private static void BootstrapCertificates() + private static void BootstrapCertificates(IClock clock) { using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); try @@ -34,11 +34,12 @@ private static void BootstrapCertificates() distinguishedName, validOnly: true ); - if (certificates.Count == 0) + if (certificates.Count is 0) { store.Add( JwtSigningAndEncryptionCertificateRotationJob.CreateSigningCertificate( - distinguishedName + distinguishedName, + clock ) ); } @@ -50,11 +51,12 @@ private static void BootstrapCertificates() distinguishedName, validOnly: true ); - if (certificates.Count == 0) + if (certificates.Count is 0) { store.Add( JwtSigningAndEncryptionCertificateRotationJob.CreateEncryptionCertificate( - distinguishedName + distinguishedName, + clock ) ); } @@ -93,10 +95,11 @@ private static IEnumerable FindCertificates(string distinguish public static void ConfigureServices( IServiceCollection services, IWebHostEnvironment environment, - AppSettings appSettings + AppSettings appSettings, + IClock clock ) { - BootstrapCertificates(); + BootstrapCertificates(clock); services.AddScoped(); services.AddScoped(); ConfigureAuthenticationAndAuthorizationServices(services); diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 69fbdf38..f485fdbd 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -4,6 +4,8 @@ using Database.GraphQl; using Database.GraphQl.DataX; using Database.GraphQl.Filters; +using Database.GraphQl.Scalars; +using HotChocolate.AspNetCore; using HotChocolate.Configuration; using HotChocolate.Data; using HotChocolate.Data.Filters; @@ -13,9 +15,10 @@ using HotChocolate.Language; using HotChocolate.Types; using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Descriptors.Definitions; +using HotChocolate.Types.Descriptors.Configurations; using HotChocolate.Types.NodaTime; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -32,8 +35,7 @@ IWebHostEnvironment environment { // Automatic-Persisted-Queries Services services - .AddMemoryCache() - .AddSha256DocumentHashProvider(HashFormat.Hex); // https://chillicream.com/docs/hotchocolate/v15/security/#fips-compliance + .AddMemoryCache(); // GraphQL Server var serverBuilder = services .AddGraphQLServer(); @@ -43,59 +45,92 @@ IWebHostEnvironment environment serverBuilder.TryAddTypeInterceptor(); } serverBuilder - // TODO add warmup task once we upgrade to version 16: https://chillicream.com/docs/hotchocolate/v16/server/warmup - // .AddWarmupTask(async (executor, cancellationToken) => - // { - // await executor.ExecuteAsync("{ __typename }", cancellationToken); - // }) + .AddSha256DocumentHashProvider(HashFormat.Hex) // https://chillicream.com/docs/hotchocolate/v15/security/#fips-compliance + .AddApplicationService() // for `AddHttpRequestInterceptor` + .AddApplicationService>() // for `AddDiagnosticEventListener` .DisableIntrospection(false) // if the introspection result becomes too big we need to disable it in production - .BindRuntimeType() - // Services https://chillicream.com/docs/hotchocolate/v13/integrations/entity-framework#registerdbcontext .RegisterDbContextFactory() + // .AddInstrumentation() .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) // Extensions + .AddNodaTime() + // .AddTypeConverter( + // _ => _.ToDateTimeOffset() + // ) + // .AddTypeConverter( + // _ => OffsetDateTime.FromDateTimeOffset(_) + // ) .AddProjections() .AddFiltering() .AddSorting() .AddConvention() .AddQueryContext() .AddAuthorization() - .AddGlobalObjectIdentification() .AddQueryFieldToMutationPayloads() - .ModifyOptions(options => + .AddGlobalObjectIdentification(_ => + { + // _.MaxAllowedNodeBatchSize = 100; + _.EnsureAllNodesCanBeResolved = true; + } + ) + .ModifyOptions(_ => { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Types/Configuration/Contracts/ISchemaOptions.cs - options.StrictValidation = true; - options.UseXmlDocumentation = false; - options.SortFieldsByName = true; - options.RemoveUnreachableTypes = false; - options.RemoveUnusedTypeSystemDirectives = true; - options.DefaultBindingBehavior = BindingBehavior.Implicit; + _.StrictValidation = true; + _.UseXmlDocumentation = false; + _.SortFieldsByName = true; + _.RemoveUnreachableTypes = false; + _.RemoveUnusedTypeSystemDirectives = true; + _.DefaultBindingBehavior = BindingBehavior.Implicit; // options.DefaultFieldBindingFlags = FieldBindingFlags.InstanceAndStatic; - options.EnableDirectiveIntrospection = true; - options.DefaultDirectiveVisibility = DirectiveVisibility.Public; - options.DefaultResolverStrategy = ExecutionStrategy.Parallel; - options.ValidatePipelineOrder = true; - options.StrictRuntimeTypeValidation = true; - options.EnableOneOf = true; - options.EnsureAllNodesCanBeResolved = true; - options.EnableFlagEnums = false; - options.EnableDefer = false; - options.EnableStream = false; - options.EnableSemanticNonNull = false; - options.StripLeadingIFromInterface = false; - options.EnableTag = true; - options.PublishRootFieldPagesToPromiseCache = true; + _.EnableDirectiveIntrospection = true; + _.EnableOptInFeatures = true; + _.DefaultDirectiveVisibility = DirectiveVisibility.Public; + _.DefaultResolverStrategy = ExecutionStrategy.Parallel; + _.ValidatePipelineOrder = true; + _.StrictRuntimeTypeValidation = true; + _.EnableFlagEnums = false; + _.EnableDefer = false; + _.EnableStream = false; + _.StripLeadingIFromInterface = false; + _.EnableTag = true; + _.PublishRootFieldPagesToPromiseCache = true; + // options.OperationDocumentCacheSize = 200; + // options.PreparedOperationCacheSize = 100; } ) - .ModifyRequestOptions(options => + .ModifyServerOptions(_ => + { + _.AllowedGetOperations = AllowedGetOperations.Query; + _.Batching = AllowedBatching.All; + _.EnableGetRequests = false; + _.EnableMultipartRequests = true; + _.EnableSchemaRequests = true; + // Nitro + _.Tool.DisableTelemetry = true; + _.Tool.Enable = true; // environment.IsDevelopment() + _.Tool.GraphQLEndpoint = GraphQlConstants.EndpointPath; + _.Tool.IncludeCookies = false; + _.Tool.Title = "GraphQL"; + _.Tool.UseBrowserUrlAsGraphQLEndpoint = false; + _.Tool.UseGet = false; + } + ) + .ModifyRequestOptions(_ => { // https://github.com/ChilliCream/hotchocolate/blob/main/src/HotChocolate/Core/src/Execution/Options/RequestExecutorOptions.cs - options.ExecutionTimeout = TimeSpan.FromSeconds(120); - options.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. - /* options.QueryCacheSize = ...; */ - /* options.UseComplexityMultipliers = ...; */ - options.EnableSchemaFileSupport = true; + _.ExecutionTimeout = TimeSpan.FromSeconds(120); + _.IncludeExceptionDetails = !environment.IsProduction(); // Default is `Debugger.IsAttached`. + _.AllowErrorHandlingModeOverride = true; + // options.QueryCacheSize = ...; + // options.UseComplexityMultipliers = ...; + // options.EnableSchemaFileSupport = true; + } + ) + .ModifyCostOptions(_ => + { + _.MaxFieldCost = 10000; + _.MaxTypeCost = 10000; } ) // Configure @@ -108,11 +143,6 @@ IWebHostEnvironment environment // Persisted queries /* .AddFileSystemOperationDocumentStorage("./persisted_operations") */ /* .UsePersistedOperationPipeline(); */ - // HotChocolate uses the default authentication scheme, - // which we set to `null` in `AuthConfiguration` to force - // users to be explicit about what scheme to use when - // making it easier to grasp the various authentication - // flows. .AddHttpRequestInterceptor(async (httpContext, requestExecutor, requestBuilder, cancellationToken) => { await httpContext.RequestServices @@ -125,23 +155,12 @@ await httpContext.RequestServices ) ) // Scalar Types + // TODO Add `MyUuidType` based on https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Types/Scalars/UuidType.cs .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type - .AddType(new UrlType("Url")) - .AddType(new JsonType("Any", BindingBehavior.Implicit)) // https://chillicream.com/blog/2023/02/08/new-in-hot-chocolate-13#json-scalar + .AddType(new MyUriType()) + .AddType(new AnyType("Any")) .AddType() - .AddType() - .AddType() - // .AddType() - // Register converters between NodaTime's `OffsetDateTime` and .NET's - // `DateTimeOffset` to reuse the existing `DateTimeType` - // https://chillicream.com/docs/hotchocolate/v15/defining-a-schema/scalars#custom-converters - .BindRuntimeType() - .AddTypeConverter( - _ => _.ToDateTimeOffset() - ) - .AddTypeConverter( - _ => OffsetDateTime.FromDateTimeOffset(_) - ) + .BindRuntimeType() // Object Types .AddType() // Query, Mutation, Subscription, Object, and Input Types @@ -152,10 +171,11 @@ await httpContext.RequestServices .AddTypes() // Paging .AddDbContextCursorPagingProvider() + // .AddCursorKeySerializer(new OffsetDateTimeCursorKeySerializer()) .ModifyPagingOptions(_ => { - _.MaxPageSize = 100; - _.DefaultPageSize = 100; + _.MaxPageSize = (int)GraphQlConstants.MaximumPageSize; + _.DefaultPageSize = (int)GraphQlConstants.MaximumPageSize; _.IncludeTotalCount = true; _.IncludeNodesField = false; // TODO I actually want to infer connection names from fields (which is the default in HotChocolate). However, the current `database.graphql` schema that I hand-wrote still infers connection names from types. @@ -166,6 +186,26 @@ await httpContext.RequestServices .UseAutomaticPersistedOperationPipeline() .AddInMemoryOperationDocumentStorage(); // Needed by the automatic persisted operation pipeline } + + // + // private sealed class MyUuidType : UuidType + // { + // private const string SpecifiedByString = "https://tools.ietf.org/html/rfc4122"; + // + // public MyUuidType( + // string name, + // string? description = null, + // char defaultFormat = '\0', + // bool enforceFormat = false, + // BindingBehavior bind = BindingBehavior.Explicit + // ) + // : base(name, description, defaultFormat, enforceFormat, + // bind) + // { + // SpecifiedBy = new Uri(SpecifiedByString, UriKind.Absolute); + // } + // } + } // https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Configuration/TypeInterceptor.cs @@ -177,14 +217,14 @@ public override void OnBeforeInitialize(ITypeDiscoveryContext discoveryContext) Console.WriteLine($"[INIT] Discovered type '{discoveryContext.Type.GetType().Name}'"); } - public override void OnBeforeCompleteName(ITypeCompletionContext completionContext, DefinitionBase definition) + public override void OnBeforeCompleteName(ITypeCompletionContext completionContext, TypeSystemConfiguration configuration) { - Console.WriteLine($"[NAME] Finalizing name '{definition.Name}' for type '{completionContext.Type.GetType().Name}'"); + Console.WriteLine($"[NAME] Finalizing name '{configuration.Name}' for type '{completionContext.Type.GetType().Name}'"); } - public override void OnAfterCompleteType(ITypeCompletionContext completionContext, DefinitionBase definition) + public override void OnAfterCompleteType(ITypeCompletionContext completionContext, TypeSystemConfiguration configuration) { - Console.WriteLine($"[DONE] Completed type '{completionContext.Type.GetType().Name}' with name '{definition.Name}'"); + Console.WriteLine($"[DONE] Completed type '{completionContext.Type.GetType().Name}' with name '{configuration.Name}'"); } } @@ -222,7 +262,9 @@ protected override void Configure(IFilterConventionDescriptor descriptor) descriptor.Provider( new QueryableFilterProvider(_ => _ .AddDefaultFieldHandlers() - .AddFieldHandler() + .AddFieldHandler(context => + new QueryableComparableInClosedIntervalHandler(context.TypeConverter, context.InputParser) + ) ) ); } @@ -364,16 +406,24 @@ this IFilterConventionDescriptor descriptor .BindRuntimeType() .BindRuntimeType() .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() .BindRuntimeType() .BindRuntimeType() - // .BindRuntimeType() - // .BindRuntimeType() - // .BindRuntimeType() - // .BindRuntimeType() - .BindRuntimeType() - .BindRuntimeType() - .BindRuntimeType() - .BindRuntimeType(); + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType(); } } diff --git a/backend/src/Controllers/AuthenticationController.cs b/backend/src/Controllers/AuthenticationController.cs index c661c6ef..bd788125 100644 --- a/backend/src/Controllers/AuthenticationController.cs +++ b/backend/src/Controllers/AuthenticationController.cs @@ -46,7 +46,7 @@ protected override void Dispose(bool disposing) } [HttpGet("~/connect/login")] - public ActionResult LogIn(string? returnUrl) + public ActionResult LogIn(string? returnTo) { // Ask the OpenIddict client middleware to redirect the user agent to the identity provider. return Challenge( @@ -60,7 +60,7 @@ public ActionResult LogIn(string? returnUrl) ) { // Only allow local return URLs to prevent open redirect attacks. - RedirectUri = SanitizeReturnUrl(returnUrl) + RedirectUri = SanitizeReturnUrl(returnTo) }, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme ); @@ -69,7 +69,7 @@ public ActionResult LogIn(string? returnUrl) [HttpPost("~/connect/logout")] [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [RequireAntiforgeryToken] - public async Task LogOut(string? returnUrl) + public async Task LogOut(string? returnTo) { // Retrieve the identity stored in the local authentication cookie. If it's not available, // this indicate that the user is already logged out locally (or has not logged in yet). @@ -78,7 +78,7 @@ public async Task LogOut(string? returnUrl) { // Only allow local return URLs to prevent open redirect attacks. // https://learn.microsoft.com/en-us/aspnet/core/security/preventing-open-redirects - return LocalRedirect(SanitizeReturnUrl(returnUrl)); + return LocalRedirect(SanitizeReturnUrl(returnTo)); } // Remove the local authentication cookie before triggering a redirection to the remote server. @@ -98,7 +98,7 @@ public async Task LogOut(string? returnUrl) ) { // Only allow local return URLs to prevent open redirect attacks. - RedirectUri = SanitizeReturnUrl(returnUrl) + RedirectUri = SanitizeReturnUrl(returnTo) }, OpenIddictClientAspNetCoreDefaults.AuthenticationScheme ); @@ -251,11 +251,11 @@ public async Task LogOutCallback(string provider) ); } - private string SanitizeReturnUrl(string? returnUrl) + private string SanitizeReturnUrl(string? returnTo) { return - returnUrl is not null && Url.IsLocalUrl(returnUrl) - ? returnUrl + returnTo is not null && Url.IsLocalUrl(returnTo) + ? returnTo : "/"; } } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 61575e2e..19d9ccd3 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Database.Enumerations; using Database.Extensions; -using Database.GraphQl.Extensions; using GreenDonut.Data; using Laraue.EfCoreTriggers.Common.Extensions; using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; @@ -16,6 +15,7 @@ using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; using NodaTime; using System.Text.Json; +using Database.GraphQl; namespace Database.Data; @@ -27,6 +27,7 @@ public sealed class ApplicationDbContext { private const string DefaultSchemaName = "database"; private readonly string _schemaName; + private readonly IClock _clock; internal const string CalorimetricObserverTypeName = "calorimetric_observer"; internal const string CoatedSideTypeName = "coated_side"; @@ -38,14 +39,16 @@ public sealed class ApplicationDbContext internal const string StandardizerTypeName = "standardizer"; public ApplicationDbContext( - DbContextOptions options + DbContextOptions options, + IClock clock ) : base(options) { - // The schema-name option is set in `Metabase.Startup` by an invocation of - // `UseSchemaName` on a `DbContextOptionsBuilder` instance. + // The schema-name option is set in `Metabase.Startup` by an invocation + // of `UseSchemaName` on a `DbContextOptionsBuilder` instance. var schemaNameOptions = options.FindExtension(); _schemaName = schemaNameOptions is null ? DefaultSchemaName : schemaNameOptions.SchemaName; + _clock = clock; } // https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types#dbcontext-and-dbset @@ -118,6 +121,53 @@ public OffsetDateTimeUtcValueConverter() } } + public override int SaveChanges() + { + UpdateTimestamps(); + return base.SaveChanges(); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + UpdateTimestamps(); + return base.SaveChangesAsync(cancellationToken); + } + + private void UpdateTimestamps() + { + var entries = ChangeTracker + .Entries() + .Where(_ => + _.State == EntityState.Added + || _.State == EntityState.Modified + // || _.State == EntityState.Deleted + ); + var now = _clock.GetUtcNow().ToDateTimeOffset(); + foreach (var entry in entries) + { + switch (entry.State) + { + case EntityState.Added: + if (entry.Entity.CreatedAt == default) + { + entry.Entity.CreatedAt = now; + } + entry.Entity.UpdatedAt = now; + break; + case EntityState.Modified: + entry.Entity.UpdatedAt = now; + break; + // NOTE that soft deletes do not cascade + // case EntityState.Deleted: + // // soft delete + // entry.State = EntityState.Modified; + // entry.Entity.DeletedAt = now; + // entry.Entity.UpdatedAt = now; + // break; + } + } + } + public IQueryable Data(DataKind dataKind) { return dataKind switch @@ -144,38 +194,38 @@ QueryContext queryContext { return ( CalorimetricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ) .Union( GeometricData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ) .Union( HygrothermalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ) .Union( LifeCycleData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ) .Union( OpticalData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ) .Union( PhotovoltaicData.AsQueryable() - .With(queryContext, sort => sort.StabilizeOrder()) .Where(where) + .With(queryContext, Sorting.DefaultEntityOrder) .ToAsyncEnumerable() ); } @@ -515,5 +565,63 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() ) .ToTable("institution_access_rights"); + foreach (var entityType in modelBuilder.Model.GetEntityTypes()) + { + if (typeof(IEntity).IsAssignableFrom(entityType.ClrType)) + { + var entity = modelBuilder.Entity(entityType.ClrType); + // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + entity + .Property(nameof(IEntity.Id)) + .HasDefaultValueSql("gen_random_uuid()"); + // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column + entity + .Property(nameof(IEntity.Version)) + .IsRowVersion(); + } + if (typeof(IAssociation).IsAssignableFrom(entityType.ClrType)) + { + var association = modelBuilder.Entity(entityType.ClrType); + // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column + association + .Property(nameof(IAssociation.Version)) + .IsRowVersion(); + } + if (typeof(IAuditable).IsAssignableFrom(entityType.ClrType)) + { + var auditable = modelBuilder.Entity(entityType.ClrType); + auditable + .Property(nameof(IAuditable.CreatedAt)) + .HasDefaultValueSql("now()"); + auditable + .Property(nameof(IAuditable.UpdatedAt)) + .HasDefaultValueSql("now()"); + // exclude soft-deleted entities with the effect that + // `context..ToList()` only returns rows where + // `DeletedAt` is null and + // `context..IgnoreQueryFilters().ToList()` returns + // all rows + // entity + // .HasQueryFilter((IAuditable _) => _.DeletedAt == null); + } + if (typeof(IEntity).IsAssignableFrom(entityType.ClrType) + && typeof(INamed).IsAssignableFrom(entityType.ClrType)) + { + var entity = modelBuilder.Entity(entityType.ClrType); + // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + entity + .HasIndex(nameof(INamed.Name), nameof(IEntity.Id)) + .IsUnique(); + } + if (typeof(IEntity).IsAssignableFrom(entityType.ClrType) + && typeof(IAuditable).IsAssignableFrom(entityType.ClrType)) + { + var entity = modelBuilder.Entity(entityType.ClrType); + // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation + entity + .HasIndex(nameof(IAuditable.CreatedAt), nameof(IEntity.Id)) + .IsUnique(); + } + } } } \ No newline at end of file diff --git a/backend/src/Data/Association.cs b/backend/src/Data/Association.cs new file mode 100644 index 00000000..08751fa6 --- /dev/null +++ b/backend/src/Data/Association.cs @@ -0,0 +1,8 @@ +namespace Database.Data; + +public abstract class Association +{ + // Configured via `IsRowVersion` in `ApplicationDbContext` instead of the annotation + // [Timestamp] + public uint Version { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html +} \ No newline at end of file diff --git a/backend/src/Data/AuditableAssociation.cs b/backend/src/Data/AuditableAssociation.cs new file mode 100644 index 00000000..044118cd --- /dev/null +++ b/backend/src/Data/AuditableAssociation.cs @@ -0,0 +1,10 @@ +using System; + +namespace Database.Data; + +public abstract class AuditableAssociation +: Association, IAuditable +{ + public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset UpdatedAt { get; set; } +} \ No newline at end of file diff --git a/backend/src/Data/AuditableEntity.cs b/backend/src/Data/AuditableEntity.cs new file mode 100644 index 00000000..c7d71196 --- /dev/null +++ b/backend/src/Data/AuditableEntity.cs @@ -0,0 +1,20 @@ +using System; + +namespace Database.Data; + +public abstract class AuditableEntity +: Entity, IAuditable +{ + public AuditableEntity() + : base() + { + } + + public AuditableEntity(Guid id) + : base(id) + { + } + + public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset UpdatedAt { get; set; } +} \ No newline at end of file diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index eb118c32..26e0d52e 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Extractors; -using NodaTime; namespace Database.Data; @@ -18,7 +17,7 @@ public CalorimetricData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod, double[] gValues, double[] uValues @@ -47,7 +46,7 @@ public CalorimetricData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, double[] gValues, double[] uValues ) : base( diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index eddc6d40..9076ac90 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -3,21 +3,32 @@ using System.Linq; using System.Threading.Tasks; using Database.Enumerations; -using NodaTime; namespace Database.Data; -public abstract class DataX( - Guid? userId, - string locale, - Guid componentId, - string? name, - string? description, - string[] warnings, - Guid creatorId, - OffsetDateTime createdAt -) : Entity, IData +public abstract class DataX : AuditableEntity, IData { + public DataX( + Guid? userId, + string locale, + Guid componentId, + string? name, + string? description, + string[] warnings, + Guid creatorId, + DateTimeOffset createdAt + ) + { + UserId = userId; + Locale = locale; + ComponentId = componentId; + Name = name; + Description = description; + Warnings = warnings; + CreatorId = creatorId; + CreatedAt = createdAt; + } + protected DataX( Guid? userId, string locale, @@ -26,7 +37,7 @@ protected DataX( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod ) : this( @@ -49,7 +60,7 @@ public void Update( string? name, string? description, string[] warnings, - OffsetDateTime createdAt, + DateTimeOffset createdAt, Guid creatorId ) { @@ -72,14 +83,13 @@ public void Retract() PublishingState = PublishingState.RETRACTED; } - public Guid? UserId { get; private set; } = userId; - public string Locale { get; private set; } = locale; - public Guid ComponentId { get; private set; } = componentId; - public string? Name { get; private set; } = name; - public string? Description { get; private set; } = description; - public string[] Warnings { get; private set; } = warnings; - public Guid CreatorId { get; private set; } = creatorId; - public OffsetDateTime CreatedAt { get; private set; } = createdAt; + public Guid? UserId { get; private set; } + public string Locale { get; private set; } + public Guid ComponentId { get; private set; } + public string? Name { get; private set; } + public string? Description { get; private set; } + public string[] Warnings { get; private set; } + public Guid CreatorId { get; private set; } public AppliedMethod AppliedMethod { get; private set; } = default!; public ICollection Approvals { get; } = []; diff --git a/backend/src/Data/Entity.cs b/backend/src/Data/Entity.cs index b3c54bfe..d831fdc6 100644 --- a/backend/src/Data/Entity.cs +++ b/backend/src/Data/Entity.cs @@ -1,16 +1,25 @@ using System; -// using System.ComponentModel.DataAnnotations.Schema; - namespace Database.Data; public abstract class Entity : IEntity { - public Guid Id { get; private set; } + public Entity() + { + } + + public Entity(Guid id) + { + Id = id; + } + + public Guid Id { get; init; } // [NotMapped] // public Guid Uuid { get => Id; } + // Configured via `IsRowVersion` in `ApplicationDbContext` instead of the annotation + // [Timestamp] public uint Version { get; private set; } // https://www.npgsql.org/efcore/modeling/concurrency.html } \ No newline at end of file diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 5be4f13c..bb5098d8 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Extractors; -using NodaTime; namespace Database.Data; @@ -18,7 +17,7 @@ public GeometricData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod, double[] widths, double[] heights, @@ -48,7 +47,7 @@ public GeometricData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, double[] widths, double[] heights, double[] thicknesses diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index a15ace42..e7537619 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -14,7 +14,7 @@ namespace Database.Data; public sealed class GetHttpsResource -: Entity +: AuditableEntity { public const string FilesDirectoryPath = "./files/"; public const string TableName = "get_https_resource"; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 3d33a023..79f79f12 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; -using NodaTime; namespace Database.Data; @@ -17,7 +16,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod ) : base( userId, @@ -42,7 +41,7 @@ public HygrothermalData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt + DateTimeOffset createdAt ) : base( userId, locale, diff --git a/backend/src/Data/IAssociation.cs b/backend/src/Data/IAssociation.cs new file mode 100644 index 00000000..a49e56f6 --- /dev/null +++ b/backend/src/Data/IAssociation.cs @@ -0,0 +1,7 @@ +namespace Database.Data; + +public interface IAssociation +{ + // Configured via `[Timestamp]` in `Association` + public uint Version { get; } // https://www.npgsql.org/efcore/modeling/concurrency.html +} \ No newline at end of file diff --git a/backend/src/Data/IAuditable.cs b/backend/src/Data/IAuditable.cs new file mode 100644 index 00000000..2e059fc4 --- /dev/null +++ b/backend/src/Data/IAuditable.cs @@ -0,0 +1,13 @@ +using System; + +namespace Database.Data; + +public interface IAuditable +{ + // TODO Switch to NodaTime `OffsetDateTime` once there is a `ICursorKeySerializer` implementation for it. Then sorting by `CreatedAt` and `UpdatedAt` with pagination will keep working. + public DateTimeOffset CreatedAt { get; set; } + public DateTimeOffset UpdatedAt { get; set; } + + // soft delete + // public Instant? DeletedAt { get; set; } +} \ No newline at end of file diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index 79a242e4..f00832d8 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Database.Enumerations; using Database.GraphQl; -using NodaTime; namespace Database.Data; @@ -15,7 +14,7 @@ namespace Database.Data; [JsonDerivedType(typeof(LifeCycleData), typeDiscriminator: nameof(LifeCycleData))] [JsonDerivedType(typeof(OpticalData), typeDiscriminator: nameof(OpticalData))] [JsonDerivedType(typeof(PhotovoltaicData), typeDiscriminator: nameof(PhotovoltaicData))] -public interface IData : IEntity +public interface IData : IEntity, IAuditable { public static readonly Guid BedJsonDataFormatId = new("9ca9e8f5-94bf-4fdd-81e3-31a58d7ca708"); @@ -25,7 +24,6 @@ public interface IData : IEntity string? Description { get; } string[] Warnings { get; } Guid CreatorId { get; } - OffsetDateTime CreatedAt { get; } AppliedMethod AppliedMethod { get; } ICollection Approvals { get; } ICollection Resources { get; } @@ -62,7 +60,7 @@ void Update( string? name, string? description, string[] warnings, - OffsetDateTime createdAt, + DateTimeOffset createdAt, Guid creatorId ); diff --git a/backend/src/Data/INamed.cs b/backend/src/Data/INamed.cs new file mode 100644 index 00000000..a4f46fc2 --- /dev/null +++ b/backend/src/Data/INamed.cs @@ -0,0 +1,6 @@ +namespace Database.Data; + +public interface INamed +{ + public string Name { get; } +} \ No newline at end of file diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs index 95dc8c2a..10db044f 100644 --- a/backend/src/Data/InstitutionAccessRights.cs +++ b/backend/src/Data/InstitutionAccessRights.cs @@ -16,7 +16,7 @@ public sealed class InstitutionAccessRights( uint? allowedDatasetsPerTime, Duration period ) -: Entity +: AuditableEntity { public Guid InstitutionId { get; set; } = institutionId; public uint? AllowedUserCount { get; set; } = allowedUserCount; @@ -38,7 +38,7 @@ Duration period [Projectable] public bool HasRestrictionsByUser => AllowedUserCount != null; - internal bool IsDataRestrictedByTime(IData dataItem, CacheService cacheService, out string? reason) + internal bool IsDataRestrictedByTime(IData dataItem, IClock clock, CacheService cacheService, out string? reason) { var isRestricted = false; reason = null; @@ -46,7 +46,7 @@ internal bool IsDataRestrictedByTime(IData dataItem, CacheService cacheService, if (AllowedDatasetsPerTime is not null) { var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPeriod(InstitutionId); - if (accessesPerPeriod.StartTime + Period < OffsetDateTime.UtcNow) + if (accessesPerPeriod.StartTime + Period < clock.GetUtcNow()) { if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) { diff --git a/backend/src/Data/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs index 4290aafc..c5b67c55 100644 --- a/backend/src/Data/LifeCycleData.cs +++ b/backend/src/Data/LifeCycleData.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; -using NodaTime; namespace Database.Data; @@ -17,7 +16,7 @@ public LifeCycleData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod ) : base( userId, @@ -42,7 +41,7 @@ public LifeCycleData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt + DateTimeOffset createdAt ) : base( userId, locale, diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 7f0173c0..9c929b5c 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -5,7 +5,6 @@ using Database.Enumerations; using Database.Enumerations.DataPoints; using Database.Extractors; -using NodaTime; namespace Database.Data; @@ -20,7 +19,7 @@ public OpticalData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, CoatedSide? coatedSide, @@ -65,7 +64,7 @@ public OpticalData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, CoatedSide? coatedSide, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 41d15961..9ec61855 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; -using NodaTime; namespace Database.Data; @@ -17,7 +16,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt, + DateTimeOffset createdAt, AppliedMethod appliedMethod ) : base( userId, @@ -42,7 +41,7 @@ public PhotovoltaicData( string? description, string[] warnings, Guid creatorId, - OffsetDateTime createdAt + DateTimeOffset createdAt ) : base( userId, locale, diff --git a/backend/src/Data/User.cs b/backend/src/Data/User.cs index 1ca1b218..858e7a98 100644 --- a/backend/src/Data/User.cs +++ b/backend/src/Data/User.cs @@ -4,7 +4,7 @@ public sealed class User( string subject, string name ) -: Entity +: AuditableEntity { public string Subject { get; private set; } = subject; public string Name { get; private set; } = name; diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 27942a7b..baa7653e 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -13,33 +13,33 @@ - - - + - - - - - - - - - - + + + + + + + + + + + - - - - + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + @@ -47,25 +47,25 @@ - - - - - - - - - - - - + + + + + + + + + + + + - + diff --git a/backend/src/Extensions/EnumerableExtensions.cs b/backend/src/Extensions/EnumerableExtensions.cs deleted file mode 100644 index c7143af6..00000000 --- a/backend/src/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; - -namespace Database.Extensions; - -public static class EnumerableExtensions -{ - [Pure] - public static IEnumerable NotNull(this IEnumerable enumerable) where T : class - { - return enumerable.Where(item => item is not null).Select(item => item!); - } - - [Pure] - public static IEnumerable NotNull(this IEnumerable enumerable) where T : struct - { - return enumerable.Where(item => item.HasValue).Select(item => item!.Value); - } -} \ No newline at end of file diff --git a/backend/src/Extensions/LinqExtensions.cs b/backend/src/Extensions/LinqExtensions.cs new file mode 100644 index 00000000..346869c1 --- /dev/null +++ b/backend/src/Extensions/LinqExtensions.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices; + +namespace Database.Extensions; + +public enum OrderDirection +{ + ASCENDING, + DESCENDING +} + +public static class LinqExtensions +{ + [Pure] + public static IEnumerable If( + this IEnumerable source, + bool condition, + Func, IEnumerable> transform + ) + { + return condition ? transform(source) : source; + } + + [Pure] + public static IQueryable If( + this IQueryable source, + bool condition, + Func, + IQueryable> transform + ) + { + return condition ? transform(source) : source; + } + + [Pure] + public static List IfList( + this List source, + bool condition, + Func, List> transform + ) + { + return condition ? transform(source) : source; + } + + [Pure] + public static List ToReversed(this List source) + { + var copy = new List(source); + copy.Reverse(); + return copy; + } + + [Pure] + public static T? GetAtOrDefault(this T[] array, int index, T? defaultValue = default) where T : class + { + return (index >= 0 && index < array.Length) ? array[index] : defaultValue; + } + + [Pure] + public static T? GetFirstOrDefault(this T[] array) where T : class + { + return array.Length > 0 ? array[0] : default; + } + + [Pure] + public static T? GetAtOrDefault(this IReadOnlyList list, int index) where T : class + { + return (index >= 0 && index < list.Count) ? list[index] : default; + } + + [Pure] + public static T? GetFirstOrDefault(this IReadOnlyList list) where T : class + { + return list.Count > 0 ? list[0] : default; + } + + [Pure] + public static T? GetLastOrDefault(this IReadOnlyList list) where T : class + { + return list.Count > 0 ? list[^1] : default; + } + + [Pure] + public static IEnumerable NotNull(this IEnumerable enumerable) where T : class + { + return enumerable.Where(item => item is not null).Select(item => item!); + } + + [Pure] + public static IEnumerable NotNull(this IEnumerable enumerable) where T : struct + { + return enumerable.Where(item => item.HasValue).Select(item => item!.Value); + } + + [Pure] + public static IOrderedQueryable OrderByDirection( + this IQueryable source, + Expression> keySelector, + OrderDirection direction + ) + { + return direction is OrderDirection.ASCENDING + ? source.OrderBy(keySelector) + : source.OrderByDescending(keySelector); + } + + + [Pure] + public static IEnumerable Interleave(this IEnumerable> sequences) + { + var enumerators = new LinkedList>(); + try + { + foreach (var sequence in sequences) + { + var enumerator = sequence.GetEnumerator(); + if (enumerator.MoveNext()) + { + enumerators.AddLast(enumerator); + yield return enumerator.Current; + } + else + { + enumerator.Dispose(); + } + } + var node = enumerators.First; + while (node is { Value: var enumerator, Next: var nextNode }) + { + if (enumerator.MoveNext()) + { + yield return enumerator.Current; + } + else + { + enumerators.Remove(node); + enumerator.Dispose(); + } + node = nextNode ?? enumerators.First; + } + } + finally + { + foreach (var enumerator in enumerators) + enumerator.Dispose(); + } + } + + [Pure] + public static IEnumerable Scan( + this IEnumerable source, + TAccumulate seed, + Func function) + { + var accumulate = seed; + foreach (var item in source) + { + (accumulate, var result) = function(accumulate, item); + yield return result; + } + } + + [Pure] + public static List Rotate( + this List list, + Predicate after + ) + where T : class + { + if (list.Count is 0) + { + return list; + } + var afterIndex = list.FindIndex(after); + if (afterIndex is -1) + { + return list; + } + var index = (afterIndex + 1) % list.Count; + var result = new List(list.Count); + var span = CollectionsMarshal.AsSpan(list); + result.AddRange(span.Slice(index)); + result.AddRange(span.Slice(0, index)); + return result; + } +} \ No newline at end of file diff --git a/backend/src/Extensions/NodaTimeExtensions.cs b/backend/src/Extensions/NodaTimeExtensions.cs index 9f5edc65..80d21035 100644 --- a/backend/src/Extensions/NodaTimeExtensions.cs +++ b/backend/src/Extensions/NodaTimeExtensions.cs @@ -4,13 +4,18 @@ namespace Database.Extensions; public static class NodaTimeExtensions { - extension(OffsetDateTime) + public static OffsetDateTime GetUtcNow(this IClock clock) { - public static OffsetDateTime UtcNow => - SystemClock.Instance - .GetCurrentInstant() - .WithOffset(Offset.Zero); + return clock.GetCurrentInstant().WithOffset(Offset.Zero); + } + public static int CompareTo(this OffsetDateTime current, OffsetDateTime other) + { + return OffsetDateTime.Comparer.Instant.Compare(current, other); + } + + extension(OffsetDateTime) + { public static bool operator >(OffsetDateTime x, OffsetDateTime y) { return OffsetDateTime.Comparer.Instant.Compare(x, y) > 0; diff --git a/backend/src/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 435228db..5d006340 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -5,6 +5,21 @@ namespace Database.Extensions; public static class StringExtensions { + public static string FirstCharToLower(this string value) + { + return string.IsNullOrEmpty(value) + || !char.IsLetter(value, 0) + || char.IsLower(value, 0) + ? value + : char.ToLowerInvariant(value[0]) + value[1..]; + } + + public static string? NullIfEmpty(this string value) + => string.IsNullOrEmpty(value) ? null : value; + + public static string? NullIfWhitespace(this string value) + => string.IsNullOrWhiteSpace(value) ? null : value; + public static string Base64Encode(this string plainText) { return Convert.ToBase64String( diff --git a/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs b/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs new file mode 100644 index 00000000..70059ad5 --- /dev/null +++ b/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs @@ -0,0 +1,19 @@ +using HotChocolate.Data.Filters; +using Database.Data; + +namespace Database.GraphQl.Associations; + +public abstract class AuditableAssociationFilterType + : FilterInputType + where TAssociation : IAssociation, IAuditable +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs b/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs new file mode 100644 index 00000000..05109771 --- /dev/null +++ b/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs @@ -0,0 +1,19 @@ +using HotChocolate.Data.Sorting; +using Database.Data; + +namespace Database.GraphQl.Associations; + +public abstract class AuditableAssociationSortType + : SortInputType + where TAssociation : IAssociation, IAuditable +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.BindFieldsExplicitly(); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AuthorizedConnection.cs b/backend/src/GraphQl/AuthorizedConnection.cs new file mode 100644 index 00000000..9a13a8b6 --- /dev/null +++ b/backend/src/GraphQl/AuthorizedConnection.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate.CostAnalysis.Types; +using Database.Data; + +namespace Database.GraphQl; + +public abstract class AuthorizedConnection( + TSubject subject, + Func createEdge, + Func> isAuthorized, + QueryContext queryContext +) : Connection(subject, createEdge, queryContext) + where TSubject : IEntity + where TAssociationsByOneIdDataLoader : IDataLoader +{ + [Cost(0)] + public async IAsyncEnumerable GetEdgesAsync( + ClaimsPrincipal claimsPrincipal, + TAuthorization authorization, + TAssociationsByOneIdDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + if (!await isAuthorized(claimsPrincipal, Subject, authorization, cancellationToken)) + { + yield break; + } + await foreach (var edge in GetEdgesAsync(dataLoader, cancellationToken)) + { + yield return edge; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AuthorizedPaginatedConnection.cs b/backend/src/GraphQl/AuthorizedPaginatedConnection.cs new file mode 100644 index 00000000..c418864d --- /dev/null +++ b/backend/src/GraphQl/AuthorizedPaginatedConnection.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate.CostAnalysis.Types; +using Database.Data; + +namespace Database.GraphQl; + +public abstract class AuthorizedPaginatedConnection( + TSubject subject, + Func createEdge, + Func> isAuthorized, + PagingArguments pagingArguments, + QueryContext queryContext +) : PaginatedConnection( + subject, + createEdge, + pagingArguments, + queryContext +) + where TSubject : IEntity + where TAssociation : class + where TAssociationsByOneIdDataLoader : IDataLoader> +{ + [Cost(0)] + public async IAsyncEnumerable GetEdgesAsync( + ClaimsPrincipal claimsPrincipal, + TAuthorization authorization, + TAssociationsByOneIdDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + if (!await isAuthorized(claimsPrincipal, authorization, cancellationToken)) + { + yield break; + } + await foreach (var edge in base.GetEdgesAsync(dataLoader, cancellationToken)) + { + yield return edge; + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs deleted file mode 100644 index 52cbce26..00000000 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.CalorimetricDataX; - -public sealed class CalorimetricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.CalorimetricData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataLoaders.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataLoaders.cs new file mode 100644 index 00000000..eea3cf77 --- /dev/null +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.CalorimetricDataX; + +public sealed class CalorimetricDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetCalorimetricDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.CalorimetricData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 3bd1573b..99684588 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -19,15 +18,12 @@ public sealed class CalorimetricDataQueries : DataQueriesBase { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllCalorimetricDataAsync( + public Task> GetAllCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -36,7 +32,6 @@ CancellationToken cancellationToken context.CalorimetricData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -47,11 +42,10 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingCalorimetricDataAsync( + public Task> GetAllPendingCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -61,7 +55,6 @@ CancellationToken cancellationToken context.CalorimetricData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs index 6951cdf9..f395b413 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.CalorimetricDataX; public sealed class CalorimetricDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 5fa7bbbb..1c84da8e 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -8,6 +8,7 @@ using Database.Data; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -21,7 +22,7 @@ public sealed record CreateCalorimetricDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, @@ -106,6 +107,7 @@ public async Task CreateCalorimetricDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -135,6 +137,7 @@ CancellationToken cancellationToken CreateCalorimetricDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateCalorimetricDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -167,4 +170,4 @@ CancellationToken cancellationToken return NewPayload(calorimetricData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index 595ae887..7d737d3b 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -4,41 +4,48 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Database.Data; using GreenDonut; using GreenDonut.Data; +using HotChocolate.CostAnalysis.Types; +using Database.Data; namespace Database.GraphQl; -public abstract class Connection( +public abstract class Connection( TSubject subject, Func createEdge, QueryContext queryContext - ) +) where TSubject : IEntity - where TAssociationsByAssociateIdDataLoader : IDataLoader + where TAssociationsByOneIdDataLoader : IDataLoader { - private readonly Func _createEdge = createEdge; - private readonly QueryContext _queryContext = queryContext; - protected TSubject Subject { get; } = subject; + [Cost(0)] public async Task GetTotalCountAsync( - TAssociationsByAssociateIdDataLoader dataLoader, + TAssociationsByOneIdDataLoader dataLoader, CancellationToken cancellationToken ) { - return (uint)(await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)).Length; + return (uint)( + ( + await dataLoader + .With(queryContext) + .LoadAsync(Subject.Id, cancellationToken) + ) + ?.Length ?? 0 + ); } + [Cost(0)] public async IAsyncEnumerable GetEdgesAsync( - TAssociationsByAssociateIdDataLoader dataLoader, + TAssociationsByOneIdDataLoader dataLoader, [EnumeratorCancellation] CancellationToken cancellationToken ) { - foreach (var association in await dataLoader.With(_queryContext).LoadRequiredAsync(Subject.Id, cancellationToken)) + foreach (var association in await dataLoader.With(queryContext).LoadAsync(Subject.Id, cancellationToken) ?? []) { - yield return _createEdge(association); + yield return createEdge(association); } } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataLoaders.cs b/backend/src/GraphQl/DataLoaders.cs new file mode 100644 index 00000000..e3f59548 --- /dev/null +++ b/backend/src/GraphQl/DataLoaders.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut.Data; +using LinqKit; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl; + +public abstract class DataLoaders +{ + public static async ValueTask> GetEntityByIdAsync + ( + IReadOnlyList ids, + Func> getEntities, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TEntity : class, IEntity, IAuditable + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return await getEntities(databaseContext) + .AsNoTrackingWithIdentityResolution() + .Where(_ => ids.Contains(_.Id)) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToDictionaryAsync(_ => _.Id, cancellationToken); + } + + public static async ValueTask> GetManyByOneIdAsync( + IReadOnlyList ids, + Func> getMany, + Expression> getOneId, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TMany : class, IEntity, IAuditable + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return + await getMany(databaseContext) + .AsExpandable() + .AsNoTracking() + .Where(_ => ids.Contains(getOneId.Invoke(_))) + .With(queryContext, Sorting.DefaultEntityOrder) + .GroupBy(getOneId) + .Select(_ => new { _.Key, Items = _.ToArray() }) + .ToDictionaryAsync(_ => _.Key, _ => _.Items, cancellationToken); + } + + public static async ValueTask>> GetManyByOneIdAsync( + IReadOnlyList ids, + Func> getMany, + Expression> getOneId, + PagingArguments pagingArguments, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TMany : class, IEntity, IAuditable + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return + await getMany(databaseContext) + .AsExpandable() + .AsNoTracking() + .Where(_ => ids.Contains(getOneId.Invoke(_))) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToBatchPageAsync(getOneId, pagingArguments, cancellationToken); + } + + public static async ValueTask> GetAssociationsByOneIdAsync( + IReadOnlyList ids, + Func> getAssociations, + Expression> getOneId, + Expression> getOtherId, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TAssociation : class, IAssociation, IAuditable + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return + await getAssociations(databaseContext) + .AsExpandable() + .AsNoTracking() + .Where(_ => ids.Contains(getOneId.Invoke(_))) + .With(queryContext, _ => _.AddDescending(getOneId).AddDescending(getOtherId)) + .GroupBy(getOneId) + .Select(_ => new { _.Key, Items = _.ToArray() }) + .ToDictionaryAsync(_ => _.Key, _ => _.Items, cancellationToken); + } + + public static async ValueTask>> GetAssociationsByOneIdAsync( + IReadOnlyList ids, + Func> getAssociations, + Expression> getOneId, + Expression> getOtherId, + PagingArguments pagingArguments, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + where TAssociation : class, IAssociation, IAuditable + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return + await getAssociations(databaseContext) + .AsExpandable() + .AsNoTracking() + .Where(_ => ids.Contains(getOneId.Invoke(_))) + .With(queryContext, _ => _.AddDescending(getOneId).AddDescending(getOtherId)) + .ToBatchPageAsync(getOneId, pagingArguments, cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 378f9927..7471b6c8 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -16,7 +16,7 @@ namespace Database.GraphQl.DataX; public interface IValidateCreateInput { Guid ComponentId { get; } - OffsetDateTime CreatedAt { get; } + DateTimeOffset CreatedAt { get; } Guid CreatorId { get; } AppliedMethodInput AppliedMethod { get; } RootGetHttpsResourceInput RootResource { get; } @@ -42,6 +42,7 @@ protected async Task> ValidateAsync( TErrorCode unknownCrossDatabaseData, IDataFormatByIdDataLoader dataFormatByIdDataLoader, TErrorCode unknownDataFormatErrorCode, + IClock clock, CancellationToken cancellationToken ) { @@ -56,7 +57,7 @@ CancellationToken cancellationToken ) ); } - if (input.CreatedAt > OffsetDateTime.UtcNow) + if (input.CreatedAt > clock.GetUtcNow().ToDateTimeOffset()) { errors.Add( NewError( diff --git a/backend/src/GraphQl/DataX/DataConnection.cs b/backend/src/GraphQl/DataX/DataConnection.cs index a6e4b206..23315c1f 100644 --- a/backend/src/GraphQl/DataX/DataConnection.cs +++ b/backend/src/GraphQl/DataX/DataConnection.cs @@ -1,6 +1,4 @@ -using System; using System.Collections.Generic; -using System.Linq; using HotChocolate.Types.Pagination; namespace Database.GraphQl.DataX; @@ -9,11 +7,11 @@ public sealed class DataConnection( IReadOnlyList edges, uint totalCount, ConnectionPageInfo pageInfo - ) - : DataConnectionBase( - edges, - totalCount, - pageInfo - ) +) +: DataConnectionBase( + edges, + totalCount, + pageInfo +) { } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataConnectionBase.cs b/backend/src/GraphQl/DataX/DataConnectionBase.cs index ae976dd8..2d7a0aa0 100644 --- a/backend/src/GraphQl/DataX/DataConnectionBase.cs +++ b/backend/src/GraphQl/DataX/DataConnectionBase.cs @@ -1,7 +1,6 @@ -using System; using System.Collections.Generic; +using Database.GraphQl.Scalars; using HotChocolate; -using HotChocolate.Types; using HotChocolate.Types.Pagination; namespace Database.GraphQl.DataX; @@ -10,7 +9,7 @@ public abstract class DataConnectionBase( IReadOnlyList edges, uint totalCount, ConnectionPageInfo pageInfo - ) +) { public IReadOnlyList Edges { get; } = edges; diff --git a/backend/src/GraphQl/DataX/DataDataLoaders.cs b/backend/src/GraphQl/DataX/DataDataLoaders.cs new file mode 100644 index 00000000..26ea47c6 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataDataLoaders.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.DataX; + +public sealed class DataDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetHttpsResourcesByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources, + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetHttpsResourceTreeNonRootVerticesByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId != null), + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetHttpsResourceTreeRootByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId == null), + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index 7f56ab86..001f734f 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -5,8 +5,8 @@ namespace Database.GraphQl.DataX; public abstract class DataFilterTypeBase - : EntityFilterType - where TData : IData + : AuditableEntityFilterType + where TData : IData, IAuditable { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -19,7 +19,6 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.Description); descriptor.Field(x => x.ComponentId); descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.CreatedAt); descriptor.Field(x => x.AppliedMethod); descriptor.Field(x => x.Approvals); descriptor.Field(x => x.Resources); diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 6885c5fe..d463ec65 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using Database.Data; using Database.Enumerations; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Types; diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 216b0ec6..17d29e04 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -1,16 +1,15 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; -using Database.GraphQl.Entities; using Database.GraphQl.Extensions; +using Database.GraphQl.Scalars; using Database.Services; +using GreenDonut.Data; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using Microsoft.EntityFrameworkCore; @@ -19,33 +18,31 @@ namespace Database.GraphQl.DataX; public abstract class DataQueriesBase where TData : class, IData { - protected async Task> GetAllDataAsync( + protected async Task> GetAllDataAsync( DbSet data, [GraphQLType] string? locale, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); - var filteredData = + var sortedAndFilteredData = data.AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder); + if (await sortedAndFilteredData.AnyAsync(_ => _.DataAccessRights.HasRestrictions, cancellationToken)) { - return filteredData; + sortedAndFilteredData = (await accessRightsService.ApplyAccessRightsOnData(sortedAndFilteredData, cancellationToken)).AsQueryable(); } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return await sortedAndFilteredData + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } - protected async Task> GetAllPendingDataAsync( + protected async Task> GetAllPendingDataAsync( DbSet data, [GraphQLType] string? locale, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -53,19 +50,22 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return Enumerable.Empty().AsQueryable(); + return await Enumerable.Empty() + .AsQueryable() + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } - sorting.StabilizeOrder(); - var filteredData = + var sortedAndFilteredData = data.AsNoTracking() .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) - .Sort(resolverContext) - .Filter(resolverContext); - if (!await filteredData.AnyAsync(x => x.DataAccessRights.HasRestrictions, cancellationToken)) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder); + if (await sortedAndFilteredData.AnyAsync(_ => _.DataAccessRights.HasRestrictions, cancellationToken)) { - return filteredData; + sortedAndFilteredData = (await accessRightsService.ApplyAccessRightsOnData(sortedAndFilteredData, cancellationToken)).AsQueryable(); } - return await accessRightsService.ApplyAccessRightsOnData(filteredData, cancellationToken); + return await sortedAndFilteredData + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } protected Task HasDataAsync( @@ -77,14 +77,14 @@ CancellationToken cancellationToken { return data.AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) - .Filter(resolverContext) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) .AnyAsync(cancellationToken); } protected async Task GetDataAsync( Guid id, [GraphQLType] string? locale, - EntityByIdDataLoader byId, + GreenDonut.DataLoaderBase byId, AccessRightsService accessRightsService, CancellationToken cancellationToken ) diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 68ab7997..a605d4fe 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,8 +1,6 @@ -using System; using System.Threading; using System.Threading.Tasks; using Database.Data; -using Database.Extensions; using Database.GraphQl.Extensions; using Database.GraphQl.GetHttpsResources; using GreenDonut; @@ -10,7 +8,6 @@ using HotChocolate; using HotChocolate.Data; using HotChocolate.Resolvers; -using NodaTime; namespace Database.GraphQl.DataX; @@ -18,29 +15,22 @@ public sealed class DataResolvers { [UseFiltering] [UseSorting] - public async Task GetGetHttpsResources( + public Task GetHttpsResources( [Parent] IData data, IResolverContext resolverContext, - GetHttpsResourcesByDataIdDataLoader byId, + IHttpsResourcesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - var queryContext = resolverContext.GetQueryContext(); - return await - byId - .With(queryContext) + return byId + .With(resolverContext.GetQueryContext()) .LoadRequiredAsync(data.Id, cancellationToken); } - public GetHttpsResourceTree GetGetHttpsResourceTree( + public GetHttpsResourceTree GetHttpsResourceTree( [Parent] IData data ) { return new GetHttpsResourceTree(data); } - - public OffsetDateTime GetTimestamp() - { - return OffsetDateTime.UtcNow; - } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataSortTypeBase.cs b/backend/src/GraphQl/DataX/DataSortTypeBase.cs index 880630de..c5e92791 100644 --- a/backend/src/GraphQl/DataX/DataSortTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -5,8 +5,8 @@ namespace Database.GraphQl.DataX; public abstract class DataSortTypeBase - : EntitySortType - where TData : IData + : AuditableEntitySortType + where TData : IData, IAuditable { protected override void Configure( ISortInputTypeDescriptor descriptor @@ -18,7 +18,6 @@ ISortInputTypeDescriptor descriptor descriptor.Field(x => x.Description); descriptor.Field(x => x.ComponentId); descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.CreatedAt); descriptor.Field(x => x.AppliedMethod); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 0af49f42..101b46ba 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,4 +1,5 @@ using Database.Data; +using Database.GraphQl.Scalars; using HotChocolate.Types; namespace Database.GraphQl.DataX; @@ -16,27 +17,27 @@ IInterfaceTypeDescriptor descriptor { // `1..` is a range as introduced in https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges descriptor.Name(nameof(IData)[1..]); + descriptor + .Field(_ => _.PublishingState) + .Ignore(); descriptor .Field(GraphQlConstants.UuidFieldName) .Type>(); + descriptor + .Field(_ => _.UpdatedAt) + .Name(TimestampFieldName); + descriptor + .Field(x => x.Locale) + .Type>(); descriptor .Field(DatabaseIdFieldName) .Type>() .Resolve(_ => appSettings.DatabaseId); - descriptor - .Field(TimestampFieldName) - .Type>(); descriptor .Field(ResourceTreeFieldName) .Type>>(); - descriptor - .Field(x => x.Locale) - .Type>(); descriptor .Field(x => x.Approval) .Type>>(); - descriptor - .Field(_ => _.PublishingState) - .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index 4c8373ef..bcfcda17 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -1,5 +1,7 @@ using System; using Database.Data; +using Database.GraphQl.Entities; +using Database.GraphQl.Scalars; using GreenDonut; using HotChocolate.Types; @@ -8,31 +10,30 @@ namespace Database.GraphQl.DataX; public abstract class DataTypeBase : EntityType where TData : IData - where TDataByIdDataLoader : IDataLoader + where TDataByIdDataLoader : IDataLoader { protected override void Configure( IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(_ => _.PublishingState) + .Ignore(); + descriptor + .Field(_ => _.UpdatedAt) + .Name(DataType.TimestampFieldName); descriptor .Field(x => x.Locale) .Type>(); descriptor .Field(x => x.Resources) - .ResolveWith(t => t.GetGetHttpsResources(default!, default!, default!, default!)); + .ResolveWith(t => t.GetHttpsResources(default!, default!, default!, default!)); descriptor .Field(DataType.ResourceTreeFieldName) - .ResolveWith(t => t.GetGetHttpsResourceTree(default!)); - descriptor - .Field(DataType.TimestampFieldName) - .Type>() - .ResolveWith(t => t.GetTimestamp()); + .ResolveWith(t => t.GetHttpsResourceTree(default!)); descriptor .Field(x => x.Approval) .Type>>(); - descriptor - .Field(_ => _.PublishingState) - .Ignore(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 5751c639..76c9c24a 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -16,7 +16,7 @@ Data.IData data ) { public async Task GetRoot( - GetHttpsResourceTreeRootByDataIdDataLoader byId, + IHttpsResourceTreeRootByDataIdDataLoader byId, CancellationToken cancellationToken ) { @@ -30,14 +30,13 @@ CancellationToken cancellationToken [UseSorting] public async Task> GetNonRootVertices( IResolverContext resolverContext, - GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, + IHttpsResourceTreeNonRootVerticesByDataIdDataLoader byId, CancellationToken cancellationToken ) { - var queryContext = resolverContext.GetQueryContext(); return ( await byId - .With(queryContext) + .With(resolverContext.GetQueryContext()) .LoadRequiredAsync(data.Id, cancellationToken) ) .Select(v => diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs deleted file mode 100644 index 51446c32..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX; - -public sealed class GetHttpsResourceTreeNonRootVerticesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : AssociationsByAssociateIdDataLoader( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId != null && ids.Contains( - x.CalorimetricDataId - ?? x.GeometricDataId - ?? x.HygrothermalDataId - ?? x.LifeCycleDataId - ?? x.OpticalDataId - ?? x.PhotovoltaicDataId - ?? Guid.Empty - ) - ), - x => x.DataId - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs deleted file mode 100644 index 07fe5492..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeRootByDataIdDataLoader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX; - -public sealed class GetHttpsResourceTreeRootByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : AssociationsByAssociateIdDataLoader( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsNoTracking().Where(x => - x.ParentId == null && ids.Contains( - x.CalorimetricDataId - ?? x.GeometricDataId - ?? x.HygrothermalDataId - ?? x.LifeCycleDataId - ?? x.OpticalDataId - ?? x.PhotovoltaicDataId - ?? Guid.Empty - ) - ), - x => x.DataId - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs b/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs deleted file mode 100644 index 0882dc0c..00000000 --- a/backend/src/GraphQl/DataX/GetHttpsResourcesByDataIdDataLoader.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX; - -public sealed class GetHttpsResourcesByDataIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : AssociationsByAssociateIdDataLoader( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsNoTracking().Where(x => - ids.Contains( - x.CalorimetricDataId - ?? x.GeometricDataId - ?? x.HygrothermalDataId - ?? x.LifeCycleDataId - ?? x.OpticalDataId - ?? x.PhotovoltaicDataId - ?? Guid.Empty - ) - ), - x => x.DataId - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index f9f5ae1b..7fc0321c 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -5,12 +5,12 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data; +using Database.GraphQl.Scalars; using Database.Enumerations; using Database.Extensions; using Database.Services; using HotChocolate; using HotChocolate.Types; -using NodaTime; namespace Database.GraphQl.DataX; @@ -22,7 +22,7 @@ public sealed record UpdateDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId ) : IIdentifyDataInput; diff --git a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs b/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs deleted file mode 100644 index 5efb927d..00000000 --- a/backend/src/GraphQl/Entities/AssociationsByAssociateIdDataLoader.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.Entities; - -public abstract class AssociationsByAssociateIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func, IQueryable> getAssociations, - Func getAssociateId - ) - : GroupedDataLoader(batchScheduler, options) -{ - private readonly IDbContextFactory _dbContextFactory = dbContextFactory; - private readonly Func _getAssociateId = getAssociateId; - private readonly Func, IQueryable> _getAssociations = getAssociations; - - protected override async Task> LoadGroupedBatchAsync( - IReadOnlyList keys, - CancellationToken cancellationToken - ) - { - await using var dbContext = - _dbContextFactory.CreateDbContext(); - return ( - await _getAssociations(dbContext, keys) - .ToListAsync(cancellationToken) - ) - .ToLookup(_getAssociateId); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityFilterType.cs b/backend/src/GraphQl/Entities/AuditableEntityFilterType.cs similarity index 90% rename from backend/src/GraphQl/Entities/EntityFilterType.cs rename to backend/src/GraphQl/Entities/AuditableEntityFilterType.cs index c3cc4a9e..8fe8dfca 100644 --- a/backend/src/GraphQl/Entities/EntityFilterType.cs +++ b/backend/src/GraphQl/Entities/AuditableEntityFilterType.cs @@ -1,16 +1,11 @@ -using System; -using System.Linq; -using Database.Data; -using HotChocolate.Configuration; using HotChocolate.Data.Filters; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors.Definitions; +using Database.Data; namespace Database.GraphQl.Entities; -public abstract class EntityFilterType +public abstract class AuditableEntityFilterType : FilterInputType - where TEntity : IEntity + where TEntity : IEntity, IAuditable { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -18,6 +13,8 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); // TODO Do we want to filter by: descriptor.Field(x => x.Version); } diff --git a/backend/src/GraphQl/Entities/EntitySortType.cs b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs similarity index 58% rename from backend/src/GraphQl/Entities/EntitySortType.cs rename to backend/src/GraphQl/Entities/AuditableEntitySortType.cs index f3bbdb68..88e613bd 100644 --- a/backend/src/GraphQl/Entities/EntitySortType.cs +++ b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs @@ -1,18 +1,20 @@ -using Database.Data; using HotChocolate.Data.Sorting; +using Database.Data; namespace Database.GraphQl.Entities; -public abstract class EntitySortType +public abstract class AuditableEntitySortType : SortInputType - where TEntity : IEntity + where TEntity : IEntity, IAuditable { protected override void Configure( ISortInputTypeDescriptor descriptor ) { + base.Configure(descriptor); descriptor.BindFieldsExplicitly(); descriptor.Field(x => x.Id); - // descriptor.Field(x => x.Version); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs b/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs deleted file mode 100644 index a79193fa..00000000 --- a/backend/src/GraphQl/Entities/EntityByIdDataLoader.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.Entities; - -public abstract class EntityByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory, - Func> getQueryable - ) - : BatchDataLoader(batchScheduler, options) - where TEntity : class, IEntity -{ - private readonly IDbContextFactory _dbContextFactory = dbContextFactory; - private readonly Func> _getQueryable = getQueryable; - - protected override async Task> LoadBatchAsync( - IReadOnlyList keys, - CancellationToken cancellationToken - ) - { - await using var dbContext = - _dbContextFactory.CreateDbContext(); - return await _getQueryable(dbContext).AsNoTrackingWithIdentityResolution() - .Where(entity => keys.Contains(entity.Id)) - .ToDictionaryAsync( - entity => entity.Id, - entity => (TEntity?)entity, - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Entities/EntityType.cs b/backend/src/GraphQl/Entities/EntityType.cs index 6e1402c1..6dfd7111 100644 --- a/backend/src/GraphQl/Entities/EntityType.cs +++ b/backend/src/GraphQl/Entities/EntityType.cs @@ -1,19 +1,21 @@ using System; -using Database.Data; using GreenDonut; using HotChocolate.Types; +using Database.Data; +using Database.GraphQl.Scalars; -namespace Database.GraphQl; +namespace Database.GraphQl.Entities; public abstract class EntityType : ObjectType where TEntity : IEntity - where TEntityByIdDataLoader : IDataLoader + where TEntityByIdDataLoader : IDataLoader { protected override void Configure( IObjectTypeDescriptor descriptor ) { + base.Configure(descriptor); descriptor .ImplementsNode() .IdField(t => t.Id) @@ -21,11 +23,11 @@ IObjectTypeDescriptor descriptor context .DataLoader() .LoadAsync(id, context.RequestAborted) - ! // Notice the null-forgiving operator `!`. It's bad that we need to use it here. ); descriptor .Field(GraphQlConstants.UuidFieldName) .Type>() + .Cost(0) .Resolve(context => context.Parent().Id ); diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index b91859a6..d574dfd3 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -1,15 +1,13 @@ using System; using System.Collections.Generic; -using System.Globalization; -using System.Text; using System.Text.RegularExpressions; using Database.Logging; using HotChocolate; using HotChocolate.Execution; using HotChocolate.Execution.Instrumentation; -using HotChocolate.Execution.Processing; using HotChocolate.Resolvers; using Microsoft.Extensions.Logging; +using HotChocolate.Language; namespace Database.GraphQl; @@ -27,45 +25,47 @@ public static partial void RequestError( [LoggerMessage( Level = LogLevel.Error, - Message = "Resolver error. Field: '{FieldName}'. Operation: '{Operation}'." + Message = "Request error. Document: {Document}" )] - public static partial void ResolverError( + public static partial void RequestError( this ILogger logger, - Exception? exception, - string fieldName, - IOperation operation, + IOperationDocument? document, [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( Level = LogLevel.Error, - Message = "Subscription event error. Operation: {Operation}" + Message = "Resolver error. Field: '{FieldName}'. Document: {Document}" )] - public static partial void SubscriptionEventError( + public static partial void ResolverError( this ILogger logger, - Exception exception, - IOperation operation + string fieldName, + DocumentNode document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error, + Exception? exception ); [LoggerMessage( Level = LogLevel.Error, - Message = "Subscription event error. Operation: {Operation}" + Message = "Resolver error. Field: '{FieldName}'. Document: {Document}" )] - public static partial void SubscriptionTransportError( + public static partial void ResolverError( this ILogger logger, - Exception exception, - IOperation operation + Exception? exception, + string fieldName, + IOperationDocument? document, + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error ); [LoggerMessage( Level = LogLevel.Error, - Message = "Syntax error. Document: {Document}" + Message = "Subscription event error. Id: '{SubscriptionId}'. Operation: {Document}" )] - public static partial void SyntaxError( + public static partial void SubscriptionEventError( this ILogger logger, - Exception? exception, - IOperationDocument? document, - [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + Exception exception, + ulong subscriptionId, + IOperationDocument? document ); [LoggerMessage( @@ -128,55 +128,57 @@ ILogger logger : ExecutionDiagnosticEventListener { // this diagnostic event is raised when a request is executed ... - public override IDisposable ExecuteRequest(IRequestContext context) + public override IDisposable ExecuteRequest(RequestContext context) { // ... we will return an activity scope that is used to signal when the request is finished. return new RequestScope(logger, context); } public override void RequestError( - IRequestContext context, - Exception exception + RequestContext context, + Exception error ) { - logger.RequestError(exception, context.Request.Document); - base.RequestError(context, exception); + logger.RequestError(error, context.Request.Document); + base.RequestError(context, error); } - public override void ResolverError( - IMiddlewareContext context, + public override void RequestError( + RequestContext context, IError error ) { - logger.ResolverError(error.Exception, context.Selection.Field.Name, context.Operation, error); - base.ResolverError(context, error); + logger.RequestError(context.Request.Document, error); + base.RequestError(context, error); } - public override void SubscriptionEventError( - SubscriptionEventContext context, - Exception exception + public override void ResolverError( + IMiddlewareContext context, + IError error ) { - logger.SubscriptionEventError(exception, context.Subscription.Operation); - base.SubscriptionEventError(context, exception); + logger.ResolverError(context.Selection.Field.Name, context.Operation.Document, error, error.Exception); + base.ResolverError(context, error); } - public override void SubscriptionTransportError( - ISubscription subscription, - Exception exception + public override void ResolverError( + RequestContext context, + ISelection selection, + IError error ) { - logger.SubscriptionTransportError(exception, subscription.Operation); - base.SubscriptionTransportError(subscription, exception); + logger.ResolverError(error.Exception, selection.Field.Name, context.Request?.Document, error); + base.ResolverError(context, selection, error); } - public override void SyntaxError( - IRequestContext context, - IError error + public override void SubscriptionEventError( + RequestContext context, + ulong subscriptionId, + Exception exception ) { - logger.SyntaxError(error.Exception, context.Request.Document, error); - base.SyntaxError(context, error); + logger.SubscriptionEventError(exception, subscriptionId, context.Request.Document); + base.SubscriptionEventError(context, subscriptionId, exception); } public override void TaskError( @@ -189,7 +191,7 @@ IError error } public override void ValidationErrors( - IRequestContext context, + RequestContext context, IReadOnlyList errors ) { @@ -200,7 +202,7 @@ IReadOnlyList errors base.ValidationErrors(context, errors); } - private sealed partial class RequestScope(ILogger logger, IRequestContext context) : IDisposable + private sealed partial class RequestScope(ILogger logger, RequestContext context) : IDisposable { [GeneratedRegex(@"apiKey|authKey|privateKey|password|passphrase|secret|secure|security|token", RegexOptions.IgnoreCase, "")] private static partial Regex SecretRegex(); @@ -209,43 +211,45 @@ private sealed partial class RequestScope(ILogger" - : variableValue.Value.ToString() - ); - stringBuilder.Append('\''); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - } - catch (Exception exception) - { - // all input type records will land here. - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Failed stringifying the value: {exception.Message}"); - stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - } - } - } - _variables = stringBuilder.ToString(); + // TODO Where are the variables now if not anymore in context.Variables? + // if (_variables is not null) + // { + // return _variables; + // } + // if (context.Variables is null) + // { + // return null; + // } + // StringBuilder stringBuilder = new(); + // foreach (var variableValueCollection in context.Variables) + // { + // foreach (var variableValue in variableValueCollection) + // { + // try + // { + // stringBuilder.AppendFormat( + // CultureInfo.InvariantCulture, + // $"{variableValue.Name} : {variableValue.Type} = " + // ); + // stringBuilder.Append('\''); + // stringBuilder.Append( + // SecretRegex().IsMatch(variableValue.Name) + // ? "" + // : variableValue.Value.ToString() + // ); + // stringBuilder.Append('\''); + // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + // } + // catch (Exception exception) + // { + // // all input type records will land here. + // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Failed stringifying the value: {exception.Message}"); + // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + // } + // } + // } + // _variables = stringBuilder.ToString(); + _variables = null; return _variables; } @@ -253,28 +257,29 @@ public void Dispose() { if (logger.IsEnabled(LogLevel.Debug)) { - if (context.Document is not null) + if (context.OperationDocumentInfo.Document is not null) { #pragma warning disable CA1873 // Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873) logger.Executed( - context.Document.ToString(true), + context.OperationDocumentInfo.Document.ToString(true), StringifyVariables() ); #pragma warning restore CA1873 } } // when the request is finished it will dispose the activity scope - if (context.Result is IOperationResult { Errors.Count: > 0 } operationResult) + if (context.Result is OperationResult { Errors.Count: > 0 } operationResult) { foreach (var error in operationResult.Errors) { logger.OperationError(context.Request.Document, StringifyVariables(), error); } } - if (context.Exception is { }) - { - logger.UnexpectedExecutionException(context.Request.Document, StringifyVariables(), context.Exception); - } + // TODO Where is the exception now? + // if (context.Exception is { }) + // { + // logger.UnexpectedExecutionException(context.Request.Document, StringifyVariables(), context.Exception); + // } } } } \ No newline at end of file diff --git a/backend/src/GraphQl/Extensions/PageExtensions.cs b/backend/src/GraphQl/Extensions/PageExtensions.cs new file mode 100644 index 00000000..cdbe08dc --- /dev/null +++ b/backend/src/GraphQl/Extensions/PageExtensions.cs @@ -0,0 +1,66 @@ +using System.Threading.Tasks; +using GreenDonut.Data; +using HotChocolate.Types.Pagination; + +namespace Database.GraphQl.Extensions; + +// Inspired by https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Data/src/Data/Extensions/HotChocolatePaginationResultExtensions.cs +public static class PageExtensions +{ + public static async ValueTask GetTotalCountAsync( + this Task?> pagePromise + ) + { + return (await pagePromise)?.TotalCount ?? 0; + } + + public static async ValueTask GetPageInfoAsync( + this Task?> pagePromise + ) + { + var page = await pagePromise; + return new ConnectionPageInfo( + page?.HasNextPage ?? false, + page?.HasPreviousPage ?? false, + page?.CreateStartCursor(), + page?.CreateEndCursor() + ); + } + + // public static async Task> ToConnectionAsync( + // this Task> pagePromise, + // Func, PageEntry, Task>> createEdgeAsync, + // Func>, ConnectionPageInfo, int, Connection> createConnection + // ) + // where TTarget : class + // where TSource : class + // { + // return await CreateConnectionAsync(await pagePromise, createEdgeAsync, createConnection); + // } + + // private static async Task> CreateConnectionAsync( + // Page? page, + // Func, PageEntry, Task>> createEdgeAsync, + // Func>, ConnectionPageInfo, int, Connection> createConnection + // ) + // where TTarget : class + // { + // page ??= Page.Empty; + // var entries = page.Entries; + // IEdge[] edges = entries.IsEmpty + // ? [] + // : await Task.WhenAll( + // entries + // .Select(entry => createEdgeAsync(page, entry)) + // .ToList() + // ); + // return createConnection( + // edges, + // new ConnectionPageInfo( + // page.HasNextPage, + // page.HasPreviousPage, + // page.CreateStartCursor(), + // page.CreateEndCursor()), + // page.TotalCount ?? 0); + // } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs index 9c677115..e2a1a926 100644 --- a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs +++ b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs @@ -8,16 +8,28 @@ namespace Database.GraphQl.Extensions; public static class ResolverContextExtensions { // Inspired by https://github.com/ChilliCream/graphql-platform/blob/9ae7220205412203d0a941a6b0cc779e70b02b09/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs#L76-L86 + // Using `QueryContext queryContext,` in resolvers starts up the projection engine producing many problems public static QueryContext GetQueryContext(this IResolverContext context) { var selection = context.Selection; var filterContext = context.GetFilterContext(); var sortContext = context.GetSortingContext(); - // TODO Make selection work return new QueryContext( null, // selection.AsSelector(), filterContext?.AsPredicate(), sortContext?.AsSortDefinition()); } + + // Using `PagingArguments pagingArguments,` in resolvers results in the parameter `pagingArguments: PagingArgumentsInput` in the GraphQL schema + public static PagingArguments GetPagingArguments(this IResolverContext context) + { + return new( + context.ArgumentValue("first"), + context.ArgumentValue("after"), + context.ArgumentValue("last"), + context.ArgumentValue("before"), + includeTotalCount: true + ); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs b/backend/src/GraphQl/Extensions/SortingContextExtensions.cs deleted file mode 100644 index 65b43071..00000000 --- a/backend/src/GraphQl/Extensions/SortingContextExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Linq; -using Database.Data; -using GreenDonut.Data; -using HotChocolate.Data.Sorting; - -namespace Database.GraphQl.Extensions; - -public static class SortingContextExtensions -{ - public static void StabilizeOrder(this ISortingContext sorting) where T : IEntity - { - // this signals that the expression was not handled within the resolver - // and the sorting middleware should take over. - sorting.Handled(false); - sorting.OnAfterSortingApplied>( - static (sortingApplied, query) => - { - if (sortingApplied && query is IOrderedQueryable ordered) - { - return ordered.ThenBy(_ => _.Id); - } - return query.OrderBy(_ => _.Id); - }); - } - - public static SortDefinition StabilizeOrder(this SortDefinition sort) where T : IEntity - { - return sort.AddAscending(_ => _.Id); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/INotField.cs b/backend/src/GraphQl/Filters/INotField.cs index 3e2b98ac..9b24e3f3 100644 --- a/backend/src/GraphQl/Filters/INotField.cs +++ b/backend/src/GraphQl/Filters/INotField.cs @@ -1,11 +1,10 @@ -using HotChocolate.Data.Filters; -using HotChocolate.Types; - -namespace Database.GraphQl.Filters; - -public interface INotField - : IInputField - , IHasRuntimeType -{ - new IFilterInputType DeclaringType { get; } -} \ No newline at end of file +// using HotChocolate.Data.Filters; +// +// namespace Database.GraphQl.Filters; +// +// public interface INotField +// : IInputField +// , IHasRuntimeType +// { +// new IFilterInputType DeclaringType { get; } +// } \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/NotField.cs b/backend/src/GraphQl/Filters/NotField.cs index 05d14b3d..d7234d79 100644 --- a/backend/src/GraphQl/Filters/NotField.cs +++ b/backend/src/GraphQl/Filters/NotField.cs @@ -1,44 +1,43 @@ -using HotChocolate.Configuration; -using HotChocolate.Data.Filters; -using HotChocolate.Types; -using HotChocolate.Types.Descriptors; -using HotChocolate.Types.Descriptors.Definitions; - -namespace Database.GraphQl.Filters; - -public sealed class NotField - // : FilterOperationField - : InputField - , INotField -{ - internal NotField(IDescriptorContext context, int index, string? scope) - : base(CreateDefinition(context, scope), index) - { - } - - public new FilterInputType DeclaringType => (FilterInputType)base.DeclaringType; - - IFilterInputType INotField.DeclaringType => DeclaringType; - - protected override void OnCompleteField( - ITypeCompletionContext context, - ITypeSystemMember declaringMember, - InputFieldDefinition definition) - { - definition.Type = TypeReference.Parse( - $"[{context.Type.Name}!]", - TypeContext.Input, - context.Type.Scope); - - base.OnCompleteField(context, declaringMember, definition); - } - - private static FilterOperationFieldDefinition CreateDefinition( - IDescriptorContext context, - string? scope) - { - return FilterOperationFieldDescriptor - .New(context, AdditionalFilterOperations.Not, scope) - .CreateDefinition(); - } -} \ No newline at end of file +// using HotChocolate.Configuration; +// using HotChocolate.Data.Filters; +// using HotChocolate.Types; +// using HotChocolate.Types.Descriptors; +// +// namespace Database.GraphQl.Filters; +// +// public sealed class NotField +// // : FilterOperationField +// : InputField +// , INotField +// { +// internal NotField(IDescriptorContext context, int index, string? scope) +// : base(CreateDefinition(context, scope), index) +// { +// } +// +// public new FilterInputType DeclaringType => (FilterInputType)base.DeclaringType; +// +// IFilterInputType INotField.DeclaringType => DeclaringType; +// +// protected override void OnCompleteField( +// ITypeCompletionContext context, +// ITypeSystemMember declaringMember, +// InputFieldDefinition definition) +// { +// definition.Type = TypeReference.Parse( +// $"[{context.Type.Name}!]", +// TypeContext.Input, +// context.Type.Scope); +// +// base.OnCompleteField(context, declaringMember, definition); +// } +// +// private static FilterOperationFieldDefinition CreateDefinition( +// IDescriptorContext context, +// string? scope) +// { +// return FilterOperationFieldDescriptor +// .New(context, AdditionalFilterOperations.Not, scope) +// .CreateDefinition(); +// } +// } \ No newline at end of file diff --git a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs index a43f2f96..c14e0b46 100644 --- a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs +++ b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs @@ -1,8 +1,16 @@ using HotChocolate.Data.Filters; using HotChocolate.Types; +using Database.GraphQl.Scalars; +using DateTimeType = HotChocolate.Types.NodaTime.DateTimeType; +using DurationType = HotChocolate.Types.NodaTime.DurationType; +using LocalDateTimeType = HotChocolate.Types.NodaTime.LocalDateTimeType; +using LocalDateType = HotChocolate.Types.NodaTime.LocalDateType; +using LocalTimeType = HotChocolate.Types.NodaTime.LocalTimeType; namespace Database.GraphQl.Filters; +// https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Data/src/Data/Filters/Types/ComparableOperationFilterInputType.cs + public abstract class ExtendedComparableOperationFilterInputType : ComparableOperationFilterInputType where T : notnull @@ -76,15 +84,25 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } } -// public sealed class LocalDateFilterInputType -// : ExtendedComparableOperationFilterInputType -// { -// protected override void Configure(IFilterInputTypeDescriptor descriptor) -// { -// descriptor.Name($"LocalDate{GraphQlConstants.FilterInputSuffix}"); -// base.Configure(descriptor); -// } -// } +public sealed class LocalDateFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"LocalDate{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + +public sealed class LocalDateTimeFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"LocalDateTime{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} public sealed class LongFilterInputType : ExtendedComparableOperationFilterInputType @@ -96,15 +114,15 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } } -// public sealed class LocalTimeFilterInputType -// : ExtendedComparableOperationFilterInputType -// { -// protected override void Configure(IFilterInputTypeDescriptor descriptor) -// { -// descriptor.Name($"LocalTime{GraphQlConstants.FilterInputSuffix}"); -// base.Configure(descriptor); -// } -// } +public sealed class LocalTimeFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"LocalTime{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} public sealed class FloatFilterInputType : ExtendedComparableOperationFilterInputType @@ -116,12 +134,12 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } } -public sealed class TimeSpanFilterInputType - : ExtendedComparableOperationFilterInputType +public sealed class DurationFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { - descriptor.Name($"TimeSpan{GraphQlConstants.FilterInputSuffix}"); + descriptor.Name($"Duration{GraphQlConstants.FilterInputSuffix}"); base.Configure(descriptor); } } @@ -146,8 +164,8 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } } -public sealed class UrlFilterInputType - : ExtendedComparableOperationFilterInputType +public sealed class UriFilterInputType + : ExtendedComparableOperationFilterInputType { protected override void Configure(IFilterInputTypeDescriptor descriptor) { diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index b11f44cc..52739ea5 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -9,8 +8,8 @@ using Database.Data; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; -using Database.Utilities; using HotChocolate; using HotChocolate.Types; using NodaTime; @@ -23,7 +22,7 @@ public sealed record CreateGeometricDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource, @@ -111,6 +110,7 @@ public async Task CreateGeometricDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -140,6 +140,7 @@ CancellationToken cancellationToken CreateGeometricDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateGeometricDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -172,4 +173,4 @@ CancellationToken cancellationToken return NewPayload(geometricData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs deleted file mode 100644 index ef2c94e3..00000000 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.GeometricDataX; - -public sealed class GeometricDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GeometricData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataLoaders.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataLoaders.cs new file mode 100644 index 00000000..703cf473 --- /dev/null +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GeometricDataX; + +public sealed class GeometricDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetGeometricDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.GeometricData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 2520920a..ba3fcfbe 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -21,11 +20,10 @@ public sealed class GeometricDataQueries [UsePaging] [UseFiltering] [UseSorting] - public Task> GetAllGeometricDataAsync( + public Task> GetAllGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -34,7 +32,6 @@ CancellationToken cancellationToken context.GeometricData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -43,11 +40,10 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - public Task> GetAllPendingGeometricDataAsync( + public Task> GetAllPendingGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -57,7 +53,6 @@ CancellationToken cancellationToken context.GeometricData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs index 978f7481..c1caea06 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.GeometricDataX; public sealed class GeometricDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs deleted file mode 100644 index f937d1f3..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed class GetHttpsResourceByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.GetHttpsResources - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs deleted file mode 100644 index 276b992d..00000000 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Linq; -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.GetHttpsResources; - -public sealed class GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : AssociationsByAssociateIdDataLoader( - batchScheduler, - options, - dbContextFactory, - (dbContext, ids) => - dbContext.GetHttpsResources.AsNoTracking().Where(x => - ids.Contains(x.ParentId ?? Guid.Empty) - ), - x => x.Id - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs new file mode 100644 index 00000000..a04f9b0a --- /dev/null +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.GetHttpsResources; + +public sealed class GetHttpsResourceDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetGetHttpsResourceByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.GetHttpsResources, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetHttpsResourceChildrenByGetHttpsResourceIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources, + _ => _.ParentId ?? Guid.Empty, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 6e1a5f92..312f34d0 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.GetHttpsResources; public class GetHttpsResourceFilterType - : EntityFilterType + : AuditableEntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 95850511..8e2df359 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -6,8 +6,9 @@ using Database.Data; using Database.Enumerations; using Database.GraphQl.Extensions; +using GreenDonut.Data; using HotChocolate.Data; -using HotChocolate.Data.Sorting; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -17,58 +18,61 @@ namespace Database.GraphQl.GetHttpsResources; public sealed class GetHttpsResourceQueries { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public IQueryable GetGetHttpsResources( + public ValueTask> GetGetHttpsResources( ApplicationDbContext context, - ISortingContext sorting + IResolverContext resolverContext, + CancellationToken cancellationToken ) { - sorting.StabilizeOrder(); return context.GetHttpsResources.AsNoTracking() .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState != PublishingState.PENDING) .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState != PublishingState.PENDING) .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState != PublishingState.PENDING) .Where(_ => _.LifeCycleData == null || _.LifeCycleData.PublishingState != PublishingState.PENDING) .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState != PublishingState.PENDING) - .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING); + .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } [UsePaging] // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public async Task> GetPendingGetHttpsResources( + public async ValueTask> GetPendingGetHttpsResources( ApplicationDbContext context, - ISortingContext sorting, CommonAuthorization authorization, + IResolverContext resolverContext, CancellationToken cancellationToken ) { if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return Enumerable.Empty().AsQueryable(); + return await Enumerable.Empty().AsQueryable() + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } - sorting.StabilizeOrder(); - return context.GetHttpsResources.AsNoTracking() + return await context.GetHttpsResources.AsNoTracking() .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState == PublishingState.PENDING) .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState == PublishingState.PENDING) .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState == PublishingState.PENDING) .Where(_ => _.LifeCycleData == null || _.LifeCycleData.PublishingState == PublishingState.PENDING) .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState == PublishingState.PENDING) - .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState == PublishingState.PENDING); + .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState == PublishingState.PENDING) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } public Task GetGetHttpsResourceAsync( Guid id, - GetHttpsResourceByIdDataLoader byId, + IGetHttpsResourceByIdDataLoader byId, CancellationToken cancellationToken ) { - return byId.LoadAsync( - id, - cancellationToken - ); + return byId.LoadAsync(id, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index d6801662..aca20aa5 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -16,12 +16,12 @@ public sealed class GetHttpsResourceResolvers { public async Task GetData( [Parent] GetHttpsResource getHttpsResource, - CalorimetricDataByIdDataLoader calorimetricDataById, - HygrothermalDataByIdDataLoader hygrothermalDataById, - LifeCycleDataByIdDataLoader lifeCycleDataById, - OpticalDataByIdDataLoader opticalDataById, - PhotovoltaicDataByIdDataLoader photovoltaicDataById, - GeometricDataByIdDataLoader geometricDataById, + ICalorimetricDataByIdDataLoader calorimetricDataById, + IHygrothermalDataByIdDataLoader hygrothermalDataById, + ILifeCycleDataByIdDataLoader lifeCycleDataById, + IOpticalDataByIdDataLoader opticalDataById, + IPhotovoltaicDataByIdDataLoader photovoltaicDataById, + IGeometricDataByIdDataLoader geometricDataById, CancellationToken cancellationToken ) { @@ -83,7 +83,7 @@ AppSettings appSettings public async Task GetParent( [Parent] GetHttpsResource getHttpsResource, - GetHttpsResourceByIdDataLoader byId, + IGetHttpsResourceByIdDataLoader byId, CancellationToken cancellationToken ) { @@ -94,7 +94,7 @@ CancellationToken cancellationToken public Task GetChildren( [Parent] GetHttpsResource getHttpsResource, - GetHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, + IHttpsResourceChildrenByGetHttpsResourceIdDataLoader byId, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs index c41a6e9f..9504cc60 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.GetHttpsResources; public class GetHttpsResourceSortType - : EntitySortType + : AuditableEntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index f93cac99..01bc6cab 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,10 +1,11 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Types; namespace Database.GraphQl.GetHttpsResources; public sealed class GetHttpsResourceType - : EntityType + : EntityType { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 00e5503a..6e232dc3 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -88,8 +88,6 @@ public async Task RecomputeGetHttpsR CancellationToken cancellationToken ) { - var queryContext = resolverContext.GetQueryContext(); - if ((await AuthorizeAsync( RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHENTICATED, RecomputeGetHttpsResourceHashValuesErrorCode.UNAUTHORIZED, @@ -101,10 +99,9 @@ CancellationToken cancellationToken { return authorizeErrorPayload; } - var resources = await context.GetHttpsResourcesWithData - .With(queryContext, sort => sort.StabilizeOrder()) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) .ToListAsync(cancellationToken); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs index 5854e8a3..b8ce2ece 100644 --- a/backend/src/GraphQl/GraphQlConstants.cs +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -2,11 +2,13 @@ namespace Database.GraphQl; internal static class GraphQlConstants { + internal const uint MaximumPageSize = 100; internal const string EndpointPath = "/graphql"; internal const string CorsPolicy = "GraphQlCorsPolicy"; internal const string TypeDiscriminatorPropertyName = "__typename"; internal const string FilterInputSuffix = "PropositionInput"; internal const string SortInputSuffix = "SortInput"; + internal const string PendingPrefix = "pending"; internal const string UuidFieldName = "uuid"; internal const string VersionFieldName = "version"; } \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlThrowHelper.cs b/backend/src/GraphQl/GraphQlThrowHelper.cs new file mode 100644 index 00000000..45fdff6d --- /dev/null +++ b/backend/src/GraphQl/GraphQlThrowHelper.cs @@ -0,0 +1,52 @@ +using System; +using System.Text.Json; +using HotChocolate; +using HotChocolate.Language; +using HotChocolate.Types; + +namespace Database.GraphQl; + +// https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Utilities/ThrowHelper.cs +public static class GraphQlThrowHelper +{ + public static LeafCoercionException ScalarCannotCoerceInputLiteral( + ITypeDefinition scalarType, + IValueNode? valueLiteral, + Exception? error = null) + { + valueLiteral ??= NullValueNode.Default; + var errorBuilder = + ErrorBuilder.New() + .SetMessage( + GraphQlTypeResources.ScalarCannotCoerceInputLiteral, + scalarType.Name, + valueLiteral.Kind); + if (error is not null) + { + errorBuilder.SetException(error); + } + return new LeafCoercionException( + errorBuilder.Build(), + scalarType); + } + + public static LeafCoercionException ScalarCannotCoerceInputValue( + ITypeDefinition scalarType, + JsonElement inputValue, + Exception? error = null) + { + var errorBuilder = + ErrorBuilder.New() + .SetMessage( + GraphQlTypeResources.ScalarCannotCoerceInputValue, + scalarType.Name, + inputValue.ValueKind); + if (error is not null) + { + errorBuilder.SetException(error); + } + return new LeafCoercionException( + errorBuilder.Build(), + scalarType); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlTypeResources.cs b/backend/src/GraphQl/GraphQlTypeResources.cs new file mode 100644 index 00000000..d51cf4ca --- /dev/null +++ b/backend/src/GraphQl/GraphQlTypeResources.cs @@ -0,0 +1,8 @@ +namespace Database.GraphQl; + +// Inspired by https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx +public static class GraphQlTypeResources +{ + public const string ScalarCannotCoerceInputLiteral = "{0} cannot coerce the given literal of type `{1}` to a runtime value."; + public const string ScalarCannotCoerceInputValue = "{0} cannot coerce the given value JSON element of type `{1}` to a runtime value."; +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index c182836a..0803a74e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -8,6 +8,7 @@ using Database.Data; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -21,7 +22,7 @@ public sealed record CreateHygrothermalDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource @@ -102,6 +103,7 @@ public async Task CreateHygrothermalDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -131,6 +133,7 @@ CancellationToken cancellationToken CreateHygrothermalDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateHygrothermalDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -163,4 +166,4 @@ CancellationToken cancellationToken return NewPayload(hygrothermalData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs deleted file mode 100644 index deac4fcf..00000000 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.HygrothermalDataX; - -public sealed class HygrothermalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.HygrothermalData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataLoaders.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataLoaders.cs new file mode 100644 index 00000000..b21bfca6 --- /dev/null +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.HygrothermalDataX; + +public sealed class HygrothermalDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetHygrothermalDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.HygrothermalData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 6f22f619..335dc032 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -23,12 +22,11 @@ public sealed class HygrothermalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllHygrothermalDataAsync( + public Task> GetAllHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, IResolverContext resolverContext, - ISortingContext sorting, CancellationToken cancellationToken ) { @@ -36,7 +34,6 @@ CancellationToken cancellationToken context.HygrothermalData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -47,12 +44,11 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingHygrothermalDataAsync( + public Task> GetAllPendingHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, IResolverContext resolverContext, - ISortingContext sorting, CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -61,7 +57,6 @@ CancellationToken cancellationToken context.HygrothermalData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs index 78a411af..9f5ec67d 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.HygrothermalDataX; public sealed class HygrothermalDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs index 0880b08d..a276eba6 100644 --- a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -8,6 +8,7 @@ using Database.Data; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -21,7 +22,7 @@ public sealed record CreateLifeCycleDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource @@ -102,6 +103,7 @@ public async Task CreateLifeCycleDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -131,6 +133,7 @@ CancellationToken cancellationToken CreateLifeCycleDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateLifeCycleDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -163,4 +166,4 @@ CancellationToken cancellationToken return NewPayload(lifeCycleData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs deleted file mode 100644 index c3bd0833..00000000 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.LifeCycleDataX; - -public sealed class LifeCycleDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.LifeCycleData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataLoaders.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataLoaders.cs new file mode 100644 index 00000000..a0c8dbb7 --- /dev/null +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.LifeCycleDataX; + +public sealed class LifeCycleDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetLifeCycleDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.LifeCycleData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index 01ffe18e..1f088c6d 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -23,12 +22,11 @@ public sealed class LifeCycleDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllLifeCycleDataAsync( + public Task> GetAllLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, IResolverContext resolverContext, - ISortingContext sorting, CancellationToken cancellationToken ) { @@ -36,7 +34,6 @@ CancellationToken cancellationToken context.LifeCycleData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -47,12 +44,11 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingLifeCycleDataAsync( + public Task> GetAllPendingLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, IResolverContext resolverContext, - ISortingContext sorting, CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -61,7 +57,6 @@ CancellationToken cancellationToken context.LifeCycleData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs index 3498cec9..0aebaf0e 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.LifeCycleDataX; public sealed class LifeCycleDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 8f71758c..7802a3c9 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -10,6 +10,7 @@ using Database.Enumerations; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Types; @@ -23,7 +24,7 @@ public sealed record CreateOpticalDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, OpticalComponentType? Type, OpticalComponentSubtype? Subtype, @@ -124,6 +125,7 @@ public async Task CreateOpticalDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -153,6 +155,7 @@ CancellationToken cancellationToken CreateOpticalDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreateOpticalDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -185,4 +188,4 @@ CancellationToken cancellationToken return NewPayload(opticalData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs deleted file mode 100644 index e1a27bc1..00000000 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.OpticalDataX; - -public sealed class OpticalDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.OpticalData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataLoaders.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataLoaders.cs new file mode 100644 index 00000000..08ec8081 --- /dev/null +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.OpticalDataX; + +public sealed class OpticalDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetOpticalDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.OpticalData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 24196d2f..b9bd9864 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -1,14 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -23,11 +22,10 @@ public sealed class OpticalDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllOpticalDataAsync( + public Task> GetAllOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -36,7 +34,6 @@ CancellationToken cancellationToken context.OpticalData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -47,11 +44,10 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingOpticalDataAsync( + public Task> GetAllPendingOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -61,7 +57,6 @@ CancellationToken cancellationToken context.OpticalData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs index b1e97cf3..5adb469b 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.OpticalDataX; public sealed class OpticalDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/PaginatedConnection.cs b/backend/src/GraphQl/PaginatedConnection.cs new file mode 100644 index 00000000..cfb462d4 --- /dev/null +++ b/backend/src/GraphQl/PaginatedConnection.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate.CostAnalysis.Types; +using HotChocolate.Types.Pagination; +using Database.Data; +using Database.GraphQl.Extensions; + +namespace Database.GraphQl; + +public abstract class PaginatedConnection< + TSubject, + TAssociation, + TEdge, + TAssociationsByOneIdDataLoader +>( + TSubject subject, + Func createEdge, + PagingArguments pagingArguments, + QueryContext queryContext +) + where TSubject : IEntity + where TAssociation : class + where TAssociationsByOneIdDataLoader : IDataLoader> +{ + protected TSubject Subject { get; } = subject; + + [Cost(0)] + public ValueTask GetTotalCountAsync( + TAssociationsByOneIdDataLoader dataLoader, + CancellationToken cancellationToken + ) + { + return dataLoader + .With(pagingArguments, queryContext) + .LoadAsync(Subject.Id, cancellationToken) + .GetTotalCountAsync(); + } + + [Cost(0)] + public ValueTask GetPageInfoAsync( + TAssociationsByOneIdDataLoader dataLoader, + CancellationToken cancellationToken + ) + { + return dataLoader + .With(pagingArguments, queryContext) + .LoadAsync(Subject.Id, cancellationToken) + .GetPageInfoAsync(); + } + + [Cost(0)] + public async IAsyncEnumerable GetEdgesAsync( + TAssociationsByOneIdDataLoader dataLoader, + [EnumeratorCancellation] CancellationToken cancellationToken + ) + { + var page = + await dataLoader + .With(pagingArguments, queryContext) + .LoadAsync(Subject.Id, cancellationToken); + if (page is null) + { + yield break; + } + foreach (var entry in page.Entries) + { + yield return createEdge(entry.Item, page.CreateCursor(entry)); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/PaginatedEdge.cs b/backend/src/GraphQl/PaginatedEdge.cs new file mode 100644 index 00000000..07678c18 --- /dev/null +++ b/backend/src/GraphQl/PaginatedEdge.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using HotChocolate.CostAnalysis.Types; + +namespace Database.GraphQl; + +public abstract record PaginatedEdge( + TNode Node, + string Cursor +); + +public abstract class PaginatedEdge( + Guid nodeId, + string cursor +) + where TNodeByIdDataLoader : IDataLoader + where TNode : notnull +{ + [Cost(0)] + public Task GetNodeAsync( + TNodeByIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadRequiredAsync(nodeId, cancellationToken); + } + + public string Cursor => cursor; +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 42014c7b..2729dd9a 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -9,8 +8,8 @@ using Database.Data; using Database.Extensions; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; -using Database.Utilities; using HotChocolate; using HotChocolate.Types; using NodaTime; @@ -23,7 +22,7 @@ public sealed record CreatePhotovoltaicDataInput( string? Name, string? Description, string[] Warnings, - OffsetDateTime CreatedAt, + DateTimeOffset CreatedAt, Guid CreatorId, AppliedMethodInput AppliedMethod, RootGetHttpsResourceInput RootResource @@ -104,6 +103,7 @@ public async Task CreatePhotovoltaicDataAsync( IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKindDataLoader, IDataFormatByIdDataLoader dataFormatByIdDataLoader, ResponseApprovalService responseApprovalService, + IClock clock, CancellationToken cancellationToken ) { @@ -133,6 +133,7 @@ CancellationToken cancellationToken CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA, dataFormatByIdDataLoader, CreatePhotovoltaicDataErrorCode.UNKNOWN_DATA_FORMAT, + clock, cancellationToken ) ).Failed(out var dataFormat, out var validateErrorPayload) @@ -165,4 +166,4 @@ CancellationToken cancellationToken return NewPayload(photovoltaicData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs deleted file mode 100644 index 79e00ab5..00000000 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.PhotovoltaicDataX; - -public sealed class PhotovoltaicDataByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.PhotovoltaicData - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataLoaders.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataLoaders.cs new file mode 100644 index 00000000..cf51e490 --- /dev/null +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.PhotovoltaicDataX; + +public sealed class PhotovoltaicDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetPhotovoltaicDataByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.PhotovoltaicData, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index d64b19eb..1f377806 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -1,16 +1,16 @@ using System; -using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; using HotChocolate.Data; -using HotChocolate.Data.Sorting; using HotChocolate.Resolvers; using HotChocolate.Types; +using NodaTime; namespace Database.GraphQl.PhotovoltaicDataX; @@ -23,11 +23,10 @@ public sealed class PhotovoltaicDataQueries // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPhotovoltaicDataAsync( + public Task> GetAllPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -36,7 +35,6 @@ CancellationToken cancellationToken context.PhotovoltaicData, locale, accessRightsService, - sorting, resolverContext, cancellationToken ); @@ -47,11 +45,10 @@ CancellationToken cancellationToken // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] - public Task> GetAllPendingPhotovoltaicDataAsync( + public Task> GetAllPendingPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, AccessRightsService accessRightsService, - ISortingContext sorting, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -61,7 +58,6 @@ CancellationToken cancellationToken context.PhotovoltaicData, locale, accessRightsService, - sorting, resolverContext, authorization, cancellationToken @@ -89,6 +85,7 @@ CancellationToken cancellationToken [GraphQLType] string? locale, PhotovoltaicDataByIdDataLoader byId, AccessRightsService accessRightsService, + IClock clock, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs index 2a445324..ee2a79e9 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.PhotovoltaicDataX; public sealed class PhotovoltaicDataType - : DataTypeBase + : DataTypeBase { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index 09643128..e10dd56d 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -88,7 +88,6 @@ public async Task CreateResponseApprovalsAsync( CancellationToken cancellationToken ) { - var queryContext = resolverContext.GetQueryContext(); if ((await AuthorizeAsync( CreateResponseApprovalsErrorCode.UNAUTHENTICATED, CreateResponseApprovalsErrorCode.UNAUTHORIZED, @@ -103,7 +102,7 @@ CancellationToken cancellationToken var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - context.GetAllDataAsync(_ => _.Approval == null, queryContext), + context.GetAllDataAsync(_ => _.Approval == null, resolverContext.GetQueryContext()), cancellationToken, async (data, cancellationToken) => { diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index 9f2fc9c8..ca198b36 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -5,7 +5,7 @@ namespace Database.GraphQl.ResponseApprovals; public abstract class ResponseApprovalFilterType -: EntityFilterType +: AuditableEntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -19,7 +19,6 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.Description); descriptor.Field(x => x.ComponentId); descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.CreatedAt); descriptor.Field(x => x.AppliedMethod); descriptor.Field(x => x.Approvals); descriptor.Field(x => x.Resources); diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 8903d6fa..7c59e799 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -86,7 +86,6 @@ public async Task UpdateResponseApprovalsAsync( CancellationToken cancellationToken ) { - var queryContext = resolverContext.GetQueryContext(); if ((await AuthorizeAsync( UpdateResponseApprovalsErrorCode.UNAUTHENTICATED, UpdateResponseApprovalsErrorCode.UNAUTHORIZED, @@ -102,7 +101,7 @@ CancellationToken cancellationToken var dataSets = new ConcurrentBag(); var errors = new ConcurrentBag(); await Parallel.ForEachAsync( - context.GetAllDataAsync(_ => _.Approval != null, queryContext), + context.GetAllDataAsync(_ => _.Approval != null, resolverContext.GetQueryContext()), cancellationToken, async (data, cancellationToken) => { diff --git a/backend/src/GraphQl/LocaleType.cs b/backend/src/GraphQl/Scalars/LocaleType.cs similarity index 94% rename from backend/src/GraphQl/LocaleType.cs rename to backend/src/GraphQl/Scalars/LocaleType.cs index 0c337de6..1b6f9c86 100644 --- a/backend/src/GraphQl/LocaleType.cs +++ b/backend/src/GraphQl/Scalars/LocaleType.cs @@ -2,7 +2,7 @@ using HotChocolate.Types; // TODO Maybe use an enumeration as runtime type instead of string (and fallback to english when the given one does not exist).namespace Database.GraphQl -namespace Database.GraphQl; +namespace Database.GraphQl.Scalars; /// /// [BCP 47](https://tools.ietf.org/html/bcp47) @@ -29,12 +29,12 @@ public sealed class LocaleType( BindingBehavior bind = BindingBehavior.Explicit) : RegexType( name, - _validationPattern, + ValidationPattern, description, RegexOptions.Compiled | RegexOptions.IgnoreCase, bind) { - private const string _validationPattern = + private const string ValidationPattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; /// diff --git a/backend/src/GraphQl/Scalars/MyUriType.cs b/backend/src/GraphQl/Scalars/MyUriType.cs new file mode 100644 index 00000000..0d6c2e41 --- /dev/null +++ b/backend/src/GraphQl/Scalars/MyUriType.cs @@ -0,0 +1,110 @@ +using System; +using System.Text.Json; +using HotChocolate.Features; +using HotChocolate.Language; +using HotChocolate.Text.Json; +using Database.GraphQl; +using Microsoft.Extensions.DependencyInjection; +using HotChocolate.Types; + +namespace Database.GraphQl.Scalars; + +// Inspired by https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Core/src/Types/Types/Scalars/UriType.cs +/// +/// [RFC 3986](https://tools.ietf.org/html/rfc3986) +/// and +/// [RFC 3987](https://tools.ietf.org/html/rfc3987) +/// compliant +/// [absolute Uniform Resource Locator (URL)](https://tools.ietf.org/html/rfc3986#section-4.3) +/// string with optional +/// [fragment identifier](https://tools.ietf.org/html/rfc3986#section-3.5). +/// [Valid values are for example](https://datatracker.ietf.org/doc/html/rfc3986#section-1.1.2) +/// `ftp://ftp.is.co.za/rfc/rfc1808.txt`, `http://www.ietf.org/rfc/rfc2396.txt`, +/// `ldap://[2001:db8::7]/c=GB?objectClass?one`, `mailto:John.Doe@example.com`, +/// `news:comp.infosystems.www.servers.unix`, `tel:+1-816-555-1212`, +/// `telnet://192.0.2.16:80/`, +/// `urn:oasis:names:specification:docbook:dtd:xml:4.1.2` +/// +/// See also +/// [URL Living Standard](https://url.spec.whatwg.org/#absolute-url-with-fragment-string) +/// and +/// [Identifying resources on the Web](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web). +/// +/// Specification +public sealed class MyUriType : ScalarType +{ + private const string ScalarName = "Url"; + + private const string SpecifiedByUri = "https://tools.ietf.org/html/rfc3986"; + + public MyUriType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit) + : base(name, bind) + { + Description = description; + SpecifiedBy = new Uri(SpecifiedByUri); + } + + /// + [ActivatorUtilitiesConstructor] + public MyUriType() + : this( + ScalarName, + $"The `{ScalarName}` scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986.", + BindingBehavior.Implicit) + { + } + + /// + protected override Uri OnCoerceInputLiteral(StringValueNode valueLiteral) + { + if (TryParseUri(valueLiteral.Value, out var value)) + { + return value; + } + + throw GraphQlThrowHelper.ScalarCannotCoerceInputLiteral(this, valueLiteral); + } + + /// + protected override Uri OnCoerceInputValue(JsonElement inputValue, IFeatureProvider context) + { + if (TryParseUri(inputValue.GetString()!, out var value)) + { + return value; + } + + throw GraphQlThrowHelper.ScalarCannotCoerceInputValue(this, inputValue); + } + + /// + protected override void OnCoerceOutputValue(Uri runtimeValue, ResultElement resultValue) + { + var serialized = runtimeValue.IsAbsoluteUri + ? runtimeValue.AbsoluteUri + : runtimeValue.ToString(); + resultValue.SetStringValue(serialized); + } + + /// + protected override StringValueNode OnValueToLiteral(Uri runtimeValue) + { + var value = runtimeValue.IsAbsoluteUri + ? runtimeValue.AbsoluteUri + : runtimeValue.ToString(); + return new StringValueNode(value); + } + + private static bool TryParseUri(string value, out Uri uri) + { + if (!Uri.TryCreate(value, UriKind.RelativeOrAbsolute, out var parsedUri)) + { + uri = null!; + return false; + } + uri = parsedUri; + return true; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Scalars/NonNegativeIntType.cs b/backend/src/GraphQl/Scalars/NonNegativeIntType.cs new file mode 100644 index 00000000..480014f5 --- /dev/null +++ b/backend/src/GraphQl/Scalars/NonNegativeIntType.cs @@ -0,0 +1,72 @@ +using System.Text.Json; +using HotChocolate.Language; +using HotChocolate.Text.Json; +using HotChocolate.Types; +using Microsoft.Extensions.DependencyInjection; + +namespace Database.GraphQl.Scalars; + +/// +/// +/// The NonNegativeInt scalar type represents an unsigned 32‐bit numeric +/// non‐fractional value. Response formats that support an unsigned 32‐bit +/// integer or a number type should use that type to represent this scalar. +/// +/// +public sealed class NonNegativeIntType +: IntegerTypeBase +{ + public const string ScalarName = "NonNegativeInt"; + + /// + /// Initializes a new instance of the class. + /// + public NonNegativeIntType(uint min, uint max) + : this( + ScalarName, + $"The `{ScalarName}` scalar type represents an unsigned 32-bit numeric non-fractional value.", + min, + max, + BindingBehavior.Implicit) + { + } + + /// + /// Initializes a new instance of the class. + /// + public NonNegativeIntType( + string name, + string? description = null, + uint min = uint.MinValue, + uint max = uint.MaxValue, + BindingBehavior bind = BindingBehavior.Explicit) + : base(name, min, max, bind) + { + Description = description; + } + + /// + /// Initializes a new instance of the class. + /// + [ActivatorUtilitiesConstructor] + public NonNegativeIntType() + : this(uint.MinValue, uint.MaxValue) + { + } + + /// + protected override uint OnCoerceInputLiteral(IntValueNode valueLiteral) + => valueLiteral.ToUInt32(); + + /// + protected override uint OnCoerceInputValue(JsonElement inputValue) + => inputValue.GetUInt32(); + + /// + protected override void OnCoerceOutputValue(uint runtimeValue, ResultElement resultValue) + => resultValue.SetNumberValue(runtimeValue); + + /// + protected override IValueNode OnValueToLiteral(uint runtimeValue) + => new IntValueNode(runtimeValue); +} \ No newline at end of file diff --git a/backend/src/GraphQl/Sorting.cs b/backend/src/GraphQl/Sorting.cs new file mode 100644 index 00000000..91a8770d --- /dev/null +++ b/backend/src/GraphQl/Sorting.cs @@ -0,0 +1,18 @@ +using GreenDonut.Data; +using Database.Data; + +namespace Database.GraphQl; + +public static class Sorting +{ + public static SortDefinition DefaultEntityOrder( + SortDefinition sort + ) + where TEntity : class, IEntity//, IAuditable + { + // always sort by primary key to make pagination cursors unique + return sort + // .IfEmpty(_ => _.AddDescending(_ => _.CreatedAt)) + .AddDescending(_ => _.Id); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserByIdDataLoader.cs b/backend/src/GraphQl/Users/UserByIdDataLoader.cs deleted file mode 100644 index afc10301..00000000 --- a/backend/src/GraphQl/Users/UserByIdDataLoader.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using GreenDonut; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.Users; - -public sealed class UserByIdDataLoader( - IBatchScheduler batchScheduler, - DataLoaderOptions options, - IDbContextFactory dbContextFactory - ) - : EntityByIdDataLoader( - batchScheduler, - options, - dbContextFactory, - dbContext => dbContext.Users - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserDataLoaders.cs b/backend/src/GraphQl/Users/UserDataLoaders.cs new file mode 100644 index 00000000..4a4db545 --- /dev/null +++ b/backend/src/GraphQl/Users/UserDataLoaders.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.Users; + +public sealed class UserDataLoaders +: DataLoaders +{ + [DataLoader] + public static ValueTask> GetUserByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.Users, + queryContext, + databaseContextFactory, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserType.cs b/backend/src/GraphQl/Users/UserType.cs index 08a24a23..d05d62c0 100644 --- a/backend/src/GraphQl/Users/UserType.cs +++ b/backend/src/GraphQl/Users/UserType.cs @@ -1,10 +1,11 @@ using Database.Data; +using Database.GraphQl.Entities; using HotChocolate.Types; namespace Database.GraphQl.Users; public sealed class UserType - : EntityType + : EntityType { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs index 580efe5f..3165f2ec 100644 --- a/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs +++ b/backend/src/Jobs/JwtSigningAndEncryptionCertificateRotationJob.cs @@ -1,10 +1,11 @@ using System; -using System.Runtime.ConstrainedExecution; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Database.Authentication; +using Database.Extensions; using Microsoft.Extensions.Logging; +using NodaTime; using Quartz; namespace Database.Jobs; @@ -48,6 +49,7 @@ Exception exception } public sealed class JwtSigningAndEncryptionCertificateRotationJob( + IClock clock, ILogger logger ) : IJob @@ -71,7 +73,7 @@ public async Task Execute(IJobExecutionContext context) // TODO: Trigger OpenIddict reload. Currently done dialy with a cron job that restart all services. } - public static X509Certificate2 CreateSigningCertificate(string distinguishedName) + public static X509Certificate2 CreateSigningCertificate(string distinguishedName, IClock clock) { // In the future use ECDSA. // using var algorithm = ECDsa.Create(ECCurve.NamedCurves.nistP256); @@ -93,7 +95,7 @@ public static X509Certificate2 CreateSigningCertificate(string distinguishedName critical: true ) ); - var now = TimeProvider.System.GetUtcNow(); + var now = clock.GetUtcNow().ToDateTimeOffset(); var ephemeralCertificate = request.CreateSelfSigned( notBefore: now.Add(s_notBeforeOffset), notAfter: now.Add(s_notAfterOffset) @@ -110,7 +112,7 @@ public static X509Certificate2 CreateSigningCertificate(string distinguishedName { try { - return CreateSigningCertificate(distinguishedName); + return CreateSigningCertificate(distinguishedName, clock); } catch (Exception exception) { @@ -119,7 +121,7 @@ public static X509Certificate2 CreateSigningCertificate(string distinguishedName } } - public static X509Certificate2 CreateEncryptionCertificate(string distinguishedName) + public static X509Certificate2 CreateEncryptionCertificate(string distinguishedName, IClock clock) { // In the furture use `ML-KEM`. using var algorithm = RSA.Create(keySizeInBits: 3072); @@ -135,7 +137,7 @@ public static X509Certificate2 CreateEncryptionCertificate(string distinguishedN critical: true ) ); - var now = TimeProvider.System.GetUtcNow(); + var now = clock.GetUtcNow().ToDateTimeOffset(); var ephemeralCertificate = request.CreateSelfSigned( notBefore: now.Add(s_notBeforeOffset), notAfter: now.Add(s_notAfterOffset) @@ -152,7 +154,7 @@ public static X509Certificate2 CreateEncryptionCertificate(string distinguishedN { try { - return CreateEncryptionCertificate(distinguishedName); + return CreateEncryptionCertificate(distinguishedName, clock); } catch (Exception exception) { @@ -198,10 +200,10 @@ public void CleanupLongExpiredCertificatesWithErrorHandling(params string[] dist distinguishedName, validOnly: false ); - var now = TimeProvider.System.GetUtcNow(); + var now = clock.GetUtcNow().ToDateTimeOffset(); foreach (var certificate in certificates) { - // Use `NotAfterDaysOffset` as overlap period. + // Use `RefreshTokenLifetime` as overlap period. if (certificate.NotAfter.Add(OpenIdConnectConstants.RefreshTokenLifetime) < now) { try diff --git a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs index 117db51e..367dc4ff 100644 --- a/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs +++ b/backend/src/Methods/SpectralToIntegral/SpectralToIntegralMethod.cs @@ -226,7 +226,7 @@ public double Calculate( ImmutableArray<(int wavelength, double weight, double deltaWavelength)> wavelengthsWeights ) { - if (spectralDataPoints == null || spectralDataPoints.Count == 0) + if (spectralDataPoints is null || spectralDataPoints.Count is 0) { throw new ArgumentException("The list `spectralDataPoints` is empty."); } diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 3f99d381..2f644af0 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Threading.Tasks; -using HotChocolate.AspNetCore; using Database.Data; using Database.Services; using Microsoft.AspNetCore.Builder; diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs index 6c66b454..2f3c1f5d 100644 --- a/backend/src/Services/AccessRightsService.cs +++ b/backend/src/Services/AccessRightsService.cs @@ -6,6 +6,7 @@ using Database.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using NodaTime; using static Database.ApiRequests.QueryCurrentUserOrInstitution; namespace Database.Services; @@ -27,7 +28,9 @@ public sealed class AccessRightsService( IDbContextFactory dbContextFactory, UserService userService, CacheService cacheService, - ILogger logger) + IClock clock, + ILogger logger +) { public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) where T : IData @@ -65,7 +68,7 @@ CancellationToken cancellationToken institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); } - var result = ProcessData(data, currentUserOrInstitution.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount, cacheService); + var result = ProcessData(data, currentUserOrInstitution.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount); if (context is not null) { @@ -95,8 +98,7 @@ private IEnumerable ProcessData( string? openIdConnectClientId, IReadOnlyList? institutionIds, IReadOnlyList? institutionAccessRights, - uint? alreadyAccessedByUserCount, - CacheService cacheService + uint? alreadyAccessedByUserCount ) where T : IData { @@ -145,7 +147,7 @@ currentUser is null && institutionAccessRights.Any(x => ( x.HasRestrictionsByTime - && x.IsDataRestrictedByTime(dataItem, cacheService, out reason) + && x.IsDataRestrictedByTime(dataItem, clock, cacheService, out reason) ) || ( x.HasRestrictionsByUser diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 77dca2e3..0a4dc806 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -10,7 +10,9 @@ namespace Database.Services; public sealed class CacheService( IMemoryCache currentUserOrInstitutionCache, IMemoryCache accessCountCache, - IMemoryCache timePeriodCountCache) + IMemoryCache timePeriodCountCache, + IClock clock +) { public CurrentUserOrInstitution? SetCurrentUserOrInstitution(string token, CurrentUserOrInstitution cachedUserOrInstitution) { @@ -43,7 +45,7 @@ public uint SetAccessCountForUser(Guid userId, uint count) { if (!timePeriodCountCache.TryGetValue(institutionId, out (OffsetDateTime StartTime, uint Count) accessesPerPeriod)) { - return timePeriodCountCache.Set(institutionId, (OffsetDateTime.UtcNow, (uint)0)); + return timePeriodCountCache.Set(institutionId, (clock.GetUtcNow(), (uint)0)); } return accessesPerPeriod; } @@ -57,6 +59,6 @@ public uint SetAccessCountForUser(Guid userId, uint count) public (OffsetDateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId) { - return timePeriodCountCache.Set(institutionId, (OffsetDateTime.UtcNow, (uint)0)); + return timePeriodCountCache.Set(institutionId, (clock.GetUtcNow(), (uint)0)); } -} +} \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 7d78b304..a5840469 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -32,7 +32,8 @@ public static partial class Log public sealed class ResponseApprovalService( AppSettings appSettings, SigningService signingService, - IRequestExecutorResolver requestExecutorResolver, + IRequestExecutorProvider requestExecutorProvider, + IClock clock, ILogger logger ) { @@ -97,7 +98,7 @@ public async Task CreateResponseApproval(IData dataObject, Can } var (signature, fingerprint) = await signingService.SignData(response); return new ResponseApproval( - OffsetDateTime.UtcNow, + clock.GetUtcNow(), signature, fingerprint, query, @@ -134,7 +135,7 @@ CancellationToken cancellationToken .SetDocument(query) .SetVariableValues(variables) .Build(); - var requestExecutor = await requestExecutorResolver.GetRequestExecutorAsync(cancellationToken: cancellationToken); + var requestExecutor = await requestExecutorProvider.GetExecutorAsync(cancellationToken: cancellationToken); var executionResult = await requestExecutor.ExecuteAsync(operationRequest, cancellationToken); var response = executionResult.ToJson(withIndentations: false); return (query, JsonSerializer.SerializeToElement(variables), response); diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 299411a2..08b297d1 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -171,6 +171,6 @@ private async Task ReceiveKey(string fingerprint) logger.ExecuteCommandOutput(output); logger.ExecuteCommandDiagnostics(diagnostics); logger.ExecuteCommandExitCode(process.ExitCode); - return (process.ExitCode == 0, output, diagnostics); + return (process.ExitCode is 0, output, diagnostics); } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index c1f2c3ca..ab086e01 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -13,7 +13,6 @@ using Database.Enumerations; using Database.GraphQl; using Database.Services; -using HotChocolate.AspNetCore; using Laraue.EfCoreTriggers.PostgreSql.Extensions; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; @@ -29,6 +28,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi; +using NodaTime; using Npgsql; using OpenTelemetry.Exporter; using OpenTelemetry.Logs; @@ -52,10 +52,10 @@ IConfiguration configuration }) ?? throw new InvalidOperationException("Failed to get application settings from configuration."); + private readonly IClock _clock = SystemClock.Instance; + public void ConfigureServices(IServiceCollection services) { - AuthConfiguration.ConfigureServices(services, environment, _appSettings); - GraphQlConfiguration.ConfigureServices(services, environment); ConfigureDatabaseServices(services); ConfigureRequestResponseServices(services); // ConfigureSessionServices(services); // Not used @@ -75,9 +75,12 @@ public void ConfigureServices(IServiceCollection services) .AddDbContextCheck(); services.AddSingleton(_appSettings); services.AddSingleton(environment); + services.AddSingleton(_clock); // services.AddDatabaseDeveloperPageExceptionFilter(); ConfigureCustomServices(services); ConfigureApiRequests(services); + AuthConfiguration.ConfigureServices(services, environment, _appSettings, _clock); + GraphQlConfiguration.ConfigureServices(services, environment); } private static void ConfigureRequestResponseServices(IServiceCollection services) @@ -85,7 +88,6 @@ private static void ConfigureRequestResponseServices(IServiceCollection services // https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer#forwarded-headers-middleware-order services.Configure(_ => { - // TODO _.AllowedHosts = ... _.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | @@ -169,6 +171,7 @@ IServiceCollection services // { // _.AddAspNetCoreInstrumentation(); // _.AddHttpClientInstrumentation(); + // _.AddHotChocolateInstrumentation(); // _.AddOtlpExporter(_ => // { // _.Endpoint = _appSettings.OpenTelemetry.GrpcUri; @@ -251,6 +254,7 @@ private void ConfigureDatabaseServices(IServiceCollection services) // Configure the database-context options only once in // `AddPooledDbContextFactory` and not a second time in `AddDbContext` // as suggested in + // https://github.com/npgsql/efcore.pg/issues/3375#issuecomment-2509746639 services.AddPooledDbContextFactory(ConfigureDatabaseContext); // Database context as services are used by `OpenIddict`, see in // particular `AuthConfiguration`. @@ -330,11 +334,11 @@ public void Configure(WebApplication app) app.UseStaticFiles(); app.UseCookiePolicy(); // [SameSite cookies](https://learn.microsoft.com/en-us/aspnet/core/security/samesite) app.UseRouting(); - // TODO Do we really want this? See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-5.0 + // [Localization](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) app.UseRequestLocalization(_ => { - _.AddSupportedCultures("en-US", "de-DE"); - _.AddSupportedUICultures("en-US", "de-DE"); + _.AddSupportedCultures("en-US"); + _.AddSupportedUICultures("en-US"); _.SetDefaultCulture("en-US"); }); app.UseCors(); @@ -357,25 +361,6 @@ public void Configure(WebApplication app) _.WithOpenApiRoutePattern(OpenApiConstants.RoutePattern); }); app.MapGraphQL() - .WithOptions( - // https://chillicream.com/docs/hotchocolate/server/middleware - new GraphQLServerOptions - { - EnableSchemaRequests = true, - EnableGetRequests = false, - // AllowedGetOperations = AllowedGetOperations.Query - EnableMultipartRequests = false, - Tool = - { - DisableTelemetry = true, - Enable = true, // environment.IsDevelopment() - IncludeCookies = false, - GraphQLEndpoint = GraphQlConstants.EndpointPath, - HttpMethod = DefaultHttpMethod.Post, - Title = "GraphQL" - } - } - ) .RequireCors(GraphQlConstants.CorsPolicy); app.MapControllers(); app.MapHealthChecks("/health", diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs index 42b4013b..1bca5e53 100644 --- a/backend/src/Utilities/FileHelpers.cs +++ b/backend/src/Utilities/FileHelpers.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; -using System.Linq; using System.Net; using System.Reflection; using System.Threading.Tasks; @@ -42,7 +40,7 @@ ModelStateDictionary modelState formFile.FileName); // Check the file length. This check doesn't catch files that only have // a BOM as their content. - if (formFile.Length == 0) + if (formFile.Length is 0) { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); @@ -55,7 +53,7 @@ ModelStateDictionary modelState // Check the content length in case the file's only // content was a BOM and the content is actually // empty after removing the BOM. - if (memoryStream.Length == 0) + if (memoryStream.Length is 0) { modelState.AddModelError(formFile.Name, $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); @@ -85,7 +83,7 @@ ModelStateDictionary modelState { using var memoryStream = new MemoryStream(); await section.Body.CopyToAsync(memoryStream); - if (memoryStream.Length == 0) + if (memoryStream.Length is 0) { modelState.AddModelError("File", "The file is empty."); } diff --git a/backend/test/AuditableTests.cs b/backend/test/AuditableTests.cs new file mode 100644 index 00000000..a699a184 --- /dev/null +++ b/backend/test/AuditableTests.cs @@ -0,0 +1,75 @@ +using NodaTime; +using NodaTime.Testing; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using Database.Data; +using System; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +namespace Database.Tests; + +[TestFixture] +public sealed class AuditableTests +{ + private DbContextOptions _options = default!; + + [SetUp] + public void Setup() + { + _options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .Options; + } + + [Test] + [SuppressMessage("Naming", "CA1707")] + public async Task SaveChanges_SetsAndUpdatesTimestamps_UsingFakeClock() + { + // Arrange + var startInstant = Instant.FromUtc(2024, 1, 1, 10, 0); + var fakeClock = new FakeClock(startInstant); + using var context = new ApplicationDbContext(_options, fakeClock); + var entity = new User("Subject", "Name"); + // Act + context.Add(entity); + await context.SaveChangesAsync(); + // Assert + Assert.Multiple(() => + { + Assert.That(entity.CreatedAt, Is.EqualTo(startInstant.WithOffset(Offset.Zero).ToDateTimeOffset())); + Assert.That(entity.UpdatedAt, Is.EqualTo(startInstant.WithOffset(Offset.Zero).ToDateTimeOffset())); + }); + // Act + var duration = Duration.FromHours(1); + fakeClock.Advance(duration); + var updatedInstant = startInstant.Plus(duration); + entity.Update("New Name"); + await context.SaveChangesAsync(); + // Assert + Assert.Multiple(() => + { + Assert.That(entity.CreatedAt, Is.EqualTo(startInstant.WithOffset(Offset.Zero).ToDateTimeOffset()), "CreatedAt should not change on update."); + Assert.That(entity.UpdatedAt, Is.EqualTo(updatedInstant.WithOffset(Offset.Zero).ToDateTimeOffset()), "UpdatedAt should reflect the new fake time."); + }); + } + + // [Test] + // public async Task Remove_PerformsSoftDelete_AndSetsDeletedAt() + // { + // var deleteTime = Instant.FromUtc(2024, 1, 1, 15, 0); + // var fakeClock = new FakeClock(deleteTime); + // using var context = new ApplicationDbContext(_options, fakeClock); + // var entity = new YourModel { Name = "To Be Deleted" }; + // context.Add(entity); + // await context.SaveChangesAsync(); + // // Act + // context.Remove(entity); + // await context.SaveChangesAsync(); + // // Assert + // Assert.That(entity.DeletedAt, Is.EqualTo(deleteTime)); + // // Ensure it's hidden from normal queries + // var count = await context.YourModels.CountAsync(); + // Assert.That(count, Is.Zero); + // } +} \ No newline at end of file diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 01e4126b..e052358d 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -9,21 +9,23 @@ - + - - - + + + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + From 7bcbf430764e03cab5f6dec6286a5cff93bc9d35 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 27 May 2026 19:27:21 +0200 Subject: [PATCH 804/878] Level-up the user interface --- backend/dotnet-tools.json | 4 +- .../src/ApiRequests/ComponentDataLoader.cs | 9 +- .../src/ApiRequests/DataFormatDataLoader.cs | 8 +- backend/src/ApiRequests/DatabaseDataLoader.cs | 64 + .../src/ApiRequests/InstitutionDataLoader.cs | 9 +- backend/src/ApiRequests/MethodDataLoader.cs | 9 +- .../OpenIdConnectApplicationDataLoader.cs | 9 +- .../ApiRequests/Queries/Components.graphql | 2 +- .../src/ApiRequests/Queries/Database.graphql | 19 - .../src/ApiRequests/Queries/Databases.graphql | 22 + .../ApiRequests/Queries/Institutions.graphql | 2 +- .../src/ApiRequests/Queries/Methods.graphql | 2 +- .../Queries/OpenIdConnectApplications.graphql | 1 + .../Queries/UpdateDatabase.graphql | 49 +- backend/src/ApiRequests/Queries/Users.graphql | 1 + .../Queries/VerifyDatabase.graphql | 27 + backend/src/ApiRequests/QueryDatabase.cs | 85 - backend/src/ApiRequests/UpdateDatabase.cs | 2 +- backend/src/ApiRequests/UserDataLoader.cs | 9 +- backend/src/ApiRequests/VerifyDatabase.cs | 86 + .../src/Configuration/GraphQlConfiguration.cs | 4 +- backend/src/Data/GetHttpsResource.cs | 2 +- backend/src/Database.csproj | 26 +- backend/src/GraphQl/AppliedMethodType.cs | 35 + .../CalorimetricDataFilterType.cs | 17 + .../Common/OpenEndedDateTimeRangeType.cs | 7 +- .../GraphQl/CrossDatabaseDataReferenceType.cs | 35 + .../GraphQl/DataApprovals/DataApprovalType.cs | 22 +- backend/src/GraphQl/DataX/DataDataLoaders.cs | 69 - backend/src/GraphQl/DataX/DataFilterType.cs | 17 + .../src/GraphQl/DataX/DataFilterTypeBase.cs | 2 +- backend/src/GraphQl/DataX/DataResolvers.cs | 26 + backend/src/GraphQl/DataX/DataType.cs | 16 + backend/src/GraphQl/DataX/DataTypeBase.cs | 30 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 5 +- .../src/GraphQl/Databases/DatabaseQueries.cs | 8 +- .../Databases/VerifyDatabaseMutation.cs | 42 + .../src/GraphQl/FileMetaInformationType.cs | 35 + .../GeometricDataX/GeometricDataFilterType.cs | 17 + .../GetHttpsResourceDataLoaders.cs | 55 + .../GetHttpsResourceResolvers.cs | 9 + .../GetHttpsResources/GetHttpsResourceType.cs | 11 + backend/src/GraphQl/GraphQlConstants.cs | 1 + .../HygrothermalDataFilterType.cs | 17 + .../LifeCycleDataX/LifeCycleDataFilterType.cs | 17 + backend/src/GraphQl/Methods/MethodQueries.cs | 6 +- .../OpticalDataX/OpticalDataFilterType.cs | 17 + .../PhotovoltaicDataFilterType.cs | 17 + .../src/GraphQl/References/ReferenceType.cs | 3 - ...ToTreeVertexAppliedConversionMethodType.cs | 35 + ...27161543_MakeEntitiesAuditable.Designer.cs | 3226 ++++++++++++++ .../20260527161543_MakeEntitiesAuditable.cs | 407 ++ .../ApplicationDbContextModelSnapshot.cs | 137 +- backend/src/Migrations/migrate.sql | 200 + ...o_20260527161543_MakeEntitiesAuditable.sql | 200 + ...ble_to_20260316165622_AddLifeCycleData.sql | 172 + backend/src/Startup.cs | 3 +- backend/test/Database.Tests.csproj | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 1661 ++++--- frontend/codegen.ts | 95 +- .../components/ActiveFilterAndSortBar.tsx | 132 + frontend/components/CielabColorView.tsx | 19 + frontend/components/CodeView.tsx | 32 + frontend/components/CopyButton.tsx | 85 + frontend/components/Copyable.tsx | 26 + frontend/components/CopyableBlock.tsx | 53 + frontend/components/DateTimeX.tsx | 34 + frontend/components/DeleteButton.tsx | 63 + frontend/components/EditButton.tsx | 34 + frontend/components/EnumSelect.tsx | 52 + frontend/components/EnumTag.tsx | 16 + frontend/components/Float.tsx | 63 + frontend/components/Footer.tsx | 11 +- frontend/components/GnuPgKeyLink.tsx | 24 + frontend/components/Highlight.tsx | 19 - frontend/components/Iconize.tsx | 16 + frontend/components/Id.tsx | 5 + frontend/components/IdentifierItem.tsx | 95 + frontend/components/InlineList.tsx | 35 + frontend/components/JsonView.tsx | 36 + frontend/components/JumpToId.tsx | 47 + frontend/components/Layout.tsx | 49 +- frontend/components/NavBar.tsx | 191 +- frontend/components/PaginatedIdSelect.tsx | 163 + frontend/components/Pagination.tsx | 82 + frontend/components/QueryToolbar.tsx | 47 + frontend/components/Reference.tsx | 108 + frontend/components/SlideDown.tsx | 51 + frontend/components/UuidFormItem.tsx | 38 + frontend/components/data/DataPageHeader.tsx | 69 - frontend/components/data/DataSummary.tsx | 316 ++ .../data/calorimetric/CalorimetricData.tsx | 68 +- .../calorimetric/CalorimetricDataList.tsx | 24 + .../calorimetric/CalorimetricDataSummary.tsx | 47 + .../PaginatedCalorimetricData.tsx | 76 + .../data/geometric/GeometricData.tsx | 60 +- .../data/geometric/GeometricDataList.tsx | 24 + .../data/geometric/GeometricDataSummary.tsx | 32 + .../data/geometric/PaginatedGeometricData.tsx | 69 + .../data/hygrothermal/HygrothermalData.tsx | 57 +- .../hygrothermal/HygrothermalDataList.tsx | 24 + .../hygrothermal/HygrothermalDataSummary.tsx | 13 + .../PaginatedHygrothermalData.tsx | 62 + .../data/lifeCycle/LifeCycleData.tsx | 52 +- .../data/lifeCycle/LifeCycleDataList.tsx | 24 + .../data/lifeCycle/LifeCycleDataSummary.tsx | 13 + .../data/lifeCycle/PaginatedLifeCycleData.tsx | 62 + .../components/data/optical/OpticalData.tsx | 101 +- .../data/optical/OpticalDataList.tsx | 27 + .../data/optical/OpticalDataRibbon.tsx | 26 + .../data/optical/OpticalDataSummary.tsx | 106 + .../data/optical/PaginatedOpticalData.tsx | 140 + .../PaginatedPhotovoltaicData.tsx | 62 + .../data/photovoltaic/PhotovoltaicData.tsx | 57 +- .../photovoltaic/PhotovoltaicDataList.tsx | 24 + .../photovoltaic/PhotovoltaicDataSummary.tsx | 13 + frontend/components/databases/Database.tsx | 82 +- .../components/databases/DatabaseSummary.tsx | 86 + .../components/databases/UpdateDatabase.tsx | 127 +- .../components/databases/VerifyDatabase.tsx | 47 + frontend/components/entities/EntityItem.tsx | 10 + frontend/components/entities/EntityLink.tsx | 31 + frontend/components/entities/EntityList.tsx | 58 + .../components/entities/EntitySummary.tsx | 114 + .../components/entities/PaginatedEntities.tsx | 379 ++ .../filtering/BaseFilterSubform.tsx | 56 + .../filtering/EnumFilterSubform.tsx | 58 + .../components/filtering/FilterSubform.tsx | 67 + .../filtering/FloatFilterSubform.tsx | 78 + .../components/filtering/IntFilterSubform.tsx | 77 + .../filtering/ListFilterSubform.tsx | 35 + .../filtering/ObjectFilterSubform.tsx | 58 + .../filtering/StringFilterSubform.tsx | 52 + .../components/filtering/UrlFilterSubform.tsx | 50 + .../filtering/UuidFilterSubform.tsx | 40 + .../components/methods/AppliedMethodView.tsx | 82 + frontend/components/sorting/SortSubform.tsx | 35 + frontend/jest.config.js | 13 - frontend/lib/apollo.ts | 23 + frontend/lib/array.ts | 59 + frontend/lib/assert.ts | 3 + frontend/lib/connection.ts | 12 + frontend/lib/filter.ts | 507 +++ frontend/lib/form.ts | 60 +- frontend/lib/freeTextFilter.tsx | 107 - frontend/lib/hooks/useDebounce.ts | 26 + frontend/lib/hooks/useMutationHandler.ts | 124 + frontend/lib/hooks/usePaginatedQuery.ts | 178 + frontend/lib/hooks/usePreviousNonNull.ts | 9 + frontend/lib/hooks/useQueryHandler.ts | 18 + frontend/lib/sort.ts | 36 + frontend/lib/string.ts | 69 + frontend/lib/table.tsx | 691 --- frontend/next.config.ts | 2 +- frontend/package.json | 43 +- frontend/pages/_app.tsx | 1 + frontend/pages/data/calorimetric/[uuid].tsx | 11 +- frontend/pages/data/calorimetric/index.tsx | 328 +- frontend/pages/data/create.tsx | 2 +- frontend/pages/data/geometric/[uuid].tsx | 13 +- frontend/pages/data/geometric/index.tsx | 265 +- frontend/pages/data/hygrothermal/[uuid].tsx | 11 +- frontend/pages/data/hygrothermal/index.tsx | 250 +- frontend/pages/data/life-cycle/[uuid].tsx | 11 +- frontend/pages/data/life-cycle/index.tsx | 250 +- frontend/pages/data/optical/[uuid].tsx | 11 +- frontend/pages/data/optical/index.tsx | 465 +- frontend/pages/data/photovoltaic/[uuid].tsx | 11 +- frontend/pages/data/photovoltaic/index.tsx | 250 +- frontend/pages/index.tsx | 24 +- frontend/pages/unauthorized.tsx | 16 +- frontend/pages/upload-file.tsx | 2 +- frontend/pages/user-info.tsx | 13 +- frontend/paths.ts | 145 +- frontend/queries/common.graphql | 25 + frontend/queries/currentUser.graphql | 1 + frontend/queries/data.graphql | 499 ++- frontend/queries/databases.graphql | 48 +- .../test/__snapshots__/index.test.tsx.snap | 33 - frontend/test/index.test.tsx | 37 - frontend/type-defs.graphqls | 2573 ++++++----- frontend/yarn.lock | 3938 ++++++----------- 182 files changed, 14810 insertions(+), 8424 deletions(-) create mode 100644 backend/src/ApiRequests/DatabaseDataLoader.cs delete mode 100644 backend/src/ApiRequests/Queries/Database.graphql create mode 100644 backend/src/ApiRequests/Queries/Databases.graphql create mode 100644 backend/src/ApiRequests/Queries/VerifyDatabase.graphql delete mode 100644 backend/src/ApiRequests/QueryDatabase.cs create mode 100644 backend/src/ApiRequests/VerifyDatabase.cs create mode 100644 backend/src/GraphQl/AppliedMethodType.cs create mode 100644 backend/src/GraphQl/CrossDatabaseDataReferenceType.cs delete mode 100644 backend/src/GraphQl/DataX/DataDataLoaders.cs create mode 100644 backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs create mode 100644 backend/src/GraphQl/FileMetaInformationType.cs create mode 100644 backend/src/GraphQl/ToTreeVertexAppliedConversionMethodType.cs create mode 100644 backend/src/Migrations/20260527161543_MakeEntitiesAuditable.Designer.cs create mode 100644 backend/src/Migrations/20260527161543_MakeEntitiesAuditable.cs create mode 100644 backend/src/Migrations/migrate_from_20260316165622_AddLifeCycleData_to_20260527161543_MakeEntitiesAuditable.sql create mode 100644 backend/src/Migrations/rollback_from_20260527161543_MakeEntitiesAuditable_to_20260316165622_AddLifeCycleData.sql create mode 100644 frontend/components/ActiveFilterAndSortBar.tsx create mode 100644 frontend/components/CielabColorView.tsx create mode 100644 frontend/components/CodeView.tsx create mode 100644 frontend/components/CopyButton.tsx create mode 100644 frontend/components/Copyable.tsx create mode 100644 frontend/components/CopyableBlock.tsx create mode 100644 frontend/components/DateTimeX.tsx create mode 100644 frontend/components/DeleteButton.tsx create mode 100644 frontend/components/EditButton.tsx create mode 100644 frontend/components/EnumSelect.tsx create mode 100644 frontend/components/EnumTag.tsx create mode 100644 frontend/components/Float.tsx create mode 100644 frontend/components/GnuPgKeyLink.tsx delete mode 100644 frontend/components/Highlight.tsx create mode 100644 frontend/components/Iconize.tsx create mode 100644 frontend/components/Id.tsx create mode 100644 frontend/components/IdentifierItem.tsx create mode 100644 frontend/components/InlineList.tsx create mode 100644 frontend/components/JsonView.tsx create mode 100644 frontend/components/JumpToId.tsx create mode 100644 frontend/components/PaginatedIdSelect.tsx create mode 100644 frontend/components/Pagination.tsx create mode 100644 frontend/components/QueryToolbar.tsx create mode 100644 frontend/components/Reference.tsx create mode 100644 frontend/components/SlideDown.tsx create mode 100644 frontend/components/UuidFormItem.tsx delete mode 100644 frontend/components/data/DataPageHeader.tsx create mode 100644 frontend/components/data/DataSummary.tsx create mode 100644 frontend/components/data/calorimetric/CalorimetricDataList.tsx create mode 100644 frontend/components/data/calorimetric/CalorimetricDataSummary.tsx create mode 100644 frontend/components/data/calorimetric/PaginatedCalorimetricData.tsx create mode 100644 frontend/components/data/geometric/GeometricDataList.tsx create mode 100644 frontend/components/data/geometric/GeometricDataSummary.tsx create mode 100644 frontend/components/data/geometric/PaginatedGeometricData.tsx create mode 100644 frontend/components/data/hygrothermal/HygrothermalDataList.tsx create mode 100644 frontend/components/data/hygrothermal/HygrothermalDataSummary.tsx create mode 100644 frontend/components/data/hygrothermal/PaginatedHygrothermalData.tsx create mode 100644 frontend/components/data/lifeCycle/LifeCycleDataList.tsx create mode 100644 frontend/components/data/lifeCycle/LifeCycleDataSummary.tsx create mode 100644 frontend/components/data/lifeCycle/PaginatedLifeCycleData.tsx create mode 100644 frontend/components/data/optical/OpticalDataList.tsx create mode 100644 frontend/components/data/optical/OpticalDataRibbon.tsx create mode 100644 frontend/components/data/optical/OpticalDataSummary.tsx create mode 100644 frontend/components/data/optical/PaginatedOpticalData.tsx create mode 100644 frontend/components/data/photovoltaic/PaginatedPhotovoltaicData.tsx create mode 100644 frontend/components/data/photovoltaic/PhotovoltaicDataList.tsx create mode 100644 frontend/components/data/photovoltaic/PhotovoltaicDataSummary.tsx create mode 100644 frontend/components/databases/DatabaseSummary.tsx create mode 100644 frontend/components/databases/VerifyDatabase.tsx create mode 100644 frontend/components/entities/EntityItem.tsx create mode 100644 frontend/components/entities/EntityLink.tsx create mode 100644 frontend/components/entities/EntityList.tsx create mode 100644 frontend/components/entities/EntitySummary.tsx create mode 100644 frontend/components/entities/PaginatedEntities.tsx create mode 100644 frontend/components/filtering/BaseFilterSubform.tsx create mode 100644 frontend/components/filtering/EnumFilterSubform.tsx create mode 100644 frontend/components/filtering/FilterSubform.tsx create mode 100644 frontend/components/filtering/FloatFilterSubform.tsx create mode 100644 frontend/components/filtering/IntFilterSubform.tsx create mode 100644 frontend/components/filtering/ListFilterSubform.tsx create mode 100644 frontend/components/filtering/ObjectFilterSubform.tsx create mode 100644 frontend/components/filtering/StringFilterSubform.tsx create mode 100644 frontend/components/filtering/UrlFilterSubform.tsx create mode 100644 frontend/components/filtering/UuidFilterSubform.tsx create mode 100644 frontend/components/methods/AppliedMethodView.tsx create mode 100644 frontend/components/sorting/SortSubform.tsx delete mode 100644 frontend/jest.config.js create mode 100644 frontend/lib/array.ts create mode 100644 frontend/lib/assert.ts create mode 100644 frontend/lib/connection.ts create mode 100644 frontend/lib/filter.ts delete mode 100644 frontend/lib/freeTextFilter.tsx create mode 100644 frontend/lib/hooks/useDebounce.ts create mode 100644 frontend/lib/hooks/useMutationHandler.ts create mode 100644 frontend/lib/hooks/usePaginatedQuery.ts create mode 100644 frontend/lib/hooks/usePreviousNonNull.ts create mode 100644 frontend/lib/hooks/useQueryHandler.ts create mode 100644 frontend/lib/sort.ts create mode 100644 frontend/lib/string.ts delete mode 100644 frontend/lib/table.tsx create mode 100644 frontend/queries/common.graphql delete mode 100644 frontend/test/__snapshots__/index.test.tsx.snap delete mode 100644 frontend/test/index.test.tsx diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index 972eb2a4..d67bced7 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -31,7 +31,7 @@ "rollForward": false }, "dotnet-script": { - "version": "2.0.0", + "version": "2.0.1", "commands": [ "dotnet-script" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2026.1.1", + "version": "2026.1.2", "commands": [ "jb" ], diff --git a/backend/src/ApiRequests/ComponentDataLoader.cs b/backend/src/ApiRequests/ComponentDataLoader.cs index edc0ecfe..c585a9b7 100644 --- a/backend/src/ApiRequests/ComponentDataLoader.cs +++ b/backend/src/ApiRequests/ComponentDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,8 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Component( - Guid Id - ) : IIdNode; + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } private sealed record ComponentsData( Connection? Connection diff --git a/backend/src/ApiRequests/DataFormatDataLoader.cs b/backend/src/ApiRequests/DataFormatDataLoader.cs index 09350273..762d630b 100644 --- a/backend/src/ApiRequests/DataFormatDataLoader.cs +++ b/backend/src/ApiRequests/DataFormatDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,12 +17,15 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record DataFormat( - Guid Id, + [property: GraphQLIgnore] Guid Id, string Name, string? Extension, string MediaType, Uri? SchemaLocator - ) : IIdNode; + ) : IIdNode + { + public Guid Uuid => Id; + } private sealed record DataFormatsData( Connection? Connection diff --git a/backend/src/ApiRequests/DatabaseDataLoader.cs b/backend/src/ApiRequests/DatabaseDataLoader.cs new file mode 100644 index 00000000..35374435 --- /dev/null +++ b/backend/src/ApiRequests/DatabaseDataLoader.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Database.Services; +using GreenDonut; +using HotChocolate; +using static Database.ApiRequests.QueryByIdDataLoader; + +namespace Database.ApiRequests; + +public sealed class DatabaseDataLoader +{ + private const string QueryFileName = "Databases.graphql"; + + public static Uri GetGraphQlEndpoint(AppSettings appSettings) => + QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); + + public sealed record Database( + [property: GraphQLIgnore] Guid Id, + string Name, + string Description, + Uri Locator, + DatabaseVerificationState VerificationState, + string VerificationCode, + DatabaseOperatorEdge Operator, + bool IsAuthorizedToUpdateNode, + bool IsAuthorizedToVerifyNode + ) : IIdNode + { + public Guid Uuid => Id; + } + + public enum DatabaseVerificationState + { + PENDING, + VERIFIED + } + + public sealed record DatabaseOperatorEdge( + InstitutionDataLoader.Institution Node + ); + + private sealed record DatabasesData( + Connection? Connection + ) : IConnectionData; + + [DataLoader] + public static Task> GetDatabaseByIdAsync( + IReadOnlyList componentIds, + ApiRequestService apiRequestService, + AppSettings appSettings, + CancellationToken cancellationToken + ) + { + return QueryByIdDataLoader.GetByIdAsync( + componentIds, + [QueryFileName], + apiRequestService, + appSettings, + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/ApiRequests/InstitutionDataLoader.cs b/backend/src/ApiRequests/InstitutionDataLoader.cs index 9d431466..37aca9ae 100644 --- a/backend/src/ApiRequests/InstitutionDataLoader.cs +++ b/backend/src/ApiRequests/InstitutionDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,8 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Institution( - Guid Id - ) : IIdNode; + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } private sealed record InstitutionsData( Connection? Connection diff --git a/backend/src/ApiRequests/MethodDataLoader.cs b/backend/src/ApiRequests/MethodDataLoader.cs index 20808157..10d1f9ed 100644 --- a/backend/src/ApiRequests/MethodDataLoader.cs +++ b/backend/src/ApiRequests/MethodDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,8 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record Method( - Guid Id - ) : IIdNode; + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } private sealed record MethodsData( Connection? Connection diff --git a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs index 7d1b4144..fc13548c 100644 --- a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,8 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record OpenIdConnectApplication( - string Id - ) : IIdNode; + [property: GraphQLIgnore] string Id, + string Name + ) : IIdNode + { + string ClientId => Id; + } private sealed record OpenIdConnectApplicationsData( Connection? Connection diff --git a/backend/src/ApiRequests/Queries/Components.graphql b/backend/src/ApiRequests/Queries/Components.graphql index 610926ea..4c680835 100644 --- a/backend/src/ApiRequests/Queries/Components.graphql +++ b/backend/src/ApiRequests/Queries/Components.graphql @@ -3,8 +3,8 @@ query Components($ids: [Uuid!]!) { edges { node { id: uuid + name } } } } - diff --git a/backend/src/ApiRequests/Queries/Database.graphql b/backend/src/ApiRequests/Queries/Database.graphql deleted file mode 100644 index 5ba53c63..00000000 --- a/backend/src/ApiRequests/Queries/Database.graphql +++ /dev/null @@ -1,19 +0,0 @@ -query Database( - $id: Uuid! -) { - database(id: $id) { - uuid - name - description - locator - verificationState - verificationCode - operator { - node { - uuid - } - } - isAuthorizedToUpdateNode - isAuthorizedToVerifyNode - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/Queries/Databases.graphql b/backend/src/ApiRequests/Queries/Databases.graphql new file mode 100644 index 00000000..5585f6ed --- /dev/null +++ b/backend/src/ApiRequests/Queries/Databases.graphql @@ -0,0 +1,22 @@ +query Databases($ids: [Uuid!]!) { + connection: databases(where: { id: { in: $ids } }) { + edges { + node { + id: uuid + name + description + locator + verificationState + verificationCode + operator { + node { + id: uuid + name + } + } + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/Institutions.graphql b/backend/src/ApiRequests/Queries/Institutions.graphql index d81a0594..59dcc7ce 100644 --- a/backend/src/ApiRequests/Queries/Institutions.graphql +++ b/backend/src/ApiRequests/Queries/Institutions.graphql @@ -3,8 +3,8 @@ query Institutions($ids: [Uuid!]!) { edges { node { id: uuid + name } } } } - diff --git a/backend/src/ApiRequests/Queries/Methods.graphql b/backend/src/ApiRequests/Queries/Methods.graphql index 05b068cd..d7ea1fd2 100644 --- a/backend/src/ApiRequests/Queries/Methods.graphql +++ b/backend/src/ApiRequests/Queries/Methods.graphql @@ -3,8 +3,8 @@ query Methods($ids: [Uuid!]!) { edges { node { id: uuid + name } } } } - diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql index 932ffec3..8eb9c946 100644 --- a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -3,6 +3,7 @@ query OpenIdConnectApplications($ids: [String!]!) { edges { node { id: clientId + displayName } } } diff --git a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql index 05c7eba1..fddff65e 100644 --- a/backend/src/ApiRequests/Queries/UpdateDatabase.graphql +++ b/backend/src/ApiRequests/Queries/UpdateDatabase.graphql @@ -1,26 +1,27 @@ -mutation UpdateDatabase( - $input: UpdateDatabaseInput! -) { - updateDatabase(input: $input) { - database { - uuid - name - description - locator - verificationState - verificationCode - operator { - node { - uuid - } - } - isAuthorizedToUpdateNode - isAuthorizedToVerifyNode - } - errors { - code - message - path +mutation UpdateDatabase($input: UpdateDatabaseInput!) { + updateDatabase(input: $input) { + database { + id + uuid + name + description + locator + verificationState + verificationCode + operator { + node { + id + uuid + name } + } + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode + } + errors { + code + message + path } -} \ No newline at end of file + } +} diff --git a/backend/src/ApiRequests/Queries/Users.graphql b/backend/src/ApiRequests/Queries/Users.graphql index 09a0a69b..ebdea272 100644 --- a/backend/src/ApiRequests/Queries/Users.graphql +++ b/backend/src/ApiRequests/Queries/Users.graphql @@ -3,6 +3,7 @@ query Users($ids: [Uuid!]!) { edges { node { id: uuid + name } } } diff --git a/backend/src/ApiRequests/Queries/VerifyDatabase.graphql b/backend/src/ApiRequests/Queries/VerifyDatabase.graphql new file mode 100644 index 00000000..dd97f77f --- /dev/null +++ b/backend/src/ApiRequests/Queries/VerifyDatabase.graphql @@ -0,0 +1,27 @@ +mutation VerifyDatabase($input: VerifyDatabaseInput!) { + verifyDatabase(input: $input) { + database { + id + uuid + name + description + locator + verificationState + verificationCode + operator { + node { + id + uuid + name + } + } + isAuthorizedToUpdateNode + isAuthorizedToVerifyNode + } + errors { + code + message + path + } + } +} diff --git a/backend/src/ApiRequests/QueryDatabase.cs b/backend/src/ApiRequests/QueryDatabase.cs deleted file mode 100644 index 0a4953d8..00000000 --- a/backend/src/ApiRequests/QueryDatabase.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Database.Logging; -using Database.Services; -using GraphQL; -using Microsoft.Extensions.Logging; - -namespace Database.ApiRequests; - -public static partial class Log -{ - [LoggerMessage( - Level = LogLevel.Error, - Message = "Response contains errors.") - ] - internal static partial void ResponseErrors( - this ILogger logger, - [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors - ); -} - -public sealed class QueryDatabase( - AppSettings appSettings, - ApiRequestService apiRequestService, - ILogger logger -) -{ - private const string QueryFileName = "Database.graphql"; - - public Uri GetGraphQlEndpoint => - appSettings.MetabaseGraphQlEndpoint; - - public sealed record Database( - Guid Uuid, - string Name, - string Description, - Uri Locator, - DatabaseVerificationState VerificationState, - string VerificationCode, - DatabaseOperatorEdge Operator, - bool IsAuthorizedToUpdateNode, - bool IsAuthorizedToVerifyNode - ); - - public enum DatabaseVerificationState - { - PENDING, - VERIFIED - } - - public sealed record DatabaseOperatorEdge( - Institution Node - ); - - public sealed record Institution( - Guid Uuid - ); - - private sealed record DatabaseData(Database? Database); - - public async Task Do( - Guid databaseId, - CancellationToken cancellationToken - ) - { - var response = (await apiRequestService.QueryGraphQl( - GetGraphQlEndpoint, - new GraphQLRequest( - await GraphQlQueryHelpers.Construct(QueryFileName), - new - { - id = databaseId - }, - "Database" - ), - cancellationToken - )); - if (response.Errors is not null) - { - logger.ResponseErrors(response.Errors); - } - return response.Data.Database; ; - } -} \ No newline at end of file diff --git a/backend/src/ApiRequests/UpdateDatabase.cs b/backend/src/ApiRequests/UpdateDatabase.cs index e876b2a2..4e842b34 100644 --- a/backend/src/ApiRequests/UpdateDatabase.cs +++ b/backend/src/ApiRequests/UpdateDatabase.cs @@ -41,7 +41,7 @@ Uri Locator ); public sealed record UpdateDatabasePayload( - QueryDatabase.Database? Database, + DatabaseDataLoader.Database? Database, IReadOnlyList? Errors ); diff --git a/backend/src/ApiRequests/UserDataLoader.cs b/backend/src/ApiRequests/UserDataLoader.cs index be2ff55a..bffa2e9d 100644 --- a/backend/src/ApiRequests/UserDataLoader.cs +++ b/backend/src/ApiRequests/UserDataLoader.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Services; using GreenDonut; +using HotChocolate; using static Database.ApiRequests.QueryByIdDataLoader; namespace Database.ApiRequests; @@ -16,8 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record User( - Guid Id - ) : IIdNode; + [property: GraphQLIgnore] Guid Id, + string Name + ) : IIdNode + { + public Guid Uuid => Id; + } private sealed record UsersData( Connection? Connection diff --git a/backend/src/ApiRequests/VerifyDatabase.cs b/backend/src/ApiRequests/VerifyDatabase.cs new file mode 100644 index 00000000..e85efa6b --- /dev/null +++ b/backend/src/ApiRequests/VerifyDatabase.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Logging; +using Database.Services; +using GraphQL; +using Microsoft.Extensions.Logging; + +namespace Database.ApiRequests; + +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Error, + Message = "Response contains errors.") + ] + internal static partial void ResponseErrors( + this ILogger logger, + [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors + ); +} + +public sealed class VerifyDatabase( + AppSettings appSettings, + ApiRequestService apiRequestService, + ILogger logger +) +{ + private const string VerifyDatabaseFileName = "VerifyDatabase.graphql"; + + public Uri GetGraphQlEndpoint => + appSettings.MetabaseGraphQlEndpoint; + + public sealed record VerifyDatabaseInput( + Guid DatabaseId + ); + + public sealed record VerifyDatabasePayload( + DatabaseDataLoader.Database? Database, + IReadOnlyList? Errors + ); + + [SuppressMessage("Naming", "CA1707")] + public enum VerifyDatabaseErrorCode + { + UNKNOWN, + UNAUTHORIZED, + UNKNOWN_DATABASE + } + + public sealed record VerifyDatabaseError( + VerifyDatabaseErrorCode Code, + string Message, + IReadOnlyList Path + ); + + private sealed record VerifyDatabaseData(VerifyDatabasePayload? VerifyDatabase); + + public async Task Do( + VerifyDatabaseInput verifyDatabaseInput, + CancellationToken cancellationToken + ) + { + var response = (await apiRequestService.QueryGraphQl( + GetGraphQlEndpoint, + new GraphQLRequest( + await GraphQlQueryHelpers.Construct( + VerifyDatabaseFileName + ), + new + { + input = verifyDatabaseInput + }, + "VerifyDatabase" + ), + cancellationToken + )); + if (response.Errors is not null) + { + logger.ResponseErrors(response.Errors); + } + return response.Data.VerifyDatabase; + } +} \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index f485fdbd..c2d1d9e8 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -129,8 +129,8 @@ IWebHostEnvironment environment ) .ModifyCostOptions(_ => { - _.MaxFieldCost = 10000; - _.MaxTypeCost = 10000; + _.MaxFieldCost = 12000; + _.MaxTypeCost = 12000; } ) // Configure diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index e7537619..f14fe843 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -146,7 +146,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() public ICollection ArchivedFilesMetaInformation { get; private set; } = []; // Note that at least one data ID is always present. So `Guid.Empty` will never be used. - [NotMapped] + [Projectable] public Guid DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; [NotMapped] diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index baa7653e..601ea1f0 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -16,17 +16,17 @@ - - - - - - - - - - - + + + + + + + + + + + @@ -41,8 +41,8 @@ - - + + diff --git a/backend/src/GraphQl/AppliedMethodType.cs b/backend/src/GraphQl/AppliedMethodType.cs new file mode 100644 index 00000000..7b623ade --- /dev/null +++ b/backend/src/GraphQl/AppliedMethodType.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class AppliedMethodType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(nameof(AppliedMethod.MethodId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetMethodAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetMethodAsync( + [Parent] AppliedMethod parent, + IMethodByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.MethodId); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index 90a22c50..14a711c6 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -15,5 +15,22 @@ IFilterInputTypeDescriptor descriptor descriptor.Name(nameof(CalorimetricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.GValues); descriptor.Field(x => x.UValues); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs index c0989874..20108396 100644 --- a/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs +++ b/backend/src/GraphQl/Common/OpenEndedDateTimeRangeType.cs @@ -1,6 +1,7 @@ using HotChocolate.Types; using NodaTime; using NpgsqlTypes; +using DateTimeType = HotChocolate.Types.NodaTime.DateTimeType; namespace Database.GraphQl.Common; @@ -12,10 +13,7 @@ IObjectTypeDescriptor> descriptor ) { descriptor.BindFieldsExplicitly(); - - var suffixedName = nameof(OpenEndedDateTimeRangeType); - descriptor.Name(suffixedName[..^"Type".Length]); - + descriptor.Name(nameof(OpenEndedDateTimeRangeType)[..^"Type".Length]); descriptor .Field("from") .Type() @@ -27,7 +25,6 @@ IObjectTypeDescriptor> descriptor : range.LowerBound; } ); - descriptor .Field("until") .Type() diff --git a/backend/src/GraphQl/CrossDatabaseDataReferenceType.cs b/backend/src/GraphQl/CrossDatabaseDataReferenceType.cs new file mode 100644 index 00000000..32c929b9 --- /dev/null +++ b/backend/src/GraphQl/CrossDatabaseDataReferenceType.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class CrossDatabaseDataReferenceType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(nameof(CrossDatabaseDataReference.DatabaseId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetDatabaseAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetDatabaseAsync( + [Parent] CrossDatabaseDataReference parent, + IDatabaseByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.DatabaseId); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs index 9d306bbf..ff19a04b 100644 --- a/backend/src/GraphQl/DataApprovals/DataApprovalType.cs +++ b/backend/src/GraphQl/DataApprovals/DataApprovalType.cs @@ -1,5 +1,9 @@ +using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data; +using Database.Extensions; using Database.GraphQl.References; +using HotChocolate; using HotChocolate.Types; namespace Database.GraphQl.DataApprovals; @@ -11,11 +15,27 @@ protected override void Configure(IObjectTypeDescriptor descriptor { descriptor .Field(t => t.Statement) - .Type() + .Type>() .Resolve(context => context .Parent() .Statement .TheReference ); + descriptor + .Field(nameof(DataApproval.ApproverId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetApproverAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetApproverAsync( + [Parent] DataApproval parent, + IInstitutionByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.ApproverId); + } } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataDataLoaders.cs b/backend/src/GraphQl/DataX/DataDataLoaders.cs deleted file mode 100644 index 26ea47c6..00000000 --- a/backend/src/GraphQl/DataX/DataDataLoaders.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using GreenDonut; -using GreenDonut.Data; -using Database.Data; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.DataX; - -public sealed class DataDataLoaders -: DataLoaders -{ - [DataLoader] - public static ValueTask> GetHttpsResourcesByDataIdAsync( - IReadOnlyList ids, - QueryContext queryContext, - IDbContextFactory databaseContextFactory, - CancellationToken cancellationToken - ) - { - return GetManyByOneIdAsync( - ids, - (databaseContext) => databaseContext.GetHttpsResources, - _ => _.DataId, - queryContext, - databaseContextFactory, - cancellationToken - ); - } - - [DataLoader] - public static ValueTask> GetHttpsResourceTreeNonRootVerticesByDataIdAsync( - IReadOnlyList ids, - QueryContext queryContext, - IDbContextFactory databaseContextFactory, - CancellationToken cancellationToken - ) - { - return GetManyByOneIdAsync( - ids, - (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId != null), - _ => _.DataId, - queryContext, - databaseContextFactory, - cancellationToken - ); - } - - [DataLoader] - public static ValueTask> GetHttpsResourceTreeRootByDataIdAsync( - IReadOnlyList ids, - QueryContext queryContext, - IDbContextFactory databaseContextFactory, - CancellationToken cancellationToken - ) - { - return GetManyByOneIdAsync( - ids, - (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId == null), - _ => _.DataId, - queryContext, - databaseContextFactory, - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index fcf30ee9..93cc8e2d 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -12,5 +12,22 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(DataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index 001f734f..7ef5a8ca 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -6,7 +6,7 @@ namespace Database.GraphQl.DataX; public abstract class DataFilterTypeBase : AuditableEntityFilterType - where TData : IData, IAuditable + where TData : IData { protected override void Configure( IFilterInputTypeDescriptor descriptor diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index a605d4fe..1e90eb81 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data; using Database.GraphQl.Extensions; using Database.GraphQl.GetHttpsResources; @@ -33,4 +34,29 @@ [Parent] IData data { return new GetHttpsResourceTree(data); } + + public Task GetDatabaseAsync( + [Parent] IData data, + IDatabaseByIdDataLoader byId, + AppSettings appSettings + ) + { + return byId.LoadAsync(appSettings.DatabaseId); + } + + public Task GetComponentAsync( + [Parent] IData data, + IComponentByIdDataLoader byId + ) + { + return byId.LoadAsync(data.ComponentId); + } + + public Task GetInstitutionAsync( + [Parent] IData data, + IInstitutionByIdDataLoader byId + ) + { + return byId.LoadAsync(data.CreatorId); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 101b46ba..344c48e6 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -1,4 +1,6 @@ +using Database.ApiRequests; using Database.Data; +using Database.Extensions; using Database.GraphQl.Scalars; using HotChocolate.Types; @@ -20,12 +22,17 @@ IInterfaceTypeDescriptor descriptor descriptor .Field(_ => _.PublishingState) .Ignore(); + descriptor + .Field(GraphQlConstants.IdFieldName) + .Type>(); descriptor .Field(GraphQlConstants.UuidFieldName) .Type>(); descriptor .Field(_ => _.UpdatedAt) .Name(TimestampFieldName); + descriptor + .Field(_ => _.CreatedAt); descriptor .Field(x => x.Locale) .Type>(); @@ -39,5 +46,14 @@ IInterfaceTypeDescriptor descriptor descriptor .Field(x => x.Approval) .Type>>(); + descriptor + .Field(DataType.DatabaseIdFieldName[..^2].FirstCharToLower()) + .Type>(); + descriptor + .Field(nameof(IData.ComponentId)[..^2].FirstCharToLower()) + .Type>(); + descriptor + .Field(nameof(IData.CreatorId)[..^2].FirstCharToLower()) + .Type>(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index bcfcda17..cad3965c 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -1,7 +1,9 @@ using System; +using Database.ApiRequests; using Database.Data; using Database.GraphQl.Entities; using Database.GraphQl.Scalars; +using Database.Extensions; using GreenDonut; using HotChocolate.Types; @@ -24,16 +26,34 @@ IObjectTypeDescriptor descriptor .Field(_ => _.UpdatedAt) .Name(DataType.TimestampFieldName); descriptor - .Field(x => x.Locale) + .Field(_ => _.Locale) .Type>(); descriptor - .Field(x => x.Resources) - .ResolveWith(t => t.GetHttpsResources(default!, default!, default!, default!)); + .Field(_ => _.Resources) + .Cost(1) + .ResolveWith(_ => _.GetHttpsResources(default!, default!, default!, default!)); descriptor .Field(DataType.ResourceTreeFieldName) - .ResolveWith(t => t.GetHttpsResourceTree(default!)); + .Cost(1) + .ResolveWith(_ => _.GetHttpsResourceTree(default!)); descriptor - .Field(x => x.Approval) + .Field(_ => _.Approval) + .Cost(3) .Type>>(); + descriptor + .Field(DataType.DatabaseIdFieldName[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => _.GetDatabaseAsync(default!, default!, default!)); + descriptor + .Field(nameof(IData.ComponentId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => _.GetComponentAsync(default!, default!)); + descriptor + .Field(nameof(IData.CreatorId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => _.GetInstitutionAsync(default!, default!)); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index 76c9c24a..bee24b31 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Database.Data; using Database.GraphQl.Extensions; +using Database.GraphQl.GetHttpsResources; using GreenDonut; using GreenDonut.Data; using HotChocolate.Data; @@ -39,8 +40,8 @@ await byId .With(resolverContext.GetQueryContext()) .LoadRequiredAsync(data.Id, cancellationToken) ) - .Select(v => - new GetHttpsResourceTreeNonRootVertex(v) + .Select(_ => + new GetHttpsResourceTreeNonRootVertex(_) ) .ToList() .AsReadOnly(); diff --git a/backend/src/GraphQl/Databases/DatabaseQueries.cs b/backend/src/GraphQl/Databases/DatabaseQueries.cs index 75142f1b..a769526f 100644 --- a/backend/src/GraphQl/Databases/DatabaseQueries.cs +++ b/backend/src/GraphQl/Databases/DatabaseQueries.cs @@ -10,20 +10,20 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Query))] public sealed class DatabaseQueries { - public async Task GetDatabaseAsync( + public async Task GetDatabaseAsync( AppSettings appSettings, - QueryDatabase queryDatabase, + IDatabaseByIdDataLoader byId, IResolverContext resolverContext, CancellationToken cancellationToken ) { var database = await GraphQlRequestHelper.TransformExceptionsAsync( - () => queryDatabase.Do( + () => byId.LoadAsync( appSettings.DatabaseId, cancellationToken ), resolverContext, - queryDatabase.GetGraphQlEndpoint + DatabaseDataLoader.GetGraphQlEndpoint(appSettings) ); if (database is null) { diff --git a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs new file mode 100644 index 00000000..663a72d6 --- /dev/null +++ b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs @@ -0,0 +1,42 @@ +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using HotChocolate.Resolvers; +using static Database.ApiRequests.VerifyDatabase; + +namespace Database.GraphQl.Databases; + +// TODO [ExtendObjectType(nameof(Mutation))] +public sealed class VerifyDatabaseMutation +{ + public async Task VerifyDatabaseAsync( + VerifyDatabaseInput input, + VerifyDatabase verifyDatabase, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + var databasePayload = await GraphQlRequestHelper.TransformExceptionsAsync( + () => verifyDatabase.Do( + input, + cancellationToken + ), + resolverContext, + verifyDatabase.GetGraphQlEndpoint + ); + if (databasePayload is null) + { + return new VerifyDatabasePayload( + null, + [ + new VerifyDatabaseError( + VerifyDatabaseErrorCode.UNKNOWN, + "Unknown error.", + [] + ) + ] + ); + } + return databasePayload; + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/FileMetaInformationType.cs b/backend/src/GraphQl/FileMetaInformationType.cs new file mode 100644 index 00000000..bbabc84e --- /dev/null +++ b/backend/src/GraphQl/FileMetaInformationType.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class FileMetaInformationType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(nameof(FileMetaInformation.DataFormatId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetDataFormatAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetDataFormatAsync( + [Parent] FileMetaInformation parent, + IDataFormatByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.DataFormatId); + } + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs index 8f168aab..a1d68e0d 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -14,5 +14,22 @@ IFilterInputTypeDescriptor descriptor base.Configure(descriptor); descriptor.Name(nameof(GeometricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(x => x.Thicknesses); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs index a04f9b0a..77155783 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceDataLoaders.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -46,4 +47,58 @@ CancellationToken cancellationToken cancellationToken ); } + + [DataLoader] + public static ValueTask> GetHttpsResourcesByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources, + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetHttpsResourceTreeNonRootVerticesByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId != null), + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetHttpsResourceTreeRootByDataIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.GetHttpsResources.Where(_ => _.ParentId == null), + _ => _.DataId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index aca20aa5..56b6a57a 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -1,6 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.GeometricDataX; @@ -100,4 +101,12 @@ CancellationToken cancellationToken { return byId.LoadAsync(getHttpsResource.Id, cancellationToken)!; } + + public Task GetDataFormatAsync( + [Parent] GetHttpsResource getHttpsResource, + IDataFormatByIdDataLoader byId + ) + { + return byId.LoadAsync(getHttpsResource.DataFormatId); + } } \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 01bc6cab..9b7fdf6a 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -1,5 +1,7 @@ +using Database.ApiRequests; using Database.Data; using Database.GraphQl.Entities; +using Database.Extensions; using HotChocolate.Types; namespace Database.GraphQl.GetHttpsResources; @@ -14,6 +16,7 @@ IObjectTypeDescriptor descriptor base.Configure(descriptor); descriptor .Field("locator") + .Cost(0) .ResolveWith(t => t.GetLocator(default!, default!)); descriptor .Field(_ => _.FileName) @@ -29,9 +32,11 @@ IObjectTypeDescriptor descriptor .Ignore(); descriptor .Field(x => x.Parent) + .Cost(0) .ResolveWith(t => t.GetParent(default!, default!, default!)); descriptor .Field(x => x.Children) + .Cost(0) .ResolveWith(t => t.GetChildren(default!, default!, default!)); descriptor .Field(x => x.DataId) @@ -74,7 +79,13 @@ IObjectTypeDescriptor descriptor .Ignore(); descriptor .Field(x => x.Data) + .Cost(0) .ResolveWith(t => t.GetData(default!, default!, default!, default!, default!, default!, default!, default!)); + descriptor + .Field(nameof(GetHttpsResource.DataFormatId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => _.GetDataFormatAsync(default!, default!)); } } \ No newline at end of file diff --git a/backend/src/GraphQl/GraphQlConstants.cs b/backend/src/GraphQl/GraphQlConstants.cs index b8ce2ece..30a0470a 100644 --- a/backend/src/GraphQl/GraphQlConstants.cs +++ b/backend/src/GraphQl/GraphQlConstants.cs @@ -9,6 +9,7 @@ internal static class GraphQlConstants internal const string FilterInputSuffix = "PropositionInput"; internal const string SortInputSuffix = "SortInput"; internal const string PendingPrefix = "pending"; + internal const string IdFieldName = "id"; internal const string UuidFieldName = "uuid"; internal const string VersionFieldName = "version"; } \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index 016404ee..fcadf11e 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -13,5 +13,22 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(HygrothermalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs index a61b41eb..34d92e62 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs @@ -13,5 +13,22 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(LifeCycleDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 764fc95a..07deb0f0 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -96,7 +96,7 @@ public async Task CalculateMethodAsync( AppSettings appSettings, MethodFactory methodFactory, ApiRequestService apiRequestService, - QueryDatabase queryDatabase, + IDatabaseByIdDataLoader databaseById, QueryData queryData, IResolverContext resolverContext, CancellationToken cancellationToken @@ -115,12 +115,12 @@ [new CalculateMethodError( ); } var database = await GraphQlRequestHelper.TransformExceptionsAsync( - () => queryDatabase.Do( + () => databaseById.LoadAsync( dataReference.DatabaseId, cancellationToken ), resolverContext, - queryDatabase.GetGraphQlEndpoint + DatabaseDataLoader.GetGraphQlEndpoint(appSettings) ); if (database is null) { diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 580ec31e..94e9e859 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -23,5 +23,22 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(x => x.InfraredEmittances); descriptor.Field(x => x.ColorRenderingIndices); descriptor.Field(x => x.CielabColors); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index abd8c2d9..fb479dc8 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -13,5 +13,22 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(PhotovoltaicDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + + // TODO Why are the fields below not included by `base.Configure` above? + // AuditableEntityFilterType.Configure + descriptor.Field(x => x.Id); + descriptor.Field(x => x.CreatedAt); + descriptor.Field(x => x.UpdatedAt); + // DataFilterTypeBase.Configure + descriptor.Field(x => x.UserId); + descriptor.Field(x => x.Locale); + descriptor.Field(x => x.Name); + descriptor.Field(x => x.Description); + descriptor.Field(x => x.ComponentId); + descriptor.Field(x => x.CreatorId); + descriptor.Field(x => x.AppliedMethod); + descriptor.Field(x => x.Approvals); + descriptor.Field(x => x.Resources); + descriptor.Field(x => x.Warnings); } } \ No newline at end of file diff --git a/backend/src/GraphQl/References/ReferenceType.cs b/backend/src/GraphQl/References/ReferenceType.cs index abad0cdf..047dd47e 100644 --- a/backend/src/GraphQl/References/ReferenceType.cs +++ b/backend/src/GraphQl/References/ReferenceType.cs @@ -1,7 +1,4 @@ -using System; using Database.Data; -using Database.GraphQl.Publications; -using Database.GraphQl.Standards; using HotChocolate.Types; namespace Database.GraphQl.References; diff --git a/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodType.cs b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodType.cs new file mode 100644 index 00000000..bd0857ab --- /dev/null +++ b/backend/src/GraphQl/ToTreeVertexAppliedConversionMethodType.cs @@ -0,0 +1,35 @@ +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Data; +using Database.Extensions; +using HotChocolate; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class ToTreeVertexAppliedConversionMethodType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(nameof(ToTreeVertexAppliedConversionMethod.MethodId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetMethodAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetMethodAsync( + [Parent] ToTreeVertexAppliedConversionMethod parent, + IMethodByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.MethodId); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.Designer.cs b/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.Designer.cs new file mode 100644 index 00000000..690595ea --- /dev/null +++ b/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.Designer.cs @@ -0,0 +1,3226 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260527161543_MakeEntitiesAuditable")] + partial class MakeEntitiesAuditable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("AllowedDatasetsPerTime") + .HasColumnType("bigint"); + + b.Property("AllowedUserCount") + .HasColumnType("bigint"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("Period") + .HasColumnType("interval"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.PrimitiveCollection>("UserAlreadyAccessed") + .IsRequired() + .HasColumnType("uuid[]"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("InstitutionId") + .IsUnique(); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("institution_access_rights", "database"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("AllowedApplications") + .HasColumnType("text[]"); + + b1.PrimitiveCollection("AllowedInstitutions") + .HasColumnType("uuid[]"); + + b1.Property("AllowedUserAndQuantity") + .HasColumnType("jsonb"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("DataAccessRights") + .IsRequired(); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.cs b/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.cs new file mode 100644 index 00000000..69c81dca --- /dev/null +++ b/backend/src/Migrations/20260527161543_MakeEntitiesAuditable.cs @@ -0,0 +1,407 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MakeEntitiesAuditable : Migration + { + /// + [SuppressMessage("Performance", "CA1861")] + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "user", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "user", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "lifeCycle_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "lifeCycle_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "institution_access_rights", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "institution_access_rights", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "get_https_resource", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "get_https_resource", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "geometric_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "geometric_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(OffsetDateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.CreateIndex( + name: "IX_user_CreatedAt_Id", + schema: "database", + table: "user", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_photovoltaic_data_CreatedAt_Id", + schema: "database", + table: "photovoltaic_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_optical_data_CreatedAt_Id", + schema: "database", + table: "optical_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_lifeCycle_data_CreatedAt_Id", + schema: "database", + table: "lifeCycle_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_institution_access_rights_CreatedAt_Id", + schema: "database", + table: "institution_access_rights", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_hygrothermal_data_CreatedAt_Id", + schema: "database", + table: "hygrothermal_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_get_https_resource_CreatedAt_Id", + schema: "database", + table: "get_https_resource", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_geometric_data_CreatedAt_Id", + schema: "database", + table: "geometric_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_calorimetric_data_CreatedAt_Id", + schema: "database", + table: "calorimetric_data", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + } + + /// + [SuppressMessage("Performance", "CA1861")] + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_user_CreatedAt_Id", + schema: "database", + table: "user"); + + migrationBuilder.DropIndex( + name: "IX_photovoltaic_data_CreatedAt_Id", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropIndex( + name: "IX_optical_data_CreatedAt_Id", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropIndex( + name: "IX_lifeCycle_data_CreatedAt_Id", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropIndex( + name: "IX_institution_access_rights_CreatedAt_Id", + schema: "database", + table: "institution_access_rights"); + + migrationBuilder.DropIndex( + name: "IX_hygrothermal_data_CreatedAt_Id", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropIndex( + name: "IX_get_https_resource_CreatedAt_Id", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropIndex( + name: "IX_geometric_data_CreatedAt_Id", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropIndex( + name: "IX_calorimetric_data_CreatedAt_Id", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "user"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "user"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "institution_access_rights"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "institution_access_rights"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "get_https_resource"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "photovoltaic_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "optical_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "lifeCycle_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "hygrothermal_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "geometric_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + schema: "database", + table: "calorimetric_data", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index ef9d6da4..1514594a 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -22,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); @@ -46,8 +46,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -73,6 +75,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -88,6 +95,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("calorimetric_data", "database"); }); @@ -101,8 +111,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -128,6 +140,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -147,6 +164,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("geometric_data", "database"); }); @@ -160,6 +180,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CalorimetricDataId") .HasColumnType("uuid"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("DataFormatId") .HasColumnType("uuid"); @@ -191,6 +216,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PhotovoltaicDataId") .HasColumnType("uuid"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -225,6 +255,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsUnique() .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("get_https_resource", "database", t => { t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); @@ -255,8 +288,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -274,6 +309,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PublishingState") .HasColumnType("database.publishing_state"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -289,6 +329,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("hygrothermal_data", "database"); }); @@ -305,12 +348,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("AllowedUserCount") .HasColumnType("bigint"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("InstitutionId") .HasColumnType("uuid"); b.Property("Period") .HasColumnType("interval"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.PrimitiveCollection>("UserAlreadyAccessed") .IsRequired() .HasColumnType("uuid[]"); @@ -326,6 +379,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("InstitutionId") .IsUnique(); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("institution_access_rights", "database"); }); @@ -339,8 +395,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -358,6 +416,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PublishingState") .HasColumnType("database.publishing_state"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -373,6 +436,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("lifeCycle_data", "database"); }); @@ -393,8 +459,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -438,6 +506,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Type") .HasColumnType("database.optical_component_type"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -453,6 +526,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("optical_data", "database"); }); @@ -466,8 +542,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("ComponentId") .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CreatorId") .HasColumnType("uuid"); @@ -485,6 +563,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("PublishingState") .HasColumnType("database.publishing_state"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("UserId") .HasColumnType("uuid"); @@ -500,6 +583,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("photovoltaic_data", "database"); }); @@ -510,6 +596,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("gen_random_uuid()"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("Name") .IsRequired() .HasColumnType("text"); @@ -518,6 +609,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text"); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAddOrUpdate() @@ -526,6 +622,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Id"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + b.ToTable("user", "database"); }); @@ -1088,7 +1187,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("CalorimetricDataId"); @@ -1442,7 +1541,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("GeometricDataId"); @@ -1925,7 +2024,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("HygrothermalDataId"); @@ -2279,7 +2378,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("LifeCycleDataId"); @@ -2670,7 +2769,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("OpticalDataId"); @@ -3026,7 +3125,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b1.PrimitiveCollection("AllowedInstitutions") .HasColumnType("uuid[]"); - b1.Property>("AllowedUserAndQuantity") + b1.Property("AllowedUserAndQuantity") .HasColumnType("jsonb"); b1.HasKey("PhotovoltaicDataId"); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index afc66795..dc11d21f 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -2047,3 +2047,203 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_user_CreatedAt_Id" ON database."user" ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_photovoltaic_data_CreatedAt_Id" ON database.photovoltaic_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_optical_data_CreatedAt_Id" ON database.optical_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_lifeCycle_data_CreatedAt_Id" ON database."lifeCycle_data" ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_institution_access_rights_CreatedAt_Id" ON database.institution_access_rights ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_hygrothermal_data_CreatedAt_Id" ON database.hygrothermal_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_CreatedAt_Id" ON database.get_https_resource ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_geometric_data_CreatedAt_Id" ON database.geometric_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_calorimetric_data_CreatedAt_Id" ON database.calorimetric_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260527161543_MakeEntitiesAuditable', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260316165622_AddLifeCycleData_to_20260527161543_MakeEntitiesAuditable.sql b/backend/src/Migrations/migrate_from_20260316165622_AddLifeCycleData_to_20260527161543_MakeEntitiesAuditable.sql new file mode 100644 index 00000000..0145846a --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260316165622_AddLifeCycleData_to_20260527161543_MakeEntitiesAuditable.sql @@ -0,0 +1,200 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" SET DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_user_CreatedAt_Id" ON database."user" ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_photovoltaic_data_CreatedAt_Id" ON database.photovoltaic_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_optical_data_CreatedAt_Id" ON database.optical_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_lifeCycle_data_CreatedAt_Id" ON database."lifeCycle_data" ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_institution_access_rights_CreatedAt_Id" ON database.institution_access_rights ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_hygrothermal_data_CreatedAt_Id" ON database.hygrothermal_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_get_https_resource_CreatedAt_Id" ON database.get_https_resource ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_geometric_data_CreatedAt_Id" ON database.geometric_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + CREATE UNIQUE INDEX "IX_calorimetric_data_CreatedAt_Id" ON database.calorimetric_data ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260527161543_MakeEntitiesAuditable', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260527161543_MakeEntitiesAuditable_to_20260316165622_AddLifeCycleData.sql b/backend/src/Migrations/rollback_from_20260527161543_MakeEntitiesAuditable_to_20260316165622_AddLifeCycleData.sql new file mode 100644 index 00000000..8fbaad92 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260527161543_MakeEntitiesAuditable_to_20260316165622_AddLifeCycleData.sql @@ -0,0 +1,172 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_user_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_photovoltaic_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_optical_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_lifeCycle_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_institution_access_rights_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_hygrothermal_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_get_https_resource_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_geometric_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DROP INDEX database."IX_calorimetric_data_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."user" DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.institution_access_rights DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.get_https_resource DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.optical_data ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "CreatedAt" DROP DEFAULT; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260527161543_MakeEntitiesAuditable'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index ab086e01..37859ca7 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -218,7 +218,7 @@ DbContextOptionsBuilder options connectionStringBuilder.ConnectionString, _ => _ // Keep version in sync with the one in ./docker-compose.*.yaml - .SetPostgresVersion(13, 23) + .SetPostgresVersion(18, 4) .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) // https://learn.microsoft.com/en-us/ef/core/querying/single-split-queries#enabling-split-queries-globally .UseNodaTime() // https://www.npgsql.org/efcore/mapping/enum.html @@ -301,7 +301,6 @@ public static void ConfigureApiRequests(IServiceCollection services) services.AddScoped(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); services.AddScoped(); } diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index e052358d..f8bf7d19 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -14,7 +14,7 @@ - + diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 37a3c68f..e4b69ac0 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -3,52 +3,399 @@ schema { mutation: Mutation } -interface Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - timestamp: DateTime! - variables: Any! -} - -interface Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! - locale: Locale! - name: String - resources: [GetHttpsResource!]! - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! - warnings: [String!]! -} - -interface GetHttpsResourceTreeVertex { - value: GetHttpsResource! - vertexId: ID -} - -"The node interface is implemented by entities that have a global unique identifier." -interface Node { - id: ID! +type Query { + allCalorimetricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [CalorimetricDataSortInput!] @cost(weight: "10") + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): CalorimetricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allGeometricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [GeometricDataSortInput!] @cost(weight: "10") + where: GeometricDataPropositionInput @cost(weight: "10") + ): GeometricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allHygrothermalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [HygrothermalDataSortInput!] @cost(weight: "10") + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): HygrothermalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allLifeCycleData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [LifeCycleDataSortInput!] @cost(weight: "10") + where: LifeCycleDataPropositionInput @cost(weight: "10") + ): LifeCycleDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allOpticalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [OpticalDataSortInput!] @cost(weight: "10") + where: OpticalDataPropositionInput @cost(weight: "10") + ): OpticalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingCalorimetricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [CalorimetricDataSortInput!] @cost(weight: "10") + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): CalorimetricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingGeometricData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [GeometricDataSortInput!] @cost(weight: "10") + where: GeometricDataPropositionInput @cost(weight: "10") + ): GeometricDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingHygrothermalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [HygrothermalDataSortInput!] @cost(weight: "10") + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): HygrothermalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingLifeCycleData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [LifeCycleDataSortInput!] @cost(weight: "10") + where: LifeCycleDataPropositionInput @cost(weight: "10") + ): LifeCycleDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingOpticalData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [OpticalDataSortInput!] @cost(weight: "10") + where: OpticalDataPropositionInput @cost(weight: "10") + ): OpticalDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPendingPhotovoltaicData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [PhotovoltaicDataSortInput!] @cost(weight: "10") + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): PhotovoltaicDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + allPhotovoltaicData( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + locale: Locale + order: [PhotovoltaicDataSortInput!] @cost(weight: "10") + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): PhotovoltaicDataConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + calculateMethod( + dataReference: CrossDatabaseDataReferenceInput! + methodId: Uuid! + ): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload!, methodId: Uuid!): CalculateMethodPayload! + @cost(weight: "10") + calorimetricData(id: Uuid!, locale: Locale): CalorimetricData + @cost(weight: "10") + currentInstitution: CurrentInstitution @cost(weight: "10") + currentUser: User @cost(weight: "10") + currentUserInfo: UserInfo! @cost(weight: "10") + data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") + database: Database! @cost(weight: "10") + geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") + getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") + getHttpsResources( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): GetHttpsResourceConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + hasCalorimetricData( + locale: Locale + where: CalorimetricDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasGeometricData( + locale: Locale + where: GeometricDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasHygrothermalData( + locale: Locale + where: HygrothermalDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasLifeCycleData( + locale: Locale + where: LifeCycleDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasOpticalData( + locale: Locale + where: OpticalDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hasPhotovoltaicData( + locale: Locale + where: PhotovoltaicDataPropositionInput @cost(weight: "10") + ): Boolean! @cost(weight: "10") + hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData + @cost(weight: "10") + lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") + "Fetches an object given its ID." + node("ID of the object." id: ID!): Node @cost(weight: "10") + "Lookup nodes by a list of IDs." + nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") + pendingGetHttpsResources( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [GetHttpsResourceSortInput!] @cost(weight: "10") + where: GetHttpsResourcePropositionInput @cost(weight: "10") + ): GetHttpsResourceConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData + @cost(weight: "10") + verificationCode: String! } -interface UserError { - message: String! - path: [String!]! +type Mutation { + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! + @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! + @cost(weight: "10") + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! + @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! + @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! + @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! + @cost(weight: "10") + createLifeCycleData(input: CreateLifeCycleDataInput!): CreateLifeCycleDataPayload! + @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! + @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! + @cost(weight: "10") + createResponseApprovals( + where: CreateResponseApprovalsPropositionInput @cost(weight: "10") + ): CreateResponseApprovalsPayload! @cost(weight: "10") + deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! + @cost(weight: "10") + publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues( + where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") + ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! + @cost(weight: "10") + retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! + @cost(weight: "10") + updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! + @cost(weight: "10") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! + @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! + @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! + @cost(weight: "10") + updateResponseApprovals( + where: UpdateResponseApprovalsPropositionInput @cost(weight: "10") + ): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! + @cost(weight: "10") } type AddDataApprovalError implements UserError { @@ -81,6 +428,7 @@ type Address { type AppliedMethod { arguments: [NamedMethodArgument!]! + method: Method @cost(weight: "3") methodId: Uuid! sources: [NamedMethodSource!]! } @@ -98,26 +446,32 @@ type CalculateMethodPayload { type CalorimetricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String gValues: [Float!]! id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! uValues: [Float!]! warnings: [String!]! } @@ -148,6 +502,11 @@ type CielabColor { observer: CalorimetricObserver } +type Component { + name: String! + uuid: Uuid! +} + type CreateCalorimetricDataError implements UserError { code: CreateCalorimetricDataErrorCode! message: String! @@ -245,16 +604,25 @@ type CreateResponseApprovalsPayload { } type CrossDatabaseDataReference { + database: Database @cost(weight: "3") databaseId: Uuid! dataId: Uuid! dataKind: DataKind! dataTimestamp: DateTime! } +type CurrentInstitution { + databaseOperatingDatabases: DatabaseOperatingDatabaseConnection! + databaseOperatingManagedInstitutions: DatabaseOperatingManagedInstitutionConnection! + isDatabaseOperator: Boolean! + name: String! + uuid: Uuid! +} + type DataAccessRights { allowedApplications: [String!] allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32!] hasRestrictions: Boolean! hasRestrictionsByApplication: Boolean! hasRestrictionsByInstitution: Boolean! @@ -262,27 +630,17 @@ type DataAccessRights { } type DataApproval implements Approval { + approver: Institution @cost(weight: "3") approverId: Uuid! keyFingerprint: String! message: String! query: String! signature: String! - statement: Reference @cost(weight: "10") + statement: Reference! @cost(weight: "10") timestamp: DateTime! variables: Any! } -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - type Database { description: String! isAuthorizedToUpdateNode: Boolean! @@ -295,10 +653,37 @@ type Database { verificationState: DatabaseVerificationState! } +type DatabaseOperatingDatabaseConnection { + totalCount: NonNegativeInt! +} + +type DatabaseOperatingManagedInstitutionConnection { + totalCount: NonNegativeInt! +} + type DatabaseOperatorEdge { node: Institution! } +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + +type DataFormat { + extension: String + mediaType: String! + name: String! + schemaLocator: Url + uuid: Uuid! +} + type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! @@ -323,33 +708,40 @@ type DeleteGetHttpsResourcePayload { } type FileMetaInformation { + dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! path: [String!]! } type GeometricData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String heights: [Float!]! id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! thicknesses: [Float!]! timestamp: DateTime! userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! warnings: [String!]! widths: [Float!]! } @@ -375,8 +767,10 @@ type GeometricDataEdge { type GetHttpsResource implements Node { appliedConversionMethod: ToTreeVertexAppliedConversionMethod archivedFilesMetaInformation: [FileMetaInformation!]! - children: [GetHttpsResource!]! @cost(weight: "10") - data: Data @cost(weight: "10") + children: [GetHttpsResource!]! @cost(weight: "0") + createdAt: DateTime! + data: Data @cost(weight: "0") + dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! description: String doesFileExist: Boolean! @@ -385,8 +779,9 @@ type GetHttpsResource implements Node { isChild: Boolean! isRoot: Boolean! locator: Url! - parent: GetHttpsResource @cost(weight: "10") - uuid: Uuid! @cost(weight: "10") + parent: GetHttpsResource @cost(weight: "0") + updatedAt: DateTime! + uuid: Uuid! } "A connection to a list of items." @@ -408,7 +803,10 @@ type GetHttpsResourceEdge { } type GetHttpsResourceTree { - nonRootVertices(order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10")): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + nonRootVertices( + order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") + where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + ): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") root: GetHttpsResourceTreeRoot! @cost(weight: "10") } @@ -426,25 +824,31 @@ type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { type HygrothermalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! warnings: [String!]! } @@ -467,48 +871,57 @@ type HygrothermalDataEdge { } type Institution { + name: String! uuid: Uuid! } type InstitutionAccessRights { allowedDatasetsPerTime: NonNegativeInt allowedUserCount: NonNegativeInt + createdAt: DateTime! hasRestrictions: Boolean! hasRestrictionsByTime: Boolean! hasRestrictionsByUser: Boolean! id: Uuid! institutionId: Uuid! period: Duration! + updatedAt: DateTime! userAlreadyAccessed: [Uuid!]! version: NonNegativeInt! } -type KeyValuePairOfGuidAndNullableOfUInt32 { +type KeyValuePairOfGuidAndNullableUInt32 { key: Uuid! value: NonNegativeInt } type LifeCycleData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! warnings: [String!]! } @@ -530,31 +943,9 @@ type LifeCycleDataEdge { node: LifeCycleData! } -type Mutation { - addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! @cost(weight: "10") - createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") - createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @cost(weight: "10") - createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! @cost(weight: "10") - createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! @cost(weight: "10") - createLifeCycleData(input: CreateLifeCycleDataInput!): CreateLifeCycleDataPayload! @cost(weight: "10") - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! @cost(weight: "10") - createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals(where: CreateResponseApprovalsPropositionInput @cost(weight: "10")): CreateResponseApprovalsPayload! @cost(weight: "10") - deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") - publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues(where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10")): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") - retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") - setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") - updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") - updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! @cost(weight: "10") - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") - updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals(where: UpdateResponseApprovalsPropositionInput @cost(weight: "10")): UpdateResponseApprovalsPayload! @cost(weight: "10") - updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") +type Method { + name: String! + uuid: Uuid! } type NamedMethodArgument { @@ -580,35 +971,41 @@ type OpenEndedDateTimeRange { type OpticalData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! cielabColors: [CielabColor!]! coatedSide: CoatedSide colorRenderingIndices: [Float!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! infraredEmittances: [Float!]! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! subtype: OpticalComponentSubtype timestamp: DateTime! type: OpticalComponentType userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! warnings: [String!]! } @@ -644,25 +1041,31 @@ type PageInfo { type PhotovoltaicData implements Node & Data { appliedMethod: AppliedMethod! - approval: ResponseApproval! + approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! + component: Component @cost(weight: "3") componentId: Uuid! createdAt: DateTime! + creator: Institution @cost(weight: "3") creatorId: Uuid! dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! isRestrictedByApplication(applicationId: String!): Boolean! isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt! uuid: Uuid!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String - resources(order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): [GetHttpsResource!]! @cost(weight: "10") + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! userId: Uuid - uuid: Uuid! @cost(weight: "10") + uuid: Uuid! warnings: [String!]! } @@ -712,47 +1115,6 @@ type PublishDataPayload { query: Query! } -type Query { - allCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingCalorimetricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [CalorimetricDataSortInput!] @cost(weight: "10") where: CalorimetricDataPropositionInput @cost(weight: "10")): CalorimetricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingGeometricData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [GeometricDataSortInput!] @cost(weight: "10") where: GeometricDataPropositionInput @cost(weight: "10")): GeometricDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingHygrothermalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [HygrothermalDataSortInput!] @cost(weight: "10") where: HygrothermalDataPropositionInput @cost(weight: "10")): HygrothermalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingLifeCycleData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [LifeCycleDataSortInput!] @cost(weight: "10") where: LifeCycleDataPropositionInput @cost(weight: "10")): LifeCycleDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingOpticalData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [OpticalDataSortInput!] @cost(weight: "10") where: OpticalDataPropositionInput @cost(weight: "10")): OpticalDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPendingPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - allPhotovoltaicData("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int locale: Locale order: [PhotovoltaicDataSortInput!] @cost(weight: "10") where: PhotovoltaicDataPropositionInput @cost(weight: "10")): PhotovoltaicDataConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - calculateMethod(dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calculateMethodWithDataUpload(data: Upload! methodId: Uuid!): CalculateMethodPayload! @cost(weight: "10") - calorimetricData(id: Uuid! locale: Locale): CalorimetricData @cost(weight: "10") - currentUser: User @cost(weight: "10") - currentUserInfo: UserInfo! @cost(weight: "10") - data(dataKind: DataKind! id: Uuid! locale: Locale): Data @cost(weight: "10") - database: Database! @cost(weight: "10") - geometricData(id: Uuid! locale: Locale): GeometricData @cost(weight: "10") - getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") - getHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - hasCalorimetricData(locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasGeometricData(locale: Locale where: GeometricDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasHygrothermalData(locale: Locale where: HygrothermalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasLifeCycleData(locale: Locale where: LifeCycleDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasOpticalData(locale: Locale where: OpticalDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hasPhotovoltaicData(locale: Locale where: PhotovoltaicDataPropositionInput @cost(weight: "10")): Boolean! @cost(weight: "10") - hygrothermalData(id: Uuid! locale: Locale): HygrothermalData @cost(weight: "10") - lifeCycleData(id: Uuid! locale: Locale): LifeCycleData @cost(weight: "10") - "Fetches an object given its ID." - node("ID of the object." id: ID!): Node @cost(weight: "10") - "Lookup nodes by a list of IDs." - nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - opticalData(id: Uuid! locale: Locale): OpticalData @cost(weight: "10") - pendingGetHttpsResources("Returns the elements in the list that come after the specified cursor." after: String "Returns the elements in the list that come before the specified cursor." before: String "Returns the first _n_ elements from the list." first: Int "Returns the last _n_ elements from the list." last: Int order: [GetHttpsResourceSortInput!] @cost(weight: "10") where: GetHttpsResourcePropositionInput @cost(weight: "10")): GetHttpsResourceConnection @listSize(assumedSize: 100, slicingArguments: [ "first", "last" ], slicingArgumentDefaultValue: 100, sizedFields: [ "edges", "nodes" ], requireOneSlicingArgument: false) @cost(weight: "10") - photovoltaicData(id: Uuid! locale: Locale): PhotovoltaicData @cost(weight: "10") - verificationCode: String! -} - type RecomputeGetHttpsResourceHashValuesError implements UserError { code: RecomputeGetHttpsResourceHashValuesErrorCode! message: String! @@ -826,6 +1188,7 @@ type Standard { type ToTreeVertexAppliedConversionMethod { arguments: [NamedMethodArgument!]! + method: Method @cost(weight: "3") methodId: Uuid! sourceName: String! } @@ -854,27 +1217,27 @@ type UpdateDataAccessRightsPayload { query: Query! } -type UpdateDataError implements UserError { - code: UpdateDataErrorCode! +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! message: String! path: [String!]! } -type UpdateDataPayload { - data: Data - errors: [UpdateDataError!] +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] query: Query! } -type UpdateDatabaseError { - code: UpdateDatabaseErrorCode! +type UpdateDataError implements UserError { + code: UpdateDataErrorCode! message: String! path: [String!]! } -type UpdateDatabasePayload { - database: Database - errors: [UpdateDatabaseError!] +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] query: Query! } @@ -915,10 +1278,12 @@ type UpdateRootGetHttpsResourcePayload { } type User implements Node { + createdAt: DateTime! id: ID! name: String! subject: String! - uuid: Uuid! @cost(weight: "10") + updatedAt: DateTime! + uuid: Uuid! } type UserInfo { @@ -933,6 +1298,58 @@ type UserInfo { website: String } +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + +interface Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + component: Component + componentId: Uuid! + createdAt: DateTime! + creator: Institution + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +interface GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +interface UserError { + message: String! + path: [String!]! +} + union Reference = Publication | Standard input AddDataApprovalInput { @@ -963,9 +1380,9 @@ input AppliedMethodInput { input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] - or: [AppliedMethodPropositionInput!] - methodId: UuidPropositionInput arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [AppliedMethodPropositionInput!] sources: FilterInputTypeOfNamedMethodSourcesPropositionInput } @@ -980,57 +1397,49 @@ input BooleanPropositionInput { input BytePropositionInput { equalTo: Byte @cost(weight: "10") - notEqualTo: Byte @cost(weight: "10") - in: [Byte] @cost(weight: "10") - notIn: [Byte] @cost(weight: "10") greaterThan: Byte @cost(weight: "10") - notGreaterThan: Byte @cost(weight: "10") greaterThanOrEqualTo: Byte @cost(weight: "10") - notGreaterThanOrEqualTo: Byte @cost(weight: "10") + in: [Byte] @cost(weight: "10") lessThan: Byte @cost(weight: "10") - notLessThan: Byte @cost(weight: "10") lessThanOrEqualTo: Byte @cost(weight: "10") + notEqualTo: Byte @cost(weight: "10") + notGreaterThan: Byte @cost(weight: "10") + notGreaterThanOrEqualTo: Byte @cost(weight: "10") + notIn: [Byte] @cost(weight: "10") + notLessThan: Byte @cost(weight: "10") notLessThanOrEqualTo: Byte @cost(weight: "10") } -input CalendarSystemPropositionInput { - and: [CalendarSystemPropositionInput!] - or: [CalendarSystemPropositionInput!] - id: StringPropositionInput - name: StringPropositionInput - minYear: IntPropositionInput - maxYear: IntPropositionInput - eras: FilterInputTypeOfErasPropositionInput -} - input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] - or: [CalorimetricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + gValues: FloatsPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [CalorimetricDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput - gValues: FloatsPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput uValues: FloatsPropositionInput + warnings: StringsPropositionInput } input CalorimetricDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input CielabColorInput { @@ -1043,12 +1452,12 @@ input CielabColorInput { input CielabColorPropositionInput { and: [CielabColorPropositionInput!] - or: [CielabColorPropositionInput!] - lStar: FloatPropositionInput aStar: FloatPropositionInput bStar: FloatPropositionInput - observer: NullableOfCalorimetricObserverPropositionInput illuminant: NullableOfIlluminantPropositionInput + lStar: FloatPropositionInput + observer: NullableOfCalorimetricObserverPropositionInput + or: [CielabColorPropositionInput!] } input ClosedIntervalInput { @@ -1155,18 +1564,16 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsPropositionInput { and: [CreateResponseApprovalsPropositionInput!] - or: [CreateResponseApprovalsPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [CreateResponseApprovalsPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -1179,88 +1586,90 @@ input CrossDatabaseDataReferenceInput { input CrossDatabaseDataReferencePropositionInput { and: [CrossDatabaseDataReferencePropositionInput!] - or: [CrossDatabaseDataReferencePropositionInput!] + databaseId: UuidPropositionInput dataId: UuidPropositionInput - dataTimestamp: OffsetDateTimePropositionInput dataKind: DataKindPropositionInput - databaseId: UuidPropositionInput + dataTimestamp: DateTimePropositionInput + or: [CrossDatabaseDataReferencePropositionInput!] } input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] - or: [DataApprovalPropositionInput!] approverId: UuidPropositionInput - timestamp: OffsetDateTimePropositionInput - signature: StringPropositionInput keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput message: StringPropositionInput + or: [DataApprovalPropositionInput!] + query: StringPropositionInput + signature: StringPropositionInput statement: ReferencePropositionInput + timestamp: DateTimePropositionInput + variables: JsonElementPropositionInput } input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") - notEqualTo: DataKind @cost(weight: "10") in: [DataKind!] @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") notIn: [DataKind!] @cost(weight: "10") } input DataPropositionInput { and: [DataPropositionInput!] - or: [DataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [DataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input DataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") - notEqualTo: DateTime @cost(weight: "10") - in: [DateTime] @cost(weight: "10") - notIn: [DateTime] @cost(weight: "10") greaterThan: DateTime @cost(weight: "10") - notGreaterThan: DateTime @cost(weight: "10") greaterThanOrEqualTo: DateTime @cost(weight: "10") - notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") lessThan: DateTime @cost(weight: "10") - notLessThan: DateTime @cost(weight: "10") lessThanOrEqualTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + notIn: [DateTime] @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") notLessThanOrEqualTo: DateTime @cost(weight: "10") } input DecimalPropositionInput { equalTo: Decimal @cost(weight: "10") - notEqualTo: Decimal @cost(weight: "10") - in: [Decimal] @cost(weight: "10") - notIn: [Decimal] @cost(weight: "10") greaterThan: Decimal @cost(weight: "10") - notGreaterThan: Decimal @cost(weight: "10") greaterThanOrEqualTo: Decimal @cost(weight: "10") - notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + in: [Decimal] @cost(weight: "10") lessThan: Decimal @cost(weight: "10") - notLessThan: Decimal @cost(weight: "10") lessThanOrEqualTo: Decimal @cost(weight: "10") + notEqualTo: Decimal @cost(weight: "10") + notGreaterThan: Decimal @cost(weight: "10") + notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + notIn: [Decimal] @cost(weight: "10") + notLessThan: Decimal @cost(weight: "10") notLessThanOrEqualTo: Decimal @cost(weight: "10") } @@ -1273,10 +1682,19 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input EraPropositionInput { - and: [EraPropositionInput!] - or: [EraPropositionInput!] - name: StringPropositionInput +input DurationPropositionInput { + equalTo: Duration @cost(weight: "10") + greaterThan: Duration @cost(weight: "10") + greaterThanOrEqualTo: Duration @cost(weight: "10") + in: [Duration] @cost(weight: "10") + lessThan: Duration @cost(weight: "10") + lessThanOrEqualTo: Duration @cost(weight: "10") + notEqualTo: Duration @cost(weight: "10") + notGreaterThan: Duration @cost(weight: "10") + notGreaterThanOrEqualTo: Duration @cost(weight: "10") + notIn: [Duration] @cost(weight: "10") + notLessThan: Duration @cost(weight: "10") + notLessThanOrEqualTo: Duration @cost(weight: "10") } input FileMetaInformationInput { @@ -1286,210 +1704,202 @@ input FileMetaInformationInput { input FileMetaInformationPropositionInput { and: [FileMetaInformationPropositionInput!] + dataFormatId: UuidPropositionInput or: [FileMetaInformationPropositionInput!] path: StringsPropositionInput - dataFormatId: UuidPropositionInput } input FilterInputTypeOfCielabColorsPropositionInput { all: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: CielabColorPropositionInput @cost(weight: "10") some: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfDataApprovalsPropositionInput { all: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: DataApprovalPropositionInput @cost(weight: "10") some: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input FilterInputTypeOfErasPropositionInput { - all: EraPropositionInput @cost(weight: "10") - none: EraPropositionInput @cost(weight: "10") - some: EraPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfFileMetaInformationsPropositionInput { all: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: FileMetaInformationPropositionInput @cost(weight: "10") some: FileMetaInformationPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfGetHttpsResourcesPropositionInput { all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfNamedMethodArgumentsPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: NamedMethodArgumentPropositionInput @cost(weight: "10") some: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfNamedMethodSourcesPropositionInput { all: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: NamedMethodSourcePropositionInput @cost(weight: "10") some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FloatPropositionInput { equalTo: Float @cost(weight: "10") - notEqualTo: Float @cost(weight: "10") - in: [Float] @cost(weight: "10") - notIn: [Float] @cost(weight: "10") greaterThan: Float @cost(weight: "10") - notGreaterThan: Float @cost(weight: "10") greaterThanOrEqualTo: Float @cost(weight: "10") - notGreaterThanOrEqualTo: Float @cost(weight: "10") + in: [Float] @cost(weight: "10") + inClosedInterval: ClosedIntervalInput @cost(weight: "10") lessThan: Float @cost(weight: "10") - notLessThan: Float @cost(weight: "10") lessThanOrEqualTo: Float @cost(weight: "10") + notEqualTo: Float @cost(weight: "10") + notGreaterThan: Float @cost(weight: "10") + notGreaterThanOrEqualTo: Float @cost(weight: "10") + notIn: [Float] @cost(weight: "10") + notLessThan: Float @cost(weight: "10") notLessThanOrEqualTo: Float @cost(weight: "10") - inClosedInterval: ClosedIntervalInput @cost(weight: "10") } input FloatsPropositionInput { all: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: FloatPropositionInput @cost(weight: "10") some: FloatPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] - or: [GeometricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [GeometricDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput thicknesses: FloatsPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput + warnings: StringsPropositionInput } input GeometricDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input GetHttpsResourcePropositionInput { and: [GetHttpsResourcePropositionInput!] - or: [GetHttpsResourcePropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput - parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput + or: [GetHttpsResourcePropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } input GetHttpsResourceSortInput { - id: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - hashValue: SortEnumType @cost(weight: "10") - dataFormatId: SortEnumType @cost(weight: "10") appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") - parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") -} - -input GetHttpsResourceTreeNonRootVertSortInput { - id: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") - dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + id: SortEnumType @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] - or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput } +input GetHttpsResourceTreeNonRootVertSortInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") +} + input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] - or: [HygrothermalDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [HygrothermalDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input HygrothermalDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input IntPropositionInput { equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - notIn: [Int] @cost(weight: "10") greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") lessThanOrEqualTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + notIn: [Int] @cost(weight: "10") + notLessThan: Int @cost(weight: "10") notLessThanOrEqualTo: Int @cost(weight: "10") } -input IsoDayOfWeekPropositionInput { - equalTo: IsoDayOfWeek @cost(weight: "10") - notEqualTo: IsoDayOfWeek @cost(weight: "10") - in: [IsoDayOfWeek!] @cost(weight: "10") - notIn: [IsoDayOfWeek!] @cost(weight: "10") -} - input JsonElementPropositionInput { and: [JsonElementPropositionInput!] or: [JsonElementPropositionInput!] @@ -1498,107 +1908,103 @@ input JsonElementPropositionInput { input JsonValueKindPropositionInput { equalTo: JsonValueKind @cost(weight: "10") - notEqualTo: JsonValueKind @cost(weight: "10") in: [JsonValueKind!] @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") notIn: [JsonValueKind!] @cost(weight: "10") } -input KeyValuePairOfGuidAndNullableOfUInt32Input { +input KeyValuePairOfGuidAndNullableUInt32Input { key: Uuid! value: NonNegativeInt } input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] - or: [LifeCycleDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [LifeCycleDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input LifeCycleDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input LocalDatePropositionInput { - and: [LocalDatePropositionInput!] - or: [LocalDatePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - month: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - dayOfYear: IntPropositionInput + equalTo: LocalDate @cost(weight: "10") + greaterThan: LocalDate @cost(weight: "10") + greaterThanOrEqualTo: LocalDate @cost(weight: "10") + in: [LocalDate] @cost(weight: "10") + lessThan: LocalDate @cost(weight: "10") + lessThanOrEqualTo: LocalDate @cost(weight: "10") + notEqualTo: LocalDate @cost(weight: "10") + notGreaterThan: LocalDate @cost(weight: "10") + notGreaterThanOrEqualTo: LocalDate @cost(weight: "10") + notIn: [LocalDate] @cost(weight: "10") + notLessThan: LocalDate @cost(weight: "10") + notLessThanOrEqualTo: LocalDate @cost(weight: "10") } input LocalDateTimePropositionInput { - and: [LocalDateTimePropositionInput!] - or: [LocalDateTimePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - month: IntPropositionInput - dayOfYear: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput - timeOfDay: LocalTimePropositionInput - date: LocalDatePropositionInput + equalTo: LocalDateTime @cost(weight: "10") + greaterThan: LocalDateTime @cost(weight: "10") + greaterThanOrEqualTo: LocalDateTime @cost(weight: "10") + in: [LocalDateTime] @cost(weight: "10") + lessThan: LocalDateTime @cost(weight: "10") + lessThanOrEqualTo: LocalDateTime @cost(weight: "10") + notEqualTo: LocalDateTime @cost(weight: "10") + notGreaterThan: LocalDateTime @cost(weight: "10") + notGreaterThanOrEqualTo: LocalDateTime @cost(weight: "10") + notIn: [LocalDateTime] @cost(weight: "10") + notLessThan: LocalDateTime @cost(weight: "10") + notLessThanOrEqualTo: LocalDateTime @cost(weight: "10") } input LocalTimePropositionInput { - and: [LocalTimePropositionInput!] - or: [LocalTimePropositionInput!] - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput + equalTo: LocalTime @cost(weight: "10") + greaterThan: LocalTime @cost(weight: "10") + greaterThanOrEqualTo: LocalTime @cost(weight: "10") + in: [LocalTime] @cost(weight: "10") + lessThan: LocalTime @cost(weight: "10") + lessThanOrEqualTo: LocalTime @cost(weight: "10") + notEqualTo: LocalTime @cost(weight: "10") + notGreaterThan: LocalTime @cost(weight: "10") + notGreaterThanOrEqualTo: LocalTime @cost(weight: "10") + notIn: [LocalTime] @cost(weight: "10") + notLessThan: LocalTime @cost(weight: "10") + notLessThanOrEqualTo: LocalTime @cost(weight: "10") } input LongPropositionInput { equalTo: Long @cost(weight: "10") - notEqualTo: Long @cost(weight: "10") - in: [Long] @cost(weight: "10") - notIn: [Long] @cost(weight: "10") greaterThan: Long @cost(weight: "10") - notGreaterThan: Long @cost(weight: "10") greaterThanOrEqualTo: Long @cost(weight: "10") - notGreaterThanOrEqualTo: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") lessThan: Long @cost(weight: "10") - notLessThan: Long @cost(weight: "10") lessThanOrEqualTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + notIn: [Long] @cost(weight: "10") + notLessThan: Long @cost(weight: "10") notLessThanOrEqualTo: Long @cost(weight: "10") } @@ -1609,8 +2015,8 @@ input NamedMethodArgumentInput { input NamedMethodArgumentPropositionInput { and: [NamedMethodArgumentPropositionInput!] - or: [NamedMethodArgumentPropositionInput!] name: StringPropositionInput + or: [NamedMethodArgumentPropositionInput!] } input NamedMethodArgumentSortInput { @@ -1624,43 +2030,43 @@ input NamedMethodSourceInput { input NamedMethodSourcePropositionInput { and: [NamedMethodSourcePropositionInput!] - or: [NamedMethodSourcePropositionInput!] name: StringPropositionInput + or: [NamedMethodSourcePropositionInput!] value: CrossDatabaseDataReferencePropositionInput } input NullableOfCalorimetricObserverPropositionInput { equalTo: CalorimetricObserver @cost(weight: "10") - notEqualTo: CalorimetricObserver @cost(weight: "10") in: [CalorimetricObserver] @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") notIn: [CalorimetricObserver] @cost(weight: "10") } input NullableOfCoatedSidePropositionInput { equalTo: CoatedSide @cost(weight: "10") - notEqualTo: CoatedSide @cost(weight: "10") in: [CoatedSide] @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") notIn: [CoatedSide] @cost(weight: "10") } input NullableOfIlluminantPropositionInput { equalTo: Illuminant @cost(weight: "10") - notEqualTo: Illuminant @cost(weight: "10") in: [Illuminant] @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") notIn: [Illuminant] @cost(weight: "10") } input NullableOfOpticalComponentSubtypePropositionInput { equalTo: OpticalComponentSubtype @cost(weight: "10") - notEqualTo: OpticalComponentSubtype @cost(weight: "10") in: [OpticalComponentSubtype] @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") notIn: [OpticalComponentSubtype] @cost(weight: "10") } input NullableOfOpticalComponentTypePropositionInput { equalTo: OpticalComponentType @cost(weight: "10") - notEqualTo: OpticalComponentType @cost(weight: "10") in: [OpticalComponentType] @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") notIn: [OpticalComponentType] @cost(weight: "10") } @@ -1672,117 +2078,86 @@ input NumerationInput { input NumerationPropositionInput { and: [NumerationPropositionInput!] + mainNumber: StringPropositionInput or: [NumerationPropositionInput!] prefix: StringPropositionInput - mainNumber: StringPropositionInput suffix: StringPropositionInput } input NumerationSortInput { - prefix: SortEnumType @cost(weight: "10") mainNumber: SortEnumType @cost(weight: "10") + prefix: SortEnumType @cost(weight: "10") suffix: SortEnumType @cost(weight: "10") } -input OffsetDateTimePropositionInput { - and: [OffsetDateTimePropositionInput!] - or: [OffsetDateTimePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - month: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - dayOfYear: IntPropositionInput - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput - localDateTime: LocalDateTimePropositionInput - date: LocalDatePropositionInput - timeOfDay: LocalTimePropositionInput - offset: OffsetPropositionInput -} - -input OffsetPropositionInput { - and: [OffsetPropositionInput!] - or: [OffsetPropositionInput!] - seconds: IntPropositionInput - milliseconds: IntPropositionInput - ticks: LongPropositionInput - nanoseconds: LongPropositionInput -} - input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] - or: [OpticalDataPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput - type: NullableOfOpticalComponentTypePropositionInput - subtype: NullableOfOpticalComponentSubtypePropositionInput + cielabColors: FilterInputTypeOfCielabColorsPropositionInput coatedSide: NullableOfCoatedSidePropositionInput + colorRenderingIndices: FloatsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + infraredEmittances: FloatsPropositionInput + locale: StringPropositionInput + name: StringPropositionInput nearnormalHemisphericalSolarReflectances: FloatsPropositionInput nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput - infraredEmittances: FloatsPropositionInput - colorRenderingIndices: FloatsPropositionInput - cielabColors: FilterInputTypeOfCielabColorsPropositionInput + or: [OpticalDataPropositionInput!] + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + subtype: NullableOfOpticalComponentSubtypePropositionInput + type: NullableOfOpticalComponentTypePropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput + warnings: StringsPropositionInput } input OpticalDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] - or: [PhotovoltaicDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [PhotovoltaicDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input PhotovoltaicDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input PublicationInput { @@ -1797,36 +2172,36 @@ input PublicationInput { } input PublicationPropositionInput { + abstract: StringPropositionInput and: [PublicationPropositionInput!] - or: [PublicationPropositionInput!] + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: StringPropositionInput authors: StringsPropositionInput "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." doi: StringPropositionInput - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: StringPropositionInput + or: [PublicationPropositionInput!] + "Referenced section" + section: StringPropositionInput + title: StringPropositionInput "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: StringPropositionInput "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UrlPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput - "Referenced section" - section: StringPropositionInput } input PublicationSortInput { - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." arXiv: SortEnumType @cost(weight: "10") + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: SortEnumType @cost(weight: "10") + "Referenced section" + section: SortEnumType @cost(weight: "10") + title: SortEnumType @cost(weight: "10") "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: SortEnumType @cost(weight: "10") "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") - abstract: SortEnumType @cost(weight: "10") - "Referenced section" - section: SortEnumType @cost(weight: "10") } input PublishDataInput { @@ -1836,19 +2211,18 @@ input PublishDataInput { input RecomputeGetHttpsResourceHashValuesPropositionInput { and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] - or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput - parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput + or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -1860,13 +2234,13 @@ input ReferenceInput { input ReferencePropositionInput { and: [ReferencePropositionInput!] or: [ReferencePropositionInput!] - standard: StandardPropositionInput publication: PublicationPropositionInput + standard: StandardPropositionInput } input ReferenceSortInput { - standard: StandardSortInput @cost(weight: "10") publication: PublicationSortInput @cost(weight: "10") + standard: StandardSortInput @cost(weight: "10") } input RemoveDataApprovalInput { @@ -1894,16 +2268,16 @@ input SetGetHttpsResourceParentInput { input ShortPropositionInput { equalTo: Short @cost(weight: "10") - notEqualTo: Short @cost(weight: "10") - in: [Short] @cost(weight: "10") - notIn: [Short] @cost(weight: "10") greaterThan: Short @cost(weight: "10") - notGreaterThan: Short @cost(weight: "10") greaterThanOrEqualTo: Short @cost(weight: "10") - notGreaterThanOrEqualTo: Short @cost(weight: "10") + in: [Short] @cost(weight: "10") lessThan: Short @cost(weight: "10") - notLessThan: Short @cost(weight: "10") lessThanOrEqualTo: Short @cost(weight: "10") + notEqualTo: Short @cost(weight: "10") + notGreaterThan: Short @cost(weight: "10") + notGreaterThanOrEqualTo: Short @cost(weight: "10") + notIn: [Short] @cost(weight: "10") + notLessThan: Short @cost(weight: "10") notLessThanOrEqualTo: Short @cost(weight: "10") } @@ -1917,82 +2291,67 @@ input StandardInput { year: Int } +input StandardizerPropositionInput { + equalTo: Standardizer @cost(weight: "10") + in: [Standardizer!] @cost(weight: "10") + notEqualTo: Standardizer @cost(weight: "10") + notIn: [Standardizer!] @cost(weight: "10") +} + +input StandardizersPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." input StandardPropositionInput { + abstract: StringPropositionInput and: [StandardPropositionInput!] - or: [StandardPropositionInput!] - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: IntPropositionInput - numeration: NumerationPropositionInput - standardizers: StandardizersPropositionInput locator: UrlPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput + numeration: NumerationPropositionInput + or: [StandardPropositionInput!] "The section of the standard to which the reference refers to." section: StringPropositionInput + standardizers: StandardizersPropositionInput + title: StringPropositionInput + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: IntPropositionInput } "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." input StandardSortInput { - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: SortEnumType @cost(weight: "10") - numeration: NumerationSortInput @cost(weight: "10") - locator: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") abstract: SortEnumType @cost(weight: "10") + locator: UriSortInput @cost(weight: "10") + numeration: NumerationSortInput @cost(weight: "10") "The section of the standard to which the reference refers to." section: SortEnumType @cost(weight: "10") -} - -input StandardizerPropositionInput { - equalTo: Standardizer @cost(weight: "10") - notEqualTo: Standardizer @cost(weight: "10") - in: [Standardizer!] @cost(weight: "10") - notIn: [Standardizer!] @cost(weight: "10") -} - -input StandardizersPropositionInput { - all: StandardizerPropositionInput @cost(weight: "10") - none: StandardizerPropositionInput @cost(weight: "10") - some: StandardizerPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: SortEnumType @cost(weight: "10") } input StringPropositionInput { and: [StringPropositionInput!] - or: [StringPropositionInput!] - equalTo: String @cost(weight: "10") - notEqualTo: String @cost(weight: "10") contains: String @cost(weight: "20") doesNotContain: String @cost(weight: "20") + doesNotEndWith: String @cost(weight: "20") + doesNotStartWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + equalTo: String @cost(weight: "10") in: [String] @cost(weight: "10") + notEqualTo: String @cost(weight: "10") notIn: [String] @cost(weight: "10") + or: [StringPropositionInput!] startsWith: String @cost(weight: "20") - doesNotStartWith: String @cost(weight: "20") - endsWith: String @cost(weight: "20") - doesNotEndWith: String @cost(weight: "20") } input StringsPropositionInput { all: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: StringPropositionInput @cost(weight: "10") some: StringPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input TimeSpanPropositionInput { - equalTo: TimeSpan @cost(weight: "10") - notEqualTo: TimeSpan @cost(weight: "10") - in: [TimeSpan] @cost(weight: "10") - notIn: [TimeSpan] @cost(weight: "10") - greaterThan: TimeSpan @cost(weight: "10") - notGreaterThan: TimeSpan @cost(weight: "10") - greaterThanOrEqualTo: TimeSpan @cost(weight: "10") - notGreaterThanOrEqualTo: TimeSpan @cost(weight: "10") - lessThan: TimeSpan @cost(weight: "10") - notLessThan: TimeSpan @cost(weight: "10") - lessThanOrEqualTo: TimeSpan @cost(weight: "10") - notLessThanOrEqualTo: TimeSpan @cost(weight: "10") } input ToTreeVertexAppliedConversionMethodInput { @@ -2003,9 +2362,9 @@ input ToTreeVertexAppliedConversionMethodInput { input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] - or: [ToTreeVertexAppliedConversionMethodPropositionInput!] - methodId: UuidPropositionInput arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [ToTreeVertexAppliedConversionMethodPropositionInput!] sourceName: StringPropositionInput } @@ -2025,11 +2384,18 @@ input UpdateChildGetHttpsResourceInput { input UpdateDataAccessRightsInput { allowedApplications: [String!] allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32Input!] dataId: Uuid! dataKind: DataKind! } +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + input UpdateDataInput { componentId: Uuid! createdAt: DateTime! @@ -2042,13 +2408,6 @@ input UpdateDataInput { warnings: [String!]! } -input UpdateDatabaseInput { - databaseId: Uuid! - description: String! - locator: Url! - name: String! -} - input UpdateInstitutionAccessRightsInput { allowedDatasetsPerTimeSpan: NonNegativeInt allowedUserCount: NonNegativeInt @@ -2058,18 +2417,16 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] - or: [UpdateResponseApprovalsPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [UpdateResponseApprovalsPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2083,54 +2440,54 @@ input UpdateRootGetHttpsResourceInput { input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") - localPath: SortEnumType @cost(weight: "10") authority: SortEnumType @cost(weight: "10") + dnsSafeHost: SortEnumType @cost(weight: "10") + fragment: SortEnumType @cost(weight: "10") + host: SortEnumType @cost(weight: "10") hostNameType: SortEnumType @cost(weight: "10") + idnHost: SortEnumType @cost(weight: "10") + isAbsoluteUri: SortEnumType @cost(weight: "10") isDefaultPort: SortEnumType @cost(weight: "10") isFile: SortEnumType @cost(weight: "10") isLoopback: SortEnumType @cost(weight: "10") - pathAndQuery: SortEnumType @cost(weight: "10") isUnc: SortEnumType @cost(weight: "10") - host: SortEnumType @cost(weight: "10") + localPath: SortEnumType @cost(weight: "10") + originalString: SortEnumType @cost(weight: "10") + pathAndQuery: SortEnumType @cost(weight: "10") port: SortEnumType @cost(weight: "10") query: SortEnumType @cost(weight: "10") - fragment: SortEnumType @cost(weight: "10") scheme: SortEnumType @cost(weight: "10") - originalString: SortEnumType @cost(weight: "10") - dnsSafeHost: SortEnumType @cost(weight: "10") - idnHost: SortEnumType @cost(weight: "10") - isAbsoluteUri: SortEnumType @cost(weight: "10") userEscaped: SortEnumType @cost(weight: "10") userInfo: SortEnumType @cost(weight: "10") } input UrlPropositionInput { equalTo: Url @cost(weight: "10") - notEqualTo: Url @cost(weight: "10") - in: [Url] @cost(weight: "10") - notIn: [Url] @cost(weight: "10") greaterThan: Url @cost(weight: "10") - notGreaterThan: Url @cost(weight: "10") greaterThanOrEqualTo: Url @cost(weight: "10") - notGreaterThanOrEqualTo: Url @cost(weight: "10") + in: [Url] @cost(weight: "10") lessThan: Url @cost(weight: "10") - notLessThan: Url @cost(weight: "10") lessThanOrEqualTo: Url @cost(weight: "10") + notEqualTo: Url @cost(weight: "10") + notGreaterThan: Url @cost(weight: "10") + notGreaterThanOrEqualTo: Url @cost(weight: "10") + notIn: [Url] @cost(weight: "10") + notLessThan: Url @cost(weight: "10") notLessThanOrEqualTo: Url @cost(weight: "10") } input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid] @cost(weight: "10") - notIn: [Uuid] @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") lessThanOrEqualTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") notLessThanOrEqualTo: Uuid @cost(weight: "10") } @@ -2293,6 +2650,11 @@ enum CreateResponseApprovalsErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum DatabaseVerificationState { + PENDING + VERIFIED +} + enum DataKind { CALORIMETRIC_DATA GEOMETRIC_DATA @@ -2302,11 +2664,6 @@ enum DataKind { PHOTOVOLTAIC_DATA } -enum DatabaseVerificationState { - PENDING - VERIFIED -} - enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -2330,17 +2687,6 @@ enum Illuminant { D65 } -enum IsoDayOfWeek { - NONE - MONDAY - TUESDAY - WEDNESDAY - THURSDAY - FRIDAY - SATURDAY - SUNDAY -} - enum JsonValueKind { UNDEFINED OBJECT @@ -2475,6 +2821,15 @@ enum UpdateDataAccessRightsErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + UNKNOWN_INSTITUTION + UNKNOWN_USER + UNKNOWN_APPLICATION +} + +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE } enum UpdateDataErrorCode { @@ -2484,12 +2839,6 @@ enum UpdateDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateDatabaseErrorCode { - UNKNOWN - UNAUTHORIZED - UNKNOWN_DATABASE -} - enum UpdateInstitutionAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED @@ -2514,63 +2863,83 @@ enum UpdateRootGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost("The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." weight: String!) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION - -"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize("The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." assumedSize: Int "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." requireOneSlicingArgument: Boolean! = true "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." sizedFields: [String!] "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." slicingArgumentDefaultValue: Int "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." slicingArguments: [String!]) on FIELD_DEFINITION - -"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy("The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." url: String!) on SCALAR - -scalar Any +scalar Any @specifiedBy(url: "https://scalars.graphql.org/chillicream/any.html") -"The `Byte` scalar type represents non-fractional whole numeric values. Byte can represent values between 0 and 255." +"The `Byte` scalar type represents a signed 8-bit integer." scalar Byte + @specifiedBy(url: "https://scalars.graphql.org/chillicream/byte.html") -"The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https:\/\/www.graphql-scalars.com\/date-time") - -""" -Represents a time zone - a mapping between UTC and local time. -A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. - -Example: `Europe/Zurich` -""" -scalar DateTimeZone +"The `DateTime` scalar type represents a date and time with time zone offset information." +scalar DateTime + @specifiedBy(url: "https://scalars.graphql.org/chillicream/date-time.html") -"The `Decimal` scalar type represents a decimal floating-point number." +"The `Decimal` scalar type represents a decimal floating-point number with high precision." scalar Decimal + @specifiedBy(url: "https://scalars.graphql.org/chillicream/decimal.html") -""" -Represents a fixed (and calendar-independent) length of time. +"The `Duration` scalar type represents a duration of time." +scalar Duration + @specifiedBy(url: "https://scalars.graphql.org/chillicream/duration.html") -Allowed patterns: -- `-D:hh:mm:ss.sssssssss` +"The `LocalDate` scalar type represents a date without time or time zone information." +scalar LocalDate + @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-date.html") -Examples: -- `-1:20:00:00.999999999` -""" -scalar Duration +"The `LocalDateTime` scalar type represents a date and time without time zone information." +scalar LocalDateTime + @specifiedBy( + url: "https://scalars.graphql.org/chillicream/local-date-time.html" + ) "BCP 47 compliant Language Tag string" scalar Locale -"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +"The `LocalTime` scalar type represents a time of day without date or time zone information." +scalar LocalTime + @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-time.html") + +"The `Long` scalar type represents a signed 64-bit integer." scalar Long + @specifiedBy(url: "https://scalars.graphql.org/chillicream/long.html") -"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." +"The `NonNegativeInt` scalar type represents an unsigned 32-bit numeric non-fractional value." scalar NonNegativeInt -"The `Short` scalar type represents non-fractional signed whole 16-bit numeric values. Short can represent values between -(2^15) and 2^15 - 1." +"The `Short` scalar type represents a signed 16-bit integer." scalar Short - -"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." -scalar TimeSpan + @specifiedBy(url: "https://scalars.graphql.org/chillicream/short.html") "The `Upload` scalar type represents a file upload." scalar Upload -scalar Url +"The `Url` scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986." +scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986") -scalar Uuid \ No newline at end of file +scalar Uuid + @specifiedBy(url: "https://scalars.graphql.org/chillicream/uuid.html") + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost( + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! +) on + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | ENUM + | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize( + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean = true + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!] + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] +) on FIELD_DEFINITION \ No newline at end of file diff --git a/frontend/codegen.ts b/frontend/codegen.ts index 9e23c42d..8662a5aa 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -1,5 +1,54 @@ import type { CodegenConfig } from "@graphql-codegen/cli"; +const scalars = { + ID: { + input: "string", + output: "string", + }, + Any: "object", + Byte: "number", + DateTime: "string", + DateTimeZone: "string", + Decimal: "string", + Duration: "string", + LocalDate: "string", + LocalDateTime: "string", + LocalTime: "string", + Locale: "string", + Long: "number", + NonNegativeInt: "number", + Short: "number", + TimeSpan: "string", + UnsignedByte: "string", + Upload: "unknown", + Url: "string", + Uuid: "string", +}; + +// "Apollo Client" conform config +// https://the-guild.dev/graphql/codegen/plugins/typescript/typescript +const graphQlConfig = { + avoidOptionals: { + // Use `null` for nullable fields instead of optionals + field: true, + // Allow nullable input fields to remain unspecified + inputValue: false, + }, + // Use `unknown` instead of `any` for unconfigured scalars + defaultScalarType: "unknown", + // Apollo Client always includes `__typename` fields + nonOptionalTypename: true, + // Apollo Client doesn't add the `__typename` field to root types so + // don't generate a type for the `__typename` for root operation types. + skipTypeNameForRoot: true, + // ... + strictScalars: true, + scalars: scalars, + useTypeImports: true, + // immutableTypes: true, + // nullability: true, +}; + const config: CodegenConfig = { overwrite: true, schema: "./type-defs.graphqls", @@ -10,55 +59,18 @@ const config: CodegenConfig = { // Inspired by https://www.apollographql.com/docs/react/development-testing/graphql-codegen#generating-precompiled-graphql-documents-with-their-type-definitions "./__generated__/graphql.ts": { plugins: ["typescript"], + config: graphQlConfig, }, "./queries/": { preset: "near-operation-file", + // https://the-guild.dev/graphql/codegen/docs/migration/apollo-tooling#per-operation-file-generation-with-near-operation-file presetConfig: { // This should be the file generated by the "typescript" plugin above, // relative to the directory specified for this configuration baseTypesPath: "../__generated__/graphql.ts", }, plugins: ["typescript-operations", "typed-document-node"], - config: { - avoidOptionals: { - // Use `null` for nullable fields instead of optionals - field: true, - // Allow nullable input fields to remain unspecified - inputValue: false, - }, - // Use `unknown` instead of `any` for unconfigured scalars - defaultScalarType: "unknown", - // Apollo Client always includes `__typename` fields - nonOptionalTypename: true, - // Apollo Client doesn't add the `__typename` field to root types so - // don't generate a type for the `__typename` for root operation types. - skipTypeNameForRoot: true, - // ... - strictScalars: true, - scalars: { - ID: { - input: "string", - output: "string", - }, - Any: "unknown", - Byte: "number", - DateTime: "string", - DateTimeZone: "string", - Decimal: "string", - Duration: "string", - Locale: "string", - Long: "number", - NonNegativeInt: "number", - Short: "number", - TimeSpan: "string", - Upload: "unknown", - Url: "string", - Uuid: "string", - }, - useTypeImports: true, - // immutableTypes: true, - // nullability: true, - }, + config: graphQlConfig, }, "./__generated__/fragmentMatcherTypes.ts": { plugins: ["fragment-matcher"], @@ -69,6 +81,9 @@ const config: CodegenConfig = { deterministic: true, }, }, + "./__generated__/apolloHelpers.ts": { + plugins: ["typescript-apollo-client-helpers"], + }, // './queries/': { // preset: 'near-operation-file', // presetConfig: { diff --git a/frontend/components/ActiveFilterAndSortBar.tsx b/frontend/components/ActiveFilterAndSortBar.tsx new file mode 100644 index 00000000..3a4da899 --- /dev/null +++ b/frontend/components/ActiveFilterAndSortBar.tsx @@ -0,0 +1,132 @@ +import { Tag, Space, Typography, Flex } from "antd"; +import { + ObjectFilterState, + getFilterOperatorLabel, + createFilterStateReducer, + FilterDefinition, + FilterStateReducerContext, +} from "../lib/filter"; +import { formatSortDirection, SortState, SortDefinition } from "../lib/sort"; +import DeleteButton from "./DeleteButton"; +import { Key } from "react"; +import { getLabel } from "../lib/string"; + +const stringifySort = ( + sort: SortState, + definitions: readonly SortDefinition[], +) => `${String(definitions[sort.index].field)}|${sort.direction}`; + +const renderSort = ( + sort: SortState, + definitions: readonly SortDefinition[], +) => ( + <> + {getLabel(definitions[sort.index], "none-upper")} ( + {formatSortDirection(sort.direction)}) + +); + +const stringifyFilter = ( + filter: ObjectFilterState, + definitions: readonly FilterDefinition[], +): Key => + createFilterStateReducer( + (scalar) => + `${getFilterOperatorLabel(scalar.operator)}|${JSON.stringify(scalar.value)}`, + (list, stringify) => + `${getFilterOperatorLabel(list.operator)}|${stringify(list.value)}`, + (object, context, stringify) => + `${String(context[object.index].field)}|${stringify(object.value)}`, + )(filter, definitions as FilterStateReducerContext); + +const renderFilter = ( + value: ObjectFilterState, + definitions: readonly FilterDefinition[], +) => + createFilterStateReducer( + (scalar) => ( + <> + {getFilterOperatorLabel(scalar.operator)}{" "} + {Array.isArray(scalar.value) + ? `{${scalar.value.join(", ")}}` + : scalar.value} + + ), + (list, render) => ( + <> + {getFilterOperatorLabel(list.operator)} {render(list.value)} + + ), + (object, context, render) => ( + <> + {getLabel(context[object.index], "none-upper")} {render(object.value)} + + ), + )(value, definitions as FilterStateReducerContext); + +export default function ActiveFilterAndSortBar({ + values, + filterDefinitions, + sortDefinitions, + onRemoveFilter, + onRemoveSort, + onRemoveAll, +}: { + values: { + filters: readonly ObjectFilterState[]; + sorts: readonly SortState[]; + }; + filterDefinitions: readonly FilterDefinition[]; + sortDefinitions: readonly SortDefinition[]; + onRemoveFilter: (index: number) => void; + onRemoveSort: (index: number) => void; + onRemoveAll: () => void; +}) { + return ( + + + {values.filters.length > 0 && ( + <> + Filtered by + {values.filters.map((filter, index: number) => ( + + Remove + + } + key={stringifyFilter(filter, filterDefinitions)} + onClose={() => onRemoveFilter(index)} + > + {renderFilter(filter, filterDefinitions)} + + ))} + + )} + {values.sorts.length > 0 && ( + <> + Sorted by + {values.sorts.map((sort, index: number) => ( + } + key={stringifySort(sort, sortDefinitions)} + onClose={() => onRemoveSort(index)} + > + {renderSort(sort, sortDefinitions)} + + ))} + + )} + + {(values.filters.length > 0 || values.sorts.length > 0) && ( + + Remove All + + )} + + ); +} diff --git a/frontend/components/CielabColorView.tsx b/frontend/components/CielabColorView.tsx new file mode 100644 index 00000000..8dc5fbc4 --- /dev/null +++ b/frontend/components/CielabColorView.tsx @@ -0,0 +1,19 @@ +import Copyable from "./Copyable"; +import { CielabColor } from "../__generated__/graphql"; +import Float, { Unit } from "./Float"; + +export default function CielabColorView({ value }: { value: CielabColor }) { + return ( + { + if (key === "__typename") return undefined; + return value; + })} + > + (L* , a*{" "} + , b*{" "} + ) + + ); +} diff --git a/frontend/components/CodeView.tsx b/frontend/components/CodeView.tsx new file mode 100644 index 00000000..fb828e3d --- /dev/null +++ b/frontend/components/CodeView.tsx @@ -0,0 +1,32 @@ +import Copyable from "./Copyable"; +import CopyableBlock from "./CopyableBlock"; + +export default function CodeView({ + code, + inline = false, +}: { + code: string; + inline?: boolean; +}) { + if (inline) { + return ( + + {code} + + ); + } else { + return ( + +
    +          
    +            {code}
    +          
    +        
    +
    + ); + } +} diff --git a/frontend/components/CopyButton.tsx b/frontend/components/CopyButton.tsx new file mode 100644 index 00000000..952d734e --- /dev/null +++ b/frontend/components/CopyButton.tsx @@ -0,0 +1,85 @@ +import { Button, Tooltip } from "antd"; +import { CopyOutlined, CheckOutlined } from "@ant-design/icons"; +import { ReactNode, useState } from "react"; + +export default function CopyButton({ + getText, + type = "text", + size = "small", + copyIcon = , + onlyIcon = false, + color = undefined, + children, +}: { + getText: () => string; + type?: "text" | "default"; + size?: "small" | "medium" | "large"; + copyIcon?: ReactNode; + onlyIcon?: boolean; + color?: "white"; + children?: ReactNode; +}) { + const [copied, setCopied] = useState(false); + + return onlyIcon ? ( + + + ); +} diff --git a/frontend/components/Copyable.tsx b/frontend/components/Copyable.tsx new file mode 100644 index 00000000..29d321a1 --- /dev/null +++ b/frontend/components/Copyable.tsx @@ -0,0 +1,26 @@ +import { Space } from "antd"; +import { ReactNode } from "react"; +import CopyButton from "./CopyButton"; + +export default function Copyable({ + text, + onlyIcon, + color, + children, +}: { + text: string; + onlyIcon?: boolean; + color?: "white"; + children?: ReactNode; +}) { + const content = ( + <> + {children == null ? text : children} + text} onlyIcon={onlyIcon} color={color}> + Copy + + + ); + + return {onlyIcon ? content : {content}}; +} diff --git a/frontend/components/CopyableBlock.tsx b/frontend/components/CopyableBlock.tsx new file mode 100644 index 00000000..edc69062 --- /dev/null +++ b/frontend/components/CopyableBlock.tsx @@ -0,0 +1,53 @@ +import { Button } from "antd"; +import { CheckOutlined, CopyOutlined } from "@ant-design/icons"; +import { ReactNode, useState } from "react"; + +export default function CopyableBlock({ + text, + color, + children, +}: { + text: string; + color?: "white"; + children: ReactNode; +}) { + const [copied, setCopied] = useState(false); + + return ( +
    + {children} + +
    + ); +} diff --git a/frontend/components/DateTimeX.tsx b/frontend/components/DateTimeX.tsx new file mode 100644 index 00000000..fdd12a3e --- /dev/null +++ b/frontend/components/DateTimeX.tsx @@ -0,0 +1,34 @@ +import { Calendar, Popover } from "antd"; +import dayjs from "dayjs"; +import { Scalars } from "../__generated__/graphql"; + +interface DateTimeProps { + value: Scalars["DateTime"]["output"]; +} + +export default function DateTimeX({ value }: DateTimeProps) { + const parsedValue = dayjs(value); + + const calendarContent = ( +
    + null} + /> +
    + ); + + return ( + + + {parsedValue.format("YYYY-MM-DD HH:mm")} + + + ); +} diff --git a/frontend/components/DeleteButton.tsx b/frontend/components/DeleteButton.tsx new file mode 100644 index 00000000..d38c06d0 --- /dev/null +++ b/frontend/components/DeleteButton.tsx @@ -0,0 +1,63 @@ +import { Button, Tooltip } from "antd"; +import { DeleteOutlined, SyncOutlined } from "@ant-design/icons"; +import { capitalize } from "../lib/string"; +import { CSSProperties, forwardRef } from "react"; + +interface DeleteButtonProps { + kind?: "delete" | "remove"; + type?: "primary" | "text" | "default" | "icon"; + deleting?: boolean; + style?: CSSProperties; + onClick?: (e?: React.MouseEvent) => void; + children?: React.ReactNode; + // This allows Popconfirm to inject its internal event handlers + [key: string]: any; +} + +const DeleteButton = forwardRef( + ( + { + kind = "delete", + type = "primary", + deleting = false, + style, + onClick, + children, + ...rest + }, + ref, + ) => { + const label = children ?? capitalize(kind); + + const commonProps = { + ...rest, // contains Popconfirm's events + ref, // allows Popconfirm to measure position + danger: true, + loading: deleting, + style, + onClick, + }; + + switch (type) { + case "icon": + return ( + + + ); + } + }, +); + +export default DeleteButton; diff --git a/frontend/components/EditButton.tsx b/frontend/components/EditButton.tsx new file mode 100644 index 00000000..21a085b8 --- /dev/null +++ b/frontend/components/EditButton.tsx @@ -0,0 +1,34 @@ +import { EditOutlined } from "@ant-design/icons"; +import { Button, ButtonProps, Tooltip } from "antd"; + +interface EditButtonProps extends Omit< + ButtonProps, + "type" | "icon" | "shape" | "children" +> { + type?: "text" | "default" | "icon"; +} + +export default function EditButton({ + type = "default", + ...rest +}: EditButtonProps) { + switch (type) { + case "icon": + return ( + + + ); + } +} diff --git a/frontend/components/EnumSelect.tsx b/frontend/components/EnumSelect.tsx new file mode 100644 index 00000000..6c6476c7 --- /dev/null +++ b/frontend/components/EnumSelect.tsx @@ -0,0 +1,52 @@ +import { Select, SelectProps } from "antd"; +import { humanize } from "../lib/string"; + +export const allEnumValues = ( + enumObject: Record, +) => + Object.entries(enumObject) + .filter(([key]) => isNaN(Number(key))) + .map(([_, value]) => value); + +export const allEnumSelectOptions = ( + values: T[], + filter?: (value: T) => boolean, +) => + values + // exclude reverse mappings + .filter((value) => filter?.(value) ?? true) + .map((value) => ({ + label: humanize(String(value), "all-upper"), + value: value, + })); + +interface BaseProps extends Omit< + SelectProps, + "options" +> {} + +type EnumSelectProps = + | (BaseProps & { + enumObject: Record; + filter?: (value: T) => boolean; + }) + | (BaseProps & { + values: T[]; + }); + +export default function EnumSelect( + props: EnumSelectProps, +) { + if ("enumObject" in props) { + const { enumObject, filter, ...rest } = props; + return ( + + {...rest} + options={allEnumSelectOptions(allEnumValues(enumObject))} + /> + ); + } else { + const { values, ...rest } = props; + return {...rest} options={allEnumSelectOptions(values)} />; + } +} diff --git a/frontend/components/EnumTag.tsx b/frontend/components/EnumTag.tsx new file mode 100644 index 00000000..5cb07837 --- /dev/null +++ b/frontend/components/EnumTag.tsx @@ -0,0 +1,16 @@ +import { Tag, TagProps } from "antd"; +import { humanize } from "../lib/string"; + +export default function EnumTag(props: TagProps) { + return ( + + {props.children && humanize(props.children.toString(), "none-upper")} + + ); +} diff --git a/frontend/components/Float.tsx b/frontend/components/Float.tsx new file mode 100644 index 00000000..5b11ac08 --- /dev/null +++ b/frontend/components/Float.tsx @@ -0,0 +1,63 @@ +import Copyable from "./Copyable"; +import { Scalars } from "../__generated__/graphql"; +import { Tooltip } from "antd"; + +export enum Unit { + UNITLESS, + METER, + WATT_PER_SQUARE_METER_KELVIN, +} + +function formatSmartCut(value: number) { + const string = value.toString(); + const dotIndex = string.indexOf("."); + if (dotIndex === -1) return string; + const firstNonZero = string.search(/[1-9]/); + const significantCut = firstNonZero + 3; + const minDecimalCut = dotIndex + 4; + const finalCutIndex = Math.max(significantCut, minDecimalCut); + if (string.length > finalCutIndex) { + return string.substring(0, finalCutIndex) + "…"; + } + return string; +} + +const unitLabels = { + [Unit.METER]: "m", + [Unit.WATT_PER_SQUARE_METER_KELVIN]: ( + <> + W·m-2·K-1 + {/* W/(m2·K) */} + + ), +}; + +export default function Float({ + value, + unit, +}: { + value: Scalars["Float"]["output"]; + unit: Unit; +}) { + return ( + + {value} + + } + styles={{ + container: { + whiteSpace: "nowrap", + minWidth: "max-content", + maxWidth: "none", + }, + }} + > + + {formatSmartCut(value)} + {unit != Unit.UNITLESS && <> {unitLabels[unit]}} + + + ); +} diff --git a/frontend/components/Footer.tsx b/frontend/components/Footer.tsx index 5544d81c..8505147a 100644 --- a/frontend/components/Footer.tsx +++ b/frontend/components/Footer.tsx @@ -1,16 +1,13 @@ -import { Typography } from "antd"; +import { Space, Typography } from "antd"; import paths from "../paths"; -export type FooterProps = {}; - export default function Footer() { return ( - <> - Legal Notice{" "} - ·{" "} + + Legal Notice Data Protection Information - + ); } diff --git a/frontend/components/GnuPgKeyLink.tsx b/frontend/components/GnuPgKeyLink.tsx new file mode 100644 index 00000000..0b98e78d --- /dev/null +++ b/frontend/components/GnuPgKeyLink.tsx @@ -0,0 +1,24 @@ +import Link from "next/link"; +import Copyable from "./Copyable"; +import paths from "../paths"; +import CopyableBlock from "./CopyableBlock"; + +export default function GnuPgKeyLink({ + fingerprint, + block = false, +}: { + fingerprint: string; + block?: boolean; +}) { + const link = ( + + {fingerprint} + + ); + + return block ? ( + {link} + ) : ( + {link} + ); +} diff --git a/frontend/components/Highlight.tsx b/frontend/components/Highlight.tsx deleted file mode 100644 index d75e078e..00000000 --- a/frontend/components/Highlight.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { forwardRef } from "react"; -import Highlighter from "react-highlight-words"; - -export type HighlightProps = { - text: string | null | undefined; - snippet: string | null | undefined; -}; - -export const Highlight = forwardRef( - ({ text, snippet }, ref) => ( - - ), -); diff --git a/frontend/components/Iconize.tsx b/frontend/components/Iconize.tsx new file mode 100644 index 00000000..11e394fc --- /dev/null +++ b/frontend/components/Iconize.tsx @@ -0,0 +1,16 @@ +import { Space } from "antd"; + +export const Iconize = ({ + icon, + children, +}: { + icon: React.ReactNode; + children: React.ReactNode; +}) => { + return ( + + {icon} + {children} + + ); +}; diff --git a/frontend/components/Id.tsx b/frontend/components/Id.tsx new file mode 100644 index 00000000..452ac3ff --- /dev/null +++ b/frontend/components/Id.tsx @@ -0,0 +1,5 @@ +import { Scalars } from "../__generated__/graphql"; + +export default function Id({ value }: { value: Scalars["Uuid"]["output"] }) { + return {value}; +} diff --git a/frontend/components/IdentifierItem.tsx b/frontend/components/IdentifierItem.tsx new file mode 100644 index 00000000..526d8a8b --- /dev/null +++ b/frontend/components/IdentifierItem.tsx @@ -0,0 +1,95 @@ +import { Typography } from "antd"; +import Icon, { BarcodeOutlined, GlobalOutlined } from "@ant-design/icons"; +import { CustomIconComponentProps } from "@ant-design/icons/lib/components/Icon"; +import { Iconize } from "./Iconize"; +import { addPrefix, removePrefix } from "../lib/string"; +import CopyButton from "./CopyButton"; + +const ArXivSvg = () => ( + + + +); + +const DoiSvg = () => ( + + + +); + +const ArXivIcon = (props: Partial) => ( + +); +const DoiIcon = (props: Partial) => ( + +); + +const IDENTIFIER_CONFIG = { + arXiv: { + label: null, + prefix: "arXiv:", + icon: , + url: ({ valueWithoutPrefix }: { valueWithoutPrefix: string }) => + `https://arxiv.org/abs/${valueWithoutPrefix}`, + }, + doi: { + label: null, + prefix: "doi:", + icon: , + url: ({ valueWithoutPrefix }: { valueWithoutPrefix: string }) => + `https://doi.org/${valueWithoutPrefix}`, + }, + urn: { + label: null, + prefix: "urn:", + icon: , + url: ({ + valueWithPrefix, + valueWithoutPrefix, + }: { + valueWithPrefix: string; + valueWithoutPrefix: string; + }) => { + if (valueWithPrefix.startsWith("urn:isbn:")) { + return `https://isbnsearch.org/isbn/${removePrefix(valueWithoutPrefix, "isbn:")}`; + } + if (valueWithPrefix.startsWith("urn:issn:")) { + return `https://urn.issn.org/${valueWithPrefix}`; + } + if (valueWithPrefix.startsWith("urn:nbn:")) { + return `https://nbn-resolving.org/${valueWithPrefix}`; + } + return `https://www.ecosia.org/search?q=${valueWithPrefix}`; + }, + }, + webAddress: { + label: "Web", + prefix: "https://", + icon: , + url: ({ value }: { value: string }) => value, + }, +}; + +export default function IdentifierItem({ + type, + value, +}: { + type: keyof typeof IDENTIFIER_CONFIG; + value: string; +}) { + const config = IDENTIFIER_CONFIG[type]; + const valueWithoutPrefix = removePrefix(value, config.prefix); + const valueWithPrefix = addPrefix(value, config.prefix); + const href = config.url({ value, valueWithPrefix, valueWithoutPrefix }); + + return ( + + + + {config.label ? config.label : valueWithPrefix} + + + value} /> + + ); +} diff --git a/frontend/components/InlineList.tsx b/frontend/components/InlineList.tsx new file mode 100644 index 00000000..e6542433 --- /dev/null +++ b/frontend/components/InlineList.tsx @@ -0,0 +1,35 @@ +import { Fragment } from "react/jsx-runtime"; + +export default function InlineList({ + items, + renderItem, +}: { + items: readonly TItem[]; + renderItem: (item: TItem, index: number) => React.ReactNode; +}) { + return ( + <> + {/* */} + + {items.map((item, index) => { + const isLast = index === items.length - 1; + const isSecondToLast = index === items.length - 2; + const isOnlyTwo = items.length === 2; + return ( + + {renderItem(item, index)} + {!isLast && + (isSecondToLast ? (isOnlyTwo ? " and " : ", and ") : ", ")} + + ); + })} + + + ); +} diff --git a/frontend/components/JsonView.tsx b/frontend/components/JsonView.tsx new file mode 100644 index 00000000..dc53cfb3 --- /dev/null +++ b/frontend/components/JsonView.tsx @@ -0,0 +1,36 @@ +import { Tooltip } from "antd"; +import CopyableBlock from "./CopyableBlock"; + +export default function JsonView({ + data, + inline = false, + color, +}: { + data: object; + inline?: boolean; + color?: "white"; +}) { + if (inline) { + const jsonString = JSON.stringify(data); + return ( + }> + {jsonString} + + ); + } else { + const jsonString = JSON.stringify(data, null, 2); + return ( + +
    +          
    +            {jsonString}
    +          
    +        
    +
    + ); + } +} diff --git a/frontend/components/JumpToId.tsx b/frontend/components/JumpToId.tsx new file mode 100644 index 00000000..5356e671 --- /dev/null +++ b/frontend/components/JumpToId.tsx @@ -0,0 +1,47 @@ +import { CSSProperties, useState } from "react"; +import { Button, Input, Space } from "antd"; +import { useRouter } from "next/router"; +import { Scalars } from "../__generated__/graphql"; +import { Route } from "next"; +import PaginatedIdSelect, { PaginatedSelectProps } from "./PaginatedIdSelect"; +import { isUuid } from "../lib/string"; + +export type JumpToIdProps = { + query?: PaginatedSelectProps["query"]; + route: (id: Scalars["Uuid"]["output"]) => Route; + style?: CSSProperties; +}; + +export default function JumpToId({ query, route, style }: JumpToIdProps) { + const router = useRouter(); + const [id, setId] = useState(undefined); + + const handleJump = () => { + if (id && isUuid(id)) { + router.push(route(id)); + } + }; + + return ( + + {/* 36 characters is what a UUID of the form "ffffffff-ffff-ffff-ffff-ffffffffffff" has */} + {query ? ( + + ) : ( + setId(e.target.value)} + /> + )} + + + ); +} diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 71d68fad..3ffccb4a 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -2,7 +2,7 @@ import Head from "next/head"; import { ReactNode, useEffect } from "react"; import Footer from "./Footer"; import NavBar from "./NavBar"; -import { App, Layout as AntLayout, Typography } from "antd"; +import { App, Layout as AntLayout, Typography, Flex, Divider } from "antd"; import paths from "../paths"; import { useCookies } from "react-cookie"; @@ -12,27 +12,27 @@ const navItems = [ label: "Home", }, { - path: paths.calorimetricData, + path: paths.allCalorimetricData, label: "Calorimetric Data", }, { - path: paths.geometricData, + path: paths.allGeometricData, label: "Geometric Data", }, { - path: paths.hygrothermalData, + path: paths.allHygrothermalData, label: "Hygrothermal Data", }, { - path: paths.lifeCycleData, + path: paths.allLifeCycleData, label: "Life-Cycle Data", }, { - path: paths.opticalData, + path: paths.allOpticalData, label: "Optical Data", }, { - path: paths.photovoltaicData, + path: paths.allPhotovoltaicData, label: "Photovoltaic Data", }, { @@ -42,13 +42,14 @@ const navItems = [ ]; export type LayoutProps = { + pageTitles?: string[]; children?: ReactNode; }; const cookieConsentName = "consent"; const cookieConsentValue = "yes"; -export default function Layout({ children }: LayoutProps) { +export default function Layout({ pageTitles = [], children }: LayoutProps) { const appTitle = "TestLab Solar Façades"; const [cookies, setCookie] = useCookies([cookieConsentName]); @@ -61,7 +62,7 @@ export default function Layout({ children }: LayoutProps) { modal.info({ title: "Cookie Consent", content: ( - + This website employs cookies to make it work securely. As these cookies are essential you need to agree to their usage to use this website. @@ -73,23 +74,41 @@ export default function Layout({ children }: LayoutProps) { }, }); } - }, [shouldShowCookieConsent, setCookie]); + }, [shouldShowCookieConsent, setCookie, modal]); return ( - {appTitle} + {[...pageTitles, appTitle].join(" • ")} - + + + - - {children} + + +
    {children}
    +
    -
    + + +
    + ); diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 8ac1ff76..575baf2d 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -1,81 +1,156 @@ import { useQuery } from "@apollo/client/react"; import Link from "next/link"; import { useRouter } from "next/router"; -import { Button, Menu } from "antd"; -import { UserOutlined } from "@ant-design/icons"; +import { Button, Menu, Spin } from "antd"; +import { LoadingOutlined, UserOutlined } from "@ant-design/icons"; import paths from "../paths"; -import { CurrentUserDocument } from "../queries/currentUser.generated"; +import { + CurrentUserDocument, + CurrentUserPartialFragment, +} from "../queries/currentUser.generated"; import { extractAntiforgeryTokenFromCookie } from "../lib/apollo"; import { Route } from "next"; +import { CSSProperties, useMemo } from "react"; -type NavItemProps = { - path: Route; - label: string; -}; +const firstUserOrLoginItemStyle = { marginLeft: "auto" }; -export type NavBarProps = { - items: NavItemProps[]; +const userLoadingItem = { + key: "userLoading", + style: firstUserOrLoginItemStyle, + label: ( + } /> + ), }; -const moderatorItems = [ +const loginOrRegisterItems = (returnTo: string | string[] | undefined) => [ { - path: paths.userInfo, - label: "User Info", + key: paths.login, + style: firstUserOrLoginItemStyle, + label: ( + + Login + + ), }, { - path: paths.createData, - label: "Create Data", + key: paths.metabase.register, + label: ( + + Register + + ), }, +]; + +const userItems = (currentUser: CurrentUserPartialFragment) => [ { - path: paths.uploadFile, - label: "Upload File", + key: "user", + label: currentUser.name, + icon: , + style: firstUserOrLoginItemStyle, + children: [ + { + key: paths.userInfo, + label: User Info, + }, + { + key: paths.createData, + label: Create Data, + }, + { + key: paths.uploadFile, + label: Upload File, + }, + { + key: paths.logout, + label: ( + + + + + + ), + }, + ], }, ]; -export default function NavBar({ items }: NavBarProps) { +type NavItemProps = { + path: Route; + label: string; +}; + +export type NavBarProps = { + items: NavItemProps[]; + style?: CSSProperties; +}; + +export default function NavBar({ items, style }: NavBarProps) { const router = useRouter(); - const currentUser = useQuery(CurrentUserDocument)?.data?.currentUser; + const returnTo = router.query.returnTo; + const { loading, data } = useQuery(CurrentUserDocument); + const currentUser = data?.currentUser; + + const mainItems = useMemo( + () => + items.map((item) => ({ + key: item.path, + label: {item.label}, + })), + [items], + ); + + const userOrLoginItems = useMemo( + () => + loading + ? [userLoadingItem] + : currentUser + ? userItems(currentUser) + : loginOrRegisterItems(returnTo), + [loading, currentUser], + ); return ( - - {items.map(({ path, label }) => ( - - {label} - - ))} - {currentUser ? ( - } - > - {moderatorItems.map(({ path, label }) => ( - - {label} - - ))} - -
    - - - -
    -
    - ) : ( - - Login - - )} -
    + ); } diff --git a/frontend/components/PaginatedIdSelect.tsx b/frontend/components/PaginatedIdSelect.tsx new file mode 100644 index 00000000..f15146a5 --- /dev/null +++ b/frontend/components/PaginatedIdSelect.tsx @@ -0,0 +1,163 @@ +import React, { useState } from "react"; +import { Select, SelectProps, Spin } from "antd"; +import { useQuery } from "@apollo/client/react"; +import { useDebounce } from "../lib/hooks/useDebounce"; +import { Scalars, SortEnumType } from "../__generated__/graphql"; +import { TypedDocumentNode } from "@apollo/client"; +import { isUuid, notEmpty } from "../lib/string"; +import { isTruthy } from "../lib/array"; +import Id from "./Id"; + +// Inspired by https://ant.design/components/select/#components-select-demo-select-users + +export const createPaginatedIdSelectOption = (entity: { + uuid: Scalars["Uuid"]["output"]; + name: string; +}) => ({ + label: ( + + {entity.name} () + + ), + value: entity.uuid, +}); + +interface PageInfo { + endCursor: string | null; + hasNextPage: boolean; +} + +interface Item { + uuid: Scalars["Uuid"]["output"]; + name: string; +} + +interface ItemsData { + connection: { + edges: { node: Item }[]; + pageInfo: PageInfo; + }; +} + +interface ItemsVariables { + first: number; + after?: string | null; + where?: { or?: { id?: { equalTo: any }; name?: { contains: any } }[] }; + order?: { name?: SortEnumType }; +} + +interface BaseProps extends Pick< + SelectProps, + "value" | "onChange" | "labelInValue" | "style" +> { + query: TypedDocumentNode; +} + +interface SingleProps extends BaseProps { + mode?: undefined; +} + +interface MultiProps extends BaseProps { + mode: "multiple" | "tags"; +} + +export type PaginatedSelectProps = SingleProps | MultiProps; + +const order = { + name: SortEnumType.Asc, +}; + +export default function PaginatedIdSelect({ + query, + style = { width: "100%" }, + ...rest +}: PaginatedSelectProps) { + const [search, setSearch] = useState(""); + const pageSize = 10; + + const filters = [ + isUuid(search) && { id: { equalTo: search } }, + notEmpty(search) && { name: { contains: search } }, + ].filter(isTruthy); + const where = filters.length > 0 ? { or: filters } : undefined; + + const { data, loading, fetchMore, refetch } = useQuery< + ItemsData, + ItemsVariables + >(query, { + variables: { + first: pageSize, + after: null, + where: where, + order: order, + }, + }); + + const { edges, pageInfo } = data?.connection || { + edges: [], + pageInfo: { hasNextPage: false, endCursor: null }, + }; + + const handleSearch = useDebounce((value: string) => { + setSearch(value); + refetch({ + first: pageSize, + after: null, + where: where, + order: order, + }); + }, 500); + + const handleScroll = (e: React.UIEvent) => { + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + const isBottom = scrollHeight - scrollTop <= clientHeight + 10; + + if (isBottom && !loading && pageInfo?.hasNextPage) { + fetchMore({ + variables: { + first: pageSize, + after: pageInfo.endCursor, + where: where, + order: order, + }, + }); + } + }; + + return ( + + + ); +} diff --git a/frontend/components/QueryToolbar.tsx b/frontend/components/QueryToolbar.tsx new file mode 100644 index 00000000..0ca5f6f2 --- /dev/null +++ b/frontend/components/QueryToolbar.tsx @@ -0,0 +1,47 @@ +import { Button, Space } from "antd"; +import { RocketOutlined } from "@ant-design/icons"; +import { print } from "graphql"; +import { TypedDocumentNode } from "@apollo/client"; +import CopyButton from "./CopyButton"; + +export default function QueryToolbar({ + query, + variables, +}: { + query: TypedDocumentNode; + variables?: TVariables | null; +}) { + const openInNitro = () => { + const nitroUrl = new URL("/graphql/", window.location.origin); + // const minifiedQuery = print(query) + // .replace(/#.*$/gm, "") + // .replace(/\s+/g, " ") + // .trim(); + // nitroUrl.searchParams.set("query", minifiedQuery); + // if (variables != null) { + // nitroUrl.searchParams.set("variables", JSON.stringify(variables)); + // } + window.open(nitroUrl.toString(), "_blank"); + }; + + return ( + + GraphQL + print(query)}> + Copy Query + + {variables && ( + JSON.stringify(variables)} + > + Copy Variables + + )} + + + ); +} diff --git a/frontend/components/Reference.tsx b/frontend/components/Reference.tsx new file mode 100644 index 00000000..285f1fbc --- /dev/null +++ b/frontend/components/Reference.tsx @@ -0,0 +1,108 @@ +import { Space, Typography } from "antd"; +import { + GlobalOutlined, + BookOutlined, + SafetyCertificateOutlined, +} from "@ant-design/icons"; +import { Iconize } from "./Iconize"; +import { intersperse, isTruthy } from "../lib/array"; +import IdentifierItem from "./IdentifierItem"; +import { ReferencePartialFragment } from "../queries/common.generated"; + +interface ReferenceProps { + data: ReferencePartialFragment; +} + +export default function Reference({ data }: ReferenceProps) { + const Icon = + data.__typename === "Standard" ? SafetyCertificateOutlined : BookOutlined; + + return ( +
    + }>{data.__typename}{" "} + + {data.__typename === "Publication" && + intersperse( + [ + data.authors && data.authors.length > 0 && ( + + {data.authors.join(", ")} + + ), + data.title && ( + + {data.title}, + + ), + data.section && ( + + Section{" "} + {data.section}. + + ), + (data.arXiv || data.doi || data.urn || data.webAddress) && ( + + {(["arXiv", "doi", "urn", "webAddress"] as const) + .map( + (key) => + data[key] && ( + + ), + ) + .filter(isTruthy)} + + ), + ].filter(isTruthy), + )} + {data.__typename === "Standard" && + intersperse( + [ + data.standardizers && data.standardizers.length > 0 && ( + + {data.standardizers?.join(", ")} + + ), + + {data.numeration.prefix ?? ""} + {data.numeration.mainNumber} + {data.numeration.suffix ? `-${data.numeration.suffix}` : ""} + , + data.year && ({data.year})., + data.title && ( + + {data.title}. + + ), + data.section && ( + + Section{" "} + {data.section}. + + ), + data.locator && ( + + }>Web + + ), + ].filter(isTruthy), + )} + + {data.abstract && ( + + {data.abstract} + + )} +
    + ); +} diff --git a/frontend/components/SlideDown.tsx b/frontend/components/SlideDown.tsx new file mode 100644 index 00000000..a9f05133 --- /dev/null +++ b/frontend/components/SlideDown.tsx @@ -0,0 +1,51 @@ +import { useLayoutEffect, useRef, useState } from "react"; + +export default function SlideDown({ + open, + children, +}: { + open: boolean; + children: React.ReactNode; +}) { + const contentRef = useRef(null); + const [height, setHeight] = useState(0); + const [transitioning, setTransitioning] = useState(false); + + useLayoutEffect(() => { + const element = contentRef.current; + if (!element) return; + setTransitioning(true); + const startHeight = open ? 0 : element.scrollHeight; + const endHeight = open ? element.scrollHeight : 0; + setHeight(startHeight); + void element.offsetHeight; // force reflow + requestAnimationFrame(() => { + requestAnimationFrame(() => { + setHeight(endHeight); + }); + }); + }, [open]); + + const handleTransitionEnd = () => { + setTransitioning(false); + setHeight(open ? "auto" : 0); + }; + + if (!open && !transitioning) { + return null; + } + + return ( +
    +
    {children}
    +
    + ); +} diff --git a/frontend/components/UuidFormItem.tsx b/frontend/components/UuidFormItem.tsx new file mode 100644 index 00000000..91e9aae8 --- /dev/null +++ b/frontend/components/UuidFormItem.tsx @@ -0,0 +1,38 @@ +import { Input, Form } from "antd"; +import { uuidRegex } from "../lib/string"; +import { CSSProperties } from "react"; + +export default function UuidFormItem({ + name, + style, +}: { + name: (string | number)[]; + style?: CSSProperties; +}) { + return ( + value?.toLowerCase()} + rules={[ + { + required: true, + }, + { + whitespace: true, + }, + { + pattern: uuidRegex, + message: + "Invalid UUID format (e.g. 123e4567-e89b-12d3-a456-426614174000)", + }, + ]} + > + + + ); +} diff --git a/frontend/components/data/DataPageHeader.tsx b/frontend/components/data/DataPageHeader.tsx deleted file mode 100644 index d8d27cd1..00000000 --- a/frontend/components/data/DataPageHeader.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Descriptions, Typography } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; -import Link from "next/link"; -import paths from "../../paths"; -import { DataPartialFragment } from "../../queries/data.generated"; -import { ReactNode } from "react"; - -export type DataPageHeaderProps = { - data: DataPartialFragment; - extra?: ReactNode; - children?: ReactNode; -}; - -export default function DataPageHeader({ - data, - extra, - children, -}: DataPageHeaderProps) { - return ( - - - {data.uuid} - - {data.timestamp} - - {data.locale} - - - {data.componentId} - - - - {data.createdAt} - - - - {data.creatorId} - - - {data.warnings} - - - {data.appliedMethod.methodId} - - - - - {data.resourceTree.root.value.uuid} - - - {data.userId != null && ( - - - {data.userId} - - - )} - {children} - - - ); -} diff --git a/frontend/components/data/DataSummary.tsx b/frontend/components/data/DataSummary.tsx new file mode 100644 index 00000000..d3a00555 --- /dev/null +++ b/frontend/components/data/DataSummary.tsx @@ -0,0 +1,316 @@ +import * as graphql from "graphql"; +import { + Breadcrumb, + Divider, + Flex, + Tag, + Tooltip, + Tree, + Typography, +} from "antd"; +import paths from "../../paths"; +import EntitySummary from "../entities/EntitySummary"; +import { + GetHttpsResourcePartialFragment, + DataPartialFragment, + ToTreeVertexAppliedConversionMethodPartialFragment, +} from "../../queries/data.generated"; +import InlineList from "../InlineList"; +import EntityLink from "../entities/EntityLink"; +import { Scalars } from "../../__generated__/graphql"; +import JsonViewer from "../JsonView"; +import { useMemo, useState } from "react"; +import Copyable from "../Copyable"; +import Reference from "../Reference"; +import CodeViewer from "../CodeView"; +import AppliedMethodViewer from "../methods/AppliedMethodView"; +import GnuPgKeyLink from "../GnuPgKeyLink"; +import DateTimeX from "../DateTimeX"; +import { Route } from "next"; + +interface ResourceTreeNode { + key: string; + title: string; + vertexId: string; + parentId?: string; + value: GetHttpsResourcePartialFragment; + appliedConversionMethod?: ToTreeVertexAppliedConversionMethodPartialFragment; + children?: ResourceTreeNode[]; +} + +const transformToTree = (resourceTree: DataPartialFragment["resourceTree"]) => { + const { root, nonRootVertices } = resourceTree; + const childrenMap = new Map< + string, + DataPartialFragment["resourceTree"]["nonRootVertices"][0][] + >(); + nonRootVertices.forEach((vertex) => { + const parentId = vertex.parentId; + if (!childrenMap.has(parentId)) { + childrenMap.set(parentId, []); + } + childrenMap.get(parentId)?.push(vertex); + }); + const buildBranch = ( + node: + | DataPartialFragment["resourceTree"]["nonRootVertices"][0] + | DataPartialFragment["resourceTree"]["root"], + ): ResourceTreeNode => { + const children = childrenMap.get(node.vertexId) || []; + return { + ...node, + key: node.vertexId, + title: node.vertexId, + // node.value.locator || node.value.description || `ID: ${node.vertexId}`, + children: children.map((child) => buildBranch(child)), + }; + }; + return buildBranch(root); +}; + +const getBreadcrumbItems = ( + selectedId: string, + flatNodes: ( + | DataPartialFragment["resourceTree"]["nonRootVertices"][0] + | DataPartialFragment["resourceTree"]["root"] + )[], +) => { + const path = []; + let currentId = selectedId; + while (currentId) { + const node = flatNodes.find((node) => node.vertexId === currentId); + if (!node) break; + path.unshift({ + title: `${node.value.dataFormat?.name ?? node.value.dataFormatId} (${node.vertexId})`, + }); + if (node.__typename === "GetHttpsResourceTreeNonRootVertex") { + currentId = node.parentId; + } else { + break; + } + } + return path; +}; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function DataSummary({ + entity, + route, + children, +}: { + entity: DataPartialFragment; + route: (id: Scalars["Uuid"]["output"]) => Route; + children?: React.ReactNode; +}) { + const rootResource = useMemo( + () => transformToTree(entity.resourceTree), + [entity], + ); + const [selectedResource, setSelectedResource] = useState(rootResource); + const resourceNodes = [ + entity.resourceTree.root, + ...entity.resourceTree.nonRootVertices, + ]; + + return ( + + Updated At + , + + Created At + , + + Locale “{entity.locale}” + , + ]} + > +
    +
    + Data for component{" "} + {entity.component && ( + + )}{" "} + created by{" "} + {" "} + and stored in the{" "} + +
    +
    + Generated by applying the method{" "} + +
    + {entity.warnings.length > 0 && ( +
    + Beware of{" "} + “{item}”} + /> +
    + )} +
    + +
    + {rootResource.children && rootResource.children.length > 0 && ( + setSelectedResource(info.node)} + /> + )} + {selectedResource && ( +
    + {resourceNodes.length > 1 && ( + + )} + {selectedResource.value.description && ( +
    {selectedResource.value.description}
    + )} +
    + Located at{" "} + + + {selectedResource.value.locator} + + +
    +
    + Having the{" "} + + SHA-256 + {" "} + hash value{" "} + as + unique digital fingerprint. Use it after downloading the data to + ensure that it has not been tampered with or gotten corrupted. +
    +
    + Being of the data format{" "} + + {selectedResource.value.archivedFilesMetaInformation?.length > + 0 && ( + <> + It is an archive format and the archive file contains the + files{" "} + ( + + ./{item.path.join("/")}= + + + )} + /> + + )} +
    + {selectedResource.appliedConversionMethod && ( +
    + Generated by applying the conversion method{" "} + +
    + )} +
    + )} +
    + {entity.approvals.length > 0 && ( + <> + +
    + + {entity.approvals.map((approval) => ( +
    + The institution{" "} + {" "} + approved on that the + data satisfies the statement given by the reference + + This is proven by the GnuPG signature + + generated with the institution's GnuPG key{" "} + of the + message + + The message contains the statement and the response of the + query + + with the variables + + against the GraphQL endpoint of the database{" "} + + {entity.database?.locator && ( + <> + {" "} + located at{" "} + + {entity.database.locator} + + + )} + . +
    + ))} +
    +
    + + )} + {children && ( + <> + +
    {children}
    + + )} +
    + ); +} diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index 3e04a127..abaee00a 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,37 +1,30 @@ import { Scalars } from "../../../__generated__/graphql"; import { CalorimetricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import CalorimetricDataSummary from "./CalorimetricDataSummary"; +import QueryToolbar from "../../QueryToolbar"; -export type CalorimetricDataProps = { - calorimetricDataId: Scalars["Uuid"]["input"]; -}; +interface CalorimetricDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function CalorimetricData({ - calorimetricDataId, -}: CalorimetricDataProps) { +export default function CalorimetricData({ id }: CalorimetricDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(CalorimetricDataDocument, { - variables: { - uuid: calorimetricDataId, - }, + variables: queryVariables, }); - const calorimetricData = data?.calorimetricData; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!calorimetricData) { + if (!theData) { return ( - , - // ]} - > - - {calorimetricData.gValues - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {calorimetricData.uValues - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - +
    + + + + +
    ); } diff --git a/frontend/components/data/calorimetric/CalorimetricDataList.tsx b/frontend/components/data/calorimetric/CalorimetricDataList.tsx new file mode 100644 index 00000000..68e90fe4 --- /dev/null +++ b/frontend/components/data/calorimetric/CalorimetricDataList.tsx @@ -0,0 +1,24 @@ +import { CalorimetricDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import CalorimetricDataSummary from "./CalorimetricDataSummary"; +import EntityItem from "../../entities/EntityItem"; + +export default function CalorimetricDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: CalorimetricDataPartialFragment[] | null; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/data/calorimetric/CalorimetricDataSummary.tsx b/frontend/components/data/calorimetric/CalorimetricDataSummary.tsx new file mode 100644 index 00000000..e4fa7a16 --- /dev/null +++ b/frontend/components/data/calorimetric/CalorimetricDataSummary.tsx @@ -0,0 +1,47 @@ +import { isTruthy } from "../../../lib/array"; +import paths from "../../../paths"; +import { CalorimetricDataPartialFragment } from "../../../queries/data.generated"; +import Float, { Unit } from "../../Float"; +import InlineList from "../../InlineList"; +import DataSummary from "../DataSummary"; + +export default function CalorimetricDataSummary({ + entity, +}: { + entity: CalorimetricDataPartialFragment; +}) { + const values = [ + entity.gValues.length > 0 && ( +
    + G-Values{" "} + ( + + )} + /> +
    + ), + entity.uValues.length > 0 && ( +
    + U-Values{" "} + ( + + )} + /> +
    + ), + ].filter(isTruthy); + + return ( + + {values.length > 0 && values} + + ); +} diff --git a/frontend/components/data/calorimetric/PaginatedCalorimetricData.tsx b/frontend/components/data/calorimetric/PaginatedCalorimetricData.tsx new file mode 100644 index 00000000..ad34ca8c --- /dev/null +++ b/frontend/components/data/calorimetric/PaginatedCalorimetricData.tsx @@ -0,0 +1,76 @@ +import { + AllCalorimetricDataDocument, + AllCalorimetricDataQueryVariables, + CalorimetricDataPartialFragment, +} from "../../../queries/data.generated"; +import CalorimetricDataList from "./CalorimetricDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { CalorimetricDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedCalorimetricData({ + where, +}: { + where?: AllCalorimetricDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.calorimetricData} + entitiesQuery={AllCalorimetricDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + { + field: "gValues", + type: "list", + item: { + type: "float", + }, + }, + { + field: "uValues", + type: "list", + item: { + type: "float", + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index d920c025..27ee6931 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -1,37 +1,30 @@ import { Scalars } from "../../../__generated__/graphql"; import { GeometricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import GeometricDataSummary from "./GeometricDataSummary"; +import QueryToolbar from "../../QueryToolbar"; -export type GeometricDataProps = { - geometricDataId: Scalars["Uuid"]["input"]; -}; +interface GeometricDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function GeometricData({ geometricDataId }: GeometricDataProps) { +export default function GeometricData({ id }: GeometricDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(GeometricDataDocument, { - variables: { - uuid: geometricDataId, - }, + variables: queryVariables, }); - - const geometricData = data?.geometricData; - - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!geometricData) { + if (!theData) { return ( - , - // ]} - > - - {geometricData.thicknesses - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - +
    + + + + +
    ); } diff --git a/frontend/components/data/geometric/GeometricDataList.tsx b/frontend/components/data/geometric/GeometricDataList.tsx new file mode 100644 index 00000000..721bebe0 --- /dev/null +++ b/frontend/components/data/geometric/GeometricDataList.tsx @@ -0,0 +1,24 @@ +import { GeometricDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import GeometricDataSummary from "./GeometricDataSummary"; +import EntityItem from "../../entities/EntityItem"; + +export default function GeometricDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: GeometricDataPartialFragment[] | null; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/data/geometric/GeometricDataSummary.tsx b/frontend/components/data/geometric/GeometricDataSummary.tsx new file mode 100644 index 00000000..ff788690 --- /dev/null +++ b/frontend/components/data/geometric/GeometricDataSummary.tsx @@ -0,0 +1,32 @@ +import { isTruthy } from "../../../lib/array"; +import paths from "../../../paths"; +import { GeometricDataPartialFragment } from "../../../queries/data.generated"; +import Float, { Unit } from "../../Float"; +import InlineList from "../../InlineList"; +import DataSummary from "../DataSummary"; + +export default function GeometricDataSummary({ + entity, +}: { + entity: GeometricDataPartialFragment; +}) { + const values = [ + entity.thicknesses.length > 0 && ( +
    + Thicknesses{" "} + ( + + )} + /> +
    + ), + ].filter(isTruthy); + + return ( + + {values.length > 0 && values} + + ); +} diff --git a/frontend/components/data/geometric/PaginatedGeometricData.tsx b/frontend/components/data/geometric/PaginatedGeometricData.tsx new file mode 100644 index 00000000..b9856ab2 --- /dev/null +++ b/frontend/components/data/geometric/PaginatedGeometricData.tsx @@ -0,0 +1,69 @@ +import { + AllGeometricDataDocument, + AllGeometricDataQueryVariables, + GeometricDataPartialFragment, +} from "../../../queries/data.generated"; +import GeometricDataList from "./GeometricDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { GeometricDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedGeometricData({ + where, +}: { + where?: AllGeometricDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.geometricData} + entitiesQuery={AllGeometricDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + { + field: "thicknesses", + type: "list", + item: { + type: "float", + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index 654c05cf..a0bd05c5 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -1,37 +1,30 @@ import { Scalars } from "../../../__generated__/graphql"; import { HygrothermalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import HygrothermalDataSummary from "./HygrothermalDataSummary"; +import QueryToolbar from "../../QueryToolbar"; -export type HygrothermalDataProps = { - hygrothermalDataId: Scalars["Uuid"]["input"]; -}; +interface HygrothermalDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function HygrothermalData({ - hygrothermalDataId, -}: HygrothermalDataProps) { +export default function HygrothermalData({ id }: HygrothermalDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(HygrothermalDataDocument, { - variables: { - uuid: hygrothermalDataId, - }, + variables: queryVariables, }); - const hygrothermalData = data?.hygrothermalData; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!hygrothermalData) { + if (!theData) { return ( - , - // ]} - > - +
    + + + + +
    ); } diff --git a/frontend/components/data/hygrothermal/HygrothermalDataList.tsx b/frontend/components/data/hygrothermal/HygrothermalDataList.tsx new file mode 100644 index 00000000..b5417570 --- /dev/null +++ b/frontend/components/data/hygrothermal/HygrothermalDataList.tsx @@ -0,0 +1,24 @@ +import { HygrothermalDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import HygrothermalDataSummary from "./HygrothermalDataSummary"; +import EntityItem from "../../entities/EntityItem"; + +export default function HygrothermalDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: HygrothermalDataPartialFragment[] | null; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/data/hygrothermal/HygrothermalDataSummary.tsx b/frontend/components/data/hygrothermal/HygrothermalDataSummary.tsx new file mode 100644 index 00000000..f9b3452a --- /dev/null +++ b/frontend/components/data/hygrothermal/HygrothermalDataSummary.tsx @@ -0,0 +1,13 @@ +import paths from "../../../paths"; +import { HygrothermalDataPartialFragment } from "../../../queries/data.generated"; +import DataSummary from "../DataSummary"; + +export default function HygrothermalDataSummary({ + entity, +}: { + entity: HygrothermalDataPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/data/hygrothermal/PaginatedHygrothermalData.tsx b/frontend/components/data/hygrothermal/PaginatedHygrothermalData.tsx new file mode 100644 index 00000000..a5204e8f --- /dev/null +++ b/frontend/components/data/hygrothermal/PaginatedHygrothermalData.tsx @@ -0,0 +1,62 @@ +import { + AllHygrothermalDataDocument, + AllHygrothermalDataQueryVariables, + HygrothermalDataPartialFragment, +} from "../../../queries/data.generated"; +import HygrothermalDataList from "./HygrothermalDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { HygrothermalDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedHygrothermalData({ + where, +}: { + where?: AllHygrothermalDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.hygrothermalData} + entitiesQuery={AllHygrothermalDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/lifeCycle/LifeCycleData.tsx b/frontend/components/data/lifeCycle/LifeCycleData.tsx index 35b4ef17..aaa980f5 100644 --- a/frontend/components/data/lifeCycle/LifeCycleData.tsx +++ b/frontend/components/data/lifeCycle/LifeCycleData.tsx @@ -1,35 +1,30 @@ import { Scalars } from "../../../__generated__/graphql"; import { LifeCycleDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import LifeCycleDataSummary from "./LifeCycleDataSummary"; +import QueryToolbar from "../../QueryToolbar"; -export type LifeCycleDataProps = { - lifeCycleDataId: Scalars["Uuid"]["input"]; -}; +interface LifeCycleDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function LifeCycleData({ lifeCycleDataId }: LifeCycleDataProps) { +export default function LifeCycleData({ id }: LifeCycleDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(LifeCycleDataDocument, { - variables: { - uuid: lifeCycleDataId, - }, + variables: queryVariables, }); - const lifeCycleData = data?.lifeCycleData; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!lifeCycleData) { + if (!theData) { return ( - , - // ]} - > - +
    + + + + +
    ); } diff --git a/frontend/components/data/lifeCycle/LifeCycleDataList.tsx b/frontend/components/data/lifeCycle/LifeCycleDataList.tsx new file mode 100644 index 00000000..1453e030 --- /dev/null +++ b/frontend/components/data/lifeCycle/LifeCycleDataList.tsx @@ -0,0 +1,24 @@ +import { LifeCycleDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import LifeCycleDataSummary from "./LifeCycleDataSummary"; +import EntityItem from "../../entities/EntityItem"; + +export default function LifeCycleDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: LifeCycleDataPartialFragment[] | null; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/data/lifeCycle/LifeCycleDataSummary.tsx b/frontend/components/data/lifeCycle/LifeCycleDataSummary.tsx new file mode 100644 index 00000000..cffef373 --- /dev/null +++ b/frontend/components/data/lifeCycle/LifeCycleDataSummary.tsx @@ -0,0 +1,13 @@ +import paths from "../../../paths"; +import { LifeCycleDataPartialFragment } from "../../../queries/data.generated"; +import DataSummary from "../DataSummary"; + +export default function LifeCycleDataSummary({ + entity, +}: { + entity: LifeCycleDataPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/data/lifeCycle/PaginatedLifeCycleData.tsx b/frontend/components/data/lifeCycle/PaginatedLifeCycleData.tsx new file mode 100644 index 00000000..ee6c810b --- /dev/null +++ b/frontend/components/data/lifeCycle/PaginatedLifeCycleData.tsx @@ -0,0 +1,62 @@ +import { + AllLifeCycleDataDocument, + AllLifeCycleDataQueryVariables, + LifeCycleDataPartialFragment, +} from "../../../queries/data.generated"; +import LifeCycleDataList from "./LifeCycleDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { LifeCycleDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedLifeCycleData({ + where, +}: { + where?: AllLifeCycleDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.lifeCycleData} + entitiesQuery={AllLifeCycleDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 6581fd98..0fefe975 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,35 +1,31 @@ import { Scalars } from "../../../__generated__/graphql"; import { OpticalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Descriptions, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import OpticalDataSummary from "./OpticalDataSummary"; +import OpticalDataRibbon from "./OpticalDataRibbon"; +import QueryToolbar from "../../QueryToolbar"; -export type OpticalDataProps = { - opticalDataId: Scalars["Uuid"]["input"]; -}; +interface OpticalDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function OpticalData({ opticalDataId }: OpticalDataProps) { +export default function OpticalData({ id }: OpticalDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(OpticalDataDocument, { - variables: { - uuid: opticalDataId, - }, + variables: queryVariables, }); - const opticalData = data?.opticalData; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!opticalData) { + if (!theData) { return ( - , - // ]} - > - - {opticalData.nearnormalHemisphericalVisibleTransmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {opticalData.nearnormalHemisphericalVisibleReflectances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {opticalData.nearnormalHemisphericalSolarTransmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {opticalData.nearnormalHemisphericalSolarReflectances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {opticalData.infraredEmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {opticalData.colorRenderingIndices - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - +
    + + + + + + +
    ); } diff --git a/frontend/components/data/optical/OpticalDataList.tsx b/frontend/components/data/optical/OpticalDataList.tsx new file mode 100644 index 00000000..07685612 --- /dev/null +++ b/frontend/components/data/optical/OpticalDataList.tsx @@ -0,0 +1,27 @@ +import { OpticalDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import OpticalDataSummary from "./OpticalDataSummary"; +import EntityItem from "../../entities/EntityItem"; +import OpticalDataRibbon from "./OpticalDataRibbon"; + +export default function OpticalDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: OpticalDataPartialFragment[] | null; +}) { + return ( + ( + + + + + + )} + /> + ); +} diff --git a/frontend/components/data/optical/OpticalDataRibbon.tsx b/frontend/components/data/optical/OpticalDataRibbon.tsx new file mode 100644 index 00000000..9da55bc5 --- /dev/null +++ b/frontend/components/data/optical/OpticalDataRibbon.tsx @@ -0,0 +1,26 @@ +import { Badge } from "antd"; +import { + OpticalComponentSubtype, + OpticalComponentType, +} from "../../../__generated__/graphql"; + +export default function OpticalDataRibbon({ + type, + subtype, + children, +}: { + type?: OpticalComponentType | null; + subtype?: OpticalComponentSubtype | null; + children: React.ReactNode; +}) { + return type == null && subtype == null ? ( + children + ) : ( + + {children} + + ); +} diff --git a/frontend/components/data/optical/OpticalDataSummary.tsx b/frontend/components/data/optical/OpticalDataSummary.tsx new file mode 100644 index 00000000..317eab1f --- /dev/null +++ b/frontend/components/data/optical/OpticalDataSummary.tsx @@ -0,0 +1,106 @@ +import { OpticalDataPartialFragment } from "../../../queries/data.generated"; +import Float, { Unit } from "../../Float"; +import InlineList from "../../InlineList"; +import DataSummary from "../DataSummary"; +import CielabColorView from "../../CielabColorView"; +import { CoatedSide } from "../../../__generated__/graphql"; +import { isTruthy } from "../../../lib/array"; +import { humanize } from "../../../lib/string"; +import paths from "../../../paths"; + +export default function OpticalDataSummary({ + entity, +}: { + entity: OpticalDataPartialFragment; +}) { + const values = [ + entity.coatedSide && entity.coatedSide != CoatedSide.NotApplicable && ( +
    + Coated Side "{humanize(entity.coatedSide, "all-upper")}" +
    + ), + entity.infraredEmittances.length > 0 && ( +
    + Infrared Emittances{" "} + ( + + )} + /> +
    + ), + entity.nearnormalHemisphericalSolarReflectances.length > 0 && ( +
    + Near-Normal Hemispherical Solar Reflectances{" "} + ( + + )} + /> +
    + ), + entity.nearnormalHemisphericalSolarTransmittances.length > 0 && ( +
    + Near-Normal Hemispherical Solar Transmittances{" "} + ( + + )} + /> +
    + ), + entity.nearnormalHemisphericalVisibleReflectances.length > 0 && ( +
    + Near-Normal Hemispherical Visible Reflectances{" "} + ( + + )} + /> +
    + ), + entity.nearnormalHemisphericalVisibleTransmittances.length > 0 && ( +
    + Near-Normal Hemispherical Visible Transmittances{" "} + ( + + )} + /> +
    + ), + entity.cielabColors.length > 0 && ( +
    + CIELAB Color Space{" "} + ( + + )} + /> +
    + ), + entity.colorRenderingIndices.length > 0 && ( +
    + Color Rendering Indices{" "} + ( + + )} + /> +
    + ), + ].filter(isTruthy); + + return ( + + {values.length > 0 && values} + + ); +} diff --git a/frontend/components/data/optical/PaginatedOpticalData.tsx b/frontend/components/data/optical/PaginatedOpticalData.tsx new file mode 100644 index 00000000..78af6f2b --- /dev/null +++ b/frontend/components/data/optical/PaginatedOpticalData.tsx @@ -0,0 +1,140 @@ +import { + AllOpticalDataDocument, + AllOpticalDataQueryVariables, + OpticalDataPartialFragment, +} from "../../../queries/data.generated"; +import OpticalDataList from "./OpticalDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { OpticalDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedOpticalData({ + where, +}: { + where?: AllOpticalDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.opticalData} + entitiesQuery={AllOpticalDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + // { + // field: "type", + // type: "enum", + // enumObject: OpticalComponentType, + // }, + // { + // field: "subtype", + // type: "enum", + // enumObject: OpticalComponentSubtype, + // }, + // { + // field: "coatedSide", + // type: "enum", + // enumObject: CoatedSide, + // }, + { + field: "nearnormalHemisphericalSolarReflectances", + type: "list", + item: { + type: "float", + }, + }, + { + field: "nearnormalHemisphericalSolarTransmittances", + type: "list", + item: { + type: "float", + }, + }, + { + field: "nearnormalHemisphericalVisibleReflectances", + type: "list", + item: { + type: "float", + }, + }, + { + field: "nearnormalHemisphericalVisibleTransmittances", + type: "list", + item: { + type: "float", + }, + }, + { + field: "infraredEmittances", + type: "list", + item: { + type: "float", + }, + }, + { + field: "cielabColors", + type: "list", + item: { + type: "object", + items: [ + { + field: "lStar", + type: "float", + }, + { + field: "aStar", + type: "float", + }, + { + field: "bStar", + type: "float", + }, + ], + }, + }, + { + field: "colorRenderingIndices", + type: "list", + item: { + type: "float", + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/photovoltaic/PaginatedPhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PaginatedPhotovoltaicData.tsx new file mode 100644 index 00000000..f28e0b91 --- /dev/null +++ b/frontend/components/data/photovoltaic/PaginatedPhotovoltaicData.tsx @@ -0,0 +1,62 @@ +import { + AllPhotovoltaicDataDocument, + AllPhotovoltaicDataQueryVariables, + PhotovoltaicDataPartialFragment, +} from "../../../queries/data.generated"; +import PhotovoltaicDataList from "./PhotovoltaicDataList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { PhotovoltaicDataPropositionInput } from "../../../__generated__/graphql"; +import paths from "../../../paths"; + +export default function PaginatedPhotovoltaicData({ + where, +}: { + where?: AllPhotovoltaicDataQueryVariables["where"]; +}) { + return ( + + showJump + route={paths.photovoltaicData} + entitiesQuery={AllPhotovoltaicDataDocument} + baseWhere={where} + list={(props) => } + filterDefinitions={[ + { + field: "componentId", + type: "uuid", + }, + { + field: "resources", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + { + field: "archivedFilesMetaInformation", + type: "list", + item: { + type: "object", + items: [ + { + field: "dataFormatId", + type: "uuid", + }, + ], + }, + }, + ], + }, + }, + ]} + sortDefinitions={[]} + /> + ); +} diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 9630aceb..1ece9281 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -1,37 +1,30 @@ import { Scalars } from "../../../__generated__/graphql"; import { PhotovoltaicDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, App } from "antd"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../../../lib/apollo"; -import DataPageHeader from "../DataPageHeader"; +import { Skeleton, Result, Card } from "antd"; import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; +import PhotovoltaicDataSummary from "./PhotovoltaicDataSummary"; +import QueryToolbar from "../../QueryToolbar"; -export type PhotovoltaicDataProps = { - photovoltaicDataId: Scalars["Uuid"]["input"]; -}; +interface PhotovoltaicDataProps { + id: Scalars["Uuid"]["input"]; +} -export default function PhotovoltaicData({ - photovoltaicDataId, -}: PhotovoltaicDataProps) { +export default function PhotovoltaicData({ id }: PhotovoltaicDataProps) { + const queryVariables = { + id, + }; const { loading, error, data } = useQuery(PhotovoltaicDataDocument, { - variables: { - uuid: photovoltaicDataId, - }, + variables: queryVariables, }); - const photovoltaicData = data?.photovoltaicData; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); + useQueryHandler({ error }); + const theData = data?.data; if (loading) { return ; } - if (!photovoltaicData) { + if (!theData) { return ( - , - // ]} - > - +
    + + + + +
    ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicDataList.tsx b/frontend/components/data/photovoltaic/PhotovoltaicDataList.tsx new file mode 100644 index 00000000..01a71e6d --- /dev/null +++ b/frontend/components/data/photovoltaic/PhotovoltaicDataList.tsx @@ -0,0 +1,24 @@ +import { PhotovoltaicDataPartialFragment } from "../../../queries/data.generated"; +import EntityList from "../../entities/EntityList"; +import PhotovoltaicDataSummary from "./PhotovoltaicDataSummary"; +import EntityItem from "../../entities/EntityItem"; + +export default function PhotovoltaicDataList({ + loading, + nodes, +}: { + loading: boolean; + nodes: PhotovoltaicDataPartialFragment[] | null; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/data/photovoltaic/PhotovoltaicDataSummary.tsx b/frontend/components/data/photovoltaic/PhotovoltaicDataSummary.tsx new file mode 100644 index 00000000..7df6538a --- /dev/null +++ b/frontend/components/data/photovoltaic/PhotovoltaicDataSummary.tsx @@ -0,0 +1,13 @@ +import paths from "../../../paths"; +import { PhotovoltaicDataPartialFragment } from "../../../queries/data.generated"; +import DataSummary from "../DataSummary"; + +export default function PhotovoltaicDataSummary({ + entity, +}: { + entity: PhotovoltaicDataPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/databases/Database.tsx b/frontend/components/databases/Database.tsx index 32f48182..c8b3919f 100644 --- a/frontend/components/databases/Database.tsx +++ b/frontend/components/databases/Database.tsx @@ -1,24 +1,17 @@ -import { useQuery } from "@apollo/client/react"; import { DatabaseDocument } from "../../queries/databases.generated"; -import { Skeleton, Result, Descriptions, Typography, Tag, App } from "antd"; -import { PageHeader } from "@ant-design/pro-layout"; -import { ReactNode, useEffect } from "react"; -import { stringifyApolloError } from "../../lib/apollo"; -import UpdateDatabase from "./UpdateDatabase"; -import { DatabaseVerificationState } from "../../__generated__/graphql"; - -export type DatabaseProps = {}; +import { Skeleton, Result, Card } from "antd"; +import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../lib/hooks/useQueryHandler"; +import DatabaseSummary from "./DatabaseSummary"; +import QueryToolbar from "../QueryToolbar"; -export default function Database({}: DatabaseProps) { - const { loading, error, data } = useQuery(DatabaseDocument); +export default function Database() { + const queryVariables = {}; + const { loading, error, data } = useQuery(DatabaseDocument, { + variables: queryVariables, + }); + useQueryHandler({ error }); const database = data?.database; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); if (loading) { return ; @@ -35,52 +28,11 @@ export default function Database({}: DatabaseProps) { } return ( - <> - , - ] - : [], - )} - tags={[ - - {database.verificationState} - , - ]} - backIcon={false} - > - - {database.uuid} - - - {database.locator} - - - - - {database.isAuthorizedToVerifyNode && - database.verificationState == DatabaseVerificationState.Pending && ( - - Have your database's GraphQL endpoint return the verification - code “ - {database.verificationCode}” (without the quotation marks), - when queried for the GraphQL query “verificationCode”. - Then, press the “Verify” button above to make the - metabase assert that the verification codes match which proves that - you control the GraphQL endpoint {database.locator}. Verified - databases are publicly listed and included in data searches. - - )} - +
    + + + + +
    ); } diff --git a/frontend/components/databases/DatabaseSummary.tsx b/frontend/components/databases/DatabaseSummary.tsx new file mode 100644 index 00000000..1eb71657 --- /dev/null +++ b/frontend/components/databases/DatabaseSummary.tsx @@ -0,0 +1,86 @@ +import { Typography } from "antd"; +import paths from "../../paths"; +import { + DatabasesPartialFragment, + DatabasePartialFragment, +} from "../../queries/databases.generated"; +import EntityLink from "../entities/EntityLink"; +import EntitySummary from "../entities/EntitySummary"; +import { DatabaseVerificationState } from "../../__generated__/graphql"; +import { isTruthy } from "../../lib/array"; +import UpdateDatabase from "./UpdateDatabase"; +// import VerifyDatabase from "./VerifyDatabase"; +import EnumTag from "../EnumTag"; +import CodeView from "../CodeView"; + +export default function DatabaseSummary({ + entity, + hideInputControls = false, +}: { + entity: DatabasesPartialFragment | DatabasePartialFragment; + hideInputControls?: boolean; +}) { + return ( + paths.database} + tags={[ + + {entity.verificationState} + , + ]} + extra={[ + // !hideInputControls && + // "isAuthorizedToVerifyNode" in entity && + // entity.isAuthorizedToVerifyNode && + // entity.verificationState == DatabaseVerificationState.Pending && ( + // + // ), + !hideInputControls && + "isAuthorizedToUpdateNode" in entity && + entity.isAuthorizedToUpdateNode && ( + + ), + ].filter(isTruthy)} + > +
    + Operated by{" "} + +
    +
    + + {entity.locator} + +
    + {"isAuthorizedToVerifyNode" in entity && + entity.isAuthorizedToVerifyNode && + entity.verificationState == DatabaseVerificationState.Pending && ( + + Have your database's GraphQL endpoint return the verification + code + + when queried for the GraphQL query + + Then, press the “Verify” button{" "} + {!hideInputControls ? ( + "above" + ) : ( + <> + on paths.database} /> + + )}{" "} + to make the metabase assert that the verification codes match which + proves that you control the GraphQL endpoint{" "} + + {entity.locator} + + . Verified databases are publicly listed and included in data + searches. + + )} +
    + ); +} diff --git a/frontend/components/databases/UpdateDatabase.tsx b/frontend/components/databases/UpdateDatabase.tsx index babdb4cf..9417ea6d 100644 --- a/frontend/components/databases/UpdateDatabase.tsx +++ b/frontend/components/databases/UpdateDatabase.tsx @@ -2,93 +2,64 @@ import { useMutation } from "@apollo/client/react"; import { Form, Input, Button, Modal } from "antd"; import { UpdateDatabaseDocument, - DatabaseDocument, + UpdateDatabaseMutation, + DatabasePartialFragment, } from "../../queries/databases.generated"; import { Scalars } from "../../__generated__/graphql"; import { useState } from "react"; -import { handleFormErrors } from "../../lib/form"; +import { layout, tailLayout } from "../../lib/form"; +import { useMutationHandler } from "../../lib/hooks/useMutationHandler"; import ErrorAlert from "../ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; +import EditButton from "../EditButton"; type FormValues = { - newName: string; - newDescription: string; - newLocator: Scalars["Url"]["input"]; -}; - -export type UpdateDatabaseProps = { - databaseId: Scalars["Uuid"]["input"]; name: string; description: string; locator: Scalars["Url"]["input"]; }; -export default function UpdateDatabase({ - databaseId, - name, - description, - locator, -}: UpdateDatabaseProps) { +interface UpdateDatabaseProps { + database: DatabasePartialFragment; +} + +export default function UpdateDatabase({ database }: UpdateDatabaseProps) { const [open, setOpen] = useState(false); - const [updateDatabaseMutation] = useMutation(UpdateDatabaseDocument, { - // TODO Update the cache more efficiently as explained on https://www.apollographql.com/docs/react/caching/cache-interaction/ and https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates - // See https://www.apollographql.com/docs/react/data/mutations/#options - refetchQueries: [ - { - query: DatabaseDocument, - variables: { - uuid: databaseId, - }, - }, - ], - }); const [globalErrorMessages, setGlobalErrorMessages] = useState( new Array(), ); const [form] = Form.useForm(); - const [updating, setUpdating] = useState(false); - const onFinish = ({ newName, newDescription, newLocator }: FormValues) => { - const update = async () => { - try { - setUpdating(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const { error, data } = await updateDatabaseMutation({ + const [updateDatabaseMutation] = useMutation(UpdateDatabaseDocument); + + const { mutating, withMutationHandler, augmentFormWithErrors } = + useMutationHandler({ + getErrors: (data) => data.updateDatabase.errors, + }); + + const onFinish = (values: FormValues) => { + withMutationHandler( + () => + updateDatabaseMutation({ variables: { - databaseId: databaseId, - name: newName, - description: newDescription, - locator: newLocator, + input: { + databaseId: database.uuid, + name: values.name, + description: values.description, + locator: values.locator, + }, }, - }); - handleFormErrors( - error, - data?.updateDatabase?.errors?.map((x) => { - return { code: x.code, message: x.message, path: x.path }; - }), - setGlobalErrorMessages, - form, - ); - if (!error && !data?.updateDatabase?.errors) - data?.updateDatabase?.database; - { + }), + { + onSuccess: () => { + setGlobalErrorMessages([]); setOpen(false); - } - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setUpdating(false); - } - }; - update(); + }, + onError: (graphQlErrors, userErrors) => + setGlobalErrorMessages( + augmentFormWithErrors(graphQlErrors, userErrors, form), + ), + }, + ); }; const onFinishFailed = () => { @@ -97,12 +68,16 @@ export default function UpdateDatabase({ return ( <> - + setOpen(true)} /> setOpen(false)} + onCancel={() => { + setGlobalErrorMessages([]); + form.resetFields(); + setOpen(false); + }} footer={false} > @@ -115,31 +90,31 @@ export default function UpdateDatabase({ > - diff --git a/frontend/components/databases/VerifyDatabase.tsx b/frontend/components/databases/VerifyDatabase.tsx new file mode 100644 index 00000000..c7fd3598 --- /dev/null +++ b/frontend/components/databases/VerifyDatabase.tsx @@ -0,0 +1,47 @@ +// import { useMutation } from "@apollo/client/react"; +// import { Button } from "antd"; +// import { +// VerifyDatabaseDocument, +// DatabasesDocument, +// PendingDatabasesDocument, +// VerifyDatabaseMutation, +// } from "../../queries/databases.generated"; +// import { Scalars } from "../../__generated__/graphql"; +// import { useMutationHandler } from "../../lib/hooks/useMutationHandler"; +// +// interface VerifyDatabaseProps { +// databaseId: Scalars["Uuid"]["input"]; +// } +// +// export default function VerifyDatabase({ databaseId }: VerifyDatabaseProps) { +// const [verifyDatabaseMutation] = useMutation(VerifyDatabaseDocument, { +// refetchQueries: [DatabasesDocument, PendingDatabasesDocument], +// }); +// +// const { mutating, withMutationHandler, messageErrors } = +// useMutationHandler({ +// getErrors: (data) => data.verifyDatabase.errors, +// }); +// +// const mutate = async () => { +// withMutationHandler( +// () => +// verifyDatabaseMutation({ +// variables: { +// input: { +// databaseId: databaseId, +// }, +// }, +// }), +// { +// onError: messageErrors, +// }, +// ); +// }; +// +// return ( +// +// ); +// } diff --git a/frontend/components/entities/EntityItem.tsx b/frontend/components/entities/EntityItem.tsx new file mode 100644 index 00000000..93584dc2 --- /dev/null +++ b/frontend/components/entities/EntityItem.tsx @@ -0,0 +1,10 @@ +import { Card } from "antd"; +import React from "react"; + +export default function EntityItem({ + children, +}: { + children?: React.ReactNode; +}) { + return {children}; +} diff --git a/frontend/components/entities/EntityLink.tsx b/frontend/components/entities/EntityLink.tsx new file mode 100644 index 00000000..44f385e7 --- /dev/null +++ b/frontend/components/entities/EntityLink.tsx @@ -0,0 +1,31 @@ +import { Tooltip } from "antd"; +import { Scalars } from "../../__generated__/graphql"; +import { Route } from "next"; +import Link from "next/link"; +import Copyable from "../Copyable"; + +export default function EntityLink({ + entity, + route, +}: { + entity: { + uuid: Scalars["Uuid"]["output"]; + name: string; + }; + route: (id: Scalars["Uuid"]["output"]) => Route; +}) { + return ( + } + styles={{ + container: { + whiteSpace: "nowrap", + minWidth: "max-content", + maxWidth: "none", + }, + }} + > + {entity.name} + + ); +} diff --git a/frontend/components/entities/EntityList.tsx b/frontend/components/entities/EntityList.tsx new file mode 100644 index 00000000..21818776 --- /dev/null +++ b/frontend/components/entities/EntityList.tsx @@ -0,0 +1,58 @@ +import { Card, Empty, Flex, Skeleton } from "antd"; +import { Scalars } from "../../__generated__/graphql"; +import { useState } from "react"; +import { initialPageSize } from "../Pagination"; +import { range } from "../../lib/array"; +import { usePreviousNonNull } from "../../lib/hooks/usePreviousNonNull"; + +export default function EntityList< + TEntity extends { uuid: Scalars["Uuid"]["output"] }, +>({ + loading, + dataSource, + renderItem, +}: { + loading: boolean; + dataSource: TEntity[] | null; + renderItem: (entity: TEntity) => React.ReactNode; +}) { + const [hasLoadedOnce, setHasLoadedOnce] = useState(false); + const previousDataSource = usePreviousNonNull(dataSource); + const currentOrPreviousDataSource = dataSource ?? previousDataSource ?? []; + + if (!hasLoadedOnce && !loading) { + setHasLoadedOnce(true); + } + + if (loading && !hasLoadedOnce) { + return ( + + {range(1, initialPageSize).map((key) => ( + + + + ))} + + ); + } + + if (!loading && currentOrPreviousDataSource.length === 0) { + return ; + } + + return ( + + {currentOrPreviousDataSource.map((entity) => ( +
    {renderItem(entity)}
    + ))} +
    + ); +} diff --git a/frontend/components/entities/EntitySummary.tsx b/frontend/components/entities/EntitySummary.tsx new file mode 100644 index 00000000..e4bc37f9 --- /dev/null +++ b/frontend/components/entities/EntitySummary.tsx @@ -0,0 +1,114 @@ +import { Breadcrumb, Button, Flex, Space, Typography } from "antd"; +import { ArrowLeftOutlined, PushpinOutlined } from "@ant-design/icons"; +import { Scalars } from "../../__generated__/graphql"; +import Copyable from "../Copyable"; +import Id from "../Id"; +import CopyButton from "../CopyButton"; +import { Route } from "next"; +import Link from "next/link"; + +interface Props { + entity: { + uuid: Scalars["Uuid"]["output"]; + name?: string | null; + abbreviation?: string | null; + description?: string | null; + }; + route?: (id: Scalars["Uuid"]["output"]) => Route; + tags?: React.ReactNode[]; + onBack?: () => void; + extra?: React.ReactNode; + breadcrumb?: { title: string; href?: string }[]; + children?: React.ReactNode; +} + +export default function EntitySummary({ + entity, + route, + tags, + onBack, + extra, + breadcrumb, + children, +}: Props) { + return ( +
    + {breadcrumb && ( + ({ + title: item.title, + href: item.href, + }))} + style={{ marginBottom: 12 }} + /> + )} + + {onBack && ( +
    + ); +} diff --git a/frontend/components/entities/PaginatedEntities.tsx b/frontend/components/entities/PaginatedEntities.tsx new file mode 100644 index 00000000..42ee5b75 --- /dev/null +++ b/frontend/components/entities/PaginatedEntities.tsx @@ -0,0 +1,379 @@ +import { FilterOutlined, PlusOutlined } from "@ant-design/icons"; +import { Button, Card, Flex, Form, Space } from "antd"; +import { + QueryDocument, + QueryVariables, + usePaginatedQuery, +} from "../../lib/hooks/usePaginatedQuery"; +import Pagination from "../Pagination"; +import { useMemo, useRef, useState } from "react"; +import ActiveFilterAndSortBar from "../ActiveFilterAndSortBar"; +import { + ObjectFilterState, + FilterDefinition, + toWhereClause, + getInitialFilterSubformValues, + ObjectFilterDefinition, +} from "../../lib/filter"; +import { + SortState, + SortDefinition, + toOrderClause, + initialSortSubformValues, +} from "../../lib/sort"; +import JumpToId, { JumpToIdProps } from "../JumpToId"; +import { Scalars, SortEnumType } from "../../__generated__/graphql"; +import { Route } from "next"; +import DeleteButton from "../DeleteButton"; +import ObjectFilterSubform from "../filtering/ObjectFilterSubform"; +import SortSubform from "../sorting/SortSubform"; +import QueryToolbar from "../QueryToolbar"; +import SlideDown from "../SlideDown"; +import { notEmpty } from "../../lib/array"; + +const reduceWhere = (where: { and: TPropositionInput[] }) => + where.and.length == 0 ? null : where.and.length == 1 ? where.and[0] : where; + +const reduceOrder = ( + order: TSortInput | TSortInput[] | null | undefined, +) => { + if (!Array.isArray(order)) return order; + return order.length == 0 ? null : order.length == 1 ? order[0] : order; +}; + +const reduceClauses = (clauses: { + where: { and: TPropositionInput[] }; + order?: TSortInput | TSortInput[] | null | undefined; +}) => { + var where = reduceWhere(clauses.where); + var order = reduceOrder(clauses.order); + return { + ...(where !== null && { where }), + ...(order !== null && { order }), + }; +}; + +type FilterAndSortFormValues = { + filters: ObjectFilterState[] | null | undefined; + sorts: SortState[] | null | undefined; +}; + +type BaseProps< + TNode, + TPropositionInput extends PropositionInput, + TSortInput extends SortInput, +> = { + entitiesQuery: QueryDocument; + extra?: React.ReactNode; + list: (props: { loading: boolean; nodes: TNode[] | null }) => React.ReactNode; + filterDefinitions: readonly FilterDefinition[]; + sortDefinitions: readonly SortDefinition[]; + baseWhere?: TPropositionInput | null; + defaultOrder?: TSortInput[] | TSortInput | null; + loading?: boolean; +}; + +type Props< + TNode, + TPropositionInput extends PropositionInput, + TSortInput extends SortInput, +> = + | (BaseProps & { + showJump: false; + }) + | (BaseProps & { + showJump: true; + route: (id: Scalars["Uuid"]["output"]) => Route; + namesQuery?: JumpToIdProps["query"]; + }); + +type PropositionInput = + | { + and?: (PropositionInput | undefined)[] | null; + or?: (PropositionInput | undefined)[] | null; + [x: string]: { [x: string]: any | null | undefined } | null | undefined; + } + | null + | undefined; + +type SortInput = + | { [x: string]: SortInput | SortEnumType | null | undefined } + | { [x: string]: SortInput | SortEnumType | null | undefined }[] + | null + | undefined; + +export default function PaginatedEntities< + TNode, + TPropositionInput extends PropositionInput, + TSortInput extends SortInput, +>(props: Props) { + // const router = useRouter(); + const [form] = Form.useForm(); + const [filters, setFilters] = useState([]); + const [sorts, setSorts] = useState([]); + const [isFilterAndSortOpen, setIsFilterAndSortOpen] = useState(false); + const [paginatedQueryVariables, setPaginatedQueryVariables] = + useState | null>(null); + const filterFormListRemoveRef = + useRef<(index: number | number[]) => void | null>(null); + const sortFormListRemoveRef = + useRef<(index: number | number[]) => void | null>(null); + + const applyFiltersAndSortsFormValues = (values: FilterAndSortFormValues) => { + setFilters(values.filters ?? []); + setSorts(values.sorts ?? []); + // setIsFilterAndSortOpen(false); + }; + const clearFiltersAndSortsForm = () => { + // form.resetFields(); + form.setFieldsValue({ filters: [], sorts: [] }); + // setIsFilterAndSortOpen(false); + }; + + const removeFilter = (index: number) => { + filterFormListRemoveRef.current?.(index); + const newFilters = filters.filter((_, i) => i !== index); + setFilters(newFilters); + setIsFilterAndSortOpen( + (current) => current && (newFilters.length > 0 || sorts.length > 0), + ); + }; + const removeSort = (index: number) => { + sortFormListRemoveRef.current?.(index); + const newSorts = sorts.filter((_, i) => i !== index); + setSorts(newSorts); + setIsFilterAndSortOpen( + (current) => current && (filters.length > 0 || newSorts.length > 0), + ); + }; + const removeAllFiltersAndSorts = () => { + // form.resetFields(); + form.setFieldsValue({ filters: [], sorts: [] }); + setFilters([]); + setSorts([]); + setIsFilterAndSortOpen(false); + }; + + // useEffect(() => { + // if (!router.isReady) return; + // const { filters, sorts } = router.query; + // form.setFieldsValue({ + // filters: filters ? JSON.parse(filters as string) : [], + // sorts: sorts ? JSON.parse(sorts as string) : [], + // }); + // }, [router.isReady, router.query, form]); + + // const handleValuesChange = (_: any, values: FilterAndSortFormValues) => { + // const { filters, sorts } = values; + // router.push( + // { + // pathname: router.pathname, + // query: { + // ...router.query, + // filters: JSON.stringify(filters), + // sorts: JSON.stringify(sorts), + // }, + // }, + // undefined, + // { shallow: true }, // Prevents calling getServerSideProps again + // ); + // }; + + const queryVariables = useMemo( + () => + reduceClauses({ + where: { + and: [ + props.baseWhere, + ...filters.map((filter) => + toWhereClause(filter, props.filterDefinitions), + ), + ].filter(notEmpty), + }, + order: + sorts.length == 0 + ? props.defaultOrder + : sorts.map((sort) => toOrderClause(sort, props.sortDefinitions)), + }), + [filters, sorts], + ); + const { loading, nodes, paginationProps } = usePaginatedQuery( + props.entitiesQuery, + queryVariables, + setPaginatedQueryVariables, + ); + + const objectFilterDefinition: ObjectFilterDefinition = { + type: "object", + field: null, + items: props.filterDefinitions as readonly FilterDefinition[], + }; + + const jump = props.showJump && ( + + ); + + return ( +
    + + {props.extra != null && jump} + + {props.extra || jump ? (props.extra ?? jump) :
    } + + + + +
    + + {props.filterDefinitions.length > 0 && ( +
    +

    Filter by

    + + {(fields, { add, remove }, { errors }) => { + filterFormListRemoveRef.current = remove; + return ( + + + {fields.map(({ key, name }) => { + return ( + + + remove(name)} + /> + + ); + })} + + + ); + }} + +
    + )} + {props.sortDefinitions.length > 0 && ( +
    +

    Sort by

    + + {(fields, { add, remove }, { errors }) => { + sortFormListRemoveRef.current = remove; + return ( + + + {fields.map(({ key, name }) => { + return ( + + + remove(name)} + /> + + ); + })} + + + ); + }} + +
    + )} +
    + + + Clear + + + + +
    +
    + +
    +
    + {(filters.length > 0 || sorts.length > 0) && ( + + )} + {props.list({ loading: props.loading || loading, nodes })} + + + + + +
    + ); +} diff --git a/frontend/components/filtering/BaseFilterSubform.tsx b/frontend/components/filtering/BaseFilterSubform.tsx new file mode 100644 index 00000000..54cf23b4 --- /dev/null +++ b/frontend/components/filtering/BaseFilterSubform.tsx @@ -0,0 +1,56 @@ +import { Form, Select, Space } from "antd"; +import React from "react"; +import { + getFilterOperatorLabel, + isFilterOperatorSingle, + FilterOperator, +} from "../../lib/filter"; + +type NarrowedOperator = FilterOperator & keyof TPropositionInput; + +export default function BaseFilterSubform({ + name, + ancestors, + operators, + renderSingleFormItem, + renderMultipleFormItem, + initialOperator, +}: { + _type?: TPropositionInput; + name: readonly (string | number)[]; + ancestors: readonly (string | number)[]; + operators: readonly NarrowedOperator[]; + renderSingleFormItem: (props: { + name: (string | number)[]; + }) => React.ReactNode; + renderMultipleFormItem: (props: { + name: (string | number)[]; + }) => React.ReactNode; + initialOperator: number; +}) { + const form = Form.useFormInstance(); + const currentOperator = + (Form.useWatch( + [...ancestors, ...name, "operator"], + form, + ) as NarrowedOperator) ?? operators[initialOperator]; + + return ( + + + { + if ( + !/[0-9.-]/.test(e.key) && + e.key !== "Enter" && + e.key !== "Tab" && + e.key !== "Backspace" + ) { + e.preventDefault(); + } + }} + style={{ width: "100%" }} + /> + + )} + /> + ); +} diff --git a/frontend/components/filtering/IntFilterSubform.tsx b/frontend/components/filtering/IntFilterSubform.tsx new file mode 100644 index 00000000..bf6b4ee2 --- /dev/null +++ b/frontend/components/filtering/IntFilterSubform.tsx @@ -0,0 +1,77 @@ +import { Form, InputNumber, Select } from "antd"; +import BaseFilterSubform from "./BaseFilterSubform"; +import { IntPropositionInput } from "../../__generated__/graphql"; +import { FilterTypeMap } from "../../lib/filter"; + +export default function IntFilterSubform({ + name, + ancestors, +}: { + name: readonly (string | number)[]; + ancestors: readonly (string | number)[]; +}) { + return ( + + name={name} + ancestors={ancestors} + operators={FilterTypeMap["int"].operators} + initialOperator={FilterTypeMap["int"].initialOperator} + renderSingleFormItem={(props) => ( + + + + )} + renderMultipleFormItem={(props) => ( + + value ? value.map((v) => parseInt(v)).filter((v) => !isNaN(v)) : [] + } + rules={[ + { + required: true, + }, + { + validator: (_, value) => { + if (value?.some((v: any) => isNaN(parseInt(v)))) { + return Promise.reject( + new Error("All entries must be valid integers"), + ); + } + return Promise.resolve(); + }, + }, + ]} + > + ({ + value: index, + label: getLabel(value, "all-upper"), + }))} + /> + + + + + + ); +} diff --git a/frontend/components/filtering/StringFilterSubform.tsx b/frontend/components/filtering/StringFilterSubform.tsx new file mode 100644 index 00000000..08175527 --- /dev/null +++ b/frontend/components/filtering/StringFilterSubform.tsx @@ -0,0 +1,52 @@ +import { Input, Select, Form } from "antd"; +import BaseFilterSubform from "./BaseFilterSubform"; +import { StringPropositionInput } from "../../__generated__/graphql"; +import { FilterTypeMap } from "../../lib/filter"; + +export default function StringFilterSubform< + TPropositionInput extends StringPropositionInput, +>({ + name, + ancestors, +}: { + name: readonly (string | number)[]; + ancestors: readonly (string | number)[]; +}) { + return ( + + name={name} + ancestors={ancestors} + operators={FilterTypeMap["string"].operators} + initialOperator={FilterTypeMap["string"].initialOperator} + renderSingleFormItem={(props) => ( + + + + )} + renderMultipleFormItem={(props) => ( + + + + )} + renderMultipleFormItem={(props) => ( + + + + )} + /> + ); +} diff --git a/frontend/components/methods/AppliedMethodView.tsx b/frontend/components/methods/AppliedMethodView.tsx new file mode 100644 index 00000000..db959a5d --- /dev/null +++ b/frontend/components/methods/AppliedMethodView.tsx @@ -0,0 +1,82 @@ +import { Scalars } from "../../__generated__/graphql"; +import paths from "../../paths"; +import EntityLink from "../entities/EntityLink"; +import InlineList from "../InlineList"; +import JsonView from "../JsonView"; +import { + AppliedMethodPartialFragment, + ToTreeVertexAppliedConversionMethodPartialFragment, +} from "../../queries/data.generated"; +import DateTimeX from "../DateTimeX"; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function AppliedMethodView({ + value, +}: { + value: + | AppliedMethodPartialFragment + | ToTreeVertexAppliedConversionMethodPartialFragment; +}) { + return ( + + + {value.arguments.length > 0 && ( + <> + {" "} + with the arguments{" "} + ( + + {item.name}=“ + + ” + + )} + /> + + )} + {value.__typename === "AppliedMethod" && value.sources.length > 0 && ( + <> + {" "} + on the sources{" "} + ( + + {item.name}=“ + )}`, + }} + route={(id) => + paths.metabase.data( + item.value.databaseId, + item.value.dataKind, + id, + ) + } + /> + ” + + )} + /> + + )} + {value.__typename === "ToTreeVertexAppliedConversionMethod" && ( + <> + {" "} + with its parent as the source {value.sourceName}. + + )} + + ); +} diff --git a/frontend/components/sorting/SortSubform.tsx b/frontend/components/sorting/SortSubform.tsx new file mode 100644 index 00000000..47df4725 --- /dev/null +++ b/frontend/components/sorting/SortSubform.tsx @@ -0,0 +1,35 @@ +import { Space, Form, Select } from "antd"; +import { SortEnumType } from "../../__generated__/graphql"; +import { formatSortDirection, SortDefinition } from "../../lib/sort"; +import { getLabel } from "../../lib/string"; + +const sortDirectionOptions = Object.entries(SortEnumType).map( + ([_key, value]) => ({ + value: value, + label: formatSortDirection(value), + }), +); + +export default function SortSubform({ + name, + definitions, +}: { + name: (string | number)[]; + definitions: readonly SortDefinition[]; +}) { + return ( + + + + + + ); +} diff --git a/frontend/jest.config.js b/frontend/jest.config.js deleted file mode 100644 index c7c929e1..00000000 --- a/frontend/jest.config.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - roots: [""], - moduleFileExtensions: ["ts", "tsx", "js", "json", "jsx"], - testPathIgnorePatterns: ["[/\\\\](node_modules|.next)[/\\\\]"], - transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$"], - transform: { - "^.+\\.(ts|tsx)$": "babel-jest", - "\\.graphql$": [ - "graphql-let/jestTransformer", - { subsequentTransformer: "babel-jest" }, - ], - }, -}; diff --git a/frontend/lib/apollo.ts b/frontend/lib/apollo.ts index bf951d54..d7eeae1e 100644 --- a/frontend/lib/apollo.ts +++ b/frontend/lib/apollo.ts @@ -16,6 +16,8 @@ import { sha256 } from "crypto-hash"; import fragmentMatcherTypes from "../__generated__/fragmentMatcherTypes"; // generated by `fragment-matcher` plugin import { GraphQLFormattedError } from "graphql"; import { map } from "rxjs"; +import { StrictTypedTypePolicies } from "../__generated__/apolloHelpers"; +import { relayStylePagination } from "@apollo/client/utilities"; const AntiforgeryCookieKey = "XSRF-TOKEN"; const AntiforgeryHeaderName = "X-XSRF-TOKEN"; @@ -119,10 +121,31 @@ const apolloLink = filterAuthErrorsLink .concat(persistedQueryLink) .concat(httpLink); +// [Cursor-Based Pagination](https://www.apollographql.com/docs/react/pagination/cursor-based) +// [Key Arguments](https://www.apollographql.com/docs/react/pagination/key-args) +// https://the-guild.dev/graphql/codegen/plugins/typescript/typescript-apollo-client-helpers +const typePolicies: StrictTypedTypePolicies = { + Query: { + fields: { + allCalorimetricData: relayStylePagination(["locale", "where"]), + allGeometricData: relayStylePagination(["locale", "where"]), + allHygrothermalData: relayStylePagination(["locale", "where"]), + allLifeCycleData: relayStylePagination(["locale", "where"]), + allOpticalData: relayStylePagination(["locale", "where"]), + allPhotovoltaicData: relayStylePagination(["locale", "where"]), + // users: relayStylePagination(["where", "order"]), + }, + }, + Database: { + fields: {}, + }, +}; + export const apolloClient = new ApolloClient({ ssrMode: false, link: apolloLink, cache: new InMemoryCache({ + typePolicies: typePolicies, possibleTypes: fragmentMatcherTypes.possibleTypes, }), localState: new LocalState({}), diff --git a/frontend/lib/array.ts b/frontend/lib/array.ts new file mode 100644 index 00000000..501cfa68 --- /dev/null +++ b/frontend/lib/array.ts @@ -0,0 +1,59 @@ +// Taken from https://stackoverflow.com/questions/43118692/typescript-filter-out-nulls-from-an-array/46700791#46700791 +export function notEmpty( + value: TValue | null | undefined, +): value is TValue { + return value !== null && value !== undefined; +} + +export function isTruthy( + value: T | false | null | undefined | "" | 0, +): value is T { + return Boolean(value); +} + +export function isMember( + value: string, + array: readonly T[], +): value is T { + return array.includes(value as T); +} + +export const intersperse = ( + array: React.ReactNode[], + separator: string = " ", +): React.ReactNode[] => + array.reduce( + (accumulator: React.ReactNode[], currrent, index) => + index === 0 ? [currrent] : [...accumulator, separator, currrent], + [], + ); + +/** + * Unwraps T if T is an array, otherwise becomes `never`. + * When applied to a union like `A[] | B[]`, it results in `A | B`. + */ +type UnboxArray = T extends (infer U)[] ? U : never; + +/** + * Turns `A[] | B[] | C[]` into `readonly (A | B | C)[]` without runtime costs. + */ +export function asReadonlyMixed( + array: T, +): readonly UnboxArray[] { + return array as any; +} + +export const range = (start: number, end: number): number[] => + Array.from({ length: end - start + 1 }, (_, i) => start + i); + +export function pluralize(count: number, noun: string, suffix = "s") { + return `${noun}${count !== 1 ? suffix : ""}`; +} + +export function pluralizeIrregular( + count: number, + singular: string, + plural?: string, +) { + return count === 1 ? singular : (plural ?? `${singular}s`); +} diff --git a/frontend/lib/assert.ts b/frontend/lib/assert.ts new file mode 100644 index 00000000..d2a847cf --- /dev/null +++ b/frontend/lib/assert.ts @@ -0,0 +1,3 @@ +function assertNever(x: never): never { + throw new Error("Unexpected object: " + x); +} diff --git a/frontend/lib/connection.ts b/frontend/lib/connection.ts new file mode 100644 index 00000000..ee647f5f --- /dev/null +++ b/frontend/lib/connection.ts @@ -0,0 +1,12 @@ +interface PageInfo { + hasNextPage: boolean; + hasPreviousPage?: boolean; + startCursor?: string | null; + endCursor: string | null; +} + +export interface Connection { + edges?: Array<{ cursor?: string; node: TNode }> | null; + pageInfo: PageInfo; + totalCount: number; +} diff --git a/frontend/lib/filter.ts b/frontend/lib/filter.ts new file mode 100644 index 00000000..11101328 --- /dev/null +++ b/frontend/lib/filter.ts @@ -0,0 +1,507 @@ +import { + FloatPropositionInput, + IntPropositionInput, + StringPropositionInput, + UrlPropositionInput, + UuidPropositionInput, +} from "../__generated__/graphql"; + +type DistributiveOmit = T extends any + ? Omit + : never; + +type Scalar = string | number | boolean; + +export type EnumPropositionInput = { + equalTo?: TEnum | null; + in?: TEnum[] | null; + notEqualTo?: TEnum | null; + notIn?: TEnum[] | null; +}; + +export type ListPropositionInput = { + all?: TItemPropositionInput | null; + none?: TItemPropositionInput | null; + some?: TItemPropositionInput | null; +}; + +type FilterOperatorKind = "single" | "multiple"; + +const ScalarFilterOperatorMap = { + contains: { label: "contains", kind: "single" }, + startsWith: { label: "starts with", kind: "single" }, + endsWith: { label: "ends with", kind: "single" }, + equalTo: { label: "=", kind: "single" }, + notEqualTo: { label: "≠", kind: "single" }, + greaterThan: { label: ">", kind: "single" }, + greaterThanOrEqualTo: { label: "≥", kind: "single" }, + lessThan: { label: "<", kind: "single" }, + lessThanOrEqualTo: { label: "≤", kind: "single" }, + in: { label: "∈", kind: "multiple" }, + notIn: { label: "∉", kind: "multiple" }, +} as const satisfies Record< + string, + { label: string; kind: FilterOperatorKind } +>; + +const ListFilterOperatorMap = { + all: { label: "all", kind: "single" }, + none: { label: "none", kind: "single" }, + some: { label: "some", kind: "single" }, +} as const satisfies Record< + string, + { label: string; kind: FilterOperatorKind } +>; + +const FilterOperatorMap = { + ...ScalarFilterOperatorMap, + ...ListFilterOperatorMap, +} as const; + +type ScalarFilterOperator = keyof typeof ScalarFilterOperatorMap; +type ListFilterOperator = keyof typeof ListFilterOperatorMap; +export type FilterOperator = ScalarFilterOperator | ListFilterOperator; + +export const getFilterOperatorLabel = (key: FilterOperator) => + FilterOperatorMap[key].label; +export const isFilterOperatorSingle = (key: FilterOperator) => + FilterOperatorMap[key].kind === "single"; + +const scalarFilterOperators = new Set( + Object.keys(ScalarFilterOperatorMap), +) as ReadonlySet; +const listFilterOperators = new Set( + Object.keys(ListFilterOperatorMap), +) as ReadonlySet; + +const isScalarFilterOperator = ( + value: FilterOperator | undefined, +): value is ScalarFilterOperator => { + return scalarFilterOperators.has(value as ScalarFilterOperator); +}; +const isListFilterOperator = ( + value: FilterOperator | undefined, +): value is ListFilterOperator => { + return listFilterOperators.has(value as ListFilterOperator); +}; + +const defineFilterOperators = ( + keys: readonly K[], +): readonly K[] => keys; + +type ScalarFilterState = { + readonly operator: ScalarFilterOperator; + readonly value: Scalar | Scalar[] | null; +}; +type ListFilterState = { + readonly operator: ListFilterOperator; + readonly value: FilterState; +}; +export type ObjectFilterState = { + readonly operator?: undefined; + readonly index: number; + readonly value: FilterState; +}; + +type FilterState = ScalarFilterState | ListFilterState | ObjectFilterState; + +const ScalarFilterTypeMap = { + enum: { + operators: defineFilterOperators(["equalTo", "notEqualTo", "in", "notIn"]), + initialOperator: 0, + }, + float: { + operators: defineFilterOperators([ + "equalTo", + "notEqualTo", + "greaterThan", + "greaterThanOrEqualTo", + "lessThan", + "lessThanOrEqualTo", + "in", + "notIn", + ]), + initialOperator: 0, + }, + int: { + operators: defineFilterOperators([ + "equalTo", + "notEqualTo", + "greaterThan", + "greaterThanOrEqualTo", + "lessThan", + "lessThanOrEqualTo", + "in", + "notIn", + ]), + initialOperator: 0, + }, + string: { + operators: defineFilterOperators([ + "contains", + "startsWith", + "endsWith", + "equalTo", + "notEqualTo", + "in", + "notIn", + ]), + initialOperator: 0, + }, + url: { + operators: defineFilterOperators(["equalTo", "notEqualTo", "in", "notIn"]), + initialOperator: 0, + }, + uuid: { + operators: defineFilterOperators(["equalTo", "notEqualTo", "in", "notIn"]), + initialOperator: 0, + }, +} as const satisfies Record< + string, + { operators: readonly ScalarFilterOperator[]; initialOperator: number } +>; + +const ListFilterTypeMap = { + list: { + operators: defineFilterOperators(["some", "all", "none"]), + initialOperator: 0, + }, +} as const satisfies Record< + string, + { operators: readonly ListFilterOperator[]; initialOperator: number } +>; + +export const FilterTypeMap = { + ...ScalarFilterTypeMap, + ...ListFilterTypeMap, +} as const; + +type ScalarFilterType = keyof typeof ScalarFilterTypeMap; +type ListFilterType = keyof typeof ListFilterTypeMap; +type ObjectFilterType = typeof objectFilterType; +type FilterType = ScalarFilterType | ListFilterType | ObjectFilterType; + +const scalarFilterTypes = new Set( + Object.keys(ScalarFilterTypeMap), +) as ReadonlySet; +const listFilterTypes = new Set( + Object.keys(ListFilterTypeMap), +) as ReadonlySet; +const objectFilterType = "object" as const; + +const isScalarFilterType = (value: FilterType): value is ScalarFilterType => { + return scalarFilterTypes.has(value as ScalarFilterType); +}; +const isListFilterType = (value: FilterType): value is ListFilterType => { + return listFilterTypes.has(value as ListFilterType); +}; + +type BaseFilterDefinition = { + readonly field: TKey; + readonly label?: string; +}; + +type EnumFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, + TEnum, +> = BaseFilterDefinition & { + readonly type: "enum" & ScalarFilterType; + readonly field: TKey; + readonly enumObject: TEnum; + readonly _validation?: TPropositionInput[TKey] extends EnumPropositionInput + ? TKey + : never; +}; + +type FloatFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "float" & ScalarFilterType; + readonly _validation?: TPropositionInput[TKey] extends FloatPropositionInput + ? TKey + : never; +}; + +type IntFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "int" & ScalarFilterType; + readonly _validation?: TPropositionInput[TKey] extends IntPropositionInput + ? TKey + : never; +}; + +type StringFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "string" & ScalarFilterType; + readonly _validation?: TPropositionInput[TKey] extends StringPropositionInput + ? TKey + : never; +}; + +type UrlFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "url" & ScalarFilterType; + readonly _validation?: TPropositionInput[TKey] extends UrlPropositionInput + ? TKey + : never; +}; + +type UuidFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "uuid" & ScalarFilterType; + readonly _validation?: TPropositionInput[TKey] extends UuidPropositionInput + ? TKey + : never; +}; + +type ScalarFilterDefinitionMap< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = { + enum: TPropositionInput[TKey] extends EnumPropositionInput + ? EnumFilterDefinition + : never; + float: FloatFilterDefinition; + int: IntFilterDefinition; + string: StringFilterDefinition; + url: UrlFilterDefinition; + uuid: UuidFilterDefinition; +}; + +type ScalarFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = ScalarFilterDefinitionMap[ScalarFilterType]; + +type ListItemFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = + NonNullable extends { + all?: infer TItemPropositionInput; + } + ? DistributiveOmit< + FilterDefinition>, + "field" + > + : never; + +export type ListFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "list" & ListFilterType; + readonly item: ListItemFilterDefinition; + readonly _validation?: TPropositionInput extends ListPropositionInput + ? TKey + : never; +}; + +export type ObjectFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = BaseFilterDefinition & { + readonly type: "object" & ObjectFilterType; + readonly items: readonly FilterDefinition< + NonNullable + >[]; +}; + +type FieldFilterDefinition< + TPropositionInput, + TKey extends keyof TPropositionInput, +> = + | ScalarFilterDefinition + | ListFilterDefinition + | ObjectFilterDefinition; + +type AnyFilterDefinition = FieldFilterDefinition; + +export type FilterDefinition = { + [TKey in keyof TPropositionInput]: FieldFilterDefinition< + TPropositionInput, + TKey + >; +}[keyof TPropositionInput]; + +const isScalarFilterDefinition = ( + value: FilterDefinition, +): value is ScalarFilterDefinition< + TPropositionInput, + keyof TPropositionInput +> => { + return isScalarFilterType(value.type); +}; +const isListFilterDefinition = ( + value: FilterDefinition, +): value is ListFilterDefinition< + TPropositionInput, + keyof TPropositionInput +> => { + return isListFilterType(value.type); +}; +const isObjectFilterDefinition = ( + value: FilterDefinition, +): value is ObjectFilterDefinition< + TPropositionInput, + keyof TPropositionInput +> => { + return value.type === objectFilterType; +}; + +const isScalarFilterState = ( + value: FilterState, +): value is ScalarFilterState => { + return isScalarFilterOperator(value.operator); +}; +const isListFilterState = (value: FilterState): value is ListFilterState => { + return isListFilterOperator(value.operator); +}; +const isObjectFilterState = ( + value: FilterState, +): value is ObjectFilterState => { + return value.operator === undefined; +}; + +export type FilterStateReducerContext = readonly AnyFilterDefinition[]; + +export const createFilterStateReducer = ( + reduceScalar: ( + scalar: ScalarFilterState, + context: FilterStateReducerContext, + ) => TOutput, + reduceList: ( + list: ListFilterState, + reduce: (filter: FilterState) => TOutput, + ) => TOutput, + reduceObject: ( + object: ObjectFilterState, + context: FilterStateReducerContext, + reduce: (filter: FilterState) => TOutput, + ) => TOutput, +) => { + return ( + root: ObjectFilterState, + initialContext: FilterStateReducerContext, + ): TOutput => { + const reduce = ( + filter: FilterState, + context: FilterStateReducerContext, + ): TOutput => { + if (isScalarFilterState(filter)) { + return reduceScalar(filter, context); + } + if (isListFilterState(filter)) { + return reduceList(filter, (x) => reduce(x, context)); + } + if (isObjectFilterState(filter)) { + const subContext = ( + context[filter.index] as ObjectFilterDefinition + ).items; + return reduceObject(filter, context, (x) => reduce(x, subContext)); + } + return assertNever(filter); + }; + return reduceObject(root, initialContext, (object) => + reduce(object, initialContext), + ); + }; +}; + +export const createFilterDefinitionReducer = < + TScalarOutput, + TListOutput, + TObjectOutput, +>( + reduceScalar: (scalar: ScalarFilterDefinition) => TScalarOutput, + reduceList: ( + list: ListFilterDefinition, + reduce: ( + filter: AnyFilterDefinition, + ) => TScalarOutput | TListOutput | TObjectOutput, + ) => TListOutput, + reduceObject: ( + object: ObjectFilterDefinition, + reduce: ( + filter: AnyFilterDefinition, + ) => TScalarOutput | TListOutput | TObjectOutput, + ) => TObjectOutput, +) => { + return ( + root: AnyFilterDefinition, + ): TScalarOutput | TListOutput | TObjectOutput => { + const reduce = ( + filter: AnyFilterDefinition, + ): TScalarOutput | TListOutput | TObjectOutput => { + if (isScalarFilterDefinition(filter)) { + return reduceScalar(filter); + } + if (isListFilterDefinition(filter)) { + return reduceList(filter, (x) => reduce(x)); + } + if (isObjectFilterDefinition(filter)) { + return reduceObject(filter, (x) => reduce(x)); + } + return null as TScalarOutput | TListOutput | TObjectOutput; + // TODO make the following succeed: return assertNever(filter); + }; + return reduce(root); + }; +}; + +// to GraphQL filter input, for example, to `{ name: { contains: "Simon" } }` +export const toWhereClause = ( + filter: ObjectFilterState, + configs: readonly FilterDefinition[], +): TPropositionInput => + // the initial context type is `readonly FilterDefinition[]` and subsequent + // types are of the form `readonly FilterDefinition[]` + createFilterStateReducer( + (scalar, _) => ({ + [scalar.operator]: scalar.value, + }), + (list, reduce) => ({ + [list.operator]: reduce(list.value), + }), + (object, context, reduce) => ({ + [context[object.index].field]: reduce(object.value), + }), + )(filter, configs as FilterStateReducerContext) as TPropositionInput; + +export const getInitialFilterSubformValues = ( + filterDefinition: AnyFilterDefinition, +): FilterState => + createFilterDefinitionReducer< + ScalarFilterState, + ListFilterState, + ObjectFilterState + >( + (scalar) => ({ + operator: + ScalarFilterTypeMap[scalar.type].operators[ + ScalarFilterTypeMap[scalar.type].initialOperator + ], + value: null, + }), + (list, reduce) => ({ + operator: + ListFilterTypeMap[list.type].operators[ + ListFilterTypeMap[list.type].initialOperator + ], + value: reduce(list.item), + }), + (object, reduce) => ({ + index: 0, + value: reduce(object.items[0]), + }), + )(filterDefinition); diff --git a/frontend/lib/form.ts b/frontend/lib/form.ts index 09ae2a4f..ffc03669 100644 --- a/frontend/lib/form.ts +++ b/frontend/lib/form.ts @@ -1,50 +1,12 @@ -import { FormInstance } from "antd"; -import { Dispatch, SetStateAction } from "react"; -import { CombinedGraphQLErrors, ErrorLike } from "@apollo/client"; +export const layout = { + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, +}; -export function handleFormErrors( - apolloError: ErrorLike | undefined, - userErrors: { code: string; message: string; path: string[] }[] | undefined, - setGlobalErrorMessages: Dispatch>, - form: FormInstance, -) { - const globalErrorMessages = new Array(); - if (apolloError) { - // TODO Is this how we want to handle GraphQl errors? - globalErrorMessages.push(...`${apolloError.name}: ${apolloError.message}`); - if (CombinedGraphQLErrors.is(apolloError)) { - globalErrorMessages.push( - ...apolloError.errors.map( - (e) => - `${e.message} at path ${e.path?.join(", ")} and locations ${e.locations?.join(", ")}`, - ), - ); - } - } - if (userErrors) { - const errorPathToMessage = userErrors.reduce((a, x) => { - // We use strings as keys instead of path arrays because the - // latter are compared by reference. - const pathAsString = x.path.join("."); - if (!a.has(pathAsString)) { - a.set(pathAsString, [x.path, []]); - } - a.get(pathAsString)?.[1]?.push(x.message); - return a; - }, new Map()); - console.log(errorPathToMessage); - for (let [, [path, messages]] of errorPathToMessage) { - if (path.length === 1) { - globalErrorMessages.push(...messages); - } else { - form.setFields([ - { - name: path.slice(1), - errors: messages, - }, - ]); - } - } - } - setGlobalErrorMessages(globalErrorMessages); -} +export const tailLayout = { + wrapperCol: { offset: 8, span: 16 }, +}; + +export const getFieldValue = (values: any, path: (string | number)[]) => { + return path.reduce((obj, key) => obj?.[key], values); +}; diff --git a/frontend/lib/freeTextFilter.tsx b/frontend/lib/freeTextFilter.tsx deleted file mode 100644 index 5a90355a..00000000 --- a/frontend/lib/freeTextFilter.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Input, Space, Button, InputRef } from "antd"; -import { FilterFilled } from "@ant-design/icons"; -import { - FilterConfirmProps, - FilterDropdownProps, -} from "antd/lib/table/interface"; -import { Dispatch, Key, SetStateAction } from "react"; - -export function setMapValue( - map: Map, - setMap: Dispatch>>, -) { - return (key: string) => { - return (newValue: string) => { - if (map.get(key) !== newValue) { - const copy = new Map(map); - copy.set(key, newValue); - setMap(copy); - } - }; - }; -} - -export function doesFieldIncludeFilterValue( - field: string, - value: React.Key | boolean, -) { - return field.toLowerCase().includes(value.toString().toLowerCase()); -} - -export function getFreeTextFilterProps( - getField: (record: RecordType) => string | null | undefined, - onFilterTextChange: (newFilterText: string) => void, -) { - const filter = ( - selectedKeys: Key[], - confirm: (param?: FilterConfirmProps | undefined) => void, - ) => { - confirm(); - onFilterTextChange( - selectedKeys.length === 0 ? "" : selectedKeys[0].toString(), - ); - }; - - const reset = (clearFilters: (() => void) | undefined) => { - if (clearFilters !== undefined) { - clearFilters(); - onFilterTextChange(""); - } - }; - - let searchInput: InputRef | null = null; - - return { - filterDropdown: ({ - setSelectedKeys, - selectedKeys, - confirm, - clearFilters, - }: FilterDropdownProps) => ( -
    - { - searchInput = node; - }} - placeholder={"Filter"} - value={selectedKeys?.[0]?.toString()} - onChange={(e) => - setSelectedKeys(e.target.value ? [e.target.value] : []) - } - onPressEnter={() => filter(selectedKeys, confirm)} - style={{ marginBottom: 8, display: "block" }} - /> - - - - -
    - ), - filterIcon: (filtered: boolean) => ( - - ), - onFilter: (value: React.Key | boolean, record: RecordType) => { - const field = getField(record); - return field ? doesFieldIncludeFilterValue(field, value) : false; - }, - onFilterDropdownOpenChange: (visible: boolean) => { - if (visible) { - setTimeout(() => searchInput?.select(), 100); - } - }, - }; -} diff --git a/frontend/lib/hooks/useDebounce.ts b/frontend/lib/hooks/useDebounce.ts new file mode 100644 index 00000000..bcc0b221 --- /dev/null +++ b/frontend/lib/hooks/useDebounce.ts @@ -0,0 +1,26 @@ +import { useRef, useCallback, useEffect } from "react"; + +export function useDebounce any>( + callback: T, + delay: number, +) { + const timeoutRef = useRef | null>(null); + + const debouncedFunction = useCallback( + (...args: Parameters) => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + timeoutRef.current = setTimeout(() => callback(...args), delay); + }, + [callback, delay], + ); + + // cleanup on unmount + useEffect( + () => () => { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }, + [], + ); + + return debouncedFunction; +} diff --git a/frontend/lib/hooks/useMutationHandler.ts b/frontend/lib/hooks/useMutationHandler.ts new file mode 100644 index 00000000..346c6440 --- /dev/null +++ b/frontend/lib/hooks/useMutationHandler.ts @@ -0,0 +1,124 @@ +import { ApolloClient, CombinedGraphQLErrors, ErrorLike } from "@apollo/client"; +import { App, FormInstance } from "antd"; +import { useState } from "react"; +import { UserError } from "../../__generated__/graphql"; +import { GraphQLFormattedError } from "graphql"; + +interface UseMutationHandlerProps { + getErrors: (data: TMutation) => UserError[] | null; +} + +interface Callbacks { + onSuccess?: (data: TMutation | null) => void | Promise; + onError?: ( + graphQlErrors: readonly GraphQLFormattedError[] | null, + userErrors: UserError[] | null, + ) => void | Promise; +} + +export function useMutationHandler({ + getErrors, +}: UseMutationHandlerProps) { + const [mutating, setMutating] = useState(false); + const { message } = App.useApp(); + + const withMutationHandler = async ( + mutationFunction: () => Promise>, + callbacks?: Callbacks, + ) => { + try { + setMutating(true); + const result = await mutationFunction(); + handleMutationResult(result.data, result.error, callbacks); + return result; + } catch (error) { + message.error( + error instanceof Error ? error.message : "An unexpected error occurred", + ); + } finally { + setMutating(false); + } + }; + + const handleMutationResult = async ( + data: TMutation | undefined, + apolloError: ErrorLike | undefined, + callbacks?: Callbacks, + ) => { + if (apolloError && !CombinedGraphQLErrors.is(apolloError)) { + message.error(apolloError.message); + return; + } + const graphQlErrors = CombinedGraphQLErrors.is(apolloError) + ? apolloError.errors + : null; + const userErrors = data ? getErrors(data) : null; + if (graphQlErrors?.length || userErrors?.length) { + callbacks?.onError?.(graphQlErrors, userErrors); + } else { + await callbacks?.onSuccess?.(data ?? null); + } + }; + + const messageErrors = async ( + graphQlErrors: readonly GraphQLFormattedError[] | null, + userErrors: UserError[] | null, + ) => { + const errors = [...(graphQlErrors ?? []), ...(userErrors ?? [])]; + if (errors?.length) { + message.error(errors.map((error) => error.message).join("\n")); + } + }; + + const messageMissingModel = async () => { + message.error("The mutation did not return the new model."); + }; + + return { + mutating, + withMutationHandler, + messageErrors, + augmentFormWithErrors, + messageMissingModel, + }; +} + +function augmentFormWithErrors( + graphQlErrors: readonly GraphQLFormattedError[] | undefined | null, + userErrors: UserError[] | undefined | null, + form: FormInstance, +) { + const globalErrorMessages = new Array(); + const transformedGraphQlErrors = graphQlErrors?.map((error) => ({ + message: error.message ?? "", + // TODO Do not hardcode the fallback path `["input"]` + path: error.path ?? ["input"], + })); + const errors = [...(transformedGraphQlErrors ?? []), ...(userErrors ?? [])]; + if (errors?.length) { + globalErrorMessages.push("The form contains errors."); + } + const errorPathToMessage = errors.reduce((accumulator, error) => { + // We use strings as keys instead of path arrays because the + // latter are compared by reference. + const pathAsString = error.path.join("."); + if (!accumulator.has(pathAsString)) { + accumulator.set(pathAsString, [error.path, []]); + } + accumulator.get(pathAsString)?.[1]?.push(error.message); + return accumulator; + }, new Map()); + for (let [, [path, messages]] of errorPathToMessage) { + if (path.length === 1 || form.getFieldInstance(path.slice(1)) == null) { + globalErrorMessages.push(...messages); + } else { + form.setFields([ + { + name: path.slice(1), + errors: messages, + }, + ]); + } + } + return globalErrorMessages; +} diff --git a/frontend/lib/hooks/usePaginatedQuery.ts b/frontend/lib/hooks/usePaginatedQuery.ts new file mode 100644 index 00000000..62180555 --- /dev/null +++ b/frontend/lib/hooks/usePaginatedQuery.ts @@ -0,0 +1,178 @@ +import { useState, useMemo, useEffect } from "react"; +import { TypedDocumentNode } from "@apollo/client"; +import { useQuery } from "@apollo/client/react"; +import { Connection } from "../connection"; +import { + Fetching, + initialPageSize, + PaginationProps, +} from "../../components/Pagination"; + +type QueryData = { + connection: Connection | null; +}; + +type PropositionInput = + | TPropositionInput + | { and?: TPropositionInput[] }; + +type SortInput = TSortInput | TSortInput[]; + +export type QueryVariables = { + first: number; + after?: string | null; + where?: PropositionInput; + order?: SortInput; +}; + +export type QueryDocument = + TypedDocumentNode< + QueryData, + QueryVariables + >; + +// ensure `relayStylePagination(["first", "after", ...])` is set in `InMemoryCache` for the field `fieldName` +export function usePaginatedQuery( + queryDocument: QueryDocument, + { + where, + order, + }: { + where?: PropositionInput; + order?: SortInput; + }, + onQueryVariablesChange: ( + variables: QueryVariables, + ) => void, +): { + loading: boolean; + nodes: TNode[] | null; + paginationProps: PaginationProps; +} { + const [currentPage, setCurrentPage] = useState(1); + const [pageSize, setPageSize] = useState(initialPageSize); + const [afterCursors, setAfterCursors] = useState<(string | null)[]>([null]); + const [fetching, setFetching] = useState(Fetching.INITIAL); + const [hasLoadedOnce, setHasLoadedOnce] = useState(false); + + const variables = { + first: pageSize, + after: null, + where: where, + order: order, + }; + const { loading, error, data, fetchMore, refetch } = useQuery(queryDocument, { + variables, + notifyOnNetworkStatusChange: true, + errorPolicy: "ignore", + }); + if (error) { + console.error("Paginated query failed", error); + } + + if (!hasLoadedOnce && !loading) { + setFetching(null); + setHasLoadedOnce(true); + } + + // Reset: If where OR order change, jump back to page 1 + useEffect(() => { + setCurrentPage(1); + setAfterCursors([null]); + onQueryVariablesChange(variables); + }, [JSON.stringify(where), JSON.stringify(order)]); + + const connection = data?.connection; + const edges = connection?.edges; + + // window the current page from the cache + const currentPageNodes = useMemo(() => { + const start = (currentPage - 1) * pageSize; + return edges?.slice(start, start + pageSize).map((e) => e.node); + }, [edges, currentPage, pageSize]); + + const handlePrevious = () => { + if (loading) return; + if (currentPage <= 1) return; + const previousPage = currentPage - 1; + setCurrentPage(previousPage); + onQueryVariablesChange({ + ...variables, + after: afterCursors[previousPage - 1], + }); + }; + + const handleNext = () => { + if (loading || edges == null) return; + const isLastPageInMemory = currentPage * pageSize >= edges.length; + if (isLastPageInMemory && connection?.pageInfo.hasNextPage) { + const endCursor = connection?.pageInfo.endCursor ?? null; + const variables = { + first: pageSize, + after: endCursor, + where: where, + order: order, + }; + setFetching(Fetching.NEXT); + fetchMore({ variables, errorPolicy: "ignore" }) + .then(() => { + // do not use `currentPage` or `afterCursors` to determine the new + // values because they may have changed when this callback fires + setCurrentPage((previous) => previous + 1); + setAfterCursors((previous) => [...previous, endCursor]); + onQueryVariablesChange(variables); + }) + .catch((error) => { + console.error("Fetching next page failed", error); + }) + .finally(() => setFetching(null)); + } else if (!isLastPageInMemory) { + setCurrentPage(currentPage + 1); + onQueryVariablesChange({ + ...variables, + after: afterCursors[currentPage], + }); + } + }; + + const handlePageSizeChange = (pageSize: number) => { + setPageSize(pageSize); + const variables = { + first: pageSize, + after: null, + where: where, + order: order, + }; + refetch(variables) + .then(() => { + setFetching(Fetching.INITIAL); + // note that `currentPage` or `afterCursors` may have changed when this + // callback fires + setCurrentPage(1); + setAfterCursors([null]); + onQueryVariablesChange(variables); + }) + .catch((error) => { + console.error("Changing page size failed", error); + }) + .finally(() => setFetching(null)); + }; + + return { + loading, + nodes: currentPageNodes ?? null, + paginationProps: { + fetching, + current: currentPage, + total: Math.ceil((connection?.totalCount ?? 0) / pageSize), + pageSize: pageSize, + hasNext: + !!connection?.pageInfo.hasNextPage || + currentPage * pageSize < (edges?.length ?? 0), + hasPrevious: currentPage > 1, + onNext: handleNext, + onPrevious: handlePrevious, + onPageSizeChange: handlePageSizeChange, + }, + }; +} diff --git a/frontend/lib/hooks/usePreviousNonNull.ts b/frontend/lib/hooks/usePreviousNonNull.ts new file mode 100644 index 00000000..4dff1fa7 --- /dev/null +++ b/frontend/lib/hooks/usePreviousNonNull.ts @@ -0,0 +1,9 @@ +import { useRef } from "react"; + +export function usePreviousNonNull(value: T | null) { + const ref = useRef(null); + if (value !== null) { + ref.current = value; + } + return ref.current; +} diff --git a/frontend/lib/hooks/useQueryHandler.ts b/frontend/lib/hooks/useQueryHandler.ts new file mode 100644 index 00000000..7e710ebd --- /dev/null +++ b/frontend/lib/hooks/useQueryHandler.ts @@ -0,0 +1,18 @@ +import { useEffect } from "react"; +import { stringifyApolloError } from "../apollo"; +import { App } from "antd"; +import { ErrorLike } from "@apollo/client"; + +interface UseQueryHandlerProps { + error?: ErrorLike | null; +} + +export function useQueryHandler({ error }: UseQueryHandlerProps) { + const { message } = App.useApp(); + + useEffect(() => { + if (error) { + message.error(stringifyApolloError(error)); + } + }, [error, message]); +} diff --git a/frontend/lib/sort.ts b/frontend/lib/sort.ts new file mode 100644 index 00000000..5fa105db --- /dev/null +++ b/frontend/lib/sort.ts @@ -0,0 +1,36 @@ +import { SortEnumType } from "../__generated__/graphql"; + +export type SortState = { + index: number; + direction: SortEnumType; +}; + +export type SortDefinition = { + field: keyof TSortInput; + label?: string; +}; + +export const initialSortSubformValues = { + index: 0, + direction: SortEnumType.Asc, +}; + +// to GraphQL sort input, for example, to `[{ name: "ASC" }, { date: "DESC" }]` +export const toOrderClause = ( + sort: SortState, + configs: readonly SortDefinition[], +): TSortInput => + ({ + [configs[sort.index].field]: sort.direction, + }) as TSortInput; + +export const formatSortDirection = (direction: SortEnumType) => { + switch (direction) { + case SortEnumType.Asc: + return "ascending"; + case SortEnumType.Desc: + return "descending"; + default: + return assertNever(direction); + } +}; diff --git a/frontend/lib/string.ts b/frontend/lib/string.ts new file mode 100644 index 00000000..bc0f0399 --- /dev/null +++ b/frontend/lib/string.ts @@ -0,0 +1,69 @@ +export const uuidRegex = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +export const isUuid = (value: T) => uuidRegex.test(value); + +export const notEmpty = (value: T) => value.trim().length > 0; + +export const capitalize = (value: T) => + (value[0].toUpperCase() + value.slice(1)) as Capitalize; + +export const replacePrefix = ( + value: string, + oldPrefix: string, + newPrefix: string = "", +): string => + value.startsWith(oldPrefix) + ? newPrefix + value.slice(oldPrefix.length) + : value; + +export const removePrefix = (value: string, prefix: string): string => + value.startsWith(prefix) ? value.slice(prefix.length) : value; + +export const addPrefix = (value: string, prefix: string): string => + value.startsWith(prefix) ? value : prefix + value; + +const ACRONYMS = ["ID", "URL", "UUID"]; +type CaseStyle = "all-upper" | "first-upper" | "none-upper"; + +export const humanize = ( + key: PropertyKey, + style: CaseStyle = "none-upper", +): string => { + if (!key) return ""; + const words = String(key) + .replace(/([a-z])([A-Z])/g, "$1 $2") + .replace(/[_-]/g, " ") + .trim() + .split(/\s+/); // Get individual words + const formattedWords = words.map((word, index) => { + const upper = word.toUpperCase(); + if (ACRONYMS.includes(upper)) { + return upper; + } + switch (style) { + case "all-upper": // title case + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + case "first-upper": // sentence case + return index === 0 + ? word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() + : word.toLowerCase(); + case "none-upper": // lowercase + return word.toLowerCase(); + default: + return word; + } + }); + return formattedWords.join(" "); +}; + +export const getLabel = ( + { + field, + label, + }: { + field: PropertyKey; + label?: string; + }, + style: CaseStyle = "none-upper", +) => label ?? humanize(field, style); diff --git a/frontend/lib/table.tsx b/frontend/lib/table.tsx deleted file mode 100644 index cad0b63e..00000000 --- a/frontend/lib/table.tsx +++ /dev/null @@ -1,691 +0,0 @@ -import { List, Typography, Descriptions } from "antd"; -import { SortOrder } from "antd/lib/table/interface"; -import { - doesFieldIncludeFilterValue, - getFreeTextFilterProps, -} from "./freeTextFilter"; -import Link from "next/link"; -import { Scalars, Publication, Standard } from "../__generated__/graphql"; -import { Highlight } from "../components/Highlight"; -import paths from "../paths"; -import { JSX } from "react"; -import { Route } from "next"; - -const sortDirections: SortOrder[] = ["ascend", "descend"]; - -function compare( - x: ValueType | null | undefined, - y: ValueType | null | undefined, - nonNullCompare: (a: ValueType, b: ValueType) => number, -) { - if (x != null && y != null) { - return nonNullCompare(x, y); - } - if (x == null && y != null) { - return -1; - } - if (x != null && y == null) { - return 1; - } - return 0; -} - -function compareLexicographically( - xs: (ValueType | null | undefined)[], - ys: (ValueType | null | undefined)[], - elementCompare: (x: ValueType, y: ValueType) => number, -) { - let i = 0; - while (i < xs.length && i < ys.length) { - const comparison = compare(xs[i], ys[i], elementCompare); - if (comparison !== 0) { - return comparison; - } - i++; - } - return xs.length - ys.length; -} - -export function getColumnProps( - title: string, - key: keyof RecordType, -) { - return { - title: title, - key: key, - // dataIndex: key, - }; -} - -export function getDateTimeColumnProps( - title: string, - key: keyof RecordType, - getValue: ( - record: RecordType, - ) => Scalars["DateTime"]["output"] | null | undefined, -) { - return { - ...getColumnProps(title, key), - sorter: (a: RecordType, b: RecordType) => - compare( - getValue(a), - getValue(b), - (x, y) => Date.parse(y) - Date.parse(x), - ), - sortDirections: sortDirections, - }; -} - -export function getTimestampColumnProps< - RecordType extends { timestamp: Scalars["DateTime"]["output"] }, ->() { - return { - ...getDateTimeColumnProps( - "Timestamp", - "timestamp", - (record) => record.timestamp, - ), - }; -} - -export function getStringColumnProps( - title: string, - key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined, -) { - return { - ...getColumnProps(title, key), - sorter: (a: RecordType, b: RecordType) => - compare(getValue(a), getValue(b), (x, y) => x.localeCompare(y, "en")), - sortDirections: sortDirections, - }; -} - -export function getFilterableStringColumnProps( - title: string, - key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined, - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, - doRender: ( - record: RecordType, - highlightedValue: JSX.Element, - value: string | null | undefined, - ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue, -) { - return { - ...getStringColumnProps(title, key, getValue), - ...getFreeTextFilterProps(getValue, onFilterTextChange(key)), - render: (_text: string, record: RecordType, _index: number) => - doRender( - record, - , - getValue(record), - ), - }; -} - -export function getInternallyLinkedFilterableStringColumnProps( - title: string, - key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined, - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (record: RecordType) => Route, -) { - return getFilterableStringColumnProps( - title, - key, - getValue, - onFilterTextChange, - getFilterText, - (record, _highlightedValue, value) => - value ? ( - // TODO Why does this not work with `_highlightedValue`? An error is raised saying "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?": https://nextjs.org/docs/api-reference/next/link#if-the-child-is-a-function-component or https://reactjs.org/docs/forwarding-refs.html or https://deepscan.io/docs/rules/react-func-component-invalid-ref-prop or https://www.carlrippon.com/react-forwardref-typescript/ - {value} - ) : ( - <> - ), - ); -} - -export function getExternallyLinkedFilterableStringColumnProps( - title: string, - key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined, - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (record: RecordType) => string, -) { - return getFilterableStringColumnProps( - title, - key, - getValue, - onFilterTextChange, - getFilterText, - (record, highlightedValue, _value) => { - const href = getPath(record); - return href ? ( - {highlightedValue} - ) : ( - <> - ); - }, - ); -} - -export function getExternallyLinkedFilterableLocatorColumnProps( - title: string, - key: keyof RecordType, - getValue: (record: RecordType) => string | null | undefined, - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableStringColumnProps( - title, - key, - getValue, - onFilterTextChange, - getFilterText, - (record, highlightedValue, _value) => { - const href = getValue(record); - return href ? ( - {highlightedValue} - ) : ( - <> - ); - }, - ); -} - -export function getUuidColumnProps< - RecordType extends { uuid: Scalars["Uuid"]["output"] }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, - getPath: (uuid: Scalars["Uuid"]["output"]) => Route, -) { - return getInternallyLinkedFilterableStringColumnProps( - "UUID", - "uuid", - (record) => record.uuid, - onFilterTextChange, - getFilterText, - (record) => getPath(record.uuid), - ); -} - -export function getNameColumnProps( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableStringColumnProps( - "Name", - "name", - (record) => record.name, - onFilterTextChange, - getFilterText, - ); -} - -export function getDescriptionColumnProps< - RecordType extends { description?: string | null }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableStringColumnProps( - "Description", - "description", - (record) => record.description, - onFilterTextChange, - getFilterText, - ); -} - -export function getAbbreviationColumnProps< - RecordType extends { abbreviation?: string | null }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableStringColumnProps( - "Abbreviation", - "abbreviation", - (record) => record.abbreviation, - onFilterTextChange, - getFilterText, - ); -} - -// TODO Use `EnumType extends enum` once there is an `enum` constraint as asked for in https://github.com/microsoft/TypeScript/issues/30611 -export function getEnumListColumnProps( - title: string, - key: keyof RecordType, - getValues: (record: RecordType) => EnumType[] | null | undefined, -) { - return { - ...getColumnProps(title, key), - sorter: (a: RecordType, b: RecordType) => - compare(getValues(a), getValues(b), (xs, ys) => - compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")), - ), - sortDirections: sortDirections, - }; -} - -export function getFilterableEnumListColumnProps< - RecordType, - EnumType extends string, ->( - title: string, - key: keyof RecordType, - entries: [string, EnumType][], - getValues: (record: RecordType) => EnumType[] | null | undefined, - // TODO Call `onFilterTextChange` when the filter text changes (that is not done right now and that is the reason why matches are not highlighted). Note though that it cannot be called inside `onFilter` because that function is being called on render and we may not change state on render! - _onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, - doRender: ( - record: RecordType, - highlightedValue: JSX.Element, - value: EnumType, - ) => JSX.Element = (_record, highlightedValue, _value) => highlightedValue, -) { - return { - ...getEnumListColumnProps(title, key, getValues), - filters: entries.map(([key, value]) => ({ - text: key, - value: value, - })), - onFilter: (value: React.Key | boolean, record: RecordType) => { - const values = getValues(record); - if ( - typeof value === "string" && - entries - .map(([_enumKey, enumValue]) => enumValue.toString()) - .includes(value) - ) { - return values?.includes(value as EnumType) || false; - } - return true; - }, - render: (_value: string, record: RecordType, _index: number) => { - const values = getValues(record); - if (values == null) { - return null; - } else { - return ( - - {values.map((value) => ( - - {doRender( - record, - , - value, - )} - - ))} - - ); - } - }, - }; -} - -export function getDescriptionListColumnProps( - title: string, - key: keyof RecordType, - getEntries: (record: RecordType) => - | { - key: string; - title: string; - value: string | null | undefined; - }[] - | null - | undefined, -) { - return { - ...getColumnProps(title, key), - sorter: (a: RecordType, b: RecordType) => { - const aValues = getEntries(a)?.map((x) => x.value); - const bValues = getEntries(b)?.map((x) => x.value); - return compare(aValues, bValues, (xs, ys) => - compareLexicographically(xs, ys, (x, y) => x.localeCompare(y, "en")), - ); - }, - sortDirections: sortDirections, - }; -} - -const titleValueDescriptionListSeparator = ": "; - -function splitDescriptionListFilterText(filterText: string | null | undefined) { - if (filterText == null) { - return [null, null]; - } - const filterTexts = filterText.split(titleValueDescriptionListSeparator); - switch (filterTexts.length) { - case 1: - return [null, filterTexts[0]]; - case 2: - return [filterTexts[0], filterTexts[1]]; - default: - return [ - filterTexts[0], - // TODO There are more efficient ways to do this than creating a new array without the first entry just to join them later on. - filterTexts.slice(1, -1).join(titleValueDescriptionListSeparator), - ]; - } -} - -export function getFilterableDescriptionListColumnProps( - title: string, - key: keyof RecordType, - getEntries: (record: RecordType) => - | { - key: string; - title: string; - value: string | null | undefined; - render?: ( - record: RecordType, - highlightedValue: JSX.Element, - value: string | null | undefined, - ) => JSX.Element; - }[] - | null - | undefined, - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return { - ...getDescriptionListColumnProps(title, key, getEntries), - ...getFreeTextFilterProps( - (record) => - getEntries(record) - ?.map( - ({ title, value }) => - `${title}${titleValueDescriptionListSeparator}${value}`, - ) - ?.join("\n"), - onFilterTextChange(key), - ), - render: (_value: string, record: RecordType, _index: number) => { - const titlesAndValues = getEntries(record); - if (titlesAndValues == null) { - return null; - } else { - return ( - - {titlesAndValues.map( - ({ - key: entryKey, - title, - value, - render = (_record, highlightedValue, _value) => - highlightedValue, - }) => { - const [titleFilterText, valueFilterText] = - splitDescriptionListFilterText(getFilterText(key)); - return ( - } - > - {render( - record, - titleFilterText === null || - doesFieldIncludeFilterValue(title, titleFilterText) ? ( - - ) : ( - - ), - value, - )} - - ); - }, - )} - - ); - } - }, - }; -} - -export function getReferenceColumnProps< - RecordType extends { reference?: Publication | Standard | null }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableDescriptionListColumnProps( - "Reference", - "reference", - (record) => - record.reference && [ - { - key: "title", - title: "Title", - value: record.reference.title, - }, - { - key: "abstract", - title: "Abstract", - value: record.reference.abstract, - }, - { - key: "section", - title: "Section", - value: record.reference.section, - }, - ...(record.reference.__typename !== "Standard" - ? [] - : [ - { - key: "numeration", - title: "Numeration", - value: `${record.reference.numeration.prefix} ${record.reference.numeration.mainNumber} ${record.reference.numeration.suffix}`, - }, - { - key: "year", - title: "Year", - value: record.reference.year, - }, - { - key: "locator", - title: "Locator", - value: record.reference.locator, - render: ( - _record: RecordType, - hightlightedValue: JSX.Element, - value: string | null | undefined, - ) => ( - // TODO Actually, `value` is neither `null` nor `undefined` but the type system does not know about it. How can we make it know about it so we don't need `|| ""` here? - - {hightlightedValue} - - ), - }, - { - key: "Standardizers", - title: "Standardizers", - value: record.reference.standardizers.join(", "), - }, - ]), - ...(record.reference.__typename !== "Publication" - ? [] - : [ - { - key: "arXiv", - title: "arXiv", - value: record.reference.arXiv, - }, - { - key: "doi", - title: "DOI", - value: record.reference.doi, - }, - { - key: "urn", - title: "URN", - value: record.reference.urn, - }, - { - key: "webAddress", - title: "Website", - value: record.reference.webAddress, - }, - { - key: "authors", - title: "Authors", - value: record.reference.authors?.join(", "), - }, - ]), - ], - onFilterTextChange, - getFilterText, - ); -} - -export function getComponentUuidColumnProps< - RecordType extends { componentId: Scalars["Uuid"]["output"] }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getExternallyLinkedFilterableStringColumnProps( - "Component UUID", - "componentId", - (x) => x.componentId, - onFilterTextChange, - getFilterText, - (x) => paths.metabase.component(x.componentId), - ); -} - -export function getAppliedMethodColumnProps< - RecordType extends { appliedMethod: { methodId: Scalars["Uuid"]["output"] } }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableDescriptionListColumnProps( - "Applied Method", - "appliedMethod", - (x) => [ - { - key: "appliedMethodId", - title: "UUID", - value: x.appliedMethod.methodId, - render: (_record, highlightedValue, _value) => ( - - {highlightedValue} - - ), - }, - // { - // key: "appliedMethodName", - // title: "Name", - // value: x.appliedMethod.method?.name, - // }, - // { - // key: "appliedMethodDescription", - // title: "Description", - // value: x.appliedMethod.method?.description, - // }, - ], - onFilterTextChange, - getFilterText, - ); -} - -export function getResourceTreeColumnProps< - RecordType extends { - resourceTree: { - root: { - value: { - description?: string | null | undefined; - hashValue: string; - locator: Scalars["Url"]["output"]; - dataFormatId: Scalars["Uuid"]["output"]; - }; - }; - }; - }, ->( - onFilterTextChange: ( - key: keyof RecordType, - ) => (newFilterText: string) => void, - getFilterText: (key: keyof RecordType) => string | undefined, -) { - return getFilterableDescriptionListColumnProps( - "Resource Tree Root", - "resourceTree", - (x) => [ - { - key: "description", - title: "Description", - value: x.resourceTree.root.value.description, - }, - { - key: "hashValue", - title: "Hash Value", - value: x.resourceTree.root.value.hashValue, - }, - { - key: "locator", - title: "Locator", - value: x.resourceTree.root.value.locator, - render: (_record, hightlightedValue, _value) => ( - - {hightlightedValue} - - ), - }, - { - key: "dataFormatId", - title: "Data Format UUID", - value: x.resourceTree.root.value.dataFormatId, - render: (_record, hightlightedValue, _value) => ( - - {hightlightedValue} - - ), - }, - ], - onFilterTextChange, - getFilterText, - ); -} diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 9960a61d..abf898cb 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - output: "standalone", // https://nextjs.org/docs/app/api-reference/config/next-config-js/output#automatically-copying-traced-files + output: "standalone", reactStrictMode: true, typedRoutes: true, allowedDevOrigins: process.env.HOST == null ? [] : [`*.${process.env.HOST}`], diff --git a/frontend/package.json b/frontend/package.json index 8660286c..11258900 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,19 +1,20 @@ { "name": "frontend", - "version": "v1.1.0", + "version": "v2.0.0", "private": true, "author": "Simon Wacker", "license": "MIT", "scripts": { "codegen": "graphql-codegen --config ./codegen.ts", "codegen:watch": "graphql-codegen --config ./codegen.ts --watch", - "dev": "yarn refresh-baseline-browser-mapping && concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_TLS_REJECT_UNAUTHORIZED=0 NEXT_WEBPACK_USEPOLLING=false next dev --port 8080'", - "build": "yarn refresh-baseline-browser-mapping && yarn codegen && NODE_ENV=production next build", - "test": "yarn codegen && NODE_TLS_REJECT_UNAUTHORIZED=0 jest", + "dev": "concurrently --kill-others-on-fail 'yarn run codegen:watch' 'NODE_TLS_REJECT_UNAUTHORIZED=0 NEXT_WEBPACK_USEPOLLING=false next dev --port 8080'", + "build": "yarn codegen && NODE_ENV=production next build", "start": "next start --port 8080", - "refresh-baseline-browser-mapping": "yarn upgrade baseline-browser-mapping@latest" + "declutter": "knip", + "browsers": "browserslist" }, "dependencies": { + "@ant-design/icons": "^6.2.5", "@ant-design/pro-layout": "^7.10.3", "@apollo/client": "^4.0.9", "@parcel/watcher": "^2.5.1", @@ -25,42 +26,32 @@ "react": "^19.2.1", "react-cookie": "^8.0.1", "react-dom": "^19.2.1", - "react-highlight-words": "^0.21.0", - "rxjs": "^7.8.2", - "sharp": "^0.34.5" + "rxjs": "^7.8.2" }, "devDependencies": { - "@babel/core": "^7.0", - "@graphql-codegen/cli": "^6.0.2", - "@graphql-codegen/fragment-matcher": "^6.0.0", + "@graphql-codegen/cli": "^7.0.0", + "@graphql-codegen/fragment-matcher": "^7.0.0", "@graphql-codegen/near-operation-file-preset": "^5.0.0", - "@graphql-codegen/typescript": "^5.0.4", - "@graphql-codegen/typescript-operations": "^5.0.4", + "@graphql-codegen/typed-document-node": "^7.0.2", + "@graphql-codegen/typescript": "^6.0.1", + "@graphql-codegen/typescript-apollo-client-helpers": "^4.0.0", + "@graphql-codegen/typescript-operations": "^6.0.2", "@types/node": "^24.10.1", "@types/react": "^19.2.7", "@types/react-dom": "^19.2.3", - "@types/react-highlight-words": "^0.20.0", - "@types/react-test-renderer": "^19.1.0", - "babel-jest": "^30.2.0", - "baseline-browser-mapping": "^2.10.30", - "concurrently": "^9.2.1", - "deepmerge": "^4.0", + "browserslist": "^4.28.2", + "concurrently": "^10.0.0", "eslint": "^10.1.0", "eslint-config-next": "^16.0.8", - "graphql-tag": "^2.0", - "jest": "^30.2.0", + "knip": "^6.14.2", "license-checker": "^25.0.1", "prettier": "^3.3.2", - "react-test-renderer": "^19.2.0", - "ts-node": "^10.9.1", "typescript": "^6.0.3", "yaml-loader": "^0.9.0" }, "browserslist": { "production": [ - ">0.2%", - "not dead", - "not op_mini all" + "defaults" ], "development": [ "last 1 chrome version", diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index a14175c8..02d6d388 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -5,6 +5,7 @@ import { CookiesProvider } from "react-cookie"; import { App, ConfigProvider } from "antd"; import { ReactNode, useEffect, useState } from "react"; import paths from "../paths"; +import "antd/dist/reset.css"; import "../styles/global.css"; function AntiforgeryProvider({ children }: { children: ReactNode }) { diff --git a/frontend/pages/data/calorimetric/[uuid].tsx b/frontend/pages/data/calorimetric/[uuid].tsx index 23cbdc45..d7ca5d6b 100644 --- a/frontend/pages/data/calorimetric/[uuid].tsx +++ b/frontend/pages/data/calorimetric/[uuid].tsx @@ -1,6 +1,7 @@ import { useRouter } from "next/router"; import CalorimetricData from "../../../components/data/calorimetric/CalorimetricData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); @@ -12,9 +13,17 @@ function Page() { const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } diff --git a/frontend/pages/data/calorimetric/index.tsx b/frontend/pages/data/calorimetric/index.tsx index 7c95fb94..8c5530e9 100644 --- a/frontend/pages/data/calorimetric/index.tsx +++ b/frontend/pages/data/calorimetric/index.tsx @@ -1,332 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Typography, Descriptions } from "antd"; -import { AllCalorimetricDataDocument } from "../../../queries/data.generated"; -import { - CalorimetricData, - Scalars, - CalorimetricDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - FloatPropositionComparator, - FloatPropositionFormList, -} from "../../../components/FloatPropositionFormList"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: CalorimetricDataPropositionInput, -): CalorimetricDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: CalorimetricDataPropositionInput[], -): CalorimetricDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: CalorimetricDataPropositionInput[] -// ): CalorimetricDataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allCalorimetricDataQuery = useQuery(AllCalorimetricDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - gValues, - uValues, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - gValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - uValues: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: CalorimetricDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - // Note that `0` evaluates to `false`, so below we cannot use - // `if (value)`. - if (gValues) { - for (let { negator, comparator, value } of gValues) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - gValues: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - if (uValues) { - for (let { negator, comparator, value } of uValues) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - uValues: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - const { error, data } = await allCalorimetricDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error(error.message); - } - // TODO Casting to `CalorimetricData` is wrong and error prone! - setData( - (data?.allCalorimetricData?.edges?.map((x) => x.node) || - []) as CalorimetricData[], - ); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedCalorimetricData from "../../../components/data/calorimetric/PaginatedCalorimetricData"; +export default function Page() { return ( - Calorimetric Data - -
    - - - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.calorimetricDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - title: "g and u Values", - key: "g-and-u-values", - render: (_text, record, _index) => ( - - - {record.gValues.map((x) => x.toLocaleString("en")).join(", ")} - - - {record.uValues.map((x) => x.toLocaleString("en")).join(", ")} - - - ), - }, - ]} - dataSource={data} - /> + ); } - -export default Page; diff --git a/frontend/pages/data/create.tsx b/frontend/pages/data/create.tsx index 8c7ec9d9..e6323db0 100644 --- a/frontend/pages/data/create.tsx +++ b/frontend/pages/data/create.tsx @@ -8,7 +8,7 @@ function Page() { - + Create data by sending GraphQL mutations to the{" "} GraphQL endpoint diff --git a/frontend/pages/data/geometric/[uuid].tsx b/frontend/pages/data/geometric/[uuid].tsx index 8a01c9b8..67a5f18d 100644 --- a/frontend/pages/data/geometric/[uuid].tsx +++ b/frontend/pages/data/geometric/[uuid].tsx @@ -1,20 +1,31 @@ import { useRouter } from "next/router"; import GeometricData from "../../../components/data/geometric/GeometricData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); if (!router.isReady) { + // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 return null; } const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } + export default Page; diff --git a/frontend/pages/data/geometric/index.tsx b/frontend/pages/data/geometric/index.tsx index 581412f9..1b2cffc4 100644 --- a/frontend/pages/data/geometric/index.tsx +++ b/frontend/pages/data/geometric/index.tsx @@ -1,269 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Descriptions, Typography } from "antd"; -import { AllGeometricDataDocument } from "../../../queries/data.generated"; -import { - GeometricData, - Scalars, - GeometricDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - FloatPropositionComparator, - FloatPropositionFormList, -} from "../../../components/FloatPropositionFormList"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; - -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: GeometricDataPropositionInput, -): GeometricDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - return proposition; - } -}; - -const conjunct = ( - propositions: GeometricDataPropositionInput[], -): GeometricDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - - const allGeometricDataQuery = useQuery(AllGeometricDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - thicknesses, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - thicknesses: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - const propositions: GeometricDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - if (thicknesses) { - for (let { negator, comparator, value } of thicknesses) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - thicknesses: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - const { error, data } = await allGeometricDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - console.log(error); - message.error(error.message); - } - setData( - (data?.allGeometricData?.edges?.map((x) => x.node) || - []) as GeometricData[], - ); - } catch (error) { - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedGeometricData from "../../../components/data/geometric/PaginatedGeometricData"; +export default function Page() { return ( - Geometric Data - -
    - - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.geometricDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - title: "Thicknesses", - key: "thicknesses", - render: (_text, record, _index) => ( - - - {record.thicknesses - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - ), - }, - ]} - dataSource={data} - /> + ); } -export default Page; diff --git a/frontend/pages/data/hygrothermal/[uuid].tsx b/frontend/pages/data/hygrothermal/[uuid].tsx index 753c898c..cf1cfdfb 100644 --- a/frontend/pages/data/hygrothermal/[uuid].tsx +++ b/frontend/pages/data/hygrothermal/[uuid].tsx @@ -1,6 +1,7 @@ import { useRouter } from "next/router"; import HygrothermalData from "../../../components/data/hygrothermal/HygrothermalData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); @@ -12,9 +13,17 @@ function Page() { const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } diff --git a/frontend/pages/data/hygrothermal/index.tsx b/frontend/pages/data/hygrothermal/index.tsx index 33cd281b..56020114 100644 --- a/frontend/pages/data/hygrothermal/index.tsx +++ b/frontend/pages/data/hygrothermal/index.tsx @@ -1,254 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Typography } from "antd"; -import { AllHygrothermalDataDocument } from "../../../queries/data.generated"; -import { - HygrothermalData, - Scalars, - HygrothermalDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: HygrothermalDataPropositionInput, -): HygrothermalDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: HygrothermalDataPropositionInput[], -): HygrothermalDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: HygrothermalDataPropositionInput[] -// ): HygrothermalDataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allHygrothermalDataQuery = useQuery(AllHygrothermalDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: HygrothermalDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - const { error, data } = await allHygrothermalDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error(error.message); - } - // TODO Casting to `HygrothermalData` is wrong and error prone! - setData( - (data?.allHygrothermalData?.edges?.map((x) => x.node) || - []) as HygrothermalData[], - ); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedHygrothermalData from "../../../components/data/hygrothermal/PaginatedHygrothermalData"; +export default function Page() { return ( - Hygrothermal Data - -
    - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.hygrothermalDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - ]} - dataSource={data} - /> + ); } - -export default Page; diff --git a/frontend/pages/data/life-cycle/[uuid].tsx b/frontend/pages/data/life-cycle/[uuid].tsx index fc39e90c..a230ab1d 100644 --- a/frontend/pages/data/life-cycle/[uuid].tsx +++ b/frontend/pages/data/life-cycle/[uuid].tsx @@ -1,6 +1,7 @@ import { useRouter } from "next/router"; import LifeCycleData from "../../../components/data/lifeCycle/LifeCycleData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); @@ -12,9 +13,17 @@ function Page() { const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } diff --git a/frontend/pages/data/life-cycle/index.tsx b/frontend/pages/data/life-cycle/index.tsx index ecc010c0..6f149b09 100644 --- a/frontend/pages/data/life-cycle/index.tsx +++ b/frontend/pages/data/life-cycle/index.tsx @@ -1,254 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Typography } from "antd"; -import { AllLifeCycleDataDocument } from "../../../queries/data.generated"; -import { - LifeCycleData, - Scalars, - LifeCycleDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: LifeCycleDataPropositionInput, -): LifeCycleDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: LifeCycleDataPropositionInput[], -): LifeCycleDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: LifeCycleDataPropositionInput[] -// ): LifeCycleDataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allLifeCycleDataQuery = useQuery(AllLifeCycleDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: LifeCycleDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - const { error, data } = await allLifeCycleDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error(error.message); - } - // TODO Casting to `LifeCycleData` is wrong and error prone! - setData( - (data?.allLifeCycleData?.edges?.map((x) => x.node) || - []) as LifeCycleData[], - ); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedLifeCycleData from "../../../components/data/lifeCycle/PaginatedLifeCycleData"; +export default function Page() { return ( - Life-Cycle Data - -
    - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.lifeCycleDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - ]} - dataSource={data} - /> + ); } - -export default Page; diff --git a/frontend/pages/data/optical/[uuid].tsx b/frontend/pages/data/optical/[uuid].tsx index d6129cb9..faa808e1 100644 --- a/frontend/pages/data/optical/[uuid].tsx +++ b/frontend/pages/data/optical/[uuid].tsx @@ -1,6 +1,7 @@ import { useRouter } from "next/router"; import OpticalData from "../../../components/data/optical/OpticalData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); @@ -12,9 +13,17 @@ function Page() { const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } diff --git a/frontend/pages/data/optical/index.tsx b/frontend/pages/data/optical/index.tsx index 0215eedd..afd793cf 100644 --- a/frontend/pages/data/optical/index.tsx +++ b/frontend/pages/data/optical/index.tsx @@ -1,469 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Typography, Descriptions } from "antd"; -import { AllOpticalDataDocument } from "../../../queries/data.generated"; -import { - OpticalData, - Scalars, - OpticalDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - FloatPropositionComparator, - FloatPropositionFormList, -} from "../../../components/FloatPropositionFormList"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: OpticalDataPropositionInput, -): OpticalDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: OpticalDataPropositionInput[], -): OpticalDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: OpticalDataPropositionInput[] -// ): OpticalDataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allOpticalDataQuery = useQuery(AllOpticalDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - infraredEmittances, - nearnormalHemisphericalSolarReflectances, - nearnormalHemisphericalSolarTransmittances, - nearnormalHemisphericalVisibleReflectances, - nearnormalHemisphericalVisibleTransmittances, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - infraredEmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - nearnormalHemisphericalSolarReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - nearnormalHemisphericalSolarTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - nearnormalHemisphericalVisibleReflectances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - nearnormalHemisphericalVisibleTransmittances: - | { - negator: Negator; - comparator: FloatPropositionComparator; - value: number | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: OpticalDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - // Note that `0` evaluates to `false`, so below we cannot use - // `if (value)`. - if (infraredEmittances) { - for (let { negator, comparator, value } of infraredEmittances) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - infraredEmittances: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - if (nearnormalHemisphericalSolarReflectances) { - for (let { - negator, - comparator, - value, - } of nearnormalHemisphericalSolarReflectances) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - nearnormalHemisphericalSolarReflectances: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - if (nearnormalHemisphericalSolarTransmittances) { - for (let { - negator, - comparator, - value, - } of nearnormalHemisphericalSolarTransmittances) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - nearnormalHemisphericalSolarTransmittances: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - if (nearnormalHemisphericalVisibleReflectances) { - for (let { - negator, - comparator, - value, - } of nearnormalHemisphericalVisibleReflectances) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - nearnormalHemisphericalVisibleReflectances: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - if (nearnormalHemisphericalVisibleTransmittances) { - for (let { - negator, - comparator, - value, - } of nearnormalHemisphericalVisibleTransmittances) { - if (value !== undefined && value !== null) { - propositions.push( - negateIfNecessary(negator, { - nearnormalHemisphericalVisibleTransmittances: { - some: { - [comparator]: value, - }, - }, - }), - ); - } - } - } - const { error, data } = await allOpticalDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error(error.message); - } - // TODO Casting to `OpticalData` is wrong and error prone! - setData( - (data?.allOpticalData?.edges?.map((x) => x.node) || - []) as OpticalData[], - ); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedOpticalData from "../../../components/data/optical/PaginatedOpticalData"; +export default function Page() { return ( - Optical Data - -
    - - - - - - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.opticalDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - title: "Emittances, Reflectances, and Transmittances", - key: "nearnormalHemisphericalX", - render: (_text, record, _index) => ( - - - {record.infraredEmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {record.nearnormalHemisphericalSolarReflectances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {record.nearnormalHemisphericalSolarTransmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {record.nearnormalHemisphericalVisibleReflectances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - {record.nearnormalHemisphericalVisibleTransmittances - .map((x) => x.toLocaleString("en")) - .join(", ")} - - - ), - }, - ]} - dataSource={data} - /> + ); } - -export default Page; diff --git a/frontend/pages/data/photovoltaic/[uuid].tsx b/frontend/pages/data/photovoltaic/[uuid].tsx index 708e4e3e..84a4783e 100644 --- a/frontend/pages/data/photovoltaic/[uuid].tsx +++ b/frontend/pages/data/photovoltaic/[uuid].tsx @@ -1,6 +1,7 @@ import { useRouter } from "next/router"; import PhotovoltaicData from "../../../components/data/photovoltaic/PhotovoltaicData"; import Layout from "../../../components/Layout"; +import { Skeleton } from "antd"; function Page() { const router = useRouter(); @@ -12,9 +13,17 @@ function Page() { const { uuid } = router.query; + if (!uuid) { + return ( + + + + ); + } + return ( - + ); } diff --git a/frontend/pages/data/photovoltaic/index.tsx b/frontend/pages/data/photovoltaic/index.tsx index f1ffca90..8a759484 100644 --- a/frontend/pages/data/photovoltaic/index.tsx +++ b/frontend/pages/data/photovoltaic/index.tsx @@ -1,254 +1,10 @@ import Layout from "../../../components/Layout"; -import { Table, App, Form, Button, Typography } from "antd"; -import { AllPhotovoltaicDataDocument } from "../../../queries/data.generated"; -import { - PhotovoltaicData, - Scalars, - PhotovoltaicDataPropositionInput, -} from "../../../__generated__/graphql"; -import { useState } from "react"; -import { setMapValue } from "../../../lib/freeTextFilter"; -import { - getAppliedMethodColumnProps, - getComponentUuidColumnProps, - getDescriptionColumnProps, - getNameColumnProps, - getResourceTreeColumnProps, - getTimestampColumnProps, - getUuidColumnProps, -} from "../../../lib/table"; -import { - UuidPropositionComparator, - UuidPropositionFormList, -} from "../../../components/UuidPropositionFormList"; -import paths from "../../../paths"; -import { useQuery } from "@apollo/client/react"; -import ErrorAlert from "../../../components/ErrorAlert"; - -const layout = { - labelCol: { span: 8 }, - wrapperCol: { span: 16 }, -}; -const tailLayout = { - wrapperCol: { offset: 8, span: 16 }, -}; - -enum Negator { - Is = "is", - IsNot = "isNot", -} - -const negateIfNecessary = ( - negator: Negator, - proposition: PhotovoltaicDataPropositionInput, -): PhotovoltaicDataPropositionInput => { - switch (negator) { - case Negator.Is: - return proposition; - case Negator.IsNot: - // return { not: proposition }; - return proposition; // TODO Support `not` in filter! - } -}; - -const conjunct = ( - propositions: PhotovoltaicDataPropositionInput[], -): PhotovoltaicDataPropositionInput => { - if (propositions.length == 0) { - return {}; - } - if (propositions.length == 1) { - return propositions[0]; - } - return { and: propositions }; -}; - -// const disjunct = ( -// propositions: PhotovoltaicDataPropositionInput[] -// ): PhotovoltaicDataPropositionInput => { -// if (propositions.length == 0) { -// return {}; -// } -// if (propositions.length == 1) { -// return propositions[0]; -// } -// return { or: propositions }; -// }; - -function Page() { - const [form] = Form.useForm(); - const [filtering, setFiltering] = useState(false); - const [globalErrorMessages, setGlobalErrorMessages] = useState( - new Array(), - ); - const { message } = App.useApp(); - const [data, setData] = useState([]); - // Using `skip` is inspired by https://github.com/apollographql/apollo-client/issues/5268#issuecomment-749501801 - // An alternative would be `useLazy...` as told in https://github.com/apollographql/apollo-client/issues/5268#issuecomment-527727653 - // `useLazy...` does not return a `Promise` though as `use...Query.refetch` does which is used below. - // For error policies see https://www.apollographql.com/docs/react/v2/data/error-handling/#error-policies - const allPhotovoltaicDataQuery = useQuery(AllPhotovoltaicDataDocument, { - skip: true, - errorPolicy: "all", - }); - - const [filterText, setFilterText] = useState(() => new Map()); - const onFilterTextChange = setMapValue(filterText, setFilterText); - - const onFinish = ({ - componentIds, - dataFormatIds, - }: { - componentIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - dataFormatIds: - | { - negator: Negator; - comparator: UuidPropositionComparator; - value: Scalars["Uuid"]["input"] | undefined; - }[] - | undefined; - }) => { - const filter = async () => { - try { - setFiltering(true); - // https://www.apollographql.com/docs/react/networking/authentication/#reset-store-on-logout - const propositions: PhotovoltaicDataPropositionInput[] = []; - if (componentIds) { - for (let { negator, comparator, value } of componentIds) { - propositions.push( - negateIfNecessary(negator, { - componentId: { [comparator]: value }, - }), - ); - } - } - if (dataFormatIds) { - for (let { negator, comparator, value } of dataFormatIds) { - propositions.push( - negateIfNecessary(negator, { - resources: { - some: { - dataFormatId: { [comparator]: value }, - }, - }, - }), - ); - } - } - const { error, data } = await allPhotovoltaicDataQuery.refetch( - propositions.length == 0 - ? {} - : { - where: conjunct(propositions), - }, - ); - if (error) { - // TODO Handle properly. - console.log(error); - message.error(error.message); - } - // TODO Casting to `PhotovoltaicData` is wrong and error prone! - setData( - (data?.allPhotovoltaicData?.edges?.map((x) => x.node) || - []) as PhotovoltaicData[], - ); - } catch (error) { - // TODO Handle properly. - console.log("Failed:", error); - } finally { - setFiltering(false); - } - }; - filter(); - }; - - const onFinishFailed = () => { - setGlobalErrorMessages(["Fix the errors below."]); - }; +import PaginatedPhotovoltaicData from "../../../components/data/photovoltaic/PaginatedPhotovoltaicData"; +export default function Page() { return ( - Photovoltaic Data - -
    - - - - - - - -
    ( - onFilterTextChange, - (x) => filterText.get(x), - paths.photovoltaicDatum, - ), - }, - { - ...getNameColumnProps<(typeof data)[0]>(onFilterTextChange, (x) => - filterText.get(x), - ), - }, - { - ...getDescriptionColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getTimestampColumnProps<(typeof data)[0]>(), - }, - { - ...getComponentUuidColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - // { - // ...getInternallyLinkedFilterableStringColumnProps( - // "Database UUID", - // "databaseId", - // (x) => x.databaseId, - // onFilterTextChange, - // (x) => filterText.get(x), - // (x) => paths.database(x.databaseId) - // ), - // }, - { - ...getAppliedMethodColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - { - ...getResourceTreeColumnProps<(typeof data)[0]>( - onFilterTextChange, - (x) => filterText.get(x), - ), - }, - ]} - dataSource={data} - /> + ); } - -export default Page; diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index 2be04d8a..90105e43 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,5 +1,5 @@ import Layout from "../components/Layout"; -import { Typography } from "antd"; +import { Card, Typography } from "antd"; import Link from "next/link"; import paths from "../paths"; import Image from "next/image"; @@ -8,8 +8,8 @@ import overviewImage from "../public/overview.png"; function Page() { return ( -
    - + + solarbuildingenvelopes.com is the website of the product data server of{" "} @@ -39,17 +39,17 @@ function Page() { . - + This website is the frontend of the product data server of{" "} TestLab Solar Facades . You can use this website to search this product data server for{" "} - optical data,{" "} - optical data,{" "} - calorimetric data and{" "} - photovoltaic data. If you - would like to search the entire Product Data Network{" "} + optical data,{" "} + optical data,{" "} + calorimetric data and{" "} + photovoltaic data. If + you would like to search the entire Product Data Network{" "} buildingenvelopedata.org @@ -84,7 +84,7 @@ function Page() { . - + This website is completely based on the {" "} @@ -109,7 +109,7 @@ function Page() { is then a convenient way to query for data, components, institutions, databases, data formats and methods. - + The data format{" "} -
    +
    ); } diff --git a/frontend/pages/unauthorized.tsx b/frontend/pages/unauthorized.tsx index 6dd3905d..b6f343a4 100644 --- a/frontend/pages/unauthorized.tsx +++ b/frontend/pages/unauthorized.tsx @@ -1,16 +1,26 @@ import { Result } from "antd"; import Layout from "../components/Layout"; +import Link from "next/link"; +import paths from "../paths"; -function Page() { +export default function Page() { return ( + Login + + } /> ); } - -export default Page; diff --git a/frontend/pages/upload-file.tsx b/frontend/pages/upload-file.tsx index 9656e255..6b0a8d80 100644 --- a/frontend/pages/upload-file.tsx +++ b/frontend/pages/upload-file.tsx @@ -28,7 +28,7 @@ function Page() {
    - + For an existing GET HTTPS resource entry in the database, upload a file with the content for that resource. diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index 813c8cf6..c6c5134e 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,22 +1,15 @@ import { useQuery } from "@apollo/client/react"; import Layout from "../components/Layout"; import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; -import { Skeleton, Result, Descriptions, Typography, App } from "antd"; +import { Skeleton, Result, Descriptions, Typography } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; -import { useEffect } from "react"; -import { stringifyApolloError } from "../lib/apollo"; import paths from "../paths"; +import { useQueryHandler } from "../lib/hooks/useQueryHandler"; function Page() { const { loading, error, data } = useQuery(CurrentUserInfoDocument); + useQueryHandler({ error }); const currentUserInfo = data?.currentUserInfo; - const { message } = App.useApp(); - - useEffect(() => { - if (error) { - message.error(stringifyApolloError(error)); - } - }, [error]); if (loading) { return ; diff --git a/frontend/paths.ts b/frontend/paths.ts index 4909e06f..33925841 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -1,4 +1,5 @@ import { Route } from "next"; +import { DataKind, Scalars } from "./__generated__/graphql"; const metabaseUrl = "https://www.buildingenvelopedata.org"; @@ -9,30 +10,24 @@ export default { antiforgeryToken: "/antiforgery/token" as Route, userInfo: "/user-info" as Route, database: "/database" as Route, - calorimetricData: "/data/calorimetric" as Route, - calorimetricDatum(uuid: string) { - return `/data/calorimetric/${encodeURIComponent(uuid)}` as Route; - }, - hygrothermalData: "/data/hygrothermal" as Route, - hygrothermalDatum(uuid: string) { - return `/data/hygrothermal/${encodeURIComponent(uuid)}` as Route; - }, - lifeCycleData: "/data/life-cycle" as Route, - lifeCycleDatum(uuid: string) { - return `/data/life-cycle/${encodeURIComponent(uuid)}` as Route; - }, - opticalData: "/data/optical" as Route, - opticalDatum(uuid: string) { - return `/data/optical/${encodeURIComponent(uuid)}` as Route; - }, - photovoltaicData: "/data/photovoltaic" as Route, - photovoltaicDatum(uuid: string) { - return `/data/photovoltaic/${encodeURIComponent(uuid)}` as Route; - }, - geometricData: "/data/geometric" as Route, - geometricDatum(uuid: string) { - return `/data/geometric/${encodeURIComponent(uuid)}` as Route; - }, + allCalorimetricData: "/data/calorimetric" as Route, + calorimetricData: (id: Scalars["Uuid"]["output"]) => + `/data/calorimetric/${encodeURIComponent(String(id))}` as Route, + allGeometricData: "/data/geometric" as Route, + geometricData: (id: Scalars["Uuid"]["output"]) => + `/data/geometric/${encodeURIComponent(String(id))}` as Route, + allHygrothermalData: "/data/hygrothermal" as Route, + hygrothermalData: (id: Scalars["Uuid"]["output"]) => + `/data/hygrothermal/${encodeURIComponent(String(id))}` as Route, + allLifeCycleData: "/data/life-cycle" as Route, + lifeCycleData: (id: Scalars["Uuid"]["output"]) => + `/data/life-cycle/${encodeURIComponent(String(id))}` as Route, + allOpticalData: "/data/optical" as Route, + opticalData: (id: Scalars["Uuid"]["output"]) => + `/data/optical/${encodeURIComponent(String(id))}` as Route, + allPhotovoltaicData: "/data/photovoltaic" as Route, + photovoltaicData: (id: Scalars["Uuid"]["output"]) => + `/data/photovoltaic/${encodeURIComponent(String(id))}` as Route, createData: "/data/create" as Route, uploadFile: "/upload-file" as Route, login: "/connect/login" as Route, @@ -40,45 +35,97 @@ export default { graphQl: "/graphql/" as Route, metabase: { home: metabaseUrl, + graphQl: new URL("/graphql/", metabaseUrl).href as Route, + register: new URL("/users/register", metabaseUrl).href as Route, components: new URL("/components", metabaseUrl).href as Route, - component(uuid: string) { - return new URL(`/components/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; - }, + component: (id: Scalars["Uuid"]["output"]) => + new URL(`/components/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, databases: new URL("/databases", metabaseUrl).href as Route, - database(uuid: string) { - return new URL(`/databases/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; - }, + database: (id: Scalars["Uuid"]["output"]) => + new URL(`/databases/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, dataFormats: new URL(`/data-formats`, metabaseUrl).href as Route, - dataFormat(uuid: string) { - return new URL(`/data-formats/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; - }, + dataFormat: (id: Scalars["Uuid"]["output"]) => + new URL(`/data-formats/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, + gnuPgKeys: new URL("/gnupg-keys", metabaseUrl).href as Route, + gnuPgKey: (fingerprint: string) => + new URL(`/gnupg-keys/${encodeURIComponent(fingerprint)}`, metabaseUrl) + .href as Route, institutions: new URL("/institutions", metabaseUrl).href as Route, - institution(uuid: string) { - return new URL(`/institutions/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; - }, + institution: (id: Scalars["Uuid"]["output"]) => + new URL(`/institutions/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, methods: new URL("/methods", metabaseUrl).href as Route, - method(uuid: string) { - return new URL(`/methods/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; - }, + method: (id: Scalars["Uuid"]["output"]) => + new URL(`/methods/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, users: new URL("/users", metabaseUrl).href as Route, - user(uuid: string) { - return new URL(`/users/${encodeURIComponent(uuid)}`, metabaseUrl) - .href as Route; + user: (id: Scalars["Uuid"]["output"]) => + new URL(`/users/${encodeURIComponent(String(id))}`, metabaseUrl) + .href as Route, + allData: "/data" as Route, + data( + databaseId: Scalars["Uuid"]["output"], + dataKind: DataKind, + id: Scalars["Uuid"]["output"], + ) { + switch (dataKind) { + case DataKind.CalorimetricData: + return this.calorimetricData(databaseId, id); + case DataKind.GeometricData: + return this.geometricData(databaseId, id); + case DataKind.HygrothermalData: + return this.hygrothermalData(databaseId, id); + case DataKind.LifeCycleData: + return this.lifeCycleData(databaseId, id); + case DataKind.OpticalData: + return this.opticalData(databaseId, id); + case DataKind.PhotovoltaicData: + return this.photovoltaicData(databaseId, id); + default: + return assertNever(dataKind); + } }, allCalorimetricData: new URL(`/data/calorimetric`, metabaseUrl) .href as Route, + calorimetricData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/calorimetric/${encodeURIComponent(id)}` as Route, allGeometricData: new URL(`/data/geometric`, metabaseUrl).href as Route, + geometricData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/geometric/${encodeURIComponent(id)}` as Route, allHygrothermalData: new URL(`/data/hygrothermal`, metabaseUrl) .href as Route, - alllifeCycleData: new URL(`/data/life-cycle`, metabaseUrl).href as Route, + hygrothermalData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/hygrothermal/${encodeURIComponent(id)}` as Route, + allLifeCycleData: new URL(`/data/life-cycle`, metabaseUrl).href as Route, + lifeCycleData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/LifeCycle/${encodeURIComponent(id)}` as Route, allOpticalData: new URL(`/data/optical`, metabaseUrl).href as Route, + opticalData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/optical/${encodeURIComponent(id)}` as Route, allPhotovoltaicData: new URL(`/data/photovoltaic`, metabaseUrl) .href as Route, - graphQl: new URL("/graphql/", metabaseUrl).href as Route, + photovoltaicData: ( + databaseId: Scalars["Uuid"]["output"], + id: Scalars["Uuid"]["output"], + ) => + `/databases/${encodeURIComponent(databaseId)}/data/photovoltaic/${encodeURIComponent(id)}` as Route, }, }; diff --git a/frontend/queries/common.graphql b/frontend/queries/common.graphql new file mode 100644 index 00000000..e0106640 --- /dev/null +++ b/frontend/queries/common.graphql @@ -0,0 +1,25 @@ +fragment ReferencePartial on Reference { + ... on Standard { + abstract + section + title + locator + numeration { + mainNumber + prefix + suffix + } + standardizers + year + } + ... on Publication { + abstract + section + title + arXiv + authors + doi + urn + webAddress + } +} diff --git a/frontend/queries/currentUser.graphql b/frontend/queries/currentUser.graphql index b08beab4..48add4e8 100644 --- a/frontend/queries/currentUser.graphql +++ b/frontend/queries/currentUser.graphql @@ -1,4 +1,5 @@ fragment CurrentUserPartial on User { + subject name } diff --git a/frontend/queries/data.graphql b/frontend/queries/data.graphql index 1f00e118..28dccad9 100644 --- a/frontend/queries/data.graphql +++ b/frontend/queries/data.graphql @@ -1,106 +1,123 @@ +#import "./common.graphql" + +fragment GetHttpsResourcePartial on GetHttpsResource { + description + hashValue + locator + dataFormatId + dataFormat { + uuid + name + } + archivedFilesMetaInformation { + path + dataFormatId + dataFormat { + uuid + name + } + } +} + +fragment AppliedMethodPartial on AppliedMethod { + methodId + method { + uuid + name + } + arguments { + name + value + } + sources { + name + value { + databaseId + database { + name + } + dataId + dataKind + dataTimestamp + } + } +} + +fragment ToTreeVertexAppliedConversionMethodPartial on ToTreeVertexAppliedConversionMethod { + methodId + method { + uuid + name + } + arguments { + name + value + } + sourceName +} + fragment DataPartial on Data { + id uuid + locale timestamp - componentId name description - appliedMethod { - methodId - arguments { + warnings + createdAt + componentId + component { + uuid + name + } + creatorId + creator { + uuid + name + } + databaseId + database { + uuid + name + locator + } + approvals { + approverId + approver { + uuid name - value } - sources { - name - value { - dataId - dataKind - dataTimestamp - databaseId - } + keyFingerprint + message + query + signature + statement { + ...ReferencePartial } + timestamp + variables + } + appliedMethod { + ...AppliedMethodPartial } resourceTree { root { + vertexId value { - uuid - description - hashValue - locator - dataFormatId + ...GetHttpsResourcePartial } } nonRootVertices { + vertexId + parentId appliedConversionMethod { - arguments { - name - value - } - methodId - sourceName + ...ToTreeVertexAppliedConversionMethodPartial } - parentId value { - archivedFilesMetaInformation { - dataFormatId - path - } - description - dataFormatId - hashValue - id - locator - uuid - } - vertexId - } - } - approvals { - timestamp - signature - keyFingerprint - query - variables - message - statement { - __typename - ... on Publication { - abstract - arXiv - authors - doi - section - title - urn - webAddress - } - ... on Standard { - abstract - locator - section - standardizers - title - year - numeration { - mainNumber - prefix - suffix - } + ...GetHttpsResourcePartial } } - approverId - } - createdAt - creatorId - locale - resources { - description - hashValue - locator - dataFormatId } - warnings - userId } fragment CalorimetricDataPartial on CalorimetricData { @@ -109,14 +126,23 @@ fragment CalorimetricDataPartial on CalorimetricData { uValues } -query CalorimetricData($uuid: Uuid!) { - calorimetricData(id: $uuid) { +query CalorimetricData($id: Uuid!) { + data: calorimetricData(id: $id) { ...CalorimetricDataPartial } } -query AllCalorimetricData($where: CalorimetricDataPropositionInput) { - allCalorimetricData(where: $where) { +query AllCalorimetricData( + $first: Int + $after: String + $where: CalorimetricDataPropositionInput +) { + connection: allCalorimetricData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { ...CalorimetricDataPartial @@ -125,22 +151,119 @@ query AllCalorimetricData($where: CalorimetricDataPropositionInput) { } } -query HasCalorimetricData($where: CalorimetricDataPropositionInput!) { - hasCalorimetricData(where: $where) +query HasCalorimetricData($where: CalorimetricDataPropositionInput) { + hasData: hasCalorimetricData(where: $where) +} + +query AllCalorimetricDataNames( + $first: Int! + $after: String + $where: CalorimetricDataPropositionInput + $order: [CalorimetricDataSortInput!] +) { + connection: allCalorimetricData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + id + uuid + name + } + } + } +} + +fragment GeometricDataPartial on GeometricData { + ...DataPartial + thicknesses +} + +query GeometricData($id: Uuid!) { + data: geometricData(id: $id) { + ...GeometricDataPartial + } +} + +query AllGeometricData( + $first: Int + $after: String + $where: GeometricDataPropositionInput +) { + connection: allGeometricData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...GeometricDataPartial + } + } + } +} + +query HasGeometricData($where: GeometricDataPropositionInput) { + hasData: hasGeometricData(where: $where) +} + +query AllGeoemetricDataNames( + $first: Int! + $after: String + $where: GeometricDataPropositionInput + $order: [GeometricDataSortInput!] +) { + connection: allGeometricData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + id + uuid + name + } + } + } } fragment HygrothermalDataPartial on HygrothermalData { ...DataPartial } -query HygrothermalData($uuid: Uuid!) { - hygrothermalData(id: $uuid) { +query HygrothermalData($id: Uuid!) { + data: hygrothermalData(id: $id) { ...HygrothermalDataPartial } } -query AllHygrothermalData($where: HygrothermalDataPropositionInput) { - allHygrothermalData(where: $where) { +query AllHygrothermalData( + $first: Int + $after: String + $where: HygrothermalDataPropositionInput +) { + connection: allHygrothermalData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { ...HygrothermalDataPartial @@ -149,22 +272,58 @@ query AllHygrothermalData($where: HygrothermalDataPropositionInput) { } } -query HasHygrothermalData($where: HygrothermalDataPropositionInput!) { - hasHygrothermalData(where: $where) +query HasHygrothermalData($where: HygrothermalDataPropositionInput) { + hasData: hasHygrothermalData(where: $where) +} + +query AllHygrothermalDataNames( + $first: Int! + $after: String + $where: HygrothermalDataPropositionInput + $order: [HygrothermalDataSortInput!] +) { + connection: allHygrothermalData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + id + uuid + name + } + } + } } fragment LifeCycleDataPartial on LifeCycleData { ...DataPartial } -query LifeCycleData($uuid: Uuid!) { - lifeCycleData(id: $uuid) { +query LifeCycleData($id: Uuid!) { + data: lifeCycleData(id: $id) { ...LifeCycleDataPartial } } -query AllLifeCycleData($where: LifeCycleDataPropositionInput) { - allLifeCycleData(where: $where) { +query AllLifeCycleData( + $first: Int + $after: String + $where: LifeCycleDataPropositionInput +) { + connection: allLifeCycleData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { ...LifeCycleDataPartial @@ -173,28 +332,74 @@ query AllLifeCycleData($where: LifeCycleDataPropositionInput) { } } -query HasLifeCycleData($where: LifeCycleDataPropositionInput!) { - hasLifeCycleData(where: $where) +query HasLifeCycleData($where: LifeCycleDataPropositionInput) { + hasData: hasLifeCycleData(where: $where) +} + +query AllLifeCycleDataNames( + $first: Int! + $after: String + $where: LifeCycleDataPropositionInput + $order: [LifeCycleDataSortInput!] +) { + connection: allLifeCycleData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + id + uuid + name + } + } + } } fragment OpticalDataPartial on OpticalData { ...DataPartial + type + subtype + coatedSide infraredEmittances nearnormalHemisphericalSolarReflectances nearnormalHemisphericalSolarTransmittances nearnormalHemisphericalVisibleReflectances nearnormalHemisphericalVisibleTransmittances + cielabColors { + lStar + aStar + bStar + illuminant + observer + } colorRenderingIndices } -query OpticalData($uuid: Uuid!) { - opticalData(id: $uuid) { +query OpticalData($id: Uuid!) { + data: opticalData(id: $id) { ...OpticalDataPartial } } -query AllOpticalData($where: OpticalDataPropositionInput) { - allOpticalData(where: $where) { +query AllOpticalData( + $first: Int + $after: String + $where: OpticalDataPropositionInput +) { + connection: allOpticalData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { ...OpticalDataPartial @@ -203,22 +408,58 @@ query AllOpticalData($where: OpticalDataPropositionInput) { } } -query HasOpticalData($where: OpticalDataPropositionInput!) { - hasOpticalData(where: $where) +query HasOpticalData($where: OpticalDataPropositionInput) { + hasData: hasOpticalData(where: $where) +} + +query AllOpticalDataNames( + $first: Int! + $after: String + $where: OpticalDataPropositionInput + $order: [OpticalDataSortInput!] +) { + connection: allOpticalData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + id + uuid + name + } + } + } } fragment PhotovoltaicDataPartial on PhotovoltaicData { ...DataPartial } -query PhotovoltaicData($uuid: Uuid!) { - photovoltaicData(id: $uuid) { +query PhotovoltaicData($id: Uuid!) { + data: photovoltaicData(id: $id) { ...PhotovoltaicDataPartial } } -query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { - allPhotovoltaicData(where: $where) { +query AllPhotovoltaicData( + $first: Int + $after: String + $where: PhotovoltaicDataPropositionInput +) { + connection: allPhotovoltaicData(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { ...PhotovoltaicDataPartial @@ -227,26 +468,32 @@ query AllPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { } } -query HasPhotovoltaicData($where: PhotovoltaicDataPropositionInput!) { - hasPhotovoltaicData(where: $where) -} - -fragment GeometricDataPartial on GeometricData { - ...DataPartial - thicknesses -} - -query GeometricData($uuid: Uuid!) { - geometricData(id: $uuid) { - ...GeometricDataPartial - } +query HasPhotovoltaicData($where: PhotovoltaicDataPropositionInput) { + hasData: hasPhotovoltaicData(where: $where) } -query AllGeometricData($where: GeometricDataPropositionInput) { - allGeometricData(where: $where) { +query AllPhotovoltaicDataNames( + $first: Int! + $after: String + $where: PhotovoltaicDataPropositionInput + $order: [PhotovoltaicDataSortInput!] +) { + connection: allPhotovoltaicData( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } edges { node { - ...GeometricDataPartial + id + uuid + name } } } diff --git a/frontend/queries/databases.graphql b/frontend/queries/databases.graphql index 08c3b655..eba20fe7 100644 --- a/frontend/queries/databases.graphql +++ b/frontend/queries/databases.graphql @@ -1,16 +1,21 @@ -fragment DatabasePartial on Database { +fragment DatabasesPartial on Database { + id: uuid uuid name description locator - # operator { - # node { - # uuid - # name - # } - # } + operator { + node { + uuid + name + } + } verificationState verificationCode +} + +fragment DatabasePartial on Database { + ...DatabasesPartial isAuthorizedToUpdateNode isAuthorizedToVerifyNode } @@ -21,20 +26,8 @@ query Database { } } -mutation UpdateDatabase( - $databaseId: Uuid! - $name: String! - $description: String! - $locator: Url! -) { - updateDatabase( - input: { - databaseId: $databaseId - name: $name - description: $description - locator: $locator - } - ) { +mutation UpdateDatabase($input: UpdateDatabaseInput!) { + updateDatabase(input: $input) { database { ...DatabasePartial } @@ -45,3 +38,16 @@ mutation UpdateDatabase( } } } + +# mutation VerifyDatabase($input: VerifyDatabaseInput!) { +# verifyDatabase(input: $input) { +# database { +# ...DatabasesPartial +# } +# errors { +# code +# message +# path +# } +# } +# } diff --git a/frontend/test/__snapshots__/index.test.tsx.snap b/frontend/test/__snapshots__/index.test.tsx.snap deleted file mode 100644 index c5fde565..00000000 --- a/frontend/test/__snapshots__/index.test.tsx.snap +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Index renders the html we want 1`] = ` -
    - You're signed in as - Baa - and you're - Healthy - . Go to the - - - about - - - page. -
    - - -
    -
    -`; diff --git a/frontend/test/index.test.tsx b/frontend/test/index.test.tsx deleted file mode 100644 index c74db378..00000000 --- a/frontend/test/index.test.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// import { InMemoryCache, gql } from "@apollo/client"; -// import React from "react"; -// import Index from "../pages"; -// import renderer from "react-test-renderer"; -// import { MockedProvider } from "@apollo/client/testing"; - -// const cache = new InMemoryCache(); -// cache.writeQuery({ -// query: gql` -// query Viewer { -// viewer { -// id -// name -// status -// } -// } -// `, -// data: { -// viewer: { -// __typename: "User", -// id: "Baa", -// name: "Baa", -// status: "Healthy", -// }, -// }, -// }); - -// describe("Index", () => { -// it("renders the html we want", async () => { -// const component = renderer.create( -// -// -// -// ); -// expect(component.toJSON()).toMatchSnapshot(); -// }); -// }); diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index e6af495f..e4b69ac0 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -3,795 +3,6 @@ schema { mutation: Mutation } -interface Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - timestamp: DateTime! - variables: Any! -} - -interface Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources: [GetHttpsResource!]! - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! - warnings: [String!]! -} - -interface GetHttpsResourceTreeVertex { - value: GetHttpsResource! - vertexId: ID -} - -"The node interface is implemented by entities that have a global unique identifier." -interface Node { - id: ID! -} - -interface UserError { - message: String! - path: [String!]! -} - -type AddDataApprovalError implements UserError { - code: AddDataApprovalErrorCode! - message: String! - path: [String!]! -} - -type AddDataApprovalPayload { - dataApproval: DataApproval - errors: [AddDataApprovalError!] - query: Query! -} - -type AddInstitutionAccessRightsError implements UserError { - code: AddInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type AddInstitutionAccessRightsPayload { - errors: [AddInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - -type Address { - formatted: String! -} - -type AppliedMethod { - arguments: [NamedMethodArgument!]! - methodId: Uuid! - sources: [NamedMethodSource!]! -} - -type CalculateMethodError implements UserError { - code: CalculateMethodErrorCode! - message: String! - path: [String!]! -} - -type CalculateMethodPayload { - errors: [CalculateMethodError!] - result: Any -} - -type CalorimetricData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - gValues: [Float!]! - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - uValues: [Float!]! - warnings: [String!]! -} - -"A connection to a list of items." -type CalorimetricDataConnection { - "A list of edges." - edges: [CalorimetricDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type CalorimetricDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: CalorimetricData! -} - -type CielabColor { - aStar: Float! - bStar: Float! - illuminant: Illuminant - lStar: Float! - observer: CalorimetricObserver -} - -type CreateCalorimetricDataError implements UserError { - code: CreateCalorimetricDataErrorCode! - message: String! - path: [String!]! -} - -type CreateCalorimetricDataPayload { - calorimetricData: CalorimetricData - errors: [CreateCalorimetricDataError!] - query: Query! -} - -type CreateGeometricDataError implements UserError { - code: CreateGeometricDataErrorCode! - message: String! - path: [String!]! -} - -type CreateGeometricDataPayload { - errors: [CreateGeometricDataError!] - geometricData: GeometricData - query: Query! -} - -type CreateGetHttpsResourceError implements UserError { - code: CreateGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type CreateGetHttpsResourcePayload { - errors: [CreateGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type CreateHygrothermalDataError implements UserError { - code: CreateHygrothermalDataErrorCode! - message: String! - path: [String!]! -} - -type CreateHygrothermalDataPayload { - errors: [CreateHygrothermalDataError!] - hygrothermalData: HygrothermalData - query: Query! -} - -type CreateLifeCycleDataError implements UserError { - code: CreateLifeCycleDataErrorCode! - message: String! - path: [String!]! -} - -type CreateLifeCycleDataPayload { - errors: [CreateLifeCycleDataError!] - lifeCycleData: LifeCycleData - query: Query! -} - -type CreateOpticalDataError implements UserError { - code: CreateOpticalDataErrorCode! - message: String! - path: [String!]! -} - -type CreateOpticalDataPayload { - errors: [CreateOpticalDataError!] - opticalData: OpticalData - query: Query! -} - -type CreatePhotovoltaicDataError implements UserError { - code: CreatePhotovoltaicDataErrorCode! - message: String! - path: [String!]! -} - -type CreatePhotovoltaicDataPayload { - errors: [CreatePhotovoltaicDataError!] - photovoltaicData: PhotovoltaicData - query: Query! -} - -type CreateResponseApprovalsError implements UserError { - code: CreateResponseApprovalsErrorCode! - message: String! - path: [String!]! -} - -type CreateResponseApprovalsPayload { - data: [Data!] - errors: [CreateResponseApprovalsError!] - query: Query! -} - -type CrossDatabaseDataReference { - databaseId: Uuid! - dataId: Uuid! - dataKind: DataKind! - dataTimestamp: DateTime! -} - -type DataAccessRights { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32!] - hasRestrictions: Boolean! - hasRestrictionsByApplication: Boolean! - hasRestrictionsByInstitution: Boolean! - hasRestrictionsByUser: Boolean! -} - -type DataApproval implements Approval { - approverId: Uuid! - keyFingerprint: String! - message: String! - query: String! - signature: String! - statement: Reference @cost(weight: "10") - timestamp: DateTime! - variables: Any! -} - -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - -type Database { - description: String! - isAuthorizedToUpdateNode: Boolean! - isAuthorizedToVerifyNode: Boolean! - locator: Url! - name: String! - operator: DatabaseOperatorEdge! - uuid: Uuid! - verificationCode: String! - verificationState: DatabaseVerificationState! -} - -type DatabaseOperatorEdge { - node: Institution! -} - -type DeleteDataError implements UserError { - code: DeleteDataErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataPayload { - errors: [DeleteDataError!] - query: Query! -} - -type DeleteGetHttpsResourceError implements UserError { - code: DeleteGetHttpsResourceErrorCode! - message: String! - path: [String!]! -} - -type DeleteGetHttpsResourcePayload { - errors: [DeleteGetHttpsResourceError!] - getHttpsResource: GetHttpsResource - query: Query! -} - -type FileMetaInformation { - dataFormatId: Uuid! - path: [String!]! -} - -type GeometricData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - heights: [Float!]! - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - thicknesses: [Float!]! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! - widths: [Float!]! -} - -"A connection to a list of items." -type GeometricDataConnection { - "A list of edges." - edges: [GeometricDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type GeometricDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: GeometricData! -} - -type GetHttpsResource implements Node { - appliedConversionMethod: ToTreeVertexAppliedConversionMethod - archivedFilesMetaInformation: [FileMetaInformation!]! - children: [GetHttpsResource!]! @cost(weight: "10") - data: Data @cost(weight: "10") - dataFormatId: Uuid! - description: String - doesFileExist: Boolean! - hashValue: String! - id: ID! - isChild: Boolean! - isRoot: Boolean! - locator: Url! - parent: GetHttpsResource @cost(weight: "10") - uuid: Uuid! @cost(weight: "10") -} - -"A connection to a list of items." -type GetHttpsResourceConnection { - "A list of edges." - edges: [GetHttpsResourceEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type GetHttpsResourceEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: GetHttpsResource! -} - -type GetHttpsResourceTree { - nonRootVertices( - order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") - where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - ): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") - root: GetHttpsResourceTreeRoot! @cost(weight: "10") -} - -type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { - appliedConversionMethod: ToTreeVertexAppliedConversionMethod! - parentId: ID! - value: GetHttpsResource! - vertexId: ID! -} - -type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { - value: GetHttpsResource! - vertexId: ID! -} - -type HygrothermalData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type HygrothermalDataConnection { - "A list of edges." - edges: [HygrothermalDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type HygrothermalDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: HygrothermalData! -} - -type Institution { - uuid: Uuid! -} - -type InstitutionAccessRights { - allowedDatasetsPerTime: NonNegativeInt - allowedUserCount: NonNegativeInt - hasRestrictions: Boolean! - hasRestrictionsByTime: Boolean! - hasRestrictionsByUser: Boolean! - id: Uuid! - institutionId: Uuid! - period: Duration! - userAlreadyAccessed: [Uuid!]! - version: NonNegativeInt! -} - -type KeyValuePairOfGuidAndNullableOfUInt32 { - key: Uuid! - value: NonNegativeInt -} - -type LifeCycleData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type LifeCycleDataConnection { - "A list of edges." - edges: [LifeCycleDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type LifeCycleDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: LifeCycleData! -} - -type Mutation { - addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! - @cost(weight: "10") - addInstitutionAccessRights( - input: AddInstitutionAccessRightsInput! - ): AddInstitutionAccessRightsPayload! @cost(weight: "10") - createCalorimetricData( - input: CreateCalorimetricDataInput! - ): CreateCalorimetricDataPayload! @cost(weight: "10") - createGeometricData( - input: CreateGeometricDataInput! - ): CreateGeometricDataPayload! @cost(weight: "10") - createGetHttpsResource( - input: CreateGetHttpsResourceInput! - ): CreateGetHttpsResourcePayload! @cost(weight: "10") - createHygrothermalData( - input: CreateHygrothermalDataInput! - ): CreateHygrothermalDataPayload! @cost(weight: "10") - createLifeCycleData( - input: CreateLifeCycleDataInput! - ): CreateLifeCycleDataPayload! @cost(weight: "10") - createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! - @cost(weight: "10") - createPhotovoltaicData( - input: CreatePhotovoltaicDataInput! - ): CreatePhotovoltaicDataPayload! @cost(weight: "10") - createResponseApprovals( - where: CreateResponseApprovalsPropositionInput @cost(weight: "10") - ): CreateResponseApprovalsPayload! @cost(weight: "10") - deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteGetHttpsResource( - input: DeleteGetHttpsResourceInput! - ): DeleteGetHttpsResourcePayload! @cost(weight: "10") - publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") - recomputeGetHttpsResourceHashValues( - where: RecomputeGetHttpsResourceHashValuesPropositionInput - @cost(weight: "10") - ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") - removeDataApproval( - input: RemoveDataApprovalInput! - ): RemoveDataApprovalPayload! @cost(weight: "10") - retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") - setGetHttpsResourceParent( - input: SetGetHttpsResourceParentInput! - ): SetGetHttpsResourceParentPayload! @cost(weight: "10") - updateChildGetHttpsResource( - input: UpdateChildGetHttpsResourceInput! - ): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") - updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights( - input: UpdateDataAccessRightsInput! - ): UpdateDataAccessRightsPayload! @cost(weight: "10") - updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! - @cost(weight: "10") - updateInstitutionAccessRights( - input: UpdateInstitutionAccessRightsInput! - ): UpdateInstitutionAccessRightsPayload! @cost(weight: "10") - updateResponseApprovals( - where: UpdateResponseApprovalsPropositionInput @cost(weight: "10") - ): UpdateResponseApprovalsPayload! @cost(weight: "10") - updateRootGetHttpsResource( - input: UpdateRootGetHttpsResourceInput! - ): UpdateRootGetHttpsResourcePayload! @cost(weight: "10") -} - -type NamedMethodArgument { - name: String! - value: Any! -} - -type NamedMethodSource { - name: String! - value: CrossDatabaseDataReference! -} - -type Numeration { - mainNumber: String! - prefix: String - suffix: String -} - -type OpenEndedDateTimeRange { - from: DateTime @cost(weight: "10") - until: DateTime @cost(weight: "10") -} - -type OpticalData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - cielabColors: [CielabColor!]! - coatedSide: CoatedSide - colorRenderingIndices: [Float!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - infraredEmittances: [Float!]! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - nearnormalHemisphericalSolarReflectances: [Float!]! - nearnormalHemisphericalSolarTransmittances: [Float!]! - nearnormalHemisphericalVisibleReflectances: [Float!]! - nearnormalHemisphericalVisibleTransmittances: [Float!]! - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - subtype: OpticalComponentSubtype - timestamp: DateTime! - type: OpticalComponentType - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type OpticalDataConnection { - "A list of edges." - edges: [OpticalDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type OpticalDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: OpticalData! -} - -"Information about pagination in a connection." -type PageInfo { - "When paginating forwards, the cursor to continue." - endCursor: String - "Indicates whether more edges exist following the set defined by the clients arguments." - hasNextPage: Boolean! - "Indicates whether more edges exist prior the set defined by the clients arguments." - hasPreviousPage: Boolean! - "When paginating backwards, the cursor to continue." - startCursor: String -} - -type PhotovoltaicData implements Node & Data { - appliedMethod: AppliedMethod! - approval: ResponseApproval! - approvals: [DataApproval!]! - componentId: Uuid! - createdAt: DateTime! - creatorId: Uuid! - dataAccessRights: DataAccessRights! - databaseId: Uuid! - description: String - id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser( - alreadyAccesedCount: NonNegativeInt! - uuid: Uuid! - ): Boolean! - locale: Locale! - name: String - resources( - order: [GetHttpsResourceSortInput!] @cost(weight: "10") - where: GetHttpsResourcePropositionInput @cost(weight: "10") - ): [GetHttpsResource!]! @cost(weight: "10") - resourceTree: GetHttpsResourceTree! - timestamp: DateTime! - userId: Uuid - uuid: Uuid! @cost(weight: "10") - warnings: [String!]! -} - -"A connection to a list of items." -type PhotovoltaicDataConnection { - "A list of edges." - edges: [PhotovoltaicDataEdge!] - "Information to aid in pagination." - pageInfo: PageInfo! - "Identifies the total count of items in the connection." - totalCount: Int! @cost(weight: "10") -} - -"An edge in a connection." -type PhotovoltaicDataEdge { - "A cursor for use in pagination." - cursor: String! - "The item at the end of the edge." - node: PhotovoltaicData! -} - -type Publication { - abstract: String - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: String - authors: [String!] - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: String - "Referenced section" - section: String - title: String - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: String - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." - webAddress: Url -} - -type PublishDataError implements UserError { - code: PublishDataErrorCode! - message: String! - path: [String!]! -} - -type PublishDataPayload { - data: Data - errors: [PublishDataError!] - query: Query! -} - type Query { allCalorimetricData( "Returns the elements in the list that come after the specified cursor." @@ -1049,12 +260,11 @@ type Query { dataReference: CrossDatabaseDataReferenceInput! methodId: Uuid! ): CalculateMethodPayload! @cost(weight: "10") - calculateMethodWithDataUpload( - data: Upload! - methodId: Uuid! - ): CalculateMethodPayload! @cost(weight: "10") + calculateMethodWithDataUpload(data: Upload!, methodId: Uuid!): CalculateMethodPayload! + @cost(weight: "10") calorimetricData(id: Uuid!, locale: Locale): CalorimetricData @cost(weight: "10") + currentInstitution: CurrentInstitution @cost(weight: "10") currentUser: User @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") @@ -1138,6 +348,773 @@ type Query { verificationCode: String! } +type Mutation { + addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! + @cost(weight: "10") + addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! + @cost(weight: "10") + createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! + @cost(weight: "10") + createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! + @cost(weight: "10") + createGetHttpsResource(input: CreateGetHttpsResourceInput!): CreateGetHttpsResourcePayload! + @cost(weight: "10") + createHygrothermalData(input: CreateHygrothermalDataInput!): CreateHygrothermalDataPayload! + @cost(weight: "10") + createLifeCycleData(input: CreateLifeCycleDataInput!): CreateLifeCycleDataPayload! + @cost(weight: "10") + createOpticalData(input: CreateOpticalDataInput!): CreateOpticalDataPayload! + @cost(weight: "10") + createPhotovoltaicData(input: CreatePhotovoltaicDataInput!): CreatePhotovoltaicDataPayload! + @cost(weight: "10") + createResponseApprovals( + where: CreateResponseApprovalsPropositionInput @cost(weight: "10") + ): CreateResponseApprovalsPayload! @cost(weight: "10") + deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! + @cost(weight: "10") + publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") + recomputeGetHttpsResourceHashValues( + where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") + ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") + removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! + @cost(weight: "10") + retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! + @cost(weight: "10") + updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! + @cost(weight: "10") + updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") + updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! + @cost(weight: "10") + updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! + @cost(weight: "10") + updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! + @cost(weight: "10") + updateResponseApprovals( + where: UpdateResponseApprovalsPropositionInput @cost(weight: "10") + ): UpdateResponseApprovalsPayload! @cost(weight: "10") + updateRootGetHttpsResource(input: UpdateRootGetHttpsResourceInput!): UpdateRootGetHttpsResourcePayload! + @cost(weight: "10") +} + +type AddDataApprovalError implements UserError { + code: AddDataApprovalErrorCode! + message: String! + path: [String!]! +} + +type AddDataApprovalPayload { + dataApproval: DataApproval + errors: [AddDataApprovalError!] + query: Query! +} + +type AddInstitutionAccessRightsError implements UserError { + code: AddInstitutionAccessRightsErrorCode! + message: String! + path: [String!]! +} + +type AddInstitutionAccessRightsPayload { + errors: [AddInstitutionAccessRightsError!] + institutionAccessRights: InstitutionAccessRights + query: Query! +} + +type Address { + formatted: String! +} + +type AppliedMethod { + arguments: [NamedMethodArgument!]! + method: Method @cost(weight: "3") + methodId: Uuid! + sources: [NamedMethodSource!]! +} + +type CalculateMethodError implements UserError { + code: CalculateMethodErrorCode! + message: String! + path: [String!]! +} + +type CalculateMethodPayload { + errors: [CalculateMethodError!] + result: Any +} + +type CalorimetricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + gValues: [Float!]! + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + uValues: [Float!]! + warnings: [String!]! +} + +"A connection to a list of items." +type CalorimetricDataConnection { + "A list of edges." + edges: [CalorimetricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type CalorimetricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: CalorimetricData! +} + +type CielabColor { + aStar: Float! + bStar: Float! + illuminant: Illuminant + lStar: Float! + observer: CalorimetricObserver +} + +type Component { + name: String! + uuid: Uuid! +} + +type CreateCalorimetricDataError implements UserError { + code: CreateCalorimetricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateCalorimetricDataPayload { + calorimetricData: CalorimetricData + errors: [CreateCalorimetricDataError!] + query: Query! +} + +type CreateGeometricDataError implements UserError { + code: CreateGeometricDataErrorCode! + message: String! + path: [String!]! +} + +type CreateGeometricDataPayload { + errors: [CreateGeometricDataError!] + geometricData: GeometricData + query: Query! +} + +type CreateGetHttpsResourceError implements UserError { + code: CreateGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type CreateGetHttpsResourcePayload { + errors: [CreateGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type CreateHygrothermalDataError implements UserError { + code: CreateHygrothermalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateHygrothermalDataPayload { + errors: [CreateHygrothermalDataError!] + hygrothermalData: HygrothermalData + query: Query! +} + +type CreateLifeCycleDataError implements UserError { + code: CreateLifeCycleDataErrorCode! + message: String! + path: [String!]! +} + +type CreateLifeCycleDataPayload { + errors: [CreateLifeCycleDataError!] + lifeCycleData: LifeCycleData + query: Query! +} + +type CreateOpticalDataError implements UserError { + code: CreateOpticalDataErrorCode! + message: String! + path: [String!]! +} + +type CreateOpticalDataPayload { + errors: [CreateOpticalDataError!] + opticalData: OpticalData + query: Query! +} + +type CreatePhotovoltaicDataError implements UserError { + code: CreatePhotovoltaicDataErrorCode! + message: String! + path: [String!]! +} + +type CreatePhotovoltaicDataPayload { + errors: [CreatePhotovoltaicDataError!] + photovoltaicData: PhotovoltaicData + query: Query! +} + +type CreateResponseApprovalsError implements UserError { + code: CreateResponseApprovalsErrorCode! + message: String! + path: [String!]! +} + +type CreateResponseApprovalsPayload { + data: [Data!] + errors: [CreateResponseApprovalsError!] + query: Query! +} + +type CrossDatabaseDataReference { + database: Database @cost(weight: "3") + databaseId: Uuid! + dataId: Uuid! + dataKind: DataKind! + dataTimestamp: DateTime! +} + +type CurrentInstitution { + databaseOperatingDatabases: DatabaseOperatingDatabaseConnection! + databaseOperatingManagedInstitutions: DatabaseOperatingManagedInstitutionConnection! + isDatabaseOperator: Boolean! + name: String! + uuid: Uuid! +} + +type DataAccessRights { + allowedApplications: [String!] + allowedInstitutions: [Uuid!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32!] + hasRestrictions: Boolean! + hasRestrictionsByApplication: Boolean! + hasRestrictionsByInstitution: Boolean! + hasRestrictionsByUser: Boolean! +} + +type DataApproval implements Approval { + approver: Institution @cost(weight: "3") + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + statement: Reference! @cost(weight: "10") + timestamp: DateTime! + variables: Any! +} + +type Database { + description: String! + isAuthorizedToUpdateNode: Boolean! + isAuthorizedToVerifyNode: Boolean! + locator: Url! + name: String! + operator: DatabaseOperatorEdge! + uuid: Uuid! + verificationCode: String! + verificationState: DatabaseVerificationState! +} + +type DatabaseOperatingDatabaseConnection { + totalCount: NonNegativeInt! +} + +type DatabaseOperatingManagedInstitutionConnection { + totalCount: NonNegativeInt! +} + +type DatabaseOperatorEdge { + node: Institution! +} + +type DataConnection { + edges: [DataEdge!]! + pageInfo: PageInfo! + totalCount: NonNegativeInt +} + +type DataEdge { + cursor: String! + node: Data! +} + +type DataFormat { + extension: String + mediaType: String! + name: String! + schemaLocator: Url + uuid: Uuid! +} + +type DeleteDataError implements UserError { + code: DeleteDataErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataPayload { + errors: [DeleteDataError!] + query: Query! +} + +type DeleteGetHttpsResourceError implements UserError { + code: DeleteGetHttpsResourceErrorCode! + message: String! + path: [String!]! +} + +type DeleteGetHttpsResourcePayload { + errors: [DeleteGetHttpsResourceError!] + getHttpsResource: GetHttpsResource + query: Query! +} + +type FileMetaInformation { + dataFormat: DataFormat @cost(weight: "3") + dataFormatId: Uuid! + path: [String!]! +} + +type GeometricData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + heights: [Float!]! + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + thicknesses: [Float!]! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! + widths: [Float!]! +} + +"A connection to a list of items." +type GeometricDataConnection { + "A list of edges." + edges: [GeometricDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GeometricDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GeometricData! +} + +type GetHttpsResource implements Node { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod + archivedFilesMetaInformation: [FileMetaInformation!]! + children: [GetHttpsResource!]! @cost(weight: "0") + createdAt: DateTime! + data: Data @cost(weight: "0") + dataFormat: DataFormat @cost(weight: "3") + dataFormatId: Uuid! + description: String + doesFileExist: Boolean! + hashValue: String! + id: ID! + isChild: Boolean! + isRoot: Boolean! + locator: Url! + parent: GetHttpsResource @cost(weight: "0") + updatedAt: DateTime! + uuid: Uuid! +} + +"A connection to a list of items." +type GetHttpsResourceConnection { + "A list of edges." + edges: [GetHttpsResourceEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type GetHttpsResourceEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: GetHttpsResource! +} + +type GetHttpsResourceTree { + nonRootVertices( + order: [GetHttpsResourceTreeNonRootVertSortInput!] @cost(weight: "10") + where: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + ): [GetHttpsResourceTreeNonRootVertex!]! @cost(weight: "10") + root: GetHttpsResourceTreeRoot! @cost(weight: "10") +} + +type GetHttpsResourceTreeNonRootVertex implements GetHttpsResourceTreeVertex { + appliedConversionMethod: ToTreeVertexAppliedConversionMethod! + parentId: ID! + value: GetHttpsResource! + vertexId: ID! +} + +type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID! +} + +type HygrothermalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +"A connection to a list of items." +type HygrothermalDataConnection { + "A list of edges." + edges: [HygrothermalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type HygrothermalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: HygrothermalData! +} + +type Institution { + name: String! + uuid: Uuid! +} + +type InstitutionAccessRights { + allowedDatasetsPerTime: NonNegativeInt + allowedUserCount: NonNegativeInt + createdAt: DateTime! + hasRestrictions: Boolean! + hasRestrictionsByTime: Boolean! + hasRestrictionsByUser: Boolean! + id: Uuid! + institutionId: Uuid! + period: Duration! + updatedAt: DateTime! + userAlreadyAccessed: [Uuid!]! + version: NonNegativeInt! +} + +type KeyValuePairOfGuidAndNullableUInt32 { + key: Uuid! + value: NonNegativeInt +} + +type LifeCycleData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +"A connection to a list of items." +type LifeCycleDataConnection { + "A list of edges." + edges: [LifeCycleDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type LifeCycleDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: LifeCycleData! +} + +type Method { + name: String! + uuid: Uuid! +} + +type NamedMethodArgument { + name: String! + value: Any! +} + +type NamedMethodSource { + name: String! + value: CrossDatabaseDataReference! +} + +type Numeration { + mainNumber: String! + prefix: String + suffix: String +} + +type OpenEndedDateTimeRange { + from: DateTime @cost(weight: "10") + until: DateTime @cost(weight: "10") +} + +type OpticalData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + cielabColors: [CielabColor!]! + coatedSide: CoatedSide + colorRenderingIndices: [Float!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + infraredEmittances: [Float!]! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + nearnormalHemisphericalSolarReflectances: [Float!]! + nearnormalHemisphericalSolarTransmittances: [Float!]! + nearnormalHemisphericalVisibleReflectances: [Float!]! + nearnormalHemisphericalVisibleTransmittances: [Float!]! + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + subtype: OpticalComponentSubtype + timestamp: DateTime! + type: OpticalComponentType + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +"A connection to a list of items." +type OpticalDataConnection { + "A list of edges." + edges: [OpticalDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type OpticalDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: OpticalData! +} + +"Information about pagination in a connection." +type PageInfo { + "When paginating forwards, the cursor to continue." + endCursor: String + "Indicates whether more edges exist following the set defined by the clients arguments." + hasNextPage: Boolean! + "Indicates whether more edges exist prior the set defined by the clients arguments." + hasPreviousPage: Boolean! + "When paginating backwards, the cursor to continue." + startCursor: String +} + +type PhotovoltaicData implements Node & Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! @cost(weight: "3") + approvals: [DataApproval!]! + component: Component @cost(weight: "3") + componentId: Uuid! + createdAt: DateTime! + creator: Institution @cost(weight: "3") + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database @cost(weight: "3") + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources( + order: [GetHttpsResourceSortInput!] + where: GetHttpsResourcePropositionInput + ): [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +"A connection to a list of items." +type PhotovoltaicDataConnection { + "A list of edges." + edges: [PhotovoltaicDataEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type PhotovoltaicDataEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: PhotovoltaicData! +} + +type Publication { + abstract: String + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: String + authors: [String!] + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: String + "Referenced section" + section: String + title: String + "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + urn: String + "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + webAddress: Url +} + +type PublishDataError implements UserError { + code: PublishDataErrorCode! + message: String! + path: [String!]! +} + +type PublishDataPayload { + data: Data + errors: [PublishDataError!] + query: Query! +} + type RecomputeGetHttpsResourceHashValuesError implements UserError { code: RecomputeGetHttpsResourceHashValuesErrorCode! message: String! @@ -1211,6 +1188,7 @@ type Standard { type ToTreeVertexAppliedConversionMethod { arguments: [NamedMethodArgument!]! + method: Method @cost(weight: "3") methodId: Uuid! sourceName: String! } @@ -1239,27 +1217,27 @@ type UpdateDataAccessRightsPayload { query: Query! } -type UpdateDataError implements UserError { - code: UpdateDataErrorCode! +type UpdateDatabaseError { + code: UpdateDatabaseErrorCode! message: String! path: [String!]! } -type UpdateDataPayload { - data: Data - errors: [UpdateDataError!] +type UpdateDatabasePayload { + database: Database + errors: [UpdateDatabaseError!] query: Query! } -type UpdateDatabaseError { - code: UpdateDatabaseErrorCode! +type UpdateDataError implements UserError { + code: UpdateDataErrorCode! message: String! path: [String!]! } -type UpdateDatabasePayload { - database: Database - errors: [UpdateDatabaseError!] +type UpdateDataPayload { + data: Data + errors: [UpdateDataError!] query: Query! } @@ -1300,10 +1278,12 @@ type UpdateRootGetHttpsResourcePayload { } type User implements Node { + createdAt: DateTime! id: ID! name: String! subject: String! - uuid: Uuid! @cost(weight: "10") + updatedAt: DateTime! + uuid: Uuid! } type UserInfo { @@ -1318,6 +1298,58 @@ type UserInfo { website: String } +interface Approval { + approverId: Uuid! + keyFingerprint: String! + message: String! + query: String! + signature: String! + timestamp: DateTime! + variables: Any! +} + +interface Data { + appliedMethod: AppliedMethod! + approval: ResponseApproval! + approvals: [DataApproval!]! + component: Component + componentId: Uuid! + createdAt: DateTime! + creator: Institution + creatorId: Uuid! + dataAccessRights: DataAccessRights! + database: Database + databaseId: Uuid! + description: String + id: ID! + isRestrictedByApplication(applicationId: String!): Boolean! + isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! + isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! + locale: Locale! + name: String + resources: [GetHttpsResource!]! + resourceTree: GetHttpsResourceTree! + timestamp: DateTime! + userId: Uuid + uuid: Uuid! + warnings: [String!]! +} + +interface GetHttpsResourceTreeVertex { + value: GetHttpsResource! + vertexId: ID +} + +"The node interface is implemented by entities that have a global unique identifier." +interface Node { + id: ID! +} + +interface UserError { + message: String! + path: [String!]! +} + union Reference = Publication | Standard input AddDataApprovalInput { @@ -1348,9 +1380,9 @@ input AppliedMethodInput { input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] - or: [AppliedMethodPropositionInput!] - methodId: UuidPropositionInput arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [AppliedMethodPropositionInput!] sources: FilterInputTypeOfNamedMethodSourcesPropositionInput } @@ -1365,57 +1397,49 @@ input BooleanPropositionInput { input BytePropositionInput { equalTo: Byte @cost(weight: "10") - notEqualTo: Byte @cost(weight: "10") - in: [Byte] @cost(weight: "10") - notIn: [Byte] @cost(weight: "10") greaterThan: Byte @cost(weight: "10") - notGreaterThan: Byte @cost(weight: "10") greaterThanOrEqualTo: Byte @cost(weight: "10") - notGreaterThanOrEqualTo: Byte @cost(weight: "10") + in: [Byte] @cost(weight: "10") lessThan: Byte @cost(weight: "10") - notLessThan: Byte @cost(weight: "10") lessThanOrEqualTo: Byte @cost(weight: "10") + notEqualTo: Byte @cost(weight: "10") + notGreaterThan: Byte @cost(weight: "10") + notGreaterThanOrEqualTo: Byte @cost(weight: "10") + notIn: [Byte] @cost(weight: "10") + notLessThan: Byte @cost(weight: "10") notLessThanOrEqualTo: Byte @cost(weight: "10") } -input CalendarSystemPropositionInput { - and: [CalendarSystemPropositionInput!] - or: [CalendarSystemPropositionInput!] - id: StringPropositionInput - name: StringPropositionInput - minYear: IntPropositionInput - maxYear: IntPropositionInput - eras: FilterInputTypeOfErasPropositionInput -} - input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] - or: [CalorimetricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + gValues: FloatsPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [CalorimetricDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput - gValues: FloatsPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput uValues: FloatsPropositionInput + warnings: StringsPropositionInput } input CalorimetricDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input CielabColorInput { @@ -1428,12 +1452,12 @@ input CielabColorInput { input CielabColorPropositionInput { and: [CielabColorPropositionInput!] - or: [CielabColorPropositionInput!] - lStar: FloatPropositionInput aStar: FloatPropositionInput bStar: FloatPropositionInput - observer: NullableOfCalorimetricObserverPropositionInput illuminant: NullableOfIlluminantPropositionInput + lStar: FloatPropositionInput + observer: NullableOfCalorimetricObserverPropositionInput + or: [CielabColorPropositionInput!] } input ClosedIntervalInput { @@ -1540,18 +1564,16 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsPropositionInput { and: [CreateResponseApprovalsPropositionInput!] - or: [CreateResponseApprovalsPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [CreateResponseApprovalsPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -1564,88 +1586,90 @@ input CrossDatabaseDataReferenceInput { input CrossDatabaseDataReferencePropositionInput { and: [CrossDatabaseDataReferencePropositionInput!] - or: [CrossDatabaseDataReferencePropositionInput!] + databaseId: UuidPropositionInput dataId: UuidPropositionInput - dataTimestamp: OffsetDateTimePropositionInput dataKind: DataKindPropositionInput - databaseId: UuidPropositionInput + dataTimestamp: DateTimePropositionInput + or: [CrossDatabaseDataReferencePropositionInput!] } input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] - or: [DataApprovalPropositionInput!] approverId: UuidPropositionInput - timestamp: OffsetDateTimePropositionInput - signature: StringPropositionInput keyFingerprint: StringPropositionInput - query: StringPropositionInput - variables: JsonElementPropositionInput message: StringPropositionInput + or: [DataApprovalPropositionInput!] + query: StringPropositionInput + signature: StringPropositionInput statement: ReferencePropositionInput + timestamp: DateTimePropositionInput + variables: JsonElementPropositionInput } input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") - notEqualTo: DataKind @cost(weight: "10") in: [DataKind!] @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") notIn: [DataKind!] @cost(weight: "10") } input DataPropositionInput { and: [DataPropositionInput!] - or: [DataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [DataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input DataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input DateTimePropositionInput { equalTo: DateTime @cost(weight: "10") - notEqualTo: DateTime @cost(weight: "10") - in: [DateTime] @cost(weight: "10") - notIn: [DateTime] @cost(weight: "10") greaterThan: DateTime @cost(weight: "10") - notGreaterThan: DateTime @cost(weight: "10") greaterThanOrEqualTo: DateTime @cost(weight: "10") - notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + in: [DateTime] @cost(weight: "10") lessThan: DateTime @cost(weight: "10") - notLessThan: DateTime @cost(weight: "10") lessThanOrEqualTo: DateTime @cost(weight: "10") + notEqualTo: DateTime @cost(weight: "10") + notGreaterThan: DateTime @cost(weight: "10") + notGreaterThanOrEqualTo: DateTime @cost(weight: "10") + notIn: [DateTime] @cost(weight: "10") + notLessThan: DateTime @cost(weight: "10") notLessThanOrEqualTo: DateTime @cost(weight: "10") } input DecimalPropositionInput { equalTo: Decimal @cost(weight: "10") - notEqualTo: Decimal @cost(weight: "10") - in: [Decimal] @cost(weight: "10") - notIn: [Decimal] @cost(weight: "10") greaterThan: Decimal @cost(weight: "10") - notGreaterThan: Decimal @cost(weight: "10") greaterThanOrEqualTo: Decimal @cost(weight: "10") - notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + in: [Decimal] @cost(weight: "10") lessThan: Decimal @cost(weight: "10") - notLessThan: Decimal @cost(weight: "10") lessThanOrEqualTo: Decimal @cost(weight: "10") + notEqualTo: Decimal @cost(weight: "10") + notGreaterThan: Decimal @cost(weight: "10") + notGreaterThanOrEqualTo: Decimal @cost(weight: "10") + notIn: [Decimal] @cost(weight: "10") + notLessThan: Decimal @cost(weight: "10") notLessThanOrEqualTo: Decimal @cost(weight: "10") } @@ -1658,10 +1682,19 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input EraPropositionInput { - and: [EraPropositionInput!] - or: [EraPropositionInput!] - name: StringPropositionInput +input DurationPropositionInput { + equalTo: Duration @cost(weight: "10") + greaterThan: Duration @cost(weight: "10") + greaterThanOrEqualTo: Duration @cost(weight: "10") + in: [Duration] @cost(weight: "10") + lessThan: Duration @cost(weight: "10") + lessThanOrEqualTo: Duration @cost(weight: "10") + notEqualTo: Duration @cost(weight: "10") + notGreaterThan: Duration @cost(weight: "10") + notGreaterThanOrEqualTo: Duration @cost(weight: "10") + notIn: [Duration] @cost(weight: "10") + notLessThan: Duration @cost(weight: "10") + notLessThanOrEqualTo: Duration @cost(weight: "10") } input FileMetaInformationInput { @@ -1671,212 +1704,202 @@ input FileMetaInformationInput { input FileMetaInformationPropositionInput { and: [FileMetaInformationPropositionInput!] + dataFormatId: UuidPropositionInput or: [FileMetaInformationPropositionInput!] path: StringsPropositionInput - dataFormatId: UuidPropositionInput } input FilterInputTypeOfCielabColorsPropositionInput { all: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: CielabColorPropositionInput @cost(weight: "10") some: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfDataApprovalsPropositionInput { all: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: DataApprovalPropositionInput @cost(weight: "10") some: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input FilterInputTypeOfErasPropositionInput { - all: EraPropositionInput @cost(weight: "10") - none: EraPropositionInput @cost(weight: "10") - some: EraPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfFileMetaInformationsPropositionInput { all: FileMetaInformationPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: FileMetaInformationPropositionInput @cost(weight: "10") some: FileMetaInformationPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfGetHttpsResourcesPropositionInput { all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfNamedMethodArgumentsPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: NamedMethodArgumentPropositionInput @cost(weight: "10") some: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FilterInputTypeOfNamedMethodSourcesPropositionInput { all: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: NamedMethodSourcePropositionInput @cost(weight: "10") some: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input FloatPropositionInput { equalTo: Float @cost(weight: "10") - notEqualTo: Float @cost(weight: "10") - in: [Float] @cost(weight: "10") - notIn: [Float] @cost(weight: "10") greaterThan: Float @cost(weight: "10") - notGreaterThan: Float @cost(weight: "10") greaterThanOrEqualTo: Float @cost(weight: "10") - notGreaterThanOrEqualTo: Float @cost(weight: "10") + in: [Float] @cost(weight: "10") + inClosedInterval: ClosedIntervalInput @cost(weight: "10") lessThan: Float @cost(weight: "10") - notLessThan: Float @cost(weight: "10") lessThanOrEqualTo: Float @cost(weight: "10") + notEqualTo: Float @cost(weight: "10") + notGreaterThan: Float @cost(weight: "10") + notGreaterThanOrEqualTo: Float @cost(weight: "10") + notIn: [Float] @cost(weight: "10") + notLessThan: Float @cost(weight: "10") notLessThanOrEqualTo: Float @cost(weight: "10") - inClosedInterval: ClosedIntervalInput @cost(weight: "10") } input FloatsPropositionInput { all: FloatPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: FloatPropositionInput @cost(weight: "10") some: FloatPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") } input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] - or: [GeometricDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [GeometricDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput thicknesses: FloatsPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput + warnings: StringsPropositionInput } input GeometricDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input GetHttpsResourcePropositionInput { and: [GetHttpsResourcePropositionInput!] - or: [GetHttpsResourcePropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput - parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput + or: [GetHttpsResourcePropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } input GetHttpsResourceSortInput { - id: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - hashValue: SortEnumType @cost(weight: "10") + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput - @cost(weight: "10") - parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") -} - -input GetHttpsResourceTreeNonRootVertSortInput { - id: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") - dataFormatId: SortEnumType @cost(weight: "10") - appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput - @cost(weight: "10") + id: SortEnumType @cost(weight: "10") parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] - or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput + hashValue: StringPropositionInput + or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput } +input GetHttpsResourceTreeNonRootVertSortInput { + appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + dataFormatId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") + hashValue: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + parent: GetHttpsResourceTreeNonRootVertSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") +} + input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] - or: [HygrothermalDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [HygrothermalDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input HygrothermalDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input IntPropositionInput { equalTo: Int @cost(weight: "10") - notEqualTo: Int @cost(weight: "10") - in: [Int] @cost(weight: "10") - notIn: [Int] @cost(weight: "10") greaterThan: Int @cost(weight: "10") - notGreaterThan: Int @cost(weight: "10") greaterThanOrEqualTo: Int @cost(weight: "10") - notGreaterThanOrEqualTo: Int @cost(weight: "10") + in: [Int] @cost(weight: "10") lessThan: Int @cost(weight: "10") - notLessThan: Int @cost(weight: "10") lessThanOrEqualTo: Int @cost(weight: "10") + notEqualTo: Int @cost(weight: "10") + notGreaterThan: Int @cost(weight: "10") + notGreaterThanOrEqualTo: Int @cost(weight: "10") + notIn: [Int] @cost(weight: "10") + notLessThan: Int @cost(weight: "10") notLessThanOrEqualTo: Int @cost(weight: "10") } -input IsoDayOfWeekPropositionInput { - equalTo: IsoDayOfWeek @cost(weight: "10") - notEqualTo: IsoDayOfWeek @cost(weight: "10") - in: [IsoDayOfWeek!] @cost(weight: "10") - notIn: [IsoDayOfWeek!] @cost(weight: "10") -} - input JsonElementPropositionInput { and: [JsonElementPropositionInput!] or: [JsonElementPropositionInput!] @@ -1885,107 +1908,103 @@ input JsonElementPropositionInput { input JsonValueKindPropositionInput { equalTo: JsonValueKind @cost(weight: "10") - notEqualTo: JsonValueKind @cost(weight: "10") in: [JsonValueKind!] @cost(weight: "10") + notEqualTo: JsonValueKind @cost(weight: "10") notIn: [JsonValueKind!] @cost(weight: "10") } -input KeyValuePairOfGuidAndNullableOfUInt32Input { +input KeyValuePairOfGuidAndNullableUInt32Input { key: Uuid! value: NonNegativeInt } input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] - or: [LifeCycleDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [LifeCycleDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input LifeCycleDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input LocalDatePropositionInput { - and: [LocalDatePropositionInput!] - or: [LocalDatePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - month: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - dayOfYear: IntPropositionInput + equalTo: LocalDate @cost(weight: "10") + greaterThan: LocalDate @cost(weight: "10") + greaterThanOrEqualTo: LocalDate @cost(weight: "10") + in: [LocalDate] @cost(weight: "10") + lessThan: LocalDate @cost(weight: "10") + lessThanOrEqualTo: LocalDate @cost(weight: "10") + notEqualTo: LocalDate @cost(weight: "10") + notGreaterThan: LocalDate @cost(weight: "10") + notGreaterThanOrEqualTo: LocalDate @cost(weight: "10") + notIn: [LocalDate] @cost(weight: "10") + notLessThan: LocalDate @cost(weight: "10") + notLessThanOrEqualTo: LocalDate @cost(weight: "10") } input LocalDateTimePropositionInput { - and: [LocalDateTimePropositionInput!] - or: [LocalDateTimePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - month: IntPropositionInput - dayOfYear: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput - timeOfDay: LocalTimePropositionInput - date: LocalDatePropositionInput + equalTo: LocalDateTime @cost(weight: "10") + greaterThan: LocalDateTime @cost(weight: "10") + greaterThanOrEqualTo: LocalDateTime @cost(weight: "10") + in: [LocalDateTime] @cost(weight: "10") + lessThan: LocalDateTime @cost(weight: "10") + lessThanOrEqualTo: LocalDateTime @cost(weight: "10") + notEqualTo: LocalDateTime @cost(weight: "10") + notGreaterThan: LocalDateTime @cost(weight: "10") + notGreaterThanOrEqualTo: LocalDateTime @cost(weight: "10") + notIn: [LocalDateTime] @cost(weight: "10") + notLessThan: LocalDateTime @cost(weight: "10") + notLessThanOrEqualTo: LocalDateTime @cost(weight: "10") } input LocalTimePropositionInput { - and: [LocalTimePropositionInput!] - or: [LocalTimePropositionInput!] - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput + equalTo: LocalTime @cost(weight: "10") + greaterThan: LocalTime @cost(weight: "10") + greaterThanOrEqualTo: LocalTime @cost(weight: "10") + in: [LocalTime] @cost(weight: "10") + lessThan: LocalTime @cost(weight: "10") + lessThanOrEqualTo: LocalTime @cost(weight: "10") + notEqualTo: LocalTime @cost(weight: "10") + notGreaterThan: LocalTime @cost(weight: "10") + notGreaterThanOrEqualTo: LocalTime @cost(weight: "10") + notIn: [LocalTime] @cost(weight: "10") + notLessThan: LocalTime @cost(weight: "10") + notLessThanOrEqualTo: LocalTime @cost(weight: "10") } input LongPropositionInput { equalTo: Long @cost(weight: "10") - notEqualTo: Long @cost(weight: "10") - in: [Long] @cost(weight: "10") - notIn: [Long] @cost(weight: "10") greaterThan: Long @cost(weight: "10") - notGreaterThan: Long @cost(weight: "10") greaterThanOrEqualTo: Long @cost(weight: "10") - notGreaterThanOrEqualTo: Long @cost(weight: "10") + in: [Long] @cost(weight: "10") lessThan: Long @cost(weight: "10") - notLessThan: Long @cost(weight: "10") lessThanOrEqualTo: Long @cost(weight: "10") + notEqualTo: Long @cost(weight: "10") + notGreaterThan: Long @cost(weight: "10") + notGreaterThanOrEqualTo: Long @cost(weight: "10") + notIn: [Long] @cost(weight: "10") + notLessThan: Long @cost(weight: "10") notLessThanOrEqualTo: Long @cost(weight: "10") } @@ -1996,8 +2015,8 @@ input NamedMethodArgumentInput { input NamedMethodArgumentPropositionInput { and: [NamedMethodArgumentPropositionInput!] - or: [NamedMethodArgumentPropositionInput!] name: StringPropositionInput + or: [NamedMethodArgumentPropositionInput!] } input NamedMethodArgumentSortInput { @@ -2011,43 +2030,43 @@ input NamedMethodSourceInput { input NamedMethodSourcePropositionInput { and: [NamedMethodSourcePropositionInput!] - or: [NamedMethodSourcePropositionInput!] name: StringPropositionInput + or: [NamedMethodSourcePropositionInput!] value: CrossDatabaseDataReferencePropositionInput } input NullableOfCalorimetricObserverPropositionInput { equalTo: CalorimetricObserver @cost(weight: "10") - notEqualTo: CalorimetricObserver @cost(weight: "10") in: [CalorimetricObserver] @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") notIn: [CalorimetricObserver] @cost(weight: "10") } input NullableOfCoatedSidePropositionInput { equalTo: CoatedSide @cost(weight: "10") - notEqualTo: CoatedSide @cost(weight: "10") in: [CoatedSide] @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") notIn: [CoatedSide] @cost(weight: "10") } input NullableOfIlluminantPropositionInput { equalTo: Illuminant @cost(weight: "10") - notEqualTo: Illuminant @cost(weight: "10") in: [Illuminant] @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") notIn: [Illuminant] @cost(weight: "10") } input NullableOfOpticalComponentSubtypePropositionInput { equalTo: OpticalComponentSubtype @cost(weight: "10") - notEqualTo: OpticalComponentSubtype @cost(weight: "10") in: [OpticalComponentSubtype] @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") notIn: [OpticalComponentSubtype] @cost(weight: "10") } input NullableOfOpticalComponentTypePropositionInput { equalTo: OpticalComponentType @cost(weight: "10") - notEqualTo: OpticalComponentType @cost(weight: "10") in: [OpticalComponentType] @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") notIn: [OpticalComponentType] @cost(weight: "10") } @@ -2059,117 +2078,86 @@ input NumerationInput { input NumerationPropositionInput { and: [NumerationPropositionInput!] + mainNumber: StringPropositionInput or: [NumerationPropositionInput!] prefix: StringPropositionInput - mainNumber: StringPropositionInput suffix: StringPropositionInput } input NumerationSortInput { - prefix: SortEnumType @cost(weight: "10") mainNumber: SortEnumType @cost(weight: "10") + prefix: SortEnumType @cost(weight: "10") suffix: SortEnumType @cost(weight: "10") } -input OffsetDateTimePropositionInput { - and: [OffsetDateTimePropositionInput!] - or: [OffsetDateTimePropositionInput!] - calendar: CalendarSystemPropositionInput - year: IntPropositionInput - month: IntPropositionInput - day: IntPropositionInput - dayOfWeek: IsoDayOfWeekPropositionInput - yearOfEra: IntPropositionInput - era: EraPropositionInput - dayOfYear: IntPropositionInput - hour: IntPropositionInput - clockHourOfHalfDay: IntPropositionInput - minute: IntPropositionInput - second: IntPropositionInput - millisecond: IntPropositionInput - tickOfSecond: IntPropositionInput - tickOfDay: LongPropositionInput - nanosecondOfSecond: IntPropositionInput - nanosecondOfDay: LongPropositionInput - localDateTime: LocalDateTimePropositionInput - date: LocalDatePropositionInput - timeOfDay: LocalTimePropositionInput - offset: OffsetPropositionInput -} - -input OffsetPropositionInput { - and: [OffsetPropositionInput!] - or: [OffsetPropositionInput!] - seconds: IntPropositionInput - milliseconds: IntPropositionInput - ticks: LongPropositionInput - nanoseconds: LongPropositionInput -} - input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] - or: [OpticalDataPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - warnings: StringsPropositionInput - type: NullableOfOpticalComponentTypePropositionInput - subtype: NullableOfOpticalComponentSubtypePropositionInput + cielabColors: FilterInputTypeOfCielabColorsPropositionInput coatedSide: NullableOfCoatedSidePropositionInput + colorRenderingIndices: FloatsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + id: UuidPropositionInput + infraredEmittances: FloatsPropositionInput + locale: StringPropositionInput + name: StringPropositionInput nearnormalHemisphericalSolarReflectances: FloatsPropositionInput nearnormalHemisphericalSolarTransmittances: FloatsPropositionInput nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput - infraredEmittances: FloatsPropositionInput - colorRenderingIndices: FloatsPropositionInput - cielabColors: FilterInputTypeOfCielabColorsPropositionInput + or: [OpticalDataPropositionInput!] + resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + subtype: NullableOfOpticalComponentSubtypePropositionInput + type: NullableOfOpticalComponentTypePropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput + warnings: StringsPropositionInput } input OpticalDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] - or: [PhotovoltaicDataPropositionInput!] + appliedMethod: AppliedMethodPropositionInput + approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + createdAt: DateTimePropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput id: UuidPropositionInput - userId: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput - appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + or: [PhotovoltaicDataPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } input PhotovoltaicDataSortInput { + appliedMethod: AppliedMethodSortInput @cost(weight: "10") + componentId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + creatorId: SortEnumType @cost(weight: "10") + description: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") locale: SortEnumType @cost(weight: "10") name: SortEnumType @cost(weight: "10") - description: SortEnumType @cost(weight: "10") - componentId: SortEnumType @cost(weight: "10") - creatorId: SortEnumType @cost(weight: "10") - createdAt: SortEnumType @cost(weight: "10") - appliedMethod: AppliedMethodSortInput @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input PublicationInput { @@ -2184,36 +2172,36 @@ input PublicationInput { } input PublicationPropositionInput { + abstract: StringPropositionInput and: [PublicationPropositionInput!] - or: [PublicationPropositionInput!] + "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." + arXiv: StringPropositionInput authors: StringsPropositionInput "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." doi: StringPropositionInput - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: StringPropositionInput + or: [PublicationPropositionInput!] + "Referenced section" + section: StringPropositionInput + title: StringPropositionInput "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: StringPropositionInput "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UrlPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput - "Referenced section" - section: StringPropositionInput } input PublicationSortInput { - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: SortEnumType @cost(weight: "10") + abstract: SortEnumType @cost(weight: "10") "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." arXiv: SortEnumType @cost(weight: "10") + "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." + doi: SortEnumType @cost(weight: "10") + "Referenced section" + section: SortEnumType @cost(weight: "10") + title: SortEnumType @cost(weight: "10") "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." urn: SortEnumType @cost(weight: "10") "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") - abstract: SortEnumType @cost(weight: "10") - "Referenced section" - section: SortEnumType @cost(weight: "10") } input PublishDataInput { @@ -2223,19 +2211,18 @@ input PublishDataInput { input RecomputeGetHttpsResourceHashValuesPropositionInput { and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] - or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] - id: UuidPropositionInput - description: StringPropositionInput - hashValue: StringPropositionInput - dataFormatId: UuidPropositionInput appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput - parent: GetHttpsResourceTreeNonRootVertexPropositionInput calorimetricData: CalorimetricDataPropositionInput + dataFormatId: UuidPropositionInput + description: StringPropositionInput geometricData: GeometricDataPropositionInput + hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput + or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] + parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput } @@ -2247,13 +2234,13 @@ input ReferenceInput { input ReferencePropositionInput { and: [ReferencePropositionInput!] or: [ReferencePropositionInput!] - standard: StandardPropositionInput publication: PublicationPropositionInput + standard: StandardPropositionInput } input ReferenceSortInput { - standard: StandardSortInput @cost(weight: "10") publication: PublicationSortInput @cost(weight: "10") + standard: StandardSortInput @cost(weight: "10") } input RemoveDataApprovalInput { @@ -2281,16 +2268,16 @@ input SetGetHttpsResourceParentInput { input ShortPropositionInput { equalTo: Short @cost(weight: "10") - notEqualTo: Short @cost(weight: "10") - in: [Short] @cost(weight: "10") - notIn: [Short] @cost(weight: "10") greaterThan: Short @cost(weight: "10") - notGreaterThan: Short @cost(weight: "10") greaterThanOrEqualTo: Short @cost(weight: "10") - notGreaterThanOrEqualTo: Short @cost(weight: "10") + in: [Short] @cost(weight: "10") lessThan: Short @cost(weight: "10") - notLessThan: Short @cost(weight: "10") lessThanOrEqualTo: Short @cost(weight: "10") + notEqualTo: Short @cost(weight: "10") + notGreaterThan: Short @cost(weight: "10") + notGreaterThanOrEqualTo: Short @cost(weight: "10") + notIn: [Short] @cost(weight: "10") + notLessThan: Short @cost(weight: "10") notLessThanOrEqualTo: Short @cost(weight: "10") } @@ -2304,82 +2291,67 @@ input StandardInput { year: Int } +input StandardizerPropositionInput { + equalTo: Standardizer @cost(weight: "10") + in: [Standardizer!] @cost(weight: "10") + notEqualTo: Standardizer @cost(weight: "10") + notIn: [Standardizer!] @cost(weight: "10") +} + +input StandardizersPropositionInput { + all: StandardizerPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: StandardizerPropositionInput @cost(weight: "10") + some: StandardizerPropositionInput @cost(weight: "10") +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." input StandardPropositionInput { + abstract: StringPropositionInput and: [StandardPropositionInput!] - or: [StandardPropositionInput!] - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: IntPropositionInput - numeration: NumerationPropositionInput - standardizers: StandardizersPropositionInput locator: UrlPropositionInput - title: StringPropositionInput - abstract: StringPropositionInput + numeration: NumerationPropositionInput + or: [StandardPropositionInput!] "The section of the standard to which the reference refers to." section: StringPropositionInput + standardizers: StandardizersPropositionInput + title: StringPropositionInput + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: IntPropositionInput } "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." input StandardSortInput { - "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." - year: SortEnumType @cost(weight: "10") - numeration: NumerationSortInput @cost(weight: "10") - locator: UriSortInput @cost(weight: "10") - title: SortEnumType @cost(weight: "10") abstract: SortEnumType @cost(weight: "10") + locator: UriSortInput @cost(weight: "10") + numeration: NumerationSortInput @cost(weight: "10") "The section of the standard to which the reference refers to." section: SortEnumType @cost(weight: "10") -} - -input StandardizerPropositionInput { - equalTo: Standardizer @cost(weight: "10") - notEqualTo: Standardizer @cost(weight: "10") - in: [Standardizer!] @cost(weight: "10") - notIn: [Standardizer!] @cost(weight: "10") -} - -input StandardizersPropositionInput { - all: StandardizerPropositionInput @cost(weight: "10") - none: StandardizerPropositionInput @cost(weight: "10") - some: StandardizerPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") + title: SortEnumType @cost(weight: "10") + "It is important to define the year in which the standard was issued because there can be relevant updates of one standard." + year: SortEnumType @cost(weight: "10") } input StringPropositionInput { and: [StringPropositionInput!] - or: [StringPropositionInput!] - equalTo: String @cost(weight: "10") - notEqualTo: String @cost(weight: "10") contains: String @cost(weight: "20") doesNotContain: String @cost(weight: "20") + doesNotEndWith: String @cost(weight: "20") + doesNotStartWith: String @cost(weight: "20") + endsWith: String @cost(weight: "20") + equalTo: String @cost(weight: "10") in: [String] @cost(weight: "10") + notEqualTo: String @cost(weight: "10") notIn: [String] @cost(weight: "10") + or: [StringPropositionInput!] startsWith: String @cost(weight: "20") - doesNotStartWith: String @cost(weight: "20") - endsWith: String @cost(weight: "20") - doesNotEndWith: String @cost(weight: "20") } input StringsPropositionInput { all: StringPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") none: StringPropositionInput @cost(weight: "10") some: StringPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") -} - -input TimeSpanPropositionInput { - equalTo: TimeSpan @cost(weight: "10") - notEqualTo: TimeSpan @cost(weight: "10") - in: [TimeSpan] @cost(weight: "10") - notIn: [TimeSpan] @cost(weight: "10") - greaterThan: TimeSpan @cost(weight: "10") - notGreaterThan: TimeSpan @cost(weight: "10") - greaterThanOrEqualTo: TimeSpan @cost(weight: "10") - notGreaterThanOrEqualTo: TimeSpan @cost(weight: "10") - lessThan: TimeSpan @cost(weight: "10") - notLessThan: TimeSpan @cost(weight: "10") - lessThanOrEqualTo: TimeSpan @cost(weight: "10") - notLessThanOrEqualTo: TimeSpan @cost(weight: "10") } input ToTreeVertexAppliedConversionMethodInput { @@ -2390,9 +2362,9 @@ input ToTreeVertexAppliedConversionMethodInput { input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] - or: [ToTreeVertexAppliedConversionMethodPropositionInput!] - methodId: UuidPropositionInput arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + methodId: UuidPropositionInput + or: [ToTreeVertexAppliedConversionMethodPropositionInput!] sourceName: StringPropositionInput } @@ -2412,11 +2384,18 @@ input UpdateChildGetHttpsResourceInput { input UpdateDataAccessRightsInput { allowedApplications: [String!] allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableOfUInt32Input!] + allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32Input!] dataId: Uuid! dataKind: DataKind! } +input UpdateDatabaseInput { + databaseId: Uuid! + description: String! + locator: Url! + name: String! +} + input UpdateDataInput { componentId: Uuid! createdAt: DateTime! @@ -2429,13 +2408,6 @@ input UpdateDataInput { warnings: [String!]! } -input UpdateDatabaseInput { - databaseId: Uuid! - description: String! - locator: Url! - name: String! -} - input UpdateInstitutionAccessRightsInput { allowedDatasetsPerTimeSpan: NonNegativeInt allowedUserCount: NonNegativeInt @@ -2445,18 +2417,16 @@ input UpdateInstitutionAccessRightsInput { input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] - or: [UpdateResponseApprovalsPropositionInput!] - id: UuidPropositionInput - userId: UuidPropositionInput - locale: StringPropositionInput - name: StringPropositionInput - description: StringPropositionInput - componentId: UuidPropositionInput - creatorId: UuidPropositionInput - createdAt: OffsetDateTimePropositionInput appliedMethod: AppliedMethodPropositionInput approvals: FilterInputTypeOfDataApprovalsPropositionInput + componentId: UuidPropositionInput + creatorId: UuidPropositionInput + description: StringPropositionInput + locale: StringPropositionInput + name: StringPropositionInput + or: [UpdateResponseApprovalsPropositionInput!] resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2470,54 +2440,54 @@ input UpdateRootGetHttpsResourceInput { input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") - localPath: SortEnumType @cost(weight: "10") authority: SortEnumType @cost(weight: "10") + dnsSafeHost: SortEnumType @cost(weight: "10") + fragment: SortEnumType @cost(weight: "10") + host: SortEnumType @cost(weight: "10") hostNameType: SortEnumType @cost(weight: "10") + idnHost: SortEnumType @cost(weight: "10") + isAbsoluteUri: SortEnumType @cost(weight: "10") isDefaultPort: SortEnumType @cost(weight: "10") isFile: SortEnumType @cost(weight: "10") isLoopback: SortEnumType @cost(weight: "10") - pathAndQuery: SortEnumType @cost(weight: "10") isUnc: SortEnumType @cost(weight: "10") - host: SortEnumType @cost(weight: "10") + localPath: SortEnumType @cost(weight: "10") + originalString: SortEnumType @cost(weight: "10") + pathAndQuery: SortEnumType @cost(weight: "10") port: SortEnumType @cost(weight: "10") query: SortEnumType @cost(weight: "10") - fragment: SortEnumType @cost(weight: "10") scheme: SortEnumType @cost(weight: "10") - originalString: SortEnumType @cost(weight: "10") - dnsSafeHost: SortEnumType @cost(weight: "10") - idnHost: SortEnumType @cost(weight: "10") - isAbsoluteUri: SortEnumType @cost(weight: "10") userEscaped: SortEnumType @cost(weight: "10") userInfo: SortEnumType @cost(weight: "10") } input UrlPropositionInput { equalTo: Url @cost(weight: "10") - notEqualTo: Url @cost(weight: "10") - in: [Url] @cost(weight: "10") - notIn: [Url] @cost(weight: "10") greaterThan: Url @cost(weight: "10") - notGreaterThan: Url @cost(weight: "10") greaterThanOrEqualTo: Url @cost(weight: "10") - notGreaterThanOrEqualTo: Url @cost(weight: "10") + in: [Url] @cost(weight: "10") lessThan: Url @cost(weight: "10") - notLessThan: Url @cost(weight: "10") lessThanOrEqualTo: Url @cost(weight: "10") + notEqualTo: Url @cost(weight: "10") + notGreaterThan: Url @cost(weight: "10") + notGreaterThanOrEqualTo: Url @cost(weight: "10") + notIn: [Url] @cost(weight: "10") + notLessThan: Url @cost(weight: "10") notLessThanOrEqualTo: Url @cost(weight: "10") } input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") - notEqualTo: Uuid @cost(weight: "10") - in: [Uuid] @cost(weight: "10") - notIn: [Uuid] @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") - notGreaterThan: Uuid @cost(weight: "10") greaterThanOrEqualTo: Uuid @cost(weight: "10") - notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + in: [Uuid] @cost(weight: "10") lessThan: Uuid @cost(weight: "10") - notLessThan: Uuid @cost(weight: "10") lessThanOrEqualTo: Uuid @cost(weight: "10") + notEqualTo: Uuid @cost(weight: "10") + notGreaterThan: Uuid @cost(weight: "10") + notGreaterThanOrEqualTo: Uuid @cost(weight: "10") + notIn: [Uuid] @cost(weight: "10") + notLessThan: Uuid @cost(weight: "10") notLessThanOrEqualTo: Uuid @cost(weight: "10") } @@ -2680,6 +2650,11 @@ enum CreateResponseApprovalsErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum DatabaseVerificationState { + PENDING + VERIFIED +} + enum DataKind { CALORIMETRIC_DATA GEOMETRIC_DATA @@ -2689,11 +2664,6 @@ enum DataKind { PHOTOVOLTAIC_DATA } -enum DatabaseVerificationState { - PENDING - VERIFIED -} - enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -2717,17 +2687,6 @@ enum Illuminant { D65 } -enum IsoDayOfWeek { - NONE - MONDAY - TUESDAY - WEDNESDAY - THURSDAY - FRIDAY - SATURDAY - SUNDAY -} - enum JsonValueKind { UNDEFINED OBJECT @@ -2862,6 +2821,15 @@ enum UpdateDataAccessRightsErrorCode { UNAUTHENTICATED UNAUTHORIZED UNKNOWN_DATA + UNKNOWN_INSTITUTION + UNKNOWN_USER + UNKNOWN_APPLICATION +} + +enum UpdateDatabaseErrorCode { + UNKNOWN + UNAUTHORIZED + UNKNOWN_DATABASE } enum UpdateDataErrorCode { @@ -2871,12 +2839,6 @@ enum UpdateDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateDatabaseErrorCode { - UNKNOWN - UNAUTHORIZED - UNKNOWN_DATABASE -} - enum UpdateInstitutionAccessRightsErrorCode { UNKNOWN UNAUTHENTICATED @@ -2901,80 +2863,83 @@ enum UpdateRootGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." -directive @cost( - "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." - weight: String! -) on SCALAR | OBJECT | FIELD_DEFINITION | ARGUMENT_DEFINITION | ENUM | INPUT_FIELD_DEFINITION - -"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." -directive @listSize( - "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." - assumedSize: Int - "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." - requireOneSlicingArgument: Boolean! = true - "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." - sizedFields: [String!] - "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." - slicingArgumentDefaultValue: Int - "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." - slicingArguments: [String!] -) on FIELD_DEFINITION - -"The `@specifiedBy` directive is used within the type system definition language to provide a URL for specifying the behavior of custom scalar definitions." -directive @specifiedBy( - "The specifiedBy URL points to a human-readable specification. This field will only read a result for scalar types." - url: String! -) on SCALAR +scalar Any @specifiedBy(url: "https://scalars.graphql.org/chillicream/any.html") -scalar Any - -"The `Byte` scalar type represents non-fractional whole numeric values. Byte can represent values between 0 and 255." +"The `Byte` scalar type represents a signed 8-bit integer." scalar Byte + @specifiedBy(url: "https://scalars.graphql.org/chillicream/byte.html") -"The `DateTime` scalar represents an ISO-8601 compliant date time type." -scalar DateTime @specifiedBy(url: "https://www.graphql-scalars.com/date-time") - -""" -Represents a time zone - a mapping between UTC and local time. -A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant. +"The `DateTime` scalar type represents a date and time with time zone offset information." +scalar DateTime + @specifiedBy(url: "https://scalars.graphql.org/chillicream/date-time.html") -Example: `Europe/Zurich` -""" -scalar DateTimeZone - -"The `Decimal` scalar type represents a decimal floating-point number." +"The `Decimal` scalar type represents a decimal floating-point number with high precision." scalar Decimal + @specifiedBy(url: "https://scalars.graphql.org/chillicream/decimal.html") -""" -Represents a fixed (and calendar-independent) length of time. +"The `Duration` scalar type represents a duration of time." +scalar Duration + @specifiedBy(url: "https://scalars.graphql.org/chillicream/duration.html") -Allowed patterns: -- `-D:hh:mm:ss.sssssssss` +"The `LocalDate` scalar type represents a date without time or time zone information." +scalar LocalDate + @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-date.html") -Examples: -- `-1:20:00:00.999999999` -""" -scalar Duration +"The `LocalDateTime` scalar type represents a date and time without time zone information." +scalar LocalDateTime + @specifiedBy( + url: "https://scalars.graphql.org/chillicream/local-date-time.html" + ) "BCP 47 compliant Language Tag string" scalar Locale -"The `Long` scalar type represents non-fractional signed whole 64-bit numeric values. Long can represent values between -(2^63) and 2^63 - 1." +"The `LocalTime` scalar type represents a time of day without date or time zone information." +scalar LocalTime + @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-time.html") + +"The `Long` scalar type represents a signed 64-bit integer." scalar Long + @specifiedBy(url: "https://scalars.graphql.org/chillicream/long.html") -"The NonNegativeInt scalar type represents a unsigned 32-bit numeric non-fractional value equal to or greater than 0." +"The `NonNegativeInt` scalar type represents an unsigned 32-bit numeric non-fractional value." scalar NonNegativeInt -"The `Short` scalar type represents non-fractional signed whole 16-bit numeric values. Short can represent values between -(2^15) and 2^15 - 1." +"The `Short` scalar type represents a signed 16-bit integer." scalar Short - -"The `TimeSpan` scalar represents an ISO-8601 compliant duration type." -scalar TimeSpan + @specifiedBy(url: "https://scalars.graphql.org/chillicream/short.html") "The `Upload` scalar type represents a file upload." scalar Upload -scalar Url +"The `Url` scalar type represents a Uniform Resource Identifier (URI) as defined by RFC 3986." +scalar Url @specifiedBy(url: "https://tools.ietf.org/html/rfc3986") scalar Uuid + @specifiedBy(url: "https://scalars.graphql.org/chillicream/uuid.html") + +"The purpose of the `cost` directive is to define a `weight` for GraphQL types, fields, and arguments. Static analysis can use these weights when calculating the overall cost of a query or response." +directive @cost( + "The `weight` argument defines what value to add to the overall cost for every appearance, or possible appearance, of a type, field, argument, etc." + weight: String! +) on + | SCALAR + | OBJECT + | FIELD_DEFINITION + | ARGUMENT_DEFINITION + | ENUM + | INPUT_FIELD_DEFINITION + +"The purpose of the `@listSize` directive is to either inform the static analysis about the size of returned lists (if that information is statically available), or to point the analysis to where to find that information." +directive @listSize( + "The `assumedSize` argument can be used to statically define the maximum length of a list returned by a field." + assumedSize: Int + "The `requireOneSlicingArgument` argument can be used to inform the static analysis that it should expect that exactly one of the defined slicing arguments is present in a query. If that is not the case (i.e., if none or multiple slicing arguments are present), the static analysis may throw an error." + requireOneSlicingArgument: Boolean = true + "The `sizedFields` argument can be used to define that the value of the `assumedSize` argument or of a slicing argument does not affect the size of a list returned by a field itself, but that of a list returned by one of its sub-fields." + sizedFields: [String!] + "The `slicingArgumentDefaultValue` argument can be used to define a default value for a slicing argument, which is used if the argument is not present in a query." + slicingArgumentDefaultValue: Int + "The `slicingArguments` argument can be used to define which of the field's arguments with numeric type are slicing arguments, so that their value determines the size of the list returned by that field. It may specify a list of multiple slicing arguments." + slicingArguments: [String!] +) on FIELD_DEFINITION \ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2bb05242..e4a56c59 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -79,14 +79,14 @@ classnames "^2.2.6" rc-util "^5.31.1" -"@ant-design/icons@^6.1.1": - version "6.2.2" - resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.2.tgz#ed33a140ff35d7bcb6e48f01a6970ea6939cef99" - integrity sha512-zlJtE7AMbG12TeYVPhtBXwNpFInNy8mjLzcIm+0BPw16/b8ODG87YJ1G37VIF5VFscdgfsf6EweAFPTobu/3iQ== +"@ant-design/icons@^6.2.3", "@ant-design/icons@^6.2.5": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.5.tgz#31c142aa6ce5eaf99598aaead222f4c459693512" + integrity sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw== dependencies: "@ant-design/colors" "^8.0.1" "@ant-design/icons-svg" "^4.4.2" - "@rc-component/util" "^1.10.1" + "@rc-component/util" "^1.11.0" clsx "^2.1.1" "@ant-design/pro-layout@^7.10.3": @@ -149,9 +149,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^4.0.9": - version "4.1.9" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.1.9.tgz#797186c27ad98f728e40a7e33b7539675d1509b7" - integrity sha512-qfpkQD51tdU/7iAR6aLb4w9o/L7I475DluWHRb61U/3Q0AH29nNOxOBHjBbWDdf16ncPOoQuxne1sEs2NjqBFw== + version "4.2.0" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.2.0.tgz#cd5d40f2fd04eabdda77c2e873a9e4c59ac0759f" + integrity sha512-uZAiXwIRidDqQKZRcL88O01IVZjY6IhLio6g+XzX4N4++Ue9pVK9WCoZvBm1dvx+x2mH0oGfVUZvTvacCW4WcQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -170,34 +170,34 @@ immutable "^5.1.5" invariant "^2.2.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.27.1", "@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" - integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" + integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== dependencies: - "@babel/helper-validator-identifier" "^7.28.5" + "@babel/helper-validator-identifier" "^7.29.7" js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/compat-data@^7.28.6": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.0.tgz#00d03e8c0ac24dd9be942c5370990cbe1f17d88d" - integrity sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg== - -"@babel/core@^7.0", "@babel/core@^7.23.9", "@babel/core@^7.24.4", "@babel/core@^7.27.4", "@babel/core@^7.28.6": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" - integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-compilation-targets" "^7.28.6" - "@babel/helper-module-transforms" "^7.28.6" - "@babel/helpers" "^7.28.6" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/traverse" "^7.29.0" - "@babel/types" "^7.29.0" +"@babel/compat-data@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.7.tgz#6f0237f0f36d2e51c0570a636faed9d2d0efe629" + integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== + +"@babel/core@^7.24.4", "@babel/core@^7.28.6": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.7.tgz#80c10b17248082968b57a857b91640971f2070f7" + integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-compilation-targets" "^7.29.7" + "@babel/helper-module-transforms" "^7.29.7" + "@babel/helpers" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" @@ -205,264 +205,133 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.18.13", "@babel/generator@^7.27.5", "@babel/generator@^7.29.0": - version "7.29.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" - integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== +"@babel/generator@^7.18.13", "@babel/generator@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3" + integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== dependencies: - "@babel/parser" "^7.29.0" - "@babel/types" "^7.29.0" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/helper-compilation-targets@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz#32c4a3f41f12ed1532179b108a4d746e105c2b25" - integrity sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA== +"@babel/helper-compilation-targets@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz#7a1def704302401c47f64fa85589e974ae217042" + integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g== dependencies: - "@babel/compat-data" "^7.28.6" - "@babel/helper-validator-option" "^7.27.1" + "@babel/compat-data" "^7.29.7" + "@babel/helper-validator-option" "^7.29.7" browserslist "^4.24.0" lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-globals@^7.28.0": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" - integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== +"@babel/helper-globals@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz#f04a96fbd8473241b1079243f5b3f03a3010ab7b" + integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA== -"@babel/helper-module-imports@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" - integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== +"@babel/helper-module-imports@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396" + integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g== dependencies: - "@babel/traverse" "^7.28.6" - "@babel/types" "^7.28.6" + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" -"@babel/helper-module-transforms@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" - integrity sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA== +"@babel/helper-module-transforms@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz#b062747a5997ba138637201328bbff77960574ae" + integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg== dependencies: - "@babel/helper-module-imports" "^7.28.6" - "@babel/helper-validator-identifier" "^7.28.5" - "@babel/traverse" "^7.28.6" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.28.6", "@babel/helper-plugin-utils@^7.8.0": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz#6f13ea251b68c8532e985fd532f28741a8af9ac8" - integrity sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug== - -"@babel/helper-string-parser@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" - integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== - -"@babel/helper-validator-identifier@^7.28.5": - version "7.28.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" - integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + "@babel/helper-module-imports" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + "@babel/traverse" "^7.29.7" -"@babel/helper-validator-option@^7.27.1": - version "7.27.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" - integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== +"@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz#c0a0766f1a13617d8a17407d7ab8f9d486225ea4" + integrity sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw== -"@babel/helpers@^7.28.6": - version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.2.tgz#9cfbccb02b8e229892c0b07038052cc1a8709c49" - integrity sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw== - dependencies: - "@babel/template" "^7.28.6" - "@babel/types" "^7.29.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.4", "@babel/parser@^7.28.6", "@babel/parser@^7.29.0", "@babel/parser@^7.29.2": - version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" - integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== - dependencies: - "@babel/types" "^7.29.0" +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" +"@babel/helper-validator-option@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz#cf315be940213b354eb4abcc0bd01ebe3f73bc2a" + integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw== -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== +"@babel/helpers@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.7.tgz#45abfde7548997e34376c3e69feb475cffb4a607" + integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg== dependencies: - "@babel/helper-plugin-utils" "^7.12.13" + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== +"@babel/parser@^7.24.4", "@babel/parser@^7.29.2", "@babel/parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334" + integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg== dependencies: - "@babel/helper-plugin-utils" "^7.14.5" + "@babel/types" "^7.29.7" "@babel/plugin-syntax-import-assertions@^7.26.0": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz#ae9bc1923a6ba527b70104dd2191b0cd872c8507" - integrity sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw== - dependencies: - "@babel/helper-plugin-utils" "^7.28.6" - -"@babel/plugin-syntax-import-attributes@^7.24.7": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz#b71d5914665f60124e133696f17cd7669062c503" - integrity sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw== - dependencies: - "@babel/helper-plugin-utils" "^7.28.6" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.27.1": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz#f8ca28bbd84883b5fea0e447c635b81ba73997ee" - integrity sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w== - dependencies: - "@babel/helper-plugin-utils" "^7.28.6" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz#c5cd868505269126cc18882e1f01f7b0e0e24b4e" + integrity sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw== dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.27.1": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz#c7b2ddf1d0a811145b1de800d1abd146af92e3a2" - integrity sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A== - dependencies: - "@babel/helper-plugin-utils" "^7.28.6" + "@babel/helper-plugin-utils" "^7.29.7" "@babel/runtime@^7.10.1", "@babel/runtime@^7.11.1", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.23.2", "@babel/runtime@^7.24.4", "@babel/runtime@^7.24.7", "@babel/runtime@^7.24.8", "@babel/runtime@^7.28.4", "@babel/runtime@^7.29.2": - version "7.29.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" - integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== - -"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.28.6": - version "7.28.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" - integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== - dependencies: - "@babel/code-frame" "^7.28.6" - "@babel/parser" "^7.28.6" - "@babel/types" "^7.28.6" - -"@babel/traverse@^7.26.10", "@babel/traverse@^7.28.6", "@babel/traverse@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" - integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/types" "^7.29.0" + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768" + integrity sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw== + +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.29.7.tgz#4d9d4004f645cdd304de958c725162784ecac700" + integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" + +"@babel/traverse@^7.26.10", "@babel/traverse@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.7.tgz#c47b07a41b95da0907d026b5dd894d98de7d2f2d" + integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-globals" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" debug "^4.3.1" -"@babel/types@^7.0.0", "@babel/types@^7.18.13", "@babel/types@^7.20.7", "@babel/types@^7.26.10", "@babel/types@^7.27.3", "@babel/types@^7.28.2", "@babel/types@^7.28.6", "@babel/types@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" - integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== - dependencies: - "@babel/helper-string-parser" "^7.27.1" - "@babel/helper-validator-identifier" "^7.28.5" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== +"@babel/types@^7.18.13", "@babel/types@^7.26.10", "@babel/types@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92" + integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== dependencies: - "@jridgewell/trace-mapping" "0.3.9" + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" "@ctrl/tinycolor@^3.4.0": version "3.6.1" resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz#b6c75a56a1947cc916ea058772d666a2c8932f31" integrity sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA== -"@emnapi/core@^1.4.3": +"@emnapi/core@1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467" integrity sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw== @@ -470,7 +339,7 @@ "@emnapi/wasi-threads" "1.2.1" tslib "^2.4.0" -"@emnapi/runtime@^1.4.3", "@emnapi/runtime@^1.7.0": +"@emnapi/runtime@1.10.0", "@emnapi/runtime@^1.7.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c" integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA== @@ -541,10 +410,10 @@ debug "^4.3.1" minimatch "^10.2.4" -"@eslint/config-helpers@^0.5.5": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz#ae16134e4792ac5fbdc533548a24ac1ea9f7f3ae" - integrity sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w== +"@eslint/config-helpers@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.6.0.tgz#ef9a36881d39dfd5dbeac22b0da997fabfb08b03" + integrity sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA== dependencies: "@eslint/core" "^1.2.1" @@ -573,25 +442,25 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== -"@graphql-codegen/add@^6.0.0", "@graphql-codegen/add@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-6.0.1.tgz#90f8750e1074975bef1ead05f82523e32766c61a" - integrity sha512-MSylSekjpVWbOBw2A/2ssk1fPY54sYb6Qk2C4AX5u7s2R+2pMQ9ws7DTXo8VU9qwTgWwVp6vGfdQ0AMpAn4Iug== +"@graphql-codegen/add@^7.0.0", "@graphql-codegen/add@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/add/-/add-7.0.1.tgz#9972c7944fced5fa2223474fb481308c20ec9f2c" + integrity sha512-kWw6RMu9ysBw1wcgcgf9mOnswc5M3ekOApDTiaJC/UZNTEYins01srZHYTP7z3P/WlGGC844BRtjwh3U2kNd/A== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" tslib "^2.8.0" -"@graphql-codegen/cli@^6.0.2": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-6.3.1.tgz#f64f257cf81ffaf2e6241eff64c6de105e74dab2" - integrity sha512-I5KkyX1SgQZPojMeQTRydB6fml4cysZq/mIdhNW4rmqdoOcTgdMPq1Tl+wtRp1VpBAOrBazJUJh1nAqJMMSPIQ== +"@graphql-codegen/cli@^7.0.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-7.1.0.tgz#e6c02324260d7ad53147460362d709054496c92d" + integrity sha512-2j1FzZtEnlHdQmOaDnTMGSNyu3RmDf4HGU9xbgpqDRSb+D8UYya2YFW1enIxhJccAQSLD9ACMwCXlB20wJXNiA== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" "@babel/types" "^7.18.13" - "@graphql-codegen/client-preset" "^5.3.0" - "@graphql-codegen/core" "^5.0.2" - "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/client-preset" "^6.0.1" + "@graphql-codegen/core" "^6.1.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" "@graphql-tools/apollo-engine-loader" "^8.0.28" "@graphql-tools/code-file-loader" "^8.1.28" "@graphql-tools/git-loader" "^8.0.32" @@ -602,82 +471,82 @@ "@graphql-tools/merge" "^9.0.6" "@graphql-tools/url-loader" "^9.0.6" "@graphql-tools/utils" "^11.0.0" - "@inquirer/prompts" "^7.8.2" + "@inquirer/prompts" "^8.3.2" "@whatwg-node/fetch" "^0.10.0" - chalk "^4.1.0" + chalk "^5.6.0" cosmiconfig "^9.0.0" - debounce "^2.0.0" - detect-indent "^6.0.0" + debounce "^3.0.0" + detect-indent "^7.0.0" graphql-config "^5.1.6" is-glob "^4.0.1" jiti "^2.3.0" json-to-pretty-yaml "^1.2.2" - listr2 "^9.0.0" - log-symbols "^4.0.0" + listr2 "^10.2.1" + log-symbols "^7.0.0" micromatch "^4.0.5" shell-quote "^1.7.3" string-env-interpolation "^1.0.1" - ts-log "^2.2.3" + ts-log "^3.0.0" tslib "^2.4.0" yaml "^2.3.1" - yargs "^17.0.0" + yargs "^18.0.0" -"@graphql-codegen/client-preset@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-5.3.0.tgz#e479c9d03500ba9e30f699c8644196e9759877df" - integrity sha512-K9FON+j7qyxAUDuSGqI3ofb7lWTBs16oPTYpu14lhdL4DKZQSHLyc8EMYU9e3KcyQ/13gU/d6culOppzAuexLA== +"@graphql-codegen/client-preset@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/client-preset/-/client-preset-6.0.1.tgz#bd164ed1dcef39ba3c818582e6c76c5029497bff" + integrity sha512-6wh0ZHG9WzBD6bE4AVOO6VCCMXK2orxHuXxaNKj+sj1w0qZ3Y3WIjZnqZLg6JZrHCIs/e+gy3T15Dc2pH8IbHA== dependencies: "@babel/helper-plugin-utils" "^7.20.2" "@babel/template" "^7.20.7" - "@graphql-codegen/add" "^6.0.1" - "@graphql-codegen/gql-tag-operations" "5.2.0" - "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/typed-document-node" "^6.1.8" - "@graphql-codegen/typescript" "^5.0.10" - "@graphql-codegen/typescript-operations" "^5.1.0" - "@graphql-codegen/visitor-plugin-common" "^6.3.0" + "@graphql-codegen/add" "^7.0.1" + "@graphql-codegen/gql-tag-operations" "^6.0.1" + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-codegen/typed-document-node" "^7.0.1" + "@graphql-codegen/typescript" "^6.0.2" + "@graphql-codegen/typescript-operations" "^6.0.3" + "@graphql-codegen/visitor-plugin-common" "^7.0.3" "@graphql-tools/documents" "^1.0.0" "@graphql-tools/utils" "^11.0.0" "@graphql-typed-document-node/core" "3.2.0" tslib "^2.8.0" -"@graphql-codegen/core@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-5.0.2.tgz#6f64fd4ba7bf45a7a03229ed520609c50e048186" - integrity sha512-7RX0wwjoWPlLG/tUmpaTK91ZZqHcACNWpRL0nGnnJaJrORie9pgmX8JPrcwBgYiHSC+3ERo9xY91RFPem/VrpQ== +"@graphql-codegen/core@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/core/-/core-6.1.0.tgz#2e09987ec37e5cbc4e37be51e1f9e83c7028382b" + integrity sha512-jReAzuCYlrSBJHW2bfBpDl/vMRCw0yQEoTvGi9K+3OTsazDXEQGOpCVfj8p/xO2h7ynu5Yrvzo0sUylVv0CnwA== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" "@graphql-tools/schema" "^10.0.0" "@graphql-tools/utils" "^11.0.0" tslib "^2.8.0" -"@graphql-codegen/fragment-matcher@^6.0.0": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-6.0.1.tgz#4daff16f982810a1ac770d7fe7a8a3200b11254e" - integrity sha512-gM984SjSalbvHbvxqTI8MNy2wkoiuaLLlif4/1xDbFF0E2yIVzWknbSFwVyhjDJ+HjlZvSt+uH7BQv31Szhgsw== +"@graphql-codegen/fragment-matcher@^7.0.0": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/fragment-matcher/-/fragment-matcher-7.0.1.tgz#68235854c91d477a8e927a9f08e1b2189d707391" + integrity sha512-/RlUMGEoGpt82GHvl2vnLokWjYpiRATdnTcV5hMUhWN+evn6j+i+W76QuRhT5V90qUwDr79ojEOIsPm5eUHcFw== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" tslib "^2.8.0" -"@graphql-codegen/gql-tag-operations@5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-5.2.0.tgz#e8f7c8c5adcabb54e97548db8b0973f6602be0be" - integrity sha512-B9gtJ4ziqpIv+7mHqwjtpYLFOuv0GmmRGpNDoWKM2VIx4OQqgI84d6OHKYCVeO7yu3mUr0QPvUgkSyuLVrdukA== +"@graphql-codegen/gql-tag-operations@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-6.0.1.tgz#2aa916dd01002955425e450de0f0242e35493d07" + integrity sha512-eHYUIchZLG6G+kafeKnUByL2Nkmb8Uj2vg33UVLFj8XJ2coC4b1iRDWxCdTXbupZrN0FaM0QRRBLs3zBEAzcJg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/visitor-plugin-common" "^6.3.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-codegen/visitor-plugin-common" "^7.0.3" "@graphql-tools/utils" "^11.0.0" - auto-bind "~4.0.0" + auto-bind "^5.0.0" tslib "^2.8.0" "@graphql-codegen/near-operation-file-preset@^5.0.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-5.1.0.tgz#a5ec0555951800da280a292671da17d10f26ba29" - integrity sha512-tME5X7OrrkYUr9CeW4rHrl6lUxrojNNXwN4yibdnmSP1w93Pj/p4kogKmnacd5Sns05Q82cucswuTQTQmVENKA== + version "5.2.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/near-operation-file-preset/-/near-operation-file-preset-5.2.1.tgz#18aaec7e781812965845b16a11ec52b2ef6be7f3" + integrity sha512-F+9gln4QboYozcMfsXwOxsPDdqUmMBOQoGZww+9qegvpWNvQVcZXm1LbYibi1iWOfuM0TTXVtMsDDjMeyNJcHA== dependencies: - "@graphql-codegen/add" "^6.0.0" - "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/visitor-plugin-common" "^6.3.0" + "@graphql-codegen/add" "^7.0.0" + "@graphql-codegen/plugin-helpers" "^7.0.0" + "@graphql-codegen/visitor-plugin-common" "^7.0.0" "@graphql-tools/utils" "^11.0.0" parse-filepath "^1.0.2" tslib "^2.8.1" @@ -693,48 +562,70 @@ import-from "4.0.0" tslib "^2.8.0" -"@graphql-codegen/schema-ast@^5.0.2": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-5.0.2.tgz#0d7fd83760fa10fea6804f75c801e933d832ac33" - integrity sha512-jl1F/9IjRkJisEb9B0ayG4QGqYlPldLRy8ojDdmL9NE1NsdB5ROfxQnSqyC3g+wuvBhWX7kZgMRQYn3RU1I5bA== +"@graphql-codegen/plugin-helpers@^7.0.0", "@graphql-codegen/plugin-helpers@^7.0.1": + version "7.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/plugin-helpers/-/plugin-helpers-7.0.1.tgz#b806262faf5db4ce42e4d803a8f4bfd657574672" + integrity sha512-S2X0YT3XQbP2haqhIeku8GOXo2j8QuBu7BrLsOEHz4UeMu78y3rja1Q4ri3oJ0jq4dMgaQlazoVHI/A+FAKMGw== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" "@graphql-tools/utils" "^11.0.0" + change-case-all "^2.1.0" + common-tags "1.8.2" + import-from "4.0.0" tslib "^2.8.0" -"@graphql-codegen/typed-document-node@^6.1.8": - version "6.1.8" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.8.tgz#a0190940feef2b3b16e0f04e55b64a7d92837a2e" - integrity sha512-+qDdiJSQ7Ol+vpLMAH8ZJok50CvlYxA6seQ7cwEa3emXt8MmH5hh3zdc9unQlPc7bynoJHRCgoKk7E0B7hry0w== +"@graphql-codegen/schema-ast@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/schema-ast/-/schema-ast-6.0.1.tgz#5144bce74200d328aa46f7515e48b6ec2ebd6ca0" + integrity sha512-P16b6XCWXfcrA4fkuAyqoy883USAULifv8YWgEOrNKDAnr2DR+Kr85jSomknIUTY39wiuvisv4/lrdXobwK6sA== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/visitor-plugin-common" "^6.3.0" - auto-bind "~4.0.0" - change-case-all "1.0.15" + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-tools/utils" "^11.0.0" tslib "^2.8.0" -"@graphql-codegen/typescript-operations@^5.0.4", "@graphql-codegen/typescript-operations@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-5.1.0.tgz#8e30fc79999bf913e53836670d6046b881567e77" - integrity sha512-JlmjbFl0EnsfMDIYvTE1Q0kAOrntVEZ+ZfBqWTP91g4e0F/TzuwJ/V4tiFmeDf5dx/rf9AK4VkPehIdxu7TYhw== +"@graphql-codegen/typed-document-node@^7.0.1", "@graphql-codegen/typed-document-node@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-7.0.2.tgz#5589187a385ad13f71df3e0567e97ee1bebddc9d" + integrity sha512-vrjigzqpKsaQ2Ra4QBBuG9TcwG1O0pw9EOSFU0aj7mS2EK19vWs5N4rzwDDvHrQZW62wwqzUcqBoFitVBQqlmg== dependencies: - "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/typescript" "^5.0.10" - "@graphql-codegen/visitor-plugin-common" "^6.3.0" - auto-bind "~4.0.0" + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-codegen/visitor-plugin-common" "^7.0.4" + auto-bind "^5.0.0" + change-case-all "^2.1.0" tslib "^2.8.0" -"@graphql-codegen/typescript@^5.0.10", "@graphql-codegen/typescript@^5.0.4": - version "5.0.10" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-5.0.10.tgz#5991746408152503069b0c36bfdf5456b68a2e2a" - integrity sha512-Pa8OFmL9TdhEYnLYJLYA9EhP8eEeivP/YDYq4Nb8LQaL7GXm4TGX8zELYaCM9Fu8M3iZb7iQGMt7qc+1lXz8XQ== +"@graphql-codegen/typescript-apollo-client-helpers@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-apollo-client-helpers/-/typescript-apollo-client-helpers-4.0.1.tgz#574eb57432102dd41411f285eac6ca978d6b90f6" + integrity sha512-qYIuhDtcn5FBPr6IjSfP+PhgOEGYgNFj0C7MaNX+V9ri2FdY8C/m1vZp86TmHKt3X+ppjeOLgUDRYlKFaap83A== dependencies: "@graphql-codegen/plugin-helpers" "^6.3.0" - "@graphql-codegen/schema-ast" "^5.0.2" "@graphql-codegen/visitor-plugin-common" "^6.3.0" auto-bind "~4.0.0" + change-case-all "1.0.15" + tslib "^2.8.1" + +"@graphql-codegen/typescript-operations@^6.0.2", "@graphql-codegen/typescript-operations@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-6.0.3.tgz#23966ec2236833841d527cbeeeadcc09e2ba605e" + integrity sha512-5gnHdBgKkpKJOiqyKo37UfwigNtezfN94UI/NnyCROl6oDAyt0A+1VeS5cBlgRECdD/TQ1vjHCu14B41Sl7yLw== + dependencies: + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-codegen/schema-ast" "^6.0.1" + "@graphql-codegen/visitor-plugin-common" "^7.0.3" + auto-bind "^5.0.0" tslib "^2.8.0" +"@graphql-codegen/typescript@^6.0.1", "@graphql-codegen/typescript@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript/-/typescript-6.0.2.tgz#5a076dc38b9727970aef0df9fa0b33d95ed39939" + integrity sha512-zyLfKsFJ7TRkQ0PyaUVuiAek9TSbtVJwwBoOuaE9RAWr45+9Y5W1LYldpiSTcyfxKVSIniE7Gj0V87qzrpdyYw== + dependencies: + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-codegen/schema-ast" "^6.0.1" + "@graphql-codegen/visitor-plugin-common" "^7.0.3" + auto-bind "^5.0.0" + tslib "~2.8.0" + "@graphql-codegen/visitor-plugin-common@^6.3.0": version "6.3.0" resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.3.0.tgz#5c3d1b6397acd27a552beab989c5d8af155e2aef" @@ -751,6 +642,22 @@ parse-filepath "^1.0.2" tslib "^2.8.0" +"@graphql-codegen/visitor-plugin-common@^7.0.0", "@graphql-codegen/visitor-plugin-common@^7.0.3", "@graphql-codegen/visitor-plugin-common@^7.0.4": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-7.0.4.tgz#d0ab0f80dd2a29b1786a4a270ccf2f1ffaf1d5b9" + integrity sha512-IESwu40Gpl+9x5vRGLsaYAWM9KRobnN74h+SB4EtfDrt0V87Q74ZzWZGmuS2m6A/fq7E/bS6YgSEMVACjaRUVw== + dependencies: + "@graphql-codegen/plugin-helpers" "^7.0.1" + "@graphql-tools/optimize" "^2.0.0" + "@graphql-tools/relay-operation-optimizer" "^7.1.1" + "@graphql-tools/utils" "^11.0.0" + auto-bind "^5.0.0" + change-case-all "^2.1.0" + dependency-graph "^1.0.0" + graphql-tag "^2.11.0" + parse-filepath "^1.0.2" + tslib "^2.8.0" + "@graphql-hive/signal@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@graphql-hive/signal/-/signal-2.0.0.tgz#205805328b118e1ae556417ed364257234800464" @@ -787,10 +694,10 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^12.0.14": - version "12.0.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.14.tgz#982f209aa582fd3ed9dfd54fec562812785eeb33" - integrity sha512-/xCDM8zlCk1Lccww9asOIpxna9IFpIlol4yGsBD9Y2+3/Zu5k4/HzDC8LKJtw5MxdG+uJN1l9nRepr4GeBC4kA== +"@graphql-tools/delegate@^12.0.16": + version "12.0.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.16.tgz#2f6636abadcf0c57ac1356431d70cf4ac1be1e2d" + integrity sha512-WEJaFwWG82a0VzhfE4sRsaOPjxgCVfn4fOe3ho+r3uIbPYpc7qHpFdu1PLg6meikq6fuW9NJ1J88fEgnWuXDVg== dependencies: "@graphql-tools/batch-execute" "^10.0.8" "@graphql-tools/executor" "^1.4.13" @@ -831,9 +738,9 @@ ws "^8.18.3" "@graphql-tools/executor-http@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.2.1.tgz#28abc8d521b187a13b46b3dae5dba885096350e5" - integrity sha512-53i0TYO0cznIlZDJcnq4gQ6SOZ8efGgCDV33MYh6oqEapcp36tCMEVnVGVxcX5qRRyNHkqTY6hkA+/AyK9kicQ== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-3.3.0.tgz#64d351b90aa94577cdd57d44596f775cd9309534" + integrity sha512-IkKXIjSg9U8MNsQUBVJAXE4+LSxaQ0cs7p5JTALLGDABY1o17vPDRwWALsX81AXD5dY27ihi/+OhGMueW/Fopg== dependencies: "@graphql-hive/signal" "^2.0.0" "@graphql-tools/executor-common" "^1.0.6" @@ -1008,11 +915,11 @@ tslib "^2.4.0" "@graphql-tools/wrap@^11.1.1": - version "11.1.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.14.tgz#58d1854ab207676f241e9b405fda02556aec7d8a" - integrity sha512-ebSVT7apxr+88q3Wy0i4AyRmJ42I0SuMqjPIn1fqW14yCTQRZG8YLuIALG1gKR936+GkfMLOrADh6egJvdlN6Q== + version "11.1.15" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.15.tgz#d0e19db5d4075e3768ffcd0f5249f9856141a792" + integrity sha512-GCMx6l0MPwHVaBMHf29oG8eIrsJ8PBXq9y5DNX9/r9oCpCBfqxfWzcejx4CpO4chA3+yylGOKcAyEbOUgxfI1Q== dependencies: - "@graphql-tools/delegate" "^12.0.14" + "@graphql-tools/delegate" "^12.0.16" "@graphql-tools/schema" "^10.0.29" "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" @@ -1201,396 +1108,144 @@ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8" integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw== -"@inquirer/ansi@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-1.0.2.tgz#674a4c4d81ad460695cb2a1fc69d78cd187f337e" - integrity sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ== - -"@inquirer/checkbox@^4.3.2": - version "4.3.2" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-4.3.2.tgz#e1483e6519d6ffef97281a54d2a5baa0d81b3f3b" - integrity sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA== - dependencies: - "@inquirer/ansi" "^1.0.2" - "@inquirer/core" "^10.3.2" - "@inquirer/figures" "^1.0.15" - "@inquirer/type" "^3.0.10" - yoctocolors-cjs "^2.1.3" - -"@inquirer/confirm@^5.1.21": - version "5.1.21" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-5.1.21.tgz#610c4acd7797d94890a6e2dde2c98eb1e891dd12" - integrity sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ== - dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - -"@inquirer/core@^10.3.2": - version "10.3.2" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-10.3.2.tgz#535979ff3ff4fe1e7cc4f83e2320504c743b7e20" - integrity sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A== - dependencies: - "@inquirer/ansi" "^1.0.2" - "@inquirer/figures" "^1.0.15" - "@inquirer/type" "^3.0.10" - cli-width "^4.1.0" - mute-stream "^2.0.0" - signal-exit "^4.1.0" - wrap-ansi "^6.2.0" - yoctocolors-cjs "^2.1.3" +"@inquirer/ansi@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.6.tgz#537b3e75355a7599f25a2289843ccbc3318af98c" + integrity sha512-I/INw4sHGlVZ/afZOckpLiDP9SmbMl1g/GCqeHjLw1Afw/0PlRs2tRFgTGWmdI0hoNuWZn3y2iHNmG1vyECyQQ== -"@inquirer/editor@^4.2.23": - version "4.2.23" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-4.2.23.tgz#fe046a3bfdae931262de98c1052437d794322e0b" - integrity sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ== +"@inquirer/checkbox@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-5.2.0.tgz#762c20854971bdd4ddb0105fce3c65e1e02d1bb0" + integrity sha512-1HJt+3fqxblp/GQjdntSyoSHYBc0e3CzXVgjFpKA6qFLd9FHBBqwN8Co0xYH6t2JVUZrtFwZ4bBiwptkiLxyOg== dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/external-editor" "^1.0.3" - "@inquirer/type" "^3.0.10" + "@inquirer/ansi" "^2.0.6" + "@inquirer/core" "^11.2.0" + "@inquirer/figures" "^2.0.6" + "@inquirer/type" "^4.0.6" -"@inquirer/expand@^4.0.23": - version "4.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-4.0.23.tgz#a38b5f32226d75717c370bdfed792313b92bdc05" - integrity sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew== +"@inquirer/confirm@^6.1.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-6.1.0.tgz#69f31076d59d7f85f59a2d0b8434f46c95b9bf5e" + integrity sha512-USpeB76eqK7yGricDlGAupxWlp4a59qpeZOoNWaxO/nJln7agpJveyNkQ1d5u8YXG6TOqxZtQpKPORQQDrdVsA== dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - yoctocolors-cjs "^2.1.3" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" -"@inquirer/external-editor@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-1.0.3.tgz#c23988291ee676290fdab3fd306e64010a6d13b8" - integrity sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA== +"@inquirer/core@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-11.2.0.tgz#dd59e490099cde25da4f5a93db47fe5c563df72a" + integrity sha512-joR1YS2sI0us+9d0I8ViqFbrRLONO8CFTuyvBX4ZVBSch+VsZiugUABdrhBXXJR1VyEzvpz5SQCix3keETQ58g== dependencies: - chardet "^2.1.1" - iconv-lite "^0.7.0" - -"@inquirer/figures@^1.0.15": - version "1.0.15" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.15.tgz#dbb49ed80df11df74268023b496ac5d9acd22b3a" - integrity sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g== + "@inquirer/ansi" "^2.0.6" + "@inquirer/figures" "^2.0.6" + "@inquirer/type" "^4.0.6" + cli-width "^4.1.0" + fast-wrap-ansi "^0.2.0" + mute-stream "^4.0.0" + signal-exit "^4.1.0" -"@inquirer/input@^4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-4.3.1.tgz#778683b4c4c4d95d05d4b05c4a854964b73565b4" - integrity sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g== +"@inquirer/editor@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-5.2.0.tgz#ffac65843db4477c59a4aa223e193de5f8a98a86" + integrity sha512-/m+sgRmzSdK6HDtVnl3PmI6MnZC4O+LLezedoJcrX7mINhTjjb0hlC7aEDGZXkFTB4b5uQ0q59AhYTah88KbNg== dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - -"@inquirer/number@^3.0.23": - version "3.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-3.0.23.tgz#3fdec2540d642093fd7526818fd8d4bdc7335094" - integrity sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg== - dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - -"@inquirer/password@^4.0.23": - version "4.0.23" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-4.0.23.tgz#b9f5187c8c92fd7aa9eceb9d8f2ead0d7e7b000d" - integrity sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA== - dependencies: - "@inquirer/ansi" "^1.0.2" - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - -"@inquirer/prompts@^7.8.2": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-7.10.1.tgz#e1436c0484cf04c22548c74e2cd239e989d5f847" - integrity sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg== - dependencies: - "@inquirer/checkbox" "^4.3.2" - "@inquirer/confirm" "^5.1.21" - "@inquirer/editor" "^4.2.23" - "@inquirer/expand" "^4.0.23" - "@inquirer/input" "^4.3.1" - "@inquirer/number" "^3.0.23" - "@inquirer/password" "^4.0.23" - "@inquirer/rawlist" "^4.1.11" - "@inquirer/search" "^3.2.2" - "@inquirer/select" "^4.4.2" - -"@inquirer/rawlist@^4.1.11": - version "4.1.11" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-4.1.11.tgz#313c8c3ffccb7d41e990c606465726b4a898a033" - integrity sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw== - dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/type" "^3.0.10" - yoctocolors-cjs "^2.1.3" - -"@inquirer/search@^3.2.2": - version "3.2.2" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-3.2.2.tgz#4cc6fd574dcd434e4399badc37c742c3fd534ac8" - integrity sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA== - dependencies: - "@inquirer/core" "^10.3.2" - "@inquirer/figures" "^1.0.15" - "@inquirer/type" "^3.0.10" - yoctocolors-cjs "^2.1.3" - -"@inquirer/select@^4.4.2": - version "4.4.2" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-4.4.2.tgz#2ac8fca960913f18f1d1b35323ed8fcd27d89323" - integrity sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w== - dependencies: - "@inquirer/ansi" "^1.0.2" - "@inquirer/core" "^10.3.2" - "@inquirer/figures" "^1.0.15" - "@inquirer/type" "^3.0.10" - yoctocolors-cjs "^2.1.3" - -"@inquirer/type@^3.0.10": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-3.0.10.tgz#11ed564ec78432a200ea2601a212d24af8150d50" - integrity sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.6.tgz#8dc9afa2ac1506cb1a58f89940f1c124446c8df3" - integrity sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw== - -"@jest/console@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-30.3.0.tgz#42ccc3f995d400a8fe35b8850cfe10a8d4804cdf" - integrity sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww== - dependencies: - "@jest/types" "30.3.0" - "@types/node" "*" - chalk "^4.1.2" - jest-message-util "30.3.0" - jest-util "30.3.0" - slash "^3.0.0" - -"@jest/core@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-30.3.0.tgz#d06bb8456f35350f6494fd2405bcec4abb97b994" - integrity sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw== - dependencies: - "@jest/console" "30.3.0" - "@jest/pattern" "30.0.1" - "@jest/reporters" "30.3.0" - "@jest/test-result" "30.3.0" - "@jest/transform" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - ansi-escapes "^4.3.2" - chalk "^4.1.2" - ci-info "^4.2.0" - exit-x "^0.2.2" - graceful-fs "^4.2.11" - jest-changed-files "30.3.0" - jest-config "30.3.0" - jest-haste-map "30.3.0" - jest-message-util "30.3.0" - jest-regex-util "30.0.1" - jest-resolve "30.3.0" - jest-resolve-dependencies "30.3.0" - jest-runner "30.3.0" - jest-runtime "30.3.0" - jest-snapshot "30.3.0" - jest-util "30.3.0" - jest-validate "30.3.0" - jest-watcher "30.3.0" - pretty-format "30.3.0" - slash "^3.0.0" - -"@jest/diff-sequences@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz#25b0818d3d83f00b9c7b04e069b8810f9014b143" - integrity sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA== + "@inquirer/core" "^11.2.0" + "@inquirer/external-editor" "^3.0.1" + "@inquirer/type" "^4.0.6" -"@jest/environment@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.3.0.tgz#b0657c2944b6ef3352f7b25903cc3a23e6ab70f6" - integrity sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw== +"@inquirer/expand@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-5.1.0.tgz#77807c5e4ffc0904a9ca95f0deef7053153e441a" + integrity sha512-fR7g4BVnIcs+4NApF6C5byflNM/EULxSxsv/2Jvg+gmop0R6eBIPvZqE6RYnTy1tQTFnf9wyHkwNoQSZbofaGA== dependencies: - "@jest/fake-timers" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - jest-mock "30.3.0" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" -"@jest/expect-utils@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-30.3.0.tgz#c45b2da9802ffed33bf43b3e019ddb95e5ad95e8" - integrity sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA== +"@inquirer/external-editor@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-3.0.1.tgz#764d43a509734ce81d083a553a2b79ec3dc8d984" + integrity sha512-tam+Gwjsxg2sx3iUVPkAnhKT/yrk2rd2NAa7XJU/J8OYpU0ifXsnp12xlvzp/DCpWBXVv+vLQsqnpAWwUcWD5Q== dependencies: - "@jest/get-type" "30.1.0" + chardet "^2.1.1" + iconv-lite "^0.7.2" -"@jest/expect@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-30.3.0.tgz#08ee7f5b610167b0068743246c0b568f4c40c773" - integrity sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg== - dependencies: - expect "30.3.0" - jest-snapshot "30.3.0" +"@inquirer/figures@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-2.0.6.tgz#2760cc9a9ea8d238ba48059b16b77e5c7eb319ba" + integrity sha512-dsZgQtH2t5Q6ah3aPbZbeEZAxsD9qQu0DXf01AltuEfRTm+NoLN6+rLVbr+4edeEbNCp/wBNM6mALRWtsQpfkw== -"@jest/fake-timers@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.3.0.tgz#2b2868130c1d28233a79566874c42cae1c5a70bc" - integrity sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ== - dependencies: - "@jest/types" "30.3.0" - "@sinonjs/fake-timers" "^15.0.0" - "@types/node" "*" - jest-message-util "30.3.0" - jest-mock "30.3.0" - jest-util "30.3.0" - -"@jest/get-type@30.1.0": - version "30.1.0" - resolved "https://registry.yarnpkg.com/@jest/get-type/-/get-type-30.1.0.tgz#4fcb4dc2ebcf0811be1c04fd1cb79c2dba431cbc" - integrity sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA== - -"@jest/globals@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-30.3.0.tgz#40f4c90e5602629ecda1ca773a8fb21575bb64ea" - integrity sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA== - dependencies: - "@jest/environment" "30.3.0" - "@jest/expect" "30.3.0" - "@jest/types" "30.3.0" - jest-mock "30.3.0" - -"@jest/pattern@30.0.1": - version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/pattern/-/pattern-30.0.1.tgz#d5304147f49a052900b4b853dedb111d080e199f" - integrity sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA== +"@inquirer/input@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-5.1.0.tgz#0e3dab4866f69c8b38cb4686da19cd56bdea5bd8" + integrity sha512-sVZCz6P6e8tW5g2bSFel1oLpa6jK/u7BexFfrgTqR8syIdnHqy+iopnlSbYBZMsCK52chLjhGNBxt0eRqhsghw== dependencies: - "@types/node" "*" - jest-regex-util "30.0.1" - -"@jest/reporters@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-30.3.0.tgz#0c1065f6c892665e5a051df22b19df4466ed816b" - integrity sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "30.3.0" - "@jest/test-result" "30.3.0" - "@jest/transform" "30.3.0" - "@jest/types" "30.3.0" - "@jridgewell/trace-mapping" "^0.3.25" - "@types/node" "*" - chalk "^4.1.2" - collect-v8-coverage "^1.0.2" - exit-x "^0.2.2" - glob "^10.5.0" - graceful-fs "^4.2.11" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^6.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^5.0.0" - istanbul-reports "^3.1.3" - jest-message-util "30.3.0" - jest-util "30.3.0" - jest-worker "30.3.0" - slash "^3.0.0" - string-length "^4.0.2" - v8-to-istanbul "^9.0.1" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" -"@jest/schemas@30.0.5": - version "30.0.5" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.5.tgz#7bdf69fc5a368a5abdb49fd91036c55225846473" - integrity sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA== +"@inquirer/number@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-4.1.0.tgz#841489e68dcaf76a8fce19b5c0b48b14eced240f" + integrity sha512-VMXB/XejCbaSTf9Xucl7dqjzzsaGsrs6XwSYXPbGZ2QbSuq/Gz8XamhSi9ClRubNXZlGry9xVg1tKkJdTDgCtQ== dependencies: - "@sinclair/typebox" "^0.34.0" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" -"@jest/snapshot-utils@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/snapshot-utils/-/snapshot-utils-30.3.0.tgz#ca003c91a3e1e4e4956dee716a2aaf04b6707f31" - integrity sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g== +"@inquirer/password@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-5.1.0.tgz#af0289bad1f7718db4e7f6f5eb398c2985a8e256" + integrity sha512-5tqRuKCDIUxdPxTI/CuLnh914kz+WMPmURHKnZgui9gk43ebudEsdu4EwSn1CPSi5R+17YpBG+ba/YqTnRAcJA== + dependencies: + "@inquirer/ansi" "^2.0.6" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" + +"@inquirer/prompts@^8.3.2": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-8.5.0.tgz#159c0f024b088af33f20c81de6abad3fe21cd115" + integrity sha512-pLjXOnY4y3R1mgyHP3pXD/8eXejp+L/dde/0N2NLKgKfMstqhNZrpvs7Wkzbl9FYFQh10LRQ7QZwq+cz9rrhyw== + dependencies: + "@inquirer/checkbox" "^5.2.0" + "@inquirer/confirm" "^6.1.0" + "@inquirer/editor" "^5.2.0" + "@inquirer/expand" "^5.1.0" + "@inquirer/input" "^5.1.0" + "@inquirer/number" "^4.1.0" + "@inquirer/password" "^5.1.0" + "@inquirer/rawlist" "^5.3.0" + "@inquirer/search" "^4.2.0" + "@inquirer/select" "^5.2.0" + +"@inquirer/rawlist@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-5.3.0.tgz#6700a3a8c06b2ea3c9e7185d7c672cd47ced9fa6" + integrity sha512-p+vAeTAD+cGXjGleP1F5LXrX2ISxNDZm+lqeBpnJausNLSZskZZkcggwhomqP8Igx9oIjnoeOrw98xvdFvdm2w== dependencies: - "@jest/types" "30.3.0" - chalk "^4.1.2" - graceful-fs "^4.2.11" - natural-compare "^1.4.0" - -"@jest/source-map@30.0.1": - version "30.0.1" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-30.0.1.tgz#305ebec50468f13e658b3d5c26f85107a5620aaa" - integrity sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg== - dependencies: - "@jridgewell/trace-mapping" "^0.3.25" - callsites "^3.1.0" - graceful-fs "^4.2.11" - -"@jest/test-result@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-30.3.0.tgz#cd8882d683d467fcffb98c09501a65687a76aae9" - integrity sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ== - dependencies: - "@jest/console" "30.3.0" - "@jest/types" "30.3.0" - "@types/istanbul-lib-coverage" "^2.0.6" - collect-v8-coverage "^1.0.2" - -"@jest/test-sequencer@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-30.3.0.tgz#27002b2093f4e0d9e0e1ebb0bc274a242fdadc14" - integrity sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA== - dependencies: - "@jest/test-result" "30.3.0" - graceful-fs "^4.2.11" - jest-haste-map "30.3.0" - slash "^3.0.0" + "@inquirer/core" "^11.2.0" + "@inquirer/type" "^4.0.6" -"@jest/transform@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-30.3.0.tgz#9e6f78ffa205449bf956e269fd707c160f47ce2f" - integrity sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A== +"@inquirer/search@^4.2.0": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-4.2.0.tgz#3a9e71fe7f6167b96450ccdcb873e8b8f6d94877" + integrity sha512-ByURoSGIaSl5O5Q0AmYmVmUsXbMUcBGNoA3FRL7TOyiA22IeFHymJKRkuILbOIlJwqnBk7AnPpseodyFUBzg+g== dependencies: - "@babel/core" "^7.27.4" - "@jest/types" "30.3.0" - "@jridgewell/trace-mapping" "^0.3.25" - babel-plugin-istanbul "^7.0.1" - chalk "^4.1.2" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.11" - jest-haste-map "30.3.0" - jest-regex-util "30.0.1" - jest-util "30.3.0" - pirates "^4.0.7" - slash "^3.0.0" - write-file-atomic "^5.0.1" + "@inquirer/core" "^11.2.0" + "@inquirer/figures" "^2.0.6" + "@inquirer/type" "^4.0.6" -"@jest/types@30.3.0": - version "30.3.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.3.0.tgz#cada800d323cb74945c24ac74615fdb312a6c85f" - integrity sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw== +"@inquirer/select@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-5.2.0.tgz#aa1669e9a24df0f319d376b5424c7ac3b0a50c22" + integrity sha512-6IzkcmEbEXfgVbxZ2d1UyJFbCBoc6dTofulFmrYuomIp88HXiVqRbqbg4/mbfZhvnNo6xYmnYo2AEmDof6fQkg== dependencies: - "@jest/pattern" "30.0.1" - "@jest/schemas" "30.0.5" - "@types/istanbul-lib-coverage" "^2.0.6" - "@types/istanbul-reports" "^3.0.4" - "@types/node" "*" - "@types/yargs" "^17.0.33" - chalk "^4.1.2" + "@inquirer/ansi" "^2.0.6" + "@inquirer/core" "^11.2.0" + "@inquirer/figures" "^2.0.6" + "@inquirer/type" "^4.0.6" + +"@inquirer/type@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-4.0.6.tgz#490b2130eccde68f8a63697e100f4c6fce7a01c7" + integrity sha512-J+9tdxOskuYuGjsvGaq00AamhDgjR7anhEW2dP4QdQpFCMPngCeC/bCYWQ5NsMWZRdsy53is7kAHb/+7cwDk2g== "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" @@ -1608,25 +1263,17 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.5" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.23", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.28": +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28": version "0.3.31" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== @@ -1634,66 +1281,64 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@napi-rs/wasm-runtime@^0.2.11": - version "0.2.12" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz#3e78a8b96e6c33a6c517e1894efbd5385a7cb6f2" - integrity sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ== +"@napi-rs/wasm-runtime@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz#a46bbfedc29751b7170c5d23bc1d8ee8c7e3c1e1" + integrity sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow== dependencies: - "@emnapi/core" "^1.4.3" - "@emnapi/runtime" "^1.4.3" - "@tybys/wasm-util" "^0.10.0" + "@tybys/wasm-util" "^0.10.1" -"@next/env@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.4.tgz#b95793a9f235744c97fa805c99cb033a6ff51ece" - integrity sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw== +"@next/env@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.6.tgz#93a173801cb088463070cc6a113c68e26c1838ea" + integrity sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw== -"@next/eslint-plugin-next@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.4.tgz#69564ea019d91a33c6ddedddc15d7e0385430b3d" - integrity sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA== +"@next/eslint-plugin-next@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz#24f3b2945b2856a5559af5ad630b7cddf1f65329" + integrity sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz#c80ad82f707b58e083fad460e9ba32ab3001ded8" - integrity sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A== - -"@next/swc-darwin-x64@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz#59a79f846130b36687ae348bc07b6b2fe4ed3da8" - integrity sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ== - -"@next/swc-linux-arm64-gnu@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz#886bcb7b164596f185724be96a2a753d5538bed8" - integrity sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ== - -"@next/swc-linux-arm64-musl@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz#9a5d1ba1416c059cec3831dcb9a14b79a7f1477e" - integrity sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg== - -"@next/swc-linux-x64-gnu@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz#2d35270e904909b38ec84d594706c2b8acfa19fa" - integrity sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ== - -"@next/swc-linux-x64-musl@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz#31c750a8cc4b350d2e839c95942361c8b35b9ac9" - integrity sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA== - -"@next/swc-win32-arm64-msvc@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz#eeb6e0d1f58b88b40dcdd8283689fa065b645a25" - integrity sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow== - -"@next/swc-win32-x64-msvc@16.2.4": - version "16.2.4" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz#8252d02b505be5025dee62628859761e0bb11597" - integrity sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw== +"@next/swc-darwin-arm64@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz#0e19055594fbd2d198ce95cf5842bdbe57235d4e" + integrity sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg== + +"@next/swc-darwin-x64@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz#487086280b56017bb547f5975ef1a3d6235a070f" + integrity sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ== + +"@next/swc-linux-arm64-gnu@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz#b28ffbc31ee527a3ad48f29c2fd7b7f75bbdf180" + integrity sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w== + +"@next/swc-linux-arm64-musl@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz#d2eac5600e7083527669fa8cb8758efbbf9c8210" + integrity sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA== + +"@next/swc-linux-x64-gnu@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz#e70dc3469bf9bfef16da2ba240c07ef6cfe8ad55" + integrity sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw== + +"@next/swc-linux-x64-musl@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz#49ecd2f622d0f3741654c59411f604dbbe1a38de" + integrity sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g== + +"@next/swc-win32-arm64-msvc@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz#8a6dfec005364e1cf4fa1724c3d7fa0093660406" + integrity sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg== + +"@next/swc-win32-x64-msvc@16.2.6": + version "16.2.6" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz#4fb656ccfcf60cbf8ffedb40fc1b625987c82742" + integrity sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1721,6 +1366,214 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== +"@oxc-parser/binding-android-arm-eabi@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.130.0.tgz#55917e12ce2bf91f5d8f7af6fa337511b2ca6278" + integrity sha512-h/xYU8/7ADWzVSf5I+YalLpj33LOy9CI/zgbJNIZ5eunRBG+Czqa3lZsvuPHHf3rOt6z1c5+UzoxjbAzAvhwVw== + +"@oxc-parser/binding-android-arm64@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.130.0.tgz#6a88c34fa1641bff439b4def7e4a86070239ac83" + integrity sha512-oFWFJrsGv9siFM4HjMqKNB7IuIZD/SMmZdCXl8xyx7lDplGvPKyewpOo272rSWgMXe2Wx7bWI0Yj+gkHv4qbeg== + +"@oxc-parser/binding-darwin-arm64@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.130.0.tgz#d056a2b3a0100a5610e3014d75fe6d567fc49bd1" + integrity sha512-sGUzupdTplK9jQg7eJZ878HfEgQjJNBc6dAYVWJ9W5aU+J8rLfRJhTVsKThiu1pNwm6Y1qKCcbC6WhNWSXR3Ig== + +"@oxc-parser/binding-darwin-x64@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.130.0.tgz#21340dd67fcdfec7b0be9d4fc6490f84b80cc641" + integrity sha512-PsB4cdCISbC00Uy8eiD8bc2AkGWjZqrSrJnkBFuG2ptrrf6mZ2F5gLFSjOAVMMgZPg8B1D7OydJwLWSfyI2Plg== + +"@oxc-parser/binding-freebsd-x64@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.130.0.tgz#08db5e6dd718b4e7e7c98e5e2ca7bb538fd460a6" + integrity sha512-DgABp3l38hS77JbXCV4qk1+n6DPym5u8zzwuweokezm2tX194nDSJDENbDRECxVsiNbprKATLbk+Z5wlHT0OHw== + +"@oxc-parser/binding-linux-arm-gnueabihf@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.130.0.tgz#3ad94b6f0b763dec37ee0412905dad8e34c1a5a0" + integrity sha512-4Kn3CTEmwFrzhTSC/JuUW16qovmaMdX7jeSKbL8w0pLtLww7To1a2XJi9Z5uD8QWUkfUHhqfV+VD6dVzBnWzoA== + +"@oxc-parser/binding-linux-arm-musleabihf@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.130.0.tgz#f2604ea11032989d779a22c47b8a636f91d2dd44" + integrity sha512-D35KZM3F4rRu1uAFKyBlg3Gaf/ybCjyaPR1hfgvk5ex8NtcTmRgc0JgSighEyNg96TPrFhemFba68SZuxaha8w== + +"@oxc-parser/binding-linux-arm64-gnu@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.130.0.tgz#e3e3da7b98a5b8988893cba16cb81e0ee513ed1d" + integrity sha512-Q9o7oVlo955KHwS8l1u0bCzIx+JsZUA3XToLXC+MsMhye/9LeBQbt84nh120cl2XLy+TEzvugYDiHShg5yaX6Q== + +"@oxc-parser/binding-linux-arm64-musl@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.130.0.tgz#97ce15b046257465757e838ce173c09d540e840a" + integrity sha512-EiJ/gC0ljbcwVpycC8YWw6ggMbtsPX8XMOt0mPx0aqWeMsNR+L9m05Flbvd5T+GlivG+GkSWQL7tM9SRFpM/dw== + +"@oxc-parser/binding-linux-ppc64-gnu@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.130.0.tgz#8c364ef28a2a4f694cc58c4fd951e1710a3703ed" + integrity sha512-b+h/lsLLurp756dMGizNs5uPaJfyEdWrTcV5t8M609jWm1DEHB1StpRXCkyvwtkJx3m+qL5BNQ0dEKan/4yGFA== + +"@oxc-parser/binding-linux-riscv64-gnu@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.130.0.tgz#1085fd4fe2664d6c138463f42a36286e9e70c3f5" + integrity sha512-O19Cil83XAyjEFfo8WhkMwY58ALqZ7ckjGL+25mjMIuF84urWBeANH0FC8B8BsSSygWU3/1aY3ADdDbp+wlBnw== + +"@oxc-parser/binding-linux-riscv64-musl@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.130.0.tgz#de0ea9c5a5dcb1dd2a0db73baab73e2f4e1005cd" + integrity sha512-BgXRVC0+83n3YzCscLQjj6nbyeBIVeZYPTI4fFMAE4WNm2+4RXhWp03IVizL7esIz36kgmT48aebk1iM+cs8sw== + +"@oxc-parser/binding-linux-s390x-gnu@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.130.0.tgz#6e0837ab6b7d1f2462cef3a86953de0288327ac2" + integrity sha512-6tJz0xvnGhsokE7N1WlUSBXibpYmT9xSJFS1Ce41Km/+8gQvdlW8MLhRv8PD0L7ix8vRG0FDDepp3jdOFzdVdw== + +"@oxc-parser/binding-linux-x64-gnu@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.130.0.tgz#738e29a90190a0d97e91cb9ed4a94c0f8121a0e3" + integrity sha512-9aCWj83dp3heTQGmGnZGdIWgxjZrr/7VQ0TGFHH5PKByxJKF2Hcr4qvaSUHhhGEa3MSsDjTL1YDP8RAgdL5/Cg== + +"@oxc-parser/binding-linux-x64-musl@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.130.0.tgz#127c87488a0d23bc0990346c66ffa6e6f8f82fc8" + integrity sha512-afXt87aZBqrUVli8TB/I8H1G50RDWcwirjWtXGXYqJ2ZqWEiErH7V72j3LUSDZaivmtu2OLX0KQ/mbhP81mr7A== + +"@oxc-parser/binding-openharmony-arm64@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.130.0.tgz#9313e4d25badec37d9c349ecab9692af3c1bf556" + integrity sha512-I0NCrZV/YZuCGWgqwNN/GO/iXlLF2z+Wgc7u+Aa9N4P51oYeIa0XT+zVBUne4csO9GqxskXgI4g8JzzWGRpfOw== + +"@oxc-parser/binding-wasm32-wasi@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.130.0.tgz#9fb2d63b814bb7052774c50cd9b8c19047839a14" + integrity sha512-sJgQkGaBX0WJvPUDfwciex6IcTk5O5NLQ1bhEb6f3nBruh1GshKMRSMt2bxZlYrgBzjyBbJzsnO+InPG0bg+fA== + dependencies: + "@emnapi/core" "1.10.0" + "@emnapi/runtime" "1.10.0" + "@napi-rs/wasm-runtime" "^1.1.4" + +"@oxc-parser/binding-win32-arm64-msvc@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.130.0.tgz#939e48db2b47c93e7e3d4601c8eb6ff113ecc1db" + integrity sha512-bjcma99sQrNh6RY4mPO9yTkfxql6TDFoN3HWdK31RCKXwNhcDgJXW/l8PUtzKNiQ+9vpKJfJtQq+LklBuxSOBA== + +"@oxc-parser/binding-win32-ia32-msvc@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.130.0.tgz#ed50388593afc1b97f57d598edf4d51fe3e6d6fa" + integrity sha512-hRYbv6HhpSTzT4xTiIkadLI7upLQxuOdLPR/9nL1fTjwhgutBTPXrwaAPb/jTFVx6/8C7Jb5HcUKhmNwloTbFA== + +"@oxc-parser/binding-win32-x64-msvc@0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.130.0.tgz#e405110c0812d028c69775c35c6fb235f0fdff55" + integrity sha512-RBpA9TsRucJq6HNVNCFF1iKg+QeTkLdZf7hi4xaOGCPvMZWvDHjQgSOEZMUpuW4JNciHbxNhLEYmz5CVygjVGQ== + +"@oxc-project/types@^0.130.0": + version "0.130.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.130.0.tgz#a7825148711dc28805c46cfc21d94b63a4d41e88" + integrity sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q== + +"@oxc-resolver/binding-android-arm-eabi@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz#6040f744ee2350f1eee744ea7b16092653c6e221" + integrity sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg== + +"@oxc-resolver/binding-android-arm64@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz#6bf3c61f1245c84a13602f18d6326f42306e69b0" + integrity sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q== + +"@oxc-resolver/binding-darwin-arm64@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz#b6fdb43ac0b67bf89c09e2865b848759bc1bcbb6" + integrity sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ== + +"@oxc-resolver/binding-darwin-x64@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz#fc065954554eca454fa50ce3e7643f33ebd14977" + integrity sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg== + +"@oxc-resolver/binding-freebsd-x64@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz#79d9c0262d959fb8eff09dfb2079184132607482" + integrity sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ== + +"@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz#67d47c22f31f98d42597b258b9daf6acab441bf3" + integrity sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg== + +"@oxc-resolver/binding-linux-arm-musleabihf@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz#da835d77174c74c49a371a0409c8a6b802bf814e" + integrity sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg== + +"@oxc-resolver/binding-linux-arm64-gnu@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz#06123d54159307a9731e70f898e9b751c9670560" + integrity sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg== + +"@oxc-resolver/binding-linux-arm64-musl@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz#a9df55e94243526e92a1c646f11e844d243aa2a3" + integrity sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw== + +"@oxc-resolver/binding-linux-ppc64-gnu@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz#45559c56a6c8cb9ea8cafffca03c79d2a04097e1" + integrity sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ== + +"@oxc-resolver/binding-linux-riscv64-gnu@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz#8687b6e6cf3592e3cd76a316580d7fcd146e3f1d" + integrity sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw== + +"@oxc-resolver/binding-linux-riscv64-musl@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz#90ee1c47c2e0cf2c7ed6f542544e4c528d82f978" + integrity sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg== + +"@oxc-resolver/binding-linux-s390x-gnu@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz#3fe4d600f79978e766a746405bfa54fe05f27a5c" + integrity sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g== + +"@oxc-resolver/binding-linux-x64-gnu@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz#bda339de35865aa54f28862e55829607c8d4e923" + integrity sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g== + +"@oxc-resolver/binding-linux-x64-musl@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz#c0d5c660faee13a916162f8d83be50015cb72bb5" + integrity sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ== + +"@oxc-resolver/binding-openharmony-arm64@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz#f4fe37c4c5cf0804be3af07a53684780d5f9d582" + integrity sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ== + +"@oxc-resolver/binding-wasm32-wasi@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz#3fbbaf065df34b44d45d8391250aeabf220acf5e" + integrity sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg== + dependencies: + "@emnapi/core" "1.10.0" + "@emnapi/runtime" "1.10.0" + "@napi-rs/wasm-runtime" "^1.1.4" + +"@oxc-resolver/binding-win32-arm64-msvc@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz#00e204aac6dddd1fa909d80aa53be6bee10e6b91" + integrity sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA== + +"@oxc-resolver/binding-win32-x64-msvc@11.20.0": + version "11.20.0" + resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz#56d62047fa2ae7c2695bc901ff16303c8b6429c7" + integrity sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw== + "@parcel/watcher-android-arm64@2.5.6": version "2.5.6" resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" @@ -1810,16 +1663,6 @@ "@parcel/watcher-win32-ia32" "2.5.6" "@parcel/watcher-win32-x64" "2.5.6" -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - -"@pkgr/core@^0.2.9": - version "0.2.9" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.9.tgz#d229a7b7f9dac167a156992ef23c7f023653f53b" - integrity sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA== - "@rc-component/async-validator@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.1.0.tgz#e81f31e676d9cadc71e4310bbf1749c7a5882291" @@ -1827,13 +1670,13 @@ dependencies: "@babel/runtime" "^7.24.4" -"@rc-component/cascader@~1.14.0": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.14.0.tgz#74e1fca58cb14f8f75f6e4bf1debd90534aaea7c" - integrity sha512-Ip9356xwZUR2nbW5PRVGif4B/bDve4pLa/N+PGbvBaTnjbvmN4PFMBGQSmlDlzKP1ovxaYMvwF/dI9lXNLT4iQ== +"@rc-component/cascader@~1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.15.0.tgz#554cba8e01e94a1288547cec96422b2cfc73ff40" + integrity sha512-ZzpMtwFCRo3fbXHuDnncARJMZQjdqA2w7aDuPofNQt+aDx39st1hgfIpEwTBLhe2Hqsvs/zOr8RTtgxTkCPySw== dependencies: "@rc-component/select" "~1.6.0" - "@rc-component/tree" "~1.2.0" + "@rc-component/tree" "~1.3.0" "@rc-component/util" "^1.4.0" clsx "^2.1.1" @@ -1871,10 +1714,10 @@ dependencies: "@rc-component/util" "^1.3.0" -"@rc-component/dialog@~1.8.4": - version "1.8.4" - resolved "https://registry.yarnpkg.com/@rc-component/dialog/-/dialog-1.8.4.tgz#e1f05f311539852f40a5717bc3874ce0af64c6ff" - integrity sha512-Ay6PM7phkTkquplG8fWfUGFZ2GTLx9diTl4f0d8Eqxd7W1u1KjE9AQooFQHOHnhZf0Ya3z51+5EKCWHmt/dNEw== +"@rc-component/dialog@~1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@rc-component/dialog/-/dialog-1.9.0.tgz#3134f8fa8644d9bc228c862668b90de048c7ea1a" + integrity sha512-zbAAogkg4kkKum79sLE6M+vq1jSAW25zdkafrahgcTP9t9S//SD634Znd1A4c8F2Gc12ZKnehGLsVaaOvZzD2A== dependencies: "@rc-component/motion" "^1.1.3" "@rc-component/portal" "^2.1.0" @@ -1901,12 +1744,12 @@ clsx "^2.1.1" "@rc-component/form@~1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.1.tgz#d811fb52df41bf72297938ebfe5cf4a4774588d4" - integrity sha512-8O7TB55Fi2mWIGvSnwZjk8jFqVNYyKDAswglwGShcbndxqzKz4cHwNtNaLjZlAeRge9wcB0LL8IWsC/Bl18raQ== + version "1.8.2" + resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.2.tgz#ba05c5e4b05e58f2c851ee4d792a7aa750f1d854" + integrity sha512-ZidCvOLmM9Xr+3vzk4UAoR7Aj1W/5IHyrzlBB7sNkygpTeRVrohQSo4TN7W/nARTH+nt8zSAPsn4BEl4zLEO2g== dependencies: "@rc-component/async-validator" "^5.1.0" - "@rc-component/util" "^1.6.2" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" "@rc-component/image@~1.9.0": @@ -1928,35 +1771,35 @@ "@rc-component/util" "^1.4.0" clsx "^2.1.1" -"@rc-component/input@~1.1.0", "@rc-component/input@~1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.1.2.tgz#5fdb55741c012a3f8847d7bd24e318ed1d02cc05" - integrity sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg== +"@rc-component/input@~1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.3.1.tgz#230b8b59cdde8521d50f0eede63ddacb61cc0cd3" + integrity sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig== dependencies: - "@rc-component/util" "^1.4.0" + "@rc-component/resize-observer" "^1.1.1" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" -"@rc-component/mentions@~1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@rc-component/mentions/-/mentions-1.6.0.tgz#8ffc86052ac6891b83ecf17beff17c7d5cd20f91" - integrity sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ== +"@rc-component/mentions@~1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@rc-component/mentions/-/mentions-1.9.0.tgz#1e133d607835854430e264b681b7b32c4b49daa7" + integrity sha512-WUwfFKDSOF5S9UPsNsXcLYtzjTxBGsftTXWRbZuxX6BYrsySISTnujfJNgaaQ6qVzaCDJ35QUkZKvsYxip1C5g== dependencies: - "@rc-component/input" "~1.1.0" - "@rc-component/menu" "~1.2.0" - "@rc-component/textarea" "~1.1.0" + "@rc-component/input" "~1.3.0" + "@rc-component/menu" "~1.3.0" "@rc-component/trigger" "^3.0.0" "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/menu@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.2.0.tgz#3ec1afd2030b2f3d7917f54eb9a9d6e5d5d1d84a" - integrity sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg== +"@rc-component/menu@~1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.3.1.tgz#16cae71a01080914e8bac08359fccdda7bfce540" + integrity sha512-pSZl9nBPgKgxN0aaW7NilIBEwWsc+43S+ulGdWAg9afak96dNOGWsGx0DLLBB1VQsAJvo6bQMTDzXoPlEHsBEw== dependencies: "@rc-component/motion" "^1.1.4" "@rc-component/overflow" "^1.0.0" "@rc-component/trigger" "^3.0.0" - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" "@rc-component/mini-decimal@^1.0.1": @@ -1981,13 +1824,13 @@ dependencies: "@rc-component/util" "^1.2.0" -"@rc-component/notification@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@rc-component/notification/-/notification-1.2.0.tgz#dd7c7d50f1d3217bfbc75bc46259e212096855c5" - integrity sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA== +"@rc-component/notification@~2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@rc-component/notification/-/notification-2.0.7.tgz#f2450a482f87e4698285833c4a8efcac169acabb" + integrity sha512-nqZzpf6BPdaj+3ILx7si79LLmqPKyUmQoXa+/9gg0SkH0v1DbD66oJgRMSBEVnd/zUT3D4gwxWIHUKebYf2ZXQ== dependencies: "@rc-component/motion" "^1.1.4" - "@rc-component/util" "^1.2.1" + "@rc-component/util" "^1.11.0" clsx "^2.1.1" "@rc-component/overflow@^1.0.0": @@ -2008,10 +1851,10 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/picker@~1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@rc-component/picker/-/picker-1.9.1.tgz#7ffcb1e4d4655fe2f3d712773e1d3ab9cd5c2a5c" - integrity sha512-9FBYYsvH3HMLICaPDA/1Th5FLaDkFa7qAtangIdlhKb3ZALaR745e9PsOhheJb6asS4QXc12ffiAcjdkZ4C5/g== +"@rc-component/picker@~1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@rc-component/picker/-/picker-1.10.0.tgz#6989f0ae67fca8db00e31f81a8217c8bc370cd34" + integrity sha512-vVOXP2RVWozwpERGUFAehVH1Jz6o/uRrAb9qSZm1LC+iJs8rvEwFo1bzz2jlOYV+uWwu0dIuG86tnDui14Ea0w== dependencies: "@rc-component/overflow" "^1.0.0" "@rc-component/resize-observer" "^1.0.0" @@ -2102,37 +1945,27 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/table@~1.9.1": - version "1.9.1" - resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.9.1.tgz#77a485f871c3a4b1abbb37f8fb9d25fb97efb481" - integrity sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg== +"@rc-component/table@~1.10.0": + version "1.10.2" + resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.10.2.tgz#7b052fd5eb2ccf6996a93eebeb7af7036a262c8b" + integrity sha512-b3PjqB9Gp25p5t/zq+9QrbXbodkptT8/zvLmwgd2FNPUUtaYyDnQqfxeD5a7ao8E8lpinLHsi2u2vdfPhyNvAw== dependencies: "@rc-component/context" "^2.0.1" "@rc-component/resize-observer" "^1.0.0" - "@rc-component/util" "^1.1.0" + "@rc-component/util" "^1.11.1" "@rc-component/virtual-list" "^1.0.1" clsx "^2.1.1" -"@rc-component/tabs@~1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.7.0.tgz#8e74ac1ce42c937a64e07fb91a7f7640560df09a" - integrity sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w== +"@rc-component/tabs@~1.9.0": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.9.1.tgz#52b9cb0392c718fba43e7d558f46f6c910d19acb" + integrity sha512-6mY08Fce6aNOHuGsxbzT+f2ekgL9mg1cGGHkittMlVGymjGg+kGupu5v90sRxcUd/paRU9jclLLXtF/PkK1FUA== dependencies: "@rc-component/dropdown" "~1.0.0" - "@rc-component/menu" "~1.2.0" + "@rc-component/menu" "~1.3.0" "@rc-component/motion" "^1.1.3" "@rc-component/resize-observer" "^1.0.0" - "@rc-component/util" "^1.3.0" - clsx "^2.1.1" - -"@rc-component/textarea@~1.1.0", "@rc-component/textarea@~1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@rc-component/textarea/-/textarea-1.1.2.tgz#2daa5dcb997840040fb8892b0d601ef28d9d1f37" - integrity sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A== - dependencies: - "@rc-component/input" "~1.1.0" - "@rc-component/resize-observer" "^1.0.0" - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" "@rc-component/tooltip@~1.4.0": @@ -2144,34 +1977,34 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/tour@~2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-2.3.0.tgz#3c45ddcab02cd1846f707e8993beecd9dee23a8f" - integrity sha512-K04K9r32kUC+auBSQfr+Fss4SpSIS9JGe56oq/ALAX0p+i2ylYOI1MgR83yBY7v96eO6ZFXcM/igCQmubps0Ow== +"@rc-component/tour@~2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@rc-component/tour/-/tour-2.4.0.tgz#caf89cf8f2f9fb68f1fb0e0c867610015d01f432" + integrity sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg== dependencies: "@rc-component/portal" "^2.2.0" "@rc-component/trigger" "^3.0.0" "@rc-component/util" "^1.7.0" clsx "^2.1.1" -"@rc-component/tree-select@~1.8.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.8.0.tgz#480e84221befbd1fa93ab2034423e2b064e41981" - integrity sha512-iYsPq3nuLYvGqdvFAW+l+I9ASRIOVbMXyA8FGZg2lGym/GwkaWeJGzI4eJ7c9IOEhRj0oyfIN4S92Fl3J05mjQ== +"@rc-component/tree-select@~1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.9.0.tgz#13ea516478b6cb558e04181abb0a01ae6fbdd31f" + integrity sha512-GXcFe15a+trUl1/J3OHWQhsVWFpwFpGFK2cqYWZ1sK22Zs3KZTvMwDpzr75PIo1s6QVioVxpE/pRwRopkeDQ6w== dependencies: "@rc-component/select" "~1.6.0" - "@rc-component/tree" "~1.2.0" + "@rc-component/tree" "~1.3.0" "@rc-component/util" "^1.4.0" clsx "^2.1.1" -"@rc-component/tree@~1.2.0", "@rc-component/tree@~1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.2.4.tgz#cb4f7d818118b3447763e74d3a82fba6454c7317" - integrity sha512-5Gli43+m4R7NhpYYz3Z61I6LOw9yI6CNChxgVtvrO6xB1qML7iE6QMLVMB3+FTjo2yF6uFdAHtqWPECz/zbX5w== +"@rc-component/tree@~1.3.0", "@rc-component/tree@~1.3.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.3.2.tgz#4b0c13564314eff61ca948c18ef923b87c9d7e44" + integrity sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA== dependencies: "@rc-component/motion" "^1.0.0" - "@rc-component/util" "^1.8.1" - "@rc-component/virtual-list" "^1.0.1" + "@rc-component/util" "^1.11.1" + "@rc-component/virtual-list" "^1.2.0" clsx "^2.1.1" "@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.0": @@ -2186,25 +2019,25 @@ clsx "^2.1.1" "@rc-component/upload@~1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/upload/-/upload-1.1.0.tgz#cb634587ffdf8a8a4a26a279fac06989fb47f593" - integrity sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw== + version "1.1.1" + resolved "https://registry.yarnpkg.com/@rc-component/upload/-/upload-1.1.1.tgz#90d16edcdaeb104ffa9111fd0b428061de7c21ac" + integrity sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA== dependencies: - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" -"@rc-component/util@^1.1.0", "@rc-component/util@^1.10.1", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.6.2", "@rc-component/util@^1.7.0", "@rc-component/util@^1.8.1", "@rc-component/util@^1.9.0": - version "1.10.1" - resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.10.1.tgz#213c84c77e8b2001095530d3b0dc47c49c34ffe3" - integrity sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng== +"@rc-component/util@^1.10.1", "@rc-component/util@^1.11.0", "@rc-component/util@^1.11.1", "@rc-component/util@^1.2.0", "@rc-component/util@^1.2.1", "@rc-component/util@^1.3.0", "@rc-component/util@^1.4.0", "@rc-component/util@^1.7.0", "@rc-component/util@^1.9.0": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@rc-component/util/-/util-1.11.1.tgz#07d698908339c55648e4f974afa739345e65b483" + integrity sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg== dependencies: is-mobile "^5.0.0" react-is "^18.2.0" -"@rc-component/virtual-list@^1.0.1": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rc-component/virtual-list/-/virtual-list-1.0.2.tgz#356c465de522ae3834731827d9fb2311ed09b7e9" - integrity sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ== +"@rc-component/virtual-list@^1.0.1", "@rc-component/virtual-list@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@rc-component/virtual-list/-/virtual-list-1.2.0.tgz#3c9ee8cd7d10da334a2a06ad86ca234e2792a381" + integrity sha512-iavRm1Jo4GDbASQwdGa7jFyk93RvSOo9xHyBT4QL1pgFJj/Fdf1G+3RErH7/7BmAMvx2AkF62mjGYxDbXsK9TQ== dependencies: "@babel/runtime" "^7.20.0" "@rc-component/resize-observer" "^1.0.1" @@ -2221,25 +2054,6 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== -"@sinclair/typebox@^0.34.0": - version "0.34.49" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.34.49.tgz#4f1369234f2ecf693866476c3b2e1b54d2a9d68e" - integrity sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A== - -"@sinonjs/commons@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.1.tgz#1029357e44ca901a615585f6d27738dbc89084cd" - integrity sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^15.0.0": - version "15.3.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-15.3.2.tgz#afecc36681e26aab9e0fe809fd9ad578096a3058" - integrity sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw== - dependencies: - "@sinonjs/commons" "^3.0.1" - "@swc/helpers@0.5.15": version "0.5.15" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" @@ -2247,75 +2061,22 @@ dependencies: tslib "^2.8.0" -"@tsconfig/node10@^1.0.7": - version "1.0.12" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.12.tgz#be57ceac1e4692b41be9de6be8c32a106636dba4" - integrity sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" - integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== - -"@tybys/wasm-util@^0.10.0": - version "0.10.1" - resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.1.tgz#ecddd3205cf1e2d5274649ff0eedd2991ed7f414" - integrity sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg== +"@tybys/wasm-util@^0.10.1": + version "0.10.2" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.2.tgz#12b3a1b33db1f9cad4ddff1f604ab7dd00bf464e" + integrity sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg== dependencies: tslib "^2.4.0" -"@types/babel__core@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.27.0.tgz#b5819294c51179957afaec341442f9341e4108a9" - integrity sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*": - version "7.28.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz#07d713d6cce0d265c9849db0cbe62d3f61f36f74" - integrity sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q== - dependencies: - "@babel/types" "^7.28.2" - "@types/esrecurse@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec" integrity sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw== "@types/estree@^1.0.6", "@types/estree@^1.0.8": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" - integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24" + integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== "@types/hoist-non-react-statics@^3.3.7": version "3.3.7" @@ -2324,25 +2085,6 @@ dependencies: hoist-non-react-statics "^3.3.0" -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.1", "@types/istanbul-lib-coverage@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7" - integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w== - -"@types/istanbul-lib-report@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf" - integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54" - integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ== - dependencies: - "@types/istanbul-lib-report" "*" - "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" @@ -2354,16 +2096,16 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "25.6.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.6.0.tgz#4e09bad9b469871f2d0f68140198cbd714f4edca" - integrity sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ== + version "25.9.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.1.tgz#3bda556db500ae4319c08e7fc9ab94f19013ba0b" + integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== dependencies: - undici-types "~7.19.0" + undici-types ">=7.24.0 <7.24.7" "@types/node@^24.10.1": - version "24.12.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.2.tgz#353cb161dbf1785ea25e8829ba7ec574c5c629ac" - integrity sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g== + version "24.12.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.4.tgz#2709745569811dcbdc57c097fafdd387c6330382" + integrity sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA== dependencies: undici-types "~7.16.0" @@ -2372,32 +2114,13 @@ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== -"@types/react-highlight-words@^0.20.0": - version "0.20.1" - resolved "https://registry.yarnpkg.com/@types/react-highlight-words/-/react-highlight-words-0.20.1.tgz#f84a47b36d5e571afc7da47f273aaabf366b71a0" - integrity sha512-EgF6RaoibBurIhxk3X3d5xL0uMqjD7KtShvZ8S+d7/o6xZ+mQ4rct1a2E49vWA9r63OzrHCIyghqvQHASb/ERw== - dependencies: - "@types/react" "*" - -"@types/react-test-renderer@^19.1.0": - version "19.1.0" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-19.1.0.tgz#1d0af8f2e1b5931e245b8b5b234d1502b854dc10" - integrity sha512-XD0WZrHqjNrxA/MaR9O22w/RNidWR9YZmBdRGI7wcnWGrv/3dA8wKCJ8m63Sn+tLJhcjmuhOi629N66W6kgWzQ== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@^19.2.7": - version "19.2.14" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.14.tgz#39604929b5e3957e3a6fa0001dafb17c7af70bad" - integrity sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w== +"@types/react@^19.2.7": + version "19.2.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.15.tgz#9e2c6a4251a290f5c525c3143caa872fa6e01e0d" + integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== dependencies: csstype "^3.2.2" -"@types/stack-utils@^2.0.3": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8" - integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== - "@types/ws@^8.0.0": version "8.18.1" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" @@ -2405,112 +2128,100 @@ dependencies: "@types/node" "*" -"@types/yargs-parser@*": - version "21.0.3" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15" - integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ== - -"@types/yargs@^17.0.33": - version "17.0.35" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.35.tgz#07013e46aa4d7d7d50a49e15604c1c5340d4eb24" - integrity sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg== - dependencies: - "@types/yargs-parser" "*" - -"@typescript-eslint/eslint-plugin@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz#fcbe76b693ce2412410cf4d48aefd617d345f2d9" - integrity sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw== +"@typescript-eslint/eslint-plugin@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz#8fc1e0a950c43270eaf0212dc060f7edaa42f9cf" + integrity sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.59.0" - "@typescript-eslint/type-utils" "8.59.0" - "@typescript-eslint/utils" "8.59.0" - "@typescript-eslint/visitor-keys" "8.59.0" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/type-utils" "8.60.0" + "@typescript-eslint/utils" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.0.tgz#57a138280b3ceaf07904fbd62c433d5cc1ee1573" - integrity sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg== +"@typescript-eslint/parser@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.60.0.tgz#38d611b8e658cb10850d4975e8a175a222fbcd6a" + integrity sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg== dependencies: - "@typescript-eslint/scope-manager" "8.59.0" - "@typescript-eslint/types" "8.59.0" - "@typescript-eslint/typescript-estree" "8.59.0" - "@typescript-eslint/visitor-keys" "8.59.0" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.0.tgz#914bf62069d870faa0389ffd725774a200f511bf" - integrity sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw== +"@typescript-eslint/project-service@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.60.0.tgz#b82ab12e64d005d0c7163d1240c432381f1bde0f" + integrity sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.59.0" - "@typescript-eslint/types" "^8.59.0" + "@typescript-eslint/tsconfig-utils" "^8.60.0" + "@typescript-eslint/types" "^8.60.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz#f71be268bd31da1c160815c689e4dde7c9bc9e8e" - integrity sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg== +"@typescript-eslint/scope-manager@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz#7617a4617c043fe235dcf066f9a40f106cfd2fd5" + integrity sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw== dependencies: - "@typescript-eslint/types" "8.59.0" - "@typescript-eslint/visitor-keys" "8.59.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" -"@typescript-eslint/tsconfig-utils@8.59.0", "@typescript-eslint/tsconfig-utils@^8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz#1276077f5ad77e384446ea28a2474e8f8be1af41" - integrity sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg== +"@typescript-eslint/tsconfig-utils@8.60.0", "@typescript-eslint/tsconfig-utils@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz#3af78c48956227a407dea9626b8db8ca53f130d2" + integrity sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ== -"@typescript-eslint/type-utils@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz#2834ea3b179cedfc9244dcd4f74105a27751a439" - integrity sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg== +"@typescript-eslint/type-utils@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz#6971a61bc4f3a1b2df45dcc14e26a43a88a4cb6a" + integrity sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg== dependencies: - "@typescript-eslint/types" "8.59.0" - "@typescript-eslint/typescript-estree" "8.59.0" - "@typescript-eslint/utils" "8.59.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/utils" "8.60.0" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.59.0", "@typescript-eslint/types@^8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.0.tgz#cfcc643c6e879016479775850d86d84c14492738" - integrity sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A== +"@typescript-eslint/types@8.60.0", "@typescript-eslint/types@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.60.0.tgz#e77ad768e933263b1960b2fe79de75fe1cc6e7db" + integrity sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA== -"@typescript-eslint/typescript-estree@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz#feba58a70ab6ea7ac53a2f3ae900db28ce3454c2" - integrity sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw== +"@typescript-eslint/typescript-estree@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz#c102196a44414481190041c99eea1d854e66001b" + integrity sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g== dependencies: - "@typescript-eslint/project-service" "8.59.0" - "@typescript-eslint/tsconfig-utils" "8.59.0" - "@typescript-eslint/types" "8.59.0" - "@typescript-eslint/visitor-keys" "8.59.0" + "@typescript-eslint/project-service" "8.60.0" + "@typescript-eslint/tsconfig-utils" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.0.tgz#f50df9bd6967881ef64fba62230111153179ead5" - integrity sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g== +"@typescript-eslint/utils@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.60.0.tgz#6110cddaef87606ae4ca6f8bf81bb5949fc8e098" + integrity sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.59.0" - "@typescript-eslint/types" "8.59.0" - "@typescript-eslint/typescript-estree" "8.59.0" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" -"@typescript-eslint/visitor-keys@8.59.0": - version "8.59.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz#2e80de30e7e944ed4bd47d751e37dcb04db03795" - integrity sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q== +"@typescript-eslint/visitor-keys@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz#f2c41eedd3d7b03b808369fb2e3fb40a93783ec2" + integrity sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg== dependencies: - "@typescript-eslint/types" "8.59.0" + "@typescript-eslint/types" "8.60.0" eslint-visitor-keys "^5.0.0" "@umijs/route-utils@^4.0.0": @@ -2523,107 +2234,119 @@ resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52" integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w== -"@ungap/structured-clone@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8" - integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g== - -"@unrs/resolver-binding-android-arm-eabi@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz#9f5b04503088e6a354295e8ea8fe3cb99e43af81" - integrity sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw== - -"@unrs/resolver-binding-android-arm64@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz#7414885431bd7178b989aedc4d25cccb3865bc9f" - integrity sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g== - -"@unrs/resolver-binding-darwin-arm64@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz#b4a8556f42171fb9c9f7bac8235045e82aa0cbdf" - integrity sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g== - -"@unrs/resolver-binding-darwin-x64@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz#fd4d81257b13f4d1a083890a6a17c00de571f0dc" - integrity sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ== - -"@unrs/resolver-binding-freebsd-x64@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz#d2513084d0f37c407757e22f32bd924a78cfd99b" - integrity sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw== - -"@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz#844d2605d057488d77fab09705f2866b86164e0a" - integrity sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw== - -"@unrs/resolver-binding-linux-arm-musleabihf@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz#204892995cefb6bd1d017d52d097193bc61ddad3" - integrity sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw== - -"@unrs/resolver-binding-linux-arm64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz#023eb0c3aac46066a10be7a3f362e7b34f3bdf9d" - integrity sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ== - -"@unrs/resolver-binding-linux-arm64-musl@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz#9e6f9abb06424e3140a60ac996139786f5d99be0" - integrity sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w== - -"@unrs/resolver-binding-linux-ppc64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz#b111417f17c9d1b02efbec8e08398f0c5527bb44" - integrity sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA== - -"@unrs/resolver-binding-linux-riscv64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz#92ffbf02748af3e99873945c9a8a5ead01d508a9" - integrity sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ== - -"@unrs/resolver-binding-linux-riscv64-musl@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz#0bec6f1258fc390e6b305e9ff44256cb207de165" - integrity sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew== - -"@unrs/resolver-binding-linux-s390x-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz#577843a084c5952f5906770633ccfb89dac9bc94" - integrity sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg== - -"@unrs/resolver-binding-linux-x64-gnu@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz#36fb318eebdd690f6da32ac5e0499a76fa881935" - integrity sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w== - -"@unrs/resolver-binding-linux-x64-musl@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz#bfb9af75f783f98f6a22c4244214efe4df1853d6" - integrity sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA== - -"@unrs/resolver-binding-wasm32-wasi@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz#752c359dd875684b27429500d88226d7cc72f71d" - integrity sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ== - dependencies: - "@napi-rs/wasm-runtime" "^0.2.11" - -"@unrs/resolver-binding-win32-arm64-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz#ce5735e600e4c2fbb409cd051b3b7da4a399af35" - integrity sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw== - -"@unrs/resolver-binding-win32-ia32-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz#72fc57bc7c64ec5c3de0d64ee0d1810317bc60a6" - integrity sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ== - -"@unrs/resolver-binding-win32-x64-msvc@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz#538b1e103bf8d9864e7b85cc96fa8d6fb6c40777" - integrity sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g== +"@unrs/resolver-binding-android-arm-eabi@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz#98a9fee62c01f209747a4ab5855f1ced38a6d03a" + integrity sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w== + +"@unrs/resolver-binding-android-arm64@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz#46b7e8a1393f907462324f1576e8883529acf066" + integrity sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ== + +"@unrs/resolver-binding-darwin-arm64@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz#0ea07b00e2583ab004b853d4c02ec5f0745d490c" + integrity sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w== + +"@unrs/resolver-binding-darwin-x64@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz#a2a6901ed58449b91b4438e582f6890cba956049" + integrity sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA== + +"@unrs/resolver-binding-freebsd-x64@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz#ebe6fe7f6706b7378ea4a48a024602e9c2f48f89" + integrity sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg== + +"@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz#e6040fedaa240124419d35b25b69c5fa15ddb499" + integrity sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A== + +"@unrs/resolver-binding-linux-arm-musleabihf@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz#d217a8fb59f659c131539326c140e7b62e3e3c6a" + integrity sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g== + +"@unrs/resolver-binding-linux-arm64-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz#edab13c46a45783a7e01351e113825c04f352e24" + integrity sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg== + +"@unrs/resolver-binding-linux-arm64-musl@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz#e5e195db1130f7d3b6aa2fd67b3c9fe1ea4859a0" + integrity sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA== + +"@unrs/resolver-binding-linux-loong64-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz#f01d22e091bae13016f4636698d9dcbbda775c3e" + integrity sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q== + +"@unrs/resolver-binding-linux-loong64-musl@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz#7d23efcb98adf076bfbcecc27b4212c36aa6697d" + integrity sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew== + +"@unrs/resolver-binding-linux-ppc64-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz#1f35f1eaa322f33cf2d96dac27f0626a93ffe2f6" + integrity sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg== + +"@unrs/resolver-binding-linux-riscv64-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz#674faa696f5ce96f214873946a1e2d6ca96723dd" + integrity sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A== + +"@unrs/resolver-binding-linux-riscv64-musl@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz#37835fdd0b472ecdcffccd4288f19018454b138c" + integrity sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w== + +"@unrs/resolver-binding-linux-s390x-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz#b6edf13db4bb0accdcd1ad482a4eea0301de9224" + integrity sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw== + +"@unrs/resolver-binding-linux-x64-gnu@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz#daddad00bf65a405202284da1eb1db8eb83b218f" + integrity sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ== + +"@unrs/resolver-binding-linux-x64-musl@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz#dfdff1e0c2bad25420b41c76a746011c3983b9bb" + integrity sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A== + +"@unrs/resolver-binding-openharmony-arm64@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz#ce07c4f5e7b42f7bfce45e7629b8659063aefefe" + integrity sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ== + +"@unrs/resolver-binding-wasm32-wasi@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz#82514f0506cfaf65f17fe16095f92d450e487183" + integrity sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A== + dependencies: + "@emnapi/core" "1.10.0" + "@emnapi/runtime" "1.10.0" + "@napi-rs/wasm-runtime" "^1.1.4" + +"@unrs/resolver-binding-win32-arm64-msvc@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz#521427dd59a8f4740ddd1dc7c3bc6af1aa1d260d" + integrity sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g== + +"@unrs/resolver-binding-win32-ia32-msvc@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz#05b63286ff2da37e0ce3083b8390884385efff62" + integrity sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g== + +"@unrs/resolver-binding-win32-x64-msvc@1.12.2": + version "1.12.2" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz#72da0da48d72b1e87831b9c0308931d3f4669027" + integrity sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA== "@whatwg-node/disposablestack@^0.0.6": version "0.0.6" @@ -2696,14 +2419,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.1.1: - version "8.3.5" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.5.tgz#8a6b8ca8fc5b34685af15dabb44118663c296496" - integrity sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw== - dependencies: - acorn "^8.11.0" - -acorn@^8.11.0, acorn@^8.16.0, acorn@^8.4.1: +acorn@^8.16.0: version "8.16.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== @@ -2718,13 +2434,6 @@ ajv@^6.14.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escapes@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - ansi-escapes@^7.0.0: version "7.3.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.3.0.tgz#5395bb74b2150a4a1d6e3c2565f4aeca78d28627" @@ -2732,11 +2441,6 @@ ansi-escapes@^7.0.0: dependencies: environment "^1.0.0" -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - ansi-regex@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" @@ -2749,53 +2453,41 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -ansi-styles@^6.1.0, ansi-styles@^6.2.1, ansi-styles@^6.2.3: +ansi-styles@^6.2.1, ansi-styles@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== antd@^6.3.3: - version "6.3.7" - resolved "https://registry.yarnpkg.com/antd/-/antd-6.3.7.tgz#620354ec04135356cbc5ce0a666871ddc73e4117" - integrity sha512-WTHi4bHVNKpYXLHESzU0Tts7rRNQeL84Bph9dfI3Qw7mHbTulExDcYKNHny5CTXcrBBOpraXbU9miBAwUR5vaw== + version "6.4.3" + resolved "https://registry.yarnpkg.com/antd/-/antd-6.4.3.tgz#80a7aab9c13c35daa0e0e7eea80585ba57cb7203" + integrity sha512-6H2avkxCGfxcF67r3J2mwm9Ck50el1pks/73vfM1wDsPL/tPtj5vHuauMgJFnrqmq7CH3g8aoZ0VBQbt+jpAsw== dependencies: "@ant-design/colors" "^8.0.1" "@ant-design/cssinjs" "^2.1.2" "@ant-design/cssinjs-utils" "^2.1.2" "@ant-design/fast-color" "^3.0.1" - "@ant-design/icons" "^6.1.1" + "@ant-design/icons" "^6.2.3" "@ant-design/react-slick" "~2.0.0" - "@babel/runtime" "^7.28.4" - "@rc-component/cascader" "~1.14.0" + "@babel/runtime" "^7.29.2" + "@rc-component/cascader" "~1.15.0" "@rc-component/checkbox" "~2.0.0" "@rc-component/collapse" "~1.2.0" "@rc-component/color-picker" "~3.1.1" - "@rc-component/dialog" "~1.8.4" + "@rc-component/dialog" "~1.9.0" "@rc-component/drawer" "~1.4.2" "@rc-component/dropdown" "~1.0.2" "@rc-component/form" "~1.8.1" "@rc-component/image" "~1.9.0" - "@rc-component/input" "~1.1.2" + "@rc-component/input" "~1.3.0" "@rc-component/input-number" "~1.6.2" - "@rc-component/mentions" "~1.6.0" - "@rc-component/menu" "~1.2.0" + "@rc-component/mentions" "~1.9.0" + "@rc-component/menu" "~1.3.0" "@rc-component/motion" "^1.3.2" "@rc-component/mutate-observer" "^2.0.1" - "@rc-component/notification" "~1.2.0" + "@rc-component/notification" "~2.0.7" "@rc-component/pagination" "~1.2.0" - "@rc-component/picker" "~1.9.1" + "@rc-component/picker" "~1.10.0" "@rc-component/progress" "~1.0.2" "@rc-component/qrcode" "~1.1.1" "@rc-component/rate" "~1.0.1" @@ -2805,41 +2497,20 @@ antd@^6.3.3: "@rc-component/slider" "~1.0.1" "@rc-component/steps" "~1.2.2" "@rc-component/switch" "~1.0.3" - "@rc-component/table" "~1.9.1" - "@rc-component/tabs" "~1.7.0" - "@rc-component/textarea" "~1.1.2" + "@rc-component/table" "~1.10.0" + "@rc-component/tabs" "~1.9.0" "@rc-component/tooltip" "~1.4.0" - "@rc-component/tour" "~2.3.0" - "@rc-component/tree" "~1.2.4" - "@rc-component/tree-select" "~1.8.0" + "@rc-component/tour" "~2.4.0" + "@rc-component/tree" "~1.3.1" + "@rc-component/tree-select" "~1.9.0" "@rc-component/trigger" "^3.9.0" "@rc-component/upload" "~1.1.0" - "@rc-component/util" "^1.10.1" + "@rc-component/util" "^1.11.0" clsx "^2.1.1" dayjs "^1.11.11" scroll-into-view-if-needed "^3.1.0" throttle-debounce "^5.0.2" -anymatch@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -2966,6 +2637,11 @@ async-function@^1.0.0: resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== +auto-bind@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-5.0.1.tgz#50d8e63ea5a1dddcb5e5e36451c1a8266ffbb2ae" + integrity sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg== + auto-bind@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-4.0.0.tgz#e3589fc6c2da8f7ca43ba9f84fa52a744fc997fb" @@ -2979,75 +2655,15 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: - version "4.11.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.3.tgz#d23cf404edaa5f97bdfd9afed6eea8405e5326e7" - integrity sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg== + version "4.11.4" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.4.tgz#5b535e381ff1e61ffdd615e5483d16186d3b46a5" + integrity sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA== axobject-query@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-4.1.0.tgz#28768c76d0e3cff21bc62a9e2d0b6ac30042a1ee" integrity sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ== -babel-jest@30.3.0, babel-jest@^30.2.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.3.0.tgz#3ff5553fa3bcbb8738d2d7335a4dbdc3bd1a0eb5" - integrity sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ== - dependencies: - "@jest/transform" "30.3.0" - "@types/babel__core" "^7.20.5" - babel-plugin-istanbul "^7.0.1" - babel-preset-jest "30.3.0" - chalk "^4.1.2" - graceful-fs "^4.2.11" - slash "^3.0.0" - -babel-plugin-istanbul@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz#d8b518c8ea199364cf84ccc82de89740236daf92" - integrity sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.3" - istanbul-lib-instrument "^6.0.2" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.3.0.tgz#235ad714a45c18b12566becf439e1c604e277015" - integrity sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg== - dependencies: - "@types/babel__core" "^7.20.5" - -babel-preset-current-node-syntax@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz#20730d6cdc7dda5d89401cab10ac6a32067acde6" - integrity sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-import-attributes" "^7.24.7" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - -babel-preset-jest@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-30.3.0.tgz#21cf3d19a6f5e9924426c879ee0b7f092636d043" - integrity sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ== - dependencies: - babel-plugin-jest-hoist "30.3.0" - babel-preset-current-node-syntax "^1.2.0" - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -3059,34 +2675,22 @@ balanced-match@^4.0.2: integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== baseline-browser-mapping@^2.10.12, baseline-browser-mapping@^2.9.19: - version "2.10.23" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz#3a1a55d1a691a8c8d74688af7f1fd17eac23c184" - integrity sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g== - -baseline-browser-mapping@^2.10.30: - version "2.10.30" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz#58915c74388b05f3b3504026194ea9fa98f6e6b6" - integrity sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg== + version "2.10.32" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz#b6b553a4285fdd606327a617de36a5351e3aaa64" + integrity sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg== brace-expansion@^1.1.7: - version "1.1.14" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.14.tgz#d9de602370d91347cd9ddad1224d4fd701eb348b" - integrity sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g== + version "1.1.15" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.15.tgz#a6d90d54067236e5f42570a3b7378d594d9b7738" + integrity sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.0.tgz#4f41a41190216ee36067ec381526fe9539c4f0ae" - integrity sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w== - dependencies: - balanced-match "^1.0.0" - brace-expansion@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb" - integrity sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ== + version "5.0.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285" + integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g== dependencies: balanced-match "^4.0.2" @@ -3097,7 +2701,7 @@ braces@^3.0.3: dependencies: fill-range "^7.1.1" -browserslist@^4.24.0: +browserslist@^4.24.0, browserslist@^4.28.2: version "4.28.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.2.tgz#f50b65362ef48974ca9f50b3680566d786b811d2" integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== @@ -3108,18 +2712,6 @@ browserslist@^4.24.0: node-releases "^2.0.36" update-browserslist-db "^1.2.3" -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" @@ -3146,7 +2738,7 @@ call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: call-bind-apply-helpers "^1.0.2" get-intrinsic "^1.3.0" -callsites@^3.0.0, callsites@^3.1.0: +callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== @@ -3159,20 +2751,10 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001782: - version "1.0.30001791" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz#dfb93d85c40ad380c57123e72e10f3c575786b51" - integrity sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ== + version "1.0.30001793" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz#238887ddf5fcfc8c36d872394d0a78a517312a72" + integrity sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA== capital-case@^1.0.4: version "1.0.4" @@ -3183,13 +2765,10 @@ capital-case@^1.0.4: tslib "^2.0.3" upper-case-first "^2.0.2" -chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" +chalk@5.6.2, chalk@^5.6.0: + version "5.6.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.6.2.tgz#b1238b6e23ea337af71c7f8a295db5af0c158aea" + integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== chalk@^2.4.1: version "2.4.2" @@ -3216,6 +2795,16 @@ change-case-all@1.0.15: upper-case "^2.0.2" upper-case-first "^2.0.2" +change-case-all@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/change-case-all/-/change-case-all-2.1.0.tgz#c838988531bba9fa9e4db124f2d3f53a9607acc1" + integrity sha512-v6b0WWWkZUMHVuYk82l+WROgkUm4qEN2w5hKRNWtEOYwWqUGoi8C6xH0l1RLF1EoWqDFK6MFclmN3od6ws3/uw== + dependencies: + change-case "^5.2.0" + sponge-case "^2.0.2" + swap-case "^3.0.2" + title-case "^3.0.3" + change-case@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" @@ -3234,26 +2823,16 @@ change-case@^4.1.2: snake-case "^3.0.4" tslib "^2.0.3" -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +change-case@^5.2.0: + version "5.4.4" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-5.4.4.tgz#0d52b507d8fb8f204343432381d1a6d7bff97a02" + integrity sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w== chardet@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/chardet/-/chardet-2.1.1.tgz#5c75593704a642f71ee53717df234031e65373c8" integrity sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ== -ci-info@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.4.0.tgz#7d54eff9f54b45b62401c26032696eb59c8bd18c" - integrity sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg== - -cjs-module-lexer@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz#b3ca5101843389259ade7d88c77bd06ce55849ca" - integrity sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ== - classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.1, classnames@^2.3.2: version "2.5.1" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b" @@ -3266,7 +2845,7 @@ cli-cursor@^5.0.0: dependencies: restore-cursor "^5.0.0" -cli-truncate@^5.0.0: +cli-truncate@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-5.2.0.tgz#c8e72aaca8339c773d128c36e0a17c6315b694eb" integrity sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw== @@ -3284,30 +2863,20 @@ client-only@0.0.1: resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== +cliui@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-9.0.1.tgz#6f7890f386f6f1f79953adc1f78dec46fcc2d291" + integrity sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w== dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" + string-width "^7.2.0" + strip-ansi "^7.1.0" + wrap-ansi "^9.0.0" clsx@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz#cc1f01eb8d02298cbc9a437c74c70ab4e5210b80" - integrity sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw== - color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -3315,28 +2884,11 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -colorette@^2.0.20: - version "2.0.20" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - common-tags@1.8.2: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -3352,17 +2904,17 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concurrently@^9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-9.2.1.tgz#248ea21b95754947be2dad9c3e4b60f18ca4e44f" - integrity sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng== +concurrently@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-10.0.0.tgz#ceb3782d36a620ae7c05ff8085076c0d96323479" + integrity sha512-DRrk10z3sVPpguNe8od2cGNqZGqbT15rwAnxD4dG3b78mdNNb/gJyr8T834Oj518WcBmTktrt4FhdwZn09ZWSg== dependencies: - chalk "4.1.2" + chalk "5.6.2" rxjs "7.8.2" - shell-quote "1.8.3" - supports-color "8.1.1" + shell-quote "1.8.4" + supports-color "10.2.2" tree-kill "1.2.2" - yargs "17.7.2" + yargs "18.0.0" constant-case@^3.0.4: version "3.0.4" @@ -3403,11 +2955,6 @@ cosmiconfig@^9.0.0: js-yaml "^4.1.0" parse-json "^5.2.0" -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - cross-inspect@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b" @@ -3415,7 +2962,7 @@ cross-inspect@1.0.1: dependencies: tslib "^2.4.0" -cross-spawn@^7.0.3, cross-spawn@^7.0.6: +cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -3477,14 +3024,14 @@ dataloader@^2.2.3: integrity sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA== dayjs@^1.0, dayjs@^1.11.10, dayjs@^1.11.11: - version "1.11.20" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.20.tgz#88d919fd639dc991415da5f4cb6f1b6650811938" - integrity sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ== + version "1.11.21" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.21.tgz#57f87562e62de76f3c704bd2b8d522fc33068eb2" + integrity sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA== -debounce@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debounce/-/debounce-2.2.0.tgz#f895fa2fbdb579a0f0d3dcf5dde19657e50eaad5" - integrity sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw== +debounce@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-3.0.0.tgz#7633adff3bcd92cdfe13370c2f46e87bdb946a1b" + integrity sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA== debug@^3.1.0, debug@^3.2.7: version "3.2.7" @@ -3493,7 +3040,7 @@ debug@^3.1.0, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: +debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -3505,21 +3052,11 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== -dedent@^1.6.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.2.tgz#34e2264ab538301e27cf7b07bf2369c19baa8dd9" - integrity sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA== - deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== -deepmerge@^4.0, deepmerge@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== - define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -3548,21 +3085,16 @@ dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -detect-indent@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" - integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-indent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.2.tgz#16c516bf75d4b2f759f68214554996d467c8d648" + integrity sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A== detect-libc@^2.0.3, detect-libc@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== -detect-newline@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - dezalgo@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" @@ -3571,11 +3103,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" - integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -3607,31 +3134,16 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - electron-to-chromium@^1.5.328: - version "1.5.344" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz#6437cc08a7d9b914a98120e182f37793c9eaffd4" - integrity sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg== - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== + version "1.5.363" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.363.tgz#6e9ee6bbb98d4aa12e96853e89993990d577eec8" + integrity sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA== emoji-regex@^10.3.0: version "10.6.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.6.0.tgz#bf3d6e8f7f8fd22a65d9703475bc0147357a6b0d" integrity sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A== -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" @@ -3747,9 +3259,9 @@ es-iterator-helpers@^1.2.1: math-intrinsics "^1.1.0" es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" - integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b" + integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw== dependencies: es-errors "^1.3.0" @@ -3789,22 +3301,17 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^16.0.8: - version "16.2.4" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.4.tgz#ed9f106bb474736e4f7c53c998321fffdedbcde6" - integrity sha512-A6ekXYFj/YQxBPMl45g3e+U8zJo+X2+ZQwcz34pPKjpc/3S4roBA2Rd9xWB4FKuSxhofo1/95WjzmUY+wHrOhg== + version "16.2.6" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.6.tgz#1a04578efe6fde5cdb6265e98e88c9f61927e2c7" + integrity sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q== dependencies: - "@next/eslint-plugin-next" "16.2.4" + "@next/eslint-plugin-next" "16.2.6" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.32.0" @@ -3945,14 +3452,14 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== eslint@^10.1.0: - version "10.2.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.2.1.tgz#224b2a6caeb34473eddcf918762363e2e063222a" - integrity sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q== + version "10.4.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.4.0.tgz#d86b6c405de0f19f3318c47139b8cb6771b3f592" + integrity sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" "@eslint/config-array" "^0.23.5" - "@eslint/config-helpers" "^0.5.5" + "@eslint/config-helpers" "^0.6.0" "@eslint/core" "^1.2.1" "@eslint/plugin-kit" "^0.7.1" "@humanfs/node" "^0.16.6" @@ -3989,11 +3496,6 @@ espree@^11.2.0: acorn-jsx "^5.3.2" eslint-visitor-keys "^5.0.1" -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esquery@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.7.0.tgz#08d048f261f0ddedb5bae95f46809463d9c9496d" @@ -4018,42 +3520,10 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -eventemitter3@^5.0.1: +eventemitter3@^5.0.4: version "5.0.4" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" - integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== - -execa@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - 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" - -exit-x@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/exit-x/-/exit-x-0.2.2.tgz#1f9052de3b8d99a696b10dad5bced9bdd5c3aa64" - integrity sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ== - -expect@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-30.3.0.tgz#1b82111517d1ab030f3db0cf1b4061c8aa644f61" - integrity sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q== - dependencies: - "@jest/expect-utils" "30.3.0" - "@jest/get-type" "30.1.0" - jest-matcher-utils "30.3.0" - jest-message-util "30.3.0" - jest-mock "30.3.0" - jest-util "30.3.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.4.tgz#a86d66170433712dde814707ac52b5271ceb1feb" + integrity sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw== fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" @@ -4082,7 +3552,7 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.8" -fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -4092,6 +3562,25 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-string-truncated-width@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz#23afe0da67d752ca0727538f1e6967759728ce49" + integrity sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g== + +fast-string-width@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-string-width/-/fast-string-width-3.0.2.tgz#16dbabb491ce5585b5ecb675b65c165d71688eeb" + integrity sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg== + dependencies: + fast-string-truncated-width "^3.0.2" + +fast-wrap-ansi@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz#95e952a0145bce3f59ad56e179f84c48d4072935" + integrity sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q== + dependencies: + fast-string-width "^3.0.2" + fastq@^1.6.0: version "1.20.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" @@ -4099,12 +3588,12 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -fb-watchman@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c" - integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA== +fd-package-json@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fd-package-json/-/fd-package-json-2.0.0.tgz#03f53ce5a0af552c2f4faf703a24e526310a2411" + integrity sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ== dependencies: - bser "2.1.1" + walk-up-path "^4.0.0" fdir@^6.5.0: version "6.5.0" @@ -4133,14 +3622,6 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -4169,13 +3650,12 @@ for-each@^0.3.3, for-each@^0.3.5: dependencies: is-callable "^1.2.7" -foreground-child@^3.1.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" - integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== +formatly@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/formatly/-/formatly-0.3.0.tgz#5bb3b4e692f5a8c74ad8fe26154dd0a74aac6819" + integrity sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w== dependencies: - cross-spawn "^7.0.6" - signal-exit "^4.0.1" + fd-package-json "^2.0.0" formdata-polyfill@^4.0.10: version "4.0.10" @@ -4189,11 +3669,6 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" @@ -4232,9 +3707,9 @@ get-caller-file@^2.0.5: integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-east-asian-width@^1.0.0, get-east-asian-width@^1.3.1, get-east-asian-width@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz#ce7008fe345edcf5497a6f557cfa54bc318a9ce7" - integrity sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA== + version "1.6.0" + resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz#216900f91df11a8b2c198c3e1d93d6c035a776b9" + integrity sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA== get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" @@ -4252,11 +3727,6 @@ get-intrinsic@^1.2.4, get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@ hasown "^2.0.2" math-intrinsics "^1.1.0" -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - get-proto@^1.0.0, get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" @@ -4265,11 +3735,6 @@ get-proto@^1.0.0, get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - get-symbol-description@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" @@ -4279,7 +3744,7 @@ get-symbol-description@^1.1.0: es-errors "^1.3.0" get-intrinsic "^1.2.6" -get-tsconfig@^4.10.0: +get-tsconfig@4.14.0, get-tsconfig@^4.10.0: version "4.14.0" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz#985d85c52a9903864280ccc2448d413fbf1efed8" integrity sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA== @@ -4300,19 +3765,7 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.5.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" - integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -glob@^7.1.1, glob@^7.1.4: +glob@^7.1.1: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -4354,7 +3807,7 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.1.2, graceful-fs@^4.2.11: +graceful-fs@^4.1.2: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -4376,7 +3829,7 @@ graphql-config@^5.1.6: string-env-interpolation "^1.0.1" tslib "^2.4.0" -graphql-tag@^2.0, graphql-tag@^2.11.0, graphql-tag@^2.12.6: +graphql-tag@^2.11.0, graphql-tag@^2.12.6: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg== @@ -4389,9 +3842,9 @@ graphql-ws@^6.0.6: integrity sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw== graphql@^16.12.0: - version "16.13.2" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.13.2.tgz#4d2b73df5796b201f1bc2765f5d7067f689cb55f" - integrity sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig== + version "16.14.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.14.0.tgz#f1128a74b16a34d1245c8436bb07b488d87b83e1" + integrity sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q== has-bigints@^1.0.2: version "1.1.0" @@ -4403,11 +3856,6 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -4434,7 +3882,7 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -hasown@^2.0.2: +hasown@^2.0.2, hasown@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c" integrity sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg== @@ -4461,11 +3909,6 @@ hermes-parser@^0.25.1: dependencies: hermes-estree "0.25.1" -highlight-words-core@^1.2.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/highlight-words-core/-/highlight-words-core-1.2.3.tgz#781f37b2a220bf998114e4ef8c8cb6c7a4802ea8" - integrity sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ== - hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -4478,17 +3921,7 @@ hosted-git-info@^2.1.4: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -iconv-lite@^0.7.0: +iconv-lite@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== @@ -4523,14 +3956,6 @@ import-from@4.0.0: resolved "https://registry.yarnpkg.com/import-from/-/import-from-4.0.0.tgz#2710b8d66817d232e16f4166e319248d3d5492e2" integrity sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ== -import-local@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" - integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4625,12 +4050,12 @@ is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" - integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== +is-core-module@^2.16.1, is-core-module@^2.16.2: + version "2.16.2" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.2.tgz#3e07450a8080ebce3fbf0cac494f4d2ab324e082" + integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== dependencies: - hasown "^2.0.2" + hasown "^2.0.3" is-data-view@^1.0.1, is-data-view@^1.0.2: version "1.0.2" @@ -4661,11 +4086,6 @@ is-finalizationregistry@^1.1.0: dependencies: call-bound "^1.0.3" -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - is-fullwidth-code-point@^5.0.0, is-fullwidth-code-point@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz#046b2a6d4f6b156b2233d3207d4b5a9783999b98" @@ -4673,11 +4093,6 @@ is-fullwidth-code-point@^5.0.0, is-fullwidth-code-point@^5.1.0: dependencies: get-east-asian-width "^1.3.1" -is-generator-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - is-generator-function@^1.0.10: version "1.1.2" resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.1.2.tgz#ae3b61e3d5ea4e4839b90bad22b02335051a17d5" @@ -4760,11 +4175,6 @@ is-shared-array-buffer@^1.0.4: dependencies: call-bound "^1.0.3" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-string@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" @@ -4796,10 +4206,10 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a" + integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ== is-upper-case@^2.0.2: version "2.0.2" @@ -4853,48 +4263,6 @@ isows@^1.0.7: resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.7.tgz#1c06400b7eed216fbba3bcbd68f12490fc342915" integrity sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg== -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" - integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== - -istanbul-lib-instrument@^6.0.0, istanbul-lib-instrument@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" - integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== - dependencies: - "@babel/core" "^7.23.9" - "@babel/parser" "^7.23.9" - "@istanbuljs/schema" "^0.1.3" - istanbul-lib-coverage "^3.2.0" - semver "^7.5.4" - -istanbul-lib-report@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" - integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^4.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^5.0.0: - version "5.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz#acaef948df7747c8eb5fbf1265cb980f6353a441" - integrity sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A== - dependencies: - "@jridgewell/trace-mapping" "^0.3.23" - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - -istanbul-reports@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.2.0.tgz#cb4535162b5784aa623cee21a7252cf2c807ac93" - integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - iterator.prototype@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.5.tgz#12c959a29de32de0aa3bbbb801f4d777066dae39" @@ -4907,393 +4275,21 @@ iterator.prototype@^1.1.5: has-symbols "^1.1.0" set-function-name "^2.0.2" -jackspeak@^3.1.2: - version "3.4.3" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" - integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - javascript-stringify@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== -jest-changed-files@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-30.3.0.tgz#055849df695f9a9fcde0ae44024f815bbc627f3a" - integrity sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA== - dependencies: - execa "^5.1.1" - jest-util "30.3.0" - p-limit "^3.1.0" - -jest-circus@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-30.3.0.tgz#153614c11ab35867f371bd93496ecb9690b92077" - integrity sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA== - dependencies: - "@jest/environment" "30.3.0" - "@jest/expect" "30.3.0" - "@jest/test-result" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - chalk "^4.1.2" - co "^4.6.0" - dedent "^1.6.0" - is-generator-fn "^2.1.0" - jest-each "30.3.0" - jest-matcher-utils "30.3.0" - jest-message-util "30.3.0" - jest-runtime "30.3.0" - jest-snapshot "30.3.0" - jest-util "30.3.0" - p-limit "^3.1.0" - pretty-format "30.3.0" - pure-rand "^7.0.0" - slash "^3.0.0" - stack-utils "^2.0.6" - -jest-cli@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-30.3.0.tgz#5ed75a337f486a1f1c5acbb2de8acddb106ead6c" - integrity sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw== - dependencies: - "@jest/core" "30.3.0" - "@jest/test-result" "30.3.0" - "@jest/types" "30.3.0" - chalk "^4.1.2" - exit-x "^0.2.2" - import-local "^3.2.0" - jest-config "30.3.0" - jest-util "30.3.0" - jest-validate "30.3.0" - yargs "^17.7.2" - -jest-config@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-30.3.0.tgz#b969e0aaaf5964419e62953bb712c16d15972425" - integrity sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w== - dependencies: - "@babel/core" "^7.27.4" - "@jest/get-type" "30.1.0" - "@jest/pattern" "30.0.1" - "@jest/test-sequencer" "30.3.0" - "@jest/types" "30.3.0" - babel-jest "30.3.0" - chalk "^4.1.2" - ci-info "^4.2.0" - deepmerge "^4.3.1" - glob "^10.5.0" - graceful-fs "^4.2.11" - jest-circus "30.3.0" - jest-docblock "30.2.0" - jest-environment-node "30.3.0" - jest-regex-util "30.0.1" - jest-resolve "30.3.0" - jest-runner "30.3.0" - jest-util "30.3.0" - jest-validate "30.3.0" - parse-json "^5.2.0" - pretty-format "30.3.0" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-30.3.0.tgz#e0a4c84ef350ffd790ffd5b0016acabeecf5f759" - integrity sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ== - dependencies: - "@jest/diff-sequences" "30.3.0" - "@jest/get-type" "30.1.0" - chalk "^4.1.2" - pretty-format "30.3.0" - -jest-docblock@30.2.0: - version "30.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-30.2.0.tgz#42cd98d69f887e531c7352309542b1ce4ee10256" - integrity sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA== - dependencies: - detect-newline "^3.1.0" - -jest-each@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-30.3.0.tgz#faa7229bf7a9fa6426dc604057a7d2a173493b1e" - integrity sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA== - dependencies: - "@jest/get-type" "30.1.0" - "@jest/types" "30.3.0" - chalk "^4.1.2" - jest-util "30.3.0" - pretty-format "30.3.0" - -jest-environment-node@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-30.3.0.tgz#aa8a57c5d0c4af0f8b1f7403ba737fec6b3aabbe" - integrity sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ== - dependencies: - "@jest/environment" "30.3.0" - "@jest/fake-timers" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - jest-mock "30.3.0" - jest-util "30.3.0" - jest-validate "30.3.0" - -jest-haste-map@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-30.3.0.tgz#1ea6843e6e45c077d91270666a4fcba958c24cd5" - integrity sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA== - dependencies: - "@jest/types" "30.3.0" - "@types/node" "*" - anymatch "^3.1.3" - fb-watchman "^2.0.2" - graceful-fs "^4.2.11" - jest-regex-util "30.0.1" - jest-util "30.3.0" - jest-worker "30.3.0" - picomatch "^4.0.3" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.3" - -jest-leak-detector@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-30.3.0.tgz#a695a851e353f517a554a2f5c91c2742fc131c98" - integrity sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ== - dependencies: - "@jest/get-type" "30.1.0" - pretty-format "30.3.0" - -jest-matcher-utils@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz#d6c739fec1ecd33809f2d2b1348f6ab01d2f2493" - integrity sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA== - dependencies: - "@jest/get-type" "30.1.0" - chalk "^4.1.2" - jest-diff "30.3.0" - pretty-format "30.3.0" - -jest-message-util@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.3.0.tgz#4d723544d36890ba862ac3961db52db5b0d1ba39" - integrity sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw== - dependencies: - "@babel/code-frame" "^7.27.1" - "@jest/types" "30.3.0" - "@types/stack-utils" "^2.0.3" - chalk "^4.1.2" - graceful-fs "^4.2.11" - picomatch "^4.0.3" - pretty-format "30.3.0" - slash "^3.0.0" - stack-utils "^2.0.6" - -jest-mock@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.3.0.tgz#e0fa4184a596a6c4fdec53d4f412158418923747" - integrity sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog== - dependencies: - "@jest/types" "30.3.0" - "@types/node" "*" - jest-util "30.3.0" - -jest-pnp-resolver@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e" - integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w== - -jest-regex-util@30.0.1: - version "30.0.1" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-30.0.1.tgz#f17c1de3958b67dfe485354f5a10093298f2a49b" - integrity sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA== - -jest-resolve-dependencies@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-30.3.0.tgz#4d638c9f0d93a62a6ed25dec874bfd7e756c8ce5" - integrity sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw== - dependencies: - jest-regex-util "30.0.1" - jest-snapshot "30.3.0" - -jest-resolve@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-30.3.0.tgz#b7bee9927279805b1b50715d2170a545553b87ff" - integrity sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g== - dependencies: - chalk "^4.1.2" - graceful-fs "^4.2.11" - jest-haste-map "30.3.0" - jest-pnp-resolver "^1.2.3" - jest-util "30.3.0" - jest-validate "30.3.0" - slash "^3.0.0" - unrs-resolver "^1.7.11" - -jest-runner@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-30.3.0.tgz#fa970fc4e45d418ad7e7d581b24cac7af5944cb7" - integrity sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw== - dependencies: - "@jest/console" "30.3.0" - "@jest/environment" "30.3.0" - "@jest/test-result" "30.3.0" - "@jest/transform" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - chalk "^4.1.2" - emittery "^0.13.1" - exit-x "^0.2.2" - graceful-fs "^4.2.11" - jest-docblock "30.2.0" - jest-environment-node "30.3.0" - jest-haste-map "30.3.0" - jest-leak-detector "30.3.0" - jest-message-util "30.3.0" - jest-resolve "30.3.0" - jest-runtime "30.3.0" - jest-util "30.3.0" - jest-watcher "30.3.0" - jest-worker "30.3.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-30.3.0.tgz#1a9bec7a9b68db12dfe4136bbe41ab883ea2c996" - integrity sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng== - dependencies: - "@jest/environment" "30.3.0" - "@jest/fake-timers" "30.3.0" - "@jest/globals" "30.3.0" - "@jest/source-map" "30.0.1" - "@jest/test-result" "30.3.0" - "@jest/transform" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - chalk "^4.1.2" - cjs-module-lexer "^2.1.0" - collect-v8-coverage "^1.0.2" - glob "^10.5.0" - graceful-fs "^4.2.11" - jest-haste-map "30.3.0" - jest-message-util "30.3.0" - jest-mock "30.3.0" - jest-regex-util "30.0.1" - jest-resolve "30.3.0" - jest-snapshot "30.3.0" - jest-util "30.3.0" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-30.3.0.tgz#6e7ea75069dda86e36311a0f73189e830d4f51ad" - integrity sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ== - dependencies: - "@babel/core" "^7.27.4" - "@babel/generator" "^7.27.5" - "@babel/plugin-syntax-jsx" "^7.27.1" - "@babel/plugin-syntax-typescript" "^7.27.1" - "@babel/types" "^7.27.3" - "@jest/expect-utils" "30.3.0" - "@jest/get-type" "30.1.0" - "@jest/snapshot-utils" "30.3.0" - "@jest/transform" "30.3.0" - "@jest/types" "30.3.0" - babel-preset-current-node-syntax "^1.2.0" - chalk "^4.1.2" - expect "30.3.0" - graceful-fs "^4.2.11" - jest-diff "30.3.0" - jest-matcher-utils "30.3.0" - jest-message-util "30.3.0" - jest-util "30.3.0" - pretty-format "30.3.0" - semver "^7.7.2" - synckit "^0.11.8" - -jest-util@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.3.0.tgz#95a4fbacf2dac20e768e2f1744b70519f2ba7980" - integrity sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg== - dependencies: - "@jest/types" "30.3.0" - "@types/node" "*" - chalk "^4.1.2" - ci-info "^4.2.0" - graceful-fs "^4.2.11" - picomatch "^4.0.3" - -jest-validate@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-30.3.0.tgz#215e11b8fcc5e2ca4b99ea5d730a5b4c969e4355" - integrity sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q== - dependencies: - "@jest/get-type" "30.1.0" - "@jest/types" "30.3.0" - camelcase "^6.3.0" - chalk "^4.1.2" - leven "^3.1.0" - pretty-format "30.3.0" - -jest-watcher@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-30.3.0.tgz#3afa1af355b9fe80f0261eb8a23981a315858596" - integrity sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w== - dependencies: - "@jest/test-result" "30.3.0" - "@jest/types" "30.3.0" - "@types/node" "*" - ansi-escapes "^4.3.2" - chalk "^4.1.2" - emittery "^0.13.1" - jest-util "30.3.0" - string-length "^4.0.2" - -jest-worker@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-30.3.0.tgz#ae4dc1f1d93d0cba1415624fcedaec40ea764f14" - integrity sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ== - dependencies: - "@types/node" "*" - "@ungap/structured-clone" "^1.3.0" - jest-util "30.3.0" - merge-stream "^2.0.0" - supports-color "^8.1.1" - -jest@^30.2.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-30.3.0.tgz#6460b889dd805e9677400505f16f1d9b14c285a3" - integrity sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg== - dependencies: - "@jest/core" "30.3.0" - "@jest/types" "30.3.0" - import-local "^3.2.0" - jest-cli "30.3.0" - -jiti@^2.0.0, jiti@^2.3.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.6.1.tgz#178ef2fc9a1a594248c20627cd820187a4d78d92" - integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ== +jiti@^2.0.0, jiti@^2.3.0, jiti@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.7.0.tgz#974228f2f4ca2bc21885a1797b45fea68e950c64" + integrity sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" - integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - js-yaml@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" @@ -5370,6 +4366,26 @@ keyv@^4.5.4: dependencies: json-buffer "3.0.1" +knip@^6.14.2: + version "6.14.2" + resolved "https://registry.yarnpkg.com/knip/-/knip-6.14.2.tgz#264f725dd2bf2104e70561b0fee7432d2c77fd76" + integrity sha512-Vg3JhIINjZew1I7qAFI4UHemW1mc4azP/BxJvsq9eGDfxpGO7oVCuD/bsWkog9TO/ZwwJeAeOMFZ1kd9jnY9+Q== + dependencies: + fdir "^6.5.0" + formatly "^0.3.0" + get-tsconfig "4.14.0" + jiti "^2.7.0" + minimist "^1.2.8" + oxc-parser "^0.130.0" + oxc-resolver "^11.19.1" + picomatch "^4.0.4" + smol-toml "^1.6.1" + strip-json-comments "5.0.3" + tinyglobby "^0.2.16" + unbash "^3.0.0" + yaml "^2.9.0" + zod "^4.1.11" + language-subtag-registry@^0.3.20: version "0.3.23" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz#23529e04d9e3b74679d70142df3fd2eb6ec572e7" @@ -5382,11 +4398,6 @@ language-tags@^1.0.9: dependencies: language-subtag-registry "^0.3.20" -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -5416,24 +4427,16 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -listr2@^9.0.0: - version "9.0.5" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-9.0.5.tgz#92df7c4416a6da630eb9ef46da469b70de97b316" - integrity sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g== +listr2@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-10.2.1.tgz#fb44e1e9e5f8b15ab817296d45149d295c47bee9" + integrity sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q== dependencies: - cli-truncate "^5.0.0" - colorette "^2.0.20" - eventemitter3 "^5.0.1" + cli-truncate "^5.2.0" + eventemitter3 "^5.0.4" log-update "^6.1.0" rfdc "^1.4.1" - wrap-ansi "^9.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" + wrap-ansi "^10.0.0" locate-path@^6.0.0: version "6.0.0" @@ -5457,13 +4460,13 @@ lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== -log-symbols@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== +log-symbols@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-7.0.1.tgz#f52e68037d96f589fc572ff2193dc424d48c195b" + integrity sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg== dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" + is-unicode-supported "^2.0.0" + yoctocolors "^2.1.1" log-update@^6.1.0: version "6.1.0" @@ -5497,11 +4500,6 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^10.2.0: - version "10.4.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" - integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== - lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -5509,25 +4507,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -make-dir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" - integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== - dependencies: - semver "^7.5.3" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - map-cache@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" @@ -5538,16 +4517,6 @@ math-intrinsics@^1.1.0: resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== -memoize-one@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-4.1.0.tgz#a2387c58c03fff27ca390c31b764a79addf3f906" - integrity sha512-2GApq0yI/b22J2j9rhbrAlsHb0Qcz+7yWxeLG8h+95sl1XPUgeLimQSOdur4Vw7cUhrBHwaUZxWFZueojqNRzA== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -5566,11 +4535,6 @@ micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - mimic-function@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/mimic-function/-/mimic-function-5.0.1.tgz#acbe2b3349f99b9deaca7fb70e48b83e94e67076" @@ -5583,30 +4547,18 @@ minimatch@^10.0.0, minimatch@^10.2.2, minimatch@^10.2.4: dependencies: brace-expansion "^5.0.5" -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.4: - version "9.0.9" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" - integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== - dependencies: - brace-expansion "^2.0.2" - -minimist@^1.2.0, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" - integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== - mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -5619,17 +4571,17 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-2.0.0.tgz#a5446fc0c512b71c83c44d908d5c7b7b4c493b2b" - integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== +mute-stream@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-4.0.0.tgz#3b04c237748aa7a2c93d8b5093cb001eaca1fbeb" + integrity sha512-gSrprq0fJ3EiOErzjdIZrjysVVmJ4uu1QWfCDss5LypA5OXvrMje5Ym5z6V6RLyJ2eF87lasX7t6a0AnFvZblg== nanoid@^3.3.6: - version "3.3.11" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" - integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + version "3.3.12" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.12.tgz#ab3d912e217a6d0a514f00a72a16543a28982c05" + integrity sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ== -napi-postinstall@^0.3.0: +napi-postinstall@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" integrity sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ== @@ -5640,25 +4592,25 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^16.0.8: - version "16.2.4" - resolved "https://registry.yarnpkg.com/next/-/next-16.2.4.tgz#b1f708a283052e8ccb86ef16001d69e558e6ef5f" - integrity sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q== + version "16.2.6" + resolved "https://registry.yarnpkg.com/next/-/next-16.2.6.tgz#4564833d2865efc598b7c63541b5771792d3d811" + integrity sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw== dependencies: - "@next/env" "16.2.4" + "@next/env" "16.2.6" "@swc/helpers" "0.5.15" baseline-browser-mapping "^2.9.19" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.2.4" - "@next/swc-darwin-x64" "16.2.4" - "@next/swc-linux-arm64-gnu" "16.2.4" - "@next/swc-linux-arm64-musl" "16.2.4" - "@next/swc-linux-x64-gnu" "16.2.4" - "@next/swc-linux-x64-musl" "16.2.4" - "@next/swc-win32-arm64-msvc" "16.2.4" - "@next/swc-win32-x64-msvc" "16.2.4" + "@next/swc-darwin-arm64" "16.2.6" + "@next/swc-darwin-x64" "16.2.6" + "@next/swc-linux-arm64-gnu" "16.2.6" + "@next/swc-linux-arm64-musl" "16.2.6" + "@next/swc-linux-x64-gnu" "16.2.6" + "@next/swc-linux-x64-musl" "16.2.6" + "@next/swc-win32-arm64-msvc" "16.2.6" + "@next/swc-win32-x64-msvc" "16.2.6" sharp "^0.34.5" no-case@^3.0.4: @@ -5698,15 +4650,10 @@ node-fetch@^3.3.2: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - node-releases@^2.0.36: - version "2.0.38" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.38.tgz#791569b9e4424a044e12c3abfad418ed83ce9947" - integrity sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw== + version "2.0.46" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.46.tgz#d188a129a83f5e03a101aacb58f260f2ee8faaa1" + integrity sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ== nopt@^4.0.1: version "4.0.3" @@ -5733,23 +4680,11 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - npm-normalize-package-bin@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5823,13 +4758,6 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - onetime@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/onetime/-/onetime-7.0.0.tgz#9f16c92d8c9ef5120e3acd9dd9957cceecc1ab60" @@ -5886,27 +4814,66 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -p-limit@3.1.0, p-limit@^3.0.2, p-limit@^3.1.0: +oxc-parser@^0.130.0: + version "0.130.0" + resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.130.0.tgz#b8f03385db908d9fdfff49c8f37b1748b1f41596" + integrity sha512-X0PJ+NmOok8qP3vK9uaW431ngkdM9UPEK7KG466urtIL2+EYTEgbZK2yqe2MWKJKBjRlFweP/pJPx0x9muMEVw== + dependencies: + "@oxc-project/types" "^0.130.0" + optionalDependencies: + "@oxc-parser/binding-android-arm-eabi" "0.130.0" + "@oxc-parser/binding-android-arm64" "0.130.0" + "@oxc-parser/binding-darwin-arm64" "0.130.0" + "@oxc-parser/binding-darwin-x64" "0.130.0" + "@oxc-parser/binding-freebsd-x64" "0.130.0" + "@oxc-parser/binding-linux-arm-gnueabihf" "0.130.0" + "@oxc-parser/binding-linux-arm-musleabihf" "0.130.0" + "@oxc-parser/binding-linux-arm64-gnu" "0.130.0" + "@oxc-parser/binding-linux-arm64-musl" "0.130.0" + "@oxc-parser/binding-linux-ppc64-gnu" "0.130.0" + "@oxc-parser/binding-linux-riscv64-gnu" "0.130.0" + "@oxc-parser/binding-linux-riscv64-musl" "0.130.0" + "@oxc-parser/binding-linux-s390x-gnu" "0.130.0" + "@oxc-parser/binding-linux-x64-gnu" "0.130.0" + "@oxc-parser/binding-linux-x64-musl" "0.130.0" + "@oxc-parser/binding-openharmony-arm64" "0.130.0" + "@oxc-parser/binding-wasm32-wasi" "0.130.0" + "@oxc-parser/binding-win32-arm64-msvc" "0.130.0" + "@oxc-parser/binding-win32-ia32-msvc" "0.130.0" + "@oxc-parser/binding-win32-x64-msvc" "0.130.0" + +oxc-resolver@^11.19.1: + version "11.20.0" + resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.20.0.tgz#2b2a7a76de753b34be1d6031da1604c8cb580b08" + integrity sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g== + optionalDependencies: + "@oxc-resolver/binding-android-arm-eabi" "11.20.0" + "@oxc-resolver/binding-android-arm64" "11.20.0" + "@oxc-resolver/binding-darwin-arm64" "11.20.0" + "@oxc-resolver/binding-darwin-x64" "11.20.0" + "@oxc-resolver/binding-freebsd-x64" "11.20.0" + "@oxc-resolver/binding-linux-arm-gnueabihf" "11.20.0" + "@oxc-resolver/binding-linux-arm-musleabihf" "11.20.0" + "@oxc-resolver/binding-linux-arm64-gnu" "11.20.0" + "@oxc-resolver/binding-linux-arm64-musl" "11.20.0" + "@oxc-resolver/binding-linux-ppc64-gnu" "11.20.0" + "@oxc-resolver/binding-linux-riscv64-gnu" "11.20.0" + "@oxc-resolver/binding-linux-riscv64-musl" "11.20.0" + "@oxc-resolver/binding-linux-s390x-gnu" "11.20.0" + "@oxc-resolver/binding-linux-x64-gnu" "11.20.0" + "@oxc-resolver/binding-linux-x64-musl" "11.20.0" + "@oxc-resolver/binding-openharmony-arm64" "11.20.0" + "@oxc-resolver/binding-wasm32-wasi" "11.20.0" + "@oxc-resolver/binding-win32-arm64-msvc" "11.20.0" + "@oxc-resolver/binding-win32-x64-msvc" "11.20.0" + +p-limit@3.1.0, p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -5914,16 +4881,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json-from-dist@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" - integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== - param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -5984,7 +4941,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -6006,14 +4963,6 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-scurry@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" - integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-to-regexp@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" @@ -6029,7 +4978,7 @@ picocolors@^1.0.0, picocolors@^1.1.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== -picomatch@^2.0.4, picomatch@^2.3.1: +picomatch@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== @@ -6039,18 +4988,6 @@ picomatch@^4.0.3, picomatch@^4.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== -pirates@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" - integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - possible-typed-array-names@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" @@ -6075,15 +5012,6 @@ prettier@^3.3.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0" integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw== -pretty-format@30.3.0: - version "30.3.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.3.0.tgz#e977eed4bcd1b6195faed418af8eac68b9ea1f29" - integrity sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ== - dependencies: - "@jest/schemas" "30.0.5" - ansi-styles "^5.2.0" - react-is "^18.3.1" - prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" @@ -6098,11 +5026,6 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -pure-rand@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-7.0.1.tgz#6f53a5a9e3e4a47445822af96821ca509ed37566" - integrity sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ== - queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -6127,56 +5050,35 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.31.1, rc-util@^5.35.0, rc-util@^5.44. react-is "^18.2.0" react-cookie@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.1.0.tgz#819f32bb4984b3157b44c533cad7883a63ff6e7e" - integrity sha512-Qs+gD3gpQmUXnJUZafhJtNWhhNdi8OYbOAF5YQRAZa/D171ILOIEMfXDz/tmhkE+nOthllmqryHH6I/qmvIYWQ== + version "8.1.2" + resolved "https://registry.yarnpkg.com/react-cookie/-/react-cookie-8.1.2.tgz#957db13f74876af047eb339af407a7eca262b9dd" + integrity sha512-S45Z1y1dHyYfLEI4bFKQICuP+SwJqTPWbdc2ZpE6aQSdjSVJAjUDfwTPq8B7BWieIsgyyEWMb/QOrudtwJMjXA== dependencies: "@types/hoist-non-react-statics" "^3.3.7" hoist-non-react-statics "^3.3.2" universal-cookie "^8.0.0" react-dom@^19.2.1: - version "19.2.5" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.5.tgz#b8768b10837d0b8e9ca5b9e2d58dff3d880ea25e" - integrity sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag== + version "19.2.6" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7" + integrity sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g== dependencies: scheduler "^0.27.0" -react-highlight-words@^0.21.0: - version "0.21.0" - resolved "https://registry.yarnpkg.com/react-highlight-words/-/react-highlight-words-0.21.0.tgz#a109acdf7dc6fac3ed7db82e9cba94e8d65c281c" - integrity sha512-SdWEeU9fIINArEPO1rO5OxPyuhdEKZQhHzZZP1ie6UeXQf+CjycT1kWaB+9bwGcVbR0NowuHK3RqgqNg6bgBDQ== - dependencies: - highlight-words-core "^1.2.0" - memoize-one "^4.0.0" - react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -react-is@^18.2.0, react-is@^18.3.1: +react-is@^18.2.0: version "18.3.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== -react-is@^19.2.5: - version "19.2.5" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.5.tgz#7e7b54143e9313fed787b23fd4295d5a23872ad9" - integrity sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ== - -react-test-renderer@^19.2.0: - version "19.2.5" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-19.2.5.tgz#0fbfb2796d09b0c133563e9468a45d77e13ffe00" - integrity sha512-kwViRpdISMTpcpy5B6TSewfJzRjnajihRaj57ZmOWKD+SPN6k9LUM13O0pfOuW8ir6B6OOiAXwCRqOoVxRNykA== - dependencies: - react-is "^19.2.5" - scheduler "^0.27.0" - react@^19.2.1: - version "19.2.5" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.5.tgz#c888ab8b8ef33e2597fae8bdb2d77edbdb42858b" - integrity sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA== + version "19.2.6" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.6.tgz#3dadb8e12b2a7934c1d5317973e5dce1301f9a4d" + integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q== read-installed@~4.0.3: version "4.0.3" @@ -6253,24 +5155,12 @@ remove-trailing-spaces@^1.0.6: resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.9.tgz#39c270a309ea16fda84253ffbdeb1b5afa0aa271" integrity sha512-xzG7w5IRijvIkHIjDk65URsJJ7k4J95wmcArY5PRcmjldIOl7oTvG8+X2Ag690R7SfwiOcHrWZKVc1Pp5WIOzA== -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@5.0.0, resolve-from@^5.0.0: +resolve-from@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== @@ -6296,12 +5186,12 @@ resolve@^1.10.0: supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.5, resolve@^2.0.0-next.6: - version "2.0.0-next.6" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.6.tgz#b3961812be69ace7b3bc35d5bf259434681294af" - integrity sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA== + version "2.0.0-next.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.7.tgz#ba3b035d4b1ee7c522426eee73cabcb0fd5515dd" + integrity sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ== dependencies: es-errors "^1.3.0" - is-core-module "^2.16.1" + is-core-module "^2.16.2" node-exports-info "^1.6.0" object-keys "^1.1.1" path-parse "^1.0.7" @@ -6399,10 +5289,10 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4, semver@^7.7.1, semver@^7.7.2, semver@^7.7.3: - version "7.7.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" - integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== +semver@^7.7.1, semver@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.1.tgz#bf4970b5e70fda0686363cc18bfe8805d5ed957e" + integrity sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg== sentence-case@^3.0.4: version "3.0.4" @@ -6490,10 +5380,10 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@1.8.3, shell-quote@^1.7.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b" - integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw== +shell-quote@1.8.4, shell-quote@^1.7.3: + version "1.8.4" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.4.tgz#2edd9a4dcefc96649e2e2cb12f637b1f1d92a190" + integrity sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ== side-channel-list@^1.0.0: version "1.0.1" @@ -6535,12 +5425,7 @@ side-channel@^1.1.0: side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" -signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.0.1, signal-exit@^4.1.0: +signal-exit@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== @@ -6571,6 +5456,11 @@ slide@~1.1.3: resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== +smol-toml@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.6.1.tgz#4fceb5f7c4b86c2544024ef686e12ff0983465be" + integrity sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg== + snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -6584,19 +5474,6 @@ source-map-js@^1.0.2: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - spdx-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7" @@ -6653,23 +5530,16 @@ sponge-case@^1.0.1: dependencies: tslib "^2.0.3" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +sponge-case@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/sponge-case/-/sponge-case-2.0.3.tgz#9e004d04332c307e4895b79eeb6c1f3da86eb203" + integrity sha512-i4h9ZGRfxV6Xw3mpZSFOfbXjf0cQcYmssGWutgNIfFZ2VM+YIWfD71N/kjjwK6X/AAHzBr+rciEcn/L34S8TGw== stable-hash@^0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== -stack-utils@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f" - integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== - dependencies: - escape-string-regexp "^2.0.0" - stop-iteration-iterator@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" @@ -6688,42 +5558,7 @@ string-env-interpolation@^1.0.1: resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== -string-length@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string-width@^7.0.0: +string-width@^7.0.0, string-width@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== @@ -6808,21 +5643,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1, strip-ansi@^7.1.0, strip-ansi@^7.1.2: +strip-ansi@^7.1.0, strip-ansi@^7.1.2: version "7.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== @@ -6834,20 +5655,10 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-5.0.3.tgz#b7304249dd402ee67fd518ada993ab3593458bcf" + integrity sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw== styled-jsx@5.1.6: version "5.1.6" @@ -6861,12 +5672,10 @@ stylis@^4.3.4: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.4.0.tgz#c5846c9345f4bfc51bd0cbd7ca35a0744f485a5d" integrity sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA== -supports-color@8.1.1, supports-color@^8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" +supports-color@10.2.2: + version "10.2.2" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-10.2.2.tgz#466c2978cc5cd0052d542a0b576461c2b802ebb4" + integrity sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g== supports-color@^5.3.0: version "5.5.0" @@ -6875,13 +5684,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -6894,6 +5696,11 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" +swap-case@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-3.0.3.tgz#363883b0e8a2837c24d2e0eccb6bdff92e32d711" + integrity sha512-6p4op8wE9CQv7uDFzulI6YXUw4lD9n4oQierdbFThEKVWVQcbQcUjdP27W8XE7V4QnWmnq9jueSHceyyQnqQVA== + swr@^2.0.0: version "2.4.1" resolved "https://registry.yarnpkg.com/swr/-/swr-2.4.1.tgz#c9e48abff6bf4b04846342e2f1f6be108a078cf6" @@ -6911,22 +5718,6 @@ sync-fetch@0.6.0: timeout-signal "^2.0.0" whatwg-mimetype "^4.0.0" -synckit@^0.11.8: - version "0.11.12" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.12.tgz#abe74124264fbc00a48011b0d98bdc1cffb64a7b" - integrity sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ== - dependencies: - "@pkgr/core" "^0.2.9" - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - throttle-debounce@^5.0.0, throttle-debounce@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz#ec5549d84e053f043c9fd0f2a6dd892ff84456b1" @@ -6937,7 +5728,7 @@ timeout-signal@^2.0.0: resolved "https://registry.yarnpkg.com/timeout-signal/-/timeout-signal-2.0.0.tgz#23207ea448d50258bb0defe3beea4a467643abba" integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== -tinyglobby@^0.2.13, tinyglobby@^0.2.15: +tinyglobby@^0.2.13, tinyglobby@^0.2.15, tinyglobby@^0.2.16: version "0.2.16" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== @@ -6952,11 +5743,6 @@ title-case@^3.0.3: dependencies: tslib "^2.0.3" -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -6979,29 +5765,10 @@ ts-api-utils@^2.5.0: resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz#4acd4a155e22734990a5ed1fe9e97f113bcb37c1" integrity sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA== -ts-log@^2.2.3: - version "2.2.7" - resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" - integrity sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg== - -ts-node@^10.9.1: - version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" +ts-log@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-3.0.2.tgz#e37ebc147c63f1ad3de1bee903c26831fd35f9cb" + integrity sha512-esq6hx2lM66sQV1YcFkIYTqrWWabmqBqobKHyn1CswdI5FgfQhkmiKiRWVGBNlIbdjBxEIkNvMIwLKKPgRYZLQ== tsconfig-paths@^3.15.0: version "3.15.0" @@ -7013,7 +5780,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1: +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.3, tslib@^2.8.0, tslib@^2.8.1, tslib@~2.8.0: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -7025,16 +5792,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - typed-array-buffer@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" @@ -7081,20 +5838,25 @@ typed-array-length@^1.0.7: reflect.getprototypeof "^1.0.6" typescript-eslint@^8.46.0: - version "8.59.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.59.0.tgz#d1cc7c63559ce7116aeb66d35ec9dbe0063379fd" - integrity sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw== + version "8.60.0" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.60.0.tgz#6686fecb1f4f367c0bf0075828e93b7ecacbc62b" + integrity sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw== dependencies: - "@typescript-eslint/eslint-plugin" "8.59.0" - "@typescript-eslint/parser" "8.59.0" - "@typescript-eslint/typescript-estree" "8.59.0" - "@typescript-eslint/utils" "8.59.0" + "@typescript-eslint/eslint-plugin" "8.60.0" + "@typescript-eslint/parser" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/utils" "8.60.0" typescript@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== +unbash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unbash/-/unbash-3.0.0.tgz#57efd0217212ee4abcb820372c87b64ad3af687d" + integrity sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA== + unbox-primitive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" @@ -7110,20 +5872,20 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== +"undici-types@>=7.24.0 <7.24.7": + version "7.24.6" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" + integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== + undici-types@~7.16.0: version "7.16.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== -undici-types@~7.19.0: - version "7.19.2" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.19.2.tgz#1b67fc26d0f157a0cba3a58a5b5c1e2276b8ba2a" - integrity sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg== - universal-cookie@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.1.0.tgz#0d7b2dd77b94d26d5e4609ce5a8df17054b15dd6" - integrity sha512-65+kikQAWq7gsJbirwB7dk6e8xeug1hx3++x2dQoymdXcV7fYv0yChOgHCg01ZwP3fE3sYeq6EWCSpFv3HLl9g== + version "8.1.2" + resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-8.1.2.tgz#2faf7fb206575e658d5bfb999021b7f5fed81cf5" + integrity sha512-kcKzTGNsxVytujrYOvQbvh//QyFrA53HrzCGyzh6i9ujCww5gfPrLK0tG+jJD40SIIldiEjBNPPSR8fBMS21GA== dependencies: cookie "^1.1.1" @@ -7134,32 +5896,35 @@ unixify@^1.0.0: dependencies: normalize-path "^2.1.1" -unrs-resolver@^1.6.2, unrs-resolver@^1.7.11: - version "1.11.1" - resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.11.1.tgz#be9cd8686c99ef53ecb96df2a473c64d304048a9" - integrity sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg== +unrs-resolver@^1.6.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.12.2.tgz#a6c6888396abba5adaac4cab6587df866f1d7afd" + integrity sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ== dependencies: - napi-postinstall "^0.3.0" + napi-postinstall "^0.3.4" optionalDependencies: - "@unrs/resolver-binding-android-arm-eabi" "1.11.1" - "@unrs/resolver-binding-android-arm64" "1.11.1" - "@unrs/resolver-binding-darwin-arm64" "1.11.1" - "@unrs/resolver-binding-darwin-x64" "1.11.1" - "@unrs/resolver-binding-freebsd-x64" "1.11.1" - "@unrs/resolver-binding-linux-arm-gnueabihf" "1.11.1" - "@unrs/resolver-binding-linux-arm-musleabihf" "1.11.1" - "@unrs/resolver-binding-linux-arm64-gnu" "1.11.1" - "@unrs/resolver-binding-linux-arm64-musl" "1.11.1" - "@unrs/resolver-binding-linux-ppc64-gnu" "1.11.1" - "@unrs/resolver-binding-linux-riscv64-gnu" "1.11.1" - "@unrs/resolver-binding-linux-riscv64-musl" "1.11.1" - "@unrs/resolver-binding-linux-s390x-gnu" "1.11.1" - "@unrs/resolver-binding-linux-x64-gnu" "1.11.1" - "@unrs/resolver-binding-linux-x64-musl" "1.11.1" - "@unrs/resolver-binding-wasm32-wasi" "1.11.1" - "@unrs/resolver-binding-win32-arm64-msvc" "1.11.1" - "@unrs/resolver-binding-win32-ia32-msvc" "1.11.1" - "@unrs/resolver-binding-win32-x64-msvc" "1.11.1" + "@unrs/resolver-binding-android-arm-eabi" "1.12.2" + "@unrs/resolver-binding-android-arm64" "1.12.2" + "@unrs/resolver-binding-darwin-arm64" "1.12.2" + "@unrs/resolver-binding-darwin-x64" "1.12.2" + "@unrs/resolver-binding-freebsd-x64" "1.12.2" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.12.2" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.12.2" + "@unrs/resolver-binding-linux-arm64-gnu" "1.12.2" + "@unrs/resolver-binding-linux-arm64-musl" "1.12.2" + "@unrs/resolver-binding-linux-loong64-gnu" "1.12.2" + "@unrs/resolver-binding-linux-loong64-musl" "1.12.2" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.12.2" + "@unrs/resolver-binding-linux-riscv64-gnu" "1.12.2" + "@unrs/resolver-binding-linux-riscv64-musl" "1.12.2" + "@unrs/resolver-binding-linux-s390x-gnu" "1.12.2" + "@unrs/resolver-binding-linux-x64-gnu" "1.12.2" + "@unrs/resolver-binding-linux-x64-musl" "1.12.2" + "@unrs/resolver-binding-openharmony-arm64" "1.12.2" + "@unrs/resolver-binding-wasm32-wasi" "1.12.2" + "@unrs/resolver-binding-win32-arm64-msvc" "1.12.2" + "@unrs/resolver-binding-win32-ia32-msvc" "1.12.2" + "@unrs/resolver-binding-win32-x64-msvc" "1.12.2" update-browserslist-db@^1.2.3: version "1.2.3" @@ -7205,20 +5970,6 @@ util-extend@^1.0.1: resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - -v8-to-istanbul@^9.0.1: - version "9.3.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz#b9572abfa62bd556c16d75fdebc1a411d5ff3175" - integrity sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -7227,12 +5978,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" +walk-up-path@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/walk-up-path/-/walk-up-path-4.0.0.tgz#590666dcf8146e2d72318164f1f2ac6ef51d4198" + integrity sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A== warning@^4.0.3: version "4.0.3" @@ -7292,12 +6041,12 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.20" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" - integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== + version "1.1.21" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.21.tgz#ea7aab68168079646af06b4a36a6f7d7b72e1c0a" + integrity sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw== dependencies: available-typed-arrays "^1.0.7" - call-bind "^1.0.8" + call-bind "^1.0.9" call-bound "^1.0.4" for-each "^0.3.5" get-proto "^1.0.1" @@ -7316,41 +6065,14 @@ word-wrap@^1.2.5: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== +wrap-ansi@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-10.0.0.tgz#b83ddcc14dbc5596f1b07e153bf6f863c1acbb57" + integrity sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ== dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" + ansi-styles "^6.2.3" + string-width "^8.2.0" + strip-ansi "^7.1.2" wrap-ansi@^9.0.0: version "9.0.2" @@ -7366,18 +6088,10 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -write-file-atomic@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.1.tgz#68df4717c55c6fa4281a7860b4c2ba0a6d2b11e7" - integrity sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^4.0.1" - ws@^8.18.3, ws@^8.20.0: - version "8.20.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.20.0.tgz#4cd9532358eba60bc863aad1623dfb045a4d4af8" - integrity sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA== + version "8.21.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.0.tgz#012e413fc07429945121b0c153158c4343086951" + integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== y18n@^5.0.5: version "5.0.8" @@ -7397,50 +6111,44 @@ yaml-loader@^0.9.0: javascript-stringify "^2.0.1" yaml "^2.0.0" -yaml@^2.0.0, yaml@^2.3.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.3.tgz#a0d6bd2efb3dd03c59370223701834e60409bd7d" - integrity sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg== +yaml@^2.0.0, yaml@^2.3.1, yaml@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4" + integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA== -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +yargs-parser@^22.0.0: + version "22.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-22.0.0.tgz#87b82094051b0567717346ecd00fd14804b357c8" + integrity sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw== -yargs@17.7.2, yargs@^17.0.0, yargs@^17.7.2: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== +yargs@18.0.0, yargs@^18.0.0: + version "18.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-18.0.0.tgz#6c84259806273a746b09f579087b68a3c2d25bd1" + integrity sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg== dependencies: - cliui "^8.0.1" + cliui "^9.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" + string-width "^7.2.0" y18n "^5.0.5" - yargs-parser "^21.1.1" - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yargs-parser "^22.0.0" yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yoctocolors-cjs@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz#7e4964ea8ec422b7a40ac917d3a344cfd2304baa" - integrity sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw== +yoctocolors@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.2.tgz#d795f54d173494e7d8db93150cec0ed7f678c83a" + integrity sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug== "zod-validation-error@^3.5.0 || ^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/zod-validation-error/-/zod-validation-error-4.0.2.tgz#bc605eba49ce0fcd598c127fee1c236be3f22918" integrity sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ== -"zod@^3.25.0 || ^4.0.0": - version "4.3.6" - resolved "https://registry.yarnpkg.com/zod/-/zod-4.3.6.tgz#89c56e0aa7d2b05107d894412227087885ab112a" - integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg== +"zod@^3.25.0 || ^4.0.0", zod@^4.1.11: + version "4.4.3" + resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" + integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ== From c8c982c2e14e06d65e361cffe4f7acc9b17a21e1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 May 2026 18:47:45 +0200 Subject: [PATCH 805/878] Upgrade Docker images and upgrade PostgreSQL to the latest major version, that is, 18 (and add automatic upgrade service) --- backend/Dockerfile.development | 2 +- backend/Dockerfile.production | 68 ++++++++++++++++----------------- docker-compose.development.yaml | 26 +++++++++++-- docker-compose.production.yaml | 26 +++++++++++-- frontend/Dockerfile.development | 24 ++++++------ frontend/Dockerfile.production | 16 ++++---- 6 files changed, 99 insertions(+), 63 deletions(-) diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index 4452979b..f5e22925 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.21 +# syntax=docker/dockerfile:1.23 # check=error=true;skip=SecretsUsedInArgOrEnv # Available versions are listed on https://hub.docker.com/r/docker/dockerfile diff --git a/backend/Dockerfile.production b/backend/Dockerfile.production index ac5d406a..1c3ac24d 100644 --- a/backend/Dockerfile.production +++ b/backend/Dockerfile.production @@ -1,4 +1,4 @@ -# syntax=docker/dockerfile:1.21 +# syntax=docker/dockerfile:1.23 # check=error=true # Available versions are listed on https://hub.docker.com/r/docker/dockerfile @@ -59,14 +59,14 @@ SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] RUN apt-get update \ && apt-get upgrade --assume-yes \ && apt-get install \ - --assume-yes \ - --no-install-recommends \ - curl \ - make \ + --assume-yes \ + --no-install-recommends \ + curl \ + make \ && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* # Install up-to-date GnuPG from the official GnuPG repository. # For the signing-key fingerprint see https://repos.gnupg.org/deb/gnupg/noble/ @@ -76,38 +76,38 @@ RUN \ version_codename="$(env --ignore-environment bash -c '. /etc/os-release; echo $VERSION_CODENAME')" && \ apt-get update && \ apt-get install \ - --assume-yes \ - --no-install-recommends \ - curl \ - gnupg2 && \ + --assume-yes \ + --no-install-recommends \ + curl \ + gnupg2 && \ curl \ - --silent \ - --show-error \ - "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ - | gpg --dearmor \ - > /usr/share/keyrings/gnupg-keyring.gpg && \ + --silent \ + --show-error \ + "https://repos.gnupg.org/deb/gnupg/${version_codename}/gnupg-signing-key.gpg" \ + | gpg --dearmor \ + > /usr/share/keyrings/gnupg-keyring.gpg && \ fingerprint=$(gpg \ - --batch \ - --with-colons \ - --import-options show-only \ - --import /usr/share/keyrings/gnupg-keyring.gpg \ - | awk -F':' '$1=="fpr"{print $10}') && \ + --batch \ + --with-colons \ + --import-options show-only \ + --import /usr/share/keyrings/gnupg-keyring.gpg \ + | awk -F':' '$1=="fpr"{print $10}') && \ if [ "${fingerprint}" != "${GNUPG_FINGERPRINT}" ]; then \ - echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ - exit 1; \ + echo "Error: Key fingerprint mismatch, got ${fingerprint} and expected ${GNUPG_FINGERPRINT}"; \ + exit 1; \ fi && \ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gnupg-keyring.gpg] https://repos.gnupg.org/deb/gnupg/${version_codename}/ ${version_codename} main" \ - > /etc/apt/sources.list.d/gnupg.list && \ + > /etc/apt/sources.list.d/gnupg.list && \ apt-get update && \ apt-get install \ - --assume-yes \ - --no-install-recommends \ - dirmngr \ - gnupg2 && \ + --assume-yes \ + --no-install-recommends \ + dirmngr \ + gnupg2 && \ rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* WORKDIR /app @@ -116,9 +116,9 @@ COPY --from=build /app/src/bin/Release/net10.0/publish ./ RUN mkdir --parents \ /root/.dotnet/corefx/cryptography/x509stores \ && mkdir --parents \ - /root/.gnupg \ + /root/.gnupg \ && chmod u=rwx,g=,o= \ - /root/.gnupg + /root/.gnupg RUN ln --symbolic ./Database.dll ./App.dll diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index 429c3584..d77f0cc8 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -2,11 +2,29 @@ name: "${NAME:?required}_${ENVIRONMENT:?required}" services: + # Upgrade PostgreSQL major version if necessary + # https://hub.docker.com/r/pgautoupgrade/pgautoupgrade#one-shot-mode + upgrade_postgres: + # Keep major version in sync with the one in the service `database` + image: "pgautoupgrade/pgautoupgrade:18-trixie" + volumes: + - data:/var/lib/postgresql + environment: + PGAUTO_ONESHOT: "yes" + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" + network_mode: "none" + restart: "no" + database: - # Keep version in sync with the one in `Startup.cs` - image: "postgres:13.23-trixie" + # Keep version in sync with the one in in the service `upgrade_postgres` + # and with the one in `Startup.cs` + image: "postgres:18.4-trixie" + depends_on: + upgrade_postgres: + condition: "service_completed_successfully" volumes: - - data:/var/lib/postgresql/data + - data:/var/lib/postgresql - ./.psqlrc:/etc/postgresql-common/psqlrc:ro environment: POSTGRES_USER: "${POSTGRES_USER:?required}" @@ -192,7 +210,7 @@ services: pull_policy: "never" reverse_proxy: - image: "nginx:1.29-trixie-otel" + image: "nginx:1.31-trixie-otel" depends_on: backend: condition: "service_started" diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index cbb0dd72..669919f9 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -2,11 +2,29 @@ name: "${NAME:?required}_${ENVIRONMENT:?required}" services: + # Upgrade PostgreSQL major version if necessary + # https://hub.docker.com/r/pgautoupgrade/pgautoupgrade#one-shot-mode + upgrade_postgres: + # Keep major version in sync with the one in the service `database` + image: "pgautoupgrade/pgautoupgrade:18-trixie" + volumes: + - data:/var/lib/postgresql + environment: + PGAUTO_ONESHOT: "yes" + POSTGRES_USER: "${POSTGRES_USER:?required}" + POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" + network_mode: "none" + restart: "no" + database: - # Keep version in sync with the one in `Startup.cs` - image: "postgres:13.23-trixie" + # Keep version in sync with the one in in the service `upgrade_postgres` + # and with the one in `Startup.cs` + image: "postgres:18.4-trixie" + depends_on: + upgrade_postgres: + condition: "service_completed_successfully" volumes: - - data:/var/lib/postgresql/data + - data:/var/lib/postgresql - ./.psqlrc:/etc/postgresql-common/psqlrc:ro environment: POSTGRES_USER: "${POSTGRES_USER:?required}" @@ -139,7 +157,7 @@ services: pull_policy: "never" reverse_proxy: - image: "nginx:1.29-trixie-otel" + image: "nginx:1.31-trixie-otel" depends_on: backend: condition: "service_started" diff --git a/frontend/Dockerfile.development b/frontend/Dockerfile.development index 27779b0b..0cf87778 100644 --- a/frontend/Dockerfile.development +++ b/frontend/Dockerfile.development @@ -1,11 +1,11 @@ -# syntax=docker/dockerfile:1.21 +# syntax=docker/dockerfile:1.23 # check=error=true # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://mherman.org/blog/dockerizing-a-react-app/ # https://hub.docker.com/_/node -FROM node:24.11-trixie-slim +FROM node:24.16-trixie-slim SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] # TODO directory node_modules is created on host machine when container is run @@ -20,21 +20,21 @@ SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] # https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run RUN apt-get update \ && apt-get install \ - --assume-yes \ - --no-install-recommends \ - make \ - tini \ - wget \ + --assume-yes \ + --no-install-recommends \ + make \ + tini \ + wget \ && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* ENV HOME=/home/node RUN mkdir --parents "${HOME}/app" \ && chown \ - node:node \ - "${HOME}/app" \ + node:node \ + "${HOME}/app" \ && ln --symbolic "${HOME}/app" /app ############# diff --git a/frontend/Dockerfile.production b/frontend/Dockerfile.production index 056def37..79107442 100644 --- a/frontend/Dockerfile.production +++ b/frontend/Dockerfile.production @@ -1,11 +1,11 @@ -# syntax=docker/dockerfile:1.21 +# syntax=docker/dockerfile:1.23 # check=error=true # Available versions are listed on https://hub.docker.com/r/docker/dockerfile # Inspired by https://github.com/vercel/next.js/blob/canary/examples/with-docker-multi-env/docker/production/Dockerfile # linked to in https://nextjs.org/docs/deployment -FROM node:24.11-trixie-slim AS base +FROM node:24.16-trixie-slim AS base FROM base AS dependencies SHELL ["/bin/bash", "-o", "errexit", "-o", "nounset", "-o", "pipefail", "-c"] @@ -29,13 +29,13 @@ RUN addgroup --system --gid 1001 nodejs \ RUN apt-get update \ && apt-get upgrade --assume-yes \ && apt-get install \ - --assume-yes \ - --no-install-recommends \ - wget \ + --assume-yes \ + --no-install-recommends \ + wget \ && rm \ - --recursive \ - --force \ - /var/lib/apt/lists/* + --recursive \ + --force \ + /var/lib/apt/lists/* COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=build --chown=nextjs:nodejs /app/public ./public COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static From f5d47b5c14fa94a0e70e983ad21f78caf024cb68 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 28 May 2026 18:55:02 +0200 Subject: [PATCH 806/878] Align placeholders --- frontend/components/JumpToId.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/components/JumpToId.tsx b/frontend/components/JumpToId.tsx index 5356e671..ea0f2c01 100644 --- a/frontend/components/JumpToId.tsx +++ b/frontend/components/JumpToId.tsx @@ -33,7 +33,7 @@ export default function JumpToId({ query, route, style }: JumpToIdProps) { /> ) : ( setId(e.target.value)} From 077178df7058c7d8fad61a4d97c4acd907f1db06 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 1 Jun 2026 18:26:50 +0200 Subject: [PATCH 807/878] Add pull policy to PostgreSQL upgrade service --- docker-compose.development.yaml | 1 + docker-compose.production.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index d77f0cc8..a8f6666b 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -15,6 +15,7 @@ services: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" network_mode: "none" restart: "no" + pull_policy: "always" database: # Keep version in sync with the one in in the service `upgrade_postgres` diff --git a/docker-compose.production.yaml b/docker-compose.production.yaml index 669919f9..8eaac633 100644 --- a/docker-compose.production.yaml +++ b/docker-compose.production.yaml @@ -15,6 +15,7 @@ services: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" network_mode: "none" restart: "no" + pull_policy: "always" database: # Keep version in sync with the one in in the service `upgrade_postgres` From 342fdbcd3643c50e0d2fdd553be049d6ee8031ca Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 1 Jun 2026 18:27:11 +0200 Subject: [PATCH 808/878] Increase maximum GraphQL field and type cost --- backend/src/Configuration/GraphQlConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index c2d1d9e8..042dd434 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -129,8 +129,8 @@ IWebHostEnvironment environment ) .ModifyCostOptions(_ => { - _.MaxFieldCost = 12000; - _.MaxTypeCost = 12000; + _.MaxFieldCost = 20000; + _.MaxTypeCost = 20000; } ) // Configure From dff74377a2392c0254c197207ea77bda8456470b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Jun 2026 18:35:42 +0200 Subject: [PATCH 809/878] Remove obsolete checks for whether router is ready --- frontend/pages/data/calorimetric/[uuid].tsx | 6 ------ frontend/pages/data/geometric/[uuid].tsx | 6 ------ frontend/pages/data/hygrothermal/[uuid].tsx | 6 ------ frontend/pages/data/life-cycle/[uuid].tsx | 6 ------ frontend/pages/data/optical/[uuid].tsx | 6 ------ frontend/pages/data/photovoltaic/[uuid].tsx | 6 ------ 6 files changed, 36 deletions(-) diff --git a/frontend/pages/data/calorimetric/[uuid].tsx b/frontend/pages/data/calorimetric/[uuid].tsx index d7ca5d6b..91213379 100644 --- a/frontend/pages/data/calorimetric/[uuid].tsx +++ b/frontend/pages/data/calorimetric/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { diff --git a/frontend/pages/data/geometric/[uuid].tsx b/frontend/pages/data/geometric/[uuid].tsx index 67a5f18d..bab2958c 100644 --- a/frontend/pages/data/geometric/[uuid].tsx +++ b/frontend/pages/data/geometric/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { diff --git a/frontend/pages/data/hygrothermal/[uuid].tsx b/frontend/pages/data/hygrothermal/[uuid].tsx index cf1cfdfb..e2499215 100644 --- a/frontend/pages/data/hygrothermal/[uuid].tsx +++ b/frontend/pages/data/hygrothermal/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { diff --git a/frontend/pages/data/life-cycle/[uuid].tsx b/frontend/pages/data/life-cycle/[uuid].tsx index a230ab1d..25b67b15 100644 --- a/frontend/pages/data/life-cycle/[uuid].tsx +++ b/frontend/pages/data/life-cycle/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { diff --git a/frontend/pages/data/optical/[uuid].tsx b/frontend/pages/data/optical/[uuid].tsx index faa808e1..067194f0 100644 --- a/frontend/pages/data/optical/[uuid].tsx +++ b/frontend/pages/data/optical/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { diff --git a/frontend/pages/data/photovoltaic/[uuid].tsx b/frontend/pages/data/photovoltaic/[uuid].tsx index 84a4783e..16f77874 100644 --- a/frontend/pages/data/photovoltaic/[uuid].tsx +++ b/frontend/pages/data/photovoltaic/[uuid].tsx @@ -5,12 +5,6 @@ import { Skeleton } from "antd"; function Page() { const router = useRouter(); - - if (!router.isReady) { - // Otherwise `uuid`, aka, `router.query`, is null on first render, see https://github.com/vercel/next.js/discussions/11484 - return null; - } - const { uuid } = router.query; if (!uuid) { From 2d49371365792252ddc274317cb6f022971bdd43 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 2 Jun 2026 18:49:47 +0200 Subject: [PATCH 810/878] Add error view to display on OpenID Connect client errors --- backend/src/Controllers/ErrorController.cs | 24 ++++++++ backend/src/Startup.cs | 2 +- .../src/ViewModels/Common/ErrorViewModel.cs | 10 ++++ backend/src/Views/Shared/Error.cshtml | 14 +++++ backend/src/Views/Shared/_Layout.cshtml | 57 +++++++++++++++++++ backend/src/Views/_ViewImports.cshtml | 2 + backend/src/Views/_ViewStart.cshtml | 3 + 7 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 backend/src/Controllers/ErrorController.cs create mode 100644 backend/src/ViewModels/Common/ErrorViewModel.cs create mode 100644 backend/src/Views/Shared/Error.cshtml create mode 100644 backend/src/Views/Shared/_Layout.cshtml create mode 100644 backend/src/Views/_ViewImports.cshtml create mode 100644 backend/src/Views/_ViewStart.cshtml diff --git a/backend/src/Controllers/ErrorController.cs b/backend/src/Controllers/ErrorController.cs new file mode 100644 index 00000000..427cd993 --- /dev/null +++ b/backend/src/Controllers/ErrorController.cs @@ -0,0 +1,24 @@ +using Database.ViewModels.Common; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Mvc; + +namespace Database.Controllers; + +public class ErrorController : Controller +{ + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + [Route("~/error")] + public IActionResult Error() + { + var response = HttpContext.GetOpenIddictServerResponse() ?? HttpContext.GetOpenIddictClientResponse(); + if (response is not null) + { + return View(new ErrorViewModel + { + Error = response.Error, + ErrorDescription = response.ErrorDescription + }); + } + return View(new ErrorViewModel()); + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 37859ca7..baad7ffb 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -327,7 +327,7 @@ public void Configure(WebApplication app) // app.UseHsts(); // Done by the reverse proxy, see https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/ } - app.UseStatusCodePages(); // [UseStatusCodePages](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-9.0#usestatuscodepages) + app.UseStatusCodePagesWithReExecute("/error"); // [UseStatusCodePages](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-9.0#usestatuscodepages) // app.UseHttpsRedirection(); // Done by the reverse proxy app.UseSerilogRequestLogging(); app.UseStaticFiles(); diff --git a/backend/src/ViewModels/Common/ErrorViewModel.cs b/backend/src/ViewModels/Common/ErrorViewModel.cs new file mode 100644 index 00000000..0ae9199a --- /dev/null +++ b/backend/src/ViewModels/Common/ErrorViewModel.cs @@ -0,0 +1,10 @@ +using System.ComponentModel.DataAnnotations; + +namespace Database.ViewModels.Common; + +public sealed class ErrorViewModel +{ + [Display(Name = "Error")] public string? Error { get; set; } + + [Display(Name = "Description")] public string? ErrorDescription { get; set; } +} \ No newline at end of file diff --git a/backend/src/Views/Shared/Error.cshtml b/backend/src/Views/Shared/Error.cshtml new file mode 100644 index 00000000..339dc9ef --- /dev/null +++ b/backend/src/Views/Shared/Error.cshtml @@ -0,0 +1,14 @@ +@model ErrorViewModel + +

    Ooooops, something went really bad! :(

    +

    + @if (!string.IsNullOrEmpty(Model.Error)) + { + @Model.Error + } + + @if (!string.IsNullOrEmpty(Model.ErrorDescription)) + { + @Model.ErrorDescription + } +

    diff --git a/backend/src/Views/Shared/_Layout.cshtml b/backend/src/Views/Shared/_Layout.cshtml new file mode 100644 index 00000000..c03aad69 --- /dev/null +++ b/backend/src/Views/Shared/_Layout.cshtml @@ -0,0 +1,57 @@ + + + + + + @ViewData["Title"] + + + +
    + @RenderBody() +
    + + diff --git a/backend/src/Views/_ViewImports.cshtml b/backend/src/Views/_ViewImports.cshtml new file mode 100644 index 00000000..d776b4b1 --- /dev/null +++ b/backend/src/Views/_ViewImports.cshtml @@ -0,0 +1,2 @@ +@using Metabase.ViewModels.Common +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/backend/src/Views/_ViewStart.cshtml b/backend/src/Views/_ViewStart.cshtml new file mode 100644 index 00000000..820a2f6e --- /dev/null +++ b/backend/src/Views/_ViewStart.cshtml @@ -0,0 +1,3 @@ +@{ + Layout = "_Layout"; +} From 0aa458f2ca84d9f0b187cce297d0ee9e6dba5a55 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 3 Jun 2026 21:51:27 +0200 Subject: [PATCH 811/878] Revamp access policies --- .../QueryCurrentUserOrInstitution.cs | 20 +- .../src/Configuration/GraphQlConfiguration.cs | 2 + .../src/Data/AccessPolicies/CountSinceTime.cs | 8 + .../Data/AccessPolicies/DataAccessPolicy.cs | 34 + .../AccessPolicies/InstitutionAccessPolicy.cs | 13 + .../OpenIdConnectApplicationAccessPolicy.cs | 12 + .../AccessPolicies/StakeholderAccessPolicy.cs | 44 + .../UpperAccessLimitPerTimeDuration.cs | 13 + .../AccessPolicies/UpperLimitPerDuration.cs | 8 + .../Data/AccessPolicies/UserAccessPolicy.cs | 13 + backend/src/Data/ApplicationDbContext.cs | 102 +- backend/src/Data/DataAccessRights.cs | 63 - backend/src/Data/DataX.cs | 42 +- backend/src/Data/GetHttpsResource.cs | 1 + backend/src/Data/IData.cs | 25 +- backend/src/Data/InstitutionAccessRights.cs | 82 - backend/src/Enumerations/LogicalCombinator.cs | 20 + .../src/Extensions/HttpContextExtensions.cs | 5 +- .../AccessPolicies/AccessPolicyDataLoaders.cs | 67 + .../AccessPolicies/AccessPolicyQueries.cs | 80 + .../ClearDataAccessPoliciesMutation.cs | 78 + .../ClearInstitutionAccessPoliciesMutation.cs | 71 + ...onnectApplicationAccessPoliciesMutation.cs | 71 + .../ClearUserAccessPoliciesMutation.cs | 71 + .../DeleteDataAccessPolicyMutation.cs | 92 + ...teDataAccessPolicyOfInstitutionMutation.cs | 113 + ...olicyOfOpenIdConnectApplicationMutation.cs | 113 + .../DeleteDataAccessPolicyOfUserMutation.cs | 113 + .../DeleteInstitutionAccessPolicyMutation.cs | 95 + ...dConnectApplicationAccessPolicyMutation.cs | 94 + .../DeleteUserAccessPolicyMutation.cs | 95 + .../InstitutionAccessPolicyFilterType.cs | 17 + .../InstitutionAccessPolicySortType.cs | 17 + ...nnectApplicationAccessPolicyFilterInput.cs | 17 + ...ConnectApplicationAccessPolicySortInput.cs | 17 + ...etDataAccessLimitForInstitutionMutation.cs | 133 + ...imitForOpenIdConnectApplicationMutation.cs | 133 + .../SetDataAccessLimitForUserMutation.cs | 133 + .../SetInstitutionAccessLimitMutation.cs | 95 + ...IdConnectApplicationAccessLimitMutation.cs | 94 + .../SetUserAccessLimitMutation.cs | 95 + .../UserAccessPolicyFilterInput.cs | 17 + .../UserAccessPolicySortType.cs | 17 + .../AddInstitutionAccessRightsMutation.cs | 108 - .../UpdateDataAccessRightsMutation.cs | 182 - .../UpdateInstitutionAccessRightsMutation.cs | 105 - .../AuditableAssociationFilterType.cs | 6 +- .../AuditableAssociationSortType.cs | 6 +- .../CalorimetricDataFilterType.cs | 32 +- .../CalorimetricDataQueries.cs | 24 +- .../CalorimetricDataSortType.cs | 4 +- .../DataApprovals/AddDataApprovalMutation.cs | 4 +- backend/src/GraphQl/DataMutationBase.cs | 4 +- .../GraphQl/DataX/CreateDataMutationBase.cs | 4 +- backend/src/GraphQl/DataX/DataFilterType.cs | 28 +- .../src/GraphQl/DataX/DataFilterTypeBase.cs | 22 +- backend/src/GraphQl/DataX/DataQueries.cs | 25 +- backend/src/GraphQl/DataX/DataQueriesBase.cs | 101 +- backend/src/GraphQl/DataX/DataSortTypeBase.cs | 14 +- backend/src/GraphQl/DataX/DataType.cs | 6 +- .../src/GraphQl/DataX/DeleteDataMutation.cs | 25 +- ...ttpsResourceTreeNonRootVertexFilterType.cs | 14 +- .../src/GraphQl/DataX/PublishDataMutation.cs | 6 +- .../Entities/AuditableEntitySortType.cs | 8 +- .../GeometricDataX/GeometricDataFilterType.cs | 30 +- .../GeometricDataX/GeometricDataQueries.cs | 24 +- .../GeometricDataX/GeometricDataSortType.cs | 4 +- .../CreateGetHttpsResourceMutation.cs | 4 +- .../DeleteGetHttpsResourceMutation.cs | 2 +- .../GetHttpsResourceFilterType.cs | 28 +- .../GetHttpsResourceSortType.cs | 12 +- .../GetHttpsResources/GetHttpsResourceType.cs | 36 +- .../SetGetHttpsResourceParentMutation.cs | 4 +- .../UpdateChildGetHttpsResourceMutation.cs | 4 +- .../UpdateRootGetHttpsResourceMutation.cs | 4 +- .../HygrothermalDataFilterType.cs | 28 +- .../HygrothermalDataQueries.cs | 24 +- .../HygrothermalDataSortType.cs | 4 +- .../LifeCycleDataX/LifeCycleDataFilterType.cs | 28 +- .../LifeCycleDataX/LifeCycleDataQueries.cs | 24 +- .../LifeCycleDataX/LifeCycleDataSortType.cs | 4 +- backend/src/GraphQl/Methods/MethodQueries.cs | 4 +- .../GraphQl/NamedMethodArgumentFilterType.cs | 6 +- .../GraphQl/NamedMethodArgumentSortType.cs | 6 +- .../OpticalDataX/OpticalDataFilterType.cs | 48 +- .../OpticalDataX/OpticalDataQueries.cs | 24 +- .../OpticalDataX/OpticalDataSortType.cs | 4 +- .../PhotovoltaicDataFilterType.cs | 28 +- .../PhotovoltaicDataQueries.cs | 24 +- .../PhotovoltaicDataSortType.cs | 4 +- .../Publications/PublicationFilterType.cs | 4 +- .../Publications/PublicationSortType.cs | 4 +- .../GraphQl/References/ReferenceFilterType.cs | 6 +- .../GraphQl/References/ReferenceSortType.cs | 6 +- .../ResponseApprovalFilterType.cs | 22 +- ...603191121_RevampAccessPolicies.Designer.cs | 3525 +++++++++++++++++ .../20260603191121_RevampAccessPolicies.cs | 407 ++ .../ApplicationDbContextModelSnapshot.cs | 683 +++- backend/src/Migrations/migrate.sql | 355 ++ ...to_20260603191121_RevampAccessPolicies.sql | 212 + ...o_20260527161543_MakeEntitiesAuditable.sql | 179 + backend/src/Program.cs | 10 +- backend/src/Services/AccessPolicyService.cs | 233 ++ backend/src/Services/AccessRightsService.cs | 169 - backend/src/Services/CacheService.cs | 46 +- .../src/Services/ResponseApprovalService.cs | 4 +- backend/src/Services/UserService.cs | 2 +- backend/src/Startup.cs | 3 +- backend/src/Views/_ViewImports.cshtml | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 673 +++- frontend/type-defs.graphqls | 673 +++- 111 files changed, 9078 insertions(+), 1725 deletions(-) create mode 100644 backend/src/Data/AccessPolicies/CountSinceTime.cs create mode 100644 backend/src/Data/AccessPolicies/DataAccessPolicy.cs create mode 100644 backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs create mode 100644 backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs create mode 100644 backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs create mode 100644 backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs create mode 100644 backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs create mode 100644 backend/src/Data/AccessPolicies/UserAccessPolicy.cs delete mode 100644 backend/src/Data/DataAccessRights.cs delete mode 100644 backend/src/Data/InstitutionAccessRights.cs create mode 100644 backend/src/Enumerations/LogicalCombinator.cs create mode 100644 backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs create mode 100644 backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs create mode 100644 backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs create mode 100644 backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs create mode 100644 backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs delete mode 100644 backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs delete mode 100644 backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs create mode 100644 backend/src/Migrations/20260603191121_RevampAccessPolicies.Designer.cs create mode 100644 backend/src/Migrations/20260603191121_RevampAccessPolicies.cs create mode 100644 backend/src/Migrations/migrate_from_20260527161543_MakeEntitiesAuditable_to_20260603191121_RevampAccessPolicies.sql create mode 100644 backend/src/Migrations/rollback_from_20260603191121_RevampAccessPolicies_to_20260527161543_MakeEntitiesAuditable.sql create mode 100644 backend/src/Services/AccessPolicyService.cs delete mode 100644 backend/src/Services/AccessRightsService.cs diff --git a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs index 1c394c56..a011868e 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs @@ -46,7 +46,7 @@ UserRepresentedInstitutionConnection DatabaseOperatingRepresentedInstitutions { public bool IsAtLeastAssistantManagerOfDatabaseOperator() { - return DatabaseOperatingRepresentedInstitutions.TotalCount >= 1; + return DatabaseOperatingRepresentedInstitutions.TotalCount > 0; } }; @@ -95,8 +95,8 @@ DatabaseOperatingManagedInstitutionConnection DatabaseOperatingManagedInstitutio { public bool IsDatabaseOperator() { - return DatabaseOperatingDatabases.TotalCount >= 1 - || DatabaseOperatingManagedInstitutions.TotalCount >= 1; + return DatabaseOperatingDatabases.TotalCount > 0 + || DatabaseOperatingManagedInstitutions.TotalCount > 0; } }; @@ -111,7 +111,17 @@ uint TotalCount public sealed record CurrentUserOrInstitution( CurrentUser? CurrentUser, CurrentInstitution? CurrentInstitution - ); + ) + { + public void Deconstruct( + out CurrentUser? currentUser, + out CurrentInstitution? currentInstitution + ) + { + currentUser = CurrentUser; + currentInstitution = CurrentInstitution; + } + }; public async Task Do( CancellationToken cancellationToken @@ -135,4 +145,4 @@ await GraphQlQueryHelpers.Construct(QueryFileName), } return response.Data; } -} \ No newline at end of file +} diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 042dd434..01ae896f 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -23,6 +23,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using NodaTime; +using DurationType = HotChocolate.Types.NodaTime.DurationType; namespace Database.Configuration; @@ -54,6 +55,7 @@ IWebHostEnvironment environment .AddMutationConventions(new MutationConventionOptions { ApplyToAllMutations = false }) // Extensions .AddNodaTime() + .BindRuntimeType() // .AddTypeConverter( // _ => _.ToDateTimeOffset() // ) diff --git a/backend/src/Data/AccessPolicies/CountSinceTime.cs b/backend/src/Data/AccessPolicies/CountSinceTime.cs new file mode 100644 index 00000000..edc4e00b --- /dev/null +++ b/backend/src/Data/AccessPolicies/CountSinceTime.cs @@ -0,0 +1,8 @@ +using System; + +namespace Database.Data.AccessPolicies; + +public sealed record CountSinceTime( + uint AccessCount, + DateTimeOffset? StartTime +); \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs new file mode 100644 index 00000000..31bba816 --- /dev/null +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using Database.Enumerations; +using EntityFrameworkCore.Projectables; + +namespace Database.Data.AccessPolicies; + +public sealed class DataAccessPolicy +{ + public LogicalCombinator Combinator { get; set; } = LogicalCombinator.ALL; + + public List? UserAccessPolicies { get; set; } + public List? InstitutionAccessPolicies { get; set; } + public List? OpenIdConnectApplicationAccessPolicies { get; set; } + + [NotMapped] + [Projectable] + public bool IsRestricted => + UserAccessPolicies is not null || + InstitutionAccessPolicies is not null || + OpenIdConnectApplicationAccessPolicies is not null; + + [NotMapped] + [Projectable] + public bool AreUsersRestricted => UserAccessPolicies is not null; + + [NotMapped] + [Projectable] + public bool AreInstitutionsRestricted => InstitutionAccessPolicies is not null; + + [NotMapped] + [Projectable] + public bool AreOpenIdConnectApplicationsRestricted => OpenIdConnectApplicationAccessPolicies is not null; +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs new file mode 100644 index 00000000..18a6f3e9 --- /dev/null +++ b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data.AccessPolicies; + +[PrimaryKey(nameof(InstitutionId))] +public sealed class InstitutionAccessPolicy( + Guid institutionId +) +: AccessPolicyBase +{ + public Guid InstitutionId { get; private set; } = institutionId; +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs new file mode 100644 index 00000000..48a0f406 --- /dev/null +++ b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs @@ -0,0 +1,12 @@ +using Microsoft.EntityFrameworkCore; + +namespace Database.Data.AccessPolicies; + +[PrimaryKey(nameof(ClientId))] +public sealed class OpenIdConnectApplicationAccessPolicy( + string clientId +) +: AccessPolicyBase +{ + public string ClientId { get; private set; } = clientId; +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs b/backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs new file mode 100644 index 00000000..04583567 --- /dev/null +++ b/backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs @@ -0,0 +1,44 @@ +using System; +using Database.Extensions; +using EntityFrameworkCore.Projectables; +using NodaTime; + +namespace Database.Data.AccessPolicies; + +public abstract class AccessPolicyBase +{ + public UpperLimitPerDuration? UpperAccessLimitPerTimeDuration { get; set; } + public CountSinceTime? AccessCountSinceStartTime { get; set; } + + [Projectable] + public bool IsAllowed => UpperAccessLimitPerTimeDuration is null || ( + UpperAccessLimitPerTimeDuration.UpperLimit > 0 && ( + AccessCountSinceStartTime is null || ( + IsWithinTimeSpan + && AccessCountSinceStartTime.AccessCount <= UpperAccessLimitPerTimeDuration.UpperLimit + ) + ) + ); + + [Projectable] + public bool IsWithinTimeSpan => + AccessCountSinceStartTime is null + || UpperAccessLimitPerTimeDuration is null + || UpperAccessLimitPerTimeDuration.Duration is null + || AccessCountSinceStartTime.StartTime + UpperAccessLimitPerTimeDuration.Duration < DateTimeOffset.UtcNow; + + public void IncrementAccessCount(IClock clock) + { + if (AccessCountSinceStartTime is null || !IsWithinTimeSpan) + { + AccessCountSinceStartTime = new(1, clock.GetUtcNow().ToDateTimeOffset()); + } + else + { + AccessCountSinceStartTime = new( + AccessCountSinceStartTime.AccessCount + 1, + AccessCountSinceStartTime.StartTime + ); + } + } +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs b/backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs new file mode 100644 index 00000000..e6084b0f --- /dev/null +++ b/backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs @@ -0,0 +1,13 @@ +using System; +using Database.Data.AccessPolicies; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record UpperLimitPerDurationInput( + uint UpperLimit, + TimeSpan? Duration +) +{ + public UpperLimitPerDuration ToDomainModel() + => new(UpperLimit, Duration); +}; \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs b/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs new file mode 100644 index 00000000..07cc272d --- /dev/null +++ b/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs @@ -0,0 +1,8 @@ +using System; + +namespace Database.Data.AccessPolicies; + +public sealed record UpperLimitPerDuration( + uint UpperLimit, + TimeSpan? Duration +); \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs new file mode 100644 index 00000000..12cbf83a --- /dev/null +++ b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data.AccessPolicies; + +[PrimaryKey(nameof(UserId))] +public sealed class UserAccessPolicy( + Guid userId +) +: AccessPolicyBase +{ + public Guid UserId { get; private set; } = userId; +} \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 19d9ccd3..7ed19e36 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -12,10 +12,10 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using SchemaNameOptionsExtension = Database.Data.Extensions.SchemaNameOptionsExtension; using NodaTime; -using System.Text.Json; using Database.GraphQl; +using Database.Data.Extensions; +using Database.Data.AccessPolicies; namespace Database.Data; @@ -33,6 +33,7 @@ public sealed class ApplicationDbContext internal const string CoatedSideTypeName = "coated_side"; internal const string DataKindTypeName = "data_kind"; internal const string IlluminantTypeName = "illuminant"; + internal const string LogicalCombinatorTypeName = "logical_combinator"; internal const string OpticalComponentSubtypeTypeName = "optical_component_subtype"; internal const string OpticalComponentTypeTypeName = "optical_component_type"; internal const string PublishingStateTypeName = "publishing_state"; @@ -55,7 +56,9 @@ IClock clock public DbSet GetHttpsResources { get; private set; } = default!; public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; - public DbSet InstitutionAccessRights { get; private set; } = default!; + public DbSet UserAccessPolicies { get; private set; } = default!; + public DbSet InstitutionAccessPolicies { get; private set; } = default!; + public DbSet OpenIdConnectApplicationAccessPolicies { get; private set; } = default!; public IQueryable GetHttpsResourcesWithData => GetHttpsResources.AsQueryable() @@ -184,14 +187,15 @@ public IQueryable Data(DataKind dataKind) public Task GetDataAsync(Guid id, DataKind dataKind, CancellationToken cancellationToken) { - return Data(dataKind).SingleOrDefaultAsync(x => x.Id == id, cancellationToken); + return Data(dataKind).SingleOrDefaultAsync(_ => _.Id == id, cancellationToken); } public IAsyncEnumerable GetAllDataAsync( - Expression> where, - QueryContext queryContext + Expression>? where = null, + QueryContext? queryContext = null ) { + where ??= _ => true; return ( CalorimetricData.AsQueryable() .Where(where) @@ -258,18 +262,27 @@ EntityTypeBuilder builder where TData : class, IData { builder - .OwnsOne( - data => data.DataAccessRights, - dataAccessRights => - { - dataAccessRights - .Property(_ => _.AllowedUserAndQuantity) - .HasConversion( - dictionary => JsonSerializer.Serialize(dictionary), - stringValue => JsonSerializer.Deserialize>(stringValue) ?? new() - ); - } + .ComplexProperty( + data => data.AccessPolicy, + _ => _.ToJson() ); + // .OwnsOne( + // dataAccessPolicy => + // { + // dataAccessPolicy + // // The issues + // // https://github.com/dotnet/efcore/issues/33170#issuecomment-1966366300 + // // https://github.com/dotnet/efcore/issues/31238 + // // track the missing support of complex properties in owned types, + // // which would be used as below: + // // .ComplexProperty(_ => _.GrantedUserAndQuantity, d => d.ToJson()) + // .Property(_ => _.GrantedUserAndQuantity) + // .HasConversion( + // dictionary => JsonSerializer.Serialize(dictionary), + // stringValue => JsonSerializer.Deserialize>(stringValue) ?? new() + // ); + // } + // ); builder .OwnsOne( data => data.AppliedMethod, @@ -281,7 +294,7 @@ EntityTypeBuilder builder ); builder .OwnsOne( - x => x.Approval, + _ => _.Approval, approval => { approval @@ -291,7 +304,7 @@ EntityTypeBuilder builder ); builder .OwnsMany( - x => x.Approvals, + _ => _.Approvals, approvals => { approvals @@ -490,11 +503,16 @@ EntityTypeBuilder builder return builder; } - private static void ConfigureIdentityEntities( - ModelBuilder builder - ) + private static + EntityTypeBuilder + ConfigureAccessPolicy( + EntityTypeBuilder builder + ) + where TAccessPolicy : AccessPolicyBase { - // https://stackoverflow.com/questions/19902756/asp-net-identity-dbcontext-confusion/35722688#35722688 + builder.ComplexProperty(_ => _.UpperAccessLimitPerTimeDuration, _ => _.ToJson()); + builder.ComplexProperty(_ => _.AccessCountSinceStartTime, _ => _.ToJson()); + return builder; } protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -502,7 +520,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); modelBuilder.HasDefaultSchema(_schemaName); modelBuilder.HasPostgresExtension("pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - ConfigureIdentityEntities(modelBuilder); ConfigureEntity( ConfigureGetHttpsResource(modelBuilder.Entity()) ) @@ -530,23 +547,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureCalorimetricData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) - ).ToTable("calorimetric_data") - ); + ) + ).ToTable("calorimetric_data"); ConfigureGeometricData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) - ).ToTable("geometric_data") - ); + ) + ).ToTable("geometric_data"); ConfigureHygrothermalData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) - ).ToTable("hygrothermal_data") - ); + ) + ).ToTable("hygrothermal_data"); ConfigureLifeCycleData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) - ).ToTable("lifeCycle_data") - ); + ) + ).ToTable("lifeCycle_data"); ConfigureOpticalData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) @@ -555,16 +572,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigurePhotovoltaicData( ConfigureData( ConfigureEntity(modelBuilder.Entity()) - ).ToTable("photovoltaic_data") - ); - ConfigureEntity( - modelBuilder.Entity() ) - .ToTable("user"); + ).ToTable("photovoltaic_data"); + ConfigureAccessPolicy( + modelBuilder.Entity() + ).ToTable("user_access_policy"); + ConfigureAccessPolicy( + modelBuilder.Entity() + ).ToTable("institution_access_policy"); + ConfigureAccessPolicy( + modelBuilder.Entity() + ).ToTable("open_id_connect_application_access_policy"); ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("institution_access_rights"); + modelBuilder.Entity() + ) + .ToTable("user"); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(IEntity).IsAssignableFrom(entityType.ClrType)) diff --git a/backend/src/Data/DataAccessRights.cs b/backend/src/Data/DataAccessRights.cs deleted file mode 100644 index e4544c5a..00000000 --- a/backend/src/Data/DataAccessRights.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using EntityFrameworkCore.Projectables; -using Microsoft.EntityFrameworkCore; - -namespace Database.Data; - -/// -/// Access rights per dataset -/// -[Owned] -public sealed class DataAccessRights -{ - /// - /// Dictionary to store allowed user with the amount of a allowed dataset calls. - /// If the user ID is mapped to `null`, then this means that the user has unlimited - /// access. - /// - [Column(TypeName = "jsonb")] - public IReadOnlyDictionary? AllowedUserAndQuantity { get; set; } - - /// - /// Allowed Institutions - /// - public IReadOnlyList? AllowedInstitutions { get; set; } - - /// - /// Allowed applications - /// - public IReadOnlyList? AllowedApplications { get; set; } - - /// - /// Is data access restricted by data access rights. - /// - [NotMapped] - [Projectable] - public bool HasRestrictions => - AllowedUserAndQuantity != null || - AllowedInstitutions != null || - AllowedApplications != null; - - /// - /// Is data access restricted by user. - /// - [NotMapped] - [Projectable] - public bool HasRestrictionsByUser => AllowedUserAndQuantity != null; - - /// - /// Is data access restricted by institution. - /// - [NotMapped] - [Projectable] - public bool HasRestrictionsByInstitution => AllowedInstitutions != null; - - /// - /// Is data access restricted by application. - /// - [NotMapped] - [Projectable] - public bool HasRestrictionsByApplication => AllowedApplications != null; -} \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index 9076ac90..f8227cee 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; +using Database.Data.AccessPolicies; using Database.Enumerations; namespace Database.Data; @@ -95,11 +95,13 @@ public void Retract() public ICollection Approvals { get; } = []; public ResponseApproval? Approval { get; set; } - // TODO Exactly one resource must not have a parent and each other resource must have one from - // this list and the graph must be connected. In other words, the resources must form a tree. + // The resources form a single, connected tree structure. Exactly one + // resource acts as the root (having no parent), while every other resource + // references a valid parent from this list. This structural integrity is + // strictly enforced by PostgreSQL database constraints. public abstract ICollection Resources { get; } - public DataAccessRights DataAccessRights { get; private set; } = new DataAccessRights(); + public DataAccessPolicy? AccessPolicy { get; set; } public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; @@ -107,36 +109,4 @@ public abstract Task ExtractAndSetValuesFromFile( string filePath, Guid dataFormatId ); - - /// - public bool IsRestrictedByApplication(string applicationId) - { - return DataAccessRights.AllowedApplications is not null - && DataAccessRights.AllowedApplications.Contains(applicationId); - } - - /// - public bool IsRestrictedByInstitutions(IEnumerable institutions) - { - return DataAccessRights.AllowedInstitutions is not null - && DataAccessRights.AllowedInstitutions.Any(a => - institutions.Any(b => - a.Equals(b) - ) - ); - } - - /// - public bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount) - { - if (DataAccessRights.AllowedUserAndQuantity is null) - { - return false; - } - if (DataAccessRights.AllowedUserAndQuantity.TryGetValue(uuid, out var limit)) - { - return limit is not null && alreadyAccesedCount >= limit; - } - return true; - } } \ No newline at end of file diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index f14fe843..26ee023f 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -146,6 +146,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() public ICollection ArchivedFilesMetaInformation { get; private set; } = []; // Note that at least one data ID is always present. So `Guid.Empty` will never be used. + [NotMapped] [Projectable] public Guid DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index f00832d8..eaed8a69 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using System.Threading.Tasks; +using Database.Data.AccessPolicies; using Database.Enumerations; using Database.GraphQl; @@ -29,31 +30,9 @@ public interface IData : IEntity, IAuditable ICollection Resources { get; } ResponseApproval? Approval { get; set; } string Locale { get; } - DataAccessRights DataAccessRights { get; } + DataAccessPolicy? AccessPolicy { get; set; } PublishingState PublishingState { get; } - /// - /// Check if dataset is restricted for passed application id. - /// - /// Id of application. - /// True, if dataset is rescricted. Otherwise false. - bool IsRestrictedByApplication(string applicationId); - - /// - /// Check if dataset is restricted for at least one of the passed institutions. - /// - /// List of institution ids. - /// True, if dataset is rescricted. Otherwise false. - bool IsRestrictedByInstitutions(IEnumerable institutions); - - /// - /// Check if dataset is restricted for passed user. - /// - /// Id of user. - /// Count of already accessed datasets by user. - /// True, if dataset is rescricted. Otherwise false. - bool IsRestrictedByUser(Guid uuid, uint alreadyAccesedCount); - void Update( string locale, Guid componentId, diff --git a/backend/src/Data/InstitutionAccessRights.cs b/backend/src/Data/InstitutionAccessRights.cs deleted file mode 100644 index 10db044f..00000000 --- a/backend/src/Data/InstitutionAccessRights.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using Database.Extensions; -using Database.Services; -using EntityFrameworkCore.Projectables; -using Microsoft.EntityFrameworkCore; -using NodaTime; - -namespace Database.Data; - -[Index(nameof(InstitutionId), IsUnique = true)] -public sealed class InstitutionAccessRights( - Guid institutionId, - uint? allowedUserCount, - uint? allowedDatasetsPerTime, - Duration period -) -: AuditableEntity -{ - public Guid InstitutionId { get; set; } = institutionId; - public uint? AllowedUserCount { get; set; } = allowedUserCount; - public uint? AllowedDatasetsPerTime { get; set; } = allowedDatasetsPerTime; - public Duration Period { get; set; } = period; - public List UserAlreadyAccessed { get; private set; } = []; - - [NotMapped] - [Projectable] - public bool HasRestrictions => - AllowedDatasetsPerTime != null - || AllowedUserCount != null; - - [NotMapped] - [Projectable] - public bool HasRestrictionsByTime => AllowedDatasetsPerTime != null; - - [NotMapped] - [Projectable] - public bool HasRestrictionsByUser => AllowedUserCount != null; - - internal bool IsDataRestrictedByTime(IData dataItem, IClock clock, CacheService cacheService, out string? reason) - { - var isRestricted = false; - reason = null; - // Check restriction for time period - if (AllowedDatasetsPerTime is not null) - { - var accessesPerPeriod = cacheService.GetOrCreateAccessCountForPeriod(InstitutionId); - if (accessesPerPeriod.StartTime + Period < clock.GetUtcNow()) - { - if (accessesPerPeriod.Count >= AllowedDatasetsPerTime) - { - isRestricted = true; - reason = $"Id: {dataItem.Id} Reason: Maximum amount of allowed datasets for institution {InstitutionId} reached."; - } - cacheService.AddAccessCountToPeriod(InstitutionId); - } - else - { - cacheService.SetNewTimePeriod(InstitutionId); - } - } - return isRestricted; - } - - internal bool IsDataRestrictedByUser(IData dataItem, Guid currentUserId, out string? reason) - { - var isRestricted = false; - reason = null; - // Check restriction for users per institution - if (AllowedUserCount is not null) - { - if (UserAlreadyAccessed.Count >= AllowedUserCount) - { - isRestricted = true; - reason = $"Id: {dataItem.Id} Reason: Maximum allowed users of institution {InstitutionId} reached."; - } - UserAlreadyAccessed.Add(currentUserId); - } - return isRestricted; - } -} \ No newline at end of file diff --git a/backend/src/Enumerations/LogicalCombinator.cs b/backend/src/Enumerations/LogicalCombinator.cs new file mode 100644 index 00000000..a1933c4d --- /dev/null +++ b/backend/src/Enumerations/LogicalCombinator.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; + +namespace Database.Enumerations; + +/// +/// How are multiple access policies evaluated together? +/// +[SuppressMessage("Naming", "CA1707")] +public enum LogicalCombinator +{ + /// + /// Conjunctive logic: Every single condition must evaluate to true (logical and) + /// + ALL, + + /// + ///Disjunctive logic: At least one condition must evaluate to true (logical or) + /// + SOME +} \ No newline at end of file diff --git a/backend/src/Extensions/HttpContextExtensions.cs b/backend/src/Extensions/HttpContextExtensions.cs index ed3897ca..3468cd50 100644 --- a/backend/src/Extensions/HttpContextExtensions.cs +++ b/backend/src/Extensions/HttpContextExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics.CodeAnalysis; using System.Linq; using Database.Authentication; using Microsoft.AspNetCore.Http; @@ -16,8 +15,8 @@ public static void SetBearerToken(this HttpContext httpContext, string accessTok var bearerTokenPrefix = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} "; return httpContext.Request?.Headers?.Authorization .FirstOrDefault( - x => x is not null - && x.TrimStart().StartsWith(bearerTokenPrefix, StringComparison.Ordinal) + _ => _ is not null + && _.TrimStart().StartsWith(bearerTokenPrefix, StringComparison.Ordinal) ) ?.TrimStart() ?[bearerTokenPrefix.Length..] diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs new file mode 100644 index 00000000..1b7c99f3 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using GreenDonut.Data; +using Database.Data; +using Microsoft.EntityFrameworkCore; +using Database.Data.AccessPolicies; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicyLoaders +: DataLoaders +{ + [DataLoader] + public static async ValueTask> GetUserAccessPolicyByUserIdAsync( + IReadOnlyList userIds, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return await databaseContext.UserAccessPolicies + .AsNoTrackingWithIdentityResolution() + .Where(_ => userIds.Contains(_.UserId)) + .With(queryContext, _ => _.AddDescending(_ => _.UserId)) + .ToDictionaryAsync(_ => _.UserId, cancellationToken); + } + + [DataLoader] + public static async ValueTask> GetInstitutionAccessPolicyByInstitutionIdAsync( + IReadOnlyList institutionIds, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return await databaseContext.InstitutionAccessPolicies + .AsNoTrackingWithIdentityResolution() + .Where(_ => institutionIds.Contains(_.InstitutionId)) + .With(queryContext, _ => _.AddDescending(_ => _.InstitutionId)) + .ToDictionaryAsync(_ => _.InstitutionId, cancellationToken); + } + + [DataLoader] + public static async ValueTask> GetOpenIdConnectApplicationAccessPolicyByClientIdAsync( + IReadOnlyList clientIds, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + await using var databaseContext = + databaseContextFactory.CreateDbContext(); + return await databaseContext.OpenIdConnectApplicationAccessPolicies + .AsNoTrackingWithIdentityResolution() + .Where(_ => clientIds.Contains(_.ClientId ?? "")) + .With(queryContext, _ => _.AddDescending(_ => _.ClientId)) + .ToDictionaryAsync(_ => _.ClientId ?? "", cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs new file mode 100644 index 00000000..9836a2f1 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.GraphQl.Extensions; +using GreenDonut.Data; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +[ExtendObjectType(nameof(Query))] +public sealed class AccessPolicyQueries +{ + public Task GetUserAccessPolicyAsync( + Guid userId, + IUserAccessPolicyByUserIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(userId, cancellationToken); + } + + public ValueTask> GetUserAccessPoliciesAsync( + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return databaseContext.UserAccessPolicies.AsNoTracking() + .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.UserId)) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + public Task GetInstitutionAccessPolicyAsync( + Guid institutionId, + IInstitutionAccessPolicyByInstitutionIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(institutionId, cancellationToken); + } + + public ValueTask> GetInstitutionAccessPoliciesAsync( + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return databaseContext.InstitutionAccessPolicies.AsNoTracking() + .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.InstitutionId)) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + public Task GetOpenIdConnectApplicationAccessPolicyAsync( + string clientId, + IOpenIdConnectApplicationAccessPolicyByClientIdDataLoader byId, + CancellationToken cancellationToken + ) + { + return byId.LoadAsync(clientId, cancellationToken); + } + + public ValueTask> GetOpenIdConnectApplicationAccessPoliciesAsync( + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + return databaseContext.OpenIdConnectApplicationAccessPolicies.AsNoTracking() + .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.ClientId)) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs new file mode 100644 index 00000000..dad93fbb --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicy; + +[SuppressMessage("Naming", "CA1707")] +public enum ClearDataAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ClearDataAccessPolicyError( + ClearDataAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearDataAccessPolicyPayload( + IReadOnlyCollection? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearDataAccessPolicyMutation +: MutationBase, ClearDataAccessPolicyPayload, ClearDataAccessPolicyError, ClearDataAccessPolicyErrorCode> +{ + protected override ClearDataAccessPolicyPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearDataAccessPolicyError NewError( + ClearDataAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ClearDataAccessPolicyAsync( + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearDataAccessPolicyErrorCode.UNAUTHENTICATED, + ClearDataAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicies = new List(); + await foreach (var data in context.GetAllDataAsync()) + { + if (data.AccessPolicy is not null) + { + accessPolicies.Add(data.AccessPolicy); + data.AccessPolicy = null; + } + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs new file mode 100644 index 00000000..d36e5621 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +[SuppressMessage("Naming", "CA1707")] +public enum ClearInstitutionAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record ClearInstitutionAccessPoliciesError( + ClearInstitutionAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearInstitutionAccessPoliciesPayload( + IReadOnlyCollection? InstitutionAccessPolicies, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearInstitutionAccessPoliciesMutation +: MutationBase, ClearInstitutionAccessPoliciesPayload, ClearInstitutionAccessPoliciesError, ClearInstitutionAccessPoliciesErrorCode> +{ + protected override ClearInstitutionAccessPoliciesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearInstitutionAccessPoliciesError NewError( + ClearInstitutionAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ClearInstitutionAccessPoliciesAsync( + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearInstitutionAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearInstitutionAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicies = await context.InstitutionAccessPolicies.ToListAsync(cancellationToken); + context.InstitutionAccessPolicies.RemoveRange(accessPolicies); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs new file mode 100644 index 00000000..4d2011b1 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +[SuppressMessage("Naming", "CA1707")] +public enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record ClearOpenIdConnectApplicationAccessPoliciesError( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearOpenIdConnectApplicationAccessPoliciesPayload( + IReadOnlyCollection? OpenIdConnectApplicationAccessPolicies, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearOpenIdConnectApplicationAccessPoliciesMutation +: MutationBase, ClearOpenIdConnectApplicationAccessPoliciesPayload, ClearOpenIdConnectApplicationAccessPoliciesError, ClearOpenIdConnectApplicationAccessPoliciesErrorCode> +{ + protected override ClearOpenIdConnectApplicationAccessPoliciesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearOpenIdConnectApplicationAccessPoliciesError NewError( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ClearOpenIdConnectApplicationAccessPoliciesAsync( + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearOpenIdConnectApplicationAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearOpenIdConnectApplicationAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicies = await context.OpenIdConnectApplicationAccessPolicies.ToListAsync(cancellationToken); + context.OpenIdConnectApplicationAccessPolicies.RemoveRange(accessPolicies); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs new file mode 100644 index 00000000..31273332 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +[SuppressMessage("Naming", "CA1707")] +public enum ClearUserAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record ClearUserAccessPoliciesError( + ClearUserAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ClearUserAccessPoliciesPayload( + IReadOnlyCollection? UserAccessPolicies, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ClearUserAccessPoliciesMutation +: MutationBase, ClearUserAccessPoliciesPayload, ClearUserAccessPoliciesError, ClearUserAccessPoliciesErrorCode> +{ + protected override ClearUserAccessPoliciesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ClearUserAccessPoliciesError NewError( + ClearUserAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ClearUserAccessPoliciesAsync( + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ClearUserAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearUserAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicies = await context.UserAccessPolicies.ToListAsync(cancellationToken); + context.UserAccessPolicies.RemoveRange(accessPolicies); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs new file mode 100644 index 00000000..d536f8a3 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteDataAccessPolicyInput +( + Guid DataId, + DataKind DataKind +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record DeleteDataAccessPolicyError( + DeleteDataAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataAccessPolicyPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteDataAccessPolicyMutation +: DataMutationBase +{ + protected override DeleteDataAccessPolicyPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteDataAccessPolicyError NewError( + DeleteDataAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteDataAccessPolicyAsync( + DeleteDataAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteDataAccessPolicyErrorCode.UNAUTHENTICATED, + DeleteDataAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + DeleteDataAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + data.AccessPolicy = null; + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs new file mode 100644 index 00000000..dc03acc6 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteDataAccessPolicyOfInstitutionInput +( + Guid DataId, + DataKind DataKind, + Guid InstitutionId +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataAccessPolicyOfInstitutionErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_INSTITUTION +} + +public sealed record DeleteDataAccessPolicyOfInstitutionError( + DeleteDataAccessPolicyOfInstitutionErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataAccessPolicyOfInstitutionPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteDataAccessPolicyOfInstitutionMutation +: DataMutationBase +{ + protected override DeleteDataAccessPolicyOfInstitutionPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteDataAccessPolicyOfInstitutionError NewError( + DeleteDataAccessPolicyOfInstitutionErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteDataAccessPolicyOfInstitutionAsync( + DeleteDataAccessPolicyOfInstitutionInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IInstitutionByIdDataLoader institutionByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteDataAccessPolicyOfInstitutionErrorCode.UNAUTHENTICATED, + DeleteDataAccessPolicyOfInstitutionErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + DeleteDataAccessPolicyOfInstitutionErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await institutionByIdDataLoader.LoadAsync(input.InstitutionId, cancellationToken) is null) + { + errors.Add( + NewError( + DeleteDataAccessPolicyOfInstitutionErrorCode.UNKNOWN_INSTITUTION, + $"The institution does not exist.", + [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy?.InstitutionAccessPolicies?.RemoveAll(_ => _.InstitutionId == input.InstitutionId); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs new file mode 100644 index 00000000..6a54feff --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationInput +( + Guid DataId, + DataKind DataKind, + string ClientId +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_APPLICATION +} + +public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationError( + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation +: DataMutationBase +{ + protected override DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteDataAccessPolicyOfOpenIdConnectApplicationError NewError( + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteDataAccessPolicyOfOpenIdConnectApplicationAsync( + DeleteDataAccessPolicyOfOpenIdConnectApplicationInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByClientIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNAUTHENTICATED, + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await openIdConnectApplicationByClientIdDataLoader.LoadAsync(input.ClientId, cancellationToken) is null) + { + errors.Add( + NewError( + DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNKNOWN_APPLICATION, + $"The Open ID Connect application does not exist.", + [nameof(input), nameof(input.ClientId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy?.OpenIdConnectApplicationAccessPolicies?.RemoveAll(_ => _.ClientId == input.ClientId); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs new file mode 100644 index 00000000..48be1842 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteDataAccessPolicyOfUserInput +( + Guid DataId, + DataKind DataKind, + Guid UserId +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteDataAccessPolicyOfUserErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_USER +} + +public sealed record DeleteDataAccessPolicyOfUserError( + DeleteDataAccessPolicyOfUserErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteDataAccessPolicyOfUserPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteDataAccessPolicyOfUserMutation +: DataMutationBase +{ + protected override DeleteDataAccessPolicyOfUserPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteDataAccessPolicyOfUserError NewError( + DeleteDataAccessPolicyOfUserErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteDataAccessPolicyOfUserAsync( + DeleteDataAccessPolicyOfUserInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IUserByIdDataLoader userByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteDataAccessPolicyOfUserErrorCode.UNAUTHENTICATED, + DeleteDataAccessPolicyOfUserErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + DeleteDataAccessPolicyOfUserErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await userByIdDataLoader.LoadAsync(input.UserId, cancellationToken) is null) + { + errors.Add( + NewError( + DeleteDataAccessPolicyOfUserErrorCode.UNKNOWN_USER, + $"The user does not exist.", + [nameof(input), nameof(input.UserId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy?.UserAccessPolicies?.RemoveAll(_ => _.UserId == input.UserId); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs new file mode 100644 index 00000000..b0d61396 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteInstitutionAccessPolicyInput +( + Guid InstitutionId +); + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteInstitutionAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_POLICY +} + +public sealed record DeleteInstitutionAccessPolicyError( + DeleteInstitutionAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteInstitutionAccessPolicyPayload( + InstitutionAccessPolicy? InstitutionAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteInstitutionAccessPolicyMutation +: MutationBase +{ + protected override DeleteInstitutionAccessPolicyPayload NewPayload( + InstitutionAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteInstitutionAccessPolicyError NewError( + DeleteInstitutionAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteInstitutionAccessPolicyAsync( + DeleteInstitutionAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteInstitutionAccessPolicyErrorCode.UNAUTHENTICATED, + DeleteInstitutionAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.InstitutionAccessPolicies + .SingleOrDefaultAsync(x => + x.InstitutionId == input.InstitutionId, + cancellationToken + ); + if (accessPolicy is null) + { + return NewPayload( + null, + [NewError( + DeleteInstitutionAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The access policy does not exist.", + [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] + )] + ); + } + context.InstitutionAccessPolicies.Remove(accessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs new file mode 100644 index 00000000..2c343b00 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteOpenIdConnectApplicationAccessPolicyInput +( + string ClientId +); + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_POLICY +} + +public sealed record DeleteOpenIdConnectApplicationAccessPolicyError( + DeleteOpenIdConnectApplicationAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteOpenIdConnectApplicationAccessPolicyPayload( + OpenIdConnectApplicationAccessPolicy? OpenIdConnectApplicationAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteOpenIdConnectApplicationAccessPolicyMutation +: MutationBase +{ + protected override DeleteOpenIdConnectApplicationAccessPolicyPayload NewPayload( + OpenIdConnectApplicationAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteOpenIdConnectApplicationAccessPolicyError NewError( + DeleteOpenIdConnectApplicationAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteOpenIdConnectApplicationAccessPolicyAsync( + DeleteOpenIdConnectApplicationAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHENTICATED, + DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.OpenIdConnectApplicationAccessPolicies + .SingleOrDefaultAsync(x => + x.ClientId == input.ClientId, + cancellationToken + ); + if (accessPolicy is null) + { + return NewPayload( + null, + [NewError( + DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The access policy does not exist.", + [nameof(input), nameof(input.ClientId).ToLowerFirst()] + )] + ); + } + context.OpenIdConnectApplicationAccessPolicies.Remove(accessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs new file mode 100644 index 00000000..ddc05b88 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record DeleteUserAccessPolicyInput +( + Guid UserId +); + +[SuppressMessage("Naming", "CA1707")] +public enum DeleteUserAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_POLICY +} + +public sealed record DeleteUserAccessPolicyError( + DeleteUserAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record DeleteUserAccessPolicyPayload( + UserAccessPolicy? UserAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class DeleteUserAccessPolicyMutation +: MutationBase +{ + protected override DeleteUserAccessPolicyPayload NewPayload( + UserAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override DeleteUserAccessPolicyError NewError( + DeleteUserAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task DeleteUserAccessPolicyAsync( + DeleteUserAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + DeleteUserAccessPolicyErrorCode.UNAUTHENTICATED, + DeleteUserAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.UserAccessPolicies + .SingleOrDefaultAsync(x => + x.UserId == input.UserId, + cancellationToken + ); + if (accessPolicy is null) + { + return NewPayload( + null, + [NewError( + DeleteUserAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The access policy does not exist.", + [nameof(input), nameof(input.UserId).ToLowerFirst()] + )] + ); + } + context.UserAccessPolicies.Remove(accessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs new file mode 100644 index 00000000..d4edcd5d --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicyFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(InstitutionAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.InstitutionId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs new file mode 100644 index 00000000..3bb8d1ad --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicySortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(InstitutionAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.InstitutionId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs new file mode 100644 index 00000000..732d8e56 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicyFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.ClientId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs new file mode 100644 index 00000000..8de450be --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicySortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.ClientId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs new file mode 100644 index 00000000..0acadfcb --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetDataAccessLimitForInstitutionInput +( + Guid DataId, + DataKind DataKind, + Guid InstitutionId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum SetDataAccessLimitForInstitutionErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_INSTITUTION +} + +public sealed record SetDataAccessLimitForInstitutionError( + SetDataAccessLimitForInstitutionErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetDataAccessLimitForInstitutionPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetDataAccessLimitForInstitutionMutation +: DataMutationBase +{ + protected override SetDataAccessLimitForInstitutionPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetDataAccessLimitForInstitutionError NewError( + SetDataAccessLimitForInstitutionErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetDataAccessLimitForInstitutionAsync( + SetDataAccessLimitForInstitutionInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IInstitutionByIdDataLoader institutionByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetDataAccessLimitForInstitutionErrorCode.UNAUTHENTICATED, + SetDataAccessLimitForInstitutionErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + SetDataAccessLimitForInstitutionErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await institutionByIdDataLoader.LoadAsync(input.InstitutionId, cancellationToken) is null) + { + errors.Add( + NewError( + SetDataAccessLimitForInstitutionErrorCode.UNKNOWN_INSTITUTION, + $"The institution does not exist.", + [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy ??= new DataAccessPolicy(); + data.AccessPolicy.InstitutionAccessPolicies ??= []; + var accessPolicy = + data.AccessPolicy.InstitutionAccessPolicies + .SingleOrDefault(_ => _.InstitutionId == input.InstitutionId); + if (accessPolicy is null) + { + accessPolicy = new InstitutionAccessPolicy(input.InstitutionId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + data.AccessPolicy.InstitutionAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs new file mode 100644 index 00000000..d589cae6 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetDataAccessLimitForOpenIdConnectApplicationInput +( + Guid DataId, + DataKind DataKind, + string ClientId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_APPLICATION +} + +public sealed record SetDataAccessLimitForOpenIdConnectApplicationError( + SetDataAccessLimitForOpenIdConnectApplicationErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetDataAccessLimitForOpenIdConnectApplicationPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetDataAccessLimitForOpenIdConnectApplicationMutation +: DataMutationBase +{ + protected override SetDataAccessLimitForOpenIdConnectApplicationPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetDataAccessLimitForOpenIdConnectApplicationError NewError( + SetDataAccessLimitForOpenIdConnectApplicationErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetDataAccessLimitForOpenIdConnectApplicationAsync( + SetDataAccessLimitForOpenIdConnectApplicationInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByClientIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNAUTHENTICATED, + SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await openIdConnectApplicationByClientIdDataLoader.LoadAsync(input.ClientId, cancellationToken) is null) + { + errors.Add( + NewError( + SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNKNOWN_APPLICATION, + $"The Open ID Connect application does not exist.", + [nameof(input), nameof(input.ClientId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy ??= new DataAccessPolicy(); + data.AccessPolicy.OpenIdConnectApplicationAccessPolicies ??= []; + var accessPolicy = + data.AccessPolicy.OpenIdConnectApplicationAccessPolicies + .SingleOrDefault(_ => _.ClientId == input.ClientId); + if (accessPolicy is null) + { + accessPolicy = new OpenIdConnectApplicationAccessPolicy(input.ClientId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + data.AccessPolicy.OpenIdConnectApplicationAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs new file mode 100644 index 00000000..17c011dd --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetDataAccessLimitForUserInput +( + Guid DataId, + DataKind DataKind, + Guid UserId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +) : IIdentifyDataInput; + +[SuppressMessage("Naming", "CA1707")] +public enum SetDataAccessLimitForUserErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_USER +} + +public sealed record SetDataAccessLimitForUserError( + SetDataAccessLimitForUserErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetDataAccessLimitForUserPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetDataAccessLimitForUserMutation +: DataMutationBase +{ + protected override SetDataAccessLimitForUserPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetDataAccessLimitForUserError NewError( + SetDataAccessLimitForUserErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetDataAccessLimitForUserAsync( + SetDataAccessLimitForUserInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IUserByIdDataLoader userByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetDataAccessLimitForUserErrorCode.UNAUTHENTICATED, + SetDataAccessLimitForUserErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if ((await FetchDataAsync( + input, + SetDataAccessLimitForUserErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + + List errors = []; + if (await userByIdDataLoader.LoadAsync(input.UserId, cancellationToken) is null) + { + errors.Add( + NewError( + SetDataAccessLimitForUserErrorCode.UNKNOWN_USER, + $"The user does not exist.", + [nameof(input), nameof(input.UserId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + data.AccessPolicy ??= new DataAccessPolicy(); + data.AccessPolicy.UserAccessPolicies ??= []; + var accessPolicy = + data.AccessPolicy.UserAccessPolicies + .SingleOrDefault(_ => _.UserId == input.UserId); + if (accessPolicy is null) + { + accessPolicy = new UserAccessPolicy(input.UserId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + data.AccessPolicy.UserAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + + await context.SaveChangesAsync(cancellationToken); + return NewPayload(data.AccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs new file mode 100644 index 00000000..1acd909f --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetInstitutionAccessLimitInput +( + Guid InstitutionId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetInstitutionAccessLimitErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record SetInstitutionAccessLimitError( + SetInstitutionAccessLimitErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetInstitutionAccessLimitPayload( + InstitutionAccessPolicy? InstitutionAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetInstitutionAccessLimitMutation +: MutationBase +{ + protected override SetInstitutionAccessLimitPayload NewPayload( + InstitutionAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetInstitutionAccessLimitError NewError( + SetInstitutionAccessLimitErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetInstitutionAccessLimitAsync( + SetInstitutionAccessLimitInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetInstitutionAccessLimitErrorCode.UNAUTHENTICATED, + SetInstitutionAccessLimitErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.InstitutionAccessPolicies + .SingleOrDefaultAsync(x => + x.InstitutionId == input.InstitutionId, + cancellationToken + ); + if (accessPolicy is null) + { + accessPolicy = new InstitutionAccessPolicy(input.InstitutionId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.InstitutionAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs new file mode 100644 index 00000000..f8f3f384 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetOpenIdConnectApplicationAccessLimitInput +( + string ClientId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetOpenIdConnectApplicationAccessLimitErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record SetOpenIdConnectApplicationAccessLimitError( + SetOpenIdConnectApplicationAccessLimitErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetOpenIdConnectApplicationAccessLimitPayload( + OpenIdConnectApplicationAccessPolicy? OpenIdConnectApplicationAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetOpenIdConnectApplicationAccessLimitMutation +: MutationBase +{ + protected override SetOpenIdConnectApplicationAccessLimitPayload NewPayload( + OpenIdConnectApplicationAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetOpenIdConnectApplicationAccessLimitError NewError( + SetOpenIdConnectApplicationAccessLimitErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetOpenIdConnectApplicationAccessLimitAsync( + SetOpenIdConnectApplicationAccessLimitInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetOpenIdConnectApplicationAccessLimitErrorCode.UNAUTHENTICATED, + SetOpenIdConnectApplicationAccessLimitErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.OpenIdConnectApplicationAccessPolicies + .SingleOrDefaultAsync(x => + x.ClientId == input.ClientId, + cancellationToken + ); + if (accessPolicy is null) + { + accessPolicy = new OpenIdConnectApplicationAccessPolicy(input.ClientId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.OpenIdConnectApplicationAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs new file mode 100644 index 00000000..f0019bae --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetUserAccessLimitInput +( + Guid UserId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetUserAccessLimitErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record SetUserAccessLimitError( + SetUserAccessLimitErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetUserAccessLimitPayload( + UserAccessPolicy? UserAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetUserAccessLimitMutation +: MutationBase +{ + protected override SetUserAccessLimitPayload NewPayload( + UserAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetUserAccessLimitError NewError( + SetUserAccessLimitErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetUserAccessLimitAsync( + SetUserAccessLimitInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetUserAccessLimitErrorCode.UNAUTHENTICATED, + SetUserAccessLimitErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + var accessPolicy = await context.UserAccessPolicies + .SingleOrDefaultAsync(x => + x.UserId == input.UserId, + cancellationToken + ); + if (accessPolicy is null) + { + accessPolicy = new UserAccessPolicy(input.UserId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.UserAccessPolicies.Add(accessPolicy); + } + else + { + accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + accessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(accessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs new file mode 100644 index 00000000..079fc75a --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicyFilterType + : FilterInputType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(UserAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.UserId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs new file mode 100644 index 00000000..a0ce92e5 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs @@ -0,0 +1,17 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicySortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(UserAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.UserId); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs deleted file mode 100644 index 1ebadb17..00000000 --- a/backend/src/GraphQl/AccessRights/AddInstitutionAccessRightsMutation.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Extensions; -using Database.Services; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; -using NodaTime; - -namespace Database.GraphQl.AccessRights; - -public sealed record AddInstitutionAccessRightsInput -( - // Id of institution - Guid InstitutionId, - // Count of allowed user for institution. Null is unlimited - uint? AllowedUserCount, - // Count of allowed datasets for institution. Null is unlimited - uint? AllowedDatasetsPerTimeSpan, - int PeriodInDays -); - -[SuppressMessage("Naming", "CA1707")] -public enum AddInstitutionAccessRightsErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - ALREADY_EXISTS, -} - -public sealed record AddInstitutionAccessRightsError( - AddInstitutionAccessRightsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record AddInstitutionAccessRightsPayload( - Data.InstitutionAccessRights? InstitutionAccessRights, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class AddInstitutionAccessRightsMutation -: MutationBase -{ - protected override AddInstitutionAccessRightsPayload NewPayload( - Data.InstitutionAccessRights? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override AddInstitutionAccessRightsError NewError( - AddInstitutionAccessRightsErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task AddInstitutionAccessRightsAsync( - AddInstitutionAccessRightsInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - AddInstitutionAccessRightsErrorCode.UNAUTHENTICATED, - AddInstitutionAccessRightsErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - var accessRights = await context.InstitutionAccessRights - .SingleOrDefaultAsync(x => - x.InstitutionId == input.InstitutionId, - cancellationToken - ); - if (accessRights is not null) - { - return NewPayload( - null, - [NewError( - AddInstitutionAccessRightsErrorCode.ALREADY_EXISTS, - $"The access rights for this institution already exist.", - [] - )] - ); - } - - accessRights = new Data.InstitutionAccessRights( - input.InstitutionId, - input.AllowedUserCount, - input.AllowedDatasetsPerTimeSpan, - Duration.FromDays(input.PeriodInDays)); - context.InstitutionAccessRights.Add(accessRights); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessRights, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs deleted file mode 100644 index ff70a5a3..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateDataAccessRightsMutation.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessRights; - -public sealed record UpdateDataAccessRightsInput -( - Guid DataId, - DataKind DataKind, - IReadOnlyDictionary? AllowedUserAndQuantity, - IReadOnlyList? AllowedInstitutions, - IReadOnlyList? AllowedApplications -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum UpdateDataAccessRightsErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_INSTITUTION, - UNKNOWN_USER, - UNKNOWN_APPLICATION -} - -public sealed record UpdateDataAccessRightsError( - UpdateDataAccessRightsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record UpdateDataAccessRightsPayload( - DataAccessRights? DataAccessRights, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class UpdateDataAccessRightsMutation -: DataMutationBase -{ - protected override UpdateDataAccessRightsPayload NewPayload( - DataAccessRights? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override UpdateDataAccessRightsError NewError( - UpdateDataAccessRightsErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task UpdateDataAccessRightsAsync( - UpdateDataAccessRightsInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IInstitutionByIdDataLoader institutionByIdDataLoader, - IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByIdDataLoader, - IUserByIdDataLoader userByIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - UpdateDataAccessRightsErrorCode.UNAUTHENTICATED, - UpdateDataAccessRightsErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - UpdateDataAccessRightsErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - - var unknownInstitutions = - input.AllowedInstitutions is null - ? null - : input.AllowedInstitutions.Zip( - await Task.WhenAll( - input.AllowedInstitutions - .Select(id => institutionByIdDataLoader.LoadAsync(id, cancellationToken)) - ) - ) - .Where(_ => _.Second is null) - .Select(_ => _.First) - .ToList().AsReadOnly(); - if (unknownInstitutions is not null && unknownInstitutions.Count > 0) - { - errors.Add( - NewError( - UpdateDataAccessRightsErrorCode.UNKNOWN_INSTITUTION, - $"The institution(s) '{string.Join("', '", unknownInstitutions)}' do(es) not exist.", - [nameof(input), nameof(input.AllowedInstitutions).ToLowerFirst()] - ) - ); - } - - var unknownOpenIdConnectApplications = - input.AllowedApplications is null - ? null - : input.AllowedApplications.Zip( - await Task.WhenAll( - input.AllowedApplications - .Select(id => openIdConnectApplicationByIdDataLoader.LoadAsync(id, cancellationToken)) - ) - ) - .Where(_ => _.Second is null) - .Select(_ => _.First) - .ToList().AsReadOnly(); - if (unknownOpenIdConnectApplications is not null && unknownOpenIdConnectApplications.Count > 0) - { - errors.Add( - NewError( - UpdateDataAccessRightsErrorCode.UNKNOWN_APPLICATION, - $"The openIdConnectApplication(s) '{string.Join("', '", unknownOpenIdConnectApplications)}' do(es) not exist.", - [nameof(input), nameof(input.AllowedApplications).ToLowerFirst()] - ) - ); - } - - var unknownUsers = - input.AllowedUserAndQuantity is null - ? null - : input.AllowedUserAndQuantity.Zip( - await Task.WhenAll( - input.AllowedUserAndQuantity - .Select(userAndQuantity => userByIdDataLoader.LoadAsync(userAndQuantity.Key, cancellationToken)) - ) - ) - .Where(_ => _.Second is null) - .Select(_ => _.First.Key) - .ToList().AsReadOnly(); - if (unknownUsers is not null && unknownUsers.Count > 0) - { - errors.Add( - NewError( - UpdateDataAccessRightsErrorCode.UNKNOWN_USER, - $"The user(s) '{string.Join("', '", unknownUsers)}' do(es) not exist.", - [nameof(input), nameof(input.AllowedUserAndQuantity).ToLowerFirst()] - ) - ); - } - - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.DataAccessRights.AllowedInstitutions = input.AllowedInstitutions; - data.DataAccessRights.AllowedApplications = input.AllowedApplications; - data.DataAccessRights.AllowedUserAndQuantity = input.AllowedUserAndQuantity; - - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.DataAccessRights, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs b/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs deleted file mode 100644 index 3d4aa0f3..00000000 --- a/backend/src/GraphQl/AccessRights/UpdateInstitutionAccessRightsMutation.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Extensions; -using Database.Services; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; -using NodaTime; - -namespace Database.GraphQl.AccessRights; - -public sealed record UpdateInstitutionAccessRightsInput -( - // Id of institution - Guid InstitutionId, - // Count of allowed user for institution. Null is unlimited - uint? AllowedUserCount, - // Count of allowed datasets for institution. Null is unlimited - uint? AllowedDatasetsPerTimeSpan, - int PeriodInDays -); - -[SuppressMessage("Naming", "CA1707")] -public enum UpdateInstitutionAccessRightsErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_ACCESS_RIGHTS -} - -public sealed record UpdateInstitutionAccessRightsError( - UpdateInstitutionAccessRightsErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record UpdateInstitutionAccessRightsPayload( - Data.InstitutionAccessRights? InstitutionAccessRights, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class UpdateInstitutionAccessRightsMutation -: MutationBase -{ - protected override UpdateInstitutionAccessRightsPayload NewPayload( - Data.InstitutionAccessRights? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override UpdateInstitutionAccessRightsError NewError( - UpdateInstitutionAccessRightsErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task UpdateInstitutionAccessRightsAsync( - UpdateInstitutionAccessRightsInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - UpdateInstitutionAccessRightsErrorCode.UNAUTHENTICATED, - UpdateInstitutionAccessRightsErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - var accessRights = await context.InstitutionAccessRights - .SingleOrDefaultAsync(x => - x.InstitutionId == input.InstitutionId, - cancellationToken - ); - if (accessRights is null) - { - return NewPayload( - null, - [NewError( - UpdateInstitutionAccessRightsErrorCode.UNKNOWN_ACCESS_RIGHTS, - $"There are no access rights for this institution.", - [] - )] - ); - } - - accessRights.AllowedUserCount = input.AllowedUserCount; - accessRights.AllowedDatasetsPerTime = input.AllowedDatasetsPerTimeSpan; - accessRights.Period = Duration.FromDays(input.PeriodInDays); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessRights, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs b/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs index 70059ad5..c03718ca 100644 --- a/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs +++ b/backend/src/GraphQl/Associations/AuditableAssociationFilterType.cs @@ -13,7 +13,7 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs b/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs index 05109771..f6c4ac73 100644 --- a/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs +++ b/backend/src/GraphQl/Associations/AuditableAssociationSortType.cs @@ -13,7 +13,7 @@ ISortInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index 14a711c6..e8d6b9e4 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -13,24 +13,24 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(CalorimetricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.GValues); - descriptor.Field(x => x.UValues); + descriptor.Field(_ => _.GValues); + descriptor.Field(_ => _.UValues); // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 99684588..341d44ed 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -23,7 +23,7 @@ public sealed class CalorimetricDataQueries public Task> GetAllCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -31,7 +31,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.CalorimetricData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -45,7 +46,7 @@ CancellationToken cancellationToken public Task> GetAllPendingCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -54,7 +55,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.CalorimetricData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -65,6 +67,7 @@ CancellationToken cancellationToken public Task HasCalorimetricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -72,6 +75,8 @@ CancellationToken cancellationToken return HasDataAsync( context.CalorimetricData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -80,17 +85,18 @@ CancellationToken cancellationToken public Task GetCalorimetricDataAsync( Guid id, [GraphQLType] string? locale, - CalorimetricDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return GetDataAsync( id, locale, - byId, - accessRightsService, + context.CalorimetricData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs index 4b1f08c0..e1136a82 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 627a2fbc..99d87d68 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -291,7 +291,7 @@ CancellationToken cancellationToken ) ); } - if (errors.Count >= 1) + if (errors.Count > 0) { return NewPayload(null, errors); } @@ -339,4 +339,4 @@ CancellationToken cancellationToken return NewPayload(approval, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index 263b000a..ca955f25 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -123,10 +123,10 @@ CancellationToken cancellationToken ); } } - if (errors.Count >= 1) + if (errors.Count > 0) { return new Result>.Error(errors); } return new Result>.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs index 7471b6c8..eb1c673f 100644 --- a/backend/src/GraphQl/DataX/CreateDataMutationBase.cs +++ b/backend/src/GraphQl/DataX/CreateDataMutationBase.cs @@ -145,7 +145,7 @@ CancellationToken cancellationToken errors.AddRange(validateRootResourceErrors); } // Note that `dataFormat` is only `null`, when `validateRootResourceResult` failed. - if (errors.Count >= 1 || dataFormat is null) + if (errors.Count > 0 || dataFormat is null) { return new Result.Error( NewPayload(null, errors) @@ -153,4 +153,4 @@ CancellationToken cancellationToken } return new Result.Data(dataFormat); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index 93cc8e2d..e0edc296 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -15,19 +15,19 @@ IFilterInputTypeDescriptor descriptor // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index 7ef5a8ca..3919ca76 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -13,16 +13,16 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } // Inspired by https://github.com/ChilliCream/hotchocolate/blob/1319a4e3841beaec3bfbb282a909d2d1441f9c52/src/HotChocolate/Data/src/Data/Filters/FilterInputType.cs#L74 @@ -49,4 +49,4 @@ IFilterInputTypeDescriptor descriptor // } // return CompleteFields(context, this, fields); // } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index d463ec65..9a3e18b7 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Data; @@ -7,26 +8,32 @@ using Database.Services; using HotChocolate; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.DataX; [ExtendObjectType(nameof(Query))] public sealed class DataQueries { - public async Task GetDataAsync( + public Task GetDataAsync( Guid id, DataKind dataKind, [GraphQLType] string? locale, - ApplicationDbContext dbContext, - AccessRightsService accessRightsService, + ApplicationDbContext databaseContext, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { - var data = await dbContext.GetDataAsync(id, dataKind, cancellationToken); - if (data is null) - { - return null; - } - return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken); + return accessPolicyService.Apply( + databaseContext.Data(dataKind).AsNoTracking() + .Where(_ => _.Id == id), + async policedData => + { + var node = await policedData.SingleOrDefaultAsync(cancellationToken); + return (node is null ? [] : [node], node); + }, + databaseContext, + cancellationToken + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 17d29e04..b4e7be79 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -18,31 +18,37 @@ namespace Database.GraphQl.DataX; public abstract class DataQueriesBase where TData : class, IData { - protected async Task> GetAllDataAsync( + protected Task> GetAllDataAsync( DbSet data, [GraphQLType] string? locale, - AccessRightsService accessRightsService, + ApplicationDbContext databaseContext, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { - var sortedAndFilteredData = + return accessPolicyService.Apply( data.AsNoTracking() - .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) - .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder); - if (await sortedAndFilteredData.AnyAsync(_ => _.DataAccessRights.HasRestrictions, cancellationToken)) - { - sortedAndFilteredData = (await accessRightsService.ApplyAccessRightsOnData(sortedAndFilteredData, cancellationToken)).AsQueryable(); - } - return await sortedAndFilteredData - .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) - .ToConnectionAsync(); + .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), + async policedData => + { + var connection = await policedData + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + var nodes = connection.Edges.Select(_ => _.Node).ToList().AsReadOnly(); + return (nodes, connection); + }, + databaseContext, + cancellationToken + ); } protected async Task> GetAllPendingDataAsync( DbSet data, [GraphQLType] string? locale, - AccessRightsService accessRightsService, + ApplicationDbContext databaseContext, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -55,52 +61,65 @@ CancellationToken cancellationToken .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) .ToConnectionAsync(); } - var sortedAndFilteredData = + return await accessPolicyService.Apply( data.AsNoTracking() - .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) - .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder); - if (await sortedAndFilteredData.AnyAsync(_ => _.DataAccessRights.HasRestrictions, cancellationToken)) - { - sortedAndFilteredData = (await accessRightsService.ApplyAccessRightsOnData(sortedAndFilteredData, cancellationToken)).AsQueryable(); - } - return await sortedAndFilteredData - .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) - .ToConnectionAsync(); + .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), + async policedData => + { + var connection = await policedData + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + var nodes = connection.Edges.Select(_ => _.Node).ToList().AsReadOnly(); + return (nodes, connection); + }, + databaseContext, + cancellationToken + ); } protected Task HasDataAsync( DbSet data, [GraphQLType] string? locale, + ApplicationDbContext databaseContext, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { - return data.AsNoTracking() - .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) - .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) - .AnyAsync(cancellationToken); + return accessPolicyService.Apply( + data.AsNoTracking() + .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), + async policedData => + { + var nodes = (await policedData.ToListAsync(cancellationToken)).AsReadOnly(); + return (nodes, nodes.Count > 0); + }, + databaseContext, + cancellationToken + ); } - protected async Task GetDataAsync( + protected Task GetDataAsync( Guid id, [GraphQLType] string? locale, - GreenDonut.DataLoaderBase byId, - AccessRightsService accessRightsService, + DbSet data, + ApplicationDbContext databaseContext, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { - var data = await byId.LoadAsync( - id, + return accessPolicyService.Apply( + data.AsNoTracking() + .Where(_ => _.Id == id), + async policedData => + { + var node = await policedData.SingleOrDefaultAsync(cancellationToken); + return (node is null ? [] : [node], node); + }, + databaseContext, cancellationToken ); - if (data is null) - { - return null; - } - if (!data.DataAccessRights.HasRestrictions) - { - return data; - } - return await accessRightsService.ApplyAccessRightsOnData(data, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataSortTypeBase.cs b/backend/src/GraphQl/DataX/DataSortTypeBase.cs index c5e92791..5c8d0eba 100644 --- a/backend/src/GraphQl/DataX/DataSortTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataSortTypeBase.cs @@ -13,11 +13,11 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 344c48e6..50ca1d98 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -34,7 +34,7 @@ IInterfaceTypeDescriptor descriptor descriptor .Field(_ => _.CreatedAt); descriptor - .Field(x => x.Locale) + .Field(_ => _.Locale) .Type>(); descriptor .Field(DatabaseIdFieldName) @@ -44,7 +44,7 @@ IInterfaceTypeDescriptor descriptor .Field(ResourceTreeFieldName) .Type>>(); descriptor - .Field(x => x.Approval) + .Field(_ => _.Approval) .Type>>(); descriptor .Field(DataType.DatabaseIdFieldName[..^2].FirstCharToLower()) @@ -56,4 +56,4 @@ IInterfaceTypeDescriptor descriptor .Field(nameof(IData.CreatorId)[..^2].FirstCharToLower()) .Type>(); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 2086c80f..feb39b02 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -105,30 +105,7 @@ await context.Entry(data) resource.DeleteFile(); } - // TODO Would a simple `context.Remove(data);` also work? - switch (data) - { - case CalorimetricData calorimetricData: - context.CalorimetricData.Remove(calorimetricData); - break; - case GeometricData geometricData: - context.GeometricData.Remove(geometricData); - break; - case HygrothermalData hygrothermalData: - context.HygrothermalData.Remove(hygrothermalData); - break; - case LifeCycleData lifeCycleData: - context.LifeCycleData.Remove(lifeCycleData); - break; - case PhotovoltaicData photovoltaicData: - context.PhotovoltaicData.Remove(photovoltaicData); - break; - case OpticalData opticalData: - context.OpticalData.Remove(opticalData); - break; - default: - throw new NotSupportedException($"The data kind '{input.DataKind}' is not supported for deletion."); - } + context.Remove(data); await context.SaveChangesAsync(cancellationToken); return NewPayload(data, null); } diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs index b1d9a664..05b15fe4 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTreeNonRootVertexFilterType.cs @@ -13,11 +13,11 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(GetHttpsResourceTreeNonRootVertexFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.CalorimetricData).Ignore(); - descriptor.Field(x => x.GeometricData).Ignore(); - descriptor.Field(x => x.HygrothermalData).Ignore(); - descriptor.Field(x => x.LifeCycleData).Ignore(); - descriptor.Field(x => x.OpticalData).Ignore(); - descriptor.Field(x => x.PhotovoltaicData).Ignore(); + descriptor.Field(_ => _.CalorimetricData).Ignore(); + descriptor.Field(_ => _.GeometricData).Ignore(); + descriptor.Field(_ => _.HygrothermalData).Ignore(); + descriptor.Field(_ => _.LifeCycleData).Ignore(); + descriptor.Field(_ => _.OpticalData).Ignore(); + descriptor.Field(_ => _.PhotovoltaicData).Ignore(); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index 6991c912..1fa13bd8 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -120,7 +120,7 @@ await context.GetHttpsResources.AsQueryable() .Where(_ => _.GetDataId(input.DataKind) == input.DataId) .ToListAsync(cancellationToken); var resourcesWithoutFile = resources.Where(_ => !_.DoesFileExist()).ToList(); - if (resourcesWithoutFile.Count >= 1) + if (resourcesWithoutFile.Count > 0) { errors.Add( NewError( @@ -130,7 +130,7 @@ await context.GetHttpsResources.AsQueryable() ) ); } - if (errors.Count >= 1) + if (errors.Count > 0) { return NewPayload(null, errors); } @@ -159,4 +159,4 @@ await context.GetHttpsResources.AsQueryable() return NewPayload(data, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Entities/AuditableEntitySortType.cs b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs index 88e613bd..33dd5ed0 100644 --- a/backend/src/GraphQl/Entities/AuditableEntitySortType.cs +++ b/backend/src/GraphQl/Entities/AuditableEntitySortType.cs @@ -13,8 +13,8 @@ ISortInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs index a1d68e0d..85e345d2 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -13,23 +13,23 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(GeometricDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Thicknesses); + descriptor.Field(_ => _.Thicknesses); // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index ba3fcfbe..e13ff683 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -23,7 +23,7 @@ public sealed class GeometricDataQueries public Task> GetAllGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -31,7 +31,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.GeometricData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -43,7 +44,7 @@ CancellationToken cancellationToken public Task> GetAllPendingGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -52,7 +53,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.GeometricData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -63,6 +65,7 @@ CancellationToken cancellationToken public Task HasGeometricDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -70,6 +73,8 @@ CancellationToken cancellationToken return HasDataAsync( context.GeometricData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -78,17 +83,18 @@ CancellationToken cancellationToken public Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, - GeometricDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return GetDataAsync( id, locale, - byId, - accessRightsService, + context.GeometricData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs index 13da19b4..d0984d79 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index dc2ef628..eeb9dffa 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -173,7 +173,7 @@ await context.GetHttpsResources ); } // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. - if (errors.Count >= 1 || dataFormat is null) + if (errors.Count > 0 || dataFormat is null) { return NewPayload(null, errors); } @@ -199,4 +199,4 @@ await context.GetHttpsResources return NewPayload(getHttpsResource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs index 55d2088d..4a7a69d9 100644 --- a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -130,7 +130,7 @@ CancellationToken cancellationToken ) ); } - if (errors.Count >= 1) + if (errors.Count > 0) { return NewPayload(null, errors); } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 312f34d0..3f36b29e 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -13,18 +13,18 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(GetHttpsResourceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.HashValue); - descriptor.Field(x => x.DataFormatId); - descriptor.Field(x => x.AppliedConversionMethod); - descriptor.Field(x => x.ArchivedFilesMetaInformation); - descriptor.Field(x => x.Parent); - // descriptor.Field(x => x.Data); - descriptor.Field(x => x.CalorimetricData); - descriptor.Field(x => x.GeometricData); - descriptor.Field(x => x.HygrothermalData); - descriptor.Field(x => x.LifeCycleData); - descriptor.Field(x => x.OpticalData); - descriptor.Field(x => x.PhotovoltaicData); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.HashValue); + descriptor.Field(_ => _.DataFormatId); + descriptor.Field(_ => _.AppliedConversionMethod); + descriptor.Field(_ => _.ArchivedFilesMetaInformation); + descriptor.Field(_ => _.Parent); + // descriptor.Field(_ => _.Data); + descriptor.Field(_ => _.CalorimetricData); + descriptor.Field(_ => _.GeometricData); + descriptor.Field(_ => _.HygrothermalData); + descriptor.Field(_ => _.LifeCycleData); + descriptor.Field(_ => _.OpticalData); + descriptor.Field(_ => _.PhotovoltaicData); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs index 9504cc60..71af5164 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -12,10 +12,10 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.HashValue); - descriptor.Field(x => x.DataFormatId); - descriptor.Field(x => x.AppliedConversionMethod); - descriptor.Field(x => x.Parent); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.HashValue); + descriptor.Field(_ => _.DataFormatId); + descriptor.Field(_ => _.AppliedConversionMethod); + descriptor.Field(_ => _.Parent); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 9b7fdf6a..25dd8dbf 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -28,57 +28,57 @@ IObjectTypeDescriptor descriptor .Field(_ => _.FileExtension) .Ignore(); descriptor - .Field(x => x.ParentId) + .Field(_ => _.ParentId) .Ignore(); descriptor - .Field(x => x.Parent) + .Field(_ => _.Parent) .Cost(0) .ResolveWith(t => t.GetParent(default!, default!, default!)); descriptor - .Field(x => x.Children) + .Field(_ => _.Children) .Cost(0) .ResolveWith(t => t.GetChildren(default!, default!, default!)); descriptor - .Field(x => x.DataId) + .Field(_ => _.DataId) .Ignore(); descriptor - .Field(x => x.CalorimetricDataId) + .Field(_ => _.CalorimetricDataId) .Ignore(); descriptor - .Field(x => x.CalorimetricData) + .Field(_ => _.CalorimetricData) .Ignore(); descriptor - .Field(x => x.GeometricDataId) + .Field(_ => _.GeometricDataId) .Ignore(); descriptor - .Field(x => x.GeometricData) + .Field(_ => _.GeometricData) .Ignore(); descriptor - .Field(x => x.HygrothermalDataId) + .Field(_ => _.HygrothermalDataId) .Ignore(); descriptor - .Field(x => x.HygrothermalData) + .Field(_ => _.HygrothermalData) .Ignore(); descriptor - .Field(x => x.LifeCycleDataId) + .Field(_ => _.LifeCycleDataId) .Ignore(); descriptor - .Field(x => x.LifeCycleData) + .Field(_ => _.LifeCycleData) .Ignore(); descriptor - .Field(x => x.OpticalDataId) + .Field(_ => _.OpticalDataId) .Ignore(); descriptor - .Field(x => x.OpticalData) + .Field(_ => _.OpticalData) .Ignore(); descriptor - .Field(x => x.PhotovoltaicDataId) + .Field(_ => _.PhotovoltaicDataId) .Ignore(); descriptor - .Field(x => x.PhotovoltaicData) + .Field(_ => _.PhotovoltaicData) .Ignore(); descriptor - .Field(x => x.Data) + .Field(_ => _.Data) .Cost(0) .ResolveWith(t => t.GetData(default!, default!, default!, default!, default!, default!, default!, default!)); @@ -88,4 +88,4 @@ IObjectTypeDescriptor descriptor .Cost(3) .ResolveWith(_ => _.GetDataFormatAsync(default!, default!)); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs index 6b3847e0..c772ec4b 100644 --- a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -138,7 +138,7 @@ await context.GetHttpsResources ); } // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. - if (errors.Count >= 1) + if (errors.Count > 0) { return NewPayload(null, errors); } @@ -165,4 +165,4 @@ await context.GetHttpsResources return NewPayload(resource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index 25325d6a..ef90b146 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -124,7 +124,7 @@ CancellationToken cancellationToken ); } // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. - if (errors.Count >= 1 || dataFormat is null) + if (errors.Count > 0 || dataFormat is null) { return NewPayload(null, errors); } @@ -154,4 +154,4 @@ CancellationToken cancellationToken return NewPayload(resource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index d91a3f4b..7d85cdba 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -112,7 +112,7 @@ CancellationToken cancellationToken errors.AddRange(validateResourceErrors); } // Note that `dataFormat` is only `null`, when `validateResourceResult` failed. - if (errors.Count >= 1 || dataFormat is null) + if (errors.Count > 0 || dataFormat is null) { return NewPayload(null, errors); } @@ -141,4 +141,4 @@ CancellationToken cancellationToken return NewPayload(resource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index fcadf11e..6c383166 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -16,19 +16,19 @@ IFilterInputTypeDescriptor descriptor // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 335dc032..628f2817 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -25,7 +25,7 @@ public sealed class HygrothermalDataQueries public Task> GetAllHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -33,7 +33,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.HygrothermalData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -47,7 +48,7 @@ CancellationToken cancellationToken public Task> GetAllPendingHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -56,7 +57,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.HygrothermalData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -67,6 +69,7 @@ CancellationToken cancellationToken public Task HasHygrothermalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -74,6 +77,8 @@ CancellationToken cancellationToken return HasDataAsync( context.HygrothermalData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -82,17 +87,18 @@ CancellationToken cancellationToken public Task GetHygrothermalDataAsync( Guid id, [GraphQLType] string? locale, - HygrothermalDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return GetDataAsync( id, locale, - byId, - accessRightsService, + context.HygrothermalData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs index 7e32a867..f1de2d25 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs index 34d92e62..cf3a9940 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs @@ -16,19 +16,19 @@ IFilterInputTypeDescriptor descriptor // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index 1f088c6d..67fd8446 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -25,7 +25,7 @@ public sealed class LifeCycleDataQueries public Task> GetAllLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -33,7 +33,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.LifeCycleData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -47,7 +48,7 @@ CancellationToken cancellationToken public Task> GetAllPendingLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -56,7 +57,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.LifeCycleData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -67,6 +69,7 @@ CancellationToken cancellationToken public Task HasLifeCycleDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -74,6 +77,8 @@ CancellationToken cancellationToken return HasDataAsync( context.LifeCycleData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -82,17 +87,18 @@ CancellationToken cancellationToken public Task GetLifeCycleDataAsync( Guid id, [GraphQLType] string? locale, - LifeCycleDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return GetDataAsync( id, locale, - byId, - accessRightsService, + context.LifeCycleData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs index 5dee0d7a..01dd73b5 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index 07deb0f0..f648729f 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -147,7 +147,7 @@ [new CalculateMethodError( ); if (response.Data?.Data is null) { - if (response.Errors?.Length >= 1) + if (response.Errors?.Length > 0) { foreach (var error in response.Errors) { @@ -197,4 +197,4 @@ query Data($id: Uuid!) { } """; } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs index 7c9e7d3b..12eb7188 100644 --- a/backend/src/GraphQl/NamedMethodArgumentFilterType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentFilterType.cs @@ -12,7 +12,7 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(NamedMethodArgumentFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Name); - // TODO descriptor.Field(x => x.Value); + descriptor.Field(_ => _.Name); + // TODO descriptor.Field(_ => _.Value); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/NamedMethodArgumentSortType.cs b/backend/src/GraphQl/NamedMethodArgumentSortType.cs index 133285a3..5bac8f21 100644 --- a/backend/src/GraphQl/NamedMethodArgumentSortType.cs +++ b/backend/src/GraphQl/NamedMethodArgumentSortType.cs @@ -11,7 +11,7 @@ ISortInputTypeDescriptor descriptor ) { descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Name); - // TODO descriptor.Field(x => x.Value); + descriptor.Field(_ => _.Name); + // TODO descriptor.Field(_ => _.Value); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index 94e9e859..fdd7b111 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -13,32 +13,32 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(OpticalDataFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Type); - descriptor.Field(x => x.Subtype); - descriptor.Field(x => x.CoatedSide); - descriptor.Field(x => x.NearnormalHemisphericalSolarReflectances); - descriptor.Field(x => x.NearnormalHemisphericalSolarTransmittances); - descriptor.Field(x => x.NearnormalHemisphericalVisibleReflectances); - descriptor.Field(x => x.NearnormalHemisphericalVisibleTransmittances); - descriptor.Field(x => x.InfraredEmittances); - descriptor.Field(x => x.ColorRenderingIndices); - descriptor.Field(x => x.CielabColors); + descriptor.Field(_ => _.Type); + descriptor.Field(_ => _.Subtype); + descriptor.Field(_ => _.CoatedSide); + descriptor.Field(_ => _.NearnormalHemisphericalSolarReflectances); + descriptor.Field(_ => _.NearnormalHemisphericalSolarTransmittances); + descriptor.Field(_ => _.NearnormalHemisphericalVisibleReflectances); + descriptor.Field(_ => _.NearnormalHemisphericalVisibleTransmittances); + descriptor.Field(_ => _.InfraredEmittances); + descriptor.Field(_ => _.ColorRenderingIndices); + descriptor.Field(_ => _.CielabColors); // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index b9bd9864..73f0a531 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -25,7 +25,7 @@ public sealed class OpticalDataQueries public Task> GetAllOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -33,7 +33,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.OpticalData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -47,7 +48,7 @@ CancellationToken cancellationToken public Task> GetAllPendingOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -56,7 +57,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.OpticalData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -67,6 +69,7 @@ CancellationToken cancellationToken public Task HasOpticalDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -74,6 +77,8 @@ CancellationToken cancellationToken return HasDataAsync( context.OpticalData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -82,17 +87,18 @@ CancellationToken cancellationToken public Task GetOpticalDataAsync( Guid id, [GraphQLType] string? locale, - OpticalDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return GetDataAsync( id, locale, - byId, - accessRightsService, + context.OpticalData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs index 0746cd41..f90b2c7f 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index fb479dc8..efc18378 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -16,19 +16,19 @@ IFilterInputTypeDescriptor descriptor // TODO Why are the fields below not included by `base.Configure` above? // AuditableEntityFilterType.Configure - descriptor.Field(x => x.Id); - descriptor.Field(x => x.CreatedAt); - descriptor.Field(x => x.UpdatedAt); + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); // DataFilterTypeBase.Configure - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 1f377806..a1b02c50 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -26,7 +26,7 @@ public sealed class PhotovoltaicDataQueries public Task> GetAllPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -34,7 +34,8 @@ CancellationToken cancellationToken return GetAllDataAsync( context.PhotovoltaicData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -48,7 +49,7 @@ CancellationToken cancellationToken public Task> GetAllPendingPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, - AccessRightsService accessRightsService, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, CancellationToken cancellationToken @@ -57,7 +58,8 @@ CancellationToken cancellationToken return GetAllPendingDataAsync( context.PhotovoltaicData, locale, - accessRightsService, + context, + accessPolicyService, resolverContext, authorization, cancellationToken @@ -68,6 +70,7 @@ CancellationToken cancellationToken public Task HasPhotovoltaicDataAsync( [GraphQLType] string? locale, ApplicationDbContext context, + AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -75,6 +78,8 @@ CancellationToken cancellationToken return HasDataAsync( context.PhotovoltaicData, locale, + context, + accessPolicyService, resolverContext, cancellationToken ); @@ -83,8 +88,8 @@ CancellationToken cancellationToken public Task GetPhotovoltaicDataAsync( Guid id, [GraphQLType] string? locale, - PhotovoltaicDataByIdDataLoader byId, - AccessRightsService accessRightsService, + ApplicationDbContext context, + AccessPolicyService accessPolicyService, IClock clock, CancellationToken cancellationToken ) @@ -92,9 +97,10 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - byId, - accessRightsService, + context.PhotovoltaicData, + context, + accessPolicyService, cancellationToken ); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs index 35395876..2beaa4d7 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataSortType.cs @@ -12,6 +12,6 @@ ISortInputTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor.Field(x => x.Id); + descriptor.Field(_ => _.Id); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Publications/PublicationFilterType.cs b/backend/src/GraphQl/Publications/PublicationFilterType.cs index 974e7a90..98ba9228 100644 --- a/backend/src/GraphQl/Publications/PublicationFilterType.cs +++ b/backend/src/GraphQl/Publications/PublicationFilterType.cs @@ -10,7 +10,7 @@ protected override void Configure( IFilterInputTypeDescriptor descriptor ) { - descriptor.Field(x => x.Exists).Ignore(); + descriptor.Field(_ => _.Exists).Ignore(); descriptor.Name(nameof(PublicationFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Publications/PublicationSortType.cs b/backend/src/GraphQl/Publications/PublicationSortType.cs index 4a27f84f..1b1f8d04 100644 --- a/backend/src/GraphQl/Publications/PublicationSortType.cs +++ b/backend/src/GraphQl/Publications/PublicationSortType.cs @@ -10,6 +10,6 @@ protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.Field(x => x.Exists).Ignore(); + descriptor.Field(_ => _.Exists).Ignore(); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/References/ReferenceFilterType.cs b/backend/src/GraphQl/References/ReferenceFilterType.cs index 7116b7cc..661ae745 100644 --- a/backend/src/GraphQl/References/ReferenceFilterType.cs +++ b/backend/src/GraphQl/References/ReferenceFilterType.cs @@ -13,7 +13,7 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(ReferenceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.Standard); - descriptor.Field(x => x.Publication); + descriptor.Field(_ => _.Standard); + descriptor.Field(_ => _.Publication); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/References/ReferenceSortType.cs b/backend/src/GraphQl/References/ReferenceSortType.cs index 8413910f..2c613290 100644 --- a/backend/src/GraphQl/References/ReferenceSortType.cs +++ b/backend/src/GraphQl/References/ReferenceSortType.cs @@ -11,7 +11,7 @@ ISortInputTypeDescriptor descriptor ) { descriptor.BindFieldsExplicitly(); - descriptor.Field(x => x.Standard); - descriptor.Field(x => x.Publication); + descriptor.Field(_ => _.Standard); + descriptor.Field(_ => _.Publication); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index ca198b36..e5a75ca1 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -13,15 +13,15 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(ResponseApprovalFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); - descriptor.Field(x => x.UserId); - descriptor.Field(x => x.Locale); - descriptor.Field(x => x.Name); - descriptor.Field(x => x.Description); - descriptor.Field(x => x.ComponentId); - descriptor.Field(x => x.CreatorId); - descriptor.Field(x => x.AppliedMethod); - descriptor.Field(x => x.Approvals); - descriptor.Field(x => x.Resources); - descriptor.Field(x => x.Warnings); + descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.Locale); + descriptor.Field(_ => _.Name); + descriptor.Field(_ => _.Description); + descriptor.Field(_ => _.ComponentId); + descriptor.Field(_ => _.CreatorId); + descriptor.Field(_ => _.AppliedMethod); + descriptor.Field(_ => _.Approvals); + descriptor.Field(_ => _.Resources); + descriptor.Field(_ => _.Warnings); } -} \ No newline at end of file +} diff --git a/backend/src/Migrations/20260603191121_RevampAccessPolicies.Designer.cs b/backend/src/Migrations/20260603191121_RevampAccessPolicies.Designer.cs new file mode 100644 index 00000000..876ed306 --- /dev/null +++ b/backend/src/Migrations/20260603191121_RevampAccessPolicies.Designer.cs @@ -0,0 +1,3525 @@ +// +using System; +using System.Collections.Generic; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260603191121_RevampAccessPolicies")] + partial class RevampAccessPolicies + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("InstitutionId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("InstitutionId"); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("ClientId") + .HasColumnType("text"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("ClientId"); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("UserId"); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + + t.HasTrigger("data_id_cannot_change"); + + t.HasTrigger("data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("gen_random_uuid()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs b/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs new file mode 100644 index 00000000..f7d39327 --- /dev/null +++ b/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs @@ -0,0 +1,407 @@ +using System; +using System.Collections.Generic; +using Database.Data.AccessPolicies; +using Microsoft.EntityFrameworkCore.Migrations; +using NodaTime; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class RevampAccessPolicies : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "institution_access_rights", + schema: "database"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.logical_combinator", "all,some"); + + migrationBuilder.CreateTable( + name: "institution_access_policy", + schema: "database", + columns: table => new + { + InstitutionId = table.Column(type: "uuid", nullable: false), + AccessCountSinceStartTime = table.Column(type: "jsonb", nullable: true), + UpperAccessLimitPerTimeDuration = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_institution_access_policy", x => x.InstitutionId); + }); + + migrationBuilder.CreateTable( + name: "open_id_connect_application_access_policy", + schema: "database", + columns: table => new + { + ClientId = table.Column(type: "text", nullable: false), + AccessCountSinceStartTime = table.Column(type: "jsonb", nullable: true), + UpperAccessLimitPerTimeDuration = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_open_id_connect_application_access_policy", x => x.ClientId); + }); + + migrationBuilder.CreateTable( + name: "user_access_policy", + schema: "database", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + AccessCountSinceStartTime = table.Column(type: "jsonb", nullable: true), + UpperAccessLimitPerTimeDuration = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_user_access_policy", x => x.UserId); + }); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "lifeCycle_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "AccessPolicy", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "institution_access_policy", + schema: "database"); + + migrationBuilder.DropTable( + name: "open_id_connect_application_access_policy", + schema: "database"); + + migrationBuilder.DropTable( + name: "user_access_policy", + schema: "database"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AlterDatabase().OldAnnotation("Npgsql:Enum:database.logical_combinator", "all,some"); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "lifeCycle_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn>( + name: "DataAccessRights_AllowedUserAndQuantity", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "photovoltaic_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "photovoltaic_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "optical_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "optical_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "lifeCycle_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "lifeCycle_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "hygrothermal_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "hygrothermal_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "geometric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "geometric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedApplications", + schema: "database", + table: "calorimetric_data", + type: "text[]", + nullable: true); + + migrationBuilder.AddColumn( + name: "DataAccessRights_AllowedInstitutions", + schema: "database", + table: "calorimetric_data", + type: "uuid[]", + nullable: true); + + migrationBuilder.CreateTable( + name: "institution_access_rights", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "gen_random_uuid()"), + AllowedDatasetsPerTime = table.Column(type: "bigint", nullable: true), + AllowedUserCount = table.Column(type: "bigint", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + InstitutionId = table.Column(type: "uuid", nullable: false), + Period = table.Column(type: "interval", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UserAlreadyAccessed = table.Column>(type: "uuid[]", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_institution_access_rights", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_institution_access_rights_CreatedAt_Id", + schema: "database", + table: "institution_access_rights", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_institution_access_rights_InstitutionId", + schema: "database", + table: "institution_access_rights", + column: "InstitutionId", + unique: true); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 1514594a..5d6c903e 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -6,9 +6,7 @@ using Database.Enumerations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using NodaTime; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -29,6 +27,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); @@ -36,6 +35,104 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("InstitutionId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("InstitutionId"); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("ClientId") + .HasColumnType("text"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("ClientId"); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => + { + b1.Property("AccessCount"); + + b1.Property("StartTime"); + + b1 + .ToJson("AccessCountSinceStartTime") + .HasColumnType("jsonb"); + }); + + b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + { + b1.Property("Duration"); + + b1.Property("UpperLimit"); + + b1 + .ToJson("UpperAccessLimitPerTimeDuration") + .HasColumnType("jsonb"); + }); + + b.HasKey("UserId"); + + b.ToTable("user_access_policy", "database"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.Property("Id") @@ -93,6 +190,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -162,6 +326,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -327,62 +558,79 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); - b.HasKey("Id"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); - b.HasIndex("CreatedAt", "Id") - .IsUnique(); + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); - b.ToTable("hygrothermal_data", "database"); - }); + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); - modelBuilder.Entity("Database.Data.InstitutionAccessRights", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + b3.Property("StartTime"); + }); - b.Property("AllowedDatasetsPerTime") - .HasColumnType("bigint"); + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); - b.Property("AllowedUserCount") - .HasColumnType("bigint"); + b3.Property("UpperLimit"); + }); + }); - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); - b.Property("InstitutionId") - .HasColumnType("uuid"); + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); - b.Property("Period") - .HasColumnType("interval"); + b3.Property("StartTime"); + }); - b.Property("UpdatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); - b.PrimitiveCollection>("UserAlreadyAccessed") - .IsRequired() - .HasColumnType("uuid[]"); + b3.Property("UpperLimit"); + }); + }); - b.Property("Version") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid") - .HasColumnName("xmin"); + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); - b.HasKey("Id"); + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); - b.HasIndex("InstitutionId") - .IsUnique(); + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("institution_access_rights", "database"); + b.ToTable("hygrothermal_data", "database"); }); modelBuilder.Entity("Database.Data.LifeCycleData", b => @@ -434,6 +682,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -524,6 +839,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -581,6 +963,73 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); + b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy", b1 => + { + b1.Property("Combinator"); + + b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => + { + b2.Property("InstitutionId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => + { + b2.Property("ClientId") + .IsRequired(); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => + { + b2.Property("UserId"); + + b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => + { + b3.Property("AccessCount"); + + b3.Property("StartTime"); + }); + + b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => + { + b3.Property("Duration"); + + b3.Property("UpperLimit"); + }); + }); + + b1 + .ToJson("AccessPolicy") + .HasColumnType("jsonb"); + }); + b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -1176,37 +1625,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("CalorimetricDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("CalorimetricDataId"); - - b1.ToTable("calorimetric_data", "database"); - - b1.WithOwner() - .HasForeignKey("CalorimetricDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("Database.Data.GeometricData", b => @@ -1530,37 +1954,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("GeometricDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("GeometricDataId"); - - b1.ToTable("geometric_data", "database"); - - b1.WithOwner() - .HasForeignKey("GeometricDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("Database.Data.GetHttpsResource", b => @@ -2013,37 +2412,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("HygrothermalDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("HygrothermalDataId"); - - b1.ToTable("hygrothermal_data", "database"); - - b1.WithOwner() - .HasForeignKey("HygrothermalDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("Database.Data.LifeCycleData", b => @@ -2367,37 +2741,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("LifeCycleDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("LifeCycleDataId"); - - b1.ToTable("lifeCycle_data", "database"); - - b1.WithOwner() - .HasForeignKey("LifeCycleDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -2758,28 +3107,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("OpticalDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("OpticalDataId"); - - b1.ToTable("optical_data", "database"); - - b1.WithOwner() - .HasForeignKey("OpticalDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); @@ -2788,9 +3115,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Approvals"); b.Navigation("CielabColors"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => @@ -3114,37 +3438,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); - b.OwnsOne("Database.Data.DataAccessRights", "DataAccessRights", b1 => - { - b1.Property("PhotovoltaicDataId") - .HasColumnType("uuid"); - - b1.PrimitiveCollection("AllowedApplications") - .HasColumnType("text[]"); - - b1.PrimitiveCollection("AllowedInstitutions") - .HasColumnType("uuid[]"); - - b1.Property("AllowedUserAndQuantity") - .HasColumnType("jsonb"); - - b1.HasKey("PhotovoltaicDataId"); - - b1.ToTable("photovoltaic_data", "database"); - - b1.WithOwner() - .HasForeignKey("PhotovoltaicDataId"); - }); - b.Navigation("AppliedMethod") .IsRequired(); b.Navigation("Approval"); b.Navigation("Approvals"); - - b.Navigation("DataAccessRights") - .IsRequired(); }); modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => @@ -3220,4 +3519,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index dc11d21f..56955370 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -2247,3 +2247,358 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TABLE database.institution_access_rights; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.photovoltaic_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.optical_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database."lifeCycle_data" ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.hygrothermal_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.geometric_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.calorimetric_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TYPE database.logical_combinator AS ENUM ('all', 'some'); + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE EXTENSION IF NOT EXISTS pgcrypto; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.institution_access_policy ( + "InstitutionId" uuid NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_institution_access_policy" PRIMARY KEY ("InstitutionId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.open_id_connect_application_access_policy ( + "ClientId" text NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_open_id_connect_application_access_policy" PRIMARY KEY ("ClientId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.user_access_policy ( + "UserId" uuid NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_user_access_policy" PRIMARY KEY ("UserId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260603191121_RevampAccessPolicies', '10.0.8'); + END IF; +END $EF$; +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_lifeCycle_data_LifeCycleDataId"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + DROP TABLE database."lifeCycle_data_Approvals"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + DROP TABLE database."lifeCycle_data_Arguments"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + DROP TABLE database."lifeCycle_data_Sources"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER TABLE database."lifeCycle_data" DROP CONSTRAINT "PK_lifeCycle_data"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER TABLE database."lifeCycle_data" RENAME TO life_cycle_data; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER INDEX database."IX_lifeCycle_data_CreatedAt_Id" RENAME TO "IX_life_cycle_data_CreatedAt_Id"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER TABLE database.life_cycle_data ADD CONSTRAINT "PK_life_cycle_data" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + CREATE TABLE database."life_cycle_data_Approvals" ( + "LifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "ApproverId" uuid NOT NULL, + "Timestamp" timestamp with time zone NOT NULL, + "Signature" text NOT NULL, + "KeyFingerprint" text NOT NULL, + "Query" text NOT NULL, + "Variables" jsonb NOT NULL DEFAULT ('{}'), + "Message" text NOT NULL, + "Statement_Standard_Year" integer, + "Statement_Standard_Numeration_Prefix" text, + "Statement_Standard_Numeration_MainNumber" text, + "Statement_Standard_Numeration_Suffix" text, + "Statement_Standard_Standardizers" database.standardizer[], + "Statement_Standard_Locator" text, + "Statement_Standard_Title" text, + "Statement_Standard_Abstract" text, + "Statement_Standard_Section" text, + "Statement_Publication_Authors" text[], + "Statement_Publication_Doi" text, + "Statement_Publication_ArXiv" text, + "Statement_Publication_Urn" text, + "Statement_Publication_WebAddress" text, + "Statement_Publication_Title" text, + "Statement_Publication_Abstract" text, + "Statement_Publication_Section" text, + "Statement_Publication_Exists" boolean, + "Statement_Exists" boolean NOT NULL, + CONSTRAINT "PK_life_cycle_data_Approvals" PRIMARY KEY ("LifeCycleDataId", "Id"), + CONSTRAINT "FK_life_cycle_data_Approvals_life_cycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + CREATE TABLE database."life_cycle_data_Arguments" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value" jsonb NOT NULL, + CONSTRAINT "PK_life_cycle_data_Arguments" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_life_cycle_data_Arguments_life_cycle_data_AppliedMethodLife~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + CREATE TABLE database."life_cycle_data_Sources" ( + "AppliedMethodLifeCycleDataId" uuid NOT NULL, + "Id" integer GENERATED BY DEFAULT AS IDENTITY, + "Name" text NOT NULL, + "Value_DataId" uuid NOT NULL, + "Value_DataTimestamp" timestamp with time zone NOT NULL, + "Value_DataKind" database.data_kind NOT NULL, + "Value_DatabaseId" uuid NOT NULL, + CONSTRAINT "PK_life_cycle_data_Sources" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), + CONSTRAINT "FK_life_cycle_data_Sources_life_cycle_data_AppliedMethodLifeCy~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_life_cycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260603194435_FixCasingOfLifeCycleDataTableName', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260527161543_MakeEntitiesAuditable_to_20260603191121_RevampAccessPolicies.sql b/backend/src/Migrations/migrate_from_20260527161543_MakeEntitiesAuditable_to_20260603191121_RevampAccessPolicies.sql new file mode 100644 index 00000000..121c37b4 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260527161543_MakeEntitiesAuditable_to_20260603191121_RevampAccessPolicies.sql @@ -0,0 +1,212 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TABLE database.institution_access_rights; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedApplications"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedInstitutions"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.photovoltaic_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.optical_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database."lifeCycle_data" ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.hygrothermal_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.geometric_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "DataAccessRights_AllowedUserAndQuantity"; + ALTER TABLE database.calorimetric_data ADD COLUMN "AccessPolicy" jsonb; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TYPE database.logical_combinator AS ENUM ('all', 'some'); + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE EXTENSION IF NOT EXISTS pgcrypto; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.institution_access_policy ( + "InstitutionId" uuid NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_institution_access_policy" PRIMARY KEY ("InstitutionId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.open_id_connect_application_access_policy ( + "ClientId" text NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_open_id_connect_application_access_policy" PRIMARY KEY ("ClientId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.user_access_policy ( + "UserId" uuid NOT NULL, + "AccessCountSinceStartTime" jsonb, + "UpperAccessLimitPerTimeDuration" jsonb, + CONSTRAINT "PK_user_access_policy" PRIMARY KEY ("UserId") + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260603191121_RevampAccessPolicies', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260603191121_RevampAccessPolicies_to_20260527161543_MakeEntitiesAuditable.sql b/backend/src/Migrations/rollback_from_20260603191121_RevampAccessPolicies_to_20260527161543_MakeEntitiesAuditable.sql new file mode 100644 index 00000000..5bd48cd9 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260603191121_RevampAccessPolicies_to_20260527161543_MakeEntitiesAuditable.sql @@ -0,0 +1,179 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TABLE database.institution_access_policy; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TABLE database.open_id_connect_application_access_policy; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TABLE database.user_access_policy; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data RENAME COLUMN "AccessPolicy" TO "DataAccessRights_AllowedUserAndQuantity"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DROP TYPE database.logical_combinator; + IF NOT EXISTS(SELECT 1 FROM pg_namespace WHERE nspname = 'database') THEN + CREATE SCHEMA database; + END IF; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE EXTENSION IF NOT EXISTS pgcrypto; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.optical_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.geometric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedApplications" text[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ADD "DataAccessRights_AllowedInstitutions" uuid[]; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE TABLE database.institution_access_rights ( + "Id" uuid NOT NULL DEFAULT (gen_random_uuid()), + "AllowedDatasetsPerTime" bigint, + "AllowedUserCount" bigint, + "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + "InstitutionId" uuid NOT NULL, + "Period" interval NOT NULL, + "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + "UserAlreadyAccessed" uuid[] NOT NULL, + CONSTRAINT "PK_institution_access_rights" PRIMARY KEY ("Id") + ); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_rights_CreatedAt_Id" ON database.institution_access_rights ("CreatedAt", "Id"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_rights_InstitutionId" ON database.institution_access_rights ("InstitutionId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603191121_RevampAccessPolicies') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260603191121_RevampAccessPolicies'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 2f644af0..a3dc2a8c 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -159,10 +159,10 @@ private static void EnsureDatabaseIsUpToDate( IServiceProvider services ) { - using var dbContext = + using var databaseContext = services.GetRequiredService>() .CreateDbContext(); - var pendingMigrations = dbContext.Database.GetPendingMigrations(); + var pendingMigrations = databaseContext.Database.GetPendingMigrations(); if (pendingMigrations.Any()) { throw new InvalidOperationException($"The database is not up to date. The pending migrations are: {string.Join(", ", pendingMigrations)}. Apply them by running `./database.mk migrate`."); @@ -175,10 +175,10 @@ IServiceProvider services { try { - using var dbContext = + using var databaseContext = services.GetRequiredService>() .CreateDbContext(); - await dbContext.Database.EnsureCreatedAsync(); + await databaseContext.Database.EnsureCreatedAsync(); } catch (Exception exception) { @@ -275,4 +275,4 @@ string[] commandLineArguments "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables .AddCommandLine(commandLineArguments); } -} \ No newline at end of file +} diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs new file mode 100644 index 00000000..5078ec2d --- /dev/null +++ b/backend/src/Services/AccessPolicyService.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using NodaTime; +using Database.Data; +using Database.Enumerations; +using Database.ApiRequests; + +namespace Database.Services; + +public sealed class AccessPolicyService( + UserService userService, + IClock clock +) +{ + public async Task Apply( + IQueryable data, + Func, Task<(IReadOnlyList Data, TResult Result)>> then, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + where TData : class, IData + { + var openIdConnectClientId = userService.GetOpenIdConnectAuthorizedPartyClientId(); + var (currentUser, currentInstitution) = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); + IReadOnlyList? institutionIds = null; + if (currentUser is not null) + { + institutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); + } + else if (currentInstitution is not null) + { + institutionIds = [currentInstitution.Uuid]; + } + var filteredData = FilterData(data, openIdConnectClientId, currentUser, institutionIds, databaseContext); + var (postProcessedData, result) = await then(filteredData); + await IncrementAccessCounts( + postProcessedData, + openIdConnectClientId, + currentUser, + institutionIds, + databaseContext, + cancellationToken + ); + return result; + } + + private static IQueryable FilterData( + IQueryable data, + string? openIdConnectClientId, + QueryCurrentUserOrInstitution.CurrentUser? currentUser, + IReadOnlyList? institutionIds, + ApplicationDbContext databaseContext + ) + where TData : class, IData + { + return data.Where(_ => + _.AccessPolicy == null || !_.AccessPolicy.IsRestricted || ( + _.AccessPolicy.Combinator == LogicalCombinator.ALL && ( + (!_.AccessPolicy.AreUsersRestricted || ( + currentUser != null + && _.AccessPolicy.UserAccessPolicies != null + && _.AccessPolicy.UserAccessPolicies + .Where(_ => _.UserId == currentUser.Uuid) + .Any(_ => _.IsAllowed) + ) + ) + && + (!_.AccessPolicy.AreInstitutionsRestricted || ( + institutionIds != null + && _.AccessPolicy.InstitutionAccessPolicies != null + && _.AccessPolicy.InstitutionAccessPolicies + .Where(_ => institutionIds.Contains(_.InstitutionId)) + .Any(_ => _.IsAllowed) + ) + ) + && + (!_.AccessPolicy.AreOpenIdConnectApplicationsRestricted || ( + openIdConnectClientId != null + && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies != null + && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies + .Where(_ => _.ClientId == openIdConnectClientId) + .Any(_ => _.IsAllowed) + ) + ) + ) + || + _.AccessPolicy.Combinator == LogicalCombinator.SOME && ( + (!_.AccessPolicy.AreUsersRestricted || ( + currentUser != null + && _.AccessPolicy.UserAccessPolicies != null + && _.AccessPolicy.UserAccessPolicies + .Where(_ => _.UserId == currentUser.Uuid) + .Any(_ => _.IsAllowed) + ) + ) + || + (!_.AccessPolicy.AreInstitutionsRestricted || ( + institutionIds != null + && _.AccessPolicy.InstitutionAccessPolicies != null + && _.AccessPolicy.InstitutionAccessPolicies + .Where(_ => institutionIds.Contains(_.InstitutionId)) + .Any(_ => _.IsAllowed) + ) + ) + || + (!_.AccessPolicy.AreOpenIdConnectApplicationsRestricted || ( + openIdConnectClientId != null + && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies != null + && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies + .Where(_ => _.ClientId == openIdConnectClientId) + .Any(_ => _.IsAllowed) + ) + ) + ) + ) + && + ( + !databaseContext.UserAccessPolicies.AsQueryable().Any() || ( + currentUser != null + && databaseContext.UserAccessPolicies.AsQueryable() + .Where(_ => _.UserId == currentUser.Uuid) + .Any(_ => _.IsAllowed) + ) + ) + && + ( + !databaseContext.InstitutionAccessPolicies.AsQueryable().Any() || ( + institutionIds != null + && databaseContext.InstitutionAccessPolicies.AsQueryable() + .Where(_ => institutionIds.Contains(_.InstitutionId)) + .Any(_ => _.IsAllowed) + ) + ) + && + ( + !databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable().Any() || ( + openIdConnectClientId != null + && databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable() + .Where(_ => _.ClientId == openIdConnectClientId) + .Any(_ => _.IsAllowed) + ) + ) + ); + + } + + private async Task IncrementAccessCounts( + IReadOnlyList allData, + string? openIdConnectClientId, + QueryCurrentUserOrInstitution.CurrentUser? currentUser, + IReadOnlyList? institutionIds, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + where TData : class, IData + { + foreach (var data in allData) + { + databaseContext.Attach(data); + if (currentUser is not null) + { + foreach (var userAccessPolicy in + data.AccessPolicy?.UserAccessPolicies + ?.Where(_ => _.UserId == currentUser.Uuid) + ?? [] + ) + { + userAccessPolicy?.IncrementAccessCount(clock); + } + } + if (institutionIds is not null) + { + foreach (var institutionAccessPolicy in + data.AccessPolicy?.InstitutionAccessPolicies + ?.Where(_ => institutionIds.Contains(_.InstitutionId)) + ?? [] + ) + { + institutionAccessPolicy?.IncrementAccessCount(clock); + } + } + if (openIdConnectClientId is not null) + { + foreach (var applicationAccessPolicy in + data.AccessPolicy?.OpenIdConnectApplicationAccessPolicies + ?.Where(_ => _.ClientId == openIdConnectClientId) + ?? [] + ) + { + applicationAccessPolicy?.IncrementAccessCount(clock); + } + } + } + if (currentUser is not null) + { + foreach (var userAccessPolicy in + await databaseContext.UserAccessPolicies.AsQueryable() + .Where(_ => _.UserId == currentUser.Uuid) + .ToListAsync(cancellationToken) + ) + { + userAccessPolicy?.IncrementAccessCount(clock); + } + } + if (institutionIds is not null) + { + foreach (var institutionAccessPolicy in + await databaseContext.InstitutionAccessPolicies.AsQueryable() + .Where(_ => institutionIds.Contains(_.InstitutionId)) + .ToListAsync(cancellationToken) + ) + { + institutionAccessPolicy.IncrementAccessCount(clock); + } + } + if (openIdConnectClientId is not null) + { + foreach (var applicationAccessPolicy in + await databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable() + .Where(_ => _.ClientId == openIdConnectClientId) + .ToListAsync(cancellationToken) + ) + { + applicationAccessPolicy?.IncrementAccessCount(clock); + } + } + await databaseContext.SaveChangesAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/backend/src/Services/AccessRightsService.cs b/backend/src/Services/AccessRightsService.cs deleted file mode 100644 index 2f3c1f5d..00000000 --- a/backend/src/Services/AccessRightsService.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Logging; -using NodaTime; -using static Database.ApiRequests.QueryCurrentUserOrInstitution; - -namespace Database.Services; - -public static partial class Log -{ - [LoggerMessage( - Level = LogLevel.Information, - Message = "Cannot apply access rights because the user or institution is unknown. The fetched user ID is {UserId} and the institution ID is {InstitutionId}.")] - public static partial void UnknownUserOrInstitution(this ILogger logger, Guid? userId, string? institutionId); - - [LoggerMessage( - Level = LogLevel.Debug, - Message = "Restricted Item Id: {Id} Reason: {Reason}")] - public static partial void DataRestriction(this ILogger logger, Guid Id, string reason); -} - -public sealed class AccessRightsService( - IDbContextFactory dbContextFactory, - UserService userService, - CacheService cacheService, - IClock clock, - ILogger logger -) -{ - public async Task ApplyAccessRightsOnData(T data, CancellationToken cancellationToken) - where T : IData - { - return ( - await ApplyAccessRightsOnData([data], cancellationToken) - ).SingleOrDefault(); - } - - public async Task> ApplyAccessRightsOnData( - IEnumerable data, - CancellationToken cancellationToken - ) - where T : IData - { - ApplicationDbContext? context = null; - uint? alreadyAccessedByUserCount = null; - IReadOnlyList? institutionIds = null; - IReadOnlyList? institutionAccessRights = null; - - var currentUserOrInstitution = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); - var openIdConnectClientId = userService.GetOpenIdConnectClientId(); - - if (currentUserOrInstitution.CurrentUser is not null) - { - context = dbContextFactory.CreateDbContext(); - alreadyAccessedByUserCount = cacheService.GetAccessCountForUser(currentUserOrInstitution.CurrentUser.Uuid); - institutionIds = currentUserOrInstitution.CurrentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); - institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); - } - else if (currentUserOrInstitution.CurrentInstitution is not null) - { - context = dbContextFactory.CreateDbContext(); - institutionIds = [currentUserOrInstitution.CurrentInstitution.Uuid]; - institutionAccessRights = await GetInstitutionAccessRightsAsync(institutionIds, context, cancellationToken); - } - - var result = ProcessData(data, currentUserOrInstitution.CurrentUser, openIdConnectClientId, institutionIds, institutionAccessRights, alreadyAccessedByUserCount); - - if (context is not null) - { - // Save InstitutionAccessRight changes - await context.SaveChangesAsync(cancellationToken); - } - return result; - } - - private static async Task> GetInstitutionAccessRightsAsync( - IReadOnlyList institutionIds, - ApplicationDbContext context, - CancellationToken cancellationToken - ) - { - return ( - await context.InstitutionAccessRights.AsQueryable() - .Where(x => institutionIds.Contains(x.InstitutionId)) - .ToListAsync(cancellationToken) - ) - .AsReadOnly(); - } - - private IEnumerable ProcessData( - IEnumerable data, - CurrentUser? currentUser, - string? openIdConnectClientId, - IReadOnlyList? institutionIds, - IReadOnlyList? institutionAccessRights, - uint? alreadyAccessedByUserCount - ) - where T : IData - { - foreach (var dataItem in data) - { - if (dataItem.DataAccessRights.HasRestrictions) - { - if (dataItem.DataAccessRights.HasRestrictionsByApplication - && ( - openIdConnectClientId is null - || dataItem.IsRestrictedByApplication(openIdConnectClientId) - ) - ) - { - logger.DataRestriction(dataItem.Id, "Application not allowed."); - continue; - } - if (dataItem.DataAccessRights.HasRestrictionsByInstitution - && ( - institutionIds is null - || dataItem.IsRestrictedByInstitutions(institutionIds) - ) - ) - { - logger.DataRestriction(dataItem.Id, "Institution not allowed."); - continue; - } - if (dataItem.DataAccessRights.HasRestrictionsByUser - && ( - currentUser is null - || alreadyAccessedByUserCount is null - || dataItem.IsRestrictedByUser(currentUser.Uuid, alreadyAccessedByUserCount ?? 0) - ) - ) - { - logger.DataRestriction(dataItem.Id, "Allowed accesses for user reached."); - continue; - } - if (currentUser is not null && alreadyAccessedByUserCount is not null) - { - alreadyAccessedByUserCount = alreadyAccessedByUserCount + 1; - cacheService.SetAccessCountForUser(currentUser.Uuid, alreadyAccessedByUserCount ?? 0); - } - var reason = ""; - if (institutionAccessRights is not null - && institutionAccessRights.Any(x => - ( - x.HasRestrictionsByTime - && x.IsDataRestrictedByTime(dataItem, clock, cacheService, out reason) - ) - || ( - x.HasRestrictionsByUser - && ( - currentUser is null - || x.IsDataRestrictedByUser(dataItem, currentUser.Uuid, out reason) - ) - ) - ) - ) - { - logger.DataRestriction(dataItem.Id, reason); - continue; - } - } - yield return dataItem; - } - } -} \ No newline at end of file diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 0a4dc806..79a57c91 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,64 +1,24 @@ using System; using System.Diagnostics.CodeAnalysis; -using Database.Extensions; using Microsoft.Extensions.Caching.Memory; -using NodaTime; using static Database.ApiRequests.QueryCurrentUserOrInstitution; namespace Database.Services; public sealed class CacheService( - IMemoryCache currentUserOrInstitutionCache, - IMemoryCache accessCountCache, - IMemoryCache timePeriodCountCache, - IClock clock + IMemoryCache currentUserOrInstitutionCache ) { - public CurrentUserOrInstitution? SetCurrentUserOrInstitution(string token, CurrentUserOrInstitution cachedUserOrInstitution) + public CurrentUserOrInstitution? SetCurrentUserOrInstitution(string token, CurrentUserOrInstitution userOrInstitution) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - return currentUserOrInstitutionCache.Set(token, cachedUserOrInstitution, cacheEntryOptions); + return currentUserOrInstitutionCache.Set(token, userOrInstitution, cacheEntryOptions); } public bool TryGetCurrentUserOrInstitution(string token, [NotNullWhen(true)] out CurrentUserOrInstitution? cachedUserOrInstitution) { return currentUserOrInstitutionCache.TryGetValue(token, out cachedUserOrInstitution); } - - public uint GetAccessCountForUser(Guid userId) - { - if (!accessCountCache.TryGetValue(userId, out uint count)) - { - return accessCountCache.Set(userId, 0); - } - return count; - } - - public uint SetAccessCountForUser(Guid userId, uint count) - { - return accessCountCache.Set(userId, count); - } - - public (OffsetDateTime StartTime, uint Count) GetOrCreateAccessCountForPeriod(Guid institutionId) - { - if (!timePeriodCountCache.TryGetValue(institutionId, out (OffsetDateTime StartTime, uint Count) accessesPerPeriod)) - { - return timePeriodCountCache.Set(institutionId, (clock.GetUtcNow(), (uint)0)); - } - return accessesPerPeriod; - } - - public (OffsetDateTime StartTime, uint Count) AddAccessCountToPeriod(Guid institutionId) - { - var accessesPerPeriod = GetOrCreateAccessCountForPeriod(institutionId); - accessesPerPeriod.Count++; - return timePeriodCountCache.Set(institutionId, accessesPerPeriod); - } - - public (OffsetDateTime StartTime, uint Count) SetNewTimePeriod(Guid institutionId) - { - return timePeriodCountCache.Set(institutionId, (clock.GetUtcNow(), (uint)0)); - } } \ No newline at end of file diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index a5840469..94471a73 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -92,7 +92,7 @@ public async Task CreateResponseApproval(IData dataObject, Can logger.QueryAndVariablesAndResponse(query, variables, response); var typedResponse = JsonSerializer.Deserialize>(response, JsonSerializerSettings.GraphQl) ?? throw new JsonException($"Failed to deserialize the GraphQL response: {response}"); - if (typedResponse.Errors is not null && typedResponse.Errors.Length >= 1) + if (typedResponse.Errors is not null && typedResponse.Errors.Length > 0) { throw new GraphQLException($"The GraphQL response contains the following errors: {string.Join(", ", typedResponse.Errors.Select(_ => $"'{_.Message}' [{string.Join(", ", _.Extensions?.Select(e => $"{e.Key}: '{e.Value}'") ?? [])}] ({string.Join(".", _.Path?.Select(p => p.ToString()) ?? [])})"))}"); } @@ -140,4 +140,4 @@ CancellationToken cancellationToken var response = executionResult.ToJson(withIndentations: false); return (query, JsonSerializer.SerializeToElement(variables), response); } -} \ No newline at end of file +} diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 60337b8c..ae430a1f 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -20,7 +20,7 @@ CacheService cacheService /// /// Get client ID from user claims. /// - public string? GetOpenIdConnectClientId() + public string? GetOpenIdConnectAuthorizedPartyClientId() { return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); } diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index baad7ffb..44a5b01b 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -226,6 +226,7 @@ DbContextOptionsBuilder options .MapEnum(ApplicationDbContext.CoatedSideTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.DataKindTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.IlluminantTypeName, _appSettings.Database.SchemaName) + .MapEnum(ApplicationDbContext.LogicalCombinatorTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentSubtypeTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.OpticalComponentTypeTypeName, _appSettings.Database.SchemaName) .MapEnum(ApplicationDbContext.PublishingStateTypeName, _appSettings.Database.SchemaName) @@ -286,7 +287,7 @@ private void ConfigureHttpClientServices(IServiceCollection services) public static void ConfigureCustomServices(IServiceCollection services) { - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); diff --git a/backend/src/Views/_ViewImports.cshtml b/backend/src/Views/_ViewImports.cshtml index d776b4b1..bd86c2b5 100644 --- a/backend/src/Views/_ViewImports.cshtml +++ b/backend/src/Views/_ViewImports.cshtml @@ -1,2 +1,2 @@ -@using Metabase.ViewModels.Common +@using Database.ViewModels.Common @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index e4b69ac0..a2faeaf1 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -317,11 +317,18 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") + institutionAccessPolicies: [InstitutionAccessPolicy!]! @cost(weight: "10") + institutionAccessPolicy(institutionId: Uuid!): InstitutionAccessPolicy + @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + @cost(weight: "10") + openIdConnectApplicationAccessPolicy(clientId: String!): OpenIdConnectApplicationAccessPolicy + @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -345,14 +352,20 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") + userAccessPolicies: [UserAccessPolicy!]! @cost(weight: "10") + userAccessPolicy(userId: Uuid!): UserAccessPolicy @cost(weight: "10") verificationCode: String! } type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! + clearDataAccessPolicy: ClearDataAccessPolicyPayload! @cost(weight: "10") + clearInstitutionAccessPolicies: ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") + clearOpenIdConnectApplicationAccessPolicies: ClearOpenIdConnectApplicationAccessPoliciesPayload! + @cost(weight: "10") + clearUserAccessPolicies: ClearUserAccessPoliciesPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -371,8 +384,26 @@ type Mutation { where: CreateResponseApprovalsPropositionInput @cost(weight: "10") ): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteDataAccessPolicy(input: DeleteDataAccessPolicyInput!): DeleteDataAccessPolicyPayload! + @cost(weight: "10") + deleteDataAccessPolicyOfInstitution( + input: DeleteDataAccessPolicyOfInstitutionInput! + ): DeleteDataAccessPolicyOfInstitutionPayload! @cost(weight: "10") + deleteDataAccessPolicyOfOpenIdConnectApplication( + input: DeleteDataAccessPolicyOfOpenIdConnectApplicationInput! + ): DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload! + @cost(weight: "10") + deleteDataAccessPolicyOfUser(input: DeleteDataAccessPolicyOfUserInput!): DeleteDataAccessPolicyOfUserPayload! + @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") + deleteInstitutionAccessPolicy(input: DeleteInstitutionAccessPolicyInput!): DeleteInstitutionAccessPolicyPayload! + @cost(weight: "10") + deleteOpenIdConnectApplicationAccessPolicy( + input: DeleteOpenIdConnectApplicationAccessPolicyInput! + ): DeleteOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + deleteUserAccessPolicy(input: DeleteUserAccessPolicyInput!): DeleteUserAccessPolicyPayload! + @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues( where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") @@ -380,17 +411,28 @@ type Mutation { removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setDataAccessLimitForInstitution( + input: SetDataAccessLimitForInstitutionInput! + ): SetDataAccessLimitForInstitutionPayload! @cost(weight: "10") + setDataAccessLimitForOpenIdConnectApplication( + input: SetDataAccessLimitForOpenIdConnectApplicationInput! + ): SetDataAccessLimitForOpenIdConnectApplicationPayload! @cost(weight: "10") + setDataAccessLimitForUser(input: SetDataAccessLimitForUserInput!): SetDataAccessLimitForUserPayload! + @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") + setInstitutionAccessLimit(input: SetInstitutionAccessLimitInput!): SetInstitutionAccessLimitPayload! + @cost(weight: "10") + setOpenIdConnectApplicationAccessLimit( + input: SetOpenIdConnectApplicationAccessLimitInput! + ): SetOpenIdConnectApplicationAccessLimitPayload! @cost(weight: "10") + setUserAccessLimit(input: SetUserAccessLimitInput!): SetUserAccessLimitPayload! + @cost(weight: "10") updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! - @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") - updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! - @cost(weight: "10") updateResponseApprovals( where: UpdateResponseApprovalsPropositionInput @cost(weight: "10") ): UpdateResponseApprovalsPayload! @cost(weight: "10") @@ -410,18 +452,6 @@ type AddDataApprovalPayload { query: Query! } -type AddInstitutionAccessRightsError implements UserError { - code: AddInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type AddInstitutionAccessRightsPayload { - errors: [AddInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - type Address { formatted: String! } @@ -445,6 +475,7 @@ type CalculateMethodPayload { } type CalorimetricData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -453,15 +484,11 @@ type CalorimetricData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String gValues: [Float!]! id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -502,11 +529,64 @@ type CielabColor { observer: CalorimetricObserver } +type ClearDataAccessPolicyError implements UserError { + code: ClearDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ClearDataAccessPolicyPayload { + dataAccessPolicy: [DataAccessPolicy!] + errors: [ClearDataAccessPolicyError!] + query: Query! +} + +type ClearInstitutionAccessPoliciesError implements UserError { + code: ClearInstitutionAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearInstitutionAccessPoliciesPayload { + errors: [ClearInstitutionAccessPoliciesError!] + institutionAccessPolicies: [InstitutionAccessPolicy!] + query: Query! +} + +type ClearOpenIdConnectApplicationAccessPoliciesError implements UserError { + code: ClearOpenIdConnectApplicationAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearOpenIdConnectApplicationAccessPoliciesPayload { + errors: [ClearOpenIdConnectApplicationAccessPoliciesError!] + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] + query: Query! +} + +type ClearUserAccessPoliciesError implements UserError { + code: ClearUserAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearUserAccessPoliciesPayload { + errors: [ClearUserAccessPoliciesError!] + query: Query! + userAccessPolicies: [UserAccessPolicy!] +} + type Component { name: String! uuid: Uuid! } +type CountSinceTime { + accessCount: NonNegativeInt! + startTime: DateTime +} + type CreateCalorimetricDataError implements UserError { code: CreateCalorimetricDataErrorCode! message: String! @@ -619,14 +699,15 @@ type CurrentInstitution { uuid: Uuid! } -type DataAccessRights { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32!] - hasRestrictions: Boolean! - hasRestrictionsByApplication: Boolean! - hasRestrictionsByInstitution: Boolean! - hasRestrictionsByUser: Boolean! +type DataAccessPolicy { + areInstitutionsRestricted: Boolean! + areOpenIdConnectApplicationsRestricted: Boolean! + areUsersRestricted: Boolean! + combinator: LogicalCombinator! + institutionAccessPolicies: [InstitutionAccessPolicy!] + isRestricted: Boolean! + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] + userAccessPolicies: [UserAccessPolicy!] } type DataApproval implements Approval { @@ -684,6 +765,54 @@ type DataFormat { uuid: Uuid! } +type DeleteDataAccessPolicyError implements UserError { + code: DeleteDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfInstitutionError implements UserError { + code: DeleteDataAccessPolicyOfInstitutionErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfInstitutionPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfInstitutionError!] + query: Query! +} + +type DeleteDataAccessPolicyOfOpenIdConnectApplicationError implements UserError { + code: DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfOpenIdConnectApplicationError!] + query: Query! +} + +type DeleteDataAccessPolicyOfUserError implements UserError { + code: DeleteDataAccessPolicyOfUserErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfUserPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfUserError!] + query: Query! +} + +type DeleteDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyError!] + query: Query! +} + type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! @@ -707,6 +836,42 @@ type DeleteGetHttpsResourcePayload { query: Query! } +type DeleteInstitutionAccessPolicyError implements UserError { + code: DeleteInstitutionAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteInstitutionAccessPolicyPayload { + errors: [DeleteInstitutionAccessPolicyError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type DeleteOpenIdConnectApplicationAccessPolicyError implements UserError { + code: DeleteOpenIdConnectApplicationAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteOpenIdConnectApplicationAccessPolicyPayload { + errors: [DeleteOpenIdConnectApplicationAccessPolicyError!] + openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + query: Query! +} + +type DeleteUserAccessPolicyError implements UserError { + code: DeleteUserAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteUserAccessPolicyPayload { + errors: [DeleteUserAccessPolicyError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + type FileMetaInformation { dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! @@ -714,6 +879,7 @@ type FileMetaInformation { } type GeometricData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -722,15 +888,11 @@ type GeometricData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String heights: [Float!]! id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -823,6 +985,7 @@ type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { } type HygrothermalData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -831,14 +994,10 @@ type HygrothermalData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -875,27 +1034,16 @@ type Institution { uuid: Uuid! } -type InstitutionAccessRights { - allowedDatasetsPerTime: NonNegativeInt - allowedUserCount: NonNegativeInt - createdAt: DateTime! - hasRestrictions: Boolean! - hasRestrictionsByTime: Boolean! - hasRestrictionsByUser: Boolean! - id: Uuid! +type InstitutionAccessPolicy { + accessCountSinceStartTime: CountSinceTime institutionId: Uuid! - period: Duration! - updatedAt: DateTime! - userAlreadyAccessed: [Uuid!]! - version: NonNegativeInt! -} - -type KeyValuePairOfGuidAndNullableUInt32 { - key: Uuid! - value: NonNegativeInt + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration } type LifeCycleData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -904,14 +1052,10 @@ type LifeCycleData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -969,7 +1113,16 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } +type OpenIdConnectApplicationAccessPolicy { + accessCountSinceStartTime: CountSinceTime + clientId: String! + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration +} + type OpticalData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -981,15 +1134,11 @@ type OpticalData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! infraredEmittances: [Float!]! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! @@ -1040,6 +1189,7 @@ type PageInfo { } type PhotovoltaicData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -1048,14 +1198,10 @@ type PhotovoltaicData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -1161,6 +1307,42 @@ type RetractDataPayload { query: Query! } +type SetDataAccessLimitForInstitutionError implements UserError { + code: SetDataAccessLimitForInstitutionErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForInstitutionPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForInstitutionError!] + query: Query! +} + +type SetDataAccessLimitForOpenIdConnectApplicationError implements UserError { + code: SetDataAccessLimitForOpenIdConnectApplicationErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForOpenIdConnectApplicationPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForOpenIdConnectApplicationError!] + query: Query! +} + +type SetDataAccessLimitForUserError implements UserError { + code: SetDataAccessLimitForUserErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForUserPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForUserError!] + query: Query! +} + type SetGetHttpsResourceParentError implements UserError { code: SetGetHttpsResourceParentErrorCode! message: String! @@ -1173,6 +1355,42 @@ type SetGetHttpsResourceParentPayload { query: Query! } +type SetInstitutionAccessLimitError implements UserError { + code: SetInstitutionAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetInstitutionAccessLimitPayload { + errors: [SetInstitutionAccessLimitError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type SetOpenIdConnectApplicationAccessLimitError implements UserError { + code: SetOpenIdConnectApplicationAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetOpenIdConnectApplicationAccessLimitPayload { + errors: [SetOpenIdConnectApplicationAccessLimitError!] + openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + query: Query! +} + +type SetUserAccessLimitError implements UserError { + code: SetUserAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetUserAccessLimitPayload { + errors: [SetUserAccessLimitError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -1205,18 +1423,6 @@ type UpdateChildGetHttpsResourcePayload { query: Query! } -type UpdateDataAccessRightsError implements UserError { - code: UpdateDataAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateDataAccessRightsPayload { - dataAccessRights: DataAccessRights - errors: [UpdateDataAccessRightsError!] - query: Query! -} - type UpdateDatabaseError { code: UpdateDatabaseErrorCode! message: String! @@ -1241,18 +1447,6 @@ type UpdateDataPayload { query: Query! } -type UpdateInstitutionAccessRightsError implements UserError { - code: UpdateInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateInstitutionAccessRightsPayload { - errors: [UpdateInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! @@ -1277,6 +1471,11 @@ type UpdateRootGetHttpsResourcePayload { query: Query! } +type UpperLimitPerDuration { + duration: Duration + upperLimit: NonNegativeInt! +} + type User implements Node { createdAt: DateTime! id: ID! @@ -1286,6 +1485,14 @@ type User implements Node { uuid: Uuid! } +type UserAccessPolicy { + accessCountSinceStartTime: CountSinceTime + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration + userId: Uuid! +} + type UserInfo { address: Address email: String @@ -1309,6 +1516,7 @@ interface Approval { } interface Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! approvals: [DataApproval!]! @@ -1317,14 +1525,10 @@ interface Data { createdAt: DateTime! creator: Institution creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -1365,13 +1569,6 @@ input AddDataApprovalInput { variables: Any! } -input AddInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -1673,6 +1870,29 @@ input DecimalPropositionInput { notLessThanOrEqualTo: Decimal @cost(weight: "10") } +input DeleteDataAccessPolicyInput { + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteDataAccessPolicyOfInstitutionInput { + dataId: Uuid! + dataKind: DataKind! + institutionId: Uuid! +} + +input DeleteDataAccessPolicyOfOpenIdConnectApplicationInput { + clientId: String! + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteDataAccessPolicyOfUserInput { + dataId: Uuid! + dataKind: DataKind! + userId: Uuid! +} + input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1682,6 +1902,18 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } +input DeleteInstitutionAccessPolicyInput { + institutionId: Uuid! +} + +input DeleteOpenIdConnectApplicationAccessPolicyInput { + clientId: String! +} + +input DeleteUserAccessPolicyInput { + userId: Uuid! +} + input DurationPropositionInput { equalTo: Duration @cost(weight: "10") greaterThan: Duration @cost(weight: "10") @@ -1885,6 +2117,16 @@ input HygrothermalDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input InstitutionAccessPolicyPropositionInput { + and: [InstitutionAccessPolicyPropositionInput!] + institutionId: UuidPropositionInput + or: [InstitutionAccessPolicyPropositionInput!] +} + +input InstitutionAccessPolicySortInput { + institutionId: SortEnumType @cost(weight: "10") +} + input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -1913,11 +2155,6 @@ input JsonValueKindPropositionInput { notIn: [JsonValueKind!] @cost(weight: "10") } -input KeyValuePairOfGuidAndNullableUInt32Input { - key: Uuid! - value: NonNegativeInt -} - input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2090,6 +2327,16 @@ input NumerationSortInput { suffix: SortEnumType @cost(weight: "10") } +input OpenIdConnectApplicationAccessPolicyPropositionInput { + and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + clientId: StringPropositionInput + or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] +} + +input OpenIdConnectApplicationAccessPolicySortInput { + clientId: SortEnumType @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2260,12 +2507,48 @@ input RootGetHttpsResourceInput { description: String! } +input SetDataAccessLimitForInstitutionInput { + dataId: Uuid! + dataKind: DataKind! + institutionId: Uuid! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetDataAccessLimitForOpenIdConnectApplicationInput { + clientId: String! + dataId: Uuid! + dataKind: DataKind! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetDataAccessLimitForUserInput { + dataId: Uuid! + dataKind: DataKind! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput + userId: Uuid! +} + input SetGetHttpsResourceParentInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! getHttpsResourceId: Uuid! parentId: Uuid! } +input SetInstitutionAccessLimitInput { + institutionId: Uuid! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetOpenIdConnectApplicationAccessLimitInput { + clientId: String! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetUserAccessLimitInput { + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput + userId: Uuid! +} + input ShortPropositionInput { equalTo: Short @cost(weight: "10") greaterThan: Short @cost(weight: "10") @@ -2381,14 +2664,6 @@ input UpdateChildGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input UpdateDataAccessRightsInput { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32Input!] - dataId: Uuid! - dataKind: DataKind! -} - input UpdateDatabaseInput { databaseId: Uuid! description: String! @@ -2408,13 +2683,6 @@ input UpdateDataInput { warnings: [String!]! } -input UpdateInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2437,6 +2705,11 @@ input UpdateRootGetHttpsResourceInput { getHttpsResourceId: Uuid! } +input UpperLimitPerDurationInput { + duration: Duration + upperLimit: NonNegativeInt! +} + input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -2476,6 +2749,16 @@ input UrlPropositionInput { notLessThanOrEqualTo: Url @cost(weight: "10") } +input UserAccessPolicyPropositionInput { + and: [UserAccessPolicyPropositionInput!] + or: [UserAccessPolicyPropositionInput!] + userId: UuidPropositionInput +} + +input UserAccessPolicySortInput { + userId: SortEnumType @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") @@ -2509,13 +2792,6 @@ enum AddDataApprovalErrorCode { WRONG_STATEMENT } -enum AddInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - ALREADY_EXISTS -} - "Defines when a policy shall be executed." enum ApplyPolicy { "Before the resolver was executed." @@ -2537,6 +2813,31 @@ enum CalorimetricObserver { TEN_DEGREES } +enum ClearDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum ClearInstitutionAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ClearUserAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + enum CoatedSide { PRIME NON_PRIME @@ -2664,6 +2965,37 @@ enum DataKind { PHOTOVOLTAIC_DATA } +enum DeleteDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum DeleteDataAccessPolicyOfInstitutionErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION +} + +enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION +} + +enum DeleteDataAccessPolicyOfUserErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER +} + enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -2682,6 +3014,27 @@ enum DeleteGetHttpsResourceErrorCode { HAS_CHILDREN } +enum DeleteInstitutionAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + +enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + +enum DeleteUserAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + enum Illuminant { A D65 @@ -2698,6 +3051,11 @@ enum JsonValueKind { NULL } +enum LogicalCombinator { + ALL + SOME +} + enum OpticalComponentSubtype { MONOLITHIC LAMINATE @@ -2763,6 +3121,30 @@ enum RetractDataErrorCode { NOT_PUBLISHED } +enum SetDataAccessLimitForInstitutionErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION +} + +enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION +} + +enum SetDataAccessLimitForUserErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER +} + enum SetGetHttpsResourceParentErrorCode { UNKNOWN UNAUTHORIZED @@ -2774,6 +3156,24 @@ enum SetGetHttpsResourceParentErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum SetInstitutionAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum SetOpenIdConnectApplicationAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum SetUserAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + enum SortEnumType { ASC DESC @@ -2816,16 +3216,6 @@ enum UpdateChildGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateDataAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION - UNKNOWN_USER - UNKNOWN_APPLICATION -} - enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED @@ -2839,13 +3229,6 @@ enum UpdateDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_ACCESS_RIGHTS -} - enum UpdateResponseApprovalsErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index e4b69ac0..a2faeaf1 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -317,11 +317,18 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") + institutionAccessPolicies: [InstitutionAccessPolicy!]! @cost(weight: "10") + institutionAccessPolicy(institutionId: Uuid!): InstitutionAccessPolicy + @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + @cost(weight: "10") + openIdConnectApplicationAccessPolicy(clientId: String!): OpenIdConnectApplicationAccessPolicy + @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -345,14 +352,20 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") + userAccessPolicies: [UserAccessPolicy!]! @cost(weight: "10") + userAccessPolicy(userId: Uuid!): UserAccessPolicy @cost(weight: "10") verificationCode: String! } type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - addInstitutionAccessRights(input: AddInstitutionAccessRightsInput!): AddInstitutionAccessRightsPayload! + clearDataAccessPolicy: ClearDataAccessPolicyPayload! @cost(weight: "10") + clearInstitutionAccessPolicies: ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") + clearOpenIdConnectApplicationAccessPolicies: ClearOpenIdConnectApplicationAccessPoliciesPayload! + @cost(weight: "10") + clearUserAccessPolicies: ClearUserAccessPoliciesPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -371,8 +384,26 @@ type Mutation { where: CreateResponseApprovalsPropositionInput @cost(weight: "10") ): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") + deleteDataAccessPolicy(input: DeleteDataAccessPolicyInput!): DeleteDataAccessPolicyPayload! + @cost(weight: "10") + deleteDataAccessPolicyOfInstitution( + input: DeleteDataAccessPolicyOfInstitutionInput! + ): DeleteDataAccessPolicyOfInstitutionPayload! @cost(weight: "10") + deleteDataAccessPolicyOfOpenIdConnectApplication( + input: DeleteDataAccessPolicyOfOpenIdConnectApplicationInput! + ): DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload! + @cost(weight: "10") + deleteDataAccessPolicyOfUser(input: DeleteDataAccessPolicyOfUserInput!): DeleteDataAccessPolicyOfUserPayload! + @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") + deleteInstitutionAccessPolicy(input: DeleteInstitutionAccessPolicyInput!): DeleteInstitutionAccessPolicyPayload! + @cost(weight: "10") + deleteOpenIdConnectApplicationAccessPolicy( + input: DeleteOpenIdConnectApplicationAccessPolicyInput! + ): DeleteOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + deleteUserAccessPolicy(input: DeleteUserAccessPolicyInput!): DeleteUserAccessPolicyPayload! + @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues( where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") @@ -380,17 +411,28 @@ type Mutation { removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") + setDataAccessLimitForInstitution( + input: SetDataAccessLimitForInstitutionInput! + ): SetDataAccessLimitForInstitutionPayload! @cost(weight: "10") + setDataAccessLimitForOpenIdConnectApplication( + input: SetDataAccessLimitForOpenIdConnectApplicationInput! + ): SetDataAccessLimitForOpenIdConnectApplicationPayload! @cost(weight: "10") + setDataAccessLimitForUser(input: SetDataAccessLimitForUserInput!): SetDataAccessLimitForUserPayload! + @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") + setInstitutionAccessLimit(input: SetInstitutionAccessLimitInput!): SetInstitutionAccessLimitPayload! + @cost(weight: "10") + setOpenIdConnectApplicationAccessLimit( + input: SetOpenIdConnectApplicationAccessLimitInput! + ): SetOpenIdConnectApplicationAccessLimitPayload! @cost(weight: "10") + setUserAccessLimit(input: SetUserAccessLimitInput!): SetUserAccessLimitPayload! + @cost(weight: "10") updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") updateData(input: UpdateDataInput!): UpdateDataPayload! @cost(weight: "10") - updateDataAccessRights(input: UpdateDataAccessRightsInput!): UpdateDataAccessRightsPayload! - @cost(weight: "10") updateDatabase(input: UpdateDatabaseInput!): UpdateDatabasePayload! @cost(weight: "10") - updateInstitutionAccessRights(input: UpdateInstitutionAccessRightsInput!): UpdateInstitutionAccessRightsPayload! - @cost(weight: "10") updateResponseApprovals( where: UpdateResponseApprovalsPropositionInput @cost(weight: "10") ): UpdateResponseApprovalsPayload! @cost(weight: "10") @@ -410,18 +452,6 @@ type AddDataApprovalPayload { query: Query! } -type AddInstitutionAccessRightsError implements UserError { - code: AddInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type AddInstitutionAccessRightsPayload { - errors: [AddInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - type Address { formatted: String! } @@ -445,6 +475,7 @@ type CalculateMethodPayload { } type CalorimetricData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -453,15 +484,11 @@ type CalorimetricData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String gValues: [Float!]! id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -502,11 +529,64 @@ type CielabColor { observer: CalorimetricObserver } +type ClearDataAccessPolicyError implements UserError { + code: ClearDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ClearDataAccessPolicyPayload { + dataAccessPolicy: [DataAccessPolicy!] + errors: [ClearDataAccessPolicyError!] + query: Query! +} + +type ClearInstitutionAccessPoliciesError implements UserError { + code: ClearInstitutionAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearInstitutionAccessPoliciesPayload { + errors: [ClearInstitutionAccessPoliciesError!] + institutionAccessPolicies: [InstitutionAccessPolicy!] + query: Query! +} + +type ClearOpenIdConnectApplicationAccessPoliciesError implements UserError { + code: ClearOpenIdConnectApplicationAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearOpenIdConnectApplicationAccessPoliciesPayload { + errors: [ClearOpenIdConnectApplicationAccessPoliciesError!] + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] + query: Query! +} + +type ClearUserAccessPoliciesError implements UserError { + code: ClearUserAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ClearUserAccessPoliciesPayload { + errors: [ClearUserAccessPoliciesError!] + query: Query! + userAccessPolicies: [UserAccessPolicy!] +} + type Component { name: String! uuid: Uuid! } +type CountSinceTime { + accessCount: NonNegativeInt! + startTime: DateTime +} + type CreateCalorimetricDataError implements UserError { code: CreateCalorimetricDataErrorCode! message: String! @@ -619,14 +699,15 @@ type CurrentInstitution { uuid: Uuid! } -type DataAccessRights { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32!] - hasRestrictions: Boolean! - hasRestrictionsByApplication: Boolean! - hasRestrictionsByInstitution: Boolean! - hasRestrictionsByUser: Boolean! +type DataAccessPolicy { + areInstitutionsRestricted: Boolean! + areOpenIdConnectApplicationsRestricted: Boolean! + areUsersRestricted: Boolean! + combinator: LogicalCombinator! + institutionAccessPolicies: [InstitutionAccessPolicy!] + isRestricted: Boolean! + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] + userAccessPolicies: [UserAccessPolicy!] } type DataApproval implements Approval { @@ -684,6 +765,54 @@ type DataFormat { uuid: Uuid! } +type DeleteDataAccessPolicyError implements UserError { + code: DeleteDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfInstitutionError implements UserError { + code: DeleteDataAccessPolicyOfInstitutionErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfInstitutionPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfInstitutionError!] + query: Query! +} + +type DeleteDataAccessPolicyOfOpenIdConnectApplicationError implements UserError { + code: DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfOpenIdConnectApplicationError!] + query: Query! +} + +type DeleteDataAccessPolicyOfUserError implements UserError { + code: DeleteDataAccessPolicyOfUserErrorCode! + message: String! + path: [String!]! +} + +type DeleteDataAccessPolicyOfUserPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyOfUserError!] + query: Query! +} + +type DeleteDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [DeleteDataAccessPolicyError!] + query: Query! +} + type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! @@ -707,6 +836,42 @@ type DeleteGetHttpsResourcePayload { query: Query! } +type DeleteInstitutionAccessPolicyError implements UserError { + code: DeleteInstitutionAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteInstitutionAccessPolicyPayload { + errors: [DeleteInstitutionAccessPolicyError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type DeleteOpenIdConnectApplicationAccessPolicyError implements UserError { + code: DeleteOpenIdConnectApplicationAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteOpenIdConnectApplicationAccessPolicyPayload { + errors: [DeleteOpenIdConnectApplicationAccessPolicyError!] + openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + query: Query! +} + +type DeleteUserAccessPolicyError implements UserError { + code: DeleteUserAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type DeleteUserAccessPolicyPayload { + errors: [DeleteUserAccessPolicyError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + type FileMetaInformation { dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! @@ -714,6 +879,7 @@ type FileMetaInformation { } type GeometricData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -722,15 +888,11 @@ type GeometricData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String heights: [Float!]! id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -823,6 +985,7 @@ type GetHttpsResourceTreeRoot implements GetHttpsResourceTreeVertex { } type HygrothermalData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -831,14 +994,10 @@ type HygrothermalData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -875,27 +1034,16 @@ type Institution { uuid: Uuid! } -type InstitutionAccessRights { - allowedDatasetsPerTime: NonNegativeInt - allowedUserCount: NonNegativeInt - createdAt: DateTime! - hasRestrictions: Boolean! - hasRestrictionsByTime: Boolean! - hasRestrictionsByUser: Boolean! - id: Uuid! +type InstitutionAccessPolicy { + accessCountSinceStartTime: CountSinceTime institutionId: Uuid! - period: Duration! - updatedAt: DateTime! - userAlreadyAccessed: [Uuid!]! - version: NonNegativeInt! -} - -type KeyValuePairOfGuidAndNullableUInt32 { - key: Uuid! - value: NonNegativeInt + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration } type LifeCycleData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -904,14 +1052,10 @@ type LifeCycleData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -969,7 +1113,16 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } +type OpenIdConnectApplicationAccessPolicy { + accessCountSinceStartTime: CountSinceTime + clientId: String! + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration +} + type OpticalData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -981,15 +1134,11 @@ type OpticalData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! infraredEmittances: [Float!]! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! @@ -1040,6 +1189,7 @@ type PageInfo { } type PhotovoltaicData implements Node & Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! @@ -1048,14 +1198,10 @@ type PhotovoltaicData implements Node & Data { createdAt: DateTime! creator: Institution @cost(weight: "3") creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database @cost(weight: "3") databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources( @@ -1161,6 +1307,42 @@ type RetractDataPayload { query: Query! } +type SetDataAccessLimitForInstitutionError implements UserError { + code: SetDataAccessLimitForInstitutionErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForInstitutionPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForInstitutionError!] + query: Query! +} + +type SetDataAccessLimitForOpenIdConnectApplicationError implements UserError { + code: SetDataAccessLimitForOpenIdConnectApplicationErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForOpenIdConnectApplicationPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForOpenIdConnectApplicationError!] + query: Query! +} + +type SetDataAccessLimitForUserError implements UserError { + code: SetDataAccessLimitForUserErrorCode! + message: String! + path: [String!]! +} + +type SetDataAccessLimitForUserPayload { + dataAccessPolicy: DataAccessPolicy + errors: [SetDataAccessLimitForUserError!] + query: Query! +} + type SetGetHttpsResourceParentError implements UserError { code: SetGetHttpsResourceParentErrorCode! message: String! @@ -1173,6 +1355,42 @@ type SetGetHttpsResourceParentPayload { query: Query! } +type SetInstitutionAccessLimitError implements UserError { + code: SetInstitutionAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetInstitutionAccessLimitPayload { + errors: [SetInstitutionAccessLimitError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type SetOpenIdConnectApplicationAccessLimitError implements UserError { + code: SetOpenIdConnectApplicationAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetOpenIdConnectApplicationAccessLimitPayload { + errors: [SetOpenIdConnectApplicationAccessLimitError!] + openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + query: Query! +} + +type SetUserAccessLimitError implements UserError { + code: SetUserAccessLimitErrorCode! + message: String! + path: [String!]! +} + +type SetUserAccessLimitPayload { + errors: [SetUserAccessLimitError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + "`ISO 52022` is an example of the abbreviation of a standardizer and the main number of the identifier." type Standard { abstract: String @@ -1205,18 +1423,6 @@ type UpdateChildGetHttpsResourcePayload { query: Query! } -type UpdateDataAccessRightsError implements UserError { - code: UpdateDataAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateDataAccessRightsPayload { - dataAccessRights: DataAccessRights - errors: [UpdateDataAccessRightsError!] - query: Query! -} - type UpdateDatabaseError { code: UpdateDatabaseErrorCode! message: String! @@ -1241,18 +1447,6 @@ type UpdateDataPayload { query: Query! } -type UpdateInstitutionAccessRightsError implements UserError { - code: UpdateInstitutionAccessRightsErrorCode! - message: String! - path: [String!]! -} - -type UpdateInstitutionAccessRightsPayload { - errors: [UpdateInstitutionAccessRightsError!] - institutionAccessRights: InstitutionAccessRights - query: Query! -} - type UpdateResponseApprovalsError implements UserError { code: UpdateResponseApprovalsErrorCode! message: String! @@ -1277,6 +1471,11 @@ type UpdateRootGetHttpsResourcePayload { query: Query! } +type UpperLimitPerDuration { + duration: Duration + upperLimit: NonNegativeInt! +} + type User implements Node { createdAt: DateTime! id: ID! @@ -1286,6 +1485,14 @@ type User implements Node { uuid: Uuid! } +type UserAccessPolicy { + accessCountSinceStartTime: CountSinceTime + isAllowed: Boolean! + isWithinTimeSpan: Boolean! + upperAccessLimitPerTimeDuration: UpperLimitPerDuration + userId: Uuid! +} + type UserInfo { address: Address email: String @@ -1309,6 +1516,7 @@ interface Approval { } interface Data { + accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! approval: ResponseApproval! approvals: [DataApproval!]! @@ -1317,14 +1525,10 @@ interface Data { createdAt: DateTime! creator: Institution creatorId: Uuid! - dataAccessRights: DataAccessRights! database: Database databaseId: Uuid! description: String id: ID! - isRestrictedByApplication(applicationId: String!): Boolean! - isRestrictedByInstitutions(institutions: [Uuid!]!): Boolean! - isRestrictedByUser(alreadyAccesedCount: NonNegativeInt!, uuid: Uuid!): Boolean! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -1365,13 +1569,6 @@ input AddDataApprovalInput { variables: Any! } -input AddInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input AppliedMethodInput { arguments: [NamedMethodArgumentInput!]! methodId: Uuid! @@ -1673,6 +1870,29 @@ input DecimalPropositionInput { notLessThanOrEqualTo: Decimal @cost(weight: "10") } +input DeleteDataAccessPolicyInput { + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteDataAccessPolicyOfInstitutionInput { + dataId: Uuid! + dataKind: DataKind! + institutionId: Uuid! +} + +input DeleteDataAccessPolicyOfOpenIdConnectApplicationInput { + clientId: String! + dataId: Uuid! + dataKind: DataKind! +} + +input DeleteDataAccessPolicyOfUserInput { + dataId: Uuid! + dataKind: DataKind! + userId: Uuid! +} + input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1682,6 +1902,18 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } +input DeleteInstitutionAccessPolicyInput { + institutionId: Uuid! +} + +input DeleteOpenIdConnectApplicationAccessPolicyInput { + clientId: String! +} + +input DeleteUserAccessPolicyInput { + userId: Uuid! +} + input DurationPropositionInput { equalTo: Duration @cost(weight: "10") greaterThan: Duration @cost(weight: "10") @@ -1885,6 +2117,16 @@ input HygrothermalDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input InstitutionAccessPolicyPropositionInput { + and: [InstitutionAccessPolicyPropositionInput!] + institutionId: UuidPropositionInput + or: [InstitutionAccessPolicyPropositionInput!] +} + +input InstitutionAccessPolicySortInput { + institutionId: SortEnumType @cost(weight: "10") +} + input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -1913,11 +2155,6 @@ input JsonValueKindPropositionInput { notIn: [JsonValueKind!] @cost(weight: "10") } -input KeyValuePairOfGuidAndNullableUInt32Input { - key: Uuid! - value: NonNegativeInt -} - input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2090,6 +2327,16 @@ input NumerationSortInput { suffix: SortEnumType @cost(weight: "10") } +input OpenIdConnectApplicationAccessPolicyPropositionInput { + and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + clientId: StringPropositionInput + or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] +} + +input OpenIdConnectApplicationAccessPolicySortInput { + clientId: SortEnumType @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2260,12 +2507,48 @@ input RootGetHttpsResourceInput { description: String! } +input SetDataAccessLimitForInstitutionInput { + dataId: Uuid! + dataKind: DataKind! + institutionId: Uuid! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetDataAccessLimitForOpenIdConnectApplicationInput { + clientId: String! + dataId: Uuid! + dataKind: DataKind! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetDataAccessLimitForUserInput { + dataId: Uuid! + dataKind: DataKind! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput + userId: Uuid! +} + input SetGetHttpsResourceParentInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! getHttpsResourceId: Uuid! parentId: Uuid! } +input SetInstitutionAccessLimitInput { + institutionId: Uuid! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetOpenIdConnectApplicationAccessLimitInput { + clientId: String! + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput +} + +input SetUserAccessLimitInput { + upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput + userId: Uuid! +} + input ShortPropositionInput { equalTo: Short @cost(weight: "10") greaterThan: Short @cost(weight: "10") @@ -2381,14 +2664,6 @@ input UpdateChildGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input UpdateDataAccessRightsInput { - allowedApplications: [String!] - allowedInstitutions: [Uuid!] - allowedUserAndQuantity: [KeyValuePairOfGuidAndNullableUInt32Input!] - dataId: Uuid! - dataKind: DataKind! -} - input UpdateDatabaseInput { databaseId: Uuid! description: String! @@ -2408,13 +2683,6 @@ input UpdateDataInput { warnings: [String!]! } -input UpdateInstitutionAccessRightsInput { - allowedDatasetsPerTimeSpan: NonNegativeInt - allowedUserCount: NonNegativeInt - institutionId: Uuid! - periodInDays: Int! -} - input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput @@ -2437,6 +2705,11 @@ input UpdateRootGetHttpsResourceInput { getHttpsResourceId: Uuid! } +input UpperLimitPerDurationInput { + duration: Duration + upperLimit: NonNegativeInt! +} + input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -2476,6 +2749,16 @@ input UrlPropositionInput { notLessThanOrEqualTo: Url @cost(weight: "10") } +input UserAccessPolicyPropositionInput { + and: [UserAccessPolicyPropositionInput!] + or: [UserAccessPolicyPropositionInput!] + userId: UuidPropositionInput +} + +input UserAccessPolicySortInput { + userId: SortEnumType @cost(weight: "10") +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") @@ -2509,13 +2792,6 @@ enum AddDataApprovalErrorCode { WRONG_STATEMENT } -enum AddInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - ALREADY_EXISTS -} - "Defines when a policy shall be executed." enum ApplyPolicy { "Before the resolver was executed." @@ -2537,6 +2813,31 @@ enum CalorimetricObserver { TEN_DEGREES } +enum ClearDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum ClearInstitutionAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ClearUserAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + enum CoatedSide { PRIME NON_PRIME @@ -2664,6 +2965,37 @@ enum DataKind { PHOTOVOLTAIC_DATA } +enum DeleteDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + +enum DeleteDataAccessPolicyOfInstitutionErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION +} + +enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION +} + +enum DeleteDataAccessPolicyOfUserErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER +} + enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -2682,6 +3014,27 @@ enum DeleteGetHttpsResourceErrorCode { HAS_CHILDREN } +enum DeleteInstitutionAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + +enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + +enum DeleteUserAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_POLICY +} + enum Illuminant { A D65 @@ -2698,6 +3051,11 @@ enum JsonValueKind { NULL } +enum LogicalCombinator { + ALL + SOME +} + enum OpticalComponentSubtype { MONOLITHIC LAMINATE @@ -2763,6 +3121,30 @@ enum RetractDataErrorCode { NOT_PUBLISHED } +enum SetDataAccessLimitForInstitutionErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION +} + +enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION +} + +enum SetDataAccessLimitForUserErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER +} + enum SetGetHttpsResourceParentErrorCode { UNKNOWN UNAUTHORIZED @@ -2774,6 +3156,24 @@ enum SetGetHttpsResourceParentErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum SetInstitutionAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum SetOpenIdConnectApplicationAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum SetUserAccessLimitErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + enum SortEnumType { ASC DESC @@ -2816,16 +3216,6 @@ enum UpdateChildGetHttpsResourceErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateDataAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION - UNKNOWN_USER - UNKNOWN_APPLICATION -} - enum UpdateDatabaseErrorCode { UNKNOWN UNAUTHORIZED @@ -2839,13 +3229,6 @@ enum UpdateDataErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum UpdateInstitutionAccessRightsErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_ACCESS_RIGHTS -} - enum UpdateResponseApprovalsErrorCode { UNKNOWN UNAUTHORIZED From dcdd93f90538a17e15785a2fe7239e7accc07fc3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 8 Jun 2026 20:15:42 +0200 Subject: [PATCH 812/878] Treat local and global access policies in the same way --- backend/Makefile | 6 +- .../src/Controllers/FileUploadController.cs | 29 + ...derAccessPolicy.cs => AccessPolicyBase.cs} | 5 +- .../src/Data/AccessPolicies/CountSinceTime.cs | 2 + .../Data/AccessPolicies/DataAccessPolicy.cs | 206 +- .../AccessPolicies/DataAccessPolicyType.cs | 99 + .../AccessPolicies/InstitutionAccessPolicy.cs | 9 +- .../OpenIdConnectApplicationAccessPolicy.cs | 10 +- .../AccessPolicies/UpperLimitPerDuration.cs | 2 + .../Data/AccessPolicies/UserAccessPolicy.cs | 9 +- backend/src/Data/ApplicationDbContext.cs | 309 +- backend/src/Data/CalorimetricData.cs | 6 + backend/src/Data/DataReference.cs | 15 + backend/src/Data/DataX.cs | 2 +- backend/src/Data/GeometricData.cs | 6 + backend/src/Data/GetHttpsResource.cs | 4 +- backend/src/Data/HygrothermalData.cs | 6 + backend/src/Data/LifeCycleData.cs | 6 + backend/src/Data/OpticalData.cs | 7 + backend/src/Data/PhotovoltaicData.cs | 6 + .../AccessPolicies/AccessPolicyDataLoaders.cs | 70 +- .../AccessPolicies/AccessPolicyQueries.cs | 70 +- .../ClearDataAccessPoliciesMutation.cs | 44 +- .../ClearInstitutionAccessPoliciesMutation.cs | 41 +- ...onnectApplicationAccessPoliciesMutation.cs | 41 +- .../ClearUserAccessPoliciesMutation.cs | 41 +- .../DeleteDataAccessPolicyMutation.cs | 92 - ...teDataAccessPolicyOfInstitutionMutation.cs | 113 - ...olicyOfOpenIdConnectApplicationMutation.cs | 113 - .../DeleteDataAccessPolicyOfUserMutation.cs | 113 - .../DeleteInstitutionAccessPolicyMutation.cs | 95 - ...dConnectApplicationAccessPolicyMutation.cs | 94 - .../DeleteUserAccessPolicyMutation.cs | 95 - .../ResetDataAccessPolicyMutation.cs | 99 + ...etDataAccessLimitForInstitutionMutation.cs | 133 - ...imitForOpenIdConnectApplicationMutation.cs | 133 - .../SetDataAccessLimitForUserMutation.cs | 133 - .../SetInstitutionAccessLimitMutation.cs | 95 - .../SetInstitutionAccessPolicyMutation.cs | 141 + ...IdConnectApplicationAccessLimitMutation.cs | 94 - ...dConnectApplicationAccessPolicyMutation.cs | 139 + .../SetUserAccessLimitMutation.cs | 95 - .../SetUserAccessPolicyMutation.cs | 140 + .../UnsetInstitutionAccessPolicyMutation.cs | 116 + ...dConnectApplicationAccessPolicyMutation.cs | 117 + .../UnsetUserAccessPolicyMutation.cs | 118 + .../CalorimetricDataQueries.cs | 4 +- .../CreateCalorimetricDataMutation.cs | 2 + backend/src/GraphQl/DataMutationBase.cs | 4 +- backend/src/GraphQl/DataReferenceInput.cs | 20 + backend/src/GraphQl/DataX/DataResolvers.cs | 22 +- backend/src/GraphQl/DataX/DataTypeBase.cs | 6 +- .../CreateGeometricDataMutation.cs | 2 + .../CreateGetHttpsResourceMutation.cs | 14 +- .../GetHttpsResourceQueries.cs | 1 - .../CreateHygrothermalDataMutation.cs | 2 + .../HygrothermalDataQueries.cs | 6 +- .../CreateLifeCycleDataMutation.cs | 2 + .../LifeCycleDataX/LifeCycleDataQueries.cs | 6 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 3 + .../OpticalDataX/OpticalDataQueries.cs | 6 +- .../CreatePhotovoltaicDataMutation.cs | 2 + .../PhotovoltaicDataQueries.cs | 6 +- .../20260603191121_RevampAccessPolicies.cs | 4 +- ...0608173427_UnifyAccessPolicies.Designer.cs | 3462 ++++++++++++++++ .../20260608173427_UnifyAccessPolicies.cs | 1020 +++++ ...yIsOnlyDeletedAlongsideItsData.Designer.cs | 3473 +++++++++++++++++ ...cessPolicyIsOnlyDeletedAlongsideItsData.cs | 64 + .../ApplicationDbContextModelSnapshot.cs | 908 ++--- backend/src/Migrations/migrate.sql | 683 +++- ..._to_20260608173427_UnifyAccessPolicies.sql | 580 +++ ...essPolicyIsOnlyDeletedAlongsideItsData.sql | 100 + ...to_20260603191121_RevampAccessPolicies.sql | 428 ++ ..._to_20260608173427_UnifyAccessPolicies.sql | 36 + backend/src/Services/AccessPolicyService.cs | 163 +- .../GraphQlSchemaTests.IsUnchanged.snap | 580 ++- frontend/type-defs.graphqls | 580 ++- 77 files changed, 12523 insertions(+), 2985 deletions(-) rename backend/src/Data/AccessPolicies/{StakeholderAccessPolicy.cs => AccessPolicyBase.cs} (88%) create mode 100644 backend/src/Data/AccessPolicies/DataAccessPolicyType.cs create mode 100644 backend/src/Data/DataReference.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/DataReferenceInput.cs create mode 100644 backend/src/Migrations/20260608173427_UnifyAccessPolicies.Designer.cs create mode 100644 backend/src/Migrations/20260608173427_UnifyAccessPolicies.cs create mode 100644 backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.Designer.cs create mode 100644 backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.cs create mode 100644 backend/src/Migrations/migrate_from_20260603191121_RevampAccessPolicies_to_20260608173427_UnifyAccessPolicies.sql create mode 100644 backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql create mode 100644 backend/src/Migrations/rollback_from_20260608173427_UnifyAccessPolicies_to_20260603191121_RevampAccessPolicies.sql create mode 100644 backend/src/Migrations/rollback_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260608173427_UnifyAccessPolicies.sql diff --git a/backend/Makefile b/backend/Makefile index a4169f1e..5bfc71d1 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -22,7 +22,7 @@ help : ## Print this help # See https://learn.microsoft.com/en-us/dotnet/core/testing/microsoft-testing-platform-run-and-debug?tabs=dotnetcli list : ARGS = -list : ## List tests, for example, `make list-tests ARGS='--filter "FullyQualifiedName~LoggedInUser_CreatesComponent"'` +list : ## List tests, for example, `make list ARGS='--filter "FullyQualifiedName~LoggedInUser_CreatesComponent"'` dotnet run \ --project ./test/*.Tests.csproj \ --list-tests \ @@ -67,7 +67,7 @@ licenses : ## Print licenses trace : ## Trace the dotnet process `Database` dotnet tool run \ dotnet-trace collect \ - --process-id $$(dotnet tool run dotnet-trace ps | grep Database | awk '{print $$1}') \ + --process-id "$$(dotnet tool run dotnet-trace ps | grep Database | awk '{print $$1}')" \ --format speedscope \ --providers Microsoft-Windows-DotNETRuntime .PHONY : trace @@ -225,6 +225,6 @@ migration : ## Add migration with name `${NAME}`, generate corresponding idempot --output $${rollback_path} && \ sed -i -e "$(shell printf '1s/^\357\273\277//')" -e "s/\r//" $${migrate_path} $${rollback_path} && \ cat $${migrate_path} >> ./src/Migrations/migrate.sql && \ - $(MAKE) dos2unix + $(MAKE) dos2unix && \ echo "Review the generated C# and SQL files and make necessary adjustments consistently across all the files. In particular, keep $${migrate_path} and ./src/Migrations/migrate.sql in sync." .PHONY : migration diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index bc8c063c..aab9c1af 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Net; using System.Net.Mime; +using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -13,8 +14,10 @@ using Database.Services; using Database.Utilities; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.WebUtilities; @@ -92,7 +95,12 @@ protected override void Dispose(bool disposing) // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [AllowAnonymous] + [Consumes(MediaTypeNames.Multipart.FormData)] + [AcceptsMultipartFormFile] // see `AcceptsMultipartFormFileAttribute` below [Produces(MediaTypeNames.Application.Json)] + [ProducesResponseType(StatusCodes.Status201Created)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task UploadFile( [FromQuery] Guid getHttpsResourceUuid, [FromServices] ApplicationDbContext context, @@ -223,4 +231,25 @@ private static Encoding GetEncoding(MultipartSection section) return mediaType.Encoding; } + + // solely for the OpenAPI documentation + private sealed record FormFile(IFormFile File); + + // solely for the OpenAPI documentation + private sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider + { + public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) + { + } + + public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) + { + builder.Metadata.Add( + new AcceptsMetadata( + [MediaTypeNames.Multipart.FormData], + typeof(FormFile) + ) + ); + } + } } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs similarity index 88% rename from backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs rename to backend/src/Data/AccessPolicies/AccessPolicyBase.cs index 04583567..6bbb8628 100644 --- a/backend/src/Data/AccessPolicies/StakeholderAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs @@ -6,12 +6,15 @@ namespace Database.Data.AccessPolicies; public abstract class AccessPolicyBase + : AuditableEntity { + public abstract DataAccessPolicy? DataAccessPolicy { get; set; } + public UpperLimitPerDuration? UpperAccessLimitPerTimeDuration { get; set; } public CountSinceTime? AccessCountSinceStartTime { get; set; } [Projectable] - public bool IsAllowed => UpperAccessLimitPerTimeDuration is null || ( + public bool IsAccessAllowed => UpperAccessLimitPerTimeDuration is null || ( UpperAccessLimitPerTimeDuration.UpperLimit > 0 && ( AccessCountSinceStartTime is null || ( IsWithinTimeSpan diff --git a/backend/src/Data/AccessPolicies/CountSinceTime.cs b/backend/src/Data/AccessPolicies/CountSinceTime.cs index edc4e00b..580bc3ac 100644 --- a/backend/src/Data/AccessPolicies/CountSinceTime.cs +++ b/backend/src/Data/AccessPolicies/CountSinceTime.cs @@ -1,7 +1,9 @@ using System; +using Microsoft.EntityFrameworkCore; namespace Database.Data.AccessPolicies; +[Owned] public sealed record CountSinceTime( uint AccessCount, DateTimeOffset? StartTime diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 31bba816..49c5bf5e 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -1,34 +1,212 @@ +using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using Database.ApiRequests; using Database.Enumerations; +using Database.Extensions; using EntityFrameworkCore.Projectables; namespace Database.Data.AccessPolicies; -public sealed class DataAccessPolicy +public sealed class DataAccessPolicy() + : AuditableEntity { - public LogicalCombinator Combinator { get; set; } = LogicalCombinator.ALL; + public const string TableName = "data_access_policy"; + + public const string GlobalPolicyCannotBeDeletedTriggerName = $"{TableName}_global_policy_cannot_be_deleted"; + public const string CanOnlyBeDeletedAlongsideCorrespondingDataTriggerName = $"{TableName}_can_only_be_deleted_after_data"; + public const string DataIdCannotChangeTriggerName = $"{TableName}_data_id_cannot_change"; + public static readonly ImmutableArray TriggerNames = [ + GlobalPolicyCannotBeDeletedTriggerName, + CanOnlyBeDeletedAlongsideCorrespondingDataTriggerName, + DataIdCannotChangeTriggerName + ]; + + public static readonly ImmutableArray<(string Field, string Table)> DataIdFieldAndDataTableNames = [ + (nameof(CalorimetricDataId), CalorimetricData.TableName), + (nameof(GeometricDataId), GeometricData.TableName), + (nameof(HygrothermalDataId), HygrothermalData.TableName), + (nameof(LifeCycleDataId), LifeCycleData.TableName), + (nameof(OpticalDataId), OpticalData.TableName), + (nameof(PhotovoltaicDataId), PhotovoltaicData.TableName) + ]; + + public DataAccessPolicy( + Guid? calorimetricDataId, + Guid? geometricDataId, + Guid? hygrothermalDataId, + Guid? lifeCycleDataId, + Guid? opticalDataId, + Guid? photovoltaicDataId + ) + : this() + { + CalorimetricDataId = calorimetricDataId; + GeometricDataId = geometricDataId; + HygrothermalDataId = hygrothermalDataId; + LifeCycleDataId = lifeCycleDataId; + OpticalDataId = opticalDataId; + PhotovoltaicDataId = photovoltaicDataId; + AssertThatExactlyOneDataIdIsNonNull(); + } - public List? UserAccessPolicies { get; set; } - public List? InstitutionAccessPolicies { get; set; } - public List? OpenIdConnectApplicationAccessPolicies { get; set; } + private void AssertThatExactlyOneDataIdIsNonNull() + { + var nonNullDataIdCount = new Guid?[] { + CalorimetricDataId, + GeometricDataId, + HygrothermalDataId, + LifeCycleDataId, + OpticalDataId, + PhotovoltaicDataId + } + .NotNull() + .Count(); + if (nonNullDataIdCount is 0) + { + throw new InvalidOperationException("All data IDs are null."); + } + if (nonNullDataIdCount >= 2) + { + throw new InvalidOperationException("There is more than 1 non-null data ID."); + } + } + // Note that at least one data ID is always present. So `Guid.Empty` will never be used. [NotMapped] [Projectable] - public bool IsRestricted => - UserAccessPolicies is not null || - InstitutionAccessPolicies is not null || - OpenIdConnectApplicationAccessPolicies is not null; + public Guid? DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; [NotMapped] + public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; + [Projectable] - public bool AreUsersRestricted => UserAccessPolicies is not null; + public Guid? GetDataId(DataKind dataKind) => + dataKind switch + { + DataKind.CALORIMETRIC_DATA => CalorimetricDataId, + DataKind.GEOMETRIC_DATA => GeometricDataId, + DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, + DataKind.OPTICAL_DATA => OpticalDataId, + DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, + _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), + }; - [NotMapped] [Projectable] - public bool AreInstitutionsRestricted => InstitutionAccessPolicies is not null; + public IData? GetData(DataKind dataKind) => + dataKind switch + { + DataKind.CALORIMETRIC_DATA => CalorimetricData, + DataKind.GEOMETRIC_DATA => GeometricData, + DataKind.HYGROTHERMAL_DATA => HygrothermalData, + DataKind.LIFE_CYCLE_DATA => LifeCycleData, + DataKind.OPTICAL_DATA => OpticalData, + DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), + }; + + public Guid? CalorimetricDataId { get; private set; } + + [InverseProperty(nameof(CalorimetricData.AccessPolicy))] + public CalorimetricData? CalorimetricData { get; set; } + + public Guid? GeometricDataId { get; private set; } + + [InverseProperty(nameof(GeometricData.AccessPolicy))] + public GeometricData? GeometricData { get; set; } + + public Guid? HygrothermalDataId { get; private set; } + + [InverseProperty(nameof(HygrothermalData.AccessPolicy))] + public HygrothermalData? HygrothermalData { get; set; } + + public Guid? LifeCycleDataId { get; private set; } + + [InverseProperty(nameof(LifeCycleData.AccessPolicy))] + public LifeCycleData? LifeCycleData { get; set; } + + public Guid? OpticalDataId { get; private set; } + + [InverseProperty(nameof(OpticalData.AccessPolicy))] + public OpticalData? OpticalData { get; set; } + + public Guid? PhotovoltaicDataId { get; private set; } + + [InverseProperty(nameof(PhotovoltaicData.AccessPolicy))] + public PhotovoltaicData? PhotovoltaicData { get; set; } + + public LogicalCombinator Combinator { get; set; } = LogicalCombinator.ALL; + + [InverseProperty(nameof(UserAccessPolicy.DataAccessPolicy))] + public ICollection UserAccessPolicies { get; } = []; + + [InverseProperty(nameof(InstitutionAccessPolicy.DataAccessPolicy))] + public ICollection InstitutionAccessPolicies { get; } = []; + + [InverseProperty(nameof(OpenIdConnectApplicationAccessPolicy.DataAccessPolicy))] + public ICollection OpenIdConnectApplicationAccessPolicies { get; } = []; - [NotMapped] [Projectable] - public bool AreOpenIdConnectApplicationsRestricted => OpenIdConnectApplicationAccessPolicies is not null; + public bool IsAccessAllowed( + QueryCurrentUserOrInstitution.CurrentUser? currentUser, + IReadOnlyList? institutionIds, + string? openIdConnectClientId + ) => + ( + Combinator == LogicalCombinator.ALL && ( + !UserAccessPolicies.Any(_ => + currentUser == null || ( + _.UserId == currentUser.Uuid + && !_.IsAccessAllowed + ) + ) + && + !InstitutionAccessPolicies.Any(_ => + institutionIds == null || ( + institutionIds.Contains(_.InstitutionId) + && !_.IsAccessAllowed + ) + ) + && + !OpenIdConnectApplicationAccessPolicies.Any(_ => + openIdConnectClientId == null || ( + _.ClientId == openIdConnectClientId + && !_.IsAccessAllowed + ) + ) + ) + ) + || + ( + Combinator == LogicalCombinator.SOME && ( + UserAccessPolicies.Any(_ => + currentUser != null + && _.UserId == currentUser.Uuid + && _.IsAccessAllowed + ) + || + InstitutionAccessPolicies.Any(_ => + institutionIds != null + && institutionIds.Contains(_.InstitutionId) + && _.IsAccessAllowed + ) + || + OpenIdConnectApplicationAccessPolicies.Any(_ => + openIdConnectClientId != null + && _.ClientId == openIdConnectClientId + && _.IsAccessAllowed + ) + ) + ); + + public void Reset() + { + Combinator = LogicalCombinator.ALL; + UserAccessPolicies.Clear(); + InstitutionAccessPolicies.Clear(); + OpenIdConnectApplicationAccessPolicies.Clear(); + } } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs b/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs new file mode 100644 index 00000000..838b80e7 --- /dev/null +++ b/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs @@ -0,0 +1,99 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data.AccessPolicies; +using Database.GraphQl.AccessPolicies; +using Database.GraphQl.Extensions; +using GreenDonut; +using GreenDonut.Data; +using HotChocolate; +using HotChocolate.Data; +using HotChocolate.Resolvers; +using HotChocolate.Types; + +namespace Database.GraphQl; + +public sealed class DataAccessPolicyType + : ObjectType +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor + .Field(_ => _.UserAccessPolicies) + .Cost(1) + .ResolveWith(_ => Resolvers.GetUserAccessPoliciesAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(_ => _.InstitutionAccessPolicies) + .Cost(1) + .ResolveWith(_ => Resolvers.GetInstitutionAccessPoliciesAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(_ => _.OpenIdConnectApplicationAccessPolicies) + .Cost(1) + .ResolveWith(_ => Resolvers.GetOpenIdConnectApplicationAccessPoliciesAsync(default!, default!, default!, default!, default!)); + } + + private sealed class Resolvers + { + [UseFiltering] + [UseSorting] + public static async Task GetUserAccessPoliciesAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + IUserAccessPoliciesByDataAccessPolicyIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return Array.Empty(); + } + return await byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + } + + [UseFiltering] + [UseSorting] + public static async Task GetInstitutionAccessPoliciesAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + IInstitutionAccessPoliciesByDataAccessPolicyIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return Array.Empty(); + } + return await byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + } + + [UseFiltering] + [UseSorting] + public static async Task GetOpenIdConnectApplicationAccessPoliciesAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IResolverContext resolverContext, + IOpenIdConnectApplicationAccessPoliciesByDataAccessPolicyIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return Array.Empty(); + } + return await byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + } + } +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs index 18a6f3e9..6fed9c72 100644 --- a/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs @@ -1,13 +1,18 @@ using System; -using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; namespace Database.Data.AccessPolicies; -[PrimaryKey(nameof(InstitutionId))] public sealed class InstitutionAccessPolicy( + Guid dataAccessPolicyId, Guid institutionId ) : AccessPolicyBase { + [InverseProperty(nameof(DataAccessPolicy.InstitutionAccessPolicies))] + public override DataAccessPolicy? DataAccessPolicy { get; set; } + + public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; + public Guid InstitutionId { get; private set; } = institutionId; } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs index 48a0f406..e12c6359 100644 --- a/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs @@ -1,12 +1,18 @@ -using Microsoft.EntityFrameworkCore; +using System; +using System.ComponentModel.DataAnnotations.Schema; namespace Database.Data.AccessPolicies; -[PrimaryKey(nameof(ClientId))] public sealed class OpenIdConnectApplicationAccessPolicy( + Guid dataAccessPolicyId, string clientId ) : AccessPolicyBase { + [InverseProperty(nameof(DataAccessPolicy.OpenIdConnectApplicationAccessPolicies))] + public override DataAccessPolicy? DataAccessPolicy { get; set; } + + public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; + public string ClientId { get; private set; } = clientId; } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs b/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs index 07cc272d..159145d1 100644 --- a/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs +++ b/backend/src/Data/AccessPolicies/UpperLimitPerDuration.cs @@ -1,7 +1,9 @@ using System; +using Microsoft.EntityFrameworkCore; namespace Database.Data.AccessPolicies; +[Owned] public sealed record UpperLimitPerDuration( uint UpperLimit, TimeSpan? Duration diff --git a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs index 12cbf83a..cae78ba1 100644 --- a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs @@ -1,13 +1,18 @@ using System; -using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; namespace Database.Data.AccessPolicies; -[PrimaryKey(nameof(UserId))] public sealed class UserAccessPolicy( + Guid dataAccessPolicyId, Guid userId ) : AccessPolicyBase { + [InverseProperty(nameof(DataAccessPolicy.UserAccessPolicies))] + public override DataAccessPolicy? DataAccessPolicy { get; set; } + + public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; + public Guid UserId { get; private set; } = userId; } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 7ed19e36..15f1c4f3 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -16,6 +16,7 @@ using Database.GraphQl; using Database.Data.Extensions; using Database.Data.AccessPolicies; +using Npgsql.EntityFrameworkCore.PostgreSQL.ValueGeneration; namespace Database.Data; @@ -56,6 +57,7 @@ IClock clock public DbSet GetHttpsResources { get; private set; } = default!; public DbSet Users { get; private set; } = default!; public DbSet DataProtectionKeys { get; private set; } = default!; + public DbSet DataAccessPolicies { get; private set; } = default!; public DbSet UserAccessPolicies { get; private set; } = default!; public DbSet InstitutionAccessPolicies { get; private set; } = default!; public DbSet OpenIdConnectApplicationAccessPolicies { get; private set; } = default!; @@ -234,26 +236,6 @@ public IAsyncEnumerable GetAllDataAsync( ); } - private static - EntityTypeBuilder - ConfigureEntity( - EntityTypeBuilder builder - ) - where TEntity : Entity - { - // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - builder - .HasKey(e => e.Id); - builder - .Property(e => e.Id) - .HasDefaultValueSql("gen_random_uuid()"); - // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column - builder - .Property(e => e.Version) - .IsRowVersion(); - return builder; - } - private static EntityTypeBuilder ConfigureData( @@ -261,28 +243,6 @@ EntityTypeBuilder builder ) where TData : class, IData { - builder - .ComplexProperty( - data => data.AccessPolicy, - _ => _.ToJson() - ); - // .OwnsOne( - // dataAccessPolicy => - // { - // dataAccessPolicy - // // The issues - // // https://github.com/dotnet/efcore/issues/33170#issuecomment-1966366300 - // // https://github.com/dotnet/efcore/issues/31238 - // // track the missing support of complex properties in owned types, - // // which would be used as below: - // // .ComplexProperty(_ => _.GrantedUserAndQuantity, d => d.ToJson()) - // .Property(_ => _.GrantedUserAndQuantity) - // .HasConversion( - // dictionary => JsonSerializer.Serialize(dictionary), - // stringValue => JsonSerializer.Deserialize>(stringValue) ?? new() - // ); - // } - // ); builder .OwnsOne( data => data.AppliedMethod, @@ -419,6 +379,11 @@ EntityTypeBuilder builder .WithOne(_ => _.CalorimetricData) .HasForeignKey(_ => _.CalorimetricDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.CalorimetricData) + .HasForeignKey(_ => _.CalorimetricDataId) + .OnDelete(DeleteBehavior.Cascade); return builder; } @@ -433,6 +398,11 @@ EntityTypeBuilder builder .WithOne(_ => _.GeometricData) .HasForeignKey(_ => _.GeometricDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.GeometricData) + .HasForeignKey(_ => _.GeometricDataId) + .OnDelete(DeleteBehavior.Cascade); return builder; } @@ -447,6 +417,11 @@ EntityTypeBuilder builder .WithOne(_ => _.HygrothermalData) .HasForeignKey(_ => _.HygrothermalDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.HygrothermalData) + .HasForeignKey(_ => _.HygrothermalDataId) + .OnDelete(DeleteBehavior.Cascade); return builder; } @@ -461,6 +436,11 @@ EntityTypeBuilder builder .WithOne(_ => _.LifeCycleData) .HasForeignKey(_ => _.LifeCycleDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.LifeCycleData) + .HasForeignKey(_ => _.LifeCycleDataId) + .OnDelete(DeleteBehavior.Cascade); return builder; } @@ -475,6 +455,11 @@ EntityTypeBuilder builder .WithOne(_ => _.OpticalData) .HasForeignKey(_ => _.OpticalDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.OpticalData) + .HasForeignKey(_ => _.OpticalDataId) + .OnDelete(DeleteBehavior.Cascade); builder.OwnsMany( data => data.CielabColors ).ToTable( @@ -500,18 +485,183 @@ EntityTypeBuilder builder .WithOne(_ => _.PhotovoltaicData) .HasForeignKey(_ => _.PhotovoltaicDataId) .OnDelete(DeleteBehavior.Cascade); + builder + .HasOne(_ => _.AccessPolicy) + .WithOne(_ => _.PhotovoltaicData) + .HasForeignKey(_ => _.PhotovoltaicDataId) + .OnDelete(DeleteBehavior.Cascade); + return builder; + } + + private static + EntityTypeBuilder + ConfigureDataAccessPolicy( + EntityTypeBuilder builder + ) + { + // each data entity has at most one access policy and there is at most one global access policy (no data ID) + builder + .HasIndex(_ => new + { + _.CalorimetricDataId, + _.GeometricDataId, + _.HygrothermalDataId, + _.LifeCycleDataId, + _.OpticalDataId, + _.PhotovoltaicDataId + }) + .IsUnique(true) + .AreNullsDistinct(false); + // configure one-to-many associations + builder + .HasMany(_ => _.UserAccessPolicies) + .WithOne(_ => _.DataAccessPolicy) + .HasForeignKey(_ => _.DataAccessPolicyId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasMany(_ => _.InstitutionAccessPolicies) + .WithOne(_ => _.DataAccessPolicy) + .HasForeignKey(_ => _.DataAccessPolicyId) + .OnDelete(DeleteBehavior.Cascade); + builder + .HasMany(_ => _.OpenIdConnectApplicationAccessPolicies) + .WithOne(_ => _.DataAccessPolicy) + .HasForeignKey(_ => _.DataAccessPolicyId) + .OnDelete(DeleteBehavior.Cascade); + // add partial indexes, see https://www.postgresql.org/docs/18/indexes-partial.html + builder + .HasIndex(_ => new { _.CalorimetricDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.CalorimetricDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .HasIndex(_ => new { _.GeometricDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.GeometricDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .HasIndex(_ => new { _.HygrothermalDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.HygrothermalDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .HasIndex(_ => new { _.LifeCycleDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.LifeCycleDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .HasIndex(_ => new { _.OpticalDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.OpticalDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .HasIndex(_ => new { _.PhotovoltaicDataId }) + .HasFilter( + $""" + {nameof(DataAccessPolicy.PhotovoltaicDataId).Enquote()} IS NOT NULL + """ + ) + .IsUnique(true); + builder + .BeforeUpdate(trigger => trigger + .SetTriggerName(DataAccessPolicy.DataIdCannotChangeTriggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF COALESCE({string.Join(", ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => $"OLD.{_.Field.Enquote()}"))}) <> COALESCE({string.Join(", ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => $"NEW.{_.Field.Enquote()}"))}) + THEN + RAISE EXCEPTION 'You cannot change the data ID of a data access policy.'; + END IF; + """ + ) + ) + ); + builder + .BeforeDelete(trigger => trigger + .SetTriggerName(DataAccessPolicy.GlobalPolicyCannotBeDeletedTriggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF {string.Join(" AND ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => $"OLD.{_.Field.Enquote()} IS NULL"))} + THEN + RAISE EXCEPTION 'You cannot delete the global data access policy.'; + END IF; + """ + ) + ) + ); + builder + .BeforeDelete(trigger => trigger + .SetTriggerName(DataAccessPolicy.CanOnlyBeDeletedAlongsideCorrespondingDataTriggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF ( + {string.Join(" OR ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => $""" + (OLD.{_.Field.Enquote()} IS NOT NULL AND EXISTS ( + SELECT 1 FROM database.{_.Table.Enquote()} + WHERE "Id" = OLD.{_.Field.Enquote()} + )) + """ + ))} + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.'; + END IF; + """ + ) + ) + ); return builder; } private static - EntityTypeBuilder - ConfigureAccessPolicy( - EntityTypeBuilder builder + EntityTypeBuilder + ConfigureUserAccessPolicy( + EntityTypeBuilder builder ) - where TAccessPolicy : AccessPolicyBase { - builder.ComplexProperty(_ => _.UpperAccessLimitPerTimeDuration, _ => _.ToJson()); - builder.ComplexProperty(_ => _.AccessCountSinceStartTime, _ => _.ToJson()); + builder + .HasIndex(_ => new { _.DataAccessPolicyId, _.UserId }) + .IsUnique(true); + return builder; + } + + private static + EntityTypeBuilder + ConfigureInstitutionAccessPolicy( + EntityTypeBuilder builder + ) + { + builder + .HasIndex(_ => new { _.DataAccessPolicyId, _.InstitutionId }) + .IsUnique(true); + return builder; + } + + private static + EntityTypeBuilder + ConfigureOpenIdConnectApplicationAccessPolicy( + EntityTypeBuilder builder + ) + { + builder + .HasIndex(_ => new { _.DataAccessPolicyId, _.ClientId }) + .IsUnique(true); return builder; } @@ -520,10 +670,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); modelBuilder.HasDefaultSchema(_schemaName); modelBuilder.HasPostgresExtension("pgcrypto"); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation - ConfigureEntity( - ConfigureGetHttpsResource(modelBuilder.Entity()) - ) - .ToTable( + ConfigureGetHttpsResource( + modelBuilder.Entity() + ).ToTable( GetHttpsResource.TableName, _ => { @@ -546,56 +695,72 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ); ConfigureCalorimetricData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("calorimetric_data"); + ).ToTable(global::Database.Data.CalorimetricData.TableName); ConfigureGeometricData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("geometric_data"); + ).ToTable(global::Database.Data.GeometricData.TableName); ConfigureHygrothermalData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("hygrothermal_data"); + ).ToTable(global::Database.Data.HygrothermalData.TableName); ConfigureLifeCycleData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("lifeCycle_data"); + ).ToTable(global::Database.Data.LifeCycleData.TableName); ConfigureOpticalData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("optical_data"); + ).ToTable(global::Database.Data.OpticalData.TableName); ConfigurePhotovoltaicData( ConfigureData( - ConfigureEntity(modelBuilder.Entity()) + modelBuilder.Entity() ) - ).ToTable("photovoltaic_data"); - ConfigureAccessPolicy( + ).ToTable(global::Database.Data.PhotovoltaicData.TableName); + ConfigureDataAccessPolicy( + modelBuilder.Entity() + ).ToTable( + DataAccessPolicy.TableName, + _ => + { + _.HasCheckConstraint( + $"CK_{nameof(DataAccessPolicy)}_At_Most_One_Data_Set", + $"NUM_NONNULLS({string.Join(", ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => _.Field.Enquote()))}) <= 1" + ); + foreach (var triggerName in DataAccessPolicy.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); + ConfigureUserAccessPolicy( modelBuilder.Entity() ).ToTable("user_access_policy"); - ConfigureAccessPolicy( + ConfigureInstitutionAccessPolicy( modelBuilder.Entity() ).ToTable("institution_access_policy"); - ConfigureAccessPolicy( + ConfigureOpenIdConnectApplicationAccessPolicy( modelBuilder.Entity() ).ToTable("open_id_connect_application_access_policy"); - ConfigureEntity( - modelBuilder.Entity() - ) - .ToTable("user"); + modelBuilder.Entity() + .ToTable("user"); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { if (typeof(IEntity).IsAssignableFrom(entityType.ClrType)) { var entity = modelBuilder.Entity(entityType.ClrType); + entity.HasKey(nameof(IEntity.Id)); // https://www.npgsql.org/efcore/modeling/generated-properties.html#guiduuid-generation entity .Property(nameof(IEntity.Id)) - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()") + .HasValueGenerator(); // https://www.npgsql.org/efcore/modeling/concurrency.html#the-postgresql-xmin-system-column entity .Property(nameof(IEntity.Version)) diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index 26e0d52e..ede597e1 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using Database.Data.AccessPolicies; using Database.Extractors; namespace Database.Data; @@ -9,6 +10,8 @@ namespace Database.Data; public sealed class CalorimetricData : DataX { + public const string TableName = "calorimetric_data"; + public CalorimetricData( Guid? userId, string locale, @@ -67,6 +70,9 @@ double[] uValues [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.CalorimetricData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public double[] GValues { get; private set; } public double[] UValues { get; private set; } diff --git a/backend/src/Data/DataReference.cs b/backend/src/Data/DataReference.cs new file mode 100644 index 00000000..77f2ff23 --- /dev/null +++ b/backend/src/Data/DataReference.cs @@ -0,0 +1,15 @@ +using System; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; + +namespace Database.Data; + +[Owned] +public sealed class DataReference( + Guid dataId, + DataKind dataKind +) +{ + public Guid DataId { get; private set; } = dataId; + public DataKind DataKind { get; private set; } = dataKind; +} \ No newline at end of file diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index f8227cee..b2e37990 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -101,7 +101,7 @@ public void Retract() // strictly enforced by PostgreSQL database constraints. public abstract ICollection Resources { get; } - public DataAccessPolicy? AccessPolicy { get; set; } + public abstract DataAccessPolicy? AccessPolicy { get; set; } public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index bb5098d8..02761f66 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using Database.Data.AccessPolicies; using Database.Extractors; namespace Database.Data; @@ -9,6 +10,8 @@ namespace Database.Data; public sealed class GeometricData : DataX { + public const string TableName = "geometric_data"; + public GeometricData( Guid? userId, string locale, @@ -70,6 +73,9 @@ double[] thicknesses [InverseProperty(nameof(GetHttpsResource.GeometricData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.GeometricData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public double[] Widths { get; private set; } public double[] Heights { get; private set; } public double[] Thicknesses { get; private set; } diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 26ee023f..50f71ae9 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -19,8 +19,8 @@ public sealed class GetHttpsResource public const string FilesDirectoryPath = "./files/"; public const string TableName = "get_https_resource"; - public const string DataIdsMustMatchTriggerName = "data_ids_must_match"; - public const string DataIdCannotChangeTriggerName = "data_id_cannot_change"; + public const string DataIdsMustMatchTriggerName = $"{TableName}_data_ids_must_match"; + public const string DataIdCannotChangeTriggerName = $"{TableName}_data_id_cannot_change"; public static readonly ImmutableArray TriggerNames = [ DataIdsMustMatchTriggerName, DataIdCannotChangeTriggerName diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 79f79f12..d04236e6 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using Database.Data.AccessPolicies; namespace Database.Data; public sealed class HygrothermalData : DataX { + public const string TableName = "hygrothermal_data"; + public HygrothermalData( Guid? userId, string locale, @@ -58,6 +61,9 @@ DateTimeOffset createdAt [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.HygrothermalData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public override Task ExtractAndSetValuesFromFile( string filePath, Guid dataFormatId diff --git a/backend/src/Data/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs index c5b67c55..4eb656cb 100644 --- a/backend/src/Data/LifeCycleData.cs +++ b/backend/src/Data/LifeCycleData.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using Database.Data.AccessPolicies; namespace Database.Data; public sealed class LifeCycleData : DataX { + public const string TableName = "lifeCycle_data"; + public LifeCycleData( Guid? userId, string locale, @@ -58,6 +61,9 @@ DateTimeOffset createdAt [InverseProperty(nameof(GetHttpsResource.LifeCycleData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.LifeCycleData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public override Task ExtractAndSetValuesFromFile( string filePath, Guid dataFormatId diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 9c929b5c..d0a6d72d 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -5,12 +5,16 @@ using Database.Enumerations; using Database.Enumerations.DataPoints; using Database.Extractors; +using Database.Data.AccessPolicies; namespace Database.Data; public sealed class OpticalData : DataX { + public const string TableName = "optical_data"; + + public OpticalData( Guid? userId, string locale, @@ -99,6 +103,9 @@ double[] colorRenderingIndices [InverseProperty(nameof(GetHttpsResource.OpticalData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.OpticalData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public OpticalComponentType? Type { get; private set; } public OpticalComponentSubtype? Subtype { get; private set; } public CoatedSide? CoatedSide { get; private set; } diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index 9ec61855..af8bb426 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; +using Database.Data.AccessPolicies; namespace Database.Data; public sealed class PhotovoltaicData : DataX { + public const string TableName = "photovoltaic_data"; + public PhotovoltaicData( Guid? userId, string locale, @@ -58,6 +61,9 @@ DateTimeOffset createdAt [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] public override ICollection Resources { get; } = []; + [InverseProperty(nameof(DataAccessPolicy.PhotovoltaicData))] + public override DataAccessPolicy? AccessPolicy { get; set; } + public override Task ExtractAndSetValuesFromFile( string filePath, Guid dataFormatId diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs index 1b7c99f3..e473b775 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs @@ -15,53 +15,73 @@ public sealed class InstitutionAccessPolicyLoaders : DataLoaders { [DataLoader] - public static async ValueTask> GetUserAccessPolicyByUserIdAsync( - IReadOnlyList userIds, - QueryContext queryContext, + public static async ValueTask> GetDataAccessPolicyByDataIdAsync( + IReadOnlyList dataIds, + QueryContext queryContext, IDbContextFactory databaseContextFactory, CancellationToken cancellationToken ) { await using var databaseContext = databaseContextFactory.CreateDbContext(); - return await databaseContext.UserAccessPolicies + return await databaseContext.DataAccessPolicies .AsNoTrackingWithIdentityResolution() - .Where(_ => userIds.Contains(_.UserId)) - .With(queryContext, _ => _.AddDescending(_ => _.UserId)) - .ToDictionaryAsync(_ => _.UserId, cancellationToken); + .Where(_ => dataIds.Contains(_.DataId ?? Guid.Empty)) + .With(queryContext, Sorting.DefaultEntityOrder) + .ToDictionaryAsync(_ => _.DataId ?? Guid.Empty, cancellationToken); } [DataLoader] - public static async ValueTask> GetInstitutionAccessPolicyByInstitutionIdAsync( - IReadOnlyList institutionIds, + public static ValueTask> GetUserAccessPoliciesByDataAccessPolicyIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.UserAccessPolicies, + _ => _.DataAccessPolicyId, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetInstitutionAccessPoliciesByDataAccessPolicyIdAsync( + IReadOnlyList ids, QueryContext queryContext, IDbContextFactory databaseContextFactory, CancellationToken cancellationToken ) { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); - return await databaseContext.InstitutionAccessPolicies - .AsNoTrackingWithIdentityResolution() - .Where(_ => institutionIds.Contains(_.InstitutionId)) - .With(queryContext, _ => _.AddDescending(_ => _.InstitutionId)) - .ToDictionaryAsync(_ => _.InstitutionId, cancellationToken); + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.InstitutionAccessPolicies, + _ => _.DataAccessPolicyId, + queryContext, + databaseContextFactory, + cancellationToken + ); } [DataLoader] - public static async ValueTask> GetOpenIdConnectApplicationAccessPolicyByClientIdAsync( - IReadOnlyList clientIds, + public static ValueTask> GetOpenIdConnectApplicationAccessPoliciesByDataAccessPolicyIdAsync( + IReadOnlyList ids, QueryContext queryContext, IDbContextFactory databaseContextFactory, CancellationToken cancellationToken ) { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); - return await databaseContext.OpenIdConnectApplicationAccessPolicies - .AsNoTrackingWithIdentityResolution() - .Where(_ => clientIds.Contains(_.ClientId ?? "")) - .With(queryContext, _ => _.AddDescending(_ => _.ClientId)) - .ToDictionaryAsync(_ => _.ClientId ?? "", cancellationToken); + return GetManyByOneIdAsync( + ids, + (databaseContext) => databaseContext.OpenIdConnectApplicationAccessPolicies, + _ => _.DataAccessPolicyId, + queryContext, + databaseContextFactory, + cancellationToken + ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs index 9836a2f1..4fb01360 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -1,12 +1,10 @@ -using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Data.AccessPolicies; -using Database.GraphQl.Extensions; using GreenDonut.Data; -using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -15,66 +13,18 @@ namespace Database.GraphQl.AccessPolicies; [ExtendObjectType(nameof(Query))] public sealed class AccessPolicyQueries { - public Task GetUserAccessPolicyAsync( - Guid userId, - IUserAccessPolicyByUserIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return byId.LoadAsync(userId, cancellationToken); - } - - public ValueTask> GetUserAccessPoliciesAsync( - ApplicationDbContext databaseContext, - IResolverContext resolverContext, - CancellationToken cancellationToken - ) - { - return databaseContext.UserAccessPolicies.AsNoTracking() - .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.UserId)) - .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) - .ToConnectionAsync(); - } - - public Task GetInstitutionAccessPolicyAsync( - Guid institutionId, - IInstitutionAccessPolicyByInstitutionIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return byId.LoadAsync(institutionId, cancellationToken); - } - - public ValueTask> GetInstitutionAccessPoliciesAsync( - ApplicationDbContext databaseContext, - IResolverContext resolverContext, - CancellationToken cancellationToken - ) - { - return databaseContext.InstitutionAccessPolicies.AsNoTracking() - .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.InstitutionId)) - .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) - .ToConnectionAsync(); - } - - public Task GetOpenIdConnectApplicationAccessPolicyAsync( - string clientId, - IOpenIdConnectApplicationAccessPolicyByClientIdDataLoader byId, - CancellationToken cancellationToken - ) - { - return byId.LoadAsync(clientId, cancellationToken); - } - - public ValueTask> GetOpenIdConnectApplicationAccessPoliciesAsync( + public async Task GetGlobalDataAccessPolicyAsync( + CommonAuthorization authorization, ApplicationDbContext databaseContext, - IResolverContext resolverContext, CancellationToken cancellationToken ) { - return databaseContext.OpenIdConnectApplicationAccessPolicies.AsNoTracking() - .With(resolverContext.GetQueryContext(), _ => _.AddDescending(_ => _.ClientId)) - .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) - .ToConnectionAsync(); + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return null; + } + return await databaseContext.DataAccessPolicies.AsNoTracking() + .Where(_ => _.DataId == null) + .SingleAsync(cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs index dad93fbb..de1cb682 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs @@ -7,11 +7,12 @@ using Database.Data.AccessPolicies; using Database.Extensions; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicy; [SuppressMessage("Naming", "CA1707")] -public enum ClearDataAccessPolicyErrorCode +public enum ClearDataAccessPoliciesErrorCode { UNKNOWN, UNAUTHENTICATED, @@ -19,42 +20,42 @@ public enum ClearDataAccessPolicyErrorCode UNKNOWN_DATA } -public sealed record ClearDataAccessPolicyError( - ClearDataAccessPolicyErrorCode Code, +public sealed record ClearDataAccessPoliciesError( + ClearDataAccessPoliciesErrorCode Code, string Message, IReadOnlyList Path ) -: UserErrorBase(Code, Message, Path); +: UserErrorBase(Code, Message, Path); -public sealed record ClearDataAccessPolicyPayload( +public sealed record ClearDataAccessPoliciesPayload( IReadOnlyCollection? DataAccessPolicy, - IReadOnlyCollection? Errors + IReadOnlyCollection? Errors ) : Payload; [ExtendObjectType(nameof(Mutation))] -public sealed class ClearDataAccessPolicyMutation -: MutationBase, ClearDataAccessPolicyPayload, ClearDataAccessPolicyError, ClearDataAccessPolicyErrorCode> +public sealed class ClearDataAccessPoliciesMutation +: MutationBase, ClearDataAccessPoliciesPayload, ClearDataAccessPoliciesError, ClearDataAccessPoliciesErrorCode> { - protected override ClearDataAccessPolicyPayload NewPayload( + protected override ClearDataAccessPoliciesPayload NewPayload( IReadOnlyCollection? data, - IReadOnlyCollection? errors + IReadOnlyCollection? errors ) => new(data, errors); - protected override ClearDataAccessPolicyError NewError( - ClearDataAccessPolicyErrorCode code, + protected override ClearDataAccessPoliciesError NewError( + ClearDataAccessPoliciesErrorCode code, string message, IReadOnlyList path ) => new(code, message, path); - public async Task ClearDataAccessPolicyAsync( + public async Task ClearDataAccessPoliciesAsync( ApplicationDbContext context, CommonAuthorization authorization, CancellationToken cancellationToken ) { if ((await AuthorizeAsync( - ClearDataAccessPolicyErrorCode.UNAUTHENTICATED, - ClearDataAccessPolicyErrorCode.UNAUTHORIZED, + ClearDataAccessPoliciesErrorCode.UNAUTHENTICATED, + ClearDataAccessPoliciesErrorCode.UNAUTHORIZED, authorization, cancellationToken ) @@ -63,16 +64,9 @@ CancellationToken cancellationToken { return authorizeErrorPayload; } - var accessPolicies = new List(); - await foreach (var data in context.GetAllDataAsync()) - { - if (data.AccessPolicy is not null) - { - accessPolicies.Add(data.AccessPolicy); - data.AccessPolicy = null; - } - } + var policies = await context.DataAccessPolicies.ToListAsync(cancellationToken); + context.DataAccessPolicies.RemoveRange(policies); await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicies, null); + return NewPayload(policies, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs index d36e5621..19debd27 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs @@ -11,12 +11,18 @@ namespace Database.GraphQl.AccessPolicies; +public sealed record ClearInstitutionAccessPoliciesInput +( + DataReferenceInput? Data +); + [SuppressMessage("Naming", "CA1707")] public enum ClearInstitutionAccessPoliciesErrorCode { UNKNOWN, UNAUTHENTICATED, - UNAUTHORIZED + UNAUTHORIZED, + UNKNOWN_DATA } public sealed record ClearInstitutionAccessPoliciesError( @@ -27,16 +33,16 @@ IReadOnlyList Path : UserErrorBase(Code, Message, Path); public sealed record ClearInstitutionAccessPoliciesPayload( - IReadOnlyCollection? InstitutionAccessPolicies, + DataAccessPolicy? DataAccessPolicy, IReadOnlyCollection? Errors ) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class ClearInstitutionAccessPoliciesMutation -: MutationBase, ClearInstitutionAccessPoliciesPayload, ClearInstitutionAccessPoliciesError, ClearInstitutionAccessPoliciesErrorCode> +: DataMutationBase { protected override ClearInstitutionAccessPoliciesPayload NewPayload( - IReadOnlyCollection? data, + DataAccessPolicy? data, IReadOnlyCollection? errors ) => new(data, errors); @@ -47,6 +53,7 @@ IReadOnlyList path ) => new(code, message, path); public async Task ClearInstitutionAccessPoliciesAsync( + ClearInstitutionAccessPoliciesInput input, ApplicationDbContext context, CommonAuthorization authorization, CancellationToken cancellationToken @@ -63,9 +70,29 @@ CancellationToken cancellationToken { return authorizeErrorPayload; } - var accessPolicies = await context.InstitutionAccessPolicies.ToListAsync(cancellationToken); - context.InstitutionAccessPolicies.RemoveRange(accessPolicies); + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + ClearInstitutionAccessPoliciesErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + dataAccessPolicy.InstitutionAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicies, null); + return NewPayload(dataAccessPolicy, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs index 4d2011b1..cb7dcdd3 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs @@ -11,12 +11,18 @@ namespace Database.GraphQl.AccessPolicies; +public sealed record ClearOpenIdConnectApplicationAccessPoliciesInput +( + DataReferenceInput? Data +); + [SuppressMessage("Naming", "CA1707")] public enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { UNKNOWN, UNAUTHENTICATED, - UNAUTHORIZED + UNAUTHORIZED, + UNKNOWN_DATA } public sealed record ClearOpenIdConnectApplicationAccessPoliciesError( @@ -27,16 +33,16 @@ IReadOnlyList Path : UserErrorBase(Code, Message, Path); public sealed record ClearOpenIdConnectApplicationAccessPoliciesPayload( - IReadOnlyCollection? OpenIdConnectApplicationAccessPolicies, + DataAccessPolicy? DataAccessPolicy, IReadOnlyCollection? Errors ) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class ClearOpenIdConnectApplicationAccessPoliciesMutation -: MutationBase, ClearOpenIdConnectApplicationAccessPoliciesPayload, ClearOpenIdConnectApplicationAccessPoliciesError, ClearOpenIdConnectApplicationAccessPoliciesErrorCode> +: DataMutationBase { protected override ClearOpenIdConnectApplicationAccessPoliciesPayload NewPayload( - IReadOnlyCollection? data, + DataAccessPolicy? data, IReadOnlyCollection? errors ) => new(data, errors); @@ -47,6 +53,7 @@ IReadOnlyList path ) => new(code, message, path); public async Task ClearOpenIdConnectApplicationAccessPoliciesAsync( + ClearOpenIdConnectApplicationAccessPoliciesInput input, ApplicationDbContext context, CommonAuthorization authorization, CancellationToken cancellationToken @@ -63,9 +70,29 @@ CancellationToken cancellationToken { return authorizeErrorPayload; } - var accessPolicies = await context.OpenIdConnectApplicationAccessPolicies.ToListAsync(cancellationToken); - context.OpenIdConnectApplicationAccessPolicies.RemoveRange(accessPolicies); + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + ClearOpenIdConnectApplicationAccessPoliciesErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + dataAccessPolicy.OpenIdConnectApplicationAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicies, null); + return NewPayload(dataAccessPolicy, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs index 31273332..e662dfb7 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs @@ -11,12 +11,18 @@ namespace Database.GraphQl.AccessPolicies; +public sealed record ClearUserAccessPoliciesInput +( + DataReferenceInput? Data +); + [SuppressMessage("Naming", "CA1707")] public enum ClearUserAccessPoliciesErrorCode { UNKNOWN, UNAUTHENTICATED, - UNAUTHORIZED + UNAUTHORIZED, + UNKNOWN_DATA } public sealed record ClearUserAccessPoliciesError( @@ -27,16 +33,16 @@ IReadOnlyList Path : UserErrorBase(Code, Message, Path); public sealed record ClearUserAccessPoliciesPayload( - IReadOnlyCollection? UserAccessPolicies, + DataAccessPolicy? DataAccessPolicy, IReadOnlyCollection? Errors ) : Payload; [ExtendObjectType(nameof(Mutation))] public sealed class ClearUserAccessPoliciesMutation -: MutationBase, ClearUserAccessPoliciesPayload, ClearUserAccessPoliciesError, ClearUserAccessPoliciesErrorCode> +: DataMutationBase { protected override ClearUserAccessPoliciesPayload NewPayload( - IReadOnlyCollection? data, + DataAccessPolicy? data, IReadOnlyCollection? errors ) => new(data, errors); @@ -47,6 +53,7 @@ IReadOnlyList path ) => new(code, message, path); public async Task ClearUserAccessPoliciesAsync( + ClearUserAccessPoliciesInput input, ApplicationDbContext context, CommonAuthorization authorization, CancellationToken cancellationToken @@ -63,9 +70,29 @@ CancellationToken cancellationToken { return authorizeErrorPayload; } - var accessPolicies = await context.UserAccessPolicies.ToListAsync(cancellationToken); - context.UserAccessPolicies.RemoveRange(accessPolicies); + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + ClearUserAccessPoliciesErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + dataAccessPolicy.UserAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicies, null); + return NewPayload(dataAccessPolicy, null); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs deleted file mode 100644 index d536f8a3..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyMutation.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteDataAccessPolicyInput -( - Guid DataId, - DataKind DataKind -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteDataAccessPolicyErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA -} - -public sealed record DeleteDataAccessPolicyError( - DeleteDataAccessPolicyErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteDataAccessPolicyPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteDataAccessPolicyMutation -: DataMutationBase -{ - protected override DeleteDataAccessPolicyPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteDataAccessPolicyError NewError( - DeleteDataAccessPolicyErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteDataAccessPolicyAsync( - DeleteDataAccessPolicyInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteDataAccessPolicyErrorCode.UNAUTHENTICATED, - DeleteDataAccessPolicyErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - DeleteDataAccessPolicyErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - data.AccessPolicy = null; - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs deleted file mode 100644 index dc03acc6..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfInstitutionMutation.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteDataAccessPolicyOfInstitutionInput -( - Guid DataId, - DataKind DataKind, - Guid InstitutionId -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteDataAccessPolicyOfInstitutionErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_INSTITUTION -} - -public sealed record DeleteDataAccessPolicyOfInstitutionError( - DeleteDataAccessPolicyOfInstitutionErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteDataAccessPolicyOfInstitutionPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteDataAccessPolicyOfInstitutionMutation -: DataMutationBase -{ - protected override DeleteDataAccessPolicyOfInstitutionPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteDataAccessPolicyOfInstitutionError NewError( - DeleteDataAccessPolicyOfInstitutionErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteDataAccessPolicyOfInstitutionAsync( - DeleteDataAccessPolicyOfInstitutionInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IInstitutionByIdDataLoader institutionByIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteDataAccessPolicyOfInstitutionErrorCode.UNAUTHENTICATED, - DeleteDataAccessPolicyOfInstitutionErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - DeleteDataAccessPolicyOfInstitutionErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await institutionByIdDataLoader.LoadAsync(input.InstitutionId, cancellationToken) is null) - { - errors.Add( - NewError( - DeleteDataAccessPolicyOfInstitutionErrorCode.UNKNOWN_INSTITUTION, - $"The institution does not exist.", - [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy?.InstitutionAccessPolicies?.RemoveAll(_ => _.InstitutionId == input.InstitutionId); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs deleted file mode 100644 index 6a54feff..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationInput -( - Guid DataId, - DataKind DataKind, - string ClientId -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_APPLICATION -} - -public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationError( - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteDataAccessPolicyOfOpenIdConnectApplicationMutation -: DataMutationBase -{ - protected override DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteDataAccessPolicyOfOpenIdConnectApplicationError NewError( - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteDataAccessPolicyOfOpenIdConnectApplicationAsync( - DeleteDataAccessPolicyOfOpenIdConnectApplicationInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByClientIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNAUTHENTICATED, - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await openIdConnectApplicationByClientIdDataLoader.LoadAsync(input.ClientId, cancellationToken) is null) - { - errors.Add( - NewError( - DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode.UNKNOWN_APPLICATION, - $"The Open ID Connect application does not exist.", - [nameof(input), nameof(input.ClientId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy?.OpenIdConnectApplicationAccessPolicies?.RemoveAll(_ => _.ClientId == input.ClientId); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs deleted file mode 100644 index 48be1842..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteDataAccessPolicyOfUserMutation.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteDataAccessPolicyOfUserInput -( - Guid DataId, - DataKind DataKind, - Guid UserId -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteDataAccessPolicyOfUserErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_USER -} - -public sealed record DeleteDataAccessPolicyOfUserError( - DeleteDataAccessPolicyOfUserErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteDataAccessPolicyOfUserPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteDataAccessPolicyOfUserMutation -: DataMutationBase -{ - protected override DeleteDataAccessPolicyOfUserPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteDataAccessPolicyOfUserError NewError( - DeleteDataAccessPolicyOfUserErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteDataAccessPolicyOfUserAsync( - DeleteDataAccessPolicyOfUserInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IUserByIdDataLoader userByIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteDataAccessPolicyOfUserErrorCode.UNAUTHENTICATED, - DeleteDataAccessPolicyOfUserErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - DeleteDataAccessPolicyOfUserErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await userByIdDataLoader.LoadAsync(input.UserId, cancellationToken) is null) - { - errors.Add( - NewError( - DeleteDataAccessPolicyOfUserErrorCode.UNKNOWN_USER, - $"The user does not exist.", - [nameof(input), nameof(input.UserId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy?.UserAccessPolicies?.RemoveAll(_ => _.UserId == input.UserId); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs deleted file mode 100644 index b0d61396..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteInstitutionAccessPolicyMutation.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteInstitutionAccessPolicyInput -( - Guid InstitutionId -); - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteInstitutionAccessPolicyErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_POLICY -} - -public sealed record DeleteInstitutionAccessPolicyError( - DeleteInstitutionAccessPolicyErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteInstitutionAccessPolicyPayload( - InstitutionAccessPolicy? InstitutionAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteInstitutionAccessPolicyMutation -: MutationBase -{ - protected override DeleteInstitutionAccessPolicyPayload NewPayload( - InstitutionAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteInstitutionAccessPolicyError NewError( - DeleteInstitutionAccessPolicyErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteInstitutionAccessPolicyAsync( - DeleteInstitutionAccessPolicyInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteInstitutionAccessPolicyErrorCode.UNAUTHENTICATED, - DeleteInstitutionAccessPolicyErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.InstitutionAccessPolicies - .SingleOrDefaultAsync(x => - x.InstitutionId == input.InstitutionId, - cancellationToken - ); - if (accessPolicy is null) - { - return NewPayload( - null, - [NewError( - DeleteInstitutionAccessPolicyErrorCode.UNKNOWN_POLICY, - $"The access policy does not exist.", - [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] - )] - ); - } - context.InstitutionAccessPolicies.Remove(accessPolicy); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs deleted file mode 100644 index 2c343b00..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteOpenIdConnectApplicationAccessPolicyMutation.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteOpenIdConnectApplicationAccessPolicyInput -( - string ClientId -); - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_POLICY -} - -public sealed record DeleteOpenIdConnectApplicationAccessPolicyError( - DeleteOpenIdConnectApplicationAccessPolicyErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteOpenIdConnectApplicationAccessPolicyPayload( - OpenIdConnectApplicationAccessPolicy? OpenIdConnectApplicationAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteOpenIdConnectApplicationAccessPolicyMutation -: MutationBase -{ - protected override DeleteOpenIdConnectApplicationAccessPolicyPayload NewPayload( - OpenIdConnectApplicationAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteOpenIdConnectApplicationAccessPolicyError NewError( - DeleteOpenIdConnectApplicationAccessPolicyErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteOpenIdConnectApplicationAccessPolicyAsync( - DeleteOpenIdConnectApplicationAccessPolicyInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHENTICATED, - DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.OpenIdConnectApplicationAccessPolicies - .SingleOrDefaultAsync(x => - x.ClientId == input.ClientId, - cancellationToken - ); - if (accessPolicy is null) - { - return NewPayload( - null, - [NewError( - DeleteOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_POLICY, - $"The access policy does not exist.", - [nameof(input), nameof(input.ClientId).ToLowerFirst()] - )] - ); - } - context.OpenIdConnectApplicationAccessPolicies.Remove(accessPolicy); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs deleted file mode 100644 index ddc05b88..00000000 --- a/backend/src/GraphQl/AccessPolicies/DeleteUserAccessPolicyMutation.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record DeleteUserAccessPolicyInput -( - Guid UserId -); - -[SuppressMessage("Naming", "CA1707")] -public enum DeleteUserAccessPolicyErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_POLICY -} - -public sealed record DeleteUserAccessPolicyError( - DeleteUserAccessPolicyErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record DeleteUserAccessPolicyPayload( - UserAccessPolicy? UserAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class DeleteUserAccessPolicyMutation -: MutationBase -{ - protected override DeleteUserAccessPolicyPayload NewPayload( - UserAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override DeleteUserAccessPolicyError NewError( - DeleteUserAccessPolicyErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task DeleteUserAccessPolicyAsync( - DeleteUserAccessPolicyInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - DeleteUserAccessPolicyErrorCode.UNAUTHENTICATED, - DeleteUserAccessPolicyErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.UserAccessPolicies - .SingleOrDefaultAsync(x => - x.UserId == input.UserId, - cancellationToken - ); - if (accessPolicy is null) - { - return NewPayload( - null, - [NewError( - DeleteUserAccessPolicyErrorCode.UNKNOWN_POLICY, - $"The access policy does not exist.", - [nameof(input), nameof(input.UserId).ToLowerFirst()] - )] - ); - } - context.UserAccessPolicies.Remove(accessPolicy); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs new file mode 100644 index 00000000..c50a9fd7 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record ResetDataAccessPolicyInput +( + DataReferenceInput? Data +); + +[SuppressMessage("Naming", "CA1707")] +public enum ResetDataAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ResetDataAccessPolicyError( + ResetDataAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ResetDataAccessPolicyPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ResetDataAccessPolicyMutation +: DataMutationBase +{ + protected override ResetDataAccessPolicyPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ResetDataAccessPolicyError NewError( + ResetDataAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ResetDataAccessPolicyAsync( + ResetDataAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ResetDataAccessPolicyErrorCode.UNAUTHENTICATED, + ResetDataAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + ResetDataAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + dataAccessPolicy.Reset(); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(dataAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs deleted file mode 100644 index 0acadfcb..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForInstitutionMutation.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetDataAccessLimitForInstitutionInput -( - Guid DataId, - DataKind DataKind, - Guid InstitutionId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum SetDataAccessLimitForInstitutionErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_INSTITUTION -} - -public sealed record SetDataAccessLimitForInstitutionError( - SetDataAccessLimitForInstitutionErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetDataAccessLimitForInstitutionPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetDataAccessLimitForInstitutionMutation -: DataMutationBase -{ - protected override SetDataAccessLimitForInstitutionPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetDataAccessLimitForInstitutionError NewError( - SetDataAccessLimitForInstitutionErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetDataAccessLimitForInstitutionAsync( - SetDataAccessLimitForInstitutionInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IInstitutionByIdDataLoader institutionByIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetDataAccessLimitForInstitutionErrorCode.UNAUTHENTICATED, - SetDataAccessLimitForInstitutionErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - SetDataAccessLimitForInstitutionErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await institutionByIdDataLoader.LoadAsync(input.InstitutionId, cancellationToken) is null) - { - errors.Add( - NewError( - SetDataAccessLimitForInstitutionErrorCode.UNKNOWN_INSTITUTION, - $"The institution does not exist.", - [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy ??= new DataAccessPolicy(); - data.AccessPolicy.InstitutionAccessPolicies ??= []; - var accessPolicy = - data.AccessPolicy.InstitutionAccessPolicies - .SingleOrDefault(_ => _.InstitutionId == input.InstitutionId); - if (accessPolicy is null) - { - accessPolicy = new InstitutionAccessPolicy(input.InstitutionId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - data.AccessPolicy.InstitutionAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs deleted file mode 100644 index d589cae6..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForOpenIdConnectApplicationMutation.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetDataAccessLimitForOpenIdConnectApplicationInput -( - Guid DataId, - DataKind DataKind, - string ClientId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_APPLICATION -} - -public sealed record SetDataAccessLimitForOpenIdConnectApplicationError( - SetDataAccessLimitForOpenIdConnectApplicationErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetDataAccessLimitForOpenIdConnectApplicationPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetDataAccessLimitForOpenIdConnectApplicationMutation -: DataMutationBase -{ - protected override SetDataAccessLimitForOpenIdConnectApplicationPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetDataAccessLimitForOpenIdConnectApplicationError NewError( - SetDataAccessLimitForOpenIdConnectApplicationErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetDataAccessLimitForOpenIdConnectApplicationAsync( - SetDataAccessLimitForOpenIdConnectApplicationInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByClientIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNAUTHENTICATED, - SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await openIdConnectApplicationByClientIdDataLoader.LoadAsync(input.ClientId, cancellationToken) is null) - { - errors.Add( - NewError( - SetDataAccessLimitForOpenIdConnectApplicationErrorCode.UNKNOWN_APPLICATION, - $"The Open ID Connect application does not exist.", - [nameof(input), nameof(input.ClientId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy ??= new DataAccessPolicy(); - data.AccessPolicy.OpenIdConnectApplicationAccessPolicies ??= []; - var accessPolicy = - data.AccessPolicy.OpenIdConnectApplicationAccessPolicies - .SingleOrDefault(_ => _.ClientId == input.ClientId); - if (accessPolicy is null) - { - accessPolicy = new OpenIdConnectApplicationAccessPolicy(input.ClientId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - data.AccessPolicy.OpenIdConnectApplicationAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs b/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs deleted file mode 100644 index 17c011dd..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetDataAccessLimitForUserMutation.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Database.ApiRequests; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Enumerations; -using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; -using HotChocolate.Types; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetDataAccessLimitForUserInput -( - Guid DataId, - DataKind DataKind, - Guid UserId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -) : IIdentifyDataInput; - -[SuppressMessage("Naming", "CA1707")] -public enum SetDataAccessLimitForUserErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA, - UNKNOWN_USER -} - -public sealed record SetDataAccessLimitForUserError( - SetDataAccessLimitForUserErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetDataAccessLimitForUserPayload( - DataAccessPolicy? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetDataAccessLimitForUserMutation -: DataMutationBase -{ - protected override SetDataAccessLimitForUserPayload NewPayload( - DataAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetDataAccessLimitForUserError NewError( - SetDataAccessLimitForUserErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetDataAccessLimitForUserAsync( - SetDataAccessLimitForUserInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - IUserByIdDataLoader userByIdDataLoader, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetDataAccessLimitForUserErrorCode.UNAUTHENTICATED, - SetDataAccessLimitForUserErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - - if ((await FetchDataAsync( - input, - SetDataAccessLimitForUserErrorCode.UNKNOWN_DATA, - context, - cancellationToken - ) - ).Failed(out var data, out var fetchDataErrorPayload) - ) - { - return fetchDataErrorPayload; - } - - List errors = []; - if (await userByIdDataLoader.LoadAsync(input.UserId, cancellationToken) is null) - { - errors.Add( - NewError( - SetDataAccessLimitForUserErrorCode.UNKNOWN_USER, - $"The user does not exist.", - [nameof(input), nameof(input.UserId).ToLowerFirst()] - ) - ); - } - if (errors.Count > 0) - { - return NewPayload(null, errors); - } - - data.AccessPolicy ??= new DataAccessPolicy(); - data.AccessPolicy.UserAccessPolicies ??= []; - var accessPolicy = - data.AccessPolicy.UserAccessPolicies - .SingleOrDefault(_ => _.UserId == input.UserId); - if (accessPolicy is null) - { - accessPolicy = new UserAccessPolicy(input.UserId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - data.AccessPolicy.UserAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - - await context.SaveChangesAsync(cancellationToken); - return NewPayload(data.AccessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs deleted file mode 100644 index 1acd909f..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessLimitMutation.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetInstitutionAccessLimitInput -( - Guid InstitutionId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -); - -[SuppressMessage("Naming", "CA1707")] -public enum SetInstitutionAccessLimitErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED -} - -public sealed record SetInstitutionAccessLimitError( - SetInstitutionAccessLimitErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetInstitutionAccessLimitPayload( - InstitutionAccessPolicy? InstitutionAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetInstitutionAccessLimitMutation -: MutationBase -{ - protected override SetInstitutionAccessLimitPayload NewPayload( - InstitutionAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetInstitutionAccessLimitError NewError( - SetInstitutionAccessLimitErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetInstitutionAccessLimitAsync( - SetInstitutionAccessLimitInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetInstitutionAccessLimitErrorCode.UNAUTHENTICATED, - SetInstitutionAccessLimitErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.InstitutionAccessPolicies - .SingleOrDefaultAsync(x => - x.InstitutionId == input.InstitutionId, - cancellationToken - ); - if (accessPolicy is null) - { - accessPolicy = new InstitutionAccessPolicy(input.InstitutionId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - context.InstitutionAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs new file mode 100644 index 00000000..5abf2202 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs @@ -0,0 +1,141 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using Database.ApiRequests; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using GraphQL.Client.Abstractions.Utilities; +using Database.Enumerations; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetInstitutionAccessPolicyInput +( + DataReferenceInput? Data, + Guid InstitutionId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetInstitutionAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_INSTITUTION +} + +public sealed record SetInstitutionAccessPolicyError( + SetInstitutionAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetInstitutionAccessPolicyPayload( + InstitutionAccessPolicy? InstitutionAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetInstitutionAccessPolicyMutation +: DataMutationBase +{ + protected override SetInstitutionAccessPolicyPayload NewPayload( + InstitutionAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetInstitutionAccessPolicyError NewError( + SetInstitutionAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetInstitutionAccessPolicyAsync( + SetInstitutionAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IInstitutionByIdDataLoader institutionByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetInstitutionAccessPolicyErrorCode.UNAUTHENTICATED, + SetInstitutionAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + SetInstitutionAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + List errors = []; + if (await institutionByIdDataLoader.LoadAsync(input.InstitutionId, cancellationToken) is null) + { + errors.Add( + NewError( + SetInstitutionAccessPolicyErrorCode.UNKNOWN_INSTITUTION, + $"The institution does not exist.", + [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + var institutionAccessPolicy = await context.InstitutionAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicyId == dataAccessPolicy.Id + && _.InstitutionId == input.InstitutionId, + cancellationToken + ); + if (institutionAccessPolicy is null) + { + institutionAccessPolicy = new InstitutionAccessPolicy(dataAccessPolicy.Id, input.InstitutionId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.InstitutionAccessPolicies.Add(institutionAccessPolicy); + } + else + { + institutionAccessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + institutionAccessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(institutionAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs deleted file mode 100644 index f8f3f384..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessLimitMutation.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetOpenIdConnectApplicationAccessLimitInput -( - string ClientId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -); - -[SuppressMessage("Naming", "CA1707")] -public enum SetOpenIdConnectApplicationAccessLimitErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED -} - -public sealed record SetOpenIdConnectApplicationAccessLimitError( - SetOpenIdConnectApplicationAccessLimitErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetOpenIdConnectApplicationAccessLimitPayload( - OpenIdConnectApplicationAccessPolicy? OpenIdConnectApplicationAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetOpenIdConnectApplicationAccessLimitMutation -: MutationBase -{ - protected override SetOpenIdConnectApplicationAccessLimitPayload NewPayload( - OpenIdConnectApplicationAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetOpenIdConnectApplicationAccessLimitError NewError( - SetOpenIdConnectApplicationAccessLimitErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetOpenIdConnectApplicationAccessLimitAsync( - SetOpenIdConnectApplicationAccessLimitInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetOpenIdConnectApplicationAccessLimitErrorCode.UNAUTHENTICATED, - SetOpenIdConnectApplicationAccessLimitErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.OpenIdConnectApplicationAccessPolicies - .SingleOrDefaultAsync(x => - x.ClientId == input.ClientId, - cancellationToken - ); - if (accessPolicy is null) - { - accessPolicy = new OpenIdConnectApplicationAccessPolicy(input.ClientId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - context.OpenIdConnectApplicationAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs new file mode 100644 index 00000000..1416bfda --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -0,0 +1,139 @@ +using System.Linq; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using GraphQL.Client.Abstractions.Utilities; +using Database.ApiRequests; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetOpenIdConnectApplicationAccessPolicyInput +( + DataReferenceInput? Data, + string ClientId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetOpenIdConnectApplicationAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_APPLICATION +} + +public sealed record SetOpenIdConnectApplicationAccessPolicyError( + SetOpenIdConnectApplicationAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetOpenIdConnectApplicationAccessPolicyPayload( + OpenIdConnectApplicationAccessPolicy? OpenIdConnectApplicationAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetOpenIdConnectApplicationAccessPolicyMutation +: DataMutationBase +{ + protected override SetOpenIdConnectApplicationAccessPolicyPayload NewPayload( + OpenIdConnectApplicationAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetOpenIdConnectApplicationAccessPolicyError NewError( + SetOpenIdConnectApplicationAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetOpenIdConnectApplicationAccessPolicyAsync( + SetOpenIdConnectApplicationAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IOpenIdConnectApplicationByClientIdDataLoader applicationByClientIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHENTICATED, + SetOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + SetOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + List errors = []; + if (await applicationByClientIdDataLoader.LoadAsync(input.ClientId, cancellationToken) is null) + { + errors.Add( + NewError( + SetOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_APPLICATION, + $"The Open ID Connect application does not exist.", + [nameof(input), nameof(input.ClientId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + var applicationAccessPolicy = await context.OpenIdConnectApplicationAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicyId == dataAccessPolicy.Id + && _.ClientId == input.ClientId, + cancellationToken + ); + if (applicationAccessPolicy is null) + { + applicationAccessPolicy = new OpenIdConnectApplicationAccessPolicy(dataAccessPolicy.Id, input.ClientId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.OpenIdConnectApplicationAccessPolicies.Add(applicationAccessPolicy); + } + else + { + applicationAccessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + applicationAccessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(applicationAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs deleted file mode 100644 index f0019bae..00000000 --- a/backend/src/GraphQl/AccessPolicies/SetUserAccessLimitMutation.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicies; - -public sealed record SetUserAccessLimitInput -( - Guid UserId, - UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration -); - -[SuppressMessage("Naming", "CA1707")] -public enum SetUserAccessLimitErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED -} - -public sealed record SetUserAccessLimitError( - SetUserAccessLimitErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record SetUserAccessLimitPayload( - UserAccessPolicy? UserAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class SetUserAccessLimitMutation -: MutationBase -{ - protected override SetUserAccessLimitPayload NewPayload( - UserAccessPolicy? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override SetUserAccessLimitError NewError( - SetUserAccessLimitErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task SetUserAccessLimitAsync( - SetUserAccessLimitInput input, - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - SetUserAccessLimitErrorCode.UNAUTHENTICATED, - SetUserAccessLimitErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var accessPolicy = await context.UserAccessPolicies - .SingleOrDefaultAsync(x => - x.UserId == input.UserId, - cancellationToken - ); - if (accessPolicy is null) - { - accessPolicy = new UserAccessPolicy(input.UserId) - { - UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() - }; - context.UserAccessPolicies.Add(accessPolicy); - } - else - { - accessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); - accessPolicy.AccessCountSinceStartTime = null; - } - await context.SaveChangesAsync(cancellationToken); - return NewPayload(accessPolicy, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs new file mode 100644 index 00000000..22da03ee --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs @@ -0,0 +1,140 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using Database.ApiRequests; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; +using GraphQL.Client.Abstractions.Utilities; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record SetUserAccessPolicyInput +( + DataReferenceInput? Data, + Guid UserId, + UpperLimitPerDurationInput? UpperAccessLimitPerTimeDuration +); + +[SuppressMessage("Naming", "CA1707")] +public enum SetUserAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_USER +} + +public sealed record SetUserAccessPolicyError( + SetUserAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record SetUserAccessPolicyPayload( + UserAccessPolicy? UserAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class SetUserAccessPolicyMutation +: DataMutationBase +{ + protected override SetUserAccessPolicyPayload NewPayload( + UserAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override SetUserAccessPolicyError NewError( + SetUserAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task SetUserAccessPolicyAsync( + SetUserAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IUserByIdDataLoader userByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + SetUserAccessPolicyErrorCode.UNAUTHENTICATED, + SetUserAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + SetUserAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + List errors = []; + if (await userByIdDataLoader.LoadAsync(input.UserId, cancellationToken) is null) + { + errors.Add( + NewError( + SetUserAccessPolicyErrorCode.UNKNOWN_USER, + $"The user does not exist.", + [nameof(input), nameof(input.UserId).ToLowerFirst()] + ) + ); + } + if (errors.Count > 0) + { + return NewPayload(null, errors); + } + + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync( + _ => _.DataId == (input.Data == null ? null : input.Data.DataId), + cancellationToken + ); + var userAccessPolicy = await context.UserAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicyId == dataAccessPolicy.Id + && _.UserId == input.UserId, + cancellationToken + ); + if (userAccessPolicy is null) + { + userAccessPolicy = new UserAccessPolicy(dataAccessPolicy.Id, input.UserId) + { + UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel() + }; + context.UserAccessPolicies.Add(userAccessPolicy); + } + else + { + userAccessPolicy.UpperAccessLimitPerTimeDuration = input.UpperAccessLimitPerTimeDuration?.ToDomainModel(); + userAccessPolicy.AccessCountSinceStartTime = null; + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(userAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs new file mode 100644 index 00000000..699c4b6d --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record UnsetInstitutionAccessPolicyInput +( + DataReferenceInput? Data, + Guid InstitutionId +); + +[SuppressMessage("Naming", "CA1707")] +public enum UnsetInstitutionAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_POLICY +} + +public sealed record UnsetInstitutionAccessPolicyError( + UnsetInstitutionAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record UnsetInstitutionAccessPolicyPayload( + InstitutionAccessPolicy? InstitutionAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class UnsetInstitutionAccessPolicyMutation +: DataMutationBase +{ + protected override UnsetInstitutionAccessPolicyPayload NewPayload( + InstitutionAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UnsetInstitutionAccessPolicyError NewError( + UnsetInstitutionAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UnsetInstitutionAccessPolicyAsync( + UnsetInstitutionAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UnsetInstitutionAccessPolicyErrorCode.UNAUTHENTICATED, + UnsetInstitutionAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + UnsetInstitutionAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var institutionAccessPolicy = await context.InstitutionAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.InstitutionId == input.InstitutionId, + cancellationToken + ); + if (institutionAccessPolicy is null) + { + return NewPayload( + null, + [NewError( + UnsetInstitutionAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The institution access policy does not exist.", + [nameof(input), nameof(input.InstitutionId).ToLowerFirst()] + )] + ); + } + + context.InstitutionAccessPolicies.Remove(institutionAccessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(institutionAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs new file mode 100644 index 00000000..7a99c4dd --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -0,0 +1,117 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record UnsetOpenIdConnectApplicationAccessPolicyInput +( + DataReferenceInput? Data, + string ClientId +); + +[SuppressMessage("Naming", "CA1707")] +public enum UnsetOpenIdConnectApplicationAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_POLICY +} + +public sealed record UnsetOpenIdConnectApplicationAccessPolicyError( + UnsetOpenIdConnectApplicationAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record UnsetOpenIdConnectApplicationAccessPolicyPayload( + OpenIdConnectApplicationAccessPolicy? ApplicationAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class UnsetOpenIdConnectApplicationAccessPolicyMutation +: DataMutationBase +{ + protected override UnsetOpenIdConnectApplicationAccessPolicyPayload NewPayload( + OpenIdConnectApplicationAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UnsetOpenIdConnectApplicationAccessPolicyError NewError( + UnsetOpenIdConnectApplicationAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UnsetOpenIdConnectApplicationAccessPolicyAsync( + UnsetOpenIdConnectApplicationAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IOpenIdConnectApplicationByClientIdDataLoader openIdConnectApplicationByClientIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UnsetOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHENTICATED, + UnsetOpenIdConnectApplicationAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + UnsetOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var applicationAccessPolicy = await context.OpenIdConnectApplicationAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.ClientId == input.ClientId, + cancellationToken + ); + if (applicationAccessPolicy is null) + { + return NewPayload( + null, + [NewError( + UnsetOpenIdConnectApplicationAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The Open ID Connect application access policy does not exist.", + [nameof(input), nameof(input.ClientId).ToLowerFirst()] + )] + ); + } + + context.OpenIdConnectApplicationAccessPolicies.Remove(applicationAccessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(applicationAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs new file mode 100644 index 00000000..4f8820e6 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Database.ApiRequests; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using GraphQL.Client.Abstractions.Utilities; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicies; + +public sealed record UnsetUserAccessPolicyInput +( + DataReferenceInput? Data, + Guid UserId +); + +[SuppressMessage("Naming", "CA1707")] +public enum UnsetUserAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA, + UNKNOWN_POLICY +} + +public sealed record UnsetUserAccessPolicyError( + UnsetUserAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record UnsetUserAccessPolicyPayload( + UserAccessPolicy? UserAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class UnsetUserAccessPolicyMutation +: DataMutationBase +{ + protected override UnsetUserAccessPolicyPayload NewPayload( + UserAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override UnsetUserAccessPolicyError NewError( + UnsetUserAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task UnsetUserAccessPolicyAsync( + UnsetUserAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IUserByIdDataLoader userByIdDataLoader, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + UnsetUserAccessPolicyErrorCode.UNAUTHENTICATED, + UnsetUserAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + UnsetUserAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var _, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var userAccessPolicy = await context.UserAccessPolicies + .SingleOrDefaultAsync(_ => + _.DataAccessPolicy != null + && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.UserId == input.UserId, + cancellationToken + ); + if (userAccessPolicy is null) + { + return NewPayload( + null, + [NewError( + UnsetUserAccessPolicyErrorCode.UNKNOWN_POLICY, + $"The user access policy does not exist.", + [nameof(input), nameof(input.UserId).ToLowerFirst()] + )] + ); + } + + context.UserAccessPolicies.Remove(userAccessPolicy); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(userAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 341d44ed..759f1f1c 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -39,8 +39,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPendingCalorimetricDataAsync( @@ -99,4 +97,4 @@ CancellationToken cancellationToken cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 1c84da8e..3cb7b635 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -6,6 +6,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Extensions; using Database.GraphQl.DataX; using Database.GraphQl.Scalars; @@ -49,6 +50,7 @@ public CalorimetricData ToDomainModel( UValues ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/DataMutationBase.cs b/backend/src/GraphQl/DataMutationBase.cs index ca955f25..4b3b1349 100644 --- a/backend/src/GraphQl/DataMutationBase.cs +++ b/backend/src/GraphQl/DataMutationBase.cs @@ -7,10 +7,8 @@ using Database.ApiRequests; using Database.Data; using Database.Enumerations; -using Database.Extensions; using Database.Services; using GraphQL.Client.Abstractions.Utilities; -using HotChocolate; using static Database.ApiRequests.DataFormatDataLoader; namespace Database.GraphQl; @@ -129,4 +127,4 @@ CancellationToken cancellationToken } return new Result>.Data(dataFormat ?? throw new InvalidOperationException("Impossible because there are no errors when this code line is being executed!")); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataReferenceInput.cs b/backend/src/GraphQl/DataReferenceInput.cs new file mode 100644 index 00000000..0f53cc6b --- /dev/null +++ b/backend/src/GraphQl/DataReferenceInput.cs @@ -0,0 +1,20 @@ +using System; +using Database.Data; +using Database.Enumerations; + +namespace Database.GraphQl; + +public sealed record DataReferenceInput( + Guid DataId, + DataKind DataKind +) + : IIdentifyDataInput +{ + public DataReference ToDomainModel() + { + return new DataReference( + DataId, + DataKind + ); + } +}; \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 1e90eb81..3a39ca30 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,7 +1,10 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; +using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; +using Database.GraphQl.AccessPolicies; using Database.GraphQl.Extensions; using Database.GraphQl.GetHttpsResources; using GreenDonut; @@ -16,7 +19,7 @@ public sealed class DataResolvers { [UseFiltering] [UseSorting] - public Task GetHttpsResources( + public Task GetHttpsResourcesAsync( [Parent] IData data, IResolverContext resolverContext, IHttpsResourcesByDataIdDataLoader byId, @@ -35,6 +38,23 @@ [Parent] IData data return new GetHttpsResourceTree(data); } + public async Task GetDataAccessPolicyAsync( + [Parent] IData data, + IResolverContext resolverContext, + IDataAccessPolicyByDataIdDataLoader byId, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + return null; + } + return await byId + .With(resolverContext.GetQueryContext()) + .LoadRequiredAsync(data.Id, cancellationToken); + } + public Task GetDatabaseAsync( [Parent] IData data, IDatabaseByIdDataLoader byId, diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index cad3965c..ccb05899 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -31,11 +31,15 @@ IObjectTypeDescriptor descriptor descriptor .Field(_ => _.Resources) .Cost(1) - .ResolveWith(_ => _.GetHttpsResources(default!, default!, default!, default!)); + .ResolveWith(_ => _.GetHttpsResourcesAsync(default!, default!, default!, default!)); descriptor .Field(DataType.ResourceTreeFieldName) .Cost(1) .ResolveWith(_ => _.GetHttpsResourceTree(default!)); + descriptor + .Field(_ => _.AccessPolicy) + .Cost(1) + .ResolveWith(_ => _.GetDataAccessPolicyAsync(default!, default!, default!, default!, default!)); descriptor .Field(_ => _.Approval) .Cost(3) diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 52739ea5..4eb732d4 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -6,6 +6,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Extensions; using Database.GraphQl.DataX; using Database.GraphQl.Scalars; @@ -51,6 +52,7 @@ public GeometricData ToDomainModel( Thicknesses ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index eeb9dffa..54ace539 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -35,12 +35,12 @@ public GetHttpsResource ToDomainModel(string? fileExtension) Sha256FileHasher.ComputeForString(""), // The correct hash value is computed when the file for this resource is being uploaded. DataFormatId, fileExtension, - DataKind == DataKind.CALORIMETRIC_DATA ? DataId : null, - DataKind == DataKind.GEOMETRIC_DATA ? DataId : null, - DataKind == DataKind.HYGROTHERMAL_DATA ? DataId : null, - DataKind == DataKind.LIFE_CYCLE_DATA ? DataId : null, - DataKind == DataKind.OPTICAL_DATA ? DataId : null, - DataKind == DataKind.PHOTOVOLTAIC_DATA ? DataId : null, + DataKind is DataKind.CALORIMETRIC_DATA ? DataId : null, + DataKind is DataKind.GEOMETRIC_DATA ? DataId : null, + DataKind is DataKind.HYGROTHERMAL_DATA ? DataId : null, + DataKind is DataKind.LIFE_CYCLE_DATA ? DataId : null, + DataKind is DataKind.OPTICAL_DATA ? DataId : null, + DataKind is DataKind.PHOTOVOLTAIC_DATA ? DataId : null, ParentId, ArchivedFilesMetaInformation.Select(i => i.ToDomainModel()).ToList(), AppliedConversionMethod.ToDomainModel() @@ -199,4 +199,4 @@ await context.GetHttpsResources return NewPayload(getHttpsResource, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 8e2df359..64341bcf 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -39,7 +39,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public async ValueTask> GetPendingGetHttpsResources( diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 0803a74e..344ed259 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -6,6 +6,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Extensions; using Database.GraphQl.DataX; using Database.GraphQl.Scalars; @@ -45,6 +46,7 @@ public HygrothermalData ToDomainModel( AppliedMethod.ToDomainModel() ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 628f2817..2561d7a4 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -18,8 +18,6 @@ public sealed class HygrothermalDataQueries : DataQueriesBase { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllHygrothermalDataAsync( @@ -41,8 +39,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPendingHygrothermalDataAsync( @@ -101,4 +97,4 @@ CancellationToken cancellationToken cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs index a276eba6..fecfc5e5 100644 --- a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -6,6 +6,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Extensions; using Database.GraphQl.DataX; using Database.GraphQl.Scalars; @@ -45,6 +46,7 @@ public LifeCycleData ToDomainModel( AppliedMethod.ToDomainModel() ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index 67fd8446..e653df6e 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -18,8 +18,6 @@ public sealed class LifeCycleDataQueries : DataQueriesBase { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllLifeCycleDataAsync( @@ -41,8 +39,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPendingLifeCycleDataAsync( @@ -101,4 +97,4 @@ CancellationToken cancellationToken cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 7802a3c9..65c9215b 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -7,6 +7,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Enumerations; using Database.Extensions; using Database.GraphQl.DataX; @@ -67,6 +68,8 @@ public OpticalData ToDomainModel( CielabColors.Select(c => c.ToDomainModel()).ToList() ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 73f0a531..57f2322f 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -18,8 +18,6 @@ public sealed class OpticalDataQueries : DataQueriesBase { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllOpticalDataAsync( @@ -41,8 +39,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPendingOpticalDataAsync( @@ -101,4 +97,4 @@ CancellationToken cancellationToken cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 2729dd9a..6b8503e0 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -6,6 +6,7 @@ using Database.ApiRequests; using Database.Authorization; using Database.Data; +using Database.Data.AccessPolicies; using Database.Extensions; using Database.GraphQl.DataX; using Database.GraphQl.Scalars; @@ -45,6 +46,7 @@ public PhotovoltaicData ToDomainModel( AppliedMethod.ToDomainModel() ); data.Resources.Add(RootResource.ToDomainModel(fileExtension)); + data.AccessPolicy = new DataAccessPolicy(); return data; } }; diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index a1b02c50..b7c6bf98 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -19,8 +19,6 @@ public sealed class PhotovoltaicDataQueries : DataQueriesBase { [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPhotovoltaicDataAsync( @@ -42,8 +40,6 @@ CancellationToken cancellationToken } [UsePaging] - // [UseProjection] // We disabled projections because when requesting `id` all results had the - // same `id` and when also requesting `uuid`, the latter was always the empty UUID `000...`. [UseFiltering] [UseSorting] public Task> GetAllPendingPhotovoltaicDataAsync( @@ -103,4 +99,4 @@ CancellationToken cancellationToken cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs b/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs index f7d39327..cb7891e4 100644 --- a/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs +++ b/backend/src/Migrations/20260603191121_RevampAccessPolicies.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Database.Data.AccessPolicies; +using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Migrations; using NodaTime; @@ -12,6 +12,7 @@ namespace Database.Migrations public partial class RevampAccessPolicies : Migration { /// + [SuppressMessage("Performance", "CA1861")] protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( @@ -197,6 +198,7 @@ protected override void Up(MigrationBuilder migrationBuilder) } /// + [SuppressMessage("Performance", "CA1861")] protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( diff --git a/backend/src/Migrations/20260608173427_UnifyAccessPolicies.Designer.cs b/backend/src/Migrations/20260608173427_UnifyAccessPolicies.Designer.cs new file mode 100644 index 00000000..a80a31af --- /dev/null +++ b/backend/src/Migrations/20260608173427_UnifyAccessPolicies.Designer.cs @@ -0,0 +1,3462 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260608173427_UnifyAccessPolicies")] + partial class UnifyAccessPolicies + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b.HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260608173427_UnifyAccessPolicies.cs b/backend/src/Migrations/20260608173427_UnifyAccessPolicies.cs new file mode 100644 index 00000000..5bb0ca84 --- /dev/null +++ b/backend/src/Migrations/20260608173427_UnifyAccessPolicies.cs @@ -0,0 +1,1020 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class UnifyAccessPolicies : Migration + { + /// + [SuppressMessage("Performance", "CA1861")] + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() CASCADE;"); + + migrationBuilder.DropPrimaryKey( + name: "PK_user_access_policy", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropPrimaryKey( + name: "PK_open_id_connect_application_access_policy", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropPrimaryKey( + name: "PK_institution_access_policy", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "photovoltaic_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "optical_data"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "lifeCycle_data"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "hygrothermal_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "geometric_data"); + + migrationBuilder.DropColumn( + name: "AccessPolicy", + schema: "database", + table: "calorimetric_data"); + + migrationBuilder.AddColumn( + name: "Id", + schema: "database", + table: "user_access_policy", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "user_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "user_access_policy", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "user_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "user_access_policy", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "user_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "user_access_policy", + type: "interval", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "user_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "xmin", + schema: "database", + table: "user_access_policy", + type: "xid", + rowVersion: true, + nullable: false, + defaultValue: 0u); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "user", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AddColumn( + name: "Id", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "interval", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "xmin", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "xid", + rowVersion: true, + nullable: false, + defaultValue: 0u); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "lifeCycle_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AddColumn( + name: "Id", + schema: "database", + table: "institution_access_policy", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "institution_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "institution_access_policy", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "CreatedAt", + schema: "database", + table: "institution_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "institution_access_policy", + type: "uuid", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); + + migrationBuilder.AddColumn( + name: "UpdatedAt", + schema: "database", + table: "institution_access_policy", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()"); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "institution_access_policy", + type: "interval", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "institution_access_policy", + type: "bigint", + nullable: true); + + migrationBuilder.AddColumn( + name: "xmin", + schema: "database", + table: "institution_access_policy", + type: "xid", + rowVersion: true, + nullable: false, + defaultValue: 0u); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "get_https_resource", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: false, + defaultValueSql: "uuidv7()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "gen_random_uuid()"); + + migrationBuilder.AddPrimaryKey( + name: "PK_user_access_policy", + schema: "database", + table: "user_access_policy", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_open_id_connect_application_access_policy", + schema: "database", + table: "open_id_connect_application_access_policy", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_institution_access_policy", + schema: "database", + table: "institution_access_policy", + column: "Id"); + + migrationBuilder.CreateTable( + name: "data_access_policy", + schema: "database", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuidv7()"), + CalorimetricDataId = table.Column(type: "uuid", nullable: true), + GeometricDataId = table.Column(type: "uuid", nullable: true), + HygrothermalDataId = table.Column(type: "uuid", nullable: true), + LifeCycleDataId = table.Column(type: "uuid", nullable: true), + OpticalDataId = table.Column(type: "uuid", nullable: true), + PhotovoltaicDataId = table.Column(type: "uuid", nullable: true), + Combinator = table.Column(type: "database.logical_combinator", nullable: false), + xmin = table.Column(type: "xid", rowVersion: true, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("PK_data_access_policy", x => x.Id); + table.CheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + table.ForeignKey( + name: "FK_data_access_policy_calorimetric_data_CalorimetricDataId", + column: x => x.CalorimetricDataId, + principalSchema: "database", + principalTable: "calorimetric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_data_access_policy_geometric_data_GeometricDataId", + column: x => x.GeometricDataId, + principalSchema: "database", + principalTable: "geometric_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_data_access_policy_hygrothermal_data_HygrothermalDataId", + column: x => x.HygrothermalDataId, + principalSchema: "database", + principalTable: "hygrothermal_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_data_access_policy_lifeCycle_data_LifeCycleDataId", + column: x => x.LifeCycleDataId, + principalSchema: "database", + principalTable: "lifeCycle_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_data_access_policy_optical_data_OpticalDataId", + column: x => x.OpticalDataId, + principalSchema: "database", + principalTable: "optical_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_data_access_policy_photovoltaic_data_PhotovoltaicDataId", + column: x => x.PhotovoltaicDataId, + principalSchema: "database", + principalTable: "photovoltaic_data", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_user_access_policy_CreatedAt_Id", + schema: "database", + table: "user_access_policy", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_user_access_policy_DataAccessPolicyId_UserId", + schema: "database", + table: "user_access_policy", + columns: new[] { "DataAccessPolicyId", "UserId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_open_id_connect_application_access_policy_CreatedAt_Id", + schema: "database", + table: "open_id_connect_application_access_policy", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_open_id_connect_application_access_policy_DataAccessPolicyI~", + schema: "database", + table: "open_id_connect_application_access_policy", + columns: new[] { "DataAccessPolicyId", "ClientId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_institution_access_policy_CreatedAt_Id", + schema: "database", + table: "institution_access_policy", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_institution_access_policy_DataAccessPolicyId_InstitutionId", + schema: "database", + table: "institution_access_policy", + columns: new[] { "DataAccessPolicyId", "InstitutionId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_CalorimetricDataId", + schema: "database", + table: "data_access_policy", + column: "CalorimetricDataId", + unique: true, + filter: "\"CalorimetricDataId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_CalorimetricDataId_GeometricDataId_Hygro~", + schema: "database", + table: "data_access_policy", + columns: new[] { "CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId" }, + unique: true) + .Annotation("Npgsql:NullsDistinct", false); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_CreatedAt_Id", + schema: "database", + table: "data_access_policy", + columns: new[] { "CreatedAt", "Id" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_GeometricDataId", + schema: "database", + table: "data_access_policy", + column: "GeometricDataId", + unique: true, + filter: "\"GeometricDataId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_HygrothermalDataId", + schema: "database", + table: "data_access_policy", + column: "HygrothermalDataId", + unique: true, + filter: "\"HygrothermalDataId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_LifeCycleDataId", + schema: "database", + table: "data_access_policy", + column: "LifeCycleDataId", + unique: true, + filter: "\"LifeCycleDataId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_OpticalDataId", + schema: "database", + table: "data_access_policy", + column: "OpticalDataId", + unique: true, + filter: "\"OpticalDataId\" IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_data_access_policy_PhotovoltaicDataId", + schema: "database", + table: "data_access_policy", + column: "PhotovoltaicDataId", + unique: true, + filter: "\"PhotovoltaicDataId\" IS NOT NULL"); + + migrationBuilder.AddForeignKey( + name: "FK_institution_access_policy_data_access_policy_DataAccessPoli~", + schema: "database", + table: "institution_access_policy", + column: "DataAccessPolicyId", + principalSchema: "database", + principalTable: "data_access_policy", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_open_id_connect_application_access_policy_data_access_polic~", + schema: "database", + table: "open_id_connect_application_access_policy", + column: "DataAccessPolicyId", + principalSchema: "database", + principalTable: "data_access_policy", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_user_access_policy_data_access_policy_DataAccessPolicyId", + schema: "database", + table: "user_access_policy", + column: "DataAccessPolicyId", + principalSchema: "database", + principalTable: "data_access_policy", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();"); + } + + /// + [SuppressMessage("Performance", "CA1861")] + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() CASCADE;"); + + migrationBuilder.DropForeignKey( + name: "FK_institution_access_policy_data_access_policy_DataAccessPoli~", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropForeignKey( + name: "FK_open_id_connect_application_access_policy_data_access_polic~", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropForeignKey( + name: "FK_user_access_policy_data_access_policy_DataAccessPolicyId", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropTable( + name: "data_access_policy", + schema: "database"); + + migrationBuilder.DropPrimaryKey( + name: "PK_user_access_policy", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_user_access_policy_CreatedAt_Id", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_user_access_policy_DataAccessPolicyId_UserId", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropPrimaryKey( + name: "PK_open_id_connect_application_access_policy", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_open_id_connect_application_access_policy_CreatedAt_Id", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_open_id_connect_application_access_policy_DataAccessPolicyI~", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropPrimaryKey( + name: "PK_institution_access_policy", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_institution_access_policy_CreatedAt_Id", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropIndex( + name: "IX_institution_access_policy_DataAccessPolicyId_InstitutionId", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "Id", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "xmin", + schema: "database", + table: "user_access_policy"); + + migrationBuilder.DropColumn( + name: "Id", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "xmin", + schema: "database", + table: "open_id_connect_application_access_policy"); + + migrationBuilder.DropColumn( + name: "Id", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_AccessCount", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "AccessCountSinceStartTime_StartTime", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "CreatedAt", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "DataAccessPolicyId", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "UpdatedAt", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_Duration", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "UpperAccessLimitPerTimeDuration_UpperLimit", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.DropColumn( + name: "xmin", + schema: "database", + table: "institution_access_policy"); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "user_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "user_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "user", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "photovoltaic_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "photovoltaic_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "optical_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "optical_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "open_id_connect_application_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "lifeCycle_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "lifeCycle_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "AccessCountSinceStartTime", + schema: "database", + table: "institution_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AddColumn( + name: "UpperAccessLimitPerTimeDuration", + schema: "database", + table: "institution_access_policy", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "hygrothermal_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "hygrothermal_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "get_https_resource", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "geometric_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "geometric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Id", + schema: "database", + table: "calorimetric_data", + type: "uuid", + nullable: false, + defaultValueSql: "gen_random_uuid()", + oldClrType: typeof(Guid), + oldType: "uuid", + oldDefaultValueSql: "uuidv7()"); + + migrationBuilder.AddColumn( + name: "AccessPolicy", + schema: "database", + table: "calorimetric_data", + type: "jsonb", + nullable: true); + + migrationBuilder.AddPrimaryKey( + name: "PK_user_access_policy", + schema: "database", + table: "user_access_policy", + column: "UserId"); + + migrationBuilder.AddPrimaryKey( + name: "PK_open_id_connect_application_access_policy", + schema: "database", + table: "open_id_connect_application_access_policy", + column: "ClientId"); + + migrationBuilder.AddPrimaryKey( + name: "PK_institution_access_policy", + schema: "database", + table: "institution_access_policy", + column: "InstitutionId"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.Designer.cs b/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.Designer.cs new file mode 100644 index 00000000..929da0dc --- /dev/null +++ b/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.Designer.cs @@ -0,0 +1,3473 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData")] + partial class EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.cs b/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.cs new file mode 100644 index 00000000..ac2c78a0 --- /dev/null +++ b/backend/src/Migrations/20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.cs @@ -0,0 +1,64 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(""" + DELETE FROM database.data_access_policy; + DELETE FROM database.user_access_policy; + DELETE FROM database.institution_access_policy; + DELETE FROM database.open_id_connect_application_access_policy; + + INSERT INTO database.data_access_policy ("CalorimetricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."calorimetric_data"; + + INSERT INTO database.data_access_policy ("GeometricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."geometric_data"; + + INSERT INTO database.data_access_policy ("HygrothermalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."hygrothermal_data"; + + INSERT INTO database.data_access_policy ("LifeCycleDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."lifeCycle_data"; + + INSERT INTO database.data_access_policy ("OpticalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."optical_data"; + + INSERT INTO database.data_access_policy ("PhotovoltaicDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."photovoltaic_data"; + """); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() CASCADE;"); + + migrationBuilder.Sql(""" + DELETE FROM database.data_access_policy; + DELETE FROM database.user_access_policy; + DELETE FROM database.institution_access_policy; + DELETE FROM database.open_id_connect_application_access_policy; + """); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 5d6c903e..d3e68bef 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,12 +1,13 @@ // using System; -using System.Collections.Generic; using System.Text.Json; using Database.Data; using Database.Enumerations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable @@ -35,100 +36,224 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => { - b.Property("InstitutionId") + b.Property("Id") .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") .HasColumnType("uuid"); - b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => - { - b1.Property("AccessCount"); + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b1.Property("StartTime"); + b.Property("GeometricDataId") + .HasColumnType("uuid"); - b1 - .ToJson("AccessCountSinceStartTime") - .HasColumnType("jsonb"); - }); + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); - b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => { - b1.Property("Duration"); + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); - b1.Property("UpperLimit"); + t.HasTrigger("data_access_policy_data_id_cannot_change"); - b1 - .ToJson("UpperAccessLimitPerTimeDuration") - .HasColumnType("jsonb"); + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); }); - b.HasKey("InstitutionId"); + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); b.ToTable("institution_access_policy", "database"); }); modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + b.Property("ClientId") + .IsRequired() .HasColumnType("text"); - b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => - { - b1.Property("AccessCount"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b1.Property("StartTime"); + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); - b1 - .ToJson("AccessCountSinceStartTime") - .HasColumnType("jsonb"); - }); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => - { - b1.Property("Duration"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); - b1.Property("UpperLimit"); + b.HasKey("Id"); - b1 - .ToJson("UpperAccessLimitPerTimeDuration") - .HasColumnType("jsonb"); - }); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); - b.HasKey("ClientId"); + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); b.ToTable("open_id_connect_application_access_policy", "database"); }); modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => { - b.Property("UserId") + b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("uuid"); + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); - b.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.AccessPolicies.UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b1 => - { - b1.Property("AccessCount"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b1.Property("StartTime"); + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); - b1 - .ToJson("AccessCountSinceStartTime") - .HasColumnType("jsonb"); - }); + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.AccessPolicies.UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b1 => - { - b1.Property("Duration"); + b.Property("UserId") + .HasColumnType("uuid"); - b1.Property("UpperLimit"); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); - b1 - .ToJson("UpperAccessLimitPerTimeDuration") - .HasColumnType("jsonb"); - }); + b.HasKey("Id"); - b.HasKey("UserId"); + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); b.ToTable("user_access_policy", "database"); }); @@ -138,7 +263,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); @@ -190,73 +315,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.CalorimetricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); - b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -270,7 +328,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); @@ -326,73 +384,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("double precision[]"); - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.GeometricData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); - b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -406,7 +397,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("CalorimetricDataId") .HasColumnType("uuid"); @@ -491,13 +482,13 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("get_https_resource", "database", t => { - t.HasTrigger("LC_TRIGGER_data_id_cannot_change"); + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); - t.HasTrigger("LC_TRIGGER_data_ids_must_match"); + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); - t.HasTrigger("data_id_cannot_change"); + t.HasTrigger("get_https_resource_data_id_cannot_change"); - t.HasTrigger("data_ids_must_match"); + t.HasTrigger("get_https_resource_data_ids_must_match"); t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); @@ -505,8 +496,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_id_cannot_change\"();") - .HasAnnotation("LC_TRIGGER_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_ids_must_match\"();"); + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => @@ -514,7 +505,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); @@ -546,84 +537,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasDefaultValueSql("now()"); b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("Version") - .IsConcurrencyToken() - .ValueGeneratedOnAddOrUpdate() - .HasColumnType("xid") - .HasColumnName("xmin"); - - b.PrimitiveCollection("Warnings") - .IsRequired() - .HasColumnType("text[]"); - - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.HygrothermalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); + .HasColumnType("uuid"); - b3.Property("UpperLimit"); - }); - }); + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); b.HasKey("Id"); @@ -638,7 +562,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); @@ -682,73 +606,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.LifeCycleData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); - b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -762,7 +619,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("CoatedSide") .HasColumnType("database.coated_side"); @@ -839,73 +696,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.OpticalData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); - b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -919,7 +709,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("ComponentId") .HasColumnType("uuid"); @@ -963,73 +753,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired() .HasColumnType("text[]"); - b.ComplexProperty(typeof(Dictionary), "AccessPolicy", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy", b1 => - { - b1.Property("Combinator"); - - b1.ComplexCollection(typeof(List>), "InstitutionAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy", b2 => - { - b2.Property("InstitutionId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.InstitutionAccessPolicies#InstitutionAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "OpenIdConnectApplicationAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy", b2 => - { - b2.Property("ClientId") - .IsRequired(); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.OpenIdConnectApplicationAccessPolicies#OpenIdConnectApplicationAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1.ComplexCollection(typeof(List>), "UserAccessPolicies", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy", b2 => - { - b2.Property("UserId"); - - b2.ComplexProperty(typeof(Dictionary), "AccessCountSinceStartTime", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.AccessCountSinceStartTime#CountSinceTime", b3 => - { - b3.Property("AccessCount"); - - b3.Property("StartTime"); - }); - - b2.ComplexProperty(typeof(Dictionary), "UpperAccessLimitPerTimeDuration", "Database.Data.PhotovoltaicData.AccessPolicy#DataAccessPolicy.UserAccessPolicies#UserAccessPolicy.UpperAccessLimitPerTimeDuration#UpperLimitPerDuration", b3 => - { - b3.Property("Duration"); - - b3.Property("UpperLimit"); - }); - }); - - b1 - .ToJson("AccessPolicy") - .HasColumnType("jsonb"); - }); - b.HasKey("Id"); b.HasIndex("CreatedAt", "Id") @@ -1043,7 +766,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValueSql("gen_random_uuid()"); + .HasDefaultValueSql("uuidv7()"); b.Property("CreatedAt") .ValueGeneratedOnAdd() @@ -1304,6 +1027,210 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("OpenIddictTokens", "database"); }); + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => @@ -3470,13 +3397,26 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Authorization"); }); + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + modelBuilder.Entity("Database.Data.CalorimetricData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); modelBuilder.Entity("Database.Data.GeometricData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); @@ -3487,21 +3427,29 @@ protected override void BuildModel(ModelBuilder modelBuilder) modelBuilder.Entity("Database.Data.HygrothermalData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); modelBuilder.Entity("Database.Data.LifeCycleData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); modelBuilder.Entity("Database.Data.OpticalData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => { + b.Navigation("AccessPolicy"); + b.Navigation("Resources"); }); @@ -3519,4 +3467,4 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning restore 612, 618 } } -} \ No newline at end of file +} diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 56955370..6c03fb2b 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -2463,141 +2463,678 @@ START TRANSACTION; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER TABLE database.get_https_resource DROP CONSTRAINT "FK_get_https_resource_lifeCycle_data_LifeCycleDataId"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() CASCADE; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - DROP TABLE database."lifeCycle_data_Approvals"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() CASCADE; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - DROP TABLE database."lifeCycle_data_Arguments"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP CONSTRAINT "PK_user_access_policy"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - DROP TABLE database."lifeCycle_data_Sources"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP CONSTRAINT "PK_open_id_connect_application_access_policy"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER TABLE database."lifeCycle_data" DROP CONSTRAINT "PK_lifeCycle_data"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP CONSTRAINT "PK_institution_access_policy"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER TABLE database."lifeCycle_data" RENAME TO life_cycle_data; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "AccessCountSinceStartTime"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER INDEX database."IX_lifeCycle_data_CreatedAt_Id" RENAME TO "IX_life_cycle_data_CreatedAt_Id"; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER TABLE database.life_cycle_data ADD CONSTRAINT "PK_life_cycle_data" PRIMARY KEY ("Id"); + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "AccessPolicy"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - CREATE TABLE database."life_cycle_data_Approvals" ( - "LifeCycleDataId" uuid NOT NULL, - "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "ApproverId" uuid NOT NULL, - "Timestamp" timestamp with time zone NOT NULL, - "Signature" text NOT NULL, - "KeyFingerprint" text NOT NULL, - "Query" text NOT NULL, - "Variables" jsonb NOT NULL DEFAULT ('{}'), - "Message" text NOT NULL, - "Statement_Standard_Year" integer, - "Statement_Standard_Numeration_Prefix" text, - "Statement_Standard_Numeration_MainNumber" text, - "Statement_Standard_Numeration_Suffix" text, - "Statement_Standard_Standardizers" database.standardizer[], - "Statement_Standard_Locator" text, - "Statement_Standard_Title" text, - "Statement_Standard_Abstract" text, - "Statement_Standard_Section" text, - "Statement_Publication_Authors" text[], - "Statement_Publication_Doi" text, - "Statement_Publication_ArXiv" text, - "Statement_Publication_Urn" text, - "Statement_Publication_WebAddress" text, - "Statement_Publication_Title" text, - "Statement_Publication_Abstract" text, - "Statement_Publication_Section" text, - "Statement_Publication_Exists" boolean, - "Statement_Exists" boolean NOT NULL, - CONSTRAINT "PK_life_cycle_data_Approvals" PRIMARY KEY ("LifeCycleDataId", "Id"), - CONSTRAINT "FK_life_cycle_data_Approvals_life_cycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE - ); + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "AccessPolicy"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - CREATE TABLE database."life_cycle_data_Arguments" ( - "AppliedMethodLifeCycleDataId" uuid NOT NULL, - "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "Name" text NOT NULL, - "Value" jsonb NOT NULL, - CONSTRAINT "PK_life_cycle_data_Arguments" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), - CONSTRAINT "FK_life_cycle_data_Arguments_life_cycle_data_AppliedMethodLife~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE - ); + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "AccessCountSinceStartTime"; END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - CREATE TABLE database."life_cycle_data_Sources" ( - "AppliedMethodLifeCycleDataId" uuid NOT NULL, - "Id" integer GENERATED BY DEFAULT AS IDENTITY, - "Name" text NOT NULL, - "Value_DataId" uuid NOT NULL, - "Value_DataTimestamp" timestamp with time zone NOT NULL, - "Value_DataKind" database.data_kind NOT NULL, - "Value_DatabaseId" uuid NOT NULL, - CONSTRAINT "PK_life_cycle_data_Sources" PRIMARY KEY ("AppliedMethodLifeCycleDataId", "Id"), - CONSTRAINT "FK_life_cycle_data_Sources_life_cycle_data_AppliedMethodLifeCy~" FOREIGN KEY ("AppliedMethodLifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "AccessCountSinceStartTime"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."user" ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.get_https_resource ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD CONSTRAINT "PK_user_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD CONSTRAINT "PK_open_id_connect_application_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD CONSTRAINT "PK_institution_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE TABLE database.data_access_policy ( + "Id" uuid NOT NULL DEFAULT (uuidv7()), + "CalorimetricDataId" uuid, + "GeometricDataId" uuid, + "HygrothermalDataId" uuid, + "LifeCycleDataId" uuid, + "OpticalDataId" uuid, + "PhotovoltaicDataId" uuid, + "Combinator" database.logical_combinator NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + CONSTRAINT "PK_data_access_policy" PRIMARY KEY ("Id"), + CONSTRAINT "CK_DataAccessPolicy_At_Most_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") <= 1), + CONSTRAINT "FK_data_access_policy_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE ); END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN - ALTER TABLE database.get_https_resource ADD CONSTRAINT "FK_get_https_resource_life_cycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database.life_cycle_data ("Id") ON DELETE CASCADE; + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_user_access_policy_CreatedAt_Id" ON database.user_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_user_access_policy_DataAccessPolicyId_UserId" ON database.user_access_policy ("DataAccessPolicyId", "UserId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_open_id_connect_application_access_policy_CreatedAt_Id" ON database.open_id_connect_application_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_open_id_connect_application_access_policy_DataAccessPolicyI~" ON database.open_id_connect_application_access_policy ("DataAccessPolicyId", "ClientId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_policy_CreatedAt_Id" ON database.institution_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_policy_DataAccessPolicyId_InstitutionId" ON database.institution_access_policy ("DataAccessPolicyId", "InstitutionId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CalorimetricDataId" ON database.data_access_policy ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CalorimetricDataId_GeometricDataId_Hygro~" ON database.data_access_policy ("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") NULLS NOT DISTINCT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CreatedAt_Id" ON database.data_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_GeometricDataId" ON database.data_access_policy ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_HygrothermalDataId" ON database.data_access_policy ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_LifeCycleDataId" ON database.data_access_policy ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_OpticalDataId" ON database.data_access_policy ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_PhotovoltaicDataId" ON database.data_access_policy ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD CONSTRAINT "FK_institution_access_policy_data_access_policy_DataAccessPoli~" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD CONSTRAINT "FK_open_id_connect_application_access_policy_data_access_polic~" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD CONSTRAINT "FK_user_access_policy_data_access_policy_DataAccessPolicyId" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a data access policy.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260608173427_UnifyAccessPolicies', '10.0.8'); + END IF; +END $EF$; +COMMIT; + +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DELETE FROM database.data_access_policy; + DELETE FROM database.user_access_policy; + DELETE FROM database.institution_access_policy; + DELETE FROM database.open_id_connect_application_access_policy; + + INSERT INTO database.data_access_policy ("CalorimetricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."calorimetric_data"; + + INSERT INTO database.data_access_policy ("GeometricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."geometric_data"; + + INSERT INTO database.data_access_policy ("HygrothermalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."hygrothermal_data"; + + INSERT INTO database.data_access_policy ("LifeCycleDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."lifeCycle_data"; + + INSERT INTO database.data_access_policy ("OpticalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."optical_data"; + + INSERT INTO database.data_access_policy ("PhotovoltaicDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."photovoltaic_data"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ + BEGIN + IF OLD."CalorimetricDataId" IS NULL AND OLD."GeometricDataId" IS NULL AND OLD."HygrothermalDataId" IS NULL AND OLD."LifeCycleDataId" IS NULL AND OLD."OpticalDataId" IS NULL AND OLD."PhotovoltaicDataId" IS NULL + THEN + RAISE EXCEPTION 'You cannot delete the global data access policy.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"(); END IF; END $EF$; DO $EF$ BEGIN - IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260603194435_FixCasingOfLifeCycleDataTableName') THEN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") - VALUES ('20260603194435_FixCasingOfLifeCycleDataTableName', '10.0.8'); + VALUES ('20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData', '10.0.8'); END IF; END $EF$; COMMIT; diff --git a/backend/src/Migrations/migrate_from_20260603191121_RevampAccessPolicies_to_20260608173427_UnifyAccessPolicies.sql b/backend/src/Migrations/migrate_from_20260603191121_RevampAccessPolicies_to_20260608173427_UnifyAccessPolicies.sql new file mode 100644 index 00000000..d2abe86b --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260603191121_RevampAccessPolicies_to_20260608173427_UnifyAccessPolicies.sql @@ -0,0 +1,580 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP CONSTRAINT "PK_user_access_policy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP CONSTRAINT "PK_open_id_connect_application_access_policy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP CONSTRAINT "PK_institution_access_policy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "AccessCountSinceStartTime"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "AccessCountSinceStartTime"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "AccessCountSinceStartTime"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data DROP COLUMN "AccessPolicy"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."user" ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "Id" uuid NOT NULL DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "AccessCountSinceStartTime_AccessCount" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "AccessCountSinceStartTime_StartTime" timestamp with time zone; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "DataAccessPolicyId" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpperAccessLimitPerTimeDuration_Duration" interval; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpperAccessLimitPerTimeDuration_UpperLimit" bigint; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.get_https_resource ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "Id" SET DEFAULT (uuidv7()); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD CONSTRAINT "PK_user_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD CONSTRAINT "PK_open_id_connect_application_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD CONSTRAINT "PK_institution_access_policy" PRIMARY KEY ("Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE TABLE database.data_access_policy ( + "Id" uuid NOT NULL DEFAULT (uuidv7()), + "CalorimetricDataId" uuid, + "GeometricDataId" uuid, + "HygrothermalDataId" uuid, + "LifeCycleDataId" uuid, + "OpticalDataId" uuid, + "PhotovoltaicDataId" uuid, + "Combinator" database.logical_combinator NOT NULL, + "CreatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + "UpdatedAt" timestamp with time zone NOT NULL DEFAULT (now()), + CONSTRAINT "PK_data_access_policy" PRIMARY KEY ("Id"), + CONSTRAINT "CK_DataAccessPolicy_At_Most_One_Data_Set" CHECK (NUM_NONNULLS("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") <= 1), + CONSTRAINT "FK_data_access_policy_calorimetric_data_CalorimetricDataId" FOREIGN KEY ("CalorimetricDataId") REFERENCES database.calorimetric_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_geometric_data_GeometricDataId" FOREIGN KEY ("GeometricDataId") REFERENCES database.geometric_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_hygrothermal_data_HygrothermalDataId" FOREIGN KEY ("HygrothermalDataId") REFERENCES database.hygrothermal_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_lifeCycle_data_LifeCycleDataId" FOREIGN KEY ("LifeCycleDataId") REFERENCES database."lifeCycle_data" ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_optical_data_OpticalDataId" FOREIGN KEY ("OpticalDataId") REFERENCES database.optical_data ("Id") ON DELETE CASCADE, + CONSTRAINT "FK_data_access_policy_photovoltaic_data_PhotovoltaicDataId" FOREIGN KEY ("PhotovoltaicDataId") REFERENCES database.photovoltaic_data ("Id") ON DELETE CASCADE + ); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_user_access_policy_CreatedAt_Id" ON database.user_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_user_access_policy_DataAccessPolicyId_UserId" ON database.user_access_policy ("DataAccessPolicyId", "UserId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_open_id_connect_application_access_policy_CreatedAt_Id" ON database.open_id_connect_application_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_open_id_connect_application_access_policy_DataAccessPolicyI~" ON database.open_id_connect_application_access_policy ("DataAccessPolicyId", "ClientId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_policy_CreatedAt_Id" ON database.institution_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_institution_access_policy_DataAccessPolicyId_InstitutionId" ON database.institution_access_policy ("DataAccessPolicyId", "InstitutionId"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CalorimetricDataId" ON database.data_access_policy ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CalorimetricDataId_GeometricDataId_Hygro~" ON database.data_access_policy ("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") NULLS NOT DISTINCT; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_CreatedAt_Id" ON database.data_access_policy ("CreatedAt", "Id"); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_GeometricDataId" ON database.data_access_policy ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_HygrothermalDataId" ON database.data_access_policy ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_LifeCycleDataId" ON database.data_access_policy ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_OpticalDataId" ON database.data_access_policy ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE UNIQUE INDEX "IX_data_access_policy_PhotovoltaicDataId" ON database.data_access_policy ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD CONSTRAINT "FK_institution_access_policy_data_access_policy_DataAccessPoli~" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD CONSTRAINT "FK_open_id_connect_application_access_policy_data_access_polic~" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD CONSTRAINT "FK_user_access_policy_data_access_policy_DataAccessPolicyId" FOREIGN KEY ("DataAccessPolicyId") REFERENCES database.data_access_policy ("Id") ON DELETE CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a data access policy.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_data_id_cannot_change"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_data_ids_must_match"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260608173427_UnifyAccessPolicies', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql b/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql new file mode 100644 index 00000000..6619fba7 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql @@ -0,0 +1,100 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DELETE FROM database.data_access_policy; + DELETE FROM database.user_access_policy; + DELETE FROM database.institution_access_policy; + DELETE FROM database.open_id_connect_application_access_policy; + + INSERT INTO database.data_access_policy ("CalorimetricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."calorimetric_data"; + + INSERT INTO database.data_access_policy ("GeometricDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."geometric_data"; + + INSERT INTO database.data_access_policy ("HygrothermalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."hygrothermal_data"; + + INSERT INTO database.data_access_policy ("LifeCycleDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."lifeCycle_data"; + + INSERT INTO database.data_access_policy ("OpticalDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."optical_data"; + + INSERT INTO database.data_access_policy ("PhotovoltaicDataId", "Combinator") + SELECT "Id", 'all'::database.logical_combinator + FROM database."photovoltaic_data"; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ + BEGIN + IF OLD."CalorimetricDataId" IS NULL AND OLD."GeometricDataId" IS NULL AND OLD."HygrothermalDataId" IS NULL AND OLD."LifeCycleDataId" IS NULL AND OLD."OpticalDataId" IS NULL AND OLD."PhotovoltaicDataId" IS NULL + THEN + RAISE EXCEPTION 'You cannot delete the global data access policy.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260608173427_UnifyAccessPolicies_to_20260603191121_RevampAccessPolicies.sql b/backend/src/Migrations/rollback_from_20260608173427_UnifyAccessPolicies_to_20260603191121_RevampAccessPolicies.sql new file mode 100644 index 00000000..662c7fed --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260608173427_UnifyAccessPolicies_to_20260603191121_RevampAccessPolicies.sql @@ -0,0 +1,428 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_data_id_cannot_change"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_data_ids_must_match"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP CONSTRAINT "FK_institution_access_policy_data_access_policy_DataAccessPoli~"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP CONSTRAINT "FK_open_id_connect_application_access_policy_data_access_polic~"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP CONSTRAINT "FK_user_access_policy_data_access_policy_DataAccessPolicyId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP TABLE database.data_access_policy; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP CONSTRAINT "PK_user_access_policy"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_user_access_policy_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_user_access_policy_DataAccessPolicyId_UserId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP CONSTRAINT "PK_open_id_connect_application_access_policy"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_open_id_connect_application_access_policy_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_open_id_connect_application_access_policy_DataAccessPolicyI~"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP CONSTRAINT "PK_institution_access_policy"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_institution_access_policy_CreatedAt_Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DROP INDEX database."IX_institution_access_policy_DataAccessPolicyId_InstitutionId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "AccessCountSinceStartTime_AccessCount"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "AccessCountSinceStartTime_StartTime"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "DataAccessPolicyId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_Duration"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_UpperLimit"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "AccessCountSinceStartTime_AccessCount"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "AccessCountSinceStartTime_StartTime"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "DataAccessPolicyId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_Duration"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_UpperLimit"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "Id"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "AccessCountSinceStartTime_AccessCount"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "AccessCountSinceStartTime_StartTime"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "CreatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "DataAccessPolicyId"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "UpdatedAt"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_Duration"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy DROP COLUMN "UpperAccessLimitPerTimeDuration_UpperLimit"; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "AccessCountSinceStartTime" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD "UpperAccessLimitPerTimeDuration" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."user" ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.photovoltaic_data ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.optical_data ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "AccessCountSinceStartTime" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD "UpperAccessLimitPerTimeDuration" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database."lifeCycle_data" ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "AccessCountSinceStartTime" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD "UpperAccessLimitPerTimeDuration" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.hygrothermal_data ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.get_https_resource ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.geometric_data ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ALTER COLUMN "Id" SET DEFAULT (gen_random_uuid()); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.calorimetric_data ADD "AccessPolicy" jsonb; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.user_access_policy ADD CONSTRAINT "PK_user_access_policy" PRIMARY KEY ("UserId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.open_id_connect_application_access_policy ADD CONSTRAINT "PK_open_id_connect_application_access_policy" PRIMARY KEY ("ClientId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + ALTER TABLE database.institution_access_policy ADD CONSTRAINT "PK_institution_access_policy" PRIMARY KEY ("InstitutionId"); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_id_cannot_change"() RETURNS trigger as $LC_TRIGGER_data_id_cannot_change$ + BEGIN + IF COALESCE(OLD."CalorimetricDataId", OLD."GeometricDataId", OLD."HygrothermalDataId", OLD."LifeCycleDataId", OLD."OpticalDataId", OLD."PhotovoltaicDataId") <> COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + THEN + RAISE EXCEPTION 'You cannot change the data ID of a resource.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_id_cannot_change$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_id_cannot_change BEFORE UPDATE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_id_cannot_change"(); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_ids_must_match"() RETURNS trigger as $LC_TRIGGER_data_ids_must_match$ + BEGIN + IF NEW."ParentId" IS NOT NULL + AND ( + SELECT COUNT("Id") + FROM database."get_https_resource" + WHERE "Id" = NEW."ParentId" AND COALESCE("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") = COALESCE(NEW."CalorimetricDataId", NEW."GeometricDataId", NEW."HygrothermalDataId", NEW."LifeCycleDataId", NEW."OpticalDataId", NEW."PhotovoltaicDataId") + ) + <> 1 + THEN + RAISE EXCEPTION 'The new resource must have the same data ID as its parent.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_data_ids_must_match$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_ids_must_match BEFORE INSERT + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_ids_must_match"(); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260608173427_UnifyAccessPolicies'; + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260608173427_UnifyAccessPolicies.sql b/backend/src/Migrations/rollback_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260608173427_UnifyAccessPolicies.sql new file mode 100644 index 00000000..5d50f3d2 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260608173427_UnifyAccessPolicies.sql @@ -0,0 +1,36 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DELETE FROM database.data_access_policy; + DELETE FROM database.user_access_policy; + DELETE FROM database.institution_access_policy; + DELETE FROM database.open_id_connect_application_access_policy; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData'; + END IF; +END $EF$; + +COMMIT; + diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index 5078ec2d..50cefc1a 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore; using NodaTime; using Database.Data; -using Database.Enumerations; using Database.ApiRequests; namespace Database.Services; @@ -35,7 +34,7 @@ CancellationToken cancellationToken { institutionIds = [currentInstitution.Uuid]; } - var filteredData = FilterData(data, openIdConnectClientId, currentUser, institutionIds, databaseContext); + var filteredData = FilterData(data, currentUser, institutionIds, openIdConnectClientId, databaseContext); var (postProcessedData, result) = await then(filteredData); await IncrementAccessCounts( postProcessedData, @@ -50,102 +49,26 @@ await IncrementAccessCounts( private static IQueryable FilterData( IQueryable data, - string? openIdConnectClientId, QueryCurrentUserOrInstitution.CurrentUser? currentUser, IReadOnlyList? institutionIds, + string? openIdConnectClientId, ApplicationDbContext databaseContext ) where TData : class, IData { return data.Where(_ => - _.AccessPolicy == null || !_.AccessPolicy.IsRestricted || ( - _.AccessPolicy.Combinator == LogicalCombinator.ALL && ( - (!_.AccessPolicy.AreUsersRestricted || ( - currentUser != null - && _.AccessPolicy.UserAccessPolicies != null - && _.AccessPolicy.UserAccessPolicies - .Where(_ => _.UserId == currentUser.Uuid) - .Any(_ => _.IsAllowed) - ) - ) - && - (!_.AccessPolicy.AreInstitutionsRestricted || ( - institutionIds != null - && _.AccessPolicy.InstitutionAccessPolicies != null - && _.AccessPolicy.InstitutionAccessPolicies - .Where(_ => institutionIds.Contains(_.InstitutionId)) - .Any(_ => _.IsAllowed) - ) - ) - && - (!_.AccessPolicy.AreOpenIdConnectApplicationsRestricted || ( - openIdConnectClientId != null - && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies != null - && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies - .Where(_ => _.ClientId == openIdConnectClientId) - .Any(_ => _.IsAllowed) - ) - ) - ) - || - _.AccessPolicy.Combinator == LogicalCombinator.SOME && ( - (!_.AccessPolicy.AreUsersRestricted || ( - currentUser != null - && _.AccessPolicy.UserAccessPolicies != null - && _.AccessPolicy.UserAccessPolicies - .Where(_ => _.UserId == currentUser.Uuid) - .Any(_ => _.IsAllowed) - ) - ) - || - (!_.AccessPolicy.AreInstitutionsRestricted || ( - institutionIds != null - && _.AccessPolicy.InstitutionAccessPolicies != null - && _.AccessPolicy.InstitutionAccessPolicies - .Where(_ => institutionIds.Contains(_.InstitutionId)) - .Any(_ => _.IsAllowed) - ) - ) - || - (!_.AccessPolicy.AreOpenIdConnectApplicationsRestricted || ( - openIdConnectClientId != null - && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies != null - && _.AccessPolicy.OpenIdConnectApplicationAccessPolicies - .Where(_ => _.ClientId == openIdConnectClientId) - .Any(_ => _.IsAllowed) - ) - ) - ) - ) - && ( - !databaseContext.UserAccessPolicies.AsQueryable().Any() || ( - currentUser != null - && databaseContext.UserAccessPolicies.AsQueryable() - .Where(_ => _.UserId == currentUser.Uuid) - .Any(_ => _.IsAllowed) - ) + _.AccessPolicy == null + || _.AccessPolicy.IsAccessAllowed(currentUser, institutionIds, openIdConnectClientId) ) && ( - !databaseContext.InstitutionAccessPolicies.AsQueryable().Any() || ( - institutionIds != null - && databaseContext.InstitutionAccessPolicies.AsQueryable() - .Where(_ => institutionIds.Contains(_.InstitutionId)) - .Any(_ => _.IsAllowed) - ) - ) - && - ( - !databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable().Any() || ( - openIdConnectClientId != null - && databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable() - .Where(_ => _.ClientId == openIdConnectClientId) - .Any(_ => _.IsAllowed) + !databaseContext.DataAccessPolicies.Any(_ => + _.DataId == null + && !_.IsAccessAllowed(currentUser, institutionIds, openIdConnectClientId) ) ) ); - } private async Task IncrementAccessCounts( @@ -158,59 +81,39 @@ CancellationToken cancellationToken ) where TData : class, IData { - foreach (var data in allData) + if (currentUser is null && institutionIds is null && openIdConnectClientId is null) { - databaseContext.Attach(data); - if (currentUser is not null) - { - foreach (var userAccessPolicy in - data.AccessPolicy?.UserAccessPolicies - ?.Where(_ => _.UserId == currentUser.Uuid) - ?? [] - ) - { - userAccessPolicy?.IncrementAccessCount(clock); - } - } - if (institutionIds is not null) - { - foreach (var institutionAccessPolicy in - data.AccessPolicy?.InstitutionAccessPolicies - ?.Where(_ => institutionIds.Contains(_.InstitutionId)) - ?? [] - ) - { - institutionAccessPolicy?.IncrementAccessCount(clock); - } - } - if (openIdConnectClientId is not null) - { - foreach (var applicationAccessPolicy in - data.AccessPolicy?.OpenIdConnectApplicationAccessPolicies - ?.Where(_ => _.ClientId == openIdConnectClientId) - ?? [] - ) - { - applicationAccessPolicy?.IncrementAccessCount(clock); - } - } + return; } + var allDataIds = allData.Select(_ => _.Id).ToArray(); if (currentUser is not null) { foreach (var userAccessPolicy in - await databaseContext.UserAccessPolicies.AsQueryable() - .Where(_ => _.UserId == currentUser.Uuid) + await databaseContext.UserAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && _.UserId == currentUser.Uuid + ) .ToListAsync(cancellationToken) ) { - userAccessPolicy?.IncrementAccessCount(clock); + userAccessPolicy.IncrementAccessCount(clock); } } if (institutionIds is not null) { foreach (var institutionAccessPolicy in - await databaseContext.InstitutionAccessPolicies.AsQueryable() - .Where(_ => institutionIds.Contains(_.InstitutionId)) + await databaseContext.InstitutionAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && institutionIds.Contains(_.InstitutionId) + ) .ToListAsync(cancellationToken) ) { @@ -220,12 +123,18 @@ await databaseContext.InstitutionAccessPolicies.AsQueryable() if (openIdConnectClientId is not null) { foreach (var applicationAccessPolicy in - await databaseContext.OpenIdConnectApplicationAccessPolicies.AsQueryable() - .Where(_ => _.ClientId == openIdConnectClientId) + await databaseContext.OpenIdConnectApplicationAccessPolicies + .Where(_ => + _.DataAccessPolicy != null && ( + _.DataAccessPolicy.DataId == null + || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) + ) + && _.ClientId == openIdConnectClientId + ) .ToListAsync(cancellationToken) ) { - applicationAccessPolicy?.IncrementAccessCount(clock); + applicationAccessPolicy.IncrementAccessCount(clock); } } await databaseContext.SaveChangesAsync(cancellationToken); diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index a2faeaf1..9ce917bd 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -360,12 +360,14 @@ type Query { type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - clearDataAccessPolicy: ClearDataAccessPolicyPayload! @cost(weight: "10") - clearInstitutionAccessPolicies: ClearInstitutionAccessPoliciesPayload! + clearDataAccessPolicies: ClearDataAccessPoliciesPayload! @cost(weight: "10") + clearInstitutionAccessPolicies(input: ClearInstitutionAccessPoliciesInput!): ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") - clearOpenIdConnectApplicationAccessPolicies: ClearOpenIdConnectApplicationAccessPoliciesPayload! + clearOpenIdConnectApplicationAccessPolicies( + input: ClearOpenIdConnectApplicationAccessPoliciesInput! + ): ClearOpenIdConnectApplicationAccessPoliciesPayload! @cost(weight: "10") + clearUserAccessPolicies(input: ClearUserAccessPoliciesInput!): ClearUserAccessPoliciesPayload! @cost(weight: "10") - clearUserAccessPolicies: ClearUserAccessPoliciesPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -384,26 +386,8 @@ type Mutation { where: CreateResponseApprovalsPropositionInput @cost(weight: "10") ): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteDataAccessPolicy(input: DeleteDataAccessPolicyInput!): DeleteDataAccessPolicyPayload! - @cost(weight: "10") - deleteDataAccessPolicyOfInstitution( - input: DeleteDataAccessPolicyOfInstitutionInput! - ): DeleteDataAccessPolicyOfInstitutionPayload! @cost(weight: "10") - deleteDataAccessPolicyOfOpenIdConnectApplication( - input: DeleteDataAccessPolicyOfOpenIdConnectApplicationInput! - ): DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload! - @cost(weight: "10") - deleteDataAccessPolicyOfUser(input: DeleteDataAccessPolicyOfUserInput!): DeleteDataAccessPolicyOfUserPayload! - @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") - deleteInstitutionAccessPolicy(input: DeleteInstitutionAccessPolicyInput!): DeleteInstitutionAccessPolicyPayload! - @cost(weight: "10") - deleteOpenIdConnectApplicationAccessPolicy( - input: DeleteOpenIdConnectApplicationAccessPolicyInput! - ): DeleteOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") - deleteUserAccessPolicy(input: DeleteUserAccessPolicyInput!): DeleteUserAccessPolicyPayload! - @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues( where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") @@ -411,22 +395,23 @@ type Mutation { removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") - setDataAccessLimitForInstitution( - input: SetDataAccessLimitForInstitutionInput! - ): SetDataAccessLimitForInstitutionPayload! @cost(weight: "10") - setDataAccessLimitForOpenIdConnectApplication( - input: SetDataAccessLimitForOpenIdConnectApplicationInput! - ): SetDataAccessLimitForOpenIdConnectApplicationPayload! @cost(weight: "10") - setDataAccessLimitForUser(input: SetDataAccessLimitForUserInput!): SetDataAccessLimitForUserPayload! - @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") - setInstitutionAccessLimit(input: SetInstitutionAccessLimitInput!): SetInstitutionAccessLimitPayload! + setInstitutionAccessPolicy(input: SetInstitutionAccessPolicyInput!): SetInstitutionAccessPolicyPayload! @cost(weight: "10") - setOpenIdConnectApplicationAccessLimit( - input: SetOpenIdConnectApplicationAccessLimitInput! - ): SetOpenIdConnectApplicationAccessLimitPayload! @cost(weight: "10") - setUserAccessLimit(input: SetUserAccessLimitInput!): SetUserAccessLimitPayload! + setOpenIdConnectApplicationAccessPolicy( + input: SetOpenIdConnectApplicationAccessPolicyInput! + ): SetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + setUserAccessPolicy(input: SetUserAccessPolicyInput!): SetUserAccessPolicyPayload! + @cost(weight: "10") + unsetDataAccessPolicy(input: UnsetDataAccessPolicyInput!): UnsetDataAccessPolicyPayload! + @cost(weight: "10") + unsetInstitutionAccessPolicy(input: UnsetInstitutionAccessPolicyInput!): UnsetInstitutionAccessPolicyPayload! + @cost(weight: "10") + unsetOpenIdConnectApplicationAccessPolicy( + input: UnsetOpenIdConnectApplicationAccessPolicyInput! + ): UnsetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + unsetUserAccessPolicy(input: UnsetUserAccessPolicyInput!): UnsetUserAccessPolicyPayload! @cost(weight: "10") updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") @@ -529,15 +514,15 @@ type CielabColor { observer: CalorimetricObserver } -type ClearDataAccessPolicyError implements UserError { - code: ClearDataAccessPolicyErrorCode! +type ClearDataAccessPoliciesError implements UserError { + code: ClearDataAccessPoliciesErrorCode! message: String! path: [String!]! } -type ClearDataAccessPolicyPayload { +type ClearDataAccessPoliciesPayload { dataAccessPolicy: [DataAccessPolicy!] - errors: [ClearDataAccessPolicyError!] + errors: [ClearDataAccessPoliciesError!] query: Query! } @@ -548,8 +533,8 @@ type ClearInstitutionAccessPoliciesError implements UserError { } type ClearInstitutionAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearInstitutionAccessPoliciesError!] - institutionAccessPolicies: [InstitutionAccessPolicy!] query: Query! } @@ -560,8 +545,8 @@ type ClearOpenIdConnectApplicationAccessPoliciesError implements UserError { } type ClearOpenIdConnectApplicationAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearOpenIdConnectApplicationAccessPoliciesError!] - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] query: Query! } @@ -572,9 +557,9 @@ type ClearUserAccessPoliciesError implements UserError { } type ClearUserAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearUserAccessPoliciesError!] query: Query! - userAccessPolicies: [UserAccessPolicy!] } type Component { @@ -700,14 +685,33 @@ type CurrentInstitution { } type DataAccessPolicy { - areInstitutionsRestricted: Boolean! - areOpenIdConnectApplicationsRestricted: Boolean! - areUsersRestricted: Boolean! + calorimetricData: CalorimetricData + calorimetricDataId: Uuid combinator: LogicalCombinator! - institutionAccessPolicies: [InstitutionAccessPolicy!] - isRestricted: Boolean! - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] - userAccessPolicies: [UserAccessPolicy!] + createdAt: DateTime! + data(dataKind: DataKind!): Data + dataId(dataKind: DataKind!): Uuid + geometricData: GeometricData + geometricDataId: Uuid + hygrothermalData: HygrothermalData + hygrothermalDataId: Uuid + id: Uuid! + institutionAccessPolicies: [InstitutionAccessPolicy!]! + isAllowed( + currentUser: CurrentUserInput + institutionIds: [Uuid!] + openIdConnectClientId: String + ): Boolean! + lifeCycleData: LifeCycleData + lifeCycleDataId: Uuid + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + opticalData: OpticalData + opticalDataId: Uuid + photovoltaicData: PhotovoltaicData + photovoltaicDataId: Uuid + updatedAt: DateTime! + userAccessPolicies: [UserAccessPolicy!]! + version: NonNegativeInt! } type DataApproval implements Approval { @@ -765,54 +769,6 @@ type DataFormat { uuid: Uuid! } -type DeleteDataAccessPolicyError implements UserError { - code: DeleteDataAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfInstitutionError implements UserError { - code: DeleteDataAccessPolicyOfInstitutionErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfInstitutionPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfInstitutionError!] - query: Query! -} - -type DeleteDataAccessPolicyOfOpenIdConnectApplicationError implements UserError { - code: DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfOpenIdConnectApplicationError!] - query: Query! -} - -type DeleteDataAccessPolicyOfUserError implements UserError { - code: DeleteDataAccessPolicyOfUserErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfUserPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfUserError!] - query: Query! -} - -type DeleteDataAccessPolicyPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyError!] - query: Query! -} - type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! @@ -836,42 +792,6 @@ type DeleteGetHttpsResourcePayload { query: Query! } -type DeleteInstitutionAccessPolicyError implements UserError { - code: DeleteInstitutionAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteInstitutionAccessPolicyPayload { - errors: [DeleteInstitutionAccessPolicyError!] - institutionAccessPolicy: InstitutionAccessPolicy - query: Query! -} - -type DeleteOpenIdConnectApplicationAccessPolicyError implements UserError { - code: DeleteOpenIdConnectApplicationAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteOpenIdConnectApplicationAccessPolicyPayload { - errors: [DeleteOpenIdConnectApplicationAccessPolicyError!] - openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy - query: Query! -} - -type DeleteUserAccessPolicyError implements UserError { - code: DeleteUserAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteUserAccessPolicyPayload { - errors: [DeleteUserAccessPolicyError!] - query: Query! - userAccessPolicy: UserAccessPolicy -} - type FileMetaInformation { dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! @@ -1036,10 +956,16 @@ type Institution { type InstitutionAccessPolicy { accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! institutionId: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + version: NonNegativeInt! } type LifeCycleData implements Node & Data { @@ -1116,9 +1042,15 @@ type OpenEndedDateTimeRange { type OpenIdConnectApplicationAccessPolicy { accessCountSinceStartTime: CountSinceTime clientId: String! + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + version: NonNegativeInt! } type OpticalData implements Node & Data { @@ -1307,42 +1239,6 @@ type RetractDataPayload { query: Query! } -type SetDataAccessLimitForInstitutionError implements UserError { - code: SetDataAccessLimitForInstitutionErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForInstitutionPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForInstitutionError!] - query: Query! -} - -type SetDataAccessLimitForOpenIdConnectApplicationError implements UserError { - code: SetDataAccessLimitForOpenIdConnectApplicationErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForOpenIdConnectApplicationPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForOpenIdConnectApplicationError!] - query: Query! -} - -type SetDataAccessLimitForUserError implements UserError { - code: SetDataAccessLimitForUserErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForUserPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForUserError!] - query: Query! -} - type SetGetHttpsResourceParentError implements UserError { code: SetGetHttpsResourceParentErrorCode! message: String! @@ -1355,38 +1251,38 @@ type SetGetHttpsResourceParentPayload { query: Query! } -type SetInstitutionAccessLimitError implements UserError { - code: SetInstitutionAccessLimitErrorCode! +type SetInstitutionAccessPolicyError implements UserError { + code: SetInstitutionAccessPolicyErrorCode! message: String! path: [String!]! } -type SetInstitutionAccessLimitPayload { - errors: [SetInstitutionAccessLimitError!] +type SetInstitutionAccessPolicyPayload { + errors: [SetInstitutionAccessPolicyError!] institutionAccessPolicy: InstitutionAccessPolicy query: Query! } -type SetOpenIdConnectApplicationAccessLimitError implements UserError { - code: SetOpenIdConnectApplicationAccessLimitErrorCode! +type SetOpenIdConnectApplicationAccessPolicyError implements UserError { + code: SetOpenIdConnectApplicationAccessPolicyErrorCode! message: String! path: [String!]! } -type SetOpenIdConnectApplicationAccessLimitPayload { - errors: [SetOpenIdConnectApplicationAccessLimitError!] +type SetOpenIdConnectApplicationAccessPolicyPayload { + errors: [SetOpenIdConnectApplicationAccessPolicyError!] openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy query: Query! } -type SetUserAccessLimitError implements UserError { - code: SetUserAccessLimitErrorCode! +type SetUserAccessPolicyError implements UserError { + code: SetUserAccessPolicyErrorCode! message: String! path: [String!]! } -type SetUserAccessLimitPayload { - errors: [SetUserAccessLimitError!] +type SetUserAccessPolicyPayload { + errors: [SetUserAccessPolicyError!] query: Query! userAccessPolicy: UserAccessPolicy } @@ -1411,6 +1307,54 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UnsetDataAccessPolicyError implements UserError { + code: UnsetDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [UnsetDataAccessPolicyError!] + query: Query! +} + +type UnsetInstitutionAccessPolicyError implements UserError { + code: UnsetInstitutionAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetInstitutionAccessPolicyPayload { + errors: [UnsetInstitutionAccessPolicyError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type UnsetOpenIdConnectApplicationAccessPolicyError implements UserError { + code: UnsetOpenIdConnectApplicationAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetOpenIdConnectApplicationAccessPolicyPayload { + applicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + errors: [UnsetOpenIdConnectApplicationAccessPolicyError!] + query: Query! +} + +type UnsetUserAccessPolicyError implements UserError { + code: UnsetUserAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetUserAccessPolicyPayload { + errors: [UnsetUserAccessPolicyError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + type UpdateChildGetHttpsResourceError implements UserError { code: UpdateChildGetHttpsResourceErrorCode! message: String! @@ -1487,10 +1431,16 @@ type User implements Node { type UserAccessPolicy { accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration userId: Uuid! + version: NonNegativeInt! } type UserInfo { @@ -1657,6 +1607,18 @@ input CielabColorPropositionInput { or: [CielabColorPropositionInput!] } +input ClearInstitutionAccessPoliciesInput { + data: DataReferenceInput +} + +input ClearOpenIdConnectApplicationAccessPoliciesInput { + data: DataReferenceInput +} + +input ClearUserAccessPoliciesInput { + data: DataReferenceInput +} + input ClosedIntervalInput { lowerBound: Float! upperBound: Float! @@ -1790,6 +1752,13 @@ input CrossDatabaseDataReferencePropositionInput { or: [CrossDatabaseDataReferencePropositionInput!] } +input CurrentUserInput { + databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnectionInput! + name: String! + representedInstitutions: UserRepresentedInstitutionConnectionInput! + uuid: Uuid! +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -1828,6 +1797,11 @@ input DataPropositionInput { warnings: StringsPropositionInput } +input DataReferenceInput { + dataId: Uuid! + dataKind: DataKind! +} + input DataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") componentId: SortEnumType @cost(weight: "10") @@ -1870,29 +1844,6 @@ input DecimalPropositionInput { notLessThanOrEqualTo: Decimal @cost(weight: "10") } -input DeleteDataAccessPolicyInput { - dataId: Uuid! - dataKind: DataKind! -} - -input DeleteDataAccessPolicyOfInstitutionInput { - dataId: Uuid! - dataKind: DataKind! - institutionId: Uuid! -} - -input DeleteDataAccessPolicyOfOpenIdConnectApplicationInput { - clientId: String! - dataId: Uuid! - dataKind: DataKind! -} - -input DeleteDataAccessPolicyOfUserInput { - dataId: Uuid! - dataKind: DataKind! - userId: Uuid! -} - input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1902,18 +1853,6 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input DeleteInstitutionAccessPolicyInput { - institutionId: Uuid! -} - -input DeleteOpenIdConnectApplicationAccessPolicyInput { - clientId: String! -} - -input DeleteUserAccessPolicyInput { - userId: Uuid! -} - input DurationPropositionInput { equalTo: Duration @cost(weight: "10") greaterThan: Duration @cost(weight: "10") @@ -2127,6 +2066,20 @@ input InstitutionAccessPolicySortInput { institutionId: SortEnumType @cost(weight: "10") } +input InstitutionManagedInstitutionConnectionInput { + edges: [InstitutionManagedInstitutionEdgeInput!]! + totalCount: NonNegativeInt! +} + +input InstitutionManagedInstitutionEdgeInput { + node: InstitutionManagedInstitutionNodeInput! +} + +input InstitutionManagedInstitutionNodeInput { + name: String! + uuid: Uuid! +} + input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -2507,44 +2460,26 @@ input RootGetHttpsResourceInput { description: String! } -input SetDataAccessLimitForInstitutionInput { - dataId: Uuid! - dataKind: DataKind! - institutionId: Uuid! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput -} - -input SetDataAccessLimitForOpenIdConnectApplicationInput { - clientId: String! - dataId: Uuid! - dataKind: DataKind! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput -} - -input SetDataAccessLimitForUserInput { - dataId: Uuid! - dataKind: DataKind! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput - userId: Uuid! -} - input SetGetHttpsResourceParentInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! getHttpsResourceId: Uuid! parentId: Uuid! } -input SetInstitutionAccessLimitInput { +input SetInstitutionAccessPolicyInput { + data: DataReferenceInput institutionId: Uuid! upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput } -input SetOpenIdConnectApplicationAccessLimitInput { +input SetOpenIdConnectApplicationAccessPolicyInput { clientId: String! + data: DataReferenceInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput } -input SetUserAccessLimitInput { +input SetUserAccessPolicyInput { + data: DataReferenceInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput userId: Uuid! } @@ -2656,6 +2591,25 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UnsetDataAccessPolicyInput { + data: DataReferenceInput +} + +input UnsetInstitutionAccessPolicyInput { + data: DataReferenceInput + institutionId: Uuid! +} + +input UnsetOpenIdConnectApplicationAccessPolicyInput { + clientId: String! + data: DataReferenceInput +} + +input UnsetUserAccessPolicyInput { + data: DataReferenceInput + userId: Uuid! +} + input UpdateChildGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -2759,6 +2713,22 @@ input UserAccessPolicySortInput { userId: SortEnumType @cost(weight: "10") } +input UserRepresentedInstitutionConnectionInput { + edges: [UserRepresentedInstitutionEdgeInput!]! + totalCount: NonNegativeInt! +} + +input UserRepresentedInstitutionEdgeInput { + node: UserRepresentedInstitutionNodeInput! + role: InstitutionRepresentativeRole! +} + +input UserRepresentedInstitutionNodeInput { + managedInstitutions: InstitutionManagedInstitutionConnectionInput! + name: String! + uuid: Uuid! +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") @@ -2813,7 +2783,7 @@ enum CalorimetricObserver { TEN_DEGREES } -enum ClearDataAccessPolicyErrorCode { +enum ClearDataAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED @@ -2824,18 +2794,21 @@ enum ClearInstitutionAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum ClearUserAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum CoatedSide { @@ -2965,37 +2938,6 @@ enum DataKind { PHOTOVOLTAIC_DATA } -enum DeleteDataAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA -} - -enum DeleteDataAccessPolicyOfInstitutionErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION -} - -enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_APPLICATION -} - -enum DeleteDataAccessPolicyOfUserErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_USER -} - enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -3014,32 +2956,16 @@ enum DeleteGetHttpsResourceErrorCode { HAS_CHILDREN } -enum DeleteInstitutionAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - -enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - -enum DeleteUserAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - enum Illuminant { A D65 } +enum InstitutionRepresentativeRole { + OWNER + ASSISTANT +} + enum JsonValueKind { UNDEFINED OBJECT @@ -3121,30 +3047,6 @@ enum RetractDataErrorCode { NOT_PUBLISHED } -enum SetDataAccessLimitForInstitutionErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION -} - -enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_APPLICATION -} - -enum SetDataAccessLimitForUserErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_USER -} - enum SetGetHttpsResourceParentErrorCode { UNKNOWN UNAUTHORIZED @@ -3156,22 +3058,28 @@ enum SetGetHttpsResourceParentErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum SetInstitutionAccessLimitErrorCode { +enum SetInstitutionAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION } -enum SetOpenIdConnectApplicationAccessLimitErrorCode { +enum SetOpenIdConnectApplicationAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION } -enum SetUserAccessLimitErrorCode { +enum SetUserAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER } enum SortEnumType { @@ -3206,6 +3114,38 @@ enum Standardizer { WELL } +enum UnsetDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetInstitutionAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetOpenIdConnectApplicationAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetUserAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + enum UpdateChildGetHttpsResourceErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index a2faeaf1..9ce917bd 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -360,12 +360,14 @@ type Query { type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - clearDataAccessPolicy: ClearDataAccessPolicyPayload! @cost(weight: "10") - clearInstitutionAccessPolicies: ClearInstitutionAccessPoliciesPayload! + clearDataAccessPolicies: ClearDataAccessPoliciesPayload! @cost(weight: "10") + clearInstitutionAccessPolicies(input: ClearInstitutionAccessPoliciesInput!): ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") - clearOpenIdConnectApplicationAccessPolicies: ClearOpenIdConnectApplicationAccessPoliciesPayload! + clearOpenIdConnectApplicationAccessPolicies( + input: ClearOpenIdConnectApplicationAccessPoliciesInput! + ): ClearOpenIdConnectApplicationAccessPoliciesPayload! @cost(weight: "10") + clearUserAccessPolicies(input: ClearUserAccessPoliciesInput!): ClearUserAccessPoliciesPayload! @cost(weight: "10") - clearUserAccessPolicies: ClearUserAccessPoliciesPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -384,26 +386,8 @@ type Mutation { where: CreateResponseApprovalsPropositionInput @cost(weight: "10") ): CreateResponseApprovalsPayload! @cost(weight: "10") deleteData(input: DeleteDataInput!): DeleteDataPayload! @cost(weight: "10") - deleteDataAccessPolicy(input: DeleteDataAccessPolicyInput!): DeleteDataAccessPolicyPayload! - @cost(weight: "10") - deleteDataAccessPolicyOfInstitution( - input: DeleteDataAccessPolicyOfInstitutionInput! - ): DeleteDataAccessPolicyOfInstitutionPayload! @cost(weight: "10") - deleteDataAccessPolicyOfOpenIdConnectApplication( - input: DeleteDataAccessPolicyOfOpenIdConnectApplicationInput! - ): DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload! - @cost(weight: "10") - deleteDataAccessPolicyOfUser(input: DeleteDataAccessPolicyOfUserInput!): DeleteDataAccessPolicyOfUserPayload! - @cost(weight: "10") deleteGetHttpsResource(input: DeleteGetHttpsResourceInput!): DeleteGetHttpsResourcePayload! @cost(weight: "10") - deleteInstitutionAccessPolicy(input: DeleteInstitutionAccessPolicyInput!): DeleteInstitutionAccessPolicyPayload! - @cost(weight: "10") - deleteOpenIdConnectApplicationAccessPolicy( - input: DeleteOpenIdConnectApplicationAccessPolicyInput! - ): DeleteOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") - deleteUserAccessPolicy(input: DeleteUserAccessPolicyInput!): DeleteUserAccessPolicyPayload! - @cost(weight: "10") publishData(input: PublishDataInput!): PublishDataPayload! @cost(weight: "10") recomputeGetHttpsResourceHashValues( where: RecomputeGetHttpsResourceHashValuesPropositionInput @cost(weight: "10") @@ -411,22 +395,23 @@ type Mutation { removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") - setDataAccessLimitForInstitution( - input: SetDataAccessLimitForInstitutionInput! - ): SetDataAccessLimitForInstitutionPayload! @cost(weight: "10") - setDataAccessLimitForOpenIdConnectApplication( - input: SetDataAccessLimitForOpenIdConnectApplicationInput! - ): SetDataAccessLimitForOpenIdConnectApplicationPayload! @cost(weight: "10") - setDataAccessLimitForUser(input: SetDataAccessLimitForUserInput!): SetDataAccessLimitForUserPayload! - @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") - setInstitutionAccessLimit(input: SetInstitutionAccessLimitInput!): SetInstitutionAccessLimitPayload! + setInstitutionAccessPolicy(input: SetInstitutionAccessPolicyInput!): SetInstitutionAccessPolicyPayload! @cost(weight: "10") - setOpenIdConnectApplicationAccessLimit( - input: SetOpenIdConnectApplicationAccessLimitInput! - ): SetOpenIdConnectApplicationAccessLimitPayload! @cost(weight: "10") - setUserAccessLimit(input: SetUserAccessLimitInput!): SetUserAccessLimitPayload! + setOpenIdConnectApplicationAccessPolicy( + input: SetOpenIdConnectApplicationAccessPolicyInput! + ): SetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + setUserAccessPolicy(input: SetUserAccessPolicyInput!): SetUserAccessPolicyPayload! + @cost(weight: "10") + unsetDataAccessPolicy(input: UnsetDataAccessPolicyInput!): UnsetDataAccessPolicyPayload! + @cost(weight: "10") + unsetInstitutionAccessPolicy(input: UnsetInstitutionAccessPolicyInput!): UnsetInstitutionAccessPolicyPayload! + @cost(weight: "10") + unsetOpenIdConnectApplicationAccessPolicy( + input: UnsetOpenIdConnectApplicationAccessPolicyInput! + ): UnsetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") + unsetUserAccessPolicy(input: UnsetUserAccessPolicyInput!): UnsetUserAccessPolicyPayload! @cost(weight: "10") updateChildGetHttpsResource(input: UpdateChildGetHttpsResourceInput!): UpdateChildGetHttpsResourcePayload! @cost(weight: "10") @@ -529,15 +514,15 @@ type CielabColor { observer: CalorimetricObserver } -type ClearDataAccessPolicyError implements UserError { - code: ClearDataAccessPolicyErrorCode! +type ClearDataAccessPoliciesError implements UserError { + code: ClearDataAccessPoliciesErrorCode! message: String! path: [String!]! } -type ClearDataAccessPolicyPayload { +type ClearDataAccessPoliciesPayload { dataAccessPolicy: [DataAccessPolicy!] - errors: [ClearDataAccessPolicyError!] + errors: [ClearDataAccessPoliciesError!] query: Query! } @@ -548,8 +533,8 @@ type ClearInstitutionAccessPoliciesError implements UserError { } type ClearInstitutionAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearInstitutionAccessPoliciesError!] - institutionAccessPolicies: [InstitutionAccessPolicy!] query: Query! } @@ -560,8 +545,8 @@ type ClearOpenIdConnectApplicationAccessPoliciesError implements UserError { } type ClearOpenIdConnectApplicationAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearOpenIdConnectApplicationAccessPoliciesError!] - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] query: Query! } @@ -572,9 +557,9 @@ type ClearUserAccessPoliciesError implements UserError { } type ClearUserAccessPoliciesPayload { + dataAccessPolicy: DataAccessPolicy errors: [ClearUserAccessPoliciesError!] query: Query! - userAccessPolicies: [UserAccessPolicy!] } type Component { @@ -700,14 +685,33 @@ type CurrentInstitution { } type DataAccessPolicy { - areInstitutionsRestricted: Boolean! - areOpenIdConnectApplicationsRestricted: Boolean! - areUsersRestricted: Boolean! + calorimetricData: CalorimetricData + calorimetricDataId: Uuid combinator: LogicalCombinator! - institutionAccessPolicies: [InstitutionAccessPolicy!] - isRestricted: Boolean! - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!] - userAccessPolicies: [UserAccessPolicy!] + createdAt: DateTime! + data(dataKind: DataKind!): Data + dataId(dataKind: DataKind!): Uuid + geometricData: GeometricData + geometricDataId: Uuid + hygrothermalData: HygrothermalData + hygrothermalDataId: Uuid + id: Uuid! + institutionAccessPolicies: [InstitutionAccessPolicy!]! + isAllowed( + currentUser: CurrentUserInput + institutionIds: [Uuid!] + openIdConnectClientId: String + ): Boolean! + lifeCycleData: LifeCycleData + lifeCycleDataId: Uuid + openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + opticalData: OpticalData + opticalDataId: Uuid + photovoltaicData: PhotovoltaicData + photovoltaicDataId: Uuid + updatedAt: DateTime! + userAccessPolicies: [UserAccessPolicy!]! + version: NonNegativeInt! } type DataApproval implements Approval { @@ -765,54 +769,6 @@ type DataFormat { uuid: Uuid! } -type DeleteDataAccessPolicyError implements UserError { - code: DeleteDataAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfInstitutionError implements UserError { - code: DeleteDataAccessPolicyOfInstitutionErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfInstitutionPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfInstitutionError!] - query: Query! -} - -type DeleteDataAccessPolicyOfOpenIdConnectApplicationError implements UserError { - code: DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfOpenIdConnectApplicationPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfOpenIdConnectApplicationError!] - query: Query! -} - -type DeleteDataAccessPolicyOfUserError implements UserError { - code: DeleteDataAccessPolicyOfUserErrorCode! - message: String! - path: [String!]! -} - -type DeleteDataAccessPolicyOfUserPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyOfUserError!] - query: Query! -} - -type DeleteDataAccessPolicyPayload { - dataAccessPolicy: DataAccessPolicy - errors: [DeleteDataAccessPolicyError!] - query: Query! -} - type DeleteDataError implements UserError { code: DeleteDataErrorCode! message: String! @@ -836,42 +792,6 @@ type DeleteGetHttpsResourcePayload { query: Query! } -type DeleteInstitutionAccessPolicyError implements UserError { - code: DeleteInstitutionAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteInstitutionAccessPolicyPayload { - errors: [DeleteInstitutionAccessPolicyError!] - institutionAccessPolicy: InstitutionAccessPolicy - query: Query! -} - -type DeleteOpenIdConnectApplicationAccessPolicyError implements UserError { - code: DeleteOpenIdConnectApplicationAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteOpenIdConnectApplicationAccessPolicyPayload { - errors: [DeleteOpenIdConnectApplicationAccessPolicyError!] - openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy - query: Query! -} - -type DeleteUserAccessPolicyError implements UserError { - code: DeleteUserAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type DeleteUserAccessPolicyPayload { - errors: [DeleteUserAccessPolicyError!] - query: Query! - userAccessPolicy: UserAccessPolicy -} - type FileMetaInformation { dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! @@ -1036,10 +956,16 @@ type Institution { type InstitutionAccessPolicy { accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! institutionId: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + version: NonNegativeInt! } type LifeCycleData implements Node & Data { @@ -1116,9 +1042,15 @@ type OpenEndedDateTimeRange { type OpenIdConnectApplicationAccessPolicy { accessCountSinceStartTime: CountSinceTime clientId: String! + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + version: NonNegativeInt! } type OpticalData implements Node & Data { @@ -1307,42 +1239,6 @@ type RetractDataPayload { query: Query! } -type SetDataAccessLimitForInstitutionError implements UserError { - code: SetDataAccessLimitForInstitutionErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForInstitutionPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForInstitutionError!] - query: Query! -} - -type SetDataAccessLimitForOpenIdConnectApplicationError implements UserError { - code: SetDataAccessLimitForOpenIdConnectApplicationErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForOpenIdConnectApplicationPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForOpenIdConnectApplicationError!] - query: Query! -} - -type SetDataAccessLimitForUserError implements UserError { - code: SetDataAccessLimitForUserErrorCode! - message: String! - path: [String!]! -} - -type SetDataAccessLimitForUserPayload { - dataAccessPolicy: DataAccessPolicy - errors: [SetDataAccessLimitForUserError!] - query: Query! -} - type SetGetHttpsResourceParentError implements UserError { code: SetGetHttpsResourceParentErrorCode! message: String! @@ -1355,38 +1251,38 @@ type SetGetHttpsResourceParentPayload { query: Query! } -type SetInstitutionAccessLimitError implements UserError { - code: SetInstitutionAccessLimitErrorCode! +type SetInstitutionAccessPolicyError implements UserError { + code: SetInstitutionAccessPolicyErrorCode! message: String! path: [String!]! } -type SetInstitutionAccessLimitPayload { - errors: [SetInstitutionAccessLimitError!] +type SetInstitutionAccessPolicyPayload { + errors: [SetInstitutionAccessPolicyError!] institutionAccessPolicy: InstitutionAccessPolicy query: Query! } -type SetOpenIdConnectApplicationAccessLimitError implements UserError { - code: SetOpenIdConnectApplicationAccessLimitErrorCode! +type SetOpenIdConnectApplicationAccessPolicyError implements UserError { + code: SetOpenIdConnectApplicationAccessPolicyErrorCode! message: String! path: [String!]! } -type SetOpenIdConnectApplicationAccessLimitPayload { - errors: [SetOpenIdConnectApplicationAccessLimitError!] +type SetOpenIdConnectApplicationAccessPolicyPayload { + errors: [SetOpenIdConnectApplicationAccessPolicyError!] openIdConnectApplicationAccessPolicy: OpenIdConnectApplicationAccessPolicy query: Query! } -type SetUserAccessLimitError implements UserError { - code: SetUserAccessLimitErrorCode! +type SetUserAccessPolicyError implements UserError { + code: SetUserAccessPolicyErrorCode! message: String! path: [String!]! } -type SetUserAccessLimitPayload { - errors: [SetUserAccessLimitError!] +type SetUserAccessPolicyPayload { + errors: [SetUserAccessPolicyError!] query: Query! userAccessPolicy: UserAccessPolicy } @@ -1411,6 +1307,54 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } +type UnsetDataAccessPolicyError implements UserError { + code: UnsetDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [UnsetDataAccessPolicyError!] + query: Query! +} + +type UnsetInstitutionAccessPolicyError implements UserError { + code: UnsetInstitutionAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetInstitutionAccessPolicyPayload { + errors: [UnsetInstitutionAccessPolicyError!] + institutionAccessPolicy: InstitutionAccessPolicy + query: Query! +} + +type UnsetOpenIdConnectApplicationAccessPolicyError implements UserError { + code: UnsetOpenIdConnectApplicationAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetOpenIdConnectApplicationAccessPolicyPayload { + applicationAccessPolicy: OpenIdConnectApplicationAccessPolicy + errors: [UnsetOpenIdConnectApplicationAccessPolicyError!] + query: Query! +} + +type UnsetUserAccessPolicyError implements UserError { + code: UnsetUserAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type UnsetUserAccessPolicyPayload { + errors: [UnsetUserAccessPolicyError!] + query: Query! + userAccessPolicy: UserAccessPolicy +} + type UpdateChildGetHttpsResourceError implements UserError { code: UpdateChildGetHttpsResourceErrorCode! message: String! @@ -1487,10 +1431,16 @@ type User implements Node { type UserAccessPolicy { accessCountSinceStartTime: CountSinceTime + createdAt: DateTime! + dataAccessPolicy: DataAccessPolicy + dataAccessPolicyId: Uuid! + id: Uuid! isAllowed: Boolean! isWithinTimeSpan: Boolean! + updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration userId: Uuid! + version: NonNegativeInt! } type UserInfo { @@ -1657,6 +1607,18 @@ input CielabColorPropositionInput { or: [CielabColorPropositionInput!] } +input ClearInstitutionAccessPoliciesInput { + data: DataReferenceInput +} + +input ClearOpenIdConnectApplicationAccessPoliciesInput { + data: DataReferenceInput +} + +input ClearUserAccessPoliciesInput { + data: DataReferenceInput +} + input ClosedIntervalInput { lowerBound: Float! upperBound: Float! @@ -1790,6 +1752,13 @@ input CrossDatabaseDataReferencePropositionInput { or: [CrossDatabaseDataReferencePropositionInput!] } +input CurrentUserInput { + databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnectionInput! + name: String! + representedInstitutions: UserRepresentedInstitutionConnectionInput! + uuid: Uuid! +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -1828,6 +1797,11 @@ input DataPropositionInput { warnings: StringsPropositionInput } +input DataReferenceInput { + dataId: Uuid! + dataKind: DataKind! +} + input DataSortInput { appliedMethod: AppliedMethodSortInput @cost(weight: "10") componentId: SortEnumType @cost(weight: "10") @@ -1870,29 +1844,6 @@ input DecimalPropositionInput { notLessThanOrEqualTo: Decimal @cost(weight: "10") } -input DeleteDataAccessPolicyInput { - dataId: Uuid! - dataKind: DataKind! -} - -input DeleteDataAccessPolicyOfInstitutionInput { - dataId: Uuid! - dataKind: DataKind! - institutionId: Uuid! -} - -input DeleteDataAccessPolicyOfOpenIdConnectApplicationInput { - clientId: String! - dataId: Uuid! - dataKind: DataKind! -} - -input DeleteDataAccessPolicyOfUserInput { - dataId: Uuid! - dataKind: DataKind! - userId: Uuid! -} - input DeleteDataInput { dataId: Uuid! dataKind: DataKind! @@ -1902,18 +1853,6 @@ input DeleteGetHttpsResourceInput { getHttpsResourceId: Uuid! } -input DeleteInstitutionAccessPolicyInput { - institutionId: Uuid! -} - -input DeleteOpenIdConnectApplicationAccessPolicyInput { - clientId: String! -} - -input DeleteUserAccessPolicyInput { - userId: Uuid! -} - input DurationPropositionInput { equalTo: Duration @cost(weight: "10") greaterThan: Duration @cost(weight: "10") @@ -2127,6 +2066,20 @@ input InstitutionAccessPolicySortInput { institutionId: SortEnumType @cost(weight: "10") } +input InstitutionManagedInstitutionConnectionInput { + edges: [InstitutionManagedInstitutionEdgeInput!]! + totalCount: NonNegativeInt! +} + +input InstitutionManagedInstitutionEdgeInput { + node: InstitutionManagedInstitutionNodeInput! +} + +input InstitutionManagedInstitutionNodeInput { + name: String! + uuid: Uuid! +} + input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -2507,44 +2460,26 @@ input RootGetHttpsResourceInput { description: String! } -input SetDataAccessLimitForInstitutionInput { - dataId: Uuid! - dataKind: DataKind! - institutionId: Uuid! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput -} - -input SetDataAccessLimitForOpenIdConnectApplicationInput { - clientId: String! - dataId: Uuid! - dataKind: DataKind! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput -} - -input SetDataAccessLimitForUserInput { - dataId: Uuid! - dataKind: DataKind! - upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput - userId: Uuid! -} - input SetGetHttpsResourceParentInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! getHttpsResourceId: Uuid! parentId: Uuid! } -input SetInstitutionAccessLimitInput { +input SetInstitutionAccessPolicyInput { + data: DataReferenceInput institutionId: Uuid! upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput } -input SetOpenIdConnectApplicationAccessLimitInput { +input SetOpenIdConnectApplicationAccessPolicyInput { clientId: String! + data: DataReferenceInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput } -input SetUserAccessLimitInput { +input SetUserAccessPolicyInput { + data: DataReferenceInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationInput userId: Uuid! } @@ -2656,6 +2591,25 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } +input UnsetDataAccessPolicyInput { + data: DataReferenceInput +} + +input UnsetInstitutionAccessPolicyInput { + data: DataReferenceInput + institutionId: Uuid! +} + +input UnsetOpenIdConnectApplicationAccessPolicyInput { + clientId: String! + data: DataReferenceInput +} + +input UnsetUserAccessPolicyInput { + data: DataReferenceInput + userId: Uuid! +} + input UpdateChildGetHttpsResourceInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodInput! archivedFilesMetaInformation: [FileMetaInformationInput!]! @@ -2759,6 +2713,22 @@ input UserAccessPolicySortInput { userId: SortEnumType @cost(weight: "10") } +input UserRepresentedInstitutionConnectionInput { + edges: [UserRepresentedInstitutionEdgeInput!]! + totalCount: NonNegativeInt! +} + +input UserRepresentedInstitutionEdgeInput { + node: UserRepresentedInstitutionNodeInput! + role: InstitutionRepresentativeRole! +} + +input UserRepresentedInstitutionNodeInput { + managedInstitutions: InstitutionManagedInstitutionConnectionInput! + name: String! + uuid: Uuid! +} + input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") @@ -2813,7 +2783,7 @@ enum CalorimetricObserver { TEN_DEGREES } -enum ClearDataAccessPolicyErrorCode { +enum ClearDataAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED @@ -2824,18 +2794,21 @@ enum ClearInstitutionAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum ClearUserAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA_POLICY } enum CoatedSide { @@ -2965,37 +2938,6 @@ enum DataKind { PHOTOVOLTAIC_DATA } -enum DeleteDataAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA -} - -enum DeleteDataAccessPolicyOfInstitutionErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION -} - -enum DeleteDataAccessPolicyOfOpenIdConnectApplicationErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_APPLICATION -} - -enum DeleteDataAccessPolicyOfUserErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_USER -} - enum DeleteDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -3014,32 +2956,16 @@ enum DeleteGetHttpsResourceErrorCode { HAS_CHILDREN } -enum DeleteInstitutionAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - -enum DeleteOpenIdConnectApplicationAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - -enum DeleteUserAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_POLICY -} - enum Illuminant { A D65 } +enum InstitutionRepresentativeRole { + OWNER + ASSISTANT +} + enum JsonValueKind { UNDEFINED OBJECT @@ -3121,30 +3047,6 @@ enum RetractDataErrorCode { NOT_PUBLISHED } -enum SetDataAccessLimitForInstitutionErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_INSTITUTION -} - -enum SetDataAccessLimitForOpenIdConnectApplicationErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_APPLICATION -} - -enum SetDataAccessLimitForUserErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_USER -} - enum SetGetHttpsResourceParentErrorCode { UNKNOWN UNAUTHORIZED @@ -3156,22 +3058,28 @@ enum SetGetHttpsResourceParentErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } -enum SetInstitutionAccessLimitErrorCode { +enum SetInstitutionAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_INSTITUTION } -enum SetOpenIdConnectApplicationAccessLimitErrorCode { +enum SetOpenIdConnectApplicationAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_APPLICATION } -enum SetUserAccessLimitErrorCode { +enum SetUserAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_USER } enum SortEnumType { @@ -3206,6 +3114,38 @@ enum Standardizer { WELL } +enum UnsetDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetInstitutionAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetOpenIdConnectApplicationAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + +enum UnsetUserAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA + UNKNOWN_POLICY +} + enum UpdateChildGetHttpsResourceErrorCode { UNKNOWN UNAUTHORIZED From c5ab4725a63bf0a9d829c11f19a13fc9cf04600a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 14:34:03 +0200 Subject: [PATCH 813/878] Only pull when missing in development --- docker-compose.development.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.development.yaml b/docker-compose.development.yaml index a8f6666b..b4b3ac5f 100644 --- a/docker-compose.development.yaml +++ b/docker-compose.development.yaml @@ -15,7 +15,7 @@ services: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?required}" network_mode: "none" restart: "no" - pull_policy: "always" + pull_policy: "missing" database: # Keep version in sync with the one in in the service `upgrade_postgres` @@ -57,7 +57,7 @@ services: start_interval: "5s" labels: autoheal: "true" - pull_policy: "always" + pull_policy: "missing" backend: build: @@ -241,7 +241,7 @@ services: start_interval: "5s" labels: autoheal: "true" - pull_policy: "always" + pull_policy: "missing" volumes: data: From 2e7015d278b5daf22ef051c0c365ff7baa4c8be2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 14:34:33 +0200 Subject: [PATCH 814/878] Add global data access policy --- backend/src/Migrations/migrate.sql | 2 ++ ...1841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql | 2 ++ 2 files changed, 4 insertions(+) diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 6c03fb2b..5c2bacc2 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -3049,6 +3049,8 @@ BEGIN DELETE FROM database.institution_access_policy; DELETE FROM database.open_id_connect_application_access_policy; + INSERT INTO database.data_access_policy ("Combinator") VALUES ('all'::database.logical_combinator); + INSERT INTO database.data_access_policy ("CalorimetricDataId", "Combinator") SELECT "Id", 'all'::database.logical_combinator FROM database."calorimetric_data"; diff --git a/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql b/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql index 6619fba7..efddc5bb 100644 --- a/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql +++ b/backend/src/Migrations/migrate_from_20260608173427_UnifyAccessPolicies_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql @@ -8,6 +8,8 @@ BEGIN DELETE FROM database.institution_access_policy; DELETE FROM database.open_id_connect_application_access_policy; + INSERT INTO database.data_access_policy ("Combinator") VALUES ('all'::database.logical_combinator); + INSERT INTO database.data_access_policy ("CalorimetricDataId", "Combinator") SELECT "Id", 'all'::database.logical_combinator FROM database."calorimetric_data"; From e29092c30ca28accfca52bf6ca22856a6649b90d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 16:08:17 +0200 Subject: [PATCH 815/878] Authorize user or institution against the metabase in the authentication handler, add authenticated, read, and write policies, use the authenticated policy where sensible, and report authentication errors were possible --- .../Authentication/AuthenticationHandler.cs | 48 ++++++++++++++++++- .../Authorization/AuthorizationPolicies.cs | 8 ++++ .../src/Authorization/CommonAuthorization.cs | 18 +++++++ .../src/Configuration/AuthConfiguration.cs | 36 ++++++++++++++ .../src/Controllers/FileUploadController.cs | 1 - .../AccessPolicies/DataAccessPolicyType.cs | 3 ++ .../AccessPolicies/AccessPolicyQueries.cs | 5 ++ backend/src/GraphQl/DataX/DataQueriesBase.cs | 1 + .../GetHttpsResourceQueries.cs | 18 ++++++- .../Institutions/InstitutionQueries.cs | 2 + backend/src/GraphQl/Users/UserQueries.cs | 29 +++++------ backend/src/Services/AccessPolicyService.cs | 6 +-- 12 files changed, 149 insertions(+), 26 deletions(-) create mode 100644 backend/src/Authorization/AuthorizationPolicies.cs diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index 5b7ff0e6..870f15f5 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Extensions; +using Database.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; @@ -12,7 +13,8 @@ namespace Database.Authentication; public sealed class AuthenticationHandler( - OpenIddictClientService openIddictClientService + OpenIddictClientService openIddictClientService, + UserService userService ) { private static async Task UpdateTokenValuesAsync( @@ -144,6 +146,48 @@ CancellationToken cancellationToken // scheme is configured in // `AuthConfiguration#ConfigureOpenIddictServices` by // `OpenIddictBuilder#AddValidation`. - return await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + // + // First, try to authenticate the possibly-existing bearer token + // through the registered OpenID Connect Client (see `AddClient` in + // `AuthConfiguration`). This only succeeds if the bearer token was + // issued for the registered client (namely + // `appSettings.OpenIdConnectClient.Id`). In particular, this is the + // case if the bearer token comes from the cookie scheme above. + var clientAuthenticateResult = await httpContext.AuthenticateAsync(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + if (clientAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + return clientAuthenticateResult; + } + // Secondly, try to authenticate ignoring which client the token was + // issued to. This is necessary when another application was issued a + // bearer token from the metabase for its own OpenID Connect Client + // (another one than `appSettings.OpenIdConnectClient.Id`) and uses + // this token to authenticate (and authorize) access to this database. + var metabaseAuthenticateResult = await userService.SwitchUserOrInstitutionAsync( + user => Task.FromResult(user is null ? null : CreateSuccessfulAuthenticateResult($"user:{user.Uuid.ToString("D")}")), + institution => Task.FromResult(CreateSuccessfulAuthenticateResult($"institution:{institution.Uuid.ToString("D")}")), + cancellationToken + ); + if (metabaseAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) + { + return metabaseAuthenticateResult; + } + return AuthenticateResult.Fail("Neither authenticated through cookie nor bearer token."); + } + + private static AuthenticateResult CreateSuccessfulAuthenticateResult(string nameIdentifier) + { + return AuthenticateResult.Success( + new AuthenticationTicket( + new ClaimsPrincipal( + new ClaimsIdentity( + claims: [new Claim(ClaimTypes.NameIdentifier, nameIdentifier)], + nameType: ClaimTypes.Name, + roleType: ClaimTypes.Role + ) + ), + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme + ) + ); } } \ No newline at end of file diff --git a/backend/src/Authorization/AuthorizationPolicies.cs b/backend/src/Authorization/AuthorizationPolicies.cs new file mode 100644 index 00000000..df341799 --- /dev/null +++ b/backend/src/Authorization/AuthorizationPolicies.cs @@ -0,0 +1,8 @@ +namespace Database.Authorization; + +public static class AuthorizationPolicies +{ + public const string AuthenticatedPolicy = "Authenticated"; + public const string ReadScopePolicy = "ReadScope"; + public const string WriteScopePolicy = "WriteScope"; +} \ No newline at end of file diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index d012b51e..f01d4352 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -4,6 +4,8 @@ using System.Threading.Tasks; using Database.Services; using Database.ApiRequests; +using HotChocolate.Resolvers; +using HotChocolate; namespace Database.Authorization; @@ -11,6 +13,9 @@ public sealed class CommonAuthorization( UserService userService ) { + // This is the same code that HotChocolate returns when autheorization via the attribute `[Authorize(Policy = ...)]` fails. + private const string UNAUTHORIZED_CODE = "AUTH_NOT_AUTHENTICATED"; + public Task SwitchUserOrInstitutionAsync( Func> handleUser, Func> handleInstitution, @@ -68,4 +73,17 @@ t.Role is QueryCurrentUserOrInstitution.InstitutionRepresentativeRole.ASSISTANT ) ); } + + public void ReportUnauthorizedError( + IResolverContext resolverContext + ) + { + resolverContext.ReportError( + ErrorBuilder.New() + .SetCode(UNAUTHORIZED_CODE) + .SetPath(resolverContext.Path) + .SetMessage($"The current user is not authorized to access this resource.") + .Build() + ); + } } \ No newline at end of file diff --git a/backend/src/Configuration/AuthConfiguration.cs b/backend/src/Configuration/AuthConfiguration.cs index 6a7e65ad..c0f0901c 100644 --- a/backend/src/Configuration/AuthConfiguration.cs +++ b/backend/src/Configuration/AuthConfiguration.cs @@ -21,6 +21,12 @@ public static class AuthConfiguration { private static readonly TimeSpan s_cookieExpirationTimeSpan = TimeSpan.FromDays(1); + private static readonly Dictionary s_policyNameToOpenIdConnectScope = new() + { + { AuthorizationPolicies.ReadScopePolicy, OpenIdConnectScope.ReadApiScope }, + { AuthorizationPolicies.WriteScopePolicy, OpenIdConnectScope.WriteApiScope }, + }; + private static void BootstrapCertificates(IClock clock) { using var store = new X509Store(OpenIdConnectConstants.CertificateStoreName, OpenIdConnectConstants.CertificateStoreLocation); @@ -154,6 +160,36 @@ IServiceCollection services AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme, _ => { } ); + services.AddAuthorization(_ => + { + _.AddPolicy(AuthorizationPolicies.AuthenticatedPolicy, policy => + { + policy.AuthenticationSchemes = + [ + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme + ]; + policy.RequireAuthenticatedUser(); + } + ); + foreach (var (policyName, scope) in s_policyNameToOpenIdConnectScope) + { + _.AddPolicy(policyName, policy => + { + policy.AuthenticationSchemes = + [ + AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme + ]; + policy.RequireAuthenticatedUser(); + policy.RequireAssertion(context => + { + return context.User.HasScope(scope); + } + ); + } + ); + } + } + ); } private static void ConfigureTaskScheduling( diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/FileUploadController.cs index aab9c1af..1f06e473 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/FileUploadController.cs @@ -94,7 +94,6 @@ protected override void Dispose(bool disposing) [DisableRequestSizeLimit] // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] - [AllowAnonymous] [Consumes(MediaTypeNames.Multipart.FormData)] [AcceptsMultipartFormFile] // see `AcceptsMultipartFormFileAttribute` below [Produces(MediaTypeNames.Application.Json)] diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs b/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs index 838b80e7..ef6083fb 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs @@ -51,6 +51,7 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return Array.Empty(); } return await byId @@ -70,6 +71,7 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return Array.Empty(); } return await byId @@ -89,6 +91,7 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return Array.Empty(); } return await byId diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs index 4fb01360..4d4ce1bb 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -5,6 +5,8 @@ using Database.Data; using Database.Data.AccessPolicies; using GreenDonut.Data; +using HotChocolate.Authorization; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -13,14 +15,17 @@ namespace Database.GraphQl.AccessPolicies; [ExtendObjectType(nameof(Query))] public sealed class AccessPolicyQueries { + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task GetGlobalDataAccessPolicyAsync( CommonAuthorization authorization, ApplicationDbContext databaseContext, + IResolverContext resolverContext, CancellationToken cancellationToken ) { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return null; } return await databaseContext.DataAccessPolicies.AsNoTracking() diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index b4e7be79..3ffd2967 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -56,6 +56,7 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return await Enumerable.Empty() .AsQueryable() .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 64341bcf..42d0bf24 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -7,6 +7,7 @@ using Database.Enumerations; using Database.GraphQl.Extensions; using GreenDonut.Data; +using HotChocolate.Authorization; using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -41,6 +42,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async ValueTask> GetPendingGetHttpsResources( ApplicationDbContext context, CommonAuthorization authorization, @@ -66,12 +68,24 @@ CancellationToken cancellationToken .ToConnectionAsync(); } - public Task GetGetHttpsResourceAsync( + public async Task GetGetHttpsResourceAsync( Guid id, IGetHttpsResourceByIdDataLoader byId, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return byId.LoadAsync(id, cancellationToken); + if (await authorization.IsDatabaseOperator(cancellationToken)) + { + return await byId.LoadAsync(id, cancellationToken); + } + return await byId + .Where(_ => _.CalorimetricData == null || _.CalorimetricData.PublishingState != PublishingState.PENDING) + .Where(_ => _.GeometricData == null || _.GeometricData.PublishingState != PublishingState.PENDING) + .Where(_ => _.HygrothermalData == null || _.HygrothermalData.PublishingState != PublishingState.PENDING) + .Where(_ => _.LifeCycleData == null || _.LifeCycleData.PublishingState != PublishingState.PENDING) + .Where(_ => _.OpticalData == null || _.OpticalData.PublishingState != PublishingState.PENDING) + .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING) + .LoadAsync(id, cancellationToken); } } \ No newline at end of file diff --git a/backend/src/GraphQl/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs index e81852dc..411adfe6 100644 --- a/backend/src/GraphQl/Institutions/InstitutionQueries.cs +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.cs @@ -3,12 +3,14 @@ using HotChocolate.Types; using Database.Authorization; using Database.ApiRequests; +using HotChocolate.Authorization; namespace Database.GraphQl.Institutions; [ExtendObjectType(nameof(Query))] public sealed class InstitutionQueries { + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public Task GetCurrentInstitutionAsync( CommonAuthorization authorization, CancellationToken cancellationToken diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 240341b9..d6b839b3 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -1,13 +1,10 @@ -using System.Linq; -using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Data; +using Database.Authorization; +using HotChocolate.Authorization; using HotChocolate.Resolvers; using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; -using OpenIddict.Abstractions; using static Database.ApiRequests.GetUserInfo; namespace Database.GraphQl.Users; @@ -15,24 +12,20 @@ namespace Database.GraphQl.Users; [ExtendObjectType(nameof(Query))] public sealed class UserQueries { - public async Task GetCurrentUserAsync( - ClaimsPrincipal claimsPrincipal, - ApplicationDbContext context, + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentUserAsync( + CommonAuthorization authorization, CancellationToken cancellationToken ) { - if (!claimsPrincipal.HasClaim(OpenIddictConstants.Claims.Subject)) - { - return null; - } - return - await context.Users.AsNoTracking() - .SingleOrDefaultAsync( - u => claimsPrincipal.GetClaims(OpenIddictConstants.Claims.Subject).Contains(u.Subject), - cancellationToken - ); + return authorization.SwitchUserOrInstitutionAsync( + user => Task.FromResult(user), + institution => Task.FromResult(null), + cancellationToken + ); } + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public Task GetCurrentUserInfoAsync( GetUserInfo getUserInfo, IResolverContext resolverContext, diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index 50cefc1a..0b7f318f 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -34,8 +34,8 @@ CancellationToken cancellationToken { institutionIds = [currentInstitution.Uuid]; } - var filteredData = FilterData(data, currentUser, institutionIds, openIdConnectClientId, databaseContext); - var (postProcessedData, result) = await then(filteredData); + var policedData = PoliceData(data, currentUser, institutionIds, openIdConnectClientId, databaseContext); + var (postProcessedData, result) = await then(policedData); await IncrementAccessCounts( postProcessedData, openIdConnectClientId, @@ -47,7 +47,7 @@ await IncrementAccessCounts( return result; } - private static IQueryable FilterData( + private static IQueryable PoliceData( IQueryable data, QueryCurrentUserOrInstitution.CurrentUser? currentUser, IReadOnlyList? institutionIds, From d409093422b155c643c44031513102cf0ee5dc5e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 18:11:17 +0200 Subject: [PATCH 816/878] Make sure that root resource cannot be deleted without the corresponding data in the same transaction and add triggers that assert that a root resource and a data access policy is always created alongside a new data entry --- backend/src/Data/ApplicationDbContext.cs | 243 +++++++++++++++++++++-- backend/src/Data/CalorimetricData.cs | 13 ++ backend/src/Data/DataX.cs | 6 + backend/src/Data/GeometricData.cs | 13 ++ backend/src/Data/GetHttpsResource.cs | 18 +- backend/src/Data/HygrothermalData.cs | 13 ++ backend/src/Data/LifeCycleData.cs | 13 ++ backend/src/Data/OpticalData.cs | 13 ++ backend/src/Data/PhotovoltaicData.cs | 13 ++ 9 files changed, 319 insertions(+), 26 deletions(-) diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 15f1c4f3..1681cde9 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -341,7 +341,7 @@ EntityTypeBuilder builder AND ( SELECT COUNT({nameof(GetHttpsResource.Id).Enquote()}) FROM {_schemaName}.{GetHttpsResource.TableName.Enquote()} - WHERE {nameof(GetHttpsResource.Id).Enquote()} = NEW.{nameof(GetHttpsResource.ParentId).Enquote()} AND COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) + WHERE {nameof(GetHttpsResource.Id).Enquote()} = NEW.{nameof(GetHttpsResource.ParentId).Enquote()} AND COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => _.Field.Enquote()))}) = COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => $"NEW.{_.Field.Enquote()}"))}) ) <> 1 THEN @@ -357,7 +357,7 @@ SELECT COUNT({nameof(GetHttpsResource.Id).Enquote()}) .Action(action => action .ExecuteRawSql( $""" - IF COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"OLD.{_.Enquote()}"))}) <> COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"NEW.{_.Enquote()}"))}) + IF COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => $"OLD.{_.Field.Enquote()}"))}) <> COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => $"NEW.{_.Field.Enquote()}"))}) THEN RAISE EXCEPTION 'You cannot change the data ID of a resource.'; END IF; @@ -365,10 +365,103 @@ IF COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => $"O ) ) ); + builder + .BeforeDelete(trigger => trigger + .SetTriggerName(GetHttpsResource.RootCanOnlyBeDeletedAlongsideItsDataTriggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF OLD.{nameof(GetHttpsResource.ParentId).Enquote()} IS NULL AND ( + {string.Join(" OR ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => $""" + (OLD.{_.Field.Enquote()} IS NOT NULL AND EXISTS ( + SELECT 1 FROM {_schemaName}.{_.Table.Enquote()} + WHERE {nameof(IData.Id).Enquote()} = OLD.{_.Field.Enquote()} + )) + """ + ))} + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + """ + ) + ) + ); + return builder; + } + + private + EntityTypeBuilder + AddTriggerThatAssertsExistenceOfRootResource( + EntityTypeBuilder builder, + string triggerName, + string dataIdColumn + ) + where TData : class, IData + { + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...` . + builder + .AfterInsert(trigger => trigger + .SetTriggerName(triggerName) + .Action(action => action + .ExecuteRawSql( + $""" + IF NOT EXISTS ( + SELECT 1 FROM {_schemaName}.{GetHttpsResource.TableName.Enquote()} + WHERE {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL + AND {dataIdColumn.Enquote()} = NEW.{nameof(IData.Id)} + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + """ + ) + ) + ); return builder; } private static + EntityTypeBuilder + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + EntityTypeBuilder builder, + string triggerName, + string dataIdColumn + ) + where TData : class, IData + { + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...` . + builder + .AfterInsert(trigger => trigger + .SetTriggerName(triggerName) + .Action(action => action + .ExecuteRawSql( + $""" + INSERT INTO {DataAccessPolicy.TableName.Enquote()} + ({dataIdColumn.Enquote()}, {nameof(DataAccessPolicy.Combinator).Enquote()}) + VALUES (NEW.{nameof(IEntity.Id).Enquote()}, '{LogicalCombinator.ALL.ToString().ToLowerInvariant()}') + ON CONFLICT ({dataIdColumn.Enquote()}) DO NOTHING; + RETURN NEW; + """ + ) + // an alternative is + // .InsertIfNotExists( + // insertedData => new DataAccessPolicy + // { + // CalorimetricDataId = insertedData.Id, + // Combinator = LogicalCombinator.ALL + // }, + // data => new { data.CalorimetricDataId } + // ) + ) + ); + return builder; + } + + private EntityTypeBuilder ConfigureCalorimetricData( EntityTypeBuilder builder @@ -384,10 +477,20 @@ EntityTypeBuilder builder .WithOne(_ => _.CalorimetricData) .HasForeignKey(_ => _.CalorimetricDataId) .OnDelete(DeleteBehavior.Cascade); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.CalorimetricData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.CalorimetricDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.CalorimetricData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.CalorimetricDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigureGeometricData( EntityTypeBuilder builder @@ -403,10 +506,20 @@ EntityTypeBuilder builder .WithOne(_ => _.GeometricData) .HasForeignKey(_ => _.GeometricDataId) .OnDelete(DeleteBehavior.Cascade); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.GeometricData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.GeometricDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.GeometricData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.GeometricDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigureHygrothermalData( EntityTypeBuilder builder @@ -422,10 +535,20 @@ EntityTypeBuilder builder .WithOne(_ => _.HygrothermalData) .HasForeignKey(_ => _.HygrothermalDataId) .OnDelete(DeleteBehavior.Cascade); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.HygrothermalData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.HygrothermalDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.HygrothermalData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.HygrothermalDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigureLifeCycleData( EntityTypeBuilder builder @@ -441,10 +564,20 @@ EntityTypeBuilder builder .WithOne(_ => _.LifeCycleData) .HasForeignKey(_ => _.LifeCycleDataId) .OnDelete(DeleteBehavior.Cascade); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.LifeCycleData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.LifeCycleDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.LifeCycleData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.LifeCycleDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigureOpticalData( EntityTypeBuilder builder @@ -471,10 +604,20 @@ EntityTypeBuilder builder ); } ); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.OpticalData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.OpticalDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.OpticalData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.OpticalDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigurePhotovoltaicData( EntityTypeBuilder builder @@ -490,10 +633,20 @@ EntityTypeBuilder builder .WithOne(_ => _.PhotovoltaicData) .HasForeignKey(_ => _.PhotovoltaicDataId) .OnDelete(DeleteBehavior.Cascade); + AddTriggerThatAssertsExistenceOfRootResource( + builder, + global::Database.Data.PhotovoltaicData.AssertExistenceOfRootResourceTriggerName, + nameof(GetHttpsResource.PhotovoltaicDataId) + ); + AddTriggerThatCreatesDataAccessPolicyIfNecessary( + builder, + global::Database.Data.PhotovoltaicData.CreateDataAccessPolicyIfNecessaryTriggerName, + nameof(DataAccessPolicy.PhotovoltaicDataId) + ); return builder; } - private static + private EntityTypeBuilder ConfigureDataAccessPolicy( EntityTypeBuilder builder @@ -614,13 +767,13 @@ IF COALESCE({string.Join(", ", DataAccessPolicy.DataIdFieldAndDataTableNames.Sel IF ( {string.Join(" OR ", DataAccessPolicy.DataIdFieldAndDataTableNames.Select(_ => $""" (OLD.{_.Field.Enquote()} IS NOT NULL AND EXISTS ( - SELECT 1 FROM database.{_.Table.Enquote()} - WHERE "Id" = OLD.{_.Field.Enquote()} + SELECT 1 FROM {_schemaName}.{_.Table.Enquote()} + WHERE {nameof(IData.Id).Enquote()} = OLD.{_.Field.Enquote()} )) """ ))} ) THEN - RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.'; + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; END IF; """ ) @@ -685,7 +838,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ); _.HasCheckConstraint( $"CK_{nameof(GetHttpsResource)}_Exactly_One_Data_Set", - $"NUM_NONNULLS({string.Join(", ", GetHttpsResource.DataIdFieldNames.Select(_ => _.Enquote()))}) = 1" + $"NUM_NONNULLS({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Select(_ => _.Field.Enquote()))}) = 1" ); foreach (var triggerName in GetHttpsResource.TriggerNames) { @@ -697,32 +850,86 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.CalorimetricData.TableName); + ).ToTable( + global::Database.Data.CalorimetricData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.CalorimetricData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigureGeometricData( ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.GeometricData.TableName); + ).ToTable( + global::Database.Data.GeometricData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.GeometricData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigureHygrothermalData( ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.HygrothermalData.TableName); + ).ToTable( + global::Database.Data.HygrothermalData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.HygrothermalData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigureLifeCycleData( ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.LifeCycleData.TableName); + ).ToTable( + global::Database.Data.LifeCycleData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.LifeCycleData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigureOpticalData( ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.OpticalData.TableName); + ).ToTable( + global::Database.Data.OpticalData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.OpticalData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigurePhotovoltaicData( ConfigureData( modelBuilder.Entity() ) - ).ToTable(global::Database.Data.PhotovoltaicData.TableName); + ).ToTable( + global::Database.Data.PhotovoltaicData.TableName, + _ => + { + foreach (var triggerName in global::Database.Data.PhotovoltaicData.TriggerNames) + { + _.HasTrigger(triggerName); + } + } + ); ConfigureDataAccessPolicy( modelBuilder.Entity() ).ToTable( diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index ede597e1..bfb4f1b1 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; +using Database.Enumerations; using Database.Extractors; namespace Database.Data; @@ -12,6 +14,17 @@ public sealed class CalorimetricData { public const string TableName = "calorimetric_data"; + public static readonly DataKind Kind = DataKind.CALORIMETRIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public CalorimetricData( Guid? userId, string locale, diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index b2e37990..ee8695de 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -8,6 +8,12 @@ namespace Database.Data; public abstract class DataX : AuditableEntity, IData { + protected static string AssertExistenceOfRootResourceTriggerName(DataKind kind) => + $"{kind.ToString().ToLowerInvariant()}_assert_existence_of_root_resource"; + + protected static string CreateDataAccessPolicyIfNecessaryTriggerName(DataKind kind) => + $"create_{kind.ToString().ToLowerInvariant()}_access_policy_if_necessary"; + public DataX( Guid? userId, string locale, diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index 02761f66..bcc6494a 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; +using Database.Enumerations; using Database.Extractors; namespace Database.Data; @@ -12,6 +14,17 @@ public sealed class GeometricData { public const string TableName = "geometric_data"; + public static readonly DataKind Kind = DataKind.GEOMETRIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public GeometricData( Guid? userId, string locale, diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 50f71ae9..b5580b7e 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -21,18 +21,20 @@ public sealed class GetHttpsResource public const string DataIdsMustMatchTriggerName = $"{TableName}_data_ids_must_match"; public const string DataIdCannotChangeTriggerName = $"{TableName}_data_id_cannot_change"; + public const string RootCanOnlyBeDeletedAlongsideItsDataTriggerName = $"{TableName}_root_can_only_be_deleted_alongside_its_data"; public static readonly ImmutableArray TriggerNames = [ DataIdsMustMatchTriggerName, - DataIdCannotChangeTriggerName + DataIdCannotChangeTriggerName, + RootCanOnlyBeDeletedAlongsideItsDataTriggerName ]; - public static readonly ImmutableArray DataIdFieldNames = [ - nameof(CalorimetricDataId), - nameof(GeometricDataId), - nameof(HygrothermalDataId), - nameof(LifeCycleDataId), - nameof(OpticalDataId), - nameof(PhotovoltaicDataId) + public static readonly ImmutableArray<(string Field, string Table)> DataIdFieldAndDataTableNames = [ + (nameof(CalorimetricDataId), CalorimetricData.TableName), + (nameof(GeometricDataId), GeometricData.TableName), + (nameof(HygrothermalDataId), HygrothermalData.TableName), + (nameof(LifeCycleDataId), LifeCycleData.TableName), + (nameof(OpticalDataId), OpticalData.TableName), + (nameof(PhotovoltaicDataId), PhotovoltaicData.TableName) ]; // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index d04236e6..82534a22 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; +using Database.Enumerations; namespace Database.Data; @@ -11,6 +13,17 @@ public sealed class HygrothermalData { public const string TableName = "hygrothermal_data"; + public static readonly DataKind Kind = DataKind.HYGROTHERMAL_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public HygrothermalData( Guid? userId, string locale, diff --git a/backend/src/Data/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs index 4eb656cb..2fd204d5 100644 --- a/backend/src/Data/LifeCycleData.cs +++ b/backend/src/Data/LifeCycleData.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; +using Database.Enumerations; namespace Database.Data; @@ -11,6 +13,17 @@ public sealed class LifeCycleData { public const string TableName = "lifeCycle_data"; + public static readonly DataKind Kind = DataKind.LIFE_CYCLE_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public LifeCycleData( Guid? userId, string locale, diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index d0a6d72d..d2d1c354 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Enumerations; using Database.Enumerations.DataPoints; using Database.Extractors; using Database.Data.AccessPolicies; +using Database.Enumerations; namespace Database.Data; @@ -14,6 +16,17 @@ public sealed class OpticalData { public const string TableName = "optical_data"; + public static readonly DataKind Kind = DataKind.OPTICAL_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public OpticalData( Guid? userId, diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index af8bb426..e69a14f6 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; +using Database.Enumerations; namespace Database.Data; @@ -11,6 +13,17 @@ public sealed class PhotovoltaicData { public const string TableName = "photovoltaic_data"; + public static readonly DataKind Kind = DataKind.PHOTOVOLTAIC_DATA; + + public new static readonly string AssertExistenceOfRootResourceTriggerName = + DataX.AssertExistenceOfRootResourceTriggerName(Kind); + public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + public static readonly ImmutableArray TriggerNames = [ + AssertExistenceOfRootResourceTriggerName, + CreateDataAccessPolicyIfNecessaryTriggerName + ]; + public PhotovoltaicData( Guid? userId, string locale, From 8ea2901b8a4c8eb92198625c8e8842531152d9db Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 20:05:31 +0200 Subject: [PATCH 817/878] Use the same path to upload and download resources (RESTful API) and have .NET Core handle download to limit access according to policies --- ...ller.cs => GetHttpsResourcesController.cs} | 154 ++++++++++++++---- backend/src/Data/CalorimetricData.cs | 9 +- backend/src/Data/DataX.cs | 4 + backend/src/Data/GeometricData.cs | 9 +- backend/src/Data/GetHttpsResource.cs | 47 ++++-- backend/src/Data/HygrothermalData.cs | 9 +- backend/src/Data/IData.cs | 4 + backend/src/Data/LifeCycleData.cs | 9 +- backend/src/Data/OpticalData.cs | 10 +- backend/src/Data/PhotovoltaicData.cs | 9 +- backend/src/Program.cs | 9 +- nginx/templates/default.conf.template | 34 ++-- 12 files changed, 228 insertions(+), 79 deletions(-) rename backend/src/Controllers/{FileUploadController.cs => GetHttpsResourcesController.cs} (62%) diff --git a/backend/src/Controllers/FileUploadController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs similarity index 62% rename from backend/src/Controllers/FileUploadController.cs rename to backend/src/Controllers/GetHttpsResourcesController.cs index 1f06e473..1aa578cf 100644 --- a/backend/src/Controllers/FileUploadController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Database.ApiRequests; using Database.Authentication; using Database.Authorization; using Database.Data; @@ -33,7 +34,7 @@ public static partial class Log Level = LogLevel.Information, Message = "Uploaded file '{FileNameForDisplay}' to '{FilePath}'.")] public static partial void SavedUploadedFile( - this ILogger logger, + this ILogger logger, string? fileNameForDisplay, string filePath ); @@ -42,7 +43,7 @@ string filePath Level = LogLevel.Error, Message = "Failed to create response approval for data with ID {DataId}.")] public static partial void FailedToCreateResponseApproval( - this ILogger logger, + this ILogger logger, Guid dataId, Exception exception ); @@ -51,8 +52,8 @@ Exception exception // Inspired by https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-5.0#upload-large-files-with-streaming // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ [ApiController] -public sealed class FileUploadController( - ILogger logger +public sealed class GetHttpsResourcesController( + ILogger logger ) : Controller { // Get the default form options so that we can use them to set the default @@ -72,36 +73,114 @@ protected override void Dispose(bool disposing) } // Disposable types implement a finalizer. - ~FileUploadController() + ~GetHttpsResourcesController() { Dispose(false); } - // The following upload methods: - // - // 1. Disable the form value model binding to take control of handling - // potentially large files. - // - // 2. Typically, antiforgery tokens are sent in request body. Since we - // don't want to read the request body early, the tokens are sent via - // headers. The antiforgery token filter first looks for tokens in the - // request header and then falls back to reading the body. + [HttpGet("~/api/resources/{id:guid}")] + [HttpGet("~/api/resources/{id:guid}.{extension}")] + [EndpointName("GetResourceById")] + [EndpointDescription("Get an HTTP resource")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Get( + [FromRoute] Guid id, + [FromServices] ApplicationDbContext context, + [FromServices] AccessPolicyService accessPolicyService, + [FromServices] IDataFormatByIdDataLoader dataFormatByIdDataLoader, + CancellationToken cancellationToken, + [FromRoute] string? extension = null + ) + { + var getHttpsResource = await context.GetHttpsResourcesWithData.AsQueryable() + .Where(_ => _.Id == id) + .SingleOrDefaultAsync(cancellationToken); + if (getHttpsResource is null) + { + return Problem( + title: "Resource Not Found", + detail: $"There is no GET HTTPS resource with ID `{id:D}`.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path + ); + } + if (extension is not null && extension != getHttpsResource.FileExtension) + { + return Problem( + title: "Extension Not Found", + detail: $"There is no GET HTTPS resource with ID `{id:D}` and the extension `{extension}`. The resource's extension is `{getHttpsResource.FileExtension}`.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path + ); + } + if (!getHttpsResource.DoesFileExist()) + { + return Problem( + title: "Content Not Found", + detail: $"The GET HTTPS resource with ID `{id:D}` and the extension `{getHttpsResource.FileExtension}` does not have content.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path + ); + } + if (getHttpsResource.Data is null) + { + ModelState.AddModelError( + nameof(id), + $"There is no data set associated with the GET HTTPS resource with ID `{id:D}`." + ); + return BadRequest(ModelState); + } + if (!await accessPolicyService.Apply( + context.Data(getHttpsResource.Data.Kind).AsNoTracking() + .Where(_ => _.Id == getHttpsResource.DataId), + async policedData => + { + var node = await policedData.SingleOrDefaultAsync(cancellationToken); + return node is null + ? ([], false) + : ([node], true); + }, + context, + cancellationToken + )) + { + return Unauthorized(); + } + var dataFormat = await dataFormatByIdDataLoader.LoadAsync(getHttpsResource.DataFormatId); + if (dataFormat is null) + { + ModelState.AddModelError( + nameof(id), + $"Failed to fetch the content type of the GET HTTPS resource with ID `{id:D}`. Please try again in a few minutes." + ); + return BadRequest(ModelState); + } + return PhysicalFile( + physicalPath: getHttpsResource.AbsoluteFilePath, + contentType: dataFormat.MediaType, + fileDownloadName: getHttpsResource.FileName, + enableRangeProcessing: true + ); + } - [HttpPost("~/api/upload-file")] - [EndpointName("UploadFile")] - [EndpointDescription("Upload file for GET HTTP resource")] + [HttpPost("~/api/resources/{id:guid}")] + [EndpointName("UploadResource")] + [EndpointDescription("Upload file for a GET HTTP resource")] [DisableFormValueModelBinding] [DisableRequestSizeLimit] - // TODO Add this `[RequireAntiforgeryToken]` once we know where to set the generation token cookie! [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [Consumes(MediaTypeNames.Multipart.FormData)] [AcceptsMultipartFormFile] // see `AcceptsMultipartFormFileAttribute` below [Produces(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status201Created)] - [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task UploadFile( - [FromQuery] Guid getHttpsResourceUuid, + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task Upload( + [FromRoute] Guid id, [FromServices] ApplicationDbContext context, [FromServices] ResponseApprovalService responseApprovalService, [FromServices] CommonAuthorization authorization, @@ -115,27 +194,28 @@ CancellationToken cancellationToken if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { ModelState.AddModelError( - "File", + "file", "The request content is not multipart." ); return BadRequest(ModelState); } var getHttpsResource = await context.GetHttpsResourcesWithData.AsQueryable() - .Where(_ => _.Id == getHttpsResourceUuid) - .SingleOrDefaultAsync(cancellationToken); + .Where(_ => _.Id == id) + .SingleOrDefaultAsync(cancellationToken); if (getHttpsResource is null) { - ModelState.AddModelError( - "GetHttpsResourceUuid", - $"There is no GET HTTPS resource with UUID {getHttpsResourceUuid:D}." + return Problem( + title: "Not Found", + detail: $"There is no GET HTTPS resource with ID `{id:D}`.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path ); - return BadRequest(ModelState); } if (getHttpsResource.Data is null) { ModelState.AddModelError( - "GetHttpsResourceUuid", - $"There is no data set associated with the GET HTTPS resource with UUID {getHttpsResourceUuid:D}." + nameof(id), + $"There is no data set associated with the GET HTTPS resource with ID `{id:D}`." ); return BadRequest(ModelState); } @@ -161,7 +241,7 @@ CancellationToken cancellationToken ) ) { - ModelState.AddModelError("File", "There is no content-disposition header."); + ModelState.AddModelError("file", "There is no content-disposition header."); return BadRequest(ModelState); } var streamedFileContent = await FileHelpers.ProcessStreamedFile( @@ -208,7 +288,14 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( // TODO How can this be reported to the user? logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, exception); } - return Created(nameof(FileUploadController), null); + return CreatedAtAction( + nameof(Get), + new + { + id = getHttpsResource.Id, + extension = getHttpsResource.FileExtension + } + ); } private static Encoding GetEncoding(MultipartSection section) @@ -235,7 +322,8 @@ private static Encoding GetEncoding(MultipartSection section) private sealed record FormFile(IFormFile File); // solely for the OpenAPI documentation - private sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider + [AttributeUsage(AttributeTargets.Method)] + internal sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider { public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) { diff --git a/backend/src/Data/CalorimetricData.cs b/backend/src/Data/CalorimetricData.cs index bfb4f1b1..e7927efb 100644 --- a/backend/src/Data/CalorimetricData.cs +++ b/backend/src/Data/CalorimetricData.cs @@ -14,12 +14,12 @@ public sealed class CalorimetricData { public const string TableName = "calorimetric_data"; - public static readonly DataKind Kind = DataKind.CALORIMETRIC_DATA; + public static readonly DataKind TheKind = DataKind.CALORIMETRIC_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -80,6 +80,9 @@ double[] uValues UValues = uValues; } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.CalorimetricData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Data/DataX.cs b/backend/src/Data/DataX.cs index ee8695de..6ffe4ea9 100644 --- a/backend/src/Data/DataX.cs +++ b/backend/src/Data/DataX.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Database.Data.AccessPolicies; using Database.Enumerations; @@ -109,6 +110,9 @@ public void Retract() public abstract DataAccessPolicy? AccessPolicy { get; set; } + [NotMapped] + public abstract DataKind Kind { get; } + public PublishingState PublishingState { get; private set; } = PublishingState.PENDING; public abstract Task ExtractAndSetValuesFromFile( diff --git a/backend/src/Data/GeometricData.cs b/backend/src/Data/GeometricData.cs index bcc6494a..e50be906 100644 --- a/backend/src/Data/GeometricData.cs +++ b/backend/src/Data/GeometricData.cs @@ -14,12 +14,12 @@ public sealed class GeometricData { public const string TableName = "geometric_data"; - public static readonly DataKind Kind = DataKind.GEOMETRIC_DATA; + public static readonly DataKind TheKind = DataKind.GEOMETRIC_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -83,6 +83,9 @@ double[] thicknesses Thicknesses = thicknesses; } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.GeometricData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index b5580b7e..cc7b37bf 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Database.Enumerations; using Database.Extensions; using Database.Utilities; using EntityFrameworkCore.Projectables; @@ -145,6 +144,9 @@ private void AssertThatExactlyOneDataIdIsNonNull() public string FilePath => Path.Combine(FilesDirectoryPath, FileName); + public string AbsoluteFilePath => + Path.GetFullPath(FilePath); + public ICollection ArchivedFilesMetaInformation { get; private set; } = []; // Note that at least one data ID is always present. So `Guid.Empty` will never be used. @@ -155,29 +157,44 @@ private void AssertThatExactlyOneDataIdIsNonNull() [NotMapped] public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; + [NotMapped] + public Enumerations.DataKind? DataKind + { + get + { + if (CalorimetricDataId is not null) return Enumerations.DataKind.CALORIMETRIC_DATA; + if (GeometricDataId is not null) return Enumerations.DataKind.GEOMETRIC_DATA; + if (HygrothermalDataId is not null) return Enumerations.DataKind.HYGROTHERMAL_DATA; + if (LifeCycleDataId is not null) return Enumerations.DataKind.LIFE_CYCLE_DATA; + if (OpticalDataId is not null) return Enumerations.DataKind.OPTICAL_DATA; + if (PhotovoltaicDataId is not null) return Enumerations.DataKind.PHOTOVOLTAIC_DATA; + return null; + } + } + [Projectable] - public Guid? GetDataId(DataKind dataKind) => + public Guid? GetDataId(Enumerations.DataKind dataKind) => dataKind switch { - DataKind.CALORIMETRIC_DATA => CalorimetricDataId, - DataKind.GEOMETRIC_DATA => GeometricDataId, - DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, - DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, - DataKind.OPTICAL_DATA => OpticalDataId, - DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, + Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricDataId, + Enumerations.DataKind.GEOMETRIC_DATA => GeometricDataId, + Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, + Enumerations.DataKind.OPTICAL_DATA => OpticalDataId, + Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; [Projectable] - public IData? GetData(DataKind dataKind) => + public IData? GetData(Enumerations.DataKind dataKind) => dataKind switch { - DataKind.CALORIMETRIC_DATA => CalorimetricData, - DataKind.GEOMETRIC_DATA => GeometricData, - DataKind.HYGROTHERMAL_DATA => HygrothermalData, - DataKind.LIFE_CYCLE_DATA => LifeCycleData, - DataKind.OPTICAL_DATA => OpticalData, - DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricData, + Enumerations.DataKind.GEOMETRIC_DATA => GeometricData, + Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalData, + Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleData, + Enumerations.DataKind.OPTICAL_DATA => OpticalData, + Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; diff --git a/backend/src/Data/HygrothermalData.cs b/backend/src/Data/HygrothermalData.cs index 82534a22..8ac18146 100644 --- a/backend/src/Data/HygrothermalData.cs +++ b/backend/src/Data/HygrothermalData.cs @@ -13,12 +13,12 @@ public sealed class HygrothermalData { public const string TableName = "hygrothermal_data"; - public static readonly DataKind Kind = DataKind.HYGROTHERMAL_DATA; + public static readonly DataKind TheKind = DataKind.HYGROTHERMAL_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -71,6 +71,9 @@ DateTimeOffset createdAt { } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.HygrothermalData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Data/IData.cs b/backend/src/Data/IData.cs index eaed8a69..a343394c 100644 --- a/backend/src/Data/IData.cs +++ b/backend/src/Data/IData.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json.Serialization; using System.Threading.Tasks; using Database.Data.AccessPolicies; @@ -33,6 +34,9 @@ public interface IData : IEntity, IAuditable DataAccessPolicy? AccessPolicy { get; set; } PublishingState PublishingState { get; } + [NotMapped] + DataKind Kind { get; } + void Update( string locale, Guid componentId, diff --git a/backend/src/Data/LifeCycleData.cs b/backend/src/Data/LifeCycleData.cs index 2fd204d5..38cbc903 100644 --- a/backend/src/Data/LifeCycleData.cs +++ b/backend/src/Data/LifeCycleData.cs @@ -13,12 +13,12 @@ public sealed class LifeCycleData { public const string TableName = "lifeCycle_data"; - public static readonly DataKind Kind = DataKind.LIFE_CYCLE_DATA; + public static readonly DataKind TheKind = DataKind.LIFE_CYCLE_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -71,6 +71,9 @@ DateTimeOffset createdAt { } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.LifeCycleData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index d2d1c354..32d7f713 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -7,7 +7,6 @@ using Database.Enumerations.DataPoints; using Database.Extractors; using Database.Data.AccessPolicies; -using Database.Enumerations; namespace Database.Data; @@ -16,12 +15,12 @@ public sealed class OpticalData { public const string TableName = "optical_data"; - public static readonly DataKind Kind = DataKind.OPTICAL_DATA; + public static readonly DataKind TheKind = DataKind.OPTICAL_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -113,6 +112,9 @@ double[] colorRenderingIndices ColorRenderingIndices = colorRenderingIndices; } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.OpticalData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Data/PhotovoltaicData.cs b/backend/src/Data/PhotovoltaicData.cs index e69a14f6..fb987981 100644 --- a/backend/src/Data/PhotovoltaicData.cs +++ b/backend/src/Data/PhotovoltaicData.cs @@ -13,12 +13,12 @@ public sealed class PhotovoltaicData { public const string TableName = "photovoltaic_data"; - public static readonly DataKind Kind = DataKind.PHOTOVOLTAIC_DATA; + public static readonly DataKind TheKind = DataKind.PHOTOVOLTAIC_DATA; public new static readonly string AssertExistenceOfRootResourceTriggerName = - DataX.AssertExistenceOfRootResourceTriggerName(Kind); + DataX.AssertExistenceOfRootResourceTriggerName(TheKind); public new static readonly string CreateDataAccessPolicyIfNecessaryTriggerName = - DataX.CreateDataAccessPolicyIfNecessaryTriggerName(Kind); + DataX.CreateDataAccessPolicyIfNecessaryTriggerName(TheKind); public static readonly ImmutableArray TriggerNames = [ AssertExistenceOfRootResourceTriggerName, CreateDataAccessPolicyIfNecessaryTriggerName @@ -71,6 +71,9 @@ DateTimeOffset createdAt { } + [NotMapped] + public override DataKind Kind => TheKind; + [InverseProperty(nameof(GetHttpsResource.PhotovoltaicData))] public override ICollection Resources { get; } = []; diff --git a/backend/src/Program.cs b/backend/src/Program.cs index a3dc2a8c..6146739d 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -7,6 +7,7 @@ using Database.Data; using Database.Services; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -248,6 +249,12 @@ Uri openTelemetryHost loggerConfiguration .ReadFrom.Configuration(webHostBuilderContext.Configuration); }); + builder.WebHost.ConfigureKestrel(_ => + { + // matches the keep-alive timeout configured for `/api/resources/` in NGINX + _.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(300); + _.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30); + }); return builder; } @@ -275,4 +282,4 @@ string[] commandLineArguments "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables .AddCommandLine(commandLineArguments); } -} +} \ No newline at end of file diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 5b67632e..e602dc6e 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -138,24 +138,36 @@ server { limit_req zone=sensitive burst=6 nodelay; } - location /api/upload-file { + location /api/resources/ { + # Route POST requests to the dedicated upload location + if ($request_method = POST) { + error_page 418 = @resource_post; + return 418; + } + # Route GET requests to the dedicated download location + if ($request_method = GET) { + error_page 418 = @resource_get; + return 418; + } + # Fallback for other HTTP methods (PUT, DELETE, etc.) + proxy_pass http://backend; + } + + location @resource_post { + # Optimize for multi-gigabyte POST uploads proxy_pass http://backend; - limit_req zone=sensitive burst=2 nodelay; client_max_body_size 10G; client_body_timeout 300s; proxy_read_timeout 300s; proxy_send_timeout 300s; - proxy_request_buffering off; + proxy_request_buffering off; } - location /api/resources/ { - # Inspired by https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/ - alias /data/files/; # https://nginx.org/en/docs/http/ngx_http_core_module.html#alias - sendfile on; - sendfile_max_chunk 1m; - tcp_nopush on; - tcp_nodelay on; - keepalive_timeout 65s; + location @resource_get { + # Optimize for multi-gigabyte GET streaming downloads + proxy_pass http://backend; + proxy_buffering off; + proxy_read_timeout 300s; } location /openapi { From c8392fd8ab8cc5132eb0e9bd5182b8d00b9c4adc Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 20:21:40 +0200 Subject: [PATCH 818/878] Add migration that asserts existence of access policy and root resource in PostgreSQL --- ...icyAndRootResourceInPostgreSql.Designer.cs | 3556 +++++++++++++++++ ...AccessPolicyAndRootResourceInPostgreSql.cs | 78 + .../ApplicationDbContextModelSnapshot.cs | 99 +- backend/src/Migrations/migrate.sql | 338 ++ ...ccessPolicyAndRootResourceInPostgreSql.sql | 338 ++ ...essPolicyIsOnlyDeletedAlongsideItsData.sql | 130 + 6 files changed, 4531 insertions(+), 8 deletions(-) create mode 100644 backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.Designer.cs create mode 100644 backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.cs create mode 100644 backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql create mode 100644 backend/src/Migrations/rollback_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql diff --git a/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.Designer.cs b/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.Designer.cs new file mode 100644 index 00000000..91b0b4ce --- /dev/null +++ b/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.Designer.cs @@ -0,0 +1,3556 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql")] + partial class AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"); + + t.HasTrigger("calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_calorimetric_data_access_policy_if_necessary"); + }); + + b + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_geometric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("geometric_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("life_cycle_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_optical_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("optical_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("photovoltaic_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.cs b/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.cs new file mode 100644 index 00000000..e75bddcd --- /dev/null +++ b/backend/src/Migrations/20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.cs @@ -0,0 +1,78 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() CASCADE;"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() CASCADE;"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index d3e68bef..37c0c396 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -132,7 +132,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); }); @@ -320,7 +320,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("calorimetric_data", "database"); + b.ToTable("calorimetric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"); + + t.HasTrigger("calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_calorimetric_data_access_policy_if_necessary"); + }); + + b + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); }); modelBuilder.Entity("Database.Data.GeometricData", b => @@ -389,7 +402,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("geometric_data", "database"); + b.ToTable("geometric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_geometric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("geometric_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.GetHttpsResource", b => @@ -486,10 +512,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + t.HasTrigger("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"); + t.HasTrigger("get_https_resource_data_id_cannot_change"); t.HasTrigger("get_https_resource_data_ids_must_match"); + t.HasTrigger("get_https_resource_root_can_only_be_deleted_alongside_its_data"); + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); @@ -497,7 +527,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") - .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();"); + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => @@ -554,7 +585,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("hygrothermal_data", "database"); + b.ToTable("hygrothermal_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.LifeCycleData", b => @@ -611,7 +655,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("lifeCycle_data", "database"); + b.ToTable("lifeCycle_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("life_cycle_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -701,7 +758,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("optical_data", "database"); + b.ToTable("optical_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_optical_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("optical_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => @@ -758,7 +828,20 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("CreatedAt", "Id") .IsUnique(); - b.ToTable("photovoltaic_data", "database"); + b.ToTable("photovoltaic_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("photovoltaic_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.User", b => diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 5c2bacc2..3cf72139 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -3141,3 +3141,341 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "CalorimetricDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."calorimetric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("CalorimetricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("CalorimetricDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT + ON "database"."calorimetric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("GeometricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("GeometricDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT + ON "database"."geometric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_geometric_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "GeometricDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."geometric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_geometric_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ + BEGIN + IF OLD."ParentId" IS NULL AND ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("HygrothermalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("HygrothermalDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT + ON "database"."hygrothermal_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "HygrothermalDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."hygrothermal_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("LifeCycleDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("LifeCycleDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT + ON "database"."lifeCycle_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "LifeCycleDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."lifeCycle_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("OpticalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("OpticalDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT + ON "database"."optical_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_optical_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "OpticalDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."optical_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_optical_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("PhotovoltaicDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("PhotovoltaicDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT + ON "database"."photovoltaic_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "PhotovoltaicDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."photovoltaic_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql b/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql new file mode 100644 index 00000000..d6e15a21 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql @@ -0,0 +1,338 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "CalorimetricDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."calorimetric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("CalorimetricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("CalorimetricDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT + ON "database"."calorimetric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("GeometricDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("GeometricDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT + ON "database"."geometric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_geometric_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "GeometricDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."geometric_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_geometric_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ + BEGIN + IF OLD."ParentId" IS NULL AND ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("HygrothermalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("HygrothermalDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT + ON "database"."hygrothermal_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "HygrothermalDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."hygrothermal_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("LifeCycleDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("LifeCycleDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT + ON "database"."lifeCycle_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "LifeCycleDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."lifeCycle_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("OpticalDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("OpticalDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT + ON "database"."optical_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_optical_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "OpticalDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."optical_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_optical_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ + BEGIN + INSERT INTO "data_access_policy" + ("PhotovoltaicDataId", "Combinator") + VALUES (NEW."Id", 'all') + ON CONFLICT ("PhotovoltaicDataId") DO NOTHING; + RETURN NEW; + RETURN NEW; + END; + $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT + ON "database"."photovoltaic_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM database."get_https_resource" + WHERE "ParentId" IS NULL + AND "PhotovoltaicDataId" = NEW.Id + ) + THEN + RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + END IF; + RETURN NEW; + END; + $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT + ON "database"."photovoltaic_data" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql', '10.0.8'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql b/backend/src/Migrations/rollback_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql new file mode 100644 index 00000000..b012ffa8 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData.sql @@ -0,0 +1,130 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_geometric_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_optical_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DROP FUNCTION "database"."LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql'; + END IF; +END $EF$; +COMMIT; + From cc73fdece3891bcdadb371959acfccb8d795c901 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 20:46:07 +0200 Subject: [PATCH 819/878] Construct path to controller action properly using `LinkGenerator` where necessary --- .../GetHttpsResourcesController.cs | 7 ++++++- .../GetHttpsResourceResolvers.cs | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 1aa578cf..9ca5a6c5 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -290,7 +290,12 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( } return CreatedAtAction( nameof(Get), - new + getHttpsResource.FileExtension is null + ? new + { + id = getHttpsResource.Id, + } + : new { id = getHttpsResource.Id, extension = getHttpsResource.FileExtension diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 56b6a57a..03d4d05f 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; +using Database.Controllers; using Database.Data; using Database.GraphQl.CalorimetricDataX; using Database.GraphQl.GeometricDataX; @@ -10,6 +11,7 @@ using Database.GraphQl.OpticalDataX; using Database.GraphQl.PhotovoltaicDataX; using HotChocolate; +using Microsoft.AspNetCore.Routing; namespace Database.GraphQl.GetHttpsResources; @@ -73,12 +75,26 @@ CancellationToken cancellationToken public Uri GetLocator( [Parent] GetHttpsResource getHttpsResource, - AppSettings appSettings + AppSettings appSettings, + LinkGenerator linkGenerator ) { return new UriBuilder(appSettings.Uri) { - Path = $"/api/resources/{getHttpsResource.FileName}" + Path = linkGenerator.GetPathByAction( + controller: nameof(GetHttpsResourcesController), + action: nameof(GetHttpsResourcesController.Get), + values: getHttpsResource.FileExtension is null + ? new + { + id = getHttpsResource.Id + } + : new + { + id = getHttpsResource.Id, + extension = getHttpsResource.FileExtension + } + ) }.Uri; } From 20626b4d6eae625ddf7ac78efc389eea3d249293 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 11 Jun 2026 20:46:38 +0200 Subject: [PATCH 820/878] Use RESTful API for file upload in frontend --- frontend/pages/upload-file.tsx | 85 +++++++++++++++++++--------------- frontend/paths.ts | 2 + 2 files changed, 49 insertions(+), 38 deletions(-) diff --git a/frontend/pages/upload-file.tsx b/frontend/pages/upload-file.tsx index 6b0a8d80..0fd56dec 100644 --- a/frontend/pages/upload-file.tsx +++ b/frontend/pages/upload-file.tsx @@ -1,7 +1,9 @@ -import { Form, Input, Button, Row, Col, Card, Typography, Upload } from "antd"; +import { Form, Input, Button, Card, Typography, Upload, Flex } from "antd"; import { UploadOutlined } from "@ant-design/icons"; import Layout from "../components/Layout"; import { RcFile } from "antd/lib/upload/interface"; +import paths from "../paths"; +import { uuidRegex } from "../lib/string"; const layout = { labelCol: { span: 8 }, @@ -19,47 +21,54 @@ function Page() { const [form] = Form.useForm(); const constructFileUploadAction = (_file: RcFile) => - `/api/upload-file?getHttpsResourceUuid=${encodeURIComponent( - form.getFieldValue("getHttpsResourceUuid"), - )}`; + paths.resource(form.getFieldValue("getHttpsResourceId")); return ( - -
    - - - For an existing GET HTTPS resource entry in the database, upload a - file with the content for that resource. - -
    - + + + For an existing GET HTTPS resource entry in the database, upload a + file with the content for that resource. + + + + + + + - - - - - - - - - - - - + + + + +
    + ); } diff --git a/frontend/paths.ts b/frontend/paths.ts index 33925841..f236da3c 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -30,6 +30,8 @@ export default { `/data/photovoltaic/${encodeURIComponent(String(id))}` as Route, createData: "/data/create" as Route, uploadFile: "/upload-file" as Route, + resource: (id: Scalars["Uuid"]["output"]) => + `/api/resources/${encodeURIComponent(String(id))}` as Route, login: "/connect/login" as Route, logout: "/connect/logout" as Route, graphQl: "/graphql/" as Route, From dee17a7dcd3ba95be727f61ecfc29f4dd2f03362 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 13 Jun 2026 01:00:30 +0200 Subject: [PATCH 821/878] Repair some PostgreSQL triggers, fix nested frontend filters, use new current user model in frontend, correct access policy mutations, mark pure extensions as `Pure`, make `uint` filterable, improve URI generation for resource upload/download actions, correct access policy logical formula, and configure filter types --- .../src/Configuration/GraphQlConfiguration.cs | 2 + .../GetHttpsResourcesController.cs | 57 ++- .../Data/AccessPolicies/DataAccessPolicy.cs | 41 +- backend/src/Data/ApplicationDbContext.cs | 9 +- backend/src/Data/GetHttpsResource.cs | 42 +- .../DataPointsEnumerationsExtensions.cs | 5 + .../src/Extensions/HttpContextExtensions.cs | 2 + backend/src/Extensions/NodaTimeExtensions.cs | 5 + backend/src/Extensions/ResultExtensions.cs | 5 + backend/src/Extensions/StringExtensions.cs | 7 + .../AccessPolicies/AccessPolicyDataLoaders.cs | 68 ++++ .../AccessPolicies/AccessPolicyTypeBase.cs | 22 + .../ClearDataAccessPoliciesMutation.cs | 72 ---- .../ClearInstitutionAccessPoliciesMutation.cs | 7 +- ...onnectApplicationAccessPoliciesMutation.cs | 7 +- .../ClearUserAccessPoliciesMutation.cs | 7 +- .../ConfigureDataAccessPoliciesMutation.cs | 105 +++++ .../ConfigureDataAccessPolicyMutation.cs | 99 +++++ .../DataAccessPolicyFilterType.cs | 28 ++ .../AccessPolicies/DataAccessPolicyType.cs | 13 +- .../InstitutionAccessPolicyFilterType.cs | 6 +- .../InstitutionAccessPolicyType.cs | 15 + ...nnectApplicationAccessPolicyFilterInput.cs | 6 +- ...penIdConnectApplicationAccessPolicyType.cs | 15 + .../ResetDataAccessPoliciesMutation.cs | 101 +++++ .../ResetDataAccessPolicyMutation.cs | 10 +- .../SetInstitutionAccessPolicyMutation.cs | 9 +- ...dConnectApplicationAccessPolicyMutation.cs | 6 +- .../SetUserAccessPolicyMutation.cs | 6 +- .../UnsetInstitutionAccessPolicyMutation.cs | 3 +- ...dConnectApplicationAccessPolicyMutation.cs | 3 +- .../UnsetUserAccessPolicyMutation.cs | 3 +- .../UserAccessPolicyFilterInput.cs | 6 +- .../AccessPolicies/UserAccessPolicyType.cs | 15 + backend/src/GraphQl/DataX/DataResolvers.cs | 1 + backend/src/GraphQl/DataX/DataType.cs | 2 +- .../src/GraphQl/DataX/GetHttpsResourceTree.cs | 6 +- .../src/GraphQl/Extensions/PageExtensions.cs | 5 + .../Extensions/ResolverContextExtensions.cs | 3 + .../GraphQl/Filters/ScalarFilterInputTypes.cs | 10 + .../GetHttpsResourceResolvers.cs | 16 +- .../GetHttpsResources/GetHttpsResourceType.cs | 4 +- backend/src/Migrations/migrate.sql | 42 +- ...ccessPolicyAndRootResourceInPostgreSql.sql | 42 +- backend/src/Services/AccessPolicyService.cs | 28 +- backend/src/Services/UserService.cs | 9 +- .../GraphQlSchemaTests.IsUnchanged.snap | 378 ++++++++++++++---- .../components/ActiveFilterAndSortBar.tsx | 22 +- frontend/components/Layout.tsx | 10 +- frontend/components/NavBar.tsx | 7 +- frontend/components/data/DataSummary.tsx | 7 +- .../components/databases/DatabaseSummary.tsx | 1 + .../components/entities/EntitySummary.tsx | 2 +- frontend/lib/filter.ts | 67 ++-- frontend/queries/currentUser.graphql | 21 +- frontend/type-defs.graphqls | 360 ++++++++++++++--- 56 files changed, 1410 insertions(+), 440 deletions(-) create mode 100644 backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs delete mode 100644 backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs rename backend/src/{Data => GraphQl}/AccessPolicies/DataAccessPolicyType.cs (92%) create mode 100644 backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs create mode 100644 backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs create mode 100644 backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs create mode 100644 backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 01ae896f..f8330fd9 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -396,6 +396,8 @@ this IFilterConventionDescriptor descriptor .BindRuntimeType() .BindRuntimeType() .BindRuntimeType() + .BindRuntimeType() + .BindRuntimeType() .BindRuntimeType() .BindRuntimeType() .BindRuntimeType() diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 9ca5a6c5..b547eb54 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -20,7 +20,6 @@ using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.WebUtilities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -78,9 +77,30 @@ protected override void Dispose(bool disposing) Dispose(false); } - [HttpGet("~/api/resources/{id:guid}")] - [HttpGet("~/api/resources/{id:guid}.{extension}")] - [EndpointName("GetResourceById")] + private const string GetByIdRouteName = "GetResourceById"; + private const string GetByIdAndExtensionRouteName = "GetResourceByIdAndExtension"; + + public static string ConstructGetActionRouteName(GetHttpsResource getHttpsResource) => + getHttpsResource.FileExtension is null + ? GetByIdRouteName + : GetByIdAndExtensionRouteName; + + public static object CreateGetActionRouteValues(GetHttpsResource getHttpsResource) => + getHttpsResource.FileExtension is null + ? new + { + id = getHttpsResource.Id, + } + : new + { + id = getHttpsResource.Id, + extension = getHttpsResource.FileExtension + }; + + [HttpGet("~/api/resources/{id:guid}", Name = GetByIdRouteName)] + [HttpGet("~/api/resources/{id:guid}.{extension}", Name = GetByIdAndExtensionRouteName)] + [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] + [AllowAnonymous] [EndpointDescription("Get an HTTP resource")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -166,14 +186,21 @@ public async Task Get( ); } - [HttpPost("~/api/resources/{id:guid}")] - [EndpointName("UploadResource")] + public const string UploadRouteName = "UploadResource"; + + public static object CreateUploadActionRouteValues(GetHttpsResource getHttpsResource) => + new + { + id = getHttpsResource.Id, + }; + + [HttpPost("~/api/resources/{id:guid}", Name = UploadRouteName)] [EndpointDescription("Upload file for a GET HTTP resource")] [DisableFormValueModelBinding] [DisableRequestSizeLimit] [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [Consumes(MediaTypeNames.Multipart.FormData)] - [AcceptsMultipartFormFile] // see `AcceptsMultipartFormFileAttribute` below + [AcceptsMultipartFormFile] [Produces(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -288,18 +315,10 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( // TODO How can this be reported to the user? logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, exception); } - return CreatedAtAction( - nameof(Get), - getHttpsResource.FileExtension is null - ? new - { - id = getHttpsResource.Id, - } - : new - { - id = getHttpsResource.Id, - extension = getHttpsResource.FileExtension - } + return CreatedAtRoute( + ConstructGetActionRouteName(getHttpsResource), + CreateGetActionRouteValues(getHttpsResource), + null ); } diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 49c5bf5e..c083ec70 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -74,10 +74,10 @@ private void AssertThatExactlyOneDataIdIsNonNull() } } - // Note that at least one data ID is always present. So `Guid.Empty` will never be used. + // Note that for the global data access policy, all `*DataId`s are null [NotMapped] [Projectable] - public Guid? DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; + public Guid? DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? null; [NotMapped] public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; @@ -157,24 +157,30 @@ public bool IsAccessAllowed( ) => ( Combinator == LogicalCombinator.ALL && ( - !UserAccessPolicies.Any(_ => - currentUser == null || ( - _.UserId == currentUser.Uuid - && !_.IsAccessAllowed + ( + UserAccessPolicies.Count == 0 + || UserAccessPolicies.Any(_ => + currentUser != null + && _.UserId == currentUser.Uuid + && _.IsAccessAllowed ) ) && - !InstitutionAccessPolicies.Any(_ => - institutionIds == null || ( - institutionIds.Contains(_.InstitutionId) - && !_.IsAccessAllowed + ( + InstitutionAccessPolicies.Count == 0 + || InstitutionAccessPolicies.Any(_ => + institutionIds != null + && institutionIds.Contains(_.InstitutionId) + && _.IsAccessAllowed ) ) && - !OpenIdConnectApplicationAccessPolicies.Any(_ => - openIdConnectClientId == null || ( - _.ClientId == openIdConnectClientId - && !_.IsAccessAllowed + ( + OpenIdConnectApplicationAccessPolicies.Count == 0 + || OpenIdConnectApplicationAccessPolicies.Any(_ => + openIdConnectClientId != null + && _.ClientId == openIdConnectClientId + && _.IsAccessAllowed ) ) ) @@ -209,4 +215,11 @@ public void Reset() InstitutionAccessPolicies.Clear(); OpenIdConnectApplicationAccessPolicies.Clear(); } + + public void Configure( + LogicalCombinator combinator + ) + { + Combinator = combinator; + } } \ No newline at end of file diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 1681cde9..151b8c9a 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -410,7 +410,7 @@ string dataIdColumn IF NOT EXISTS ( SELECT 1 FROM {_schemaName}.{GetHttpsResource.TableName.Enquote()} WHERE {nameof(GetHttpsResource.ParentId).Enquote()} IS NULL - AND {dataIdColumn.Enquote()} = NEW.{nameof(IData.Id)} + AND {dataIdColumn.Enquote()} = NEW.{nameof(IData.Id).Enquote()} ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -422,7 +422,7 @@ IF NOT EXISTS ( return builder; } - private static + private EntityTypeBuilder AddTriggerThatCreatesDataAccessPolicyIfNecessary( EntityTypeBuilder builder, @@ -440,11 +440,10 @@ string dataIdColumn .Action(action => action .ExecuteRawSql( $""" - INSERT INTO {DataAccessPolicy.TableName.Enquote()} + INSERT INTO {_schemaName}.{DataAccessPolicy.TableName.Enquote()} ({dataIdColumn.Enquote()}, {nameof(DataAccessPolicy.Combinator).Enquote()}) VALUES (NEW.{nameof(IEntity.Id).Enquote()}, '{LogicalCombinator.ALL.ToString().ToLowerInvariant()}') - ON CONFLICT ({dataIdColumn.Enquote()}) DO NOTHING; - RETURN NEW; + ON CONFLICT ({dataIdColumn.Enquote()}) WHERE {dataIdColumn.Enquote()} IS NOT NULL DO NOTHING; """ ) // an alternative is diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index cc7b37bf..c5041419 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -158,43 +158,43 @@ private void AssertThatExactlyOneDataIdIsNonNull() public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; [NotMapped] - public Enumerations.DataKind? DataKind + public Database.Enumerations.DataKind? DataKind { get { - if (CalorimetricDataId is not null) return Enumerations.DataKind.CALORIMETRIC_DATA; - if (GeometricDataId is not null) return Enumerations.DataKind.GEOMETRIC_DATA; - if (HygrothermalDataId is not null) return Enumerations.DataKind.HYGROTHERMAL_DATA; - if (LifeCycleDataId is not null) return Enumerations.DataKind.LIFE_CYCLE_DATA; - if (OpticalDataId is not null) return Enumerations.DataKind.OPTICAL_DATA; - if (PhotovoltaicDataId is not null) return Enumerations.DataKind.PHOTOVOLTAIC_DATA; + if (CalorimetricDataId is not null) return Database.Enumerations.DataKind.CALORIMETRIC_DATA; + if (GeometricDataId is not null) return Database.Enumerations.DataKind.GEOMETRIC_DATA; + if (HygrothermalDataId is not null) return Database.Enumerations.DataKind.HYGROTHERMAL_DATA; + if (LifeCycleDataId is not null) return Database.Enumerations.DataKind.LIFE_CYCLE_DATA; + if (OpticalDataId is not null) return Database.Enumerations.DataKind.OPTICAL_DATA; + if (PhotovoltaicDataId is not null) return Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA; return null; } } [Projectable] - public Guid? GetDataId(Enumerations.DataKind dataKind) => + public Guid? GetDataId(Database.Enumerations.DataKind dataKind) => dataKind switch { - Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricDataId, - Enumerations.DataKind.GEOMETRIC_DATA => GeometricDataId, - Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, - Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, - Enumerations.DataKind.OPTICAL_DATA => OpticalDataId, - Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, + Database.Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricDataId, + Database.Enumerations.DataKind.GEOMETRIC_DATA => GeometricDataId, + Database.Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + Database.Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, + Database.Enumerations.DataKind.OPTICAL_DATA => OpticalDataId, + Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; [Projectable] - public IData? GetData(Enumerations.DataKind dataKind) => + public IData? GetData(Database.Enumerations.DataKind dataKind) => dataKind switch { - Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricData, - Enumerations.DataKind.GEOMETRIC_DATA => GeometricData, - Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalData, - Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleData, - Enumerations.DataKind.OPTICAL_DATA => OpticalData, - Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + Database.Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricData, + Database.Enumerations.DataKind.GEOMETRIC_DATA => GeometricData, + Database.Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalData, + Database.Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleData, + Database.Enumerations.DataKind.OPTICAL_DATA => OpticalData, + Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; diff --git a/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs index a4645cf3..4b5237dc 100644 --- a/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs +++ b/backend/src/Enumerations/DataPoints/DataPointsEnumerationsExtensions.cs @@ -1,9 +1,11 @@ using System; +using System.Diagnostics.Contracts; namespace Database.Enumerations.DataPoints; public static class DataPointsEnumerationsExtensions { + [Pure] public static string ToJsonPathQuery(this IncidenceDirection incidenceDirection) { return incidenceDirection switch @@ -14,6 +16,7 @@ public static string ToJsonPathQuery(this IncidenceDirection incidenceDirection) }; } + [Pure] public static string ToJsonEnum(this WavelengthsIntegral wavelengthsIntegral) { return wavelengthsIntegral switch @@ -27,6 +30,7 @@ public static string ToJsonEnum(this WavelengthsIntegral wavelengthsIntegral) }; } + [Pure] public static string ToJsonEnum(this EmergenceDirection emergenceDirection) { return emergenceDirection switch @@ -37,6 +41,7 @@ public static string ToJsonEnum(this EmergenceDirection emergenceDirection) }; } + [Pure] public static string ToJsonProperty(this DataPointResult result) { return result switch diff --git a/backend/src/Extensions/HttpContextExtensions.cs b/backend/src/Extensions/HttpContextExtensions.cs index 3468cd50..585e10ba 100644 --- a/backend/src/Extensions/HttpContextExtensions.cs +++ b/backend/src/Extensions/HttpContextExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.Linq; using Database.Authentication; using Microsoft.AspNetCore.Http; @@ -10,6 +11,7 @@ public static class HttpContextExtensions public static void SetBearerToken(this HttpContext httpContext, string accessToken) => httpContext.Request.Headers.Authorization = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} {accessToken}"; + [Pure] public static string? ExtractBearerToken(this HttpContext httpContext) { var bearerTokenPrefix = $"{OpenIdConnectConstants.AuthorizationHeaderBearer} "; diff --git a/backend/src/Extensions/NodaTimeExtensions.cs b/backend/src/Extensions/NodaTimeExtensions.cs index 80d21035..0fb6d107 100644 --- a/backend/src/Extensions/NodaTimeExtensions.cs +++ b/backend/src/Extensions/NodaTimeExtensions.cs @@ -1,14 +1,17 @@ +using System.Diagnostics.Contracts; using NodaTime; namespace Database.Extensions; public static class NodaTimeExtensions { + [Pure] public static OffsetDateTime GetUtcNow(this IClock clock) { return clock.GetCurrentInstant().WithOffset(Offset.Zero); } + [Pure] public static int CompareTo(this OffsetDateTime current, OffsetDateTime other) { return OffsetDateTime.Comparer.Instant.Compare(current, other); @@ -16,11 +19,13 @@ public static int CompareTo(this OffsetDateTime current, OffsetDateTime other) extension(OffsetDateTime) { + [Pure] public static bool operator >(OffsetDateTime x, OffsetDateTime y) { return OffsetDateTime.Comparer.Instant.Compare(x, y) > 0; } + [Pure] public static bool operator <(OffsetDateTime x, OffsetDateTime y) { return OffsetDateTime.Comparer.Instant.Compare(x, y) < 0; diff --git a/backend/src/Extensions/ResultExtensions.cs b/backend/src/Extensions/ResultExtensions.cs index bbe641fd..216a54d9 100644 --- a/backend/src/Extensions/ResultExtensions.cs +++ b/backend/src/Extensions/ResultExtensions.cs @@ -1,10 +1,12 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Contracts; namespace Database.Extensions; public static class ResultExtensions { + [Pure] public static bool Failed( this Result result, [NotNullWhen(false)] out TData? data, @@ -28,6 +30,7 @@ public static bool Failed( } } + [Pure] public static bool Failed( this Result result, [NotNullWhen(true)] out TError? error @@ -47,6 +50,7 @@ public static bool Failed( } } + [Pure] public static bool Succeeded( this Result result, [NotNullWhen(true)] out TData? data, @@ -70,6 +74,7 @@ public static bool Succeeded( } } + [Pure] public static bool Succeeded( this Result result, [NotNullWhen(true)] out TData? data diff --git a/backend/src/Extensions/StringExtensions.cs b/backend/src/Extensions/StringExtensions.cs index 5d006340..9fa2d2d1 100644 --- a/backend/src/Extensions/StringExtensions.cs +++ b/backend/src/Extensions/StringExtensions.cs @@ -1,10 +1,12 @@ using System; using System.Text; +using System.Diagnostics.Contracts; namespace Database.Extensions; public static class StringExtensions { + [Pure] public static string FirstCharToLower(this string value) { return string.IsNullOrEmpty(value) @@ -14,12 +16,15 @@ public static string FirstCharToLower(this string value) : char.ToLowerInvariant(value[0]) + value[1..]; } + [Pure] public static string? NullIfEmpty(this string value) => string.IsNullOrEmpty(value) ? null : value; + [Pure] public static string? NullIfWhitespace(this string value) => string.IsNullOrWhiteSpace(value) ? null : value; + [Pure] public static string Base64Encode(this string plainText) { return Convert.ToBase64String( @@ -27,6 +32,7 @@ public static string Base64Encode(this string plainText) ); } + [Pure] public static string Base64Decode(this string base64EncodedData) { return Encoding.UTF8.GetString( @@ -34,6 +40,7 @@ public static string Base64Decode(this string base64EncodedData) ); } + [Pure] public static string Enquote(this string str) { return "\"" + str + "\""; diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs index e473b775..667a05cc 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyDataLoaders.cs @@ -14,6 +14,74 @@ namespace Database.GraphQl.AccessPolicies; public sealed class InstitutionAccessPolicyLoaders : DataLoaders { + [DataLoader] + public static ValueTask> GetDataAccessPolicyByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.DataAccessPolicies, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetUserAccessPolicyByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.UserAccessPolicies, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetInstitutionAccessPolicyByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.InstitutionAccessPolicies, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + + [DataLoader] + public static ValueTask> GetOpenIdConnectApplicationAccessPolicyByIdAsync( + IReadOnlyList ids, + QueryContext queryContext, + IDbContextFactory databaseContextFactory, + CancellationToken cancellationToken + ) + { + return GetEntityByIdAsync( + ids, + databaseContext => databaseContext.OpenIdConnectApplicationAccessPolicies, + queryContext, + databaseContextFactory, + cancellationToken + ); + } + [DataLoader] public static async ValueTask> GetDataAccessPolicyByDataIdAsync( IReadOnlyList dataIds, diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs new file mode 100644 index 00000000..63922857 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs @@ -0,0 +1,22 @@ +using System; +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using GreenDonut; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public abstract class AccessPolicyTypeBase + : EntityType + where TAccessPolicy : AccessPolicyBase + where TAccessPolicyByIdDataLoader : IDataLoader +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + // descriptor.Field(_ => _.IsAccessAllowed).Ignore(); + // descriptor.Field(_ => _.IsWithinTimeSpan).Ignore(); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs deleted file mode 100644 index de1cb682..00000000 --- a/backend/src/GraphQl/AccessPolicies/ClearDataAccessPoliciesMutation.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using Database.Authorization; -using Database.Data; -using Database.Data.AccessPolicies; -using Database.Extensions; -using HotChocolate.Types; -using Microsoft.EntityFrameworkCore; - -namespace Database.GraphQl.AccessPolicy; - -[SuppressMessage("Naming", "CA1707")] -public enum ClearDataAccessPoliciesErrorCode -{ - UNKNOWN, - UNAUTHENTICATED, - UNAUTHORIZED, - UNKNOWN_DATA -} - -public sealed record ClearDataAccessPoliciesError( - ClearDataAccessPoliciesErrorCode Code, - string Message, - IReadOnlyList Path -) -: UserErrorBase(Code, Message, Path); - -public sealed record ClearDataAccessPoliciesPayload( - IReadOnlyCollection? DataAccessPolicy, - IReadOnlyCollection? Errors -) : Payload; - -[ExtendObjectType(nameof(Mutation))] -public sealed class ClearDataAccessPoliciesMutation -: MutationBase, ClearDataAccessPoliciesPayload, ClearDataAccessPoliciesError, ClearDataAccessPoliciesErrorCode> -{ - protected override ClearDataAccessPoliciesPayload NewPayload( - IReadOnlyCollection? data, - IReadOnlyCollection? errors - ) => new(data, errors); - - protected override ClearDataAccessPoliciesError NewError( - ClearDataAccessPoliciesErrorCode code, - string message, - IReadOnlyList path - ) => new(code, message, path); - - public async Task ClearDataAccessPoliciesAsync( - ApplicationDbContext context, - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - if ((await AuthorizeAsync( - ClearDataAccessPoliciesErrorCode.UNAUTHENTICATED, - ClearDataAccessPoliciesErrorCode.UNAUTHORIZED, - authorization, - cancellationToken - ) - ).Failed(out var _, out var authorizeErrorPayload) - ) - { - return authorizeErrorPayload; - } - var policies = await context.DataAccessPolicies.ToListAsync(cancellationToken); - context.DataAccessPolicies.RemoveRange(policies); - await context.SaveChangesAsync(cancellationToken); - return NewPayload(policies, null); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs index 19debd27..6802c6fe 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs @@ -86,11 +86,10 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .Include(_ => _.InstitutionAccessPolicies) + .SingleAsync(_ => _.DataId == dataId, cancellationToken); dataAccessPolicy.InstitutionAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); diff --git a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs index cb7dcdd3..d4e36922 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs @@ -86,11 +86,10 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .Include(_ => _.OpenIdConnectApplicationAccessPolicies) + .SingleAsync(_ => _.DataId == dataId, cancellationToken); dataAccessPolicy.OpenIdConnectApplicationAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); diff --git a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs index e662dfb7..3df4bd50 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs @@ -86,11 +86,10 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .Include(_ => _.UserAccessPolicies) + .SingleAsync(_ => _.DataId == dataId, cancellationToken); dataAccessPolicy.UserAccessPolicies.Clear(); await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs new file mode 100644 index 00000000..8fdb2e4b --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using Database.GraphQl.AccessPolicies; +using Database.GraphQl.Extensions; +using HotChocolate.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicy; + +public sealed record ConfigureDataAccessPoliciesInput +( + LogicalCombinator Combinator +); + +[SuppressMessage("Naming", "CA1707")] +public enum ConfigureDataAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record ConfigureDataAccessPoliciesError( + ConfigureDataAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ConfigureDataAccessPoliciesPayload( + IReadOnlyCollection? DataAccessPolicies, + IReadOnlyCollection? Errors +) : Payload; + +public sealed class ConfigureDataAccessPoliciesFilterType + : DataAccessPolicyFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(ConfigureDataAccessPoliciesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class ConfigureDataAccessPoliciesMutation +: MutationBase, ConfigureDataAccessPoliciesPayload, ConfigureDataAccessPoliciesError, ConfigureDataAccessPoliciesErrorCode> +{ + protected override ConfigureDataAccessPoliciesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ConfigureDataAccessPoliciesError NewError( + ConfigureDataAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [UseFiltering] + public async Task ConfigureDataAccessPoliciesAsync( + ConfigureDataAccessPoliciesInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ConfigureDataAccessPoliciesErrorCode.UNAUTHENTICATED, + ConfigureDataAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var policies = + await context.DataAccessPolicies + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToListAsync(cancellationToken); + foreach (var policy in policies) + { + policy.Configure(input.Combinator); + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(policies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs new file mode 100644 index 00000000..5e16b99e --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Enumerations; +using Database.Extensions; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicy; + +public sealed record ConfigureDataAccessPolicyInput +( + DataReferenceInput? Data, + LogicalCombinator Combinator +); + +[SuppressMessage("Naming", "CA1707")] +public enum ConfigureDataAccessPolicyErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED, + UNKNOWN_DATA +} + +public sealed record ConfigureDataAccessPolicyError( + ConfigureDataAccessPolicyErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ConfigureDataAccessPolicyPayload( + DataAccessPolicy? DataAccessPolicy, + IReadOnlyCollection? Errors +) : Payload; + +[ExtendObjectType(nameof(Mutation))] +public sealed class ConfigureDataAccessPolicyMutation +: DataMutationBase +{ + protected override ConfigureDataAccessPolicyPayload NewPayload( + DataAccessPolicy? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ConfigureDataAccessPolicyError NewError( + ConfigureDataAccessPolicyErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + public async Task ConfigureDataAccessPolicyAsync( + ConfigureDataAccessPolicyInput input, + ApplicationDbContext context, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ConfigureDataAccessPolicyErrorCode.UNAUTHENTICATED, + ConfigureDataAccessPolicyErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + if (input.Data is not null) + { + if ((await FetchDataAsync( + input.Data, + ConfigureDataAccessPolicyErrorCode.UNKNOWN_DATA, + context, + cancellationToken + ) + ).Failed(out var data, out var fetchDataErrorPayload) + ) + { + return fetchDataErrorPayload; + } + } + + var dataId = input.Data?.DataId; + var dataAccessPolicy = await context.DataAccessPolicies + .SingleAsync(_ => _.DataId == dataId, cancellationToken); + dataAccessPolicy.Configure(input.Combinator); + await context.SaveChangesAsync(cancellationToken); + return NewPayload(dataAccessPolicy, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs new file mode 100644 index 00000000..e912eb27 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -0,0 +1,28 @@ +using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; +using HotChocolate.Data.Filters; + +namespace Database.GraphQl.AccessPolicies; + +public class DataAccessPolicyFilterType + : AuditableEntityFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(DataAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.Combinator); + descriptor.Field(_ => _.UserAccessPolicies); + descriptor.Field(_ => _.InstitutionAccessPolicies); + descriptor.Field(_ => _.OpenIdConnectApplicationAccessPolicies); + // descriptor.Field(_ => _.Data); + descriptor.Field(_ => _.CalorimetricData); + descriptor.Field(_ => _.GeometricData); + descriptor.Field(_ => _.HygrothermalData); + descriptor.Field(_ => _.LifeCycleData); + descriptor.Field(_ => _.OpticalData); + descriptor.Field(_ => _.PhotovoltaicData); + } +} \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs similarity index 92% rename from backend/src/Data/AccessPolicies/DataAccessPolicyType.cs rename to backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index ef6083fb..1a0f2633 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -4,16 +4,14 @@ using System.Threading.Tasks; using Database.Authorization; using Database.Data.AccessPolicies; -using Database.GraphQl.AccessPolicies; using Database.GraphQl.Extensions; -using GreenDonut; using GreenDonut.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; -namespace Database.GraphQl; +namespace Database.GraphQl.AccessPolicies; public sealed class DataAccessPolicyType : ObjectType @@ -56,7 +54,8 @@ CancellationToken cancellationToken } return await byId .With(resolverContext.GetQueryContext()) - .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + .LoadAsync(dataAccessPolicy.Id, cancellationToken) + ?? []; } [UseFiltering] @@ -76,7 +75,8 @@ CancellationToken cancellationToken } return await byId .With(resolverContext.GetQueryContext()) - .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + .LoadAsync(dataAccessPolicy.Id, cancellationToken) + ?? []; } [UseFiltering] @@ -96,7 +96,8 @@ CancellationToken cancellationToken } return await byId .With(resolverContext.GetQueryContext()) - .LoadRequiredAsync(dataAccessPolicy.Id, cancellationToken); + .LoadAsync(dataAccessPolicy.Id, cancellationToken) + ?? []; } } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs index d4edcd5d..8eeab0fb 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs @@ -1,10 +1,11 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; namespace Database.GraphQl.AccessPolicies; public sealed class InstitutionAccessPolicyFilterType - : FilterInputType + : AuditableEntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -13,5 +14,8 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(InstitutionAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.InstitutionId); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs new file mode 100644 index 00000000..ee59bfcf --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs @@ -0,0 +1,15 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class InstitutionAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs index 732d8e56..10ae9554 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs @@ -1,10 +1,11 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; namespace Database.GraphQl.AccessPolicies; public sealed class OpenIdConnectApplicationAccessPolicyFilterType - : FilterInputType + : AuditableEntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -13,5 +14,8 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.ClientId); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs new file mode 100644 index 00000000..970240d6 --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs @@ -0,0 +1,15 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class OpenIdConnectApplicationAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs new file mode 100644 index 00000000..9a6aac9c --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Database.Authorization; +using Database.Data; +using Database.Data.AccessPolicies; +using Database.Extensions; +using Database.GraphQl.AccessPolicies; +using Database.GraphQl.Extensions; +using HotChocolate.Data; +using HotChocolate.Data.Filters; +using HotChocolate.Resolvers; +using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; + +namespace Database.GraphQl.AccessPolicy; + +[SuppressMessage("Naming", "CA1707")] +public enum ResetDataAccessPoliciesErrorCode +{ + UNKNOWN, + UNAUTHENTICATED, + UNAUTHORIZED +} + +public sealed record ResetDataAccessPoliciesError( + ResetDataAccessPoliciesErrorCode Code, + string Message, + IReadOnlyList Path +) +: UserErrorBase(Code, Message, Path); + +public sealed record ResetDataAccessPoliciesPayload( + IReadOnlyCollection? DataAccessPolicies, + IReadOnlyCollection? Errors +) : Payload; + +public sealed class ResetDataAccessPoliciesFilterType + : DataAccessPolicyFilterType +{ + protected override void Configure( + IFilterInputTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + descriptor.Name(nameof(ResetDataAccessPoliciesFilterType)[..^10] + GraphQlConstants.FilterInputSuffix); + } +} + +[ExtendObjectType(nameof(Mutation))] +public sealed class ResetDataAccessPoliciesMutation +: MutationBase, ResetDataAccessPoliciesPayload, ResetDataAccessPoliciesError, ResetDataAccessPoliciesErrorCode> +{ + protected override ResetDataAccessPoliciesPayload NewPayload( + IReadOnlyCollection? data, + IReadOnlyCollection? errors + ) => new(data, errors); + + protected override ResetDataAccessPoliciesError NewError( + ResetDataAccessPoliciesErrorCode code, + string message, + IReadOnlyList path + ) => new(code, message, path); + + [UseFiltering] + public async Task ResetDataAccessPoliciesAsync( + ApplicationDbContext context, + CommonAuthorization authorization, + IResolverContext resolverContext, + CancellationToken cancellationToken + ) + { + if ((await AuthorizeAsync( + ResetDataAccessPoliciesErrorCode.UNAUTHENTICATED, + ResetDataAccessPoliciesErrorCode.UNAUTHORIZED, + authorization, + cancellationToken + ) + ).Failed(out var _, out var authorizeErrorPayload) + ) + { + return authorizeErrorPayload; + } + + var policies = + await context.DataAccessPolicies + .Include(_ => _.UserAccessPolicies) + .Include(_ => _.InstitutionAccessPolicies) + .Include(_ => _.OpenIdConnectApplicationAccessPolicies) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToListAsync(cancellationToken); + foreach (var policy in policies) + { + policy.Reset(); + } + await context.SaveChangesAsync(cancellationToken); + return NewPayload(policies, null); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs index c50a9fd7..b70094c4 100644 --- a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs @@ -6,7 +6,6 @@ using Database.Data; using Database.Data.AccessPolicies; using Database.Extensions; -using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -87,11 +86,12 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .Include(_ => _.UserAccessPolicies) + .Include(_ => _.InstitutionAccessPolicies) + .Include(_ => _.OpenIdConnectApplicationAccessPolicies) + .SingleAsync(_ => _.DataId == dataId, cancellationToken); dataAccessPolicy.Reset(); await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs index 5abf2202..758ff23b 100644 --- a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs @@ -12,7 +12,6 @@ using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using GraphQL.Client.Abstractions.Utilities; -using Database.Enumerations; namespace Database.GraphQl.AccessPolicies; @@ -111,11 +110,9 @@ CancellationToken cancellationToken return NewPayload(null, errors); } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .SingleAsync(_ => _.DataId == dataId, cancellationToken); var institutionAccessPolicy = await context.InstitutionAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicyId == dataAccessPolicy.Id @@ -138,4 +135,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(institutionAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs index 1416bfda..8b705147 100644 --- a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -109,11 +109,9 @@ CancellationToken cancellationToken return NewPayload(null, errors); } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .SingleAsync(_ => _.DataId == dataId, cancellationToken); var applicationAccessPolicy = await context.OpenIdConnectApplicationAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicyId == dataAccessPolicy.Id diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs index 22da03ee..f4aaa8b0 100644 --- a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs @@ -110,11 +110,9 @@ CancellationToken cancellationToken return NewPayload(null, errors); } + var dataId = input.Data?.DataId; var dataAccessPolicy = await context.DataAccessPolicies - .SingleAsync( - _ => _.DataId == (input.Data == null ? null : input.Data.DataId), - cancellationToken - ); + .SingleAsync(_ => _.DataId == dataId, cancellationToken); var userAccessPolicy = await context.UserAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicyId == dataAccessPolicy.Id diff --git a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs index 699c4b6d..37f955ef 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs @@ -90,10 +90,11 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var institutionAccessPolicy = await context.InstitutionAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicy != null - && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.DataAccessPolicy.DataId == dataId && _.InstitutionId == input.InstitutionId, cancellationToken ); diff --git a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs index 7a99c4dd..b3c0fd09 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -91,10 +91,11 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var applicationAccessPolicy = await context.OpenIdConnectApplicationAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicy != null - && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.DataAccessPolicy.DataId == dataId && _.ClientId == input.ClientId, cancellationToken ); diff --git a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs index 4f8820e6..a7f34da0 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs @@ -92,10 +92,11 @@ CancellationToken cancellationToken } } + var dataId = input.Data?.DataId; var userAccessPolicy = await context.UserAccessPolicies .SingleOrDefaultAsync(_ => _.DataAccessPolicy != null - && _.DataAccessPolicy.DataId == (input.Data == null ? null : input.Data.DataId) + && _.DataAccessPolicy.DataId == dataId && _.UserId == input.UserId, cancellationToken ); diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs index 079fc75a..066f99c0 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs @@ -1,10 +1,11 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Filters; namespace Database.GraphQl.AccessPolicies; public sealed class UserAccessPolicyFilterType - : FilterInputType + : AuditableEntityFilterType { protected override void Configure( IFilterInputTypeDescriptor descriptor @@ -13,5 +14,8 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(UserAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.DataAccessPolicy); + descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); + descriptor.Field(_ => _.AccessCountSinceStartTime); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs new file mode 100644 index 00000000..a28cdaee --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs @@ -0,0 +1,15 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Types; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class UserAccessPolicyType + : AccessPolicyTypeBase +{ + protected override void Configure( + IObjectTypeDescriptor descriptor + ) + { + base.Configure(descriptor); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 3a39ca30..cd06a401 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -48,6 +48,7 @@ CancellationToken cancellationToken { if (!await authorization.IsDatabaseOperator(cancellationToken)) { + authorization.ReportUnauthorizedError(resolverContext); return null; } return await byId diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 50ca1d98..8f2423cd 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -56,4 +56,4 @@ IInterfaceTypeDescriptor descriptor .Field(nameof(IData.CreatorId)[..^2].FirstCharToLower()) .Type>(); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs index bee24b31..b955f5bc 100644 --- a/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs +++ b/backend/src/GraphQl/DataX/GetHttpsResourceTree.cs @@ -36,10 +36,10 @@ CancellationToken cancellationToken ) { return ( - await byId + (await byId .With(resolverContext.GetQueryContext()) - .LoadRequiredAsync(data.Id, cancellationToken) - ) + .LoadAsync(data.Id, cancellationToken) + ) ?? []) .Select(_ => new GetHttpsResourceTreeNonRootVertex(_) ) diff --git a/backend/src/GraphQl/Extensions/PageExtensions.cs b/backend/src/GraphQl/Extensions/PageExtensions.cs index cdbe08dc..ec879931 100644 --- a/backend/src/GraphQl/Extensions/PageExtensions.cs +++ b/backend/src/GraphQl/Extensions/PageExtensions.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using System.Diagnostics.Contracts; using GreenDonut.Data; using HotChocolate.Types.Pagination; @@ -7,6 +8,7 @@ namespace Database.GraphQl.Extensions; // Inspired by https://github.com/ChilliCream/graphql-platform/blob/main/src/HotChocolate/Data/src/Data/Extensions/HotChocolatePaginationResultExtensions.cs public static class PageExtensions { + [Pure] public static async ValueTask GetTotalCountAsync( this Task?> pagePromise ) @@ -14,6 +16,7 @@ this Task?> pagePromise return (await pagePromise)?.TotalCount ?? 0; } + [Pure] public static async ValueTask GetPageInfoAsync( this Task?> pagePromise ) @@ -27,6 +30,7 @@ this Task?> pagePromise ); } + // [Pure] // public static async Task> ToConnectionAsync( // this Task> pagePromise, // Func, PageEntry, Task>> createEdgeAsync, @@ -38,6 +42,7 @@ this Task?> pagePromise // return await CreateConnectionAsync(await pagePromise, createEdgeAsync, createConnection); // } + // [Pure] // private static async Task> CreateConnectionAsync( // Page? page, // Func, PageEntry, Task>> createEdgeAsync, diff --git a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs index e2a1a926..88a2fdeb 100644 --- a/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs +++ b/backend/src/GraphQl/Extensions/ResolverContextExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.Contracts; using GreenDonut.Data; using HotChocolate.Data.Filters; using HotChocolate.Data.Sorting; @@ -9,6 +10,7 @@ public static class ResolverContextExtensions { // Inspired by https://github.com/ChilliCream/graphql-platform/blob/9ae7220205412203d0a941a6b0cc779e70b02b09/src/HotChocolate/Data/src/Data/QueryContextParameterExpressionBuilder.cs#L76-L86 // Using `QueryContext queryContext,` in resolvers starts up the projection engine producing many problems + [Pure] public static QueryContext GetQueryContext(this IResolverContext context) { var selection = context.Selection; @@ -22,6 +24,7 @@ public static QueryContext GetQueryContext(this IResolverContext context) } // Using `PagingArguments pagingArguments,` in resolvers results in the parameter `pagingArguments: PagingArgumentsInput` in the GraphQL schema + [Pure] public static PagingArguments GetPagingArguments(this IResolverContext context) { return new( diff --git a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs index c14e0b46..1df78ffc 100644 --- a/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs +++ b/backend/src/GraphQl/Filters/ScalarFilterInputTypes.cs @@ -154,6 +154,16 @@ protected override void Configure(IFilterInputTypeDescriptor descriptor) } } +public sealed class NonNegativeIntFilterInputType + : ExtendedComparableOperationFilterInputType +{ + protected override void Configure(IFilterInputTypeDescriptor descriptor) + { + descriptor.Name($"NonNegativeInt{GraphQlConstants.FilterInputSuffix}"); + base.Configure(descriptor); + } +} + public sealed class DecimalFilterInputType : ExtendedComparableOperationFilterInputType { diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 03d4d05f..226f6ce8 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -81,19 +81,9 @@ LinkGenerator linkGenerator { return new UriBuilder(appSettings.Uri) { - Path = linkGenerator.GetPathByAction( - controller: nameof(GetHttpsResourcesController), - action: nameof(GetHttpsResourcesController.Get), - values: getHttpsResource.FileExtension is null - ? new - { - id = getHttpsResource.Id - } - : new - { - id = getHttpsResource.Id, - extension = getHttpsResource.FileExtension - } + Path = linkGenerator.GetPathByRouteValues( + routeName: GetHttpsResourcesController.ConstructGetActionRouteName(getHttpsResource), + values: GetHttpsResourcesController.CreateGetActionRouteValues(getHttpsResource) ) }.Uri; } diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 25dd8dbf..7e66701c 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -17,7 +17,7 @@ IObjectTypeDescriptor descriptor descriptor .Field("locator") .Cost(0) - .ResolveWith(t => t.GetLocator(default!, default!)); + .ResolveWith(t => t.GetLocator(default!, default!, default!)); descriptor .Field(_ => _.FileName) .Ignore(); @@ -88,4 +88,4 @@ IObjectTypeDescriptor descriptor .Cost(3) .ResolveWith(_ => _.GetDataFormatAsync(default!, default!)); } -} +} \ No newline at end of file diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 3cf72139..f5435907 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -3195,7 +3195,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "CalorimetricDataId" = NEW.Id + AND "CalorimetricDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -3214,11 +3214,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("CalorimetricDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("CalorimetricDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3233,11 +3232,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("GeometricDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("GeometricDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3255,7 +3253,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "GeometricDataId" = NEW.Id + AND "GeometricDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -3311,11 +3309,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("HygrothermalDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("HygrothermalDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3333,7 +3330,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "HygrothermalDataId" = NEW.Id + AND "HygrothermalDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -3352,11 +3349,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("LifeCycleDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("LifeCycleDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3374,7 +3370,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "LifeCycleDataId" = NEW.Id + AND "LifeCycleDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -3393,11 +3389,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("OpticalDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("OpticalDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3415,7 +3410,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "OpticalDataId" = NEW.Id + AND "OpticalDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -3434,11 +3429,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("PhotovoltaicDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("PhotovoltaicDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -3456,7 +3450,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "PhotovoltaicDataId" = NEW.Id + AND "PhotovoltaicDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; diff --git a/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql b/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql index d6e15a21..32e187f0 100644 --- a/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql +++ b/backend/src/Migrations/migrate_from_20260609181841_EnsureDataAccessPolicyIsOnlyDeletedAlongsideItsData_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql @@ -52,7 +52,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "CalorimetricDataId" = NEW.Id + AND "CalorimetricDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -71,11 +71,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("CalorimetricDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("CalorimetricDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -90,11 +89,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_geometric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("GeometricDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("GeometricDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("GeometricDataId") WHERE "GeometricDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -112,7 +110,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "GeometricDataId" = NEW.Id + AND "GeometricDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -168,11 +166,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("HygrothermalDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("HygrothermalDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("HygrothermalDataId") WHERE "HygrothermalDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -190,7 +187,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "HygrothermalDataId" = NEW.Id + AND "HygrothermalDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -209,11 +206,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("LifeCycleDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("LifeCycleDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("LifeCycleDataId") WHERE "LifeCycleDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -231,7 +227,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "LifeCycleDataId" = NEW.Id + AND "LifeCycleDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -250,11 +246,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_optical_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("OpticalDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("OpticalDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("OpticalDataId") WHERE "OpticalDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -272,7 +267,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "OpticalDataId" = NEW.Id + AND "OpticalDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; @@ -291,11 +286,10 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql') THEN CREATE FUNCTION "database"."LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ BEGIN - INSERT INTO "data_access_policy" + INSERT INTO database."data_access_policy" ("PhotovoltaicDataId", "Combinator") VALUES (NEW."Id", 'all') - ON CONFLICT ("PhotovoltaicDataId") DO NOTHING; - RETURN NEW; + ON CONFLICT ("PhotovoltaicDataId") WHERE "PhotovoltaicDataId" IS NOT NULL DO NOTHING; RETURN NEW; END; $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql; @@ -313,7 +307,7 @@ BEGIN IF NOT EXISTS ( SELECT 1 FROM database."get_https_resource" WHERE "ParentId" IS NULL - AND "PhotovoltaicDataId" = NEW.Id + AND "PhotovoltaicDataId" = NEW."Id" ) THEN RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index 0b7f318f..4810a79a 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -7,12 +7,27 @@ using NodaTime; using Database.Data; using Database.ApiRequests; +using Microsoft.Extensions.Logging; namespace Database.Services; +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Information, + Message = "Applying data access policy for user '{UserId}', institutions '{InstitutionIds}', and OpenID Connect application '{OpenIdConnectClientId}'")] + internal static partial void ApplyingAccessPolicy( + this ILogger logger, + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId + ); +} + public sealed class AccessPolicyService( UserService userService, - IClock clock + IClock clock, + ILogger logger ) { public async Task Apply( @@ -23,17 +38,18 @@ CancellationToken cancellationToken ) where TData : class, IData { - var openIdConnectClientId = userService.GetOpenIdConnectAuthorizedPartyClientId(); + var openIdConnectClientId = userService.GetOpenIdConnectApplicationClientId(); var (currentUser, currentInstitution) = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); - IReadOnlyList? institutionIds = null; + Guid[]? institutionIds = null; if (currentUser is not null) { - institutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToList().AsReadOnly(); + institutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToArray(); } else if (currentInstitution is not null) { institutionIds = [currentInstitution.Uuid]; } + logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); var policedData = PoliceData(data, currentUser, institutionIds, openIdConnectClientId, databaseContext); var (postProcessedData, result) = await then(policedData); await IncrementAccessCounts( @@ -50,7 +66,7 @@ await IncrementAccessCounts( private static IQueryable PoliceData( IQueryable data, QueryCurrentUserOrInstitution.CurrentUser? currentUser, - IReadOnlyList? institutionIds, + Guid[]? institutionIds, string? openIdConnectClientId, ApplicationDbContext databaseContext ) @@ -75,7 +91,7 @@ private async Task IncrementAccessCounts( IReadOnlyList allData, string? openIdConnectClientId, QueryCurrentUserOrInstitution.CurrentUser? currentUser, - IReadOnlyList? institutionIds, + Guid[]? institutionIds, ApplicationDbContext databaseContext, CancellationToken cancellationToken ) diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index ae430a1f..948d071a 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -20,9 +20,14 @@ CacheService cacheService /// /// Get client ID from user claims. /// - public string? GetOpenIdConnectAuthorizedPartyClientId() + public string? GetOpenIdConnectApplicationClientId() { - return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); + // `client_id` (Standard OAuth2 Claim) is the client ID of the application that requested the access token + // `azp` (Authorized Party - Standard OpenID Connect Claim) is the the client ID of the application to which the ID token or access token was issued + // `oi_prst` (OpenIddict Presenter - Private Internal Claim) is OpenIddict's proprietary, internal equivalent to `client_id` or `azp` + return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.ClientId) + ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty) + ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.Private.Presenter); } public async Task SwitchUserOrInstitutionAsync( diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 9ce917bd..caa18502 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -265,7 +265,7 @@ type Query { calorimetricData(id: Uuid!, locale: Locale): CalorimetricData @cost(weight: "10") currentInstitution: CurrentInstitution @cost(weight: "10") - currentUser: User @cost(weight: "10") + currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") @@ -291,6 +291,7 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") + globalDataAccessPolicy: DataAccessPolicy @cost(weight: "10") hasCalorimetricData( locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10") @@ -317,18 +318,11 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") - institutionAccessPolicies: [InstitutionAccessPolicy!]! @cost(weight: "10") - institutionAccessPolicy(institutionId: Uuid!): InstitutionAccessPolicy - @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! - @cost(weight: "10") - openIdConnectApplicationAccessPolicy(clientId: String!): OpenIdConnectApplicationAccessPolicy - @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -352,15 +346,12 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") - userAccessPolicies: [UserAccessPolicy!]! @cost(weight: "10") - userAccessPolicy(userId: Uuid!): UserAccessPolicy @cost(weight: "10") verificationCode: String! } type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - clearDataAccessPolicies: ClearDataAccessPoliciesPayload! @cost(weight: "10") clearInstitutionAccessPolicies(input: ClearInstitutionAccessPoliciesInput!): ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") clearOpenIdConnectApplicationAccessPolicies( @@ -368,6 +359,14 @@ type Mutation { ): ClearOpenIdConnectApplicationAccessPoliciesPayload! @cost(weight: "10") clearUserAccessPolicies(input: ClearUserAccessPoliciesInput!): ClearUserAccessPoliciesPayload! @cost(weight: "10") + configureDataAccessPolicies( + input: ConfigureDataAccessPoliciesInput! + where: ConfigureDataAccessPoliciesPropositionInput @cost(weight: "10") + ): ConfigureDataAccessPoliciesPayload! @cost(weight: "10") + configureDataAccessPolicy( + input: ConfigureDataAccessPolicyInput! + where: ConfigureDataAccessPolicyPropositionInput @cost(weight: "10") + ): ConfigureDataAccessPolicyPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -394,6 +393,11 @@ type Mutation { ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + resetDataAccessPolicies( + where: ResetDataAccessPoliciesPropositionInput @cost(weight: "10") + ): ResetDataAccessPoliciesPayload! @cost(weight: "10") + resetDataAccessPolicy(input: ResetDataAccessPolicyInput!): ResetDataAccessPolicyPayload! + @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") @@ -404,8 +408,6 @@ type Mutation { ): SetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") setUserAccessPolicy(input: SetUserAccessPolicyInput!): SetUserAccessPolicyPayload! @cost(weight: "10") - unsetDataAccessPolicy(input: UnsetDataAccessPolicyInput!): UnsetDataAccessPolicyPayload! - @cost(weight: "10") unsetInstitutionAccessPolicy(input: UnsetInstitutionAccessPolicyInput!): UnsetInstitutionAccessPolicyPayload! @cost(weight: "10") unsetOpenIdConnectApplicationAccessPolicy( @@ -474,6 +476,7 @@ type CalorimetricData implements Node & Data { description: String gValues: [Float!]! id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -514,18 +517,6 @@ type CielabColor { observer: CalorimetricObserver } -type ClearDataAccessPoliciesError implements UserError { - code: ClearDataAccessPoliciesErrorCode! - message: String! - path: [String!]! -} - -type ClearDataAccessPoliciesPayload { - dataAccessPolicy: [DataAccessPolicy!] - errors: [ClearDataAccessPoliciesError!] - query: Query! -} - type ClearInstitutionAccessPoliciesError implements UserError { code: ClearInstitutionAccessPoliciesErrorCode! message: String! @@ -567,6 +558,30 @@ type Component { uuid: Uuid! } +type ConfigureDataAccessPoliciesError implements UserError { + code: ConfigureDataAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ConfigureDataAccessPoliciesPayload { + dataAccessPolicies: [DataAccessPolicy!] + errors: [ConfigureDataAccessPoliciesError!] + query: Query! +} + +type ConfigureDataAccessPolicyError implements UserError { + code: ConfigureDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ConfigureDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ConfigureDataAccessPolicyError!] + query: Query! +} + type CountSinceTime { accessCount: NonNegativeInt! startTime: DateTime @@ -684,6 +699,14 @@ type CurrentInstitution { uuid: Uuid! } +type CurrentUser { + databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnection! + isAtLeastAssistantManagerOfDatabaseOperator: Boolean! + name: String! + representedInstitutions: UserRepresentedInstitutionConnection! + uuid: Uuid! +} + type DataAccessPolicy { calorimetricData: CalorimetricData calorimetricDataId: Uuid @@ -696,21 +719,30 @@ type DataAccessPolicy { hygrothermalData: HygrothermalData hygrothermalDataId: Uuid id: Uuid! - institutionAccessPolicies: [InstitutionAccessPolicy!]! - isAllowed( + institutionAccessPolicies( + order: [InstitutionAccessPolicySortInput!] + where: InstitutionAccessPolicyPropositionInput + ): [InstitutionAccessPolicy!]! + isAccessAllowed( currentUser: CurrentUserInput institutionIds: [Uuid!] openIdConnectClientId: String ): Boolean! lifeCycleData: LifeCycleData lifeCycleDataId: Uuid - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + openIdConnectApplicationAccessPolicies( + order: [OpenIdConnectApplicationAccessPolicySortInput!] + where: OpenIdConnectApplicationAccessPolicyPropositionInput + ): [OpenIdConnectApplicationAccessPolicy!]! opticalData: OpticalData opticalDataId: Uuid photovoltaicData: PhotovoltaicData photovoltaicDataId: Uuid updatedAt: DateTime! - userAccessPolicies: [UserAccessPolicy!]! + userAccessPolicies( + order: [UserAccessPolicySortInput!] + where: UserAccessPolicyPropositionInput + ): [UserAccessPolicy!]! version: NonNegativeInt! } @@ -813,6 +845,7 @@ type GeometricData implements Node & Data { description: String heights: [Float!]! id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -847,6 +880,7 @@ type GeometricDataEdge { } type GetHttpsResource implements Node { + absoluteFilePath: String! appliedConversionMethod: ToTreeVertexAppliedConversionMethod archivedFilesMetaInformation: [FileMetaInformation!]! children: [GetHttpsResource!]! @cost(weight: "0") @@ -854,6 +888,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "0") dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! + dataKind: DataKind description: String doesFileExist: Boolean! hashValue: String! @@ -918,6 +953,7 @@ type HygrothermalData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -954,18 +990,32 @@ type Institution { uuid: Uuid! } -type InstitutionAccessPolicy { +type InstitutionAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! + id: ID! institutionId: Uuid! - isAllowed: Boolean! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration - version: NonNegativeInt! + uuid: Uuid! +} + +type InstitutionManagedInstitutionConnection { + edges: [InstitutionManagedInstitutionEdge!]! + totalCount: NonNegativeInt! +} + +type InstitutionManagedInstitutionEdge { + node: InstitutionManagedInstitutionNode! +} + +type InstitutionManagedInstitutionNode { + name: String! + uuid: Uuid! } type LifeCycleData implements Node & Data { @@ -982,6 +1032,7 @@ type LifeCycleData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -1039,18 +1090,18 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } -type OpenIdConnectApplicationAccessPolicy { +type OpenIdConnectApplicationAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime clientId: String! createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! - isAllowed: Boolean! + id: ID! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration - version: NonNegativeInt! + uuid: Uuid! } type OpticalData implements Node & Data { @@ -1071,6 +1122,7 @@ type OpticalData implements Node & Data { description: String id: ID! infraredEmittances: [Float!]! + kind: DataKind! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! @@ -1134,6 +1186,7 @@ type PhotovoltaicData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -1217,6 +1270,30 @@ type RemoveDataApprovalPayload { query: Query! } +type ResetDataAccessPoliciesError implements UserError { + code: ResetDataAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ResetDataAccessPoliciesPayload { + dataAccessPolicies: [DataAccessPolicy!] + errors: [ResetDataAccessPoliciesError!] + query: Query! +} + +type ResetDataAccessPolicyError implements UserError { + code: ResetDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ResetDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ResetDataAccessPolicyError!] + query: Query! +} + type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! @@ -1307,18 +1384,6 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } -type UnsetDataAccessPolicyError implements UserError { - code: UnsetDataAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type UnsetDataAccessPolicyPayload { - dataAccessPolicy: DataAccessPolicy - errors: [UnsetDataAccessPolicyError!] - query: Query! -} - type UnsetInstitutionAccessPolicyError implements UserError { code: UnsetInstitutionAccessPolicyErrorCode! message: String! @@ -1429,18 +1494,18 @@ type User implements Node { uuid: Uuid! } -type UserAccessPolicy { +type UserAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! - isAllowed: Boolean! + id: ID! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration userId: Uuid! - version: NonNegativeInt! + uuid: Uuid! } type UserInfo { @@ -1455,6 +1520,22 @@ type UserInfo { website: String } +type UserRepresentedInstitutionConnection { + edges: [UserRepresentedInstitutionEdge!]! + totalCount: NonNegativeInt! +} + +type UserRepresentedInstitutionEdge { + node: UserRepresentedInstitutionNode! + role: InstitutionRepresentativeRole! +} + +type UserRepresentedInstitutionNode { + managedInstitutions: InstitutionManagedInstitutionConnection! + name: String! + uuid: Uuid! +} + interface Approval { approverId: Uuid! keyFingerprint: String! @@ -1479,6 +1560,7 @@ interface Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -1624,6 +1706,52 @@ input ClosedIntervalInput { upperBound: Float! } +input ConfigureDataAccessPoliciesInput { + combinator: LogicalCombinator! +} + +input ConfigureDataAccessPoliciesPropositionInput { + and: [ConfigureDataAccessPoliciesPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ConfigureDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input ConfigureDataAccessPolicyInput { + combinator: LogicalCombinator! + data: DataReferenceInput +} + +input ConfigureDataAccessPolicyPropositionInput { + and: [ConfigureDataAccessPolicyPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ConfigureDataAccessPolicyPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input CountSinceTimePropositionInput { + accessCount: NonNegativeIntPropositionInput + and: [CountSinceTimePropositionInput!] + or: [CountSinceTimePropositionInput!] + startTime: DateTimePropositionInput +} + input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! componentId: Uuid! @@ -1759,6 +1887,21 @@ input CurrentUserInput { uuid: Uuid! } +input DataAccessPolicyPropositionInput { + and: [DataAccessPolicyPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [DataAccessPolicyPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -1908,6 +2051,13 @@ input FilterInputTypeOfGetHttpsResourcesPropositionInput { some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") } +input FilterInputTypeOfInstitutionAccessPolicysPropositionInput { + all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") +} + input FilterInputTypeOfNamedMethodArgumentsPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") @@ -1922,6 +2072,20 @@ input FilterInputTypeOfNamedMethodSourcesPropositionInput { some: NamedMethodSourcePropositionInput @cost(weight: "10") } +input FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput { + all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") +} + +input FilterInputTypeOfUserAccessPolicysPropositionInput { + all: UserAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: UserAccessPolicyPropositionInput @cost(weight: "10") + some: UserAccessPolicyPropositionInput @cost(weight: "10") +} + input FloatPropositionInput { equalTo: Float @cost(weight: "10") greaterThan: Float @cost(weight: "10") @@ -2057,9 +2221,12 @@ input HygrothermalDataSortInput { } input InstitutionAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] + dataAccessPolicy: DataAccessPolicyPropositionInput institutionId: UuidPropositionInput or: [InstitutionAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input InstitutionAccessPolicySortInput { @@ -2183,6 +2350,13 @@ input LocalTimePropositionInput { notLessThanOrEqualTo: LocalTime @cost(weight: "10") } +input LogicalCombinatorPropositionInput { + equalTo: LogicalCombinator @cost(weight: "10") + in: [LogicalCombinator!] @cost(weight: "10") + notEqualTo: LogicalCombinator @cost(weight: "10") + notIn: [LogicalCombinator!] @cost(weight: "10") +} + input LongPropositionInput { equalTo: Long @cost(weight: "10") greaterThan: Long @cost(weight: "10") @@ -2225,6 +2399,21 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NonNegativeIntPropositionInput { + equalTo: NonNegativeInt @cost(weight: "10") + greaterThan: NonNegativeInt @cost(weight: "10") + greaterThanOrEqualTo: NonNegativeInt @cost(weight: "10") + in: [NonNegativeInt] @cost(weight: "10") + lessThan: NonNegativeInt @cost(weight: "10") + lessThanOrEqualTo: NonNegativeInt @cost(weight: "10") + notEqualTo: NonNegativeInt @cost(weight: "10") + notGreaterThan: NonNegativeInt @cost(weight: "10") + notGreaterThanOrEqualTo: NonNegativeInt @cost(weight: "10") + notIn: [NonNegativeInt] @cost(weight: "10") + notLessThan: NonNegativeInt @cost(weight: "10") + notLessThanOrEqualTo: NonNegativeInt @cost(weight: "10") +} + input NullableOfCalorimetricObserverPropositionInput { equalTo: CalorimetricObserver @cost(weight: "10") in: [CalorimetricObserver] @cost(weight: "10") @@ -2281,9 +2470,12 @@ input NumerationSortInput { } input OpenIdConnectApplicationAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput + dataAccessPolicy: DataAccessPolicyPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input OpenIdConnectApplicationAccessPolicySortInput { @@ -2449,6 +2641,25 @@ input RemoveDataApprovalInput { signature: String! } +input ResetDataAccessPoliciesPropositionInput { + and: [ResetDataAccessPoliciesPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ResetDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input ResetDataAccessPolicyInput { + data: DataReferenceInput +} + input RetractDataInput { dataId: Uuid! dataKind: DataKind! @@ -2591,10 +2802,6 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } -input UnsetDataAccessPolicyInput { - data: DataReferenceInput -} - input UnsetInstitutionAccessPolicyInput { data: DataReferenceInput institutionId: Uuid! @@ -2664,6 +2871,13 @@ input UpperLimitPerDurationInput { upperLimit: NonNegativeInt! } +input UpperLimitPerDurationPropositionInput { + and: [UpperLimitPerDurationPropositionInput!] + duration: DurationPropositionInput + or: [UpperLimitPerDurationPropositionInput!] + upperLimit: NonNegativeIntPropositionInput +} + input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -2704,8 +2918,11 @@ input UrlPropositionInput { } input UserAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] + dataAccessPolicy: DataAccessPolicyPropositionInput or: [UserAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput } @@ -2783,32 +3000,25 @@ enum CalorimetricObserver { TEN_DEGREES } -enum ClearDataAccessPoliciesErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA -} - enum ClearInstitutionAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum ClearUserAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum CoatedSide { @@ -2820,6 +3030,19 @@ enum CoatedSide { NOT_APPLICABLE } +enum ConfigureDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ConfigureDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED @@ -3039,6 +3262,19 @@ enum RemoveDataApprovalErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum ResetDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ResetDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum RetractDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -3114,14 +3350,6 @@ enum Standardizer { WELL } -enum UnsetDataAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_POLICY -} - enum UnsetInstitutionAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED diff --git a/frontend/components/ActiveFilterAndSortBar.tsx b/frontend/components/ActiveFilterAndSortBar.tsx index 3a4da899..478d750d 100644 --- a/frontend/components/ActiveFilterAndSortBar.tsx +++ b/frontend/components/ActiveFilterAndSortBar.tsx @@ -4,7 +4,7 @@ import { getFilterOperatorLabel, createFilterStateReducer, FilterDefinition, - FilterStateReducerContext, + AnyFilterDefinition, } from "../lib/filter"; import { formatSortDirection, SortState, SortDefinition } from "../lib/sort"; import DeleteButton from "./DeleteButton"; @@ -33,11 +33,11 @@ const stringifyFilter = ( createFilterStateReducer( (scalar) => `${getFilterOperatorLabel(scalar.operator)}|${JSON.stringify(scalar.value)}`, - (list, stringify) => - `${getFilterOperatorLabel(list.operator)}|${stringify(list.value)}`, - (object, context, stringify) => - `${String(context[object.index].field)}|${stringify(object.value)}`, - )(filter, definitions as FilterStateReducerContext); + (list, _, stringifyValue) => + `${getFilterOperatorLabel(list.operator)}|${stringifyValue()}`, + (object, context, stringifyValue) => + `${String(context.items[object.index].field)}|${stringifyValue()}`, + )(filter, definitions as AnyFilterDefinition[]); const renderFilter = ( value: ObjectFilterState, @@ -52,17 +52,17 @@ const renderFilter = ( : scalar.value} ), - (list, render) => ( + (list, _, renderValue) => ( <> - {getFilterOperatorLabel(list.operator)} {render(list.value)} + {getFilterOperatorLabel(list.operator)} {renderValue()} ), - (object, context, render) => ( + (object, context, renderValue) => ( <> - {getLabel(context[object.index], "none-upper")} {render(object.value)} + {getLabel(context.items[object.index], "none-upper")} {renderValue()} ), - )(value, definitions as FilterStateReducerContext); + )(value, definitions as AnyFilterDefinition[]); export default function ActiveFilterAndSortBar({ values, diff --git a/frontend/components/Layout.tsx b/frontend/components/Layout.tsx index 3ffccb4a..a8501103 100644 --- a/frontend/components/Layout.tsx +++ b/frontend/components/Layout.tsx @@ -13,23 +13,23 @@ const navItems = [ }, { path: paths.allCalorimetricData, - label: "Calorimetric Data", + label: "Calorimetric", }, { path: paths.allGeometricData, - label: "Geometric Data", + label: "Geometric", }, { path: paths.allHygrothermalData, - label: "Hygrothermal Data", + label: "Hygrothermal", }, { path: paths.allLifeCycleData, - label: "Life-Cycle Data", + label: "Life-Cycle", }, { path: paths.allOpticalData, - label: "Optical Data", + label: "Optical", }, { path: paths.allPhotovoltaicData, diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 575baf2d..27b8ad4d 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -11,6 +11,7 @@ import { import { extractAntiforgeryTokenFromCookie } from "../lib/apollo"; import { Route } from "next"; import { CSSProperties, useMemo } from "react"; +import { isTruthy } from "../lib/array"; const firstUserOrLoginItemStyle = { marginLeft: "auto" }; @@ -71,11 +72,11 @@ const userItems = (currentUser: CurrentUserPartialFragment) => [ key: paths.userInfo, label: User Info, }, - { + currentUser.isAtLeastAssistantManagerOfDatabaseOperator && { key: paths.createData, label: Create Data, }, - { + currentUser.isAtLeastAssistantManagerOfDatabaseOperator && { key: paths.uploadFile, label: Upload File, }, @@ -105,7 +106,7 @@ const userItems = (currentUser: CurrentUserPartialFragment) => [ ), }, - ], + ].filter(isTruthy), }, ]; diff --git a/frontend/components/data/DataSummary.tsx b/frontend/components/data/DataSummary.tsx index d3a00555..f8f71507 100644 --- a/frontend/components/data/DataSummary.tsx +++ b/frontend/components/data/DataSummary.tsx @@ -194,6 +194,7 @@ export default function DataSummary({ {selectedResource.value.locator} @@ -258,9 +259,9 @@ export default function DataSummary({ <>
    - - {entity.approvals.map((approval) => ( -
    + + {entity.approvals.map((approval, index) => ( +
    The institution{" "}
    + GraphQL endpoint located at{" "} {entity.locator} diff --git a/frontend/components/entities/EntitySummary.tsx b/frontend/components/entities/EntitySummary.tsx index e4bc37f9..a0247b2b 100644 --- a/frontend/components/entities/EntitySummary.tsx +++ b/frontend/components/entities/EntitySummary.tsx @@ -77,7 +77,7 @@ export default function EntitySummary({ Copy Permalink
    - + {route ? ( diff --git a/frontend/lib/filter.ts b/frontend/lib/filter.ts index 11101328..e1b5e23a 100644 --- a/frontend/lib/filter.ts +++ b/frontend/lib/filter.ts @@ -325,7 +325,7 @@ type FieldFilterDefinition< | ListFilterDefinition | ObjectFilterDefinition; -type AnyFilterDefinition = FieldFilterDefinition; +export type AnyFilterDefinition = FieldFilterDefinition; export type FilterDefinition = { [TKey in keyof TPropositionInput]: FieldFilterDefinition< @@ -373,47 +373,64 @@ const isObjectFilterState = ( return value.operator === undefined; }; -export type FilterStateReducerContext = readonly AnyFilterDefinition[]; - export const createFilterStateReducer = ( reduceScalar: ( scalar: ScalarFilterState, - context: FilterStateReducerContext, + context: ScalarFilterDefinition, ) => TOutput, reduceList: ( list: ListFilterState, - reduce: (filter: FilterState) => TOutput, + context: ListFilterDefinition, + reduce: () => TOutput, ) => TOutput, reduceObject: ( object: ObjectFilterState, - context: FilterStateReducerContext, - reduce: (filter: FilterState) => TOutput, + context: ObjectFilterDefinition, + reduce: () => TOutput, ) => TOutput, ) => { return ( root: ObjectFilterState, - initialContext: FilterStateReducerContext, + rootFilterDefinitions: readonly AnyFilterDefinition[], ): TOutput => { const reduce = ( filter: FilterState, - context: FilterStateReducerContext, + context: AnyFilterDefinition, ): TOutput => { if (isScalarFilterState(filter)) { + if (context.type == "list" || context.type == "object") { + console.error("Filter: ", filter, "Context: ", context); + throw Error(`Expected scalar filter definition, got ${context.type}`); + } return reduceScalar(filter, context); } if (isListFilterState(filter)) { - return reduceList(filter, (x) => reduce(x, context)); + if (context.type != "list") { + console.error("Filter: ", filter, "Context: ", context); + throw Error(`Expected list filter definition, got ${context.type}`); + } + return reduceList(filter, context, () => + reduce(filter.value, context.item), + ); } if (isObjectFilterState(filter)) { - const subContext = ( - context[filter.index] as ObjectFilterDefinition - ).items; - return reduceObject(filter, context, (x) => reduce(x, subContext)); + if (context.type != "object") { + console.error("Filter: ", filter, "Context: ", context); + throw Error(`Expected object filter definition, got ${context.type}`); + } + return reduceObject(filter, context, () => + reduce(filter.value, context.items[filter.index]), + ); } return assertNever(filter); }; - return reduceObject(root, initialContext, (object) => - reduce(object, initialContext), + const rootFilterDefinition: ObjectFilterDefinition = { + type: "object", + field: "root", + items: rootFilterDefinitions as FilterDefinition[], + }; + return reduceObject(root, rootFilterDefinition, () => + reduce(root.value, rootFilterDefinition.items[root.index]), ); }; }; @@ -426,13 +443,13 @@ export const createFilterDefinitionReducer = < reduceScalar: (scalar: ScalarFilterDefinition) => TScalarOutput, reduceList: ( list: ListFilterDefinition, - reduce: ( + reduceValue: ( filter: AnyFilterDefinition, ) => TScalarOutput | TListOutput | TObjectOutput, ) => TListOutput, reduceObject: ( object: ObjectFilterDefinition, - reduce: ( + reduceValue: ( filter: AnyFilterDefinition, ) => TScalarOutput | TListOutput | TObjectOutput, ) => TObjectOutput, @@ -461,8 +478,8 @@ export const createFilterDefinitionReducer = < // to GraphQL filter input, for example, to `{ name: { contains: "Simon" } }` export const toWhereClause = ( - filter: ObjectFilterState, - configs: readonly FilterDefinition[], + root: ObjectFilterState, + rootFilterDefinitions: readonly FilterDefinition[], ): TPropositionInput => // the initial context type is `readonly FilterDefinition[]` and subsequent // types are of the form `readonly FilterDefinition[]` @@ -470,13 +487,13 @@ export const toWhereClause = ( (scalar, _) => ({ [scalar.operator]: scalar.value, }), - (list, reduce) => ({ - [list.operator]: reduce(list.value), + (list, _, reduceValue) => ({ + [list.operator]: reduceValue(), }), - (object, context, reduce) => ({ - [context[object.index].field]: reduce(object.value), + (object, context, reduceValue) => ({ + [context.items[object.index].field]: reduceValue(), }), - )(filter, configs as FilterStateReducerContext) as TPropositionInput; + )(root, rootFilterDefinitions as AnyFilterDefinition[]) as TPropositionInput; export const getInitialFilterSubformValues = ( filterDefinition: AnyFilterDefinition, diff --git a/frontend/queries/currentUser.graphql b/frontend/queries/currentUser.graphql index 48add4e8..fca966c0 100644 --- a/frontend/queries/currentUser.graphql +++ b/frontend/queries/currentUser.graphql @@ -1,6 +1,7 @@ -fragment CurrentUserPartial on User { - subject +fragment CurrentUserPartial on CurrentUser { + uuid name + isAtLeastAssistantManagerOfDatabaseOperator } query CurrentUser { @@ -24,3 +25,19 @@ query CurrentUserInfo { website } } + +# Force usage of +# `import type * as Types from '../__generated__/graphql';` +# in +# `./currentUser.generated.ts` +# to prevent TypeScript error +# `'Types' is declared but its value is never read.` +query ZZZDeleteDataZZZ($input: DeleteDataInput!) { + deleteData(input: $input) { + errors { + code + message + path + } + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 9ce917bd..2b06e361 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -265,7 +265,7 @@ type Query { calorimetricData(id: Uuid!, locale: Locale): CalorimetricData @cost(weight: "10") currentInstitution: CurrentInstitution @cost(weight: "10") - currentUser: User @cost(weight: "10") + currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") database: Database! @cost(weight: "10") @@ -291,6 +291,7 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") + globalDataAccessPolicy: DataAccessPolicy @cost(weight: "10") hasCalorimetricData( locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10") @@ -317,18 +318,11 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") - institutionAccessPolicies: [InstitutionAccessPolicy!]! @cost(weight: "10") - institutionAccessPolicy(institutionId: Uuid!): InstitutionAccessPolicy - @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! - @cost(weight: "10") - openIdConnectApplicationAccessPolicy(clientId: String!): OpenIdConnectApplicationAccessPolicy - @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -352,15 +346,12 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") - userAccessPolicies: [UserAccessPolicy!]! @cost(weight: "10") - userAccessPolicy(userId: Uuid!): UserAccessPolicy @cost(weight: "10") verificationCode: String! } type Mutation { addDataApproval(input: AddDataApprovalInput!): AddDataApprovalPayload! @cost(weight: "10") - clearDataAccessPolicies: ClearDataAccessPoliciesPayload! @cost(weight: "10") clearInstitutionAccessPolicies(input: ClearInstitutionAccessPoliciesInput!): ClearInstitutionAccessPoliciesPayload! @cost(weight: "10") clearOpenIdConnectApplicationAccessPolicies( @@ -368,6 +359,14 @@ type Mutation { ): ClearOpenIdConnectApplicationAccessPoliciesPayload! @cost(weight: "10") clearUserAccessPolicies(input: ClearUserAccessPoliciesInput!): ClearUserAccessPoliciesPayload! @cost(weight: "10") + configureDataAccessPolicies( + input: ConfigureDataAccessPoliciesInput! + where: ConfigureDataAccessPoliciesPropositionInput @cost(weight: "10") + ): ConfigureDataAccessPoliciesPayload! @cost(weight: "10") + configureDataAccessPolicy( + input: ConfigureDataAccessPolicyInput! + where: ConfigureDataAccessPolicyPropositionInput @cost(weight: "10") + ): ConfigureDataAccessPolicyPayload! @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -394,6 +393,11 @@ type Mutation { ): RecomputeGetHttpsResourceHashValuesPayload! @cost(weight: "10") removeDataApproval(input: RemoveDataApprovalInput!): RemoveDataApprovalPayload! @cost(weight: "10") + resetDataAccessPolicies( + where: ResetDataAccessPoliciesPropositionInput @cost(weight: "10") + ): ResetDataAccessPoliciesPayload! @cost(weight: "10") + resetDataAccessPolicy(input: ResetDataAccessPolicyInput!): ResetDataAccessPolicyPayload! + @cost(weight: "10") retractData(input: RetractDataInput!): RetractDataPayload! @cost(weight: "10") setGetHttpsResourceParent(input: SetGetHttpsResourceParentInput!): SetGetHttpsResourceParentPayload! @cost(weight: "10") @@ -404,8 +408,6 @@ type Mutation { ): SetOpenIdConnectApplicationAccessPolicyPayload! @cost(weight: "10") setUserAccessPolicy(input: SetUserAccessPolicyInput!): SetUserAccessPolicyPayload! @cost(weight: "10") - unsetDataAccessPolicy(input: UnsetDataAccessPolicyInput!): UnsetDataAccessPolicyPayload! - @cost(weight: "10") unsetInstitutionAccessPolicy(input: UnsetInstitutionAccessPolicyInput!): UnsetInstitutionAccessPolicyPayload! @cost(weight: "10") unsetOpenIdConnectApplicationAccessPolicy( @@ -474,6 +476,7 @@ type CalorimetricData implements Node & Data { description: String gValues: [Float!]! id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -514,18 +517,6 @@ type CielabColor { observer: CalorimetricObserver } -type ClearDataAccessPoliciesError implements UserError { - code: ClearDataAccessPoliciesErrorCode! - message: String! - path: [String!]! -} - -type ClearDataAccessPoliciesPayload { - dataAccessPolicy: [DataAccessPolicy!] - errors: [ClearDataAccessPoliciesError!] - query: Query! -} - type ClearInstitutionAccessPoliciesError implements UserError { code: ClearInstitutionAccessPoliciesErrorCode! message: String! @@ -567,6 +558,30 @@ type Component { uuid: Uuid! } +type ConfigureDataAccessPoliciesError implements UserError { + code: ConfigureDataAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ConfigureDataAccessPoliciesPayload { + dataAccessPolicies: [DataAccessPolicy!] + errors: [ConfigureDataAccessPoliciesError!] + query: Query! +} + +type ConfigureDataAccessPolicyError implements UserError { + code: ConfigureDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ConfigureDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ConfigureDataAccessPolicyError!] + query: Query! +} + type CountSinceTime { accessCount: NonNegativeInt! startTime: DateTime @@ -684,6 +699,14 @@ type CurrentInstitution { uuid: Uuid! } +type CurrentUser { + databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnection! + isAtLeastAssistantManagerOfDatabaseOperator: Boolean! + name: String! + representedInstitutions: UserRepresentedInstitutionConnection! + uuid: Uuid! +} + type DataAccessPolicy { calorimetricData: CalorimetricData calorimetricDataId: Uuid @@ -696,21 +719,30 @@ type DataAccessPolicy { hygrothermalData: HygrothermalData hygrothermalDataId: Uuid id: Uuid! - institutionAccessPolicies: [InstitutionAccessPolicy!]! - isAllowed( + institutionAccessPolicies( + order: [InstitutionAccessPolicySortInput!] + where: InstitutionAccessPolicyPropositionInput + ): [InstitutionAccessPolicy!]! + isAccessAllowed( currentUser: CurrentUserInput institutionIds: [Uuid!] openIdConnectClientId: String ): Boolean! lifeCycleData: LifeCycleData lifeCycleDataId: Uuid - openIdConnectApplicationAccessPolicies: [OpenIdConnectApplicationAccessPolicy!]! + openIdConnectApplicationAccessPolicies( + order: [OpenIdConnectApplicationAccessPolicySortInput!] + where: OpenIdConnectApplicationAccessPolicyPropositionInput + ): [OpenIdConnectApplicationAccessPolicy!]! opticalData: OpticalData opticalDataId: Uuid photovoltaicData: PhotovoltaicData photovoltaicDataId: Uuid updatedAt: DateTime! - userAccessPolicies: [UserAccessPolicy!]! + userAccessPolicies( + order: [UserAccessPolicySortInput!] + where: UserAccessPolicyPropositionInput + ): [UserAccessPolicy!]! version: NonNegativeInt! } @@ -813,6 +845,7 @@ type GeometricData implements Node & Data { description: String heights: [Float!]! id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -847,6 +880,7 @@ type GeometricDataEdge { } type GetHttpsResource implements Node { + absoluteFilePath: String! appliedConversionMethod: ToTreeVertexAppliedConversionMethod archivedFilesMetaInformation: [FileMetaInformation!]! children: [GetHttpsResource!]! @cost(weight: "0") @@ -854,6 +888,7 @@ type GetHttpsResource implements Node { data: Data @cost(weight: "0") dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! + dataKind: DataKind description: String doesFileExist: Boolean! hashValue: String! @@ -918,6 +953,7 @@ type HygrothermalData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -961,13 +997,27 @@ type InstitutionAccessPolicy { dataAccessPolicyId: Uuid! id: Uuid! institutionId: Uuid! - isAllowed: Boolean! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration version: NonNegativeInt! } +type InstitutionManagedInstitutionConnection { + edges: [InstitutionManagedInstitutionEdge!]! + totalCount: NonNegativeInt! +} + +type InstitutionManagedInstitutionEdge { + node: InstitutionManagedInstitutionNode! +} + +type InstitutionManagedInstitutionNode { + name: String! + uuid: Uuid! +} + type LifeCycleData implements Node & Data { accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! @@ -982,6 +1032,7 @@ type LifeCycleData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -1046,7 +1097,7 @@ type OpenIdConnectApplicationAccessPolicy { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: Uuid! - isAllowed: Boolean! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration @@ -1071,6 +1122,7 @@ type OpticalData implements Node & Data { description: String id: ID! infraredEmittances: [Float!]! + kind: DataKind! locale: Locale! name: String nearnormalHemisphericalSolarReflectances: [Float!]! @@ -1134,6 +1186,7 @@ type PhotovoltaicData implements Node & Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources( @@ -1217,6 +1270,30 @@ type RemoveDataApprovalPayload { query: Query! } +type ResetDataAccessPoliciesError implements UserError { + code: ResetDataAccessPoliciesErrorCode! + message: String! + path: [String!]! +} + +type ResetDataAccessPoliciesPayload { + dataAccessPolicies: [DataAccessPolicy!] + errors: [ResetDataAccessPoliciesError!] + query: Query! +} + +type ResetDataAccessPolicyError implements UserError { + code: ResetDataAccessPolicyErrorCode! + message: String! + path: [String!]! +} + +type ResetDataAccessPolicyPayload { + dataAccessPolicy: DataAccessPolicy + errors: [ResetDataAccessPolicyError!] + query: Query! +} + type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! @@ -1307,18 +1384,6 @@ type ToTreeVertexAppliedConversionMethod { sourceName: String! } -type UnsetDataAccessPolicyError implements UserError { - code: UnsetDataAccessPolicyErrorCode! - message: String! - path: [String!]! -} - -type UnsetDataAccessPolicyPayload { - dataAccessPolicy: DataAccessPolicy - errors: [UnsetDataAccessPolicyError!] - query: Query! -} - type UnsetInstitutionAccessPolicyError implements UserError { code: UnsetInstitutionAccessPolicyErrorCode! message: String! @@ -1435,7 +1500,7 @@ type UserAccessPolicy { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: Uuid! - isAllowed: Boolean! + isAccessAllowed: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration @@ -1455,6 +1520,22 @@ type UserInfo { website: String } +type UserRepresentedInstitutionConnection { + edges: [UserRepresentedInstitutionEdge!]! + totalCount: NonNegativeInt! +} + +type UserRepresentedInstitutionEdge { + node: UserRepresentedInstitutionNode! + role: InstitutionRepresentativeRole! +} + +type UserRepresentedInstitutionNode { + managedInstitutions: InstitutionManagedInstitutionConnection! + name: String! + uuid: Uuid! +} + interface Approval { approverId: Uuid! keyFingerprint: String! @@ -1479,6 +1560,7 @@ interface Data { databaseId: Uuid! description: String id: ID! + kind: DataKind! locale: Locale! name: String resources: [GetHttpsResource!]! @@ -1624,6 +1706,52 @@ input ClosedIntervalInput { upperBound: Float! } +input ConfigureDataAccessPoliciesInput { + combinator: LogicalCombinator! +} + +input ConfigureDataAccessPoliciesPropositionInput { + and: [ConfigureDataAccessPoliciesPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ConfigureDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input ConfigureDataAccessPolicyInput { + combinator: LogicalCombinator! + data: DataReferenceInput +} + +input ConfigureDataAccessPolicyPropositionInput { + and: [ConfigureDataAccessPolicyPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ConfigureDataAccessPolicyPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input CountSinceTimePropositionInput { + accessCount: NonNegativeIntPropositionInput + and: [CountSinceTimePropositionInput!] + or: [CountSinceTimePropositionInput!] + startTime: DateTimePropositionInput +} + input CreateCalorimetricDataInput { appliedMethod: AppliedMethodInput! componentId: Uuid! @@ -1759,6 +1887,21 @@ input CurrentUserInput { uuid: Uuid! } +input DataAccessPolicyPropositionInput { + and: [DataAccessPolicyPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [DataAccessPolicyPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -1908,6 +2051,13 @@ input FilterInputTypeOfGetHttpsResourcesPropositionInput { some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") } +input FilterInputTypeOfInstitutionAccessPolicysPropositionInput { + all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") +} + input FilterInputTypeOfNamedMethodArgumentsPropositionInput { all: NamedMethodArgumentPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") @@ -1922,6 +2072,20 @@ input FilterInputTypeOfNamedMethodSourcesPropositionInput { some: NamedMethodSourcePropositionInput @cost(weight: "10") } +input FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput { + all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") +} + +input FilterInputTypeOfUserAccessPolicysPropositionInput { + all: UserAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: UserAccessPolicyPropositionInput @cost(weight: "10") + some: UserAccessPolicyPropositionInput @cost(weight: "10") +} + input FloatPropositionInput { equalTo: Float @cost(weight: "10") greaterThan: Float @cost(weight: "10") @@ -2057,9 +2221,12 @@ input HygrothermalDataSortInput { } input InstitutionAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] + dataAccessPolicy: DataAccessPolicyPropositionInput institutionId: UuidPropositionInput or: [InstitutionAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input InstitutionAccessPolicySortInput { @@ -2183,6 +2350,13 @@ input LocalTimePropositionInput { notLessThanOrEqualTo: LocalTime @cost(weight: "10") } +input LogicalCombinatorPropositionInput { + equalTo: LogicalCombinator @cost(weight: "10") + in: [LogicalCombinator!] @cost(weight: "10") + notEqualTo: LogicalCombinator @cost(weight: "10") + notIn: [LogicalCombinator!] @cost(weight: "10") +} + input LongPropositionInput { equalTo: Long @cost(weight: "10") greaterThan: Long @cost(weight: "10") @@ -2225,6 +2399,21 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NonNegativeIntPropositionInput { + equalTo: NonNegativeInt @cost(weight: "10") + greaterThan: NonNegativeInt @cost(weight: "10") + greaterThanOrEqualTo: NonNegativeInt @cost(weight: "10") + in: [NonNegativeInt] @cost(weight: "10") + lessThan: NonNegativeInt @cost(weight: "10") + lessThanOrEqualTo: NonNegativeInt @cost(weight: "10") + notEqualTo: NonNegativeInt @cost(weight: "10") + notGreaterThan: NonNegativeInt @cost(weight: "10") + notGreaterThanOrEqualTo: NonNegativeInt @cost(weight: "10") + notIn: [NonNegativeInt] @cost(weight: "10") + notLessThan: NonNegativeInt @cost(weight: "10") + notLessThanOrEqualTo: NonNegativeInt @cost(weight: "10") +} + input NullableOfCalorimetricObserverPropositionInput { equalTo: CalorimetricObserver @cost(weight: "10") in: [CalorimetricObserver] @cost(weight: "10") @@ -2281,9 +2470,12 @@ input NumerationSortInput { } input OpenIdConnectApplicationAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput + dataAccessPolicy: DataAccessPolicyPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input OpenIdConnectApplicationAccessPolicySortInput { @@ -2449,6 +2641,25 @@ input RemoveDataApprovalInput { signature: String! } +input ResetDataAccessPoliciesPropositionInput { + and: [ResetDataAccessPoliciesPropositionInput!] + calorimetricData: CalorimetricDataPropositionInput + combinator: LogicalCombinatorPropositionInput + geometricData: GeometricDataPropositionInput + hygrothermalData: HygrothermalDataPropositionInput + institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + lifeCycleData: LifeCycleDataPropositionInput + openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + opticalData: OpticalDataPropositionInput + or: [ResetDataAccessPoliciesPropositionInput!] + photovoltaicData: PhotovoltaicDataPropositionInput + userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput +} + +input ResetDataAccessPolicyInput { + data: DataReferenceInput +} + input RetractDataInput { dataId: Uuid! dataKind: DataKind! @@ -2591,10 +2802,6 @@ input ToTreeVertexAppliedConversionMethodSortInput { sourceName: SortEnumType @cost(weight: "10") } -input UnsetDataAccessPolicyInput { - data: DataReferenceInput -} - input UnsetInstitutionAccessPolicyInput { data: DataReferenceInput institutionId: Uuid! @@ -2664,6 +2871,13 @@ input UpperLimitPerDurationInput { upperLimit: NonNegativeInt! } +input UpperLimitPerDurationPropositionInput { + and: [UpperLimitPerDurationPropositionInput!] + duration: DurationPropositionInput + or: [UpperLimitPerDurationPropositionInput!] + upperLimit: NonNegativeIntPropositionInput +} + input UriSortInput { absolutePath: SortEnumType @cost(weight: "10") absoluteUri: SortEnumType @cost(weight: "10") @@ -2704,8 +2918,11 @@ input UrlPropositionInput { } input UserAccessPolicyPropositionInput { + accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] + dataAccessPolicy: DataAccessPolicyPropositionInput or: [UserAccessPolicyPropositionInput!] + upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput } @@ -2783,32 +3000,25 @@ enum CalorimetricObserver { TEN_DEGREES } -enum ClearDataAccessPoliciesErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA -} - enum ClearInstitutionAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum ClearOpenIdConnectApplicationAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum ClearUserAccessPoliciesErrorCode { UNKNOWN UNAUTHENTICATED UNAUTHORIZED - UNKNOWN_DATA_POLICY + UNKNOWN_DATA } enum CoatedSide { @@ -2820,6 +3030,19 @@ enum CoatedSide { NOT_APPLICABLE } +enum ConfigureDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ConfigureDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum CreateCalorimetricDataErrorCode { UNKNOWN UNAUTHORIZED @@ -3039,6 +3262,19 @@ enum RemoveDataApprovalErrorCode { CREATING_RESPONSE_APPROVAL_FAILED } +enum ResetDataAccessPoliciesErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED +} + +enum ResetDataAccessPolicyErrorCode { + UNKNOWN + UNAUTHENTICATED + UNAUTHORIZED + UNKNOWN_DATA +} + enum RetractDataErrorCode { UNAUTHENTICATED UNAUTHORIZED @@ -3114,14 +3350,6 @@ enum Standardizer { WELL } -enum UnsetDataAccessPolicyErrorCode { - UNKNOWN - UNAUTHENTICATED - UNAUTHORIZED - UNKNOWN_DATA - UNKNOWN_POLICY -} - enum UnsetInstitutionAccessPolicyErrorCode { UNKNOWN UNAUTHENTICATED From 9e429632316c33a7a70a01336c2d19c4a6e57279 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 15 Jun 2026 16:07:25 +0200 Subject: [PATCH 822/878] Add GraphQL fields to check access policies --- .../Data/AccessPolicies/AccessPolicyBase.cs | 5 +++- .../Data/AccessPolicies/DataAccessPolicy.cs | 29 +++++++------------ .../AccessPolicies/InstitutionAccessPolicy.cs | 7 +++++ .../OpenIdConnectApplicationAccessPolicy.cs | 7 +++++ .../Data/AccessPolicies/UserAccessPolicy.cs | 8 +++++ .../AccessPolicies/AccessPolicyTypeBase.cs | 2 +- 6 files changed, 37 insertions(+), 21 deletions(-) diff --git a/backend/src/Data/AccessPolicies/AccessPolicyBase.cs b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs index 6bbb8628..29d9a890 100644 --- a/backend/src/Data/AccessPolicies/AccessPolicyBase.cs +++ b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs @@ -14,7 +14,10 @@ public abstract class AccessPolicyBase public CountSinceTime? AccessCountSinceStartTime { get; set; } [Projectable] - public bool IsAccessAllowed => UpperAccessLimitPerTimeDuration is null || ( + public bool IsAlwaysAllowed => UpperAccessLimitPerTimeDuration is null; + + [Projectable] + public bool IsWithinAccessLimitInTimeSpan => UpperAccessLimitPerTimeDuration is null || ( UpperAccessLimitPerTimeDuration.UpperLimit > 0 && ( AccessCountSinceStartTime is null || ( IsWithinTimeSpan diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index c083ec70..b63bdb7e 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -149,10 +149,13 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(OpenIdConnectApplicationAccessPolicy.DataAccessPolicy))] public ICollection OpenIdConnectApplicationAccessPolicies { get; } = []; + [Projectable] + public bool IsAnyoneAllowed => IsAccessAllowed(null, null, null); + [Projectable] public bool IsAccessAllowed( QueryCurrentUserOrInstitution.CurrentUser? currentUser, - IReadOnlyList? institutionIds, + Guid[]? institutionIds, string? openIdConnectClientId ) => ( @@ -160,27 +163,21 @@ public bool IsAccessAllowed( ( UserAccessPolicies.Count == 0 || UserAccessPolicies.Any(_ => - currentUser != null - && _.UserId == currentUser.Uuid - && _.IsAccessAllowed + _.IsAccessAllowed(currentUser) ) ) && ( InstitutionAccessPolicies.Count == 0 || InstitutionAccessPolicies.Any(_ => - institutionIds != null - && institutionIds.Contains(_.InstitutionId) - && _.IsAccessAllowed + _.IsAccessAllowed(institutionIds) ) ) && ( OpenIdConnectApplicationAccessPolicies.Count == 0 || OpenIdConnectApplicationAccessPolicies.Any(_ => - openIdConnectClientId != null - && _.ClientId == openIdConnectClientId - && _.IsAccessAllowed + _.IsAccessAllowed(openIdConnectClientId) ) ) ) @@ -189,21 +186,15 @@ public bool IsAccessAllowed( ( Combinator == LogicalCombinator.SOME && ( UserAccessPolicies.Any(_ => - currentUser != null - && _.UserId == currentUser.Uuid - && _.IsAccessAllowed + _.IsAccessAllowed(currentUser) ) || InstitutionAccessPolicies.Any(_ => - institutionIds != null - && institutionIds.Contains(_.InstitutionId) - && _.IsAccessAllowed + _.IsAccessAllowed(institutionIds) ) || OpenIdConnectApplicationAccessPolicies.Any(_ => - openIdConnectClientId != null - && _.ClientId == openIdConnectClientId - && _.IsAccessAllowed + _.IsAccessAllowed(openIdConnectClientId) ) ) ); diff --git a/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs index 6fed9c72..89591565 100644 --- a/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/InstitutionAccessPolicy.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; namespace Database.Data.AccessPolicies; @@ -15,4 +16,10 @@ Guid institutionId public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; public Guid InstitutionId { get; private set; } = institutionId; + + [Projectable] + public bool IsAccessAllowed(Guid[]? institutionIds) => + institutionIds != null + && institutionIds.Contains(InstitutionId) + && IsWithinAccessLimitInTimeSpan; } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs index e12c6359..3931c904 100644 --- a/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/OpenIdConnectApplicationAccessPolicy.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using EntityFrameworkCore.Projectables; namespace Database.Data.AccessPolicies; @@ -15,4 +16,10 @@ string clientId public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; public string ClientId { get; private set; } = clientId; + + [Projectable] + public bool IsAccessAllowed(string? openIdConnectClientId) => + openIdConnectClientId != null + && ClientId == openIdConnectClientId + && IsWithinAccessLimitInTimeSpan; } \ No newline at end of file diff --git a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs index cae78ba1..6404eb74 100644 --- a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs @@ -1,5 +1,7 @@ using System; using System.ComponentModel.DataAnnotations.Schema; +using Database.ApiRequests; +using EntityFrameworkCore.Projectables; namespace Database.Data.AccessPolicies; @@ -15,4 +17,10 @@ Guid userId public Guid DataAccessPolicyId { get; private set; } = dataAccessPolicyId; public Guid UserId { get; private set; } = userId; + + [Projectable] + public bool IsAccessAllowed(QueryCurrentUserOrInstitution.CurrentUser? currentUser) => + currentUser != null + && UserId == currentUser.Uuid + && IsWithinAccessLimitInTimeSpan; } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs index 63922857..b49bc369 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyTypeBase.cs @@ -16,7 +16,7 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - // descriptor.Field(_ => _.IsAccessAllowed).Ignore(); + // descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan).Ignore(); // descriptor.Field(_ => _.IsWithinTimeSpan).Ignore(); } } \ No newline at end of file From 7229d6f874ee47fa839cc1680c1c665cc800961c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 15 Jun 2026 18:42:42 +0200 Subject: [PATCH 823/878] Add filterable queries for user, institution, and application access rights, describe how data access policies work, do not use internal client ID as fallback, and make data resolvable from a a data access policy --- .../Data/AccessPolicies/DataAccessPolicy.cs | 92 +++- backend/src/Data/GetHttpsResource.cs | 25 +- .../AccessPolicies/AccessPolicyQueries.cs | 111 ++++- .../DataAccessPolicyFilterType.cs | 2 + .../DataAccessPolicySortType.cs | 18 + .../AccessPolicies/DataAccessPolicyType.cs | 73 +++ ...nnectApplicationAccessPolicyFilterType.cs} | 2 +- ...ConnectApplicationAccessPolicySortType.cs} | 0 ...Input.cs => UserAccessPolicyFilterType.cs} | 0 backend/src/GraphQl/DataX/DataDataLoaders.cs | 48 ++ .../GetHttpsResourceFilterType.cs | 4 +- .../GetHttpsResourceResolvers.cs | 64 +-- .../GetHttpsResourceSortType.cs | 3 +- .../GetHttpsResources/GetHttpsResourceType.cs | 9 +- backend/src/Services/UserService.cs | 4 +- .../GraphQlSchemaTests.IsUnchanged.snap | 443 +++++++++++++++-- frontend/queries/currentUser.graphql | 2 +- frontend/type-defs.graphqls | 461 ++++++++++++++++-- 18 files changed, 1176 insertions(+), 185 deletions(-) create mode 100644 backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs rename backend/src/GraphQl/AccessPolicies/{OpenIdConnectApplicationAccessPolicyFilterInput.cs => OpenIdConnectApplicationAccessPolicyFilterType.cs} (91%) rename backend/src/GraphQl/AccessPolicies/{OpenIdConnectApplicationAccessPolicySortInput.cs => OpenIdConnectApplicationAccessPolicySortType.cs} (100%) rename backend/src/GraphQl/AccessPolicies/{UserAccessPolicyFilterInput.cs => UserAccessPolicyFilterType.cs} (100%) create mode 100644 backend/src/GraphQl/DataX/DataDataLoaders.cs diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index b63bdb7e..91f72283 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -6,10 +6,62 @@ using Database.ApiRequests; using Database.Enumerations; using Database.Extensions; +using Database.GraphQl.AccessPolicy; using EntityFrameworkCore.Projectables; +using HotChocolate; namespace Database.Data.AccessPolicies; +[GraphQLDescription($""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`{nameof(ConfigureDataAccessPolicyMutation)}`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `{nameof(DataAccessPolicy.Data)}`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""")] public sealed class DataAccessPolicy() : AuditableEntity { @@ -75,36 +127,44 @@ private void AssertThatExactlyOneDataIdIsNonNull() } // Note that for the global data access policy, all `*DataId`s are null - [NotMapped] [Projectable] public Guid? DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? null; - [NotMapped] public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; [Projectable] - public Guid? GetDataId(DataKind dataKind) => + public Database.Enumerations.DataKind? DataKind => + CalorimetricDataId != null ? Database.Enumerations.DataKind.CALORIMETRIC_DATA + : GeometricDataId != null ? Database.Enumerations.DataKind.GEOMETRIC_DATA + : HygrothermalDataId != null ? Database.Enumerations.DataKind.HYGROTHERMAL_DATA + : LifeCycleDataId != null ? Database.Enumerations.DataKind.LIFE_CYCLE_DATA + : OpticalDataId != null ? Database.Enumerations.DataKind.OPTICAL_DATA + : PhotovoltaicDataId != null ? Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA + : null; + + [Projectable] + public Guid? GetDataId(Database.Enumerations.DataKind dataKind) => dataKind switch { - DataKind.CALORIMETRIC_DATA => CalorimetricDataId, - DataKind.GEOMETRIC_DATA => GeometricDataId, - DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, - DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, - DataKind.OPTICAL_DATA => OpticalDataId, - DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, + Database.Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricDataId, + Database.Enumerations.DataKind.GEOMETRIC_DATA => GeometricDataId, + Database.Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalDataId, + Database.Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleDataId, + Database.Enumerations.DataKind.OPTICAL_DATA => OpticalDataId, + Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicDataId, _ => null, // throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; [Projectable] - public IData? GetData(DataKind dataKind) => + public IData? GetData(Database.Enumerations.DataKind dataKind) => dataKind switch { - DataKind.CALORIMETRIC_DATA => CalorimetricData, - DataKind.GEOMETRIC_DATA => GeometricData, - DataKind.HYGROTHERMAL_DATA => HygrothermalData, - DataKind.LIFE_CYCLE_DATA => LifeCycleData, - DataKind.OPTICAL_DATA => OpticalData, - DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, + Database.Enumerations.DataKind.CALORIMETRIC_DATA => CalorimetricData, + Database.Enumerations.DataKind.GEOMETRIC_DATA => GeometricData, + Database.Enumerations.DataKind.HYGROTHERMAL_DATA => HygrothermalData, + Database.Enumerations.DataKind.LIFE_CYCLE_DATA => LifeCycleData, + Database.Enumerations.DataKind.OPTICAL_DATA => OpticalData, + Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA => PhotovoltaicData, _ => null, //throw new ArgumentOutOfRangeException(nameof(dataKind), $"Unsupported data kind {dataKind}"), }; diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index c5041419..6d696ff9 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -150,27 +150,20 @@ private void AssertThatExactlyOneDataIdIsNonNull() public ICollection ArchivedFilesMetaInformation { get; private set; } = []; // Note that at least one data ID is always present. So `Guid.Empty` will never be used. - [NotMapped] [Projectable] public Guid DataId => CalorimetricDataId ?? GeometricDataId ?? HygrothermalDataId ?? LifeCycleDataId ?? OpticalDataId ?? PhotovoltaicDataId ?? Guid.Empty; - [NotMapped] public IData? Data => CalorimetricData ?? GeometricData ?? HygrothermalData ?? LifeCycleData ?? OpticalData ?? PhotovoltaicData as IData; - [NotMapped] - public Database.Enumerations.DataKind? DataKind - { - get - { - if (CalorimetricDataId is not null) return Database.Enumerations.DataKind.CALORIMETRIC_DATA; - if (GeometricDataId is not null) return Database.Enumerations.DataKind.GEOMETRIC_DATA; - if (HygrothermalDataId is not null) return Database.Enumerations.DataKind.HYGROTHERMAL_DATA; - if (LifeCycleDataId is not null) return Database.Enumerations.DataKind.LIFE_CYCLE_DATA; - if (OpticalDataId is not null) return Database.Enumerations.DataKind.OPTICAL_DATA; - if (PhotovoltaicDataId is not null) return Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA; - return null; - } - } + [Projectable] + public Database.Enumerations.DataKind DataKind => + CalorimetricDataId != null ? Database.Enumerations.DataKind.CALORIMETRIC_DATA + : GeometricDataId != null ? Database.Enumerations.DataKind.GEOMETRIC_DATA + : HygrothermalDataId != null ? Database.Enumerations.DataKind.HYGROTHERMAL_DATA + : LifeCycleDataId != null ? Database.Enumerations.DataKind.LIFE_CYCLE_DATA + : OpticalDataId != null ? Database.Enumerations.DataKind.OPTICAL_DATA + : PhotovoltaicDataId != null ? Database.Enumerations.DataKind.PHOTOVOLTAIC_DATA + : default; // the default case does not happen if the above cases are exhaustive [Projectable] public Guid? GetDataId(Database.Enumerations.DataKind dataKind) => diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs index 4d4ce1bb..e2294004 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -1,11 +1,14 @@ +using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; using Database.Data; using Database.Data.AccessPolicies; +using Database.GraphQl.Extensions; using GreenDonut.Data; using HotChocolate.Authorization; +using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -16,9 +19,11 @@ namespace Database.GraphQl.AccessPolicies; public sealed class AccessPolicyQueries { [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public async Task GetGlobalDataAccessPolicyAsync( - CommonAuthorization authorization, + public async Task GetDataAccessPolicyAsync( + Guid? dataId, ApplicationDbContext databaseContext, + IDataAccessPolicyByDataIdDataLoader byId, + CommonAuthorization authorization, IResolverContext resolverContext, CancellationToken cancellationToken ) @@ -28,8 +33,104 @@ CancellationToken cancellationToken authorization.ReportUnauthorizedError(resolverContext); return null; } - return await databaseContext.DataAccessPolicies.AsNoTracking() - .Where(_ => _.DataId == null) - .SingleAsync(cancellationToken); + if (dataId is null) + { + return await databaseContext.DataAccessPolicies.AsNoTracking() + .Where(_ => _.DataId == null) + .SingleAsync(cancellationToken); + } + return await byId.LoadAsync(dataId, cancellationToken); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async ValueTask> GetDataAccessPoliciesAsync( + CommonAuthorization authorization, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + return await databaseContext.DataAccessPolicies + .AsNoTracking() + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async ValueTask> GetUserAccessPoliciesAsync( + CommonAuthorization authorization, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + return await databaseContext.UserAccessPolicies + .AsNoTracking() + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async ValueTask> GetInstitutionAccessPoliciesAsync( + CommonAuthorization authorization, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + return await databaseContext.InstitutionAccessPolicies + .AsNoTracking() + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); + } + + [UsePaging] + [UseFiltering] + [UseSorting] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public async ValueTask> GetOpenIdConnectApplicationAccessPoliciesAsync( + CommonAuthorization authorization, + IResolverContext resolverContext, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return HotChocolate.Types.Pagination.Connection.Empty(); + } + return await databaseContext.OpenIdConnectApplicationAccessPolicies + .AsNoTracking() + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs index e912eb27..abf3be6f 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -17,6 +17,8 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.UserAccessPolicies); descriptor.Field(_ => _.InstitutionAccessPolicies); descriptor.Field(_ => _.OpenIdConnectApplicationAccessPolicies); + descriptor.Field(_ => _.DataId); + descriptor.Field(_ => _.DataKind); // descriptor.Field(_ => _.Data); descriptor.Field(_ => _.CalorimetricData); descriptor.Field(_ => _.GeometricData); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs new file mode 100644 index 00000000..023bd9fa --- /dev/null +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs @@ -0,0 +1,18 @@ +using Database.Data.AccessPolicies; +using HotChocolate.Data.Sorting; + +namespace Database.GraphQl.AccessPolicies; + +public sealed class DataAccessPolicySortType + : SortInputType +{ + protected override void Configure( + ISortInputTypeDescriptor descriptor + ) + { + descriptor.BindFieldsExplicitly(); + descriptor.Name(nameof(DataAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); + descriptor.Field(_ => _.DataId); + descriptor.Field(_ => _.DataKind); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index 1a0f2633..e1c385d6 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -3,8 +3,11 @@ using System.Threading; using System.Threading.Tasks; using Database.Authorization; +using Database.Data; using Database.Data.AccessPolicies; +using Database.GraphQl.DataX; using Database.GraphQl.Extensions; +using GreenDonut; using GreenDonut.Data; using HotChocolate; using HotChocolate.Data; @@ -21,6 +24,60 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(_ => _.GetData(default!)) + .Ignore(); + descriptor + .Field(_ => _.GetDataId(default!)) + .Ignore(); + descriptor + .Field(_ => _.CalorimetricDataId) + .Ignore(); + descriptor + .Field(_ => _.CalorimetricData) + .Ignore(); + descriptor + .Field(_ => _.GeometricDataId) + .Ignore(); + descriptor + .Field(_ => _.GeometricData) + .Ignore(); + descriptor + .Field(_ => _.HygrothermalDataId) + .Ignore(); + descriptor + .Field(_ => _.HygrothermalData) + .Ignore(); + descriptor + .Field(_ => _.LifeCycleDataId) + .Ignore(); + descriptor + .Field(_ => _.LifeCycleData) + .Ignore(); + descriptor + .Field(_ => _.OpticalDataId) + .Ignore(); + descriptor + .Field(_ => _.OpticalData) + .Ignore(); + descriptor + .Field(_ => _.PhotovoltaicDataId) + .Ignore(); + descriptor + .Field(_ => _.PhotovoltaicData) + .Ignore(); + descriptor + .Field(_ => _.DataId) + .Ignore(); + descriptor + .Field(_ => _.DataKind) + .Ignore(); + descriptor + .Field(_ => _.Data) + .Type>() + .Cost(0) + .ResolveWith(t => + Resolvers.GetData(default!, default!, default!)); descriptor .Field(_ => _.UserAccessPolicies) .Cost(1) @@ -37,6 +94,22 @@ IObjectTypeDescriptor descriptor private sealed class Resolvers { + public static async Task GetData( + [Parent] DataAccessPolicy dataAccessPolicy, + IDataByIdAndKindDataLoader dataByIdAndKindDataLoader, + CancellationToken cancellationToken + ) + { + if (dataAccessPolicy.DataId is null || dataAccessPolicy.DataKind is null) + { + return null; + } + return await dataByIdAndKindDataLoader.LoadRequiredAsync( + (dataAccessPolicy.DataId ?? Guid.Empty, dataAccessPolicy.DataKind ?? default), + cancellationToken + ); + } + [UseFiltering] [UseSorting] public static async Task GetUserAccessPoliciesAsync( diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs similarity index 91% rename from backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs rename to backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs index 10ae9554..43d0a3de 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterInput.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs @@ -4,7 +4,7 @@ namespace Database.GraphQl.AccessPolicies; -public sealed class OpenIdConnectApplicationAccessPolicyFilterType +public class OpenIdConnectApplicationAccessPolicyFilterType : AuditableEntityFilterType { protected override void Configure( diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs similarity index 100% rename from backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortInput.cs rename to backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs similarity index 100% rename from backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterInput.cs rename to backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs diff --git a/backend/src/GraphQl/DataX/DataDataLoaders.cs b/backend/src/GraphQl/DataX/DataDataLoaders.cs new file mode 100644 index 00000000..46fb1f12 --- /dev/null +++ b/backend/src/GraphQl/DataX/DataDataLoaders.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GreenDonut; +using Database.Data; +using Database.Enumerations; +using System.Linq; + +namespace Database.GraphQl.DataX; + +public sealed class DataDataLoaders +: DataLoaders +{ + [DataLoader] + public static async Task> GetDataByIdAndKindAsync( + IReadOnlyList<(Guid id, DataKind kind)> idsAndKinds, + Database.GraphQl.CalorimetricDataX.ICalorimetricDataByIdDataLoader calorimetricDataById, + Database.GraphQl.HygrothermalDataX.IHygrothermalDataByIdDataLoader hygrothermalDataById, + Database.GraphQl.LifeCycleDataX.ILifeCycleDataByIdDataLoader lifeCycleDataById, + Database.GraphQl.OpticalDataX.IOpticalDataByIdDataLoader opticalDataById, + Database.GraphQl.PhotovoltaicDataX.IPhotovoltaicDataByIdDataLoader photovoltaicDataById, + Database.GraphQl.GeometricDataX.IGeometricDataByIdDataLoader geometricDataById, + CancellationToken cancellationToken + ) + { + var allData = await Task.WhenAll( + idsAndKinds + .Select<(Guid id, DataKind kind), Task>(async _ => + _.kind switch + { + DataKind.CALORIMETRIC_DATA => await calorimetricDataById.LoadRequiredAsync(_.id, cancellationToken), + DataKind.GEOMETRIC_DATA => await geometricDataById.LoadRequiredAsync(_.id, cancellationToken), + DataKind.HYGROTHERMAL_DATA => await hygrothermalDataById.LoadRequiredAsync(_.id, cancellationToken), + DataKind.LIFE_CYCLE_DATA => await lifeCycleDataById.LoadRequiredAsync(_.id, cancellationToken), + DataKind.OPTICAL_DATA => await opticalDataById.LoadRequiredAsync(_.id, cancellationToken), + DataKind.PHOTOVOLTAIC_DATA => await photovoltaicDataById.LoadRequiredAsync(_.id, cancellationToken), + _ => null + } + ) + ); + return idsAndKinds.Zip(allData, (idAndKind, data) => + new { id = idAndKind.id, kind = idAndKind.kind, data } + ) + .Where(_ => _.data is not null) + .ToDictionary(_ => (_.id, _.kind), _ => _.data!); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index 3f36b29e..b88be728 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -19,6 +19,8 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.AppliedConversionMethod); descriptor.Field(_ => _.ArchivedFilesMetaInformation); descriptor.Field(_ => _.Parent); + descriptor.Field(_ => _.DataId); + descriptor.Field(_ => _.DataKind); // descriptor.Field(_ => _.Data); descriptor.Field(_ => _.CalorimetricData); descriptor.Field(_ => _.GeometricData); @@ -27,4 +29,4 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.OpticalData); descriptor.Field(_ => _.PhotovoltaicData); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs index 226f6ce8..0cddf177 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceResolvers.cs @@ -4,12 +4,8 @@ using Database.ApiRequests; using Database.Controllers; using Database.Data; -using Database.GraphQl.CalorimetricDataX; -using Database.GraphQl.GeometricDataX; -using Database.GraphQl.HygrothermalDataX; -using Database.GraphQl.LifeCycleDataX; -using Database.GraphQl.OpticalDataX; -using Database.GraphQl.PhotovoltaicDataX; +using Database.GraphQl.DataX; +using GreenDonut; using HotChocolate; using Microsoft.AspNetCore.Routing; @@ -17,60 +13,16 @@ namespace Database.GraphQl.GetHttpsResources; public sealed class GetHttpsResourceResolvers { - public async Task GetData( + public Task GetData( [Parent] GetHttpsResource getHttpsResource, - ICalorimetricDataByIdDataLoader calorimetricDataById, - IHygrothermalDataByIdDataLoader hygrothermalDataById, - ILifeCycleDataByIdDataLoader lifeCycleDataById, - IOpticalDataByIdDataLoader opticalDataById, - IPhotovoltaicDataByIdDataLoader photovoltaicDataById, - IGeometricDataByIdDataLoader geometricDataById, + IDataByIdAndKindDataLoader dataByIdAndKindDataLoader, CancellationToken cancellationToken ) { - if (getHttpsResource.CalorimetricDataId is not null) - { - return await calorimetricDataById.LoadAsync( - getHttpsResource.CalorimetricDataId ?? Guid.Empty, - cancellationToken - ); - } - if (getHttpsResource.GeometricDataId is not null) - { - return await geometricDataById.LoadAsync( - getHttpsResource.GeometricDataId ?? Guid.Empty, - cancellationToken - ); - } - if (getHttpsResource.HygrothermalDataId is not null) - { - return await hygrothermalDataById.LoadAsync( - getHttpsResource.HygrothermalDataId ?? Guid.Empty, - cancellationToken - ); - } - if (getHttpsResource.LifeCycleDataId is not null) - { - return await lifeCycleDataById.LoadAsync( - getHttpsResource.LifeCycleDataId ?? Guid.Empty, - cancellationToken - ); - } - if (getHttpsResource.OpticalDataId is not null) - { - return await opticalDataById.LoadAsync( - getHttpsResource.OpticalDataId ?? Guid.Empty, - cancellationToken - ); - } - if (getHttpsResource.PhotovoltaicDataId is not null) - { - return await photovoltaicDataById.LoadAsync( - getHttpsResource.PhotovoltaicDataId ?? Guid.Empty, - cancellationToken - ); - } - return null; + return dataByIdAndKindDataLoader.LoadRequiredAsync( + (getHttpsResource.DataId, getHttpsResource.DataKind), + cancellationToken + ); } public Uri GetLocator( diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs index 71af5164..0d2908ef 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceSortType.cs @@ -17,5 +17,6 @@ ISortInputTypeDescriptor descriptor descriptor.Field(_ => _.DataFormatId); descriptor.Field(_ => _.AppliedConversionMethod); descriptor.Field(_ => _.Parent); + descriptor.Field(_ => _.DataKind); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs index 7e66701c..9731f5a7 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceType.cs @@ -77,11 +77,18 @@ IObjectTypeDescriptor descriptor descriptor .Field(_ => _.PhotovoltaicData) .Ignore(); + descriptor + .Field(_ => _.DataId) + .Ignore(); + descriptor + .Field(_ => _.DataKind) + .Ignore(); descriptor .Field(_ => _.Data) + .Type>>() .Cost(0) .ResolveWith(t => - t.GetData(default!, default!, default!, default!, default!, default!, default!, default!)); + t.GetData(default!, default!, default!)); descriptor .Field(nameof(GetHttpsResource.DataFormatId)[..^2].FirstCharToLower()) .Type>() diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 948d071a..40590b85 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -26,8 +26,8 @@ CacheService cacheService // `azp` (Authorized Party - Standard OpenID Connect Claim) is the the client ID of the application to which the ID token or access token was issued // `oi_prst` (OpenIddict Presenter - Private Internal Claim) is OpenIddict's proprietary, internal equivalent to `client_id` or `azp` return httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.ClientId) - ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty) - ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.Private.Presenter); + ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.AuthorizedParty); + // ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.Private.Presenter); } public async Task SwitchUserOrInstitutionAsync( diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index caa18502..981748a4 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -268,6 +268,27 @@ type Query { currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") + dataAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [DataAccessPolicySortInput!] @cost(weight: "10") + where: DataAccessPolicyPropositionInput @cost(weight: "10") + ): DataAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + dataAccessPolicy(dataId: Uuid!): DataAccessPolicy @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -318,11 +339,51 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") + institutionAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [InstitutionAccessPolicySortInput!] @cost(weight: "10") + where: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + ): InstitutionAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + openIdConnectApplicationAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [OpenIdConnectApplicationAccessPolicySortInput!] @cost(weight: "10") + where: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + ): OpenIdConnectApplicationAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -363,10 +424,8 @@ type Mutation { input: ConfigureDataAccessPoliciesInput! where: ConfigureDataAccessPoliciesPropositionInput @cost(weight: "10") ): ConfigureDataAccessPoliciesPayload! @cost(weight: "10") - configureDataAccessPolicy( - input: ConfigureDataAccessPolicyInput! - where: ConfigureDataAccessPolicyPropositionInput @cost(weight: "10") - ): ConfigureDataAccessPolicyPayload! @cost(weight: "10") + configureDataAccessPolicy(input: ConfigureDataAccessPolicyInput!): ConfigureDataAccessPolicyPayload! + @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -707,17 +766,60 @@ type CurrentUser { uuid: Uuid! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" type DataAccessPolicy { - calorimetricData: CalorimetricData - calorimetricDataId: Uuid combinator: LogicalCombinator! createdAt: DateTime! - data(dataKind: DataKind!): Data - dataId(dataKind: DataKind!): Uuid - geometricData: GeometricData - geometricDataId: Uuid - hygrothermalData: HygrothermalData - hygrothermalDataId: Uuid + data: Data @cost(weight: "0") id: Uuid! institutionAccessPolicies( order: [InstitutionAccessPolicySortInput!] @@ -728,16 +830,11 @@ type DataAccessPolicy { institutionIds: [Uuid!] openIdConnectClientId: String ): Boolean! - lifeCycleData: LifeCycleData - lifeCycleDataId: Uuid + isAnyoneAllowed: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput ): [OpenIdConnectApplicationAccessPolicy!]! - opticalData: OpticalData - opticalDataId: Uuid - photovoltaicData: PhotovoltaicData - photovoltaicDataId: Uuid updatedAt: DateTime! userAccessPolicies( order: [UserAccessPolicySortInput!] @@ -746,6 +843,24 @@ type DataAccessPolicy { version: NonNegativeInt! } +"A connection to a list of items." +type DataAccessPolicyConnection { + "A list of edges." + edges: [DataAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type DataAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: DataAccessPolicy! +} + type DataApproval implements Approval { approver: Institution @cost(weight: "3") approverId: Uuid! @@ -885,10 +1000,9 @@ type GetHttpsResource implements Node { archivedFilesMetaInformation: [FileMetaInformation!]! children: [GetHttpsResource!]! @cost(weight: "0") createdAt: DateTime! - data: Data @cost(weight: "0") + data: Data! @cost(weight: "0") dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! - dataKind: DataKind description: String doesFileExist: Boolean! hashValue: String! @@ -997,13 +1111,33 @@ type InstitutionAccessPolicy implements Node { dataAccessPolicyId: Uuid! id: ID! institutionId: Uuid! - isAccessAllowed: Boolean! + isAccessAllowed(institutionIds: [Uuid!]): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration uuid: Uuid! } +"A connection to a list of items." +type InstitutionAccessPolicyConnection { + "A list of edges." + edges: [InstitutionAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type InstitutionAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: InstitutionAccessPolicy! +} + type InstitutionManagedInstitutionConnection { edges: [InstitutionManagedInstitutionEdge!]! totalCount: NonNegativeInt! @@ -1097,13 +1231,33 @@ type OpenIdConnectApplicationAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! - isAccessAllowed: Boolean! + isAccessAllowed(openIdConnectClientId: String): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration uuid: Uuid! } +"A connection to a list of items." +type OpenIdConnectApplicationAccessPolicyConnection { + "A list of edges." + edges: [OpenIdConnectApplicationAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type OpenIdConnectApplicationAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: OpenIdConnectApplicationAccessPolicy! +} + type OpticalData implements Node & Data { accessPolicy: DataAccessPolicy appliedMethod: AppliedMethod! @@ -1500,7 +1654,9 @@ type UserAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! - isAccessAllowed: Boolean! + isAccessAllowed(currentUser: CurrentUserInput): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration @@ -1710,10 +1866,62 @@ input ConfigureDataAccessPoliciesInput { combinator: LogicalCombinator! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput @@ -1730,21 +1938,6 @@ input ConfigureDataAccessPolicyInput { data: DataReferenceInput } -input ConfigureDataAccessPolicyPropositionInput { - and: [ConfigureDataAccessPolicyPropositionInput!] - calorimetricData: CalorimetricDataPropositionInput - combinator: LogicalCombinatorPropositionInput - geometricData: GeometricDataPropositionInput - hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput - lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput - opticalData: OpticalDataPropositionInput - or: [ConfigureDataAccessPolicyPropositionInput!] - photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput -} - input CountSinceTimePropositionInput { accessCount: NonNegativeIntPropositionInput and: [CountSinceTimePropositionInput!] @@ -1887,10 +2080,62 @@ input CurrentUserInput { uuid: Uuid! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput @@ -1902,6 +2147,61 @@ input DataAccessPolicyPropositionInput { userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" +input DataAccessPolicySortInput { + dataId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -2146,6 +2446,8 @@ input GetHttpsResourcePropositionInput { archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput @@ -2161,6 +2463,7 @@ input GetHttpsResourceSortInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") @@ -2173,6 +2476,8 @@ input GetHttpsResourceTreeNonRootVertexPropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput hashValue: StringPropositionInput or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] @@ -2183,6 +2488,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") @@ -2428,6 +2734,13 @@ input NullableOfCoatedSidePropositionInput { notIn: [CoatedSide] @cost(weight: "10") } +input NullableOfDataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + in: [DataKind] @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + notIn: [DataKind] @cost(weight: "10") +} + input NullableOfIlluminantPropositionInput { equalTo: Illuminant @cost(weight: "10") in: [Illuminant] @cost(weight: "10") @@ -2607,6 +2920,8 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput @@ -2641,10 +2956,62 @@ input RemoveDataApprovalInput { signature: String! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput diff --git a/frontend/queries/currentUser.graphql b/frontend/queries/currentUser.graphql index fca966c0..22ceaa12 100644 --- a/frontend/queries/currentUser.graphql +++ b/frontend/queries/currentUser.graphql @@ -32,7 +32,7 @@ query CurrentUserInfo { # `./currentUser.generated.ts` # to prevent TypeScript error # `'Types' is declared but its value is never read.` -query ZZZDeleteDataZZZ($input: DeleteDataInput!) { +mutation ZZZDeleteDataZZZ($input: DeleteDataInput!) { deleteData(input: $input) { errors { code diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 2b06e361..981748a4 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -268,6 +268,27 @@ type Query { currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") + dataAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [DataAccessPolicySortInput!] @cost(weight: "10") + where: DataAccessPolicyPropositionInput @cost(weight: "10") + ): DataAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") + dataAccessPolicy(dataId: Uuid!): DataAccessPolicy @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -318,11 +339,51 @@ type Query { ): Boolean! @cost(weight: "10") hygrothermalData(id: Uuid!, locale: Locale): HygrothermalData @cost(weight: "10") + institutionAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [InstitutionAccessPolicySortInput!] @cost(weight: "10") + where: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + ): InstitutionAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") lifeCycleData(id: Uuid!, locale: Locale): LifeCycleData @cost(weight: "10") "Fetches an object given its ID." node("ID of the object." id: ID!): Node @cost(weight: "10") "Lookup nodes by a list of IDs." nodes("The list of node IDs." ids: [ID!]!): [Node]! @cost(weight: "10") + openIdConnectApplicationAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [OpenIdConnectApplicationAccessPolicySortInput!] @cost(weight: "10") + where: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + ): OpenIdConnectApplicationAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") opticalData(id: Uuid!, locale: Locale): OpticalData @cost(weight: "10") pendingGetHttpsResources( "Returns the elements in the list that come after the specified cursor." @@ -363,10 +424,8 @@ type Mutation { input: ConfigureDataAccessPoliciesInput! where: ConfigureDataAccessPoliciesPropositionInput @cost(weight: "10") ): ConfigureDataAccessPoliciesPayload! @cost(weight: "10") - configureDataAccessPolicy( - input: ConfigureDataAccessPolicyInput! - where: ConfigureDataAccessPolicyPropositionInput @cost(weight: "10") - ): ConfigureDataAccessPolicyPayload! @cost(weight: "10") + configureDataAccessPolicy(input: ConfigureDataAccessPolicyInput!): ConfigureDataAccessPolicyPayload! + @cost(weight: "10") createCalorimetricData(input: CreateCalorimetricDataInput!): CreateCalorimetricDataPayload! @cost(weight: "10") createGeometricData(input: CreateGeometricDataInput!): CreateGeometricDataPayload! @@ -707,17 +766,60 @@ type CurrentUser { uuid: Uuid! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" type DataAccessPolicy { - calorimetricData: CalorimetricData - calorimetricDataId: Uuid combinator: LogicalCombinator! createdAt: DateTime! - data(dataKind: DataKind!): Data - dataId(dataKind: DataKind!): Uuid - geometricData: GeometricData - geometricDataId: Uuid - hygrothermalData: HygrothermalData - hygrothermalDataId: Uuid + data: Data @cost(weight: "0") id: Uuid! institutionAccessPolicies( order: [InstitutionAccessPolicySortInput!] @@ -728,16 +830,11 @@ type DataAccessPolicy { institutionIds: [Uuid!] openIdConnectClientId: String ): Boolean! - lifeCycleData: LifeCycleData - lifeCycleDataId: Uuid + isAnyoneAllowed: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput ): [OpenIdConnectApplicationAccessPolicy!]! - opticalData: OpticalData - opticalDataId: Uuid - photovoltaicData: PhotovoltaicData - photovoltaicDataId: Uuid updatedAt: DateTime! userAccessPolicies( order: [UserAccessPolicySortInput!] @@ -746,6 +843,24 @@ type DataAccessPolicy { version: NonNegativeInt! } +"A connection to a list of items." +type DataAccessPolicyConnection { + "A list of edges." + edges: [DataAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type DataAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: DataAccessPolicy! +} + type DataApproval implements Approval { approver: Institution @cost(weight: "3") approverId: Uuid! @@ -885,10 +1000,9 @@ type GetHttpsResource implements Node { archivedFilesMetaInformation: [FileMetaInformation!]! children: [GetHttpsResource!]! @cost(weight: "0") createdAt: DateTime! - data: Data @cost(weight: "0") + data: Data! @cost(weight: "0") dataFormat: DataFormat @cost(weight: "3") dataFormatId: Uuid! - dataKind: DataKind description: String doesFileExist: Boolean! hashValue: String! @@ -990,18 +1104,38 @@ type Institution { uuid: Uuid! } -type InstitutionAccessPolicy { +type InstitutionAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! + id: ID! institutionId: Uuid! - isAccessAllowed: Boolean! + isAccessAllowed(institutionIds: [Uuid!]): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration - version: NonNegativeInt! + uuid: Uuid! +} + +"A connection to a list of items." +type InstitutionAccessPolicyConnection { + "A list of edges." + edges: [InstitutionAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type InstitutionAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: InstitutionAccessPolicy! } type InstitutionManagedInstitutionConnection { @@ -1090,18 +1224,38 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } -type OpenIdConnectApplicationAccessPolicy { +type OpenIdConnectApplicationAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime clientId: String! createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! - isAccessAllowed: Boolean! + id: ID! + isAccessAllowed(openIdConnectClientId: String): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration - version: NonNegativeInt! + uuid: Uuid! +} + +"A connection to a list of items." +type OpenIdConnectApplicationAccessPolicyConnection { + "A list of edges." + edges: [OpenIdConnectApplicationAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type OpenIdConnectApplicationAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: OpenIdConnectApplicationAccessPolicy! } type OpticalData implements Node & Data { @@ -1494,18 +1648,20 @@ type User implements Node { uuid: Uuid! } -type UserAccessPolicy { +type UserAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! - id: Uuid! - isAccessAllowed: Boolean! + id: ID! + isAccessAllowed(currentUser: CurrentUserInput): Boolean! + isAlwaysAllowed: Boolean! + isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration userId: Uuid! - version: NonNegativeInt! + uuid: Uuid! } type UserInfo { @@ -1710,10 +1866,62 @@ input ConfigureDataAccessPoliciesInput { combinator: LogicalCombinator! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput @@ -1730,21 +1938,6 @@ input ConfigureDataAccessPolicyInput { data: DataReferenceInput } -input ConfigureDataAccessPolicyPropositionInput { - and: [ConfigureDataAccessPolicyPropositionInput!] - calorimetricData: CalorimetricDataPropositionInput - combinator: LogicalCombinatorPropositionInput - geometricData: GeometricDataPropositionInput - hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput - lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput - opticalData: OpticalDataPropositionInput - or: [ConfigureDataAccessPolicyPropositionInput!] - photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput -} - input CountSinceTimePropositionInput { accessCount: NonNegativeIntPropositionInput and: [CountSinceTimePropositionInput!] @@ -1887,10 +2080,62 @@ input CurrentUserInput { uuid: Uuid! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput @@ -1902,6 +2147,61 @@ input DataAccessPolicyPropositionInput { userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" +input DataAccessPolicySortInput { + dataId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") +} + input DataApprovalPropositionInput { and: [DataApprovalPropositionInput!] approverId: UuidPropositionInput @@ -2146,6 +2446,8 @@ input GetHttpsResourcePropositionInput { archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput @@ -2161,6 +2463,7 @@ input GetHttpsResourceSortInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") @@ -2173,6 +2476,8 @@ input GetHttpsResourceTreeNonRootVertexPropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput hashValue: StringPropositionInput or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] @@ -2183,6 +2488,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodSortInput @cost(weight: "10") createdAt: SortEnumType @cost(weight: "10") dataFormatId: SortEnumType @cost(weight: "10") + dataKind: SortEnumType @cost(weight: "10") description: SortEnumType @cost(weight: "10") hashValue: SortEnumType @cost(weight: "10") id: SortEnumType @cost(weight: "10") @@ -2428,6 +2734,13 @@ input NullableOfCoatedSidePropositionInput { notIn: [CoatedSide] @cost(weight: "10") } +input NullableOfDataKindPropositionInput { + equalTo: DataKind @cost(weight: "10") + in: [DataKind] @cost(weight: "10") + notEqualTo: DataKind @cost(weight: "10") + notIn: [DataKind] @cost(weight: "10") +} + input NullableOfIlluminantPropositionInput { equalTo: Illuminant @cost(weight: "10") in: [Illuminant] @cost(weight: "10") @@ -2607,6 +2920,8 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput + dataId: UuidPropositionInput + dataKind: DataKindPropositionInput description: StringPropositionInput geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput @@ -2641,10 +2956,62 @@ input RemoveDataApprovalInput { signature: String! } +""" +A data access policy decides who can access data, meaning which data shows up +in GraphQL queries and which associated resources can be downloaded. The +decision is made based on the authenticated user or institution, the +institutions represented by a user, and/or the communicating OpenID Connect +application. The access token issued by the metabase and associated with +same-site logins or given in the HTTP Authorization header as Bearer, tells +through the 'Subject' claim which user or institution is authenticated and +through the 'Client ID' or 'Authorized Party' claim which OpenID Connect +application is communicating. In the case of a user, the metabase informs us +about the institutions he*she represents. + +How user, institution(s), and application are allowed/restricted is decided by +user, institution, and application access policies associated with the data +access policy and specific users, institutions, and applications through their +IDs or users and institutions and client IDs for applications. Each such policy +can allow access to a user, institution, or application and can limit the +number of API accesses totally or within a time span, which is shifted to the +current moment once it has passed. For example, an institution policy without a +limit just allows access for any user representing that institution and for any +application owned by the institution itself, and it disallows access for all +other users and institutions. If an additional limit without duration is given, +than exactly that number of GraphQL or REST API accesses are allowed. And if an +additional duration is given, from the time of the first access until the +duration passed, the given number of accesses are allowed; the start time and +the access count are reset on the first access after the duration passed (a +sliding window of time). + +The individual decisions based on user, institution, and OpenID Connect +application, can be combined conjuctively ('and' or 'all' need to be positive) +or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +is configured through the combinator and the mutation +`ConfigureDataAccessPolicyMutation`. If there are no user access +policies at all, then, in the 'all' case, no restrictions based on the user +itself are imposed, and in the 'any' case, no allowances based on the user +itself are given; and analogously for institution and application policies. Put +another way, an empty list of user access policies is `true` in the 'and' case +and `false` in the 'or' case, and analogously for institution and application +access policies. + +In particular, a data access policy with the combinator 'all' and empty user, +institution, and application policies allows access to anyone, also anonymous +access. And one with the combinator 'or' and empty policies allows access to +noone, no matter if authenticated or not. + +A data access policy is either the one-and-only global one or associated with a +specific data entry, see the field `Data`. It is +global if this field is `null`. The global and individual policies are combined +conjunctively, meaning that for access both need to allow access. +""" input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + dataId: UuidPropositionInput + dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput From 63d4dcd4de55e5ed5e55f54cff9338c1ba5c5893 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 14:51:42 +0200 Subject: [PATCH 824/878] Add GraphQL fields to test whether access is allowed for anyone and for specific users/institutions/application combinations --- backend/src/ApiRequests/GetUserInfo.cs | 9 +- .../Data/AccessPolicies/DataAccessPolicy.cs | 10 +- .../Data/AccessPolicies/UserAccessPolicy.cs | 7 +- .../AccessPolicies/AccessPolicyQueries.cs | 2 +- .../DataAccessPolicyFilterType.cs | 2 + .../AccessPolicies/DataAccessPolicyType.cs | 50 +++++++ .../InstitutionAccessPolicyFilterType.cs | 3 + ...onnectApplicationAccessPolicyFilterType.cs | 3 + .../UserAccessPolicyFilterType.cs | 3 + .../src/GraphQl/DataX/DataFilterTypeBase.cs | 2 +- backend/src/GraphQl/DataX/DataResolvers.cs | 39 +++++ backend/src/GraphQl/DataX/DataTypeBase.cs | 8 ++ backend/src/Services/AccessPolicyService.cs | 21 ++- .../GraphQlSchemaTests.IsUnchanged.snap | 134 ++++++++++++------ frontend/codegen.ts | 4 +- frontend/components/data/DataSummary.tsx | 2 +- frontend/type-defs.graphqls | 134 ++++++++++++------ 17 files changed, 325 insertions(+), 108 deletions(-) diff --git a/backend/src/ApiRequests/GetUserInfo.cs b/backend/src/ApiRequests/GetUserInfo.cs index 5e1e5b17..d4b2eea4 100644 --- a/backend/src/ApiRequests/GetUserInfo.cs +++ b/backend/src/ApiRequests/GetUserInfo.cs @@ -2,7 +2,10 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Database.GraphQl.Scalars; using Database.Services; +using HotChocolate; +using HotChocolate.Types; namespace Database.ApiRequests; @@ -16,14 +19,14 @@ ApiRequestService apiRequestService public sealed record UserInfo( Address? Address, - string? Email, + [GraphQLType] string? Email, bool? EmailVerified, string Name, // is not null because the OpenId Connect client asks for the scope `Profile` - string? PhoneNumber, + [GraphQLType] string? PhoneNumber, bool? PhoneNumberVerified, IReadOnlyList? Roles, string Sub, // Subject - string? Website + [GraphQLType] string? Website ); public sealed record Address( diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 91f72283..b9f6d9e8 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; -using Database.ApiRequests; using Database.Enumerations; using Database.Extensions; using Database.GraphQl.AccessPolicy; @@ -209,12 +208,15 @@ private void AssertThatExactlyOneDataIdIsNonNull() [InverseProperty(nameof(OpenIdConnectApplicationAccessPolicy.DataAccessPolicy))] public ICollection OpenIdConnectApplicationAccessPolicies { get; } = []; + [Projectable] + public bool IsGlobal => DataId is null; + [Projectable] public bool IsAnyoneAllowed => IsAccessAllowed(null, null, null); [Projectable] public bool IsAccessAllowed( - QueryCurrentUserOrInstitution.CurrentUser? currentUser, + Guid? userId, Guid[]? institutionIds, string? openIdConnectClientId ) => @@ -223,7 +225,7 @@ public bool IsAccessAllowed( ( UserAccessPolicies.Count == 0 || UserAccessPolicies.Any(_ => - _.IsAccessAllowed(currentUser) + _.IsAccessAllowed(userId) ) ) && @@ -246,7 +248,7 @@ public bool IsAccessAllowed( ( Combinator == LogicalCombinator.SOME && ( UserAccessPolicies.Any(_ => - _.IsAccessAllowed(currentUser) + _.IsAccessAllowed(userId) ) || InstitutionAccessPolicies.Any(_ => diff --git a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs index 6404eb74..a4a928e9 100644 --- a/backend/src/Data/AccessPolicies/UserAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/UserAccessPolicy.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel.DataAnnotations.Schema; -using Database.ApiRequests; using EntityFrameworkCore.Projectables; namespace Database.Data.AccessPolicies; @@ -19,8 +18,8 @@ Guid userId public Guid UserId { get; private set; } = userId; [Projectable] - public bool IsAccessAllowed(QueryCurrentUserOrInstitution.CurrentUser? currentUser) => - currentUser != null - && UserId == currentUser.Uuid + public bool IsAccessAllowed(Guid? userId) => + userId != null + && UserId == userId && IsWithinAccessLimitInTimeSpan; } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs index e2294004..70967925 100644 --- a/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs +++ b/backend/src/GraphQl/AccessPolicies/AccessPolicyQueries.cs @@ -39,7 +39,7 @@ CancellationToken cancellationToken .Where(_ => _.DataId == null) .SingleAsync(cancellationToken); } - return await byId.LoadAsync(dataId, cancellationToken); + return await byId.LoadAsync(dataId ?? Guid.Empty, cancellationToken); } [UsePaging] diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs index abf3be6f..b93fd099 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -13,6 +13,8 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(DataAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + descriptor.Field(_ => _.IsGlobal); + descriptor.Field(_ => _.IsAnyoneAllowed); descriptor.Field(_ => _.Combinator); descriptor.Field(_ => _.UserAccessPolicies); descriptor.Field(_ => _.InstitutionAccessPolicies); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index e1c385d6..ad765160 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Database.Authorization; using Database.Data; using Database.Data.AccessPolicies; @@ -90,6 +91,16 @@ IObjectTypeDescriptor descriptor .Field(_ => _.OpenIdConnectApplicationAccessPolicies) .Cost(1) .ResolveWith(_ => Resolvers.GetOpenIdConnectApplicationAccessPoliciesAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(_ => _.IsAnyoneAllowed) + .Cost(0) + .ResolveWith(t => + Resolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!)); + descriptor + .Field(_ => _.IsAccessAllowed(default!, default!, default!)) + .Cost(0) + .ResolveWith(t => + Resolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!, default!)); } private sealed class Resolvers @@ -110,6 +121,45 @@ CancellationToken cancellationToken ); } + public static Task IsAnyoneAllowedAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IDataAccessPolicyByDataIdDataLoader policyByDataIdDataLoader, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + return IsAccessAllowedAsync(dataAccessPolicy, null, null, null, policyByDataIdDataLoader, databaseContext, cancellationToken); + } + + public static async Task IsAccessAllowedAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId, + IDataAccessPolicyByDataIdDataLoader policyByDataIdDataLoader, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (dataAccessPolicy.DataId is null) + { + return await databaseContext.DataAccessPolicies.AsQueryable() + .Where(_ => _.IsAccessAllowed(userId, institutionIds, openIdConnectClientId)) + .SingleOrDefaultAsync( + _ => _.DataId == null, + cancellationToken + ) + is not null; + } + return await policyByDataIdDataLoader + .Where(_ => _.IsAccessAllowed(userId, institutionIds, openIdConnectClientId)) + .LoadAsync( + dataAccessPolicy.DataId ?? Guid.Empty, + cancellationToken + ) + is not null; + } + [UseFiltering] [UseSorting] public static async Task GetUserAccessPoliciesAsync( diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs index 8eeab0fb..56cbb078 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs @@ -14,6 +14,9 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(InstitutionAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.InstitutionId); + descriptor.Field(_ => _.IsAlwaysAllowed); + descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); + descriptor.Field(_ => _.IsWithinTimeSpan); descriptor.Field(_ => _.DataAccessPolicy); descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); descriptor.Field(_ => _.AccessCountSinceStartTime); diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs index 43d0a3de..c3f8a4bf 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs @@ -14,6 +14,9 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.ClientId); + descriptor.Field(_ => _.IsAlwaysAllowed); + descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); + descriptor.Field(_ => _.IsWithinTimeSpan); descriptor.Field(_ => _.DataAccessPolicy); descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); descriptor.Field(_ => _.AccessCountSinceStartTime); diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs index 066f99c0..e9211b55 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs @@ -14,6 +14,9 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(UserAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.UserId); + descriptor.Field(_ => _.IsAlwaysAllowed); + descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); + descriptor.Field(_ => _.IsWithinTimeSpan); descriptor.Field(_ => _.DataAccessPolicy); descriptor.Field(_ => _.UpperAccessLimitPerTimeDuration); descriptor.Field(_ => _.AccessCountSinceStartTime); diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index 3919ca76..c367bd91 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -49,4 +49,4 @@ IFilterInputTypeDescriptor descriptor // } // return CompleteFields(context, this, fields); // } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index cd06a401..afb3bbd3 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -1,17 +1,22 @@ +using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Database.ApiRequests; using Database.Authorization; using Database.Data; using Database.Data.AccessPolicies; +using Database.Enumerations; using Database.GraphQl.AccessPolicies; using Database.GraphQl.Extensions; using Database.GraphQl.GetHttpsResources; +using Database.Services; using GreenDonut; using GreenDonut.Data; using HotChocolate; using HotChocolate.Data; using HotChocolate.Resolvers; +using System.Linq; namespace Database.GraphQl.DataX; @@ -80,4 +85,38 @@ IInstitutionByIdDataLoader byId { return byId.LoadAsync(data.CreatorId); } + + public static Task IsAnyoneAllowedAsync( + [Parent] IData data, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + return IsAccessAllowedAsync(data, null, null, null, databaseContext, cancellationToken); + } + + public static async Task IsAccessAllowedAsync( + [Parent] IData data, + Guid? userId, + Guid[]? institutionIds, + string? openIdConnectClientId, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + IQueryable policedQuery = data.Kind switch + { + DataKind.CALORIMETRIC_DATA => AccessPolicyService.PoliceData(databaseContext.CalorimetricData, userId, institutionIds, openIdConnectClientId, databaseContext), + DataKind.GEOMETRIC_DATA => AccessPolicyService.PoliceData(databaseContext.GeometricData, userId, institutionIds, openIdConnectClientId, databaseContext), + DataKind.HYGROTHERMAL_DATA => AccessPolicyService.PoliceData(databaseContext.HygrothermalData, userId, institutionIds, openIdConnectClientId, databaseContext), + DataKind.LIFE_CYCLE_DATA => AccessPolicyService.PoliceData(databaseContext.LifeCycleData, userId, institutionIds, openIdConnectClientId, databaseContext), + DataKind.OPTICAL_DATA => AccessPolicyService.PoliceData(databaseContext.OpticalData, userId, institutionIds, openIdConnectClientId, databaseContext), + DataKind.PHOTOVOLTAIC_DATA => AccessPolicyService.PoliceData(databaseContext.PhotovoltaicData, userId, institutionIds, openIdConnectClientId, databaseContext), + _ => throw new ArgumentOutOfRangeException(nameof(data), $"Unsupported data kind {data.Kind}"), + }; + return await policedQuery + .Where(_ => _.Id == data.Id) + .SingleOrDefaultAsync(cancellationToken) + is not null; + } } \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index ccb05899..c5f7e688 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -40,6 +40,14 @@ IObjectTypeDescriptor descriptor .Field(_ => _.AccessPolicy) .Cost(1) .ResolveWith(_ => _.GetDataAccessPolicyAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(nameof(DataResolvers.IsAnyoneAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!)); + descriptor + .Field(nameof(DataResolvers.IsAccessAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!)); descriptor .Field(_ => _.Approval) .Cost(3) diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index 4810a79a..0f94f09c 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore; using NodaTime; using Database.Data; -using Database.ApiRequests; using Microsoft.Extensions.Logging; namespace Database.Services; @@ -50,12 +49,12 @@ CancellationToken cancellationToken institutionIds = [currentInstitution.Uuid]; } logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); - var policedData = PoliceData(data, currentUser, institutionIds, openIdConnectClientId, databaseContext); + var policedData = PoliceData(data, currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); var (postProcessedData, result) = await then(policedData); await IncrementAccessCounts( postProcessedData, openIdConnectClientId, - currentUser, + currentUser?.Uuid, institutionIds, databaseContext, cancellationToken @@ -63,9 +62,9 @@ await IncrementAccessCounts( return result; } - private static IQueryable PoliceData( + internal static IQueryable PoliceData( IQueryable data, - QueryCurrentUserOrInstitution.CurrentUser? currentUser, + Guid? userId, Guid[]? institutionIds, string? openIdConnectClientId, ApplicationDbContext databaseContext @@ -75,13 +74,13 @@ ApplicationDbContext databaseContext return data.Where(_ => ( _.AccessPolicy == null - || _.AccessPolicy.IsAccessAllowed(currentUser, institutionIds, openIdConnectClientId) + || _.AccessPolicy.IsAccessAllowed(userId, institutionIds, openIdConnectClientId) ) && ( !databaseContext.DataAccessPolicies.Any(_ => _.DataId == null - && !_.IsAccessAllowed(currentUser, institutionIds, openIdConnectClientId) + && !_.IsAccessAllowed(userId, institutionIds, openIdConnectClientId) ) ) ); @@ -90,19 +89,19 @@ ApplicationDbContext databaseContext private async Task IncrementAccessCounts( IReadOnlyList allData, string? openIdConnectClientId, - QueryCurrentUserOrInstitution.CurrentUser? currentUser, + Guid? userId, Guid[]? institutionIds, ApplicationDbContext databaseContext, CancellationToken cancellationToken ) where TData : class, IData { - if (currentUser is null && institutionIds is null && openIdConnectClientId is null) + if (userId is null && institutionIds is null && openIdConnectClientId is null) { return; } var allDataIds = allData.Select(_ => _.Id).ToArray(); - if (currentUser is not null) + if (userId is not null) { foreach (var userAccessPolicy in await databaseContext.UserAccessPolicies @@ -111,7 +110,7 @@ await databaseContext.UserAccessPolicies _.DataAccessPolicy.DataId == null || allDataIds.Contains(_.DataAccessPolicy.DataId ?? Guid.Empty) ) - && _.UserId == currentUser.Uuid + && _.UserId == userId ) .ToListAsync(cancellationToken) ) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 981748a4..c6a6aeda 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -288,7 +288,7 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") - dataAccessPolicy(dataId: Uuid!): DataAccessPolicy @cost(weight: "10") + dataAccessPolicy(dataId: Uuid): DataAccessPolicy @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -312,7 +312,6 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") - globalDataAccessPolicy: DataAccessPolicy @cost(weight: "10") hasCalorimetricData( locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10") @@ -407,6 +406,26 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") + userAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [UserAccessPolicySortInput!] @cost(weight: "10") + where: UserAccessPolicyPropositionInput @cost(weight: "10") + ): UserAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") verificationCode: String! } @@ -535,6 +554,12 @@ type CalorimetricData implements Node & Data { description: String gValues: [Float!]! id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -826,11 +851,12 @@ type DataAccessPolicy { where: InstitutionAccessPolicyPropositionInput ): [InstitutionAccessPolicy!]! isAccessAllowed( - currentUser: CurrentUserInput institutionIds: [Uuid!] openIdConnectClientId: String + userId: Uuid ): Boolean! isAnyoneAllowed: Boolean! + isGlobal: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput @@ -960,6 +986,12 @@ type GeometricData implements Node & Data { description: String heights: [Float!]! id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1067,6 +1099,12 @@ type HygrothermalData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1166,6 +1204,12 @@ type LifeCycleData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1276,6 +1320,12 @@ type OpticalData implements Node & Data { description: String id: ID! infraredEmittances: [Float!]! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1340,6 +1390,12 @@ type PhotovoltaicData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1654,7 +1710,7 @@ type UserAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! - isAccessAllowed(currentUser: CurrentUserInput): Boolean! + isAccessAllowed(userId: Uuid): Boolean! isAlwaysAllowed: Boolean! isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! @@ -1664,6 +1720,24 @@ type UserAccessPolicy implements Node { uuid: Uuid! } +"A connection to a list of items." +type UserAccessPolicyConnection { + "A list of edges." + edges: [UserAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type UserAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: UserAccessPolicy! +} + type UserInfo { address: Address email: String @@ -1925,6 +1999,8 @@ input ConfigureDataAccessPoliciesPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2073,13 +2149,6 @@ input CrossDatabaseDataReferencePropositionInput { or: [CrossDatabaseDataReferencePropositionInput!] } -input CurrentUserInput { - databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnectionInput! - name: String! - representedInstitutions: UserRepresentedInstitutionConnectionInput! - uuid: Uuid! -} - """ A data access policy decides who can access data, meaning which data shows up in GraphQL queries and which associated resources can be downloaded. The @@ -2139,6 +2208,8 @@ input DataAccessPolicyPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2531,6 +2602,9 @@ input InstitutionAccessPolicyPropositionInput { and: [InstitutionAccessPolicyPropositionInput!] dataAccessPolicy: DataAccessPolicyPropositionInput institutionId: UuidPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [InstitutionAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } @@ -2539,20 +2613,6 @@ input InstitutionAccessPolicySortInput { institutionId: SortEnumType @cost(weight: "10") } -input InstitutionManagedInstitutionConnectionInput { - edges: [InstitutionManagedInstitutionEdgeInput!]! - totalCount: NonNegativeInt! -} - -input InstitutionManagedInstitutionEdgeInput { - node: InstitutionManagedInstitutionNodeInput! -} - -input InstitutionManagedInstitutionNodeInput { - name: String! - uuid: Uuid! -} - input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -2787,6 +2847,9 @@ input OpenIdConnectApplicationAccessPolicyPropositionInput { and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } @@ -3015,6 +3078,8 @@ input ResetDataAccessPoliciesPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -3288,6 +3353,9 @@ input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] dataAccessPolicy: DataAccessPolicyPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [UserAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput @@ -3297,22 +3365,6 @@ input UserAccessPolicySortInput { userId: SortEnumType @cost(weight: "10") } -input UserRepresentedInstitutionConnectionInput { - edges: [UserRepresentedInstitutionEdgeInput!]! - totalCount: NonNegativeInt! -} - -input UserRepresentedInstitutionEdgeInput { - node: UserRepresentedInstitutionNodeInput! - role: InstitutionRepresentativeRole! -} - -input UserRepresentedInstitutionNodeInput { - managedInstitutions: InstitutionManagedInstitutionConnectionInput! - name: String! - uuid: Uuid! -} - input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") diff --git a/frontend/codegen.ts b/frontend/codegen.ts index 8662a5aa..1619c237 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -11,15 +11,17 @@ const scalars = { DateTimeZone: "string", Decimal: "string", Duration: "string", + EmailAddress: "string", LocalDate: "string", LocalDateTime: "string", LocalTime: "string", Locale: "string", Long: "number", NonNegativeInt: "number", + PhoneNumber: "string", Short: "number", TimeSpan: "string", - UnsignedByte: "string", + UnsignedByte: "number", Upload: "unknown", Url: "string", Uuid: "string", diff --git a/frontend/components/data/DataSummary.tsx b/frontend/components/data/DataSummary.tsx index f8f71507..18bfab5b 100644 --- a/frontend/components/data/DataSummary.tsx +++ b/frontend/components/data/DataSummary.tsx @@ -60,7 +60,7 @@ const transformToTree = (resourceTree: DataPartialFragment["resourceTree"]) => { return { ...node, key: node.vertexId, - title: node.vertexId, + title: `${node.value.dataFormat?.name ?? node.value.dataFormatId} (vertex ${node.vertexId})`, // node.value.locator || node.value.description || `ID: ${node.vertexId}`, children: children.map((child) => buildBranch(child)), }; diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 981748a4..c6a6aeda 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -288,7 +288,7 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") - dataAccessPolicy(dataId: Uuid!): DataAccessPolicy @cost(weight: "10") + dataAccessPolicy(dataId: Uuid): DataAccessPolicy @cost(weight: "10") database: Database! @cost(weight: "10") geometricData(id: Uuid!, locale: Locale): GeometricData @cost(weight: "10") getHttpsResource(id: Uuid!): GetHttpsResource @cost(weight: "10") @@ -312,7 +312,6 @@ type Query { requireOneSlicingArgument: false ) @cost(weight: "10") - globalDataAccessPolicy: DataAccessPolicy @cost(weight: "10") hasCalorimetricData( locale: Locale where: CalorimetricDataPropositionInput @cost(weight: "10") @@ -407,6 +406,26 @@ type Query { @cost(weight: "10") photovoltaicData(id: Uuid!, locale: Locale): PhotovoltaicData @cost(weight: "10") + userAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int + order: [UserAccessPolicySortInput!] @cost(weight: "10") + where: UserAccessPolicyPropositionInput @cost(weight: "10") + ): UserAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) + @cost(weight: "10") verificationCode: String! } @@ -535,6 +554,12 @@ type CalorimetricData implements Node & Data { description: String gValues: [Float!]! id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -826,11 +851,12 @@ type DataAccessPolicy { where: InstitutionAccessPolicyPropositionInput ): [InstitutionAccessPolicy!]! isAccessAllowed( - currentUser: CurrentUserInput institutionIds: [Uuid!] openIdConnectClientId: String + userId: Uuid ): Boolean! isAnyoneAllowed: Boolean! + isGlobal: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput @@ -960,6 +986,12 @@ type GeometricData implements Node & Data { description: String heights: [Float!]! id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1067,6 +1099,12 @@ type HygrothermalData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1166,6 +1204,12 @@ type LifeCycleData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1276,6 +1320,12 @@ type OpticalData implements Node & Data { description: String id: ID! infraredEmittances: [Float!]! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1340,6 +1390,12 @@ type PhotovoltaicData implements Node & Data { databaseId: Uuid! description: String id: ID! + IsAccessAllowed( + institutionIds: [Uuid!] + openIdConnectClientId: String + userId: Uuid + ): Boolean! + IsAnyoneAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1654,7 +1710,7 @@ type UserAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! - isAccessAllowed(currentUser: CurrentUserInput): Boolean! + isAccessAllowed(userId: Uuid): Boolean! isAlwaysAllowed: Boolean! isWithinAccessLimitInTimeSpan: Boolean! isWithinTimeSpan: Boolean! @@ -1664,6 +1720,24 @@ type UserAccessPolicy implements Node { uuid: Uuid! } +"A connection to a list of items." +type UserAccessPolicyConnection { + "A list of edges." + edges: [UserAccessPolicyEdge!] + "Information to aid in pagination." + pageInfo: PageInfo! + "Identifies the total count of items in the connection." + totalCount: Int! @cost(weight: "10") +} + +"An edge in a connection." +type UserAccessPolicyEdge { + "A cursor for use in pagination." + cursor: String! + "The item at the end of the edge." + node: UserAccessPolicy! +} + type UserInfo { address: Address email: String @@ -1925,6 +1999,8 @@ input ConfigureDataAccessPoliciesPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2073,13 +2149,6 @@ input CrossDatabaseDataReferencePropositionInput { or: [CrossDatabaseDataReferencePropositionInput!] } -input CurrentUserInput { - databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnectionInput! - name: String! - representedInstitutions: UserRepresentedInstitutionConnectionInput! - uuid: Uuid! -} - """ A data access policy decides who can access data, meaning which data shows up in GraphQL queries and which associated resources can be downloaded. The @@ -2139,6 +2208,8 @@ input DataAccessPolicyPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2531,6 +2602,9 @@ input InstitutionAccessPolicyPropositionInput { and: [InstitutionAccessPolicyPropositionInput!] dataAccessPolicy: DataAccessPolicyPropositionInput institutionId: UuidPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [InstitutionAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } @@ -2539,20 +2613,6 @@ input InstitutionAccessPolicySortInput { institutionId: SortEnumType @cost(weight: "10") } -input InstitutionManagedInstitutionConnectionInput { - edges: [InstitutionManagedInstitutionEdgeInput!]! - totalCount: NonNegativeInt! -} - -input InstitutionManagedInstitutionEdgeInput { - node: InstitutionManagedInstitutionNodeInput! -} - -input InstitutionManagedInstitutionNodeInput { - name: String! - uuid: Uuid! -} - input IntPropositionInput { equalTo: Int @cost(weight: "10") greaterThan: Int @cost(weight: "10") @@ -2787,6 +2847,9 @@ input OpenIdConnectApplicationAccessPolicyPropositionInput { and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } @@ -3015,6 +3078,8 @@ input ResetDataAccessPoliciesPropositionInput { geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + isAnyoneAllowed: BooleanPropositionInput + isGlobal: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -3288,6 +3353,9 @@ input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] dataAccessPolicy: DataAccessPolicyPropositionInput + isAlwaysAllowed: BooleanPropositionInput + isWithinAccessLimitInTimeSpan: BooleanPropositionInput + isWithinTimeSpan: BooleanPropositionInput or: [UserAccessPolicyPropositionInput!] upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput @@ -3297,22 +3365,6 @@ input UserAccessPolicySortInput { userId: SortEnumType @cost(weight: "10") } -input UserRepresentedInstitutionConnectionInput { - edges: [UserRepresentedInstitutionEdgeInput!]! - totalCount: NonNegativeInt! -} - -input UserRepresentedInstitutionEdgeInput { - node: UserRepresentedInstitutionNodeInput! - role: InstitutionRepresentativeRole! -} - -input UserRepresentedInstitutionNodeInput { - managedInstitutions: InstitutionManagedInstitutionConnectionInput! - name: String! - uuid: Uuid! -} - input UuidPropositionInput { equalTo: Uuid @cost(weight: "10") greaterThan: Uuid @cost(weight: "10") From ba267c72d0c65d95d758055ae46ed0c12d2ff9fe Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 15:01:12 +0200 Subject: [PATCH 825/878] Start dependencies when necessary --- database.mk | 6 ------ deploy.mk | 1 - docker.mk | 2 -- 3 files changed, 9 deletions(-) diff --git a/database.mk b/database.mk index c54c43be..bf406f50 100755 --- a/database.mk +++ b/database.mk @@ -23,7 +23,6 @@ help : ## Print this help psql : ## Enter PostgreSQL interactive terminal in the `database` container docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database @@ -44,7 +43,6 @@ remove-volume : ## Remove data and files volumes create : ## Create database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database @@ -59,7 +57,6 @@ create : ## Create database with name `${POSTGRES_DATABASE_NAME}` drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database @@ -74,7 +71,6 @@ drop : ## Drop database with name `${POSTGRES_DATABASE_NAME}` sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for example, `make sql SCRIPT=./my.sql ` (note that after database schema changes it is necessary to restart the backend service for the object-relational mapper Npgsql to work seamlessly, for example, by restarting the backend service with `./docker.mk restart SERVICE=backend`) docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database @@ -108,7 +104,6 @@ backup : ## Backup database and related data to directory with absolute path `${ mkdir --parents "${DIR}" docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database @@ -143,7 +138,6 @@ restore : ## Restore database and related data from directory with absolute path backend docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ database diff --git a/deploy.mk b/deploy.mk index e4bcb547..f3772492 100755 --- a/deploy.mk +++ b/deploy.mk @@ -96,7 +96,6 @@ dotenv : ## Assert that all variables in ./.env.${ENVIRONMENT}.sample are availa services : ## Recreate services docker compose up \ --no-build \ - --no-deps \ --force-recreate \ --renew-anon-volumes \ --remove-orphans \ diff --git a/docker.mk b/docker.mk index 4189a24c..ecb96b82 100755 --- a/docker.mk +++ b/docker.mk @@ -100,7 +100,6 @@ remove : ## Remove stopped services up : symlink dotenv ## (Re)create and start services docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --remove-orphans \ --wait ${SERVICE} @@ -144,7 +143,6 @@ logs : ## Follow logs exec : ## Execute the one-time command `${COMMAND}` against the `${SERVICE}` service docker compose up \ --no-build \ - --no-deps \ --no-recreate \ --wait \ ${SERVICE} From 626405f56853140cd62207c8bd928877da4a9044 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 18:20:40 +0200 Subject: [PATCH 826/878] Upgrade dependencies to latest compatible versions --- backend/dotnet-tools.json | 6 +- .../Data/AccessPolicies/DataAccessPolicy.cs | 4 +- backend/src/Database.csproj | 48 +- backend/test/Database.Tests.csproj | 8 +- .../components/ActiveFilterAndSortBar.tsx | 3 +- frontend/components/PaginatedIdSelect.tsx | 3 +- frontend/tsconfig.json | 10 +- frontend/typings/apollo.d.ts | 21 + frontend/{ => typings}/environment.d.ts | 3 +- frontend/typings/image.d.ts | 19 + frontend/yarn.lock | 1311 +++++++++-------- 11 files changed, 751 insertions(+), 685 deletions(-) create mode 100644 frontend/typings/apollo.d.ts rename frontend/{ => typings}/environment.d.ts (70%) create mode 100644 frontend/typings/image.d.ts diff --git a/backend/dotnet-tools.json b/backend/dotnet-tools.json index d67bced7..1cf679f3 100644 --- a/backend/dotnet-tools.json +++ b/backend/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "dotnet-ef": { - "version": "10.0.8", + "version": "10.0.9", "commands": [ "dotnet-ef" ], @@ -66,7 +66,7 @@ "rollForward": false }, "jetbrains.resharper.globaltools": { - "version": "2026.1.2", + "version": "2026.1.3", "commands": [ "jb" ], @@ -87,7 +87,7 @@ "rollForward": false }, "dotnet-outdated-tool": { - "version": "4.7.1", + "version": "4.8.1", "commands": [ "dotnet-outdated" ], diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index b9f6d9e8..d0635607 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -41,11 +41,11 @@ the access count are reset on the first access after the duration passed (a The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `{nameof(ConfigureDataAccessPolicyMutation)}`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application diff --git a/backend/src/Database.csproj b/backend/src/Database.csproj index 601ea1f0..d331c23b 100644 --- a/backend/src/Database.csproj +++ b/backend/src/Database.csproj @@ -16,31 +16,31 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - - + + + + @@ -50,15 +50,15 @@ - - + + - + - - + + diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index f8bf7d19..65ccc2c7 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -12,20 +12,20 @@ - - + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/frontend/components/ActiveFilterAndSortBar.tsx b/frontend/components/ActiveFilterAndSortBar.tsx index 478d750d..5672d193 100644 --- a/frontend/components/ActiveFilterAndSortBar.tsx +++ b/frontend/components/ActiveFilterAndSortBar.tsx @@ -8,7 +8,6 @@ import { } from "../lib/filter"; import { formatSortDirection, SortState, SortDefinition } from "../lib/sort"; import DeleteButton from "./DeleteButton"; -import { Key } from "react"; import { getLabel } from "../lib/string"; const stringifySort = ( @@ -29,7 +28,7 @@ const renderSort = ( const stringifyFilter = ( filter: ObjectFilterState, definitions: readonly FilterDefinition[], -): Key => +): string => createFilterStateReducer( (scalar) => `${getFilterOperatorLabel(scalar.operator)}|${JSON.stringify(scalar.value)}`, diff --git a/frontend/components/PaginatedIdSelect.tsx b/frontend/components/PaginatedIdSelect.tsx index f15146a5..d2c6036a 100644 --- a/frontend/components/PaginatedIdSelect.tsx +++ b/frontend/components/PaginatedIdSelect.tsx @@ -83,7 +83,8 @@ export default function PaginatedIdSelect({ const { data, loading, fetchMore, refetch } = useQuery< ItemsData, - ItemsVariables + ItemsVariables, + any >(query, { variables: { first: pageSize, diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 26572294..26e009f4 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -6,10 +6,7 @@ "forceConsistentCasingInFileNames": true, "isolatedModules": true, "jsx": "react-jsx", - "lib": [ - "dom", - "es2017" - ], + "lib": ["dom", "es2017"], "module": "esnext", "moduleResolution": "bundler", "noEmit": true, @@ -24,12 +21,11 @@ // "verbatimModuleSyntax": true }, "include": [ + "typings/**/*.d.ts", "next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx" ], - "exclude": [ - "node_modules" - ] + "exclude": ["node_modules"] } diff --git a/frontend/typings/apollo.d.ts b/frontend/typings/apollo.d.ts new file mode 100644 index 00000000..faeddc27 --- /dev/null +++ b/frontend/typings/apollo.d.ts @@ -0,0 +1,21 @@ +import "@apollo/client"; + +// Inspired by https://www.apollographql.com/docs/react/data/typescript#how-to-declare-default-options +declare module "@apollo/client" { + namespace ApolloClient { + namespace DeclareDefaultOptions { + // Affects client.watchQuery() and React hooks (useQuery, useSuspenseQuery, etc.) + interface WatchQuery { + errorPolicy: "all"; + } + // Affects client.query() + interface Query { + errorPolicy: "all"; + } + // Affects client.mutate() + interface Mutate { + errorPolicy: "all"; + } + } + } +} diff --git a/frontend/environment.d.ts b/frontend/typings/environment.d.ts similarity index 70% rename from frontend/environment.d.ts rename to frontend/typings/environment.d.ts index 03537938..489fe772 100644 --- a/frontend/environment.d.ts +++ b/frontend/typings/environment.d.ts @@ -1,8 +1,9 @@ declare global { namespace NodeJS { interface ProcessEnv { - NODE_ENV: "development" | "production"; HOST: string | undefined; } } } + +export {}; diff --git a/frontend/typings/image.d.ts b/frontend/typings/image.d.ts new file mode 100644 index 00000000..5069030a --- /dev/null +++ b/frontend/typings/image.d.ts @@ -0,0 +1,19 @@ +declare module "*.png" { + const value: string; + export default value; +} + +declare module "*.jpg" { + const value: string; + export default value; +} + +declare module "*.jpeg" { + const value: string; + export default value; +} + +declare module "*.svg" { + const value: string; + export default value; +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index e4a56c59..d4b15679 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -79,7 +79,7 @@ classnames "^2.2.6" rc-util "^5.31.1" -"@ant-design/icons@^6.2.3", "@ant-design/icons@^6.2.5": +"@ant-design/icons@^6.2.5": version "6.2.5" resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.2.5.tgz#31c142aa6ce5eaf99598aaead222f4c459693512" integrity sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw== @@ -149,9 +149,9 @@ throttle-debounce "^5.0.0" "@apollo/client@^4.0.9": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.2.0.tgz#cd5d40f2fd04eabdda77c2e873a9e4c59ac0759f" - integrity sha512-uZAiXwIRidDqQKZRcL88O01IVZjY6IhLio6g+XzX4N4++Ue9pVK9WCoZvBm1dvx+x2mH0oGfVUZvTvacCW4WcQ== + version "4.2.3" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.2.3.tgz#04177a6a170e3733346f9ae15103161abd50cdce" + integrity sha512-+auRYBXow2v7cT+wKzvjyMyyEojq+G7Sf80vIR57rtEPcxRFuMXuU9IKjwxZ3muclUgdGKwZXNeuki+g0GabgQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -339,13 +339,20 @@ "@emnapi/wasi-threads" "1.2.1" tslib "^2.4.0" -"@emnapi/runtime@1.10.0", "@emnapi/runtime@^1.7.0": +"@emnapi/runtime@1.10.0": version "1.10.0" resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c" integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA== dependencies: tslib "^2.4.0" +"@emnapi/runtime@^1.7.0": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.1.tgz#58f1f3d5d81a9b12f793ab688c96371901027c24" + integrity sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw== + dependencies: + tslib "^2.4.0" + "@emnapi/wasi-threads@1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz#28fed21a1ba1ce797c44a070abc94d42f3ae8548" @@ -429,10 +436,10 @@ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091" integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw== -"@eslint/plugin-kit@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz#c4125fd015eceeb09b793109fdbcd4dd0a02d346" - integrity sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ== +"@eslint/plugin-kit@^0.7.2": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz#4b0962f3f2c7ce8bc98b3ecfe34525c09d2cb729" + integrity sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A== dependencies: "@eslint/core" "^1.2.1" levn "^0.4.1" @@ -451,9 +458,9 @@ tslib "^2.8.0" "@graphql-codegen/cli@^7.0.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-7.1.0.tgz#e6c02324260d7ad53147460362d709054496c92d" - integrity sha512-2j1FzZtEnlHdQmOaDnTMGSNyu3RmDf4HGU9xbgpqDRSb+D8UYya2YFW1enIxhJccAQSLD9ACMwCXlB20wJXNiA== + version "7.1.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-7.1.3.tgz#3e373c4e3180e4a390ac1021a517231939fe8f6c" + integrity sha512-mMYwpvpqJjjHoA/c6HBjdlbT8JqFC6W85RB80tpHACapufBnLlyNtYHYeOYAoUuU1n3cGQi1if1pKHnjLgS/eQ== dependencies: "@babel/generator" "^7.18.13" "@babel/template" "^7.18.10" @@ -583,12 +590,12 @@ tslib "^2.8.0" "@graphql-codegen/typed-document-node@^7.0.1", "@graphql-codegen/typed-document-node@^7.0.2": - version "7.0.2" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-7.0.2.tgz#5589187a385ad13f71df3e0567e97ee1bebddc9d" - integrity sha512-vrjigzqpKsaQ2Ra4QBBuG9TcwG1O0pw9EOSFU0aj7mS2EK19vWs5N4rzwDDvHrQZW62wwqzUcqBoFitVBQqlmg== + version "7.0.3" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typed-document-node/-/typed-document-node-7.0.3.tgz#98df3887322e1ba945d13391f3815f79b4e64742" + integrity sha512-l/4KenYJG5D8Aj6Aa2KPeS0fdMIIi04Qx28d4SLwMWuyFU9WXspU5mR9YMNDRZzaTgBtGR8aMIl9RzyiWf5uUw== dependencies: "@graphql-codegen/plugin-helpers" "^7.0.1" - "@graphql-codegen/visitor-plugin-common" "^7.0.4" + "@graphql-codegen/visitor-plugin-common" "^7.1.0" auto-bind "^5.0.0" change-case-all "^2.1.0" tslib "^2.8.0" @@ -605,13 +612,13 @@ tslib "^2.8.1" "@graphql-codegen/typescript-operations@^6.0.2", "@graphql-codegen/typescript-operations@^6.0.3": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-6.0.3.tgz#23966ec2236833841d527cbeeeadcc09e2ba605e" - integrity sha512-5gnHdBgKkpKJOiqyKo37UfwigNtezfN94UI/NnyCROl6oDAyt0A+1VeS5cBlgRECdD/TQ1vjHCu14B41Sl7yLw== + version "6.0.4" + resolved "https://registry.yarnpkg.com/@graphql-codegen/typescript-operations/-/typescript-operations-6.0.4.tgz#98a176d755372488c400ff4e49988eddfaa007bb" + integrity sha512-YUmnmZcJ6wMcC2VmTE7gw18zoG4S1VF1aJmaupDrBnwfKGA5BmdKrA6k2Wj6ZkUDOduHI60l/cmZBRtqel4VtQ== dependencies: "@graphql-codegen/plugin-helpers" "^7.0.1" "@graphql-codegen/schema-ast" "^6.0.1" - "@graphql-codegen/visitor-plugin-common" "^7.0.3" + "@graphql-codegen/visitor-plugin-common" "^7.1.0" auto-bind "^5.0.0" tslib "^2.8.0" @@ -642,10 +649,10 @@ parse-filepath "^1.0.2" tslib "^2.8.0" -"@graphql-codegen/visitor-plugin-common@^7.0.0", "@graphql-codegen/visitor-plugin-common@^7.0.3", "@graphql-codegen/visitor-plugin-common@^7.0.4": - version "7.0.4" - resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-7.0.4.tgz#d0ab0f80dd2a29b1786a4a270ccf2f1ffaf1d5b9" - integrity sha512-IESwu40Gpl+9x5vRGLsaYAWM9KRobnN74h+SB4EtfDrt0V87Q74ZzWZGmuS2m6A/fq7E/bS6YgSEMVACjaRUVw== +"@graphql-codegen/visitor-plugin-common@^7.0.0", "@graphql-codegen/visitor-plugin-common@^7.0.3", "@graphql-codegen/visitor-plugin-common@^7.1.0": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-7.1.0.tgz#73aaf4aa2ce72255e687348d311f8aa26749dfb1" + integrity sha512-CO4fJyflbYBuAwbQD16bAuWBIXkz9il3JwyC+pQzXh8NJ+BZZDXmYjmVeGeJuoMUIQDb+CNo2thCU0bFFamAkg== dependencies: "@graphql-codegen/plugin-helpers" "^7.0.1" "@graphql-tools/optimize" "^2.0.0" @@ -694,10 +701,10 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^12.0.16": - version "12.0.16" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.16.tgz#2f6636abadcf0c57ac1356431d70cf4ac1be1e2d" - integrity sha512-WEJaFwWG82a0VzhfE4sRsaOPjxgCVfn4fOe3ho+r3uIbPYpc7qHpFdu1PLg6meikq6fuW9NJ1J88fEgnWuXDVg== +"@graphql-tools/delegate@^12.0.17": + version "12.0.17" + resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-12.0.17.tgz#eb0d712907c60c9675964285b929fc580b39c581" + integrity sha512-pIVszWEm69rF+bkM0jUyM1KdIxGzygQbIp1GtV1CuEGRB8lN1uFY1eeTzM2nudHXg8cj+XSVO8cnRpph+o8Dmg== dependencies: "@graphql-tools/batch-execute" "^10.0.8" "@graphql-tools/executor" "^1.4.13" @@ -915,11 +922,11 @@ tslib "^2.4.0" "@graphql-tools/wrap@^11.1.1": - version "11.1.15" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.15.tgz#d0e19db5d4075e3768ffcd0f5249f9856141a792" - integrity sha512-GCMx6l0MPwHVaBMHf29oG8eIrsJ8PBXq9y5DNX9/r9oCpCBfqxfWzcejx4CpO4chA3+yylGOKcAyEbOUgxfI1Q== + version "11.1.16" + resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-11.1.16.tgz#125ec29dfb3b726f91d9042ef2410407f546f83a" + integrity sha512-JW1XGFTmltXa537J2bAr8dN/n6EWwiBuM9q8V8mWqZ0eWrf++/TT3/mlV3c0M8B8nrS/lqSsotIwPAtVZR8sWQ== dependencies: - "@graphql-tools/delegate" "^12.0.16" + "@graphql-tools/delegate" "^12.0.17" "@graphql-tools/schema" "^10.0.29" "@graphql-tools/utils" "^11.0.0" "@whatwg-node/promise-helpers" "^1.3.2" @@ -1108,144 +1115,144 @@ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz#a81ffb00e69267cd0a1d626eaedb8a8430b2b2f8" integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw== -"@inquirer/ansi@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.6.tgz#537b3e75355a7599f25a2289843ccbc3318af98c" - integrity sha512-I/INw4sHGlVZ/afZOckpLiDP9SmbMl1g/GCqeHjLw1Afw/0PlRs2tRFgTGWmdI0hoNuWZn3y2iHNmG1vyECyQQ== - -"@inquirer/checkbox@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-5.2.0.tgz#762c20854971bdd4ddb0105fce3c65e1e02d1bb0" - integrity sha512-1HJt+3fqxblp/GQjdntSyoSHYBc0e3CzXVgjFpKA6qFLd9FHBBqwN8Co0xYH6t2JVUZrtFwZ4bBiwptkiLxyOg== - dependencies: - "@inquirer/ansi" "^2.0.6" - "@inquirer/core" "^11.2.0" - "@inquirer/figures" "^2.0.6" - "@inquirer/type" "^4.0.6" - -"@inquirer/confirm@^6.1.0": - version "6.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-6.1.0.tgz#69f31076d59d7f85f59a2d0b8434f46c95b9bf5e" - integrity sha512-USpeB76eqK7yGricDlGAupxWlp4a59qpeZOoNWaxO/nJln7agpJveyNkQ1d5u8YXG6TOqxZtQpKPORQQDrdVsA== - dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" +"@inquirer/ansi@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@inquirer/ansi/-/ansi-2.0.7.tgz#86de22810cac3ed406ec10f8d66016815b8226b4" + integrity sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q== -"@inquirer/core@^11.2.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-11.2.0.tgz#dd59e490099cde25da4f5a93db47fe5c563df72a" - integrity sha512-joR1YS2sI0us+9d0I8ViqFbrRLONO8CFTuyvBX4ZVBSch+VsZiugUABdrhBXXJR1VyEzvpz5SQCix3keETQ58g== - dependencies: - "@inquirer/ansi" "^2.0.6" - "@inquirer/figures" "^2.0.6" - "@inquirer/type" "^4.0.6" +"@inquirer/checkbox@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/checkbox/-/checkbox-5.2.1.tgz#7f148b3153a776cee202015b10f9a985068d188d" + integrity sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw== + dependencies: + "@inquirer/ansi" "^2.0.7" + "@inquirer/core" "^11.2.1" + "@inquirer/figures" "^2.0.7" + "@inquirer/type" "^4.0.7" + +"@inquirer/confirm@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-6.1.1.tgz#9c6a7d79c6132b2af57fdb75747f056204e55356" + integrity sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ== + dependencies: + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" + +"@inquirer/core@^11.2.1": + version "11.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-11.2.1.tgz#54ccd8f7d47852140b6066cbd77d63b2c2b168fd" + integrity sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA== + dependencies: + "@inquirer/ansi" "^2.0.7" + "@inquirer/figures" "^2.0.7" + "@inquirer/type" "^4.0.7" cli-width "^4.1.0" fast-wrap-ansi "^0.2.0" - mute-stream "^4.0.0" + mute-stream "^3.0.0" signal-exit "^4.1.0" -"@inquirer/editor@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-5.2.0.tgz#ffac65843db4477c59a4aa223e193de5f8a98a86" - integrity sha512-/m+sgRmzSdK6HDtVnl3PmI6MnZC4O+LLezedoJcrX7mINhTjjb0hlC7aEDGZXkFTB4b5uQ0q59AhYTah88KbNg== +"@inquirer/editor@^5.2.2": + version "5.2.2" + resolved "https://registry.yarnpkg.com/@inquirer/editor/-/editor-5.2.2.tgz#7c73e2fc0e7bd4c40cfd38a180ae5bbd24d32b90" + integrity sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg== dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/external-editor" "^3.0.1" - "@inquirer/type" "^4.0.6" + "@inquirer/core" "^11.2.1" + "@inquirer/external-editor" "^3.0.3" + "@inquirer/type" "^4.0.7" -"@inquirer/expand@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-5.1.0.tgz#77807c5e4ffc0904a9ca95f0deef7053153e441a" - integrity sha512-fR7g4BVnIcs+4NApF6C5byflNM/EULxSxsv/2Jvg+gmop0R6eBIPvZqE6RYnTy1tQTFnf9wyHkwNoQSZbofaGA== +"@inquirer/expand@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/expand/-/expand-5.1.1.tgz#e2afeac247d97dd64ee18aa81e902bdd1fe0ea70" + integrity sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g== dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" -"@inquirer/external-editor@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-3.0.1.tgz#764d43a509734ce81d083a553a2b79ec3dc8d984" - integrity sha512-tam+Gwjsxg2sx3iUVPkAnhKT/yrk2rd2NAa7XJU/J8OYpU0ifXsnp12xlvzp/DCpWBXVv+vLQsqnpAWwUcWD5Q== +"@inquirer/external-editor@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@inquirer/external-editor/-/external-editor-3.0.3.tgz#d79e772542cf8d340642e9dabd3a1ea7f5a30104" + integrity sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA== dependencies: chardet "^2.1.1" iconv-lite "^0.7.2" -"@inquirer/figures@^2.0.6": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-2.0.6.tgz#2760cc9a9ea8d238ba48059b16b77e5c7eb319ba" - integrity sha512-dsZgQtH2t5Q6ah3aPbZbeEZAxsD9qQu0DXf01AltuEfRTm+NoLN6+rLVbr+4edeEbNCp/wBNM6mALRWtsQpfkw== +"@inquirer/figures@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-2.0.7.tgz#f5cc5843732a81304d06a0db4b53cc7dbda15541" + integrity sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw== -"@inquirer/input@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-5.1.0.tgz#0e3dab4866f69c8b38cb4686da19cd56bdea5bd8" - integrity sha512-sVZCz6P6e8tW5g2bSFel1oLpa6jK/u7BexFfrgTqR8syIdnHqy+iopnlSbYBZMsCK52chLjhGNBxt0eRqhsghw== +"@inquirer/input@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-5.1.2.tgz#9305cb170dfc3a5323e5eac885a945e7cddd5c4b" + integrity sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg== dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" -"@inquirer/number@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-4.1.0.tgz#841489e68dcaf76a8fce19b5c0b48b14eced240f" - integrity sha512-VMXB/XejCbaSTf9Xucl7dqjzzsaGsrs6XwSYXPbGZ2QbSuq/Gz8XamhSi9ClRubNXZlGry9xVg1tKkJdTDgCtQ== +"@inquirer/number@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/number/-/number-4.1.1.tgz#b133668d8e0e099b4133abb915221501e0ff75d7" + integrity sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA== dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" -"@inquirer/password@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-5.1.0.tgz#af0289bad1f7718db4e7f6f5eb398c2985a8e256" - integrity sha512-5tqRuKCDIUxdPxTI/CuLnh914kz+WMPmURHKnZgui9gk43ebudEsdu4EwSn1CPSi5R+17YpBG+ba/YqTnRAcJA== +"@inquirer/password@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@inquirer/password/-/password-5.1.1.tgz#f21efb614da9c905095262f51781fd2a721fceac" + integrity sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg== dependencies: - "@inquirer/ansi" "^2.0.6" - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" + "@inquirer/ansi" "^2.0.7" + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" "@inquirer/prompts@^8.3.2": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-8.5.0.tgz#159c0f024b088af33f20c81de6abad3fe21cd115" - integrity sha512-pLjXOnY4y3R1mgyHP3pXD/8eXejp+L/dde/0N2NLKgKfMstqhNZrpvs7Wkzbl9FYFQh10LRQ7QZwq+cz9rrhyw== - dependencies: - "@inquirer/checkbox" "^5.2.0" - "@inquirer/confirm" "^6.1.0" - "@inquirer/editor" "^5.2.0" - "@inquirer/expand" "^5.1.0" - "@inquirer/input" "^5.1.0" - "@inquirer/number" "^4.1.0" - "@inquirer/password" "^5.1.0" - "@inquirer/rawlist" "^5.3.0" - "@inquirer/search" "^4.2.0" - "@inquirer/select" "^5.2.0" - -"@inquirer/rawlist@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-5.3.0.tgz#6700a3a8c06b2ea3c9e7185d7c672cd47ced9fa6" - integrity sha512-p+vAeTAD+cGXjGleP1F5LXrX2ISxNDZm+lqeBpnJausNLSZskZZkcggwhomqP8Igx9oIjnoeOrw98xvdFvdm2w== - dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/type" "^4.0.6" - -"@inquirer/search@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-4.2.0.tgz#3a9e71fe7f6167b96450ccdcb873e8b8f6d94877" - integrity sha512-ByURoSGIaSl5O5Q0AmYmVmUsXbMUcBGNoA3FRL7TOyiA22IeFHymJKRkuILbOIlJwqnBk7AnPpseodyFUBzg+g== - dependencies: - "@inquirer/core" "^11.2.0" - "@inquirer/figures" "^2.0.6" - "@inquirer/type" "^4.0.6" - -"@inquirer/select@^5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-5.2.0.tgz#aa1669e9a24df0f319d376b5424c7ac3b0a50c22" - integrity sha512-6IzkcmEbEXfgVbxZ2d1UyJFbCBoc6dTofulFmrYuomIp88HXiVqRbqbg4/mbfZhvnNo6xYmnYo2AEmDof6fQkg== + version "8.5.2" + resolved "https://registry.yarnpkg.com/@inquirer/prompts/-/prompts-8.5.2.tgz#09c0132ada2bbba94c91d341115e1e41cb3f1525" + integrity sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g== + dependencies: + "@inquirer/checkbox" "^5.2.1" + "@inquirer/confirm" "^6.1.1" + "@inquirer/editor" "^5.2.2" + "@inquirer/expand" "^5.1.1" + "@inquirer/input" "^5.1.2" + "@inquirer/number" "^4.1.1" + "@inquirer/password" "^5.1.1" + "@inquirer/rawlist" "^5.3.1" + "@inquirer/search" "^4.2.1" + "@inquirer/select" "^5.2.1" + +"@inquirer/rawlist@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@inquirer/rawlist/-/rawlist-5.3.1.tgz#66f6b8e6aa82d47399c433b8262128e7c1a4f9ce" + integrity sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og== + dependencies: + "@inquirer/core" "^11.2.1" + "@inquirer/type" "^4.0.7" + +"@inquirer/search@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/search/-/search-4.2.1.tgz#c8f4b78ab3f866fdf0503fac0cd08c4a6661c11e" + integrity sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g== + dependencies: + "@inquirer/core" "^11.2.1" + "@inquirer/figures" "^2.0.7" + "@inquirer/type" "^4.0.7" + +"@inquirer/select@^5.2.1": + version "5.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-5.2.1.tgz#3a05e76e58d9e1bb095e912c3e7093aa04cd4604" + integrity sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw== dependencies: - "@inquirer/ansi" "^2.0.6" - "@inquirer/core" "^11.2.0" - "@inquirer/figures" "^2.0.6" - "@inquirer/type" "^4.0.6" + "@inquirer/ansi" "^2.0.7" + "@inquirer/core" "^11.2.1" + "@inquirer/figures" "^2.0.7" + "@inquirer/type" "^4.0.7" -"@inquirer/type@^4.0.6": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-4.0.6.tgz#490b2130eccde68f8a63697e100f4c6fce7a01c7" - integrity sha512-J+9tdxOskuYuGjsvGaq00AamhDgjR7anhEW2dP4QdQpFCMPngCeC/bCYWQ5NsMWZRdsy53is7kAHb/+7cwDk2g== +"@inquirer/type@^4.0.7": + version "4.0.7" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-4.0.7.tgz#9c6f0d857fe6ad549a3a932343b64e76acb34b10" + integrity sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g== "@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" @@ -1282,63 +1289,63 @@ "@jridgewell/sourcemap-codec" "^1.4.14" "@napi-rs/wasm-runtime@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz#a46bbfedc29751b7170c5d23bc1d8ee8c7e3c1e1" - integrity sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow== + version "1.1.5" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz#cccd6ebc40b991dea6936f9126b1b8155b6c4c95" + integrity sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q== dependencies: - "@tybys/wasm-util" "^0.10.1" + "@tybys/wasm-util" "^0.10.2" -"@next/env@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.6.tgz#93a173801cb088463070cc6a113c68e26c1838ea" - integrity sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw== +"@next/env@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/env/-/env-16.2.9.tgz#c2bbcb1647503cae0f11e6a326799acd1ea29e9e" + integrity sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg== -"@next/eslint-plugin-next@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.6.tgz#24f3b2945b2856a5559af5ad630b7cddf1f65329" - integrity sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw== +"@next/eslint-plugin-next@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.9.tgz#0d788da191971636def239cdf3b42458796df6ff" + integrity sha512-UZi8+YT/MLgTC9nrrn2Xd4lBYv1B7lVmtWHfPcthAI5Tt/C1LuDe6DfmtCtJ+WQod3ksY4VrKSvk3oMVAnL7qw== dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.6.tgz#0e19055594fbd2d198ce95cf5842bdbe57235d4e" - integrity sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg== - -"@next/swc-darwin-x64@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.6.tgz#487086280b56017bb547f5975ef1a3d6235a070f" - integrity sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ== - -"@next/swc-linux-arm64-gnu@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.6.tgz#b28ffbc31ee527a3ad48f29c2fd7b7f75bbdf180" - integrity sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w== - -"@next/swc-linux-arm64-musl@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.6.tgz#d2eac5600e7083527669fa8cb8758efbbf9c8210" - integrity sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA== - -"@next/swc-linux-x64-gnu@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.6.tgz#e70dc3469bf9bfef16da2ba240c07ef6cfe8ad55" - integrity sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw== - -"@next/swc-linux-x64-musl@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.6.tgz#49ecd2f622d0f3741654c59411f604dbbe1a38de" - integrity sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g== - -"@next/swc-win32-arm64-msvc@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.6.tgz#8a6dfec005364e1cf4fa1724c3d7fa0093660406" - integrity sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg== - -"@next/swc-win32-x64-msvc@16.2.6": - version "16.2.6" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.6.tgz#4fb656ccfcf60cbf8ffedb40fc1b625987c82742" - integrity sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA== +"@next/swc-darwin-arm64@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.9.tgz#b67a7e67466e39be621553b7a32520bc73ca4fa4" + integrity sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw== + +"@next/swc-darwin-x64@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.9.tgz#817f3609c231b07d6910c074eeaf7c41ae781f2d" + integrity sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w== + +"@next/swc-linux-arm64-gnu@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.9.tgz#f7314d5c268a6c440208c46da92ab8d914c02d36" + integrity sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw== + +"@next/swc-linux-arm64-musl@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.9.tgz#9258cf7a3f2f10a605ef85fb76601123f3b1d5ab" + integrity sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA== + +"@next/swc-linux-x64-gnu@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.9.tgz#a6db6095f6495c6d55c48ba946b1578f5e75a359" + integrity sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg== + +"@next/swc-linux-x64-musl@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.9.tgz#d7f857879886c3a378285fea438ba4636fcc3a9a" + integrity sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw== + +"@next/swc-win32-arm64-msvc@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.9.tgz#d6d67c8e803b2c44b76b16495b5eda47165faf43" + integrity sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ== + +"@next/swc-win32-x64-msvc@16.2.9": + version "16.2.9" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.9.tgz#6e36f17dd615c52761691b7f5be1d9d161abed2a" + integrity sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1366,114 +1373,114 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@oxc-parser/binding-android-arm-eabi@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.130.0.tgz#55917e12ce2bf91f5d8f7af6fa337511b2ca6278" - integrity sha512-h/xYU8/7ADWzVSf5I+YalLpj33LOy9CI/zgbJNIZ5eunRBG+Czqa3lZsvuPHHf3rOt6z1c5+UzoxjbAzAvhwVw== - -"@oxc-parser/binding-android-arm64@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.130.0.tgz#6a88c34fa1641bff439b4def7e4a86070239ac83" - integrity sha512-oFWFJrsGv9siFM4HjMqKNB7IuIZD/SMmZdCXl8xyx7lDplGvPKyewpOo272rSWgMXe2Wx7bWI0Yj+gkHv4qbeg== - -"@oxc-parser/binding-darwin-arm64@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.130.0.tgz#d056a2b3a0100a5610e3014d75fe6d567fc49bd1" - integrity sha512-sGUzupdTplK9jQg7eJZ878HfEgQjJNBc6dAYVWJ9W5aU+J8rLfRJhTVsKThiu1pNwm6Y1qKCcbC6WhNWSXR3Ig== - -"@oxc-parser/binding-darwin-x64@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.130.0.tgz#21340dd67fcdfec7b0be9d4fc6490f84b80cc641" - integrity sha512-PsB4cdCISbC00Uy8eiD8bc2AkGWjZqrSrJnkBFuG2ptrrf6mZ2F5gLFSjOAVMMgZPg8B1D7OydJwLWSfyI2Plg== - -"@oxc-parser/binding-freebsd-x64@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.130.0.tgz#08db5e6dd718b4e7e7c98e5e2ca7bb538fd460a6" - integrity sha512-DgABp3l38hS77JbXCV4qk1+n6DPym5u8zzwuweokezm2tX194nDSJDENbDRECxVsiNbprKATLbk+Z5wlHT0OHw== - -"@oxc-parser/binding-linux-arm-gnueabihf@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.130.0.tgz#3ad94b6f0b763dec37ee0412905dad8e34c1a5a0" - integrity sha512-4Kn3CTEmwFrzhTSC/JuUW16qovmaMdX7jeSKbL8w0pLtLww7To1a2XJi9Z5uD8QWUkfUHhqfV+VD6dVzBnWzoA== - -"@oxc-parser/binding-linux-arm-musleabihf@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.130.0.tgz#f2604ea11032989d779a22c47b8a636f91d2dd44" - integrity sha512-D35KZM3F4rRu1uAFKyBlg3Gaf/ybCjyaPR1hfgvk5ex8NtcTmRgc0JgSighEyNg96TPrFhemFba68SZuxaha8w== - -"@oxc-parser/binding-linux-arm64-gnu@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.130.0.tgz#e3e3da7b98a5b8988893cba16cb81e0ee513ed1d" - integrity sha512-Q9o7oVlo955KHwS8l1u0bCzIx+JsZUA3XToLXC+MsMhye/9LeBQbt84nh120cl2XLy+TEzvugYDiHShg5yaX6Q== - -"@oxc-parser/binding-linux-arm64-musl@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.130.0.tgz#97ce15b046257465757e838ce173c09d540e840a" - integrity sha512-EiJ/gC0ljbcwVpycC8YWw6ggMbtsPX8XMOt0mPx0aqWeMsNR+L9m05Flbvd5T+GlivG+GkSWQL7tM9SRFpM/dw== - -"@oxc-parser/binding-linux-ppc64-gnu@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.130.0.tgz#8c364ef28a2a4f694cc58c4fd951e1710a3703ed" - integrity sha512-b+h/lsLLurp756dMGizNs5uPaJfyEdWrTcV5t8M609jWm1DEHB1StpRXCkyvwtkJx3m+qL5BNQ0dEKan/4yGFA== - -"@oxc-parser/binding-linux-riscv64-gnu@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.130.0.tgz#1085fd4fe2664d6c138463f42a36286e9e70c3f5" - integrity sha512-O19Cil83XAyjEFfo8WhkMwY58ALqZ7ckjGL+25mjMIuF84urWBeANH0FC8B8BsSSygWU3/1aY3ADdDbp+wlBnw== - -"@oxc-parser/binding-linux-riscv64-musl@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.130.0.tgz#de0ea9c5a5dcb1dd2a0db73baab73e2f4e1005cd" - integrity sha512-BgXRVC0+83n3YzCscLQjj6nbyeBIVeZYPTI4fFMAE4WNm2+4RXhWp03IVizL7esIz36kgmT48aebk1iM+cs8sw== - -"@oxc-parser/binding-linux-s390x-gnu@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.130.0.tgz#6e0837ab6b7d1f2462cef3a86953de0288327ac2" - integrity sha512-6tJz0xvnGhsokE7N1WlUSBXibpYmT9xSJFS1Ce41Km/+8gQvdlW8MLhRv8PD0L7ix8vRG0FDDepp3jdOFzdVdw== - -"@oxc-parser/binding-linux-x64-gnu@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.130.0.tgz#738e29a90190a0d97e91cb9ed4a94c0f8121a0e3" - integrity sha512-9aCWj83dp3heTQGmGnZGdIWgxjZrr/7VQ0TGFHH5PKByxJKF2Hcr4qvaSUHhhGEa3MSsDjTL1YDP8RAgdL5/Cg== - -"@oxc-parser/binding-linux-x64-musl@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.130.0.tgz#127c87488a0d23bc0990346c66ffa6e6f8f82fc8" - integrity sha512-afXt87aZBqrUVli8TB/I8H1G50RDWcwirjWtXGXYqJ2ZqWEiErH7V72j3LUSDZaivmtu2OLX0KQ/mbhP81mr7A== - -"@oxc-parser/binding-openharmony-arm64@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.130.0.tgz#9313e4d25badec37d9c349ecab9692af3c1bf556" - integrity sha512-I0NCrZV/YZuCGWgqwNN/GO/iXlLF2z+Wgc7u+Aa9N4P51oYeIa0XT+zVBUne4csO9GqxskXgI4g8JzzWGRpfOw== - -"@oxc-parser/binding-wasm32-wasi@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.130.0.tgz#9fb2d63b814bb7052774c50cd9b8c19047839a14" - integrity sha512-sJgQkGaBX0WJvPUDfwciex6IcTk5O5NLQ1bhEb6f3nBruh1GshKMRSMt2bxZlYrgBzjyBbJzsnO+InPG0bg+fA== +"@oxc-parser/binding-android-arm-eabi@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.135.0.tgz#2c9d8eb7ebeb532c6de7cdbf477a443520c65afb" + integrity sha512-sHeZItACNcA5WRAWqF6ixriR4GkZDyY10gVgnZU7pXku1DjHFATSqnwZM809jl0gXPHxb6fKzYQCK7bNK5cACQ== + +"@oxc-parser/binding-android-arm64@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.135.0.tgz#29c22038921726520e3dba33e159c2188b80ca23" + integrity sha512-wPte+SzgzWWFgMSF8YZDNM+tBXtJg0AXBi7+tU3yS2z1f2Af9kRLZLKuJojADmuD/cZexmnMHHC3SDItTW77Iw== + +"@oxc-parser/binding-darwin-arm64@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.135.0.tgz#938661183cf8364061c0ff361c6ce5e1fd2c6f3d" + integrity sha512-BmKz3lHIsqVos+9aPcdYCT9MG3APoUyM43KlEFhJMWNVDOGG8FKyiFz81Bc+mGz2o0hpuQ3PfXLfVWJrKXjo2g== + +"@oxc-parser/binding-darwin-x64@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.135.0.tgz#3c3ba820f23be26b2b4e51107d1dbd248833cffe" + integrity sha512-dM8BS+8+Br1fNvmh2QZbGiHaYttwLebRa6J4Uz9vuFzMNmvsdRYwf7993ptOaV0JTrR63AaoVLjX7nhWbijxjQ== + +"@oxc-parser/binding-freebsd-x64@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.135.0.tgz#c8e3a43c52c5fe6b4daf9d0da1c1998d677d0f52" + integrity sha512-xlZnvvJdR9bGu2pOhvR5hMuKPHCE6Sa9owK5A484mzjHdm75VRV5nCs5w/jkmGODMMTFc+KN7EnZqEieM813kw== + +"@oxc-parser/binding-linux-arm-gnueabihf@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.135.0.tgz#9be1173702b296be51353c4a64cd734633bad406" + integrity sha512-PSR8LmBK/H/PQRiN8g7RebQgZX/ntVCrdT/JBfNxE5ezdHG1s2i4rbazsRJYD83TTI1MmgTpC0MGL42PLtskQQ== + +"@oxc-parser/binding-linux-arm-musleabihf@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.135.0.tgz#c42cbeaa92be8d0121c210a11e05b833295561f4" + integrity sha512-I85GJXzfUsigkkk7Ngdz95C217M4FdUi1Z2HrX5UyPmURobwQZ7m2bbUvwFkz4VGZd+lymFGKHvDZ3RQC9qOzA== + +"@oxc-parser/binding-linux-arm64-gnu@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.135.0.tgz#e603b2f2bf8e85c4a29bf1c55fe39f924bff7faa" + integrity sha512-zqEY0npz0g0aGZj/8a5BclunjVDytsBQHYtIC10Gd26HcrLwbVF6YDbqRQjunMGYdSo97u6xOBl05aTDI2diDQ== + +"@oxc-parser/binding-linux-arm64-musl@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.135.0.tgz#22de895f90bed56d28ad4dd5cd5a64a335ecf3f3" + integrity sha512-mWAfprP819gQ2qYst1RxgTI8b/z0b29OpoKfRflIXLHde2dZLihQD4g47Onuvtpo5GPIkMYPRlX9QoeZfs/GnQ== + +"@oxc-parser/binding-linux-ppc64-gnu@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.135.0.tgz#23554f1e8af45bbd06149f2bcb787eb91ab6290e" + integrity sha512-gri8c2AOmJKJwOux2KTHFBfUaXoJURuVMKhmKEi/2hTF55cQteTDV2XNfTiE5oCC+Tnem1Y4/MWzcyDadtsSag== + +"@oxc-parser/binding-linux-riscv64-gnu@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.135.0.tgz#b90a2358f2e83fc828ba0bd1cbc9ba029786fa5d" + integrity sha512-Y2tkupCG5wo0SxH2rMLG4d4Kmv6DaM3sBp+GuM5lox0S8Za6VxKgQrY2Mut088QQxKkEE89n/4CCCgmw2o0e3Q== + +"@oxc-parser/binding-linux-riscv64-musl@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.135.0.tgz#6dde3858aadfd46516e5e31d98add34f0d482fb8" + integrity sha512-xDRJq6i6WTynjeP+ISbDpyH4p9BaJ0wuQcL0lCSDkt9qOXC9dmwpOu1VG/TlwmPI3KpYntmO9nJCuc3TMTsNBA== + +"@oxc-parser/binding-linux-s390x-gnu@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.135.0.tgz#f272bb57f746275b7960df5b2fc85ff4e1b32542" + integrity sha512-V4MoUuiCRNvihxhIufRxvK+ka013V4joTSK0FAGA1KEjLuNprfH6N/Qw2uxQEVIFuNYMhD/hV6xJ/ptbzlKdHg== + +"@oxc-parser/binding-linux-x64-gnu@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.135.0.tgz#90bc1fff2dbcca59f270f081c5c6fb21afef31ce" + integrity sha512-JCFZ7zM7KXOKoPAbK/ZB4wY0M1jxRECiem2UQuiXLjzGqS9+hno7mtX+qyK2F7HWK2xPhyJb+frpcOtk5DKOtg== + +"@oxc-parser/binding-linux-x64-musl@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.135.0.tgz#adc1e6c8e700d40ecb9a9a8fc2080565854f01b1" + integrity sha512-9jSVS1b3hOV7sdKH4aA2DFfnTz0RgQd0v2BefR+LYbH8yIlmSM22JJZbAAjVeVXmFgUAk3zJQ1tpE/Nd+Vi2YQ== + +"@oxc-parser/binding-openharmony-arm64@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.135.0.tgz#3225195a159166fa6dad7d8f1db328923295c810" + integrity sha512-M857ZLBSdn1Uy/SJJz5zh0qGu67B4P9omCgXGBU2LLqTzraX6ZjVNaKq5yW1PDw/LgJXDXR/dbZfgmB310f11Q== + +"@oxc-parser/binding-wasm32-wasi@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.135.0.tgz#03be9f6a6ee3cc88348835842d31f62654ce0253" + integrity sha512-2w6DVcntQZX9U5RhXtgiWb3FLWFB5EcwI1U8yr3htOCJUJjagN4BFUHz/Y/d9ZsumndZ6ByxxWEtbUZNE1bfFw== dependencies: "@emnapi/core" "1.10.0" "@emnapi/runtime" "1.10.0" "@napi-rs/wasm-runtime" "^1.1.4" -"@oxc-parser/binding-win32-arm64-msvc@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.130.0.tgz#939e48db2b47c93e7e3d4601c8eb6ff113ecc1db" - integrity sha512-bjcma99sQrNh6RY4mPO9yTkfxql6TDFoN3HWdK31RCKXwNhcDgJXW/l8PUtzKNiQ+9vpKJfJtQq+LklBuxSOBA== +"@oxc-parser/binding-win32-arm64-msvc@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.135.0.tgz#8ec307640f1ee3d34b5059c7489f59ea858a452c" + integrity sha512-rX1U8+IH2Z37EJjDXKa1iifvUQAdba+vZ4Ewj1iaG5eA/QaSybzclCOwtWa0/5BuUQnnK/T2JHUEFrwhL6Ck2Q== -"@oxc-parser/binding-win32-ia32-msvc@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.130.0.tgz#ed50388593afc1b97f57d598edf4d51fe3e6d6fa" - integrity sha512-hRYbv6HhpSTzT4xTiIkadLI7upLQxuOdLPR/9nL1fTjwhgutBTPXrwaAPb/jTFVx6/8C7Jb5HcUKhmNwloTbFA== +"@oxc-parser/binding-win32-ia32-msvc@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.135.0.tgz#5cb34346b7fb33e7bf85aae9908739fb55bf7673" + integrity sha512-9FAisBbH1QICGAjlJobiuKGd/jOuVmyqniWdQMwTa5SkCl6hhuotBCJf1n46B0flYbSOR5TzfV9HZCWSyb3c/Q== -"@oxc-parser/binding-win32-x64-msvc@0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.130.0.tgz#e405110c0812d028c69775c35c6fb235f0fdff55" - integrity sha512-RBpA9TsRucJq6HNVNCFF1iKg+QeTkLdZf7hi4xaOGCPvMZWvDHjQgSOEZMUpuW4JNciHbxNhLEYmz5CVygjVGQ== +"@oxc-parser/binding-win32-x64-msvc@0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.135.0.tgz#cd72eec60d628e315361e18bda31af7bdf0cd648" + integrity sha512-wYF+A2AzJ2n7ul6q+Z2G/ia0S2+8cUp0AgWZzoFvF4WmUcl1P7p+o6se1Gdr5wGnWuF0iAMIkGddrjCarNr2yA== -"@oxc-project/types@^0.130.0": - version "0.130.0" - resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.130.0.tgz#a7825148711dc28805c46cfc21d94b63a4d41e88" - integrity sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q== +"@oxc-project/types@^0.135.0": + version "0.135.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.135.0.tgz#b41e53f6e42461dfb9588b1e3e113dac988ef1cb" + integrity sha512-wR+xRdFkUBMvcAjBJ2q2kcZM6d+DKu2NgoOyxZgYwZdLhmiv6+rnO8PZ/P68kMiZtIKm+pW7zyEJ4kSOs0vo+Q== "@oxc-resolver/binding-android-arm-eabi@11.20.0": version "11.20.0" @@ -1663,21 +1670,21 @@ "@parcel/watcher-win32-ia32" "2.5.6" "@parcel/watcher-win32-x64" "2.5.6" -"@rc-component/async-validator@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-5.1.0.tgz#e81f31e676d9cadc71e4310bbf1749c7a5882291" - integrity sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA== +"@rc-component/async-validator@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/async-validator/-/async-validator-6.0.0.tgz#1c20b8864f69bac63b7876b1321697c2ea71c68d" + integrity sha512-D3AGQwdyE58gmvx6waVSXJ80JGO+IY5L2O8HDnSOex7JNlzB3GuN/4hyHNTdhy2qtOhkpbIjmeAN3tL993wKbA== dependencies: "@babel/runtime" "^7.24.4" -"@rc-component/cascader@~1.15.0": - version "1.15.0" - resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.15.0.tgz#554cba8e01e94a1288547cec96422b2cfc73ff40" - integrity sha512-ZzpMtwFCRo3fbXHuDnncARJMZQjdqA2w7aDuPofNQt+aDx39st1hgfIpEwTBLhe2Hqsvs/zOr8RTtgxTkCPySw== +"@rc-component/cascader@~1.16.1": + version "1.16.1" + resolved "https://registry.yarnpkg.com/@rc-component/cascader/-/cascader-1.16.1.tgz#94193ee55009219999a46e005a0f8589c8c021a2" + integrity sha512-wxLopwM+EBed0zNNGdnGE4coYoqcO+XD42fHgn+pDvO+XzhNFbdgSlSNXdKocIYqccvqgWvoxDPNb0OVRdi59A== dependencies: - "@rc-component/select" "~1.6.0" - "@rc-component/tree" "~1.3.0" - "@rc-component/util" "^1.4.0" + "@rc-component/select" "~1.7.1" + "@rc-component/tree" "~1.3.2" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" "@rc-component/checkbox@~2.0.0": @@ -1708,11 +1715,11 @@ clsx "^2.1.1" "@rc-component/context@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-2.0.1.tgz#88c7a565ae92c34a7f02f33c34b145e4039deed0" - integrity sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw== + version "2.0.2" + resolved "https://registry.yarnpkg.com/@rc-component/context/-/context-2.0.2.tgz#b1047ad96501630d33f046892c8121f29629f38c" + integrity sha512-uiGpAlblCNlziHPwj4S4Iy/oemeuz/hR03mbiEjTCXwsqOIN3BOzsRMyDwpyO5Fm0vIEEJRUf9ZtbRLbhksuTA== dependencies: - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.11.0" "@rc-component/dialog@~1.9.0": version "1.9.0" @@ -1743,12 +1750,12 @@ "@rc-component/util" "^1.2.1" clsx "^2.1.1" -"@rc-component/form@~1.8.1": - version "1.8.2" - resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.2.tgz#ba05c5e4b05e58f2c851ee4d792a7aa750f1d854" - integrity sha512-ZidCvOLmM9Xr+3vzk4UAoR7Aj1W/5IHyrzlBB7sNkygpTeRVrohQSo4TN7W/nARTH+nt8zSAPsn4BEl4zLEO2g== +"@rc-component/form@~1.8.3": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@rc-component/form/-/form-1.8.5.tgz#20571cfd401dc38c74c38cdf4722ddc6c23a9806" + integrity sha512-d24EYtvUOBhxEtSd/EqIu9DaMuqrWF2IRIvAFCTM6NQ/GJIYNr8DvEpUSUlv2uPxEJ0ZPwYQ+wwlGIAaiHvdrw== dependencies: - "@rc-component/async-validator" "^5.1.0" + "@rc-component/async-validator" "^6.0.0" "@rc-component/util" "^1.11.1" clsx "^2.1.1" @@ -1771,7 +1778,7 @@ "@rc-component/util" "^1.4.0" clsx "^2.1.1" -"@rc-component/input@~1.3.0": +"@rc-component/input@~1.3.0", "@rc-component/input@~1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@rc-component/input/-/input-1.3.1.tgz#230b8b59cdde8521d50f0eede63ddacb61cc0cd3" integrity sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig== @@ -1791,7 +1798,7 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/menu@~1.3.0": +"@rc-component/menu@~1.3.0", "@rc-component/menu@~1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@rc-component/menu/-/menu-1.3.1.tgz#16cae71a01080914e8bac08359fccdda7bfce540" integrity sha512-pSZl9nBPgKgxN0aaW7NilIBEwWsc+43S+ulGdWAg9afak96dNOGWsGx0DLLBB1VQsAJvo6bQMTDzXoPlEHsBEw== @@ -1803,18 +1810,18 @@ clsx "^2.1.1" "@rc-component/mini-decimal@^1.0.1": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.3.tgz#461d0cd2a91efd3c92503763d8c00625552d211f" - integrity sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/@rc-component/mini-decimal/-/mini-decimal-1.1.4.tgz#36bccedbffb54662ba314a26f63910414f48bc44" + integrity sha512-xiuXcaCwyOWpD8a8scdExFl+bntNphAW8XeenL1ig2en0AAZY0Pcp4pC0dI22qJ+NvxKn9RoNIoRdqYU3BLH4w== dependencies: "@babel/runtime" "^7.18.0" -"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.2.tgz#bd96e0fd16ee9d98c1d9be14198f003e367d8feb" - integrity sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ== +"@rc-component/motion@^1.0.0", "@rc-component/motion@^1.1.3", "@rc-component/motion@^1.1.4", "@rc-component/motion@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@rc-component/motion/-/motion-1.3.3.tgz#6a7bbe0a9f070bd11642168f741d40e78bb28565" + integrity sha512-Xh3IszxvlSv3/PLYFyC2UZi9LNB83yOnkB/LNmRzaypZLvkhqUIPS7MQpGZcCMWrNsXV2p6YTSWbSGvFpEle9A== dependencies: - "@rc-component/util" "^1.2.0" + "@rc-component/util" "^1.11.0" clsx "^2.1.1" "@rc-component/mutate-observer@^2.0.1": @@ -1843,12 +1850,12 @@ "@rc-component/util" "^1.4.0" clsx "^2.1.1" -"@rc-component/pagination@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@rc-component/pagination/-/pagination-1.2.0.tgz#3a97abda8f1077f514e03a74b3b9c77f9e68499a" - integrity sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw== +"@rc-component/pagination@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@rc-component/pagination/-/pagination-1.3.0.tgz#ee66301e37a03974826fb3028a91a1aeacdcd0ac" + integrity sha512-12ahTY+HPITg1L2bjWKXUqBJe/oOnpA2QsChdCjthqLVf/e19StiCsv8OLKpWoHbc+8PFEkNjRqRqrLoRBHjFw== dependencies: - "@rc-component/util" "^1.3.0" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" "@rc-component/picker@~1.10.0": @@ -1863,11 +1870,11 @@ clsx "^2.1.1" "@rc-component/portal@^2.1.0", "@rc-component/portal@^2.1.2", "@rc-component/portal@^2.1.3", "@rc-component/portal@^2.2.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-2.2.0.tgz#ec4c6c3de2cd09fa3ce545f2439a7eb84852a7b9" - integrity sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@rc-component/portal/-/portal-2.2.1.tgz#37c34b4c8cd73f53cc7072c96dd0e9ac332669ec" + integrity sha512-ck+r1kW/JSv0wxPji3KN2ss9K6Z0qqwusw/mf/0JobXhZ8hC2ejZwCJObW/SvDi0uhA0VzmCnx0CaCci95tcmA== dependencies: - "@rc-component/util" "^1.2.1" + "@rc-component/util" "^1.11.0" clsx "^2.1.1" "@rc-component/progress@~1.0.2": @@ -1878,10 +1885,10 @@ "@rc-component/util" "^1.2.1" clsx "^2.1.1" -"@rc-component/qrcode@~1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-1.1.1.tgz#909f181bb9a7469d32a6e96c7f35476d4bd92008" - integrity sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA== +"@rc-component/qrcode@~2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@rc-component/qrcode/-/qrcode-2.0.0.tgz#ef4134c213002e7a43edbe609b24248914de4974" + integrity sha512-aAv3QhPP1xyafuTZOxub6a54pCeBnN3IwQkpETrBtthq4BL5IgxnCbuoBWPDpdLw1y1j6BgBUCAKV92+yX06Dw== dependencies: "@babel/runtime" "^7.24.7" @@ -1910,15 +1917,15 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/select@~1.6.0", "@rc-component/select@~1.6.15": - version "1.6.15" - resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.6.15.tgz#de2a3c8b020834cabd600b52de573a328c061eef" - integrity sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g== +"@rc-component/select@~1.7.0", "@rc-component/select@~1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.7.1.tgz#cdda0ac185f00ebed1c85e7809ae1f7855a9f7ab" + integrity sha512-GZ1cMJk2xQh0VHyOQjjG8drYL4iu24NcbkXioUcReQOCUr+ub/3fmRonZe6cRPEZhWMbJdeHsqnEltogDaZ5Tg== dependencies: "@rc-component/overflow" "^1.0.0" "@rc-component/trigger" "^3.0.0" - "@rc-component/util" "^1.3.0" - "@rc-component/virtual-list" "^1.0.1" + "@rc-component/util" "^1.11.1" + "@rc-component/virtual-list" "^1.2.0" clsx "^2.1.1" "@rc-component/slider@~1.0.1": @@ -1945,7 +1952,7 @@ "@rc-component/util" "^1.3.0" clsx "^2.1.1" -"@rc-component/table@~1.10.0": +"@rc-component/table@~1.10.2": version "1.10.2" resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.10.2.tgz#7b052fd5eb2ccf6996a93eebeb7af7036a262c8b" integrity sha512-b3PjqB9Gp25p5t/zq+9QrbXbodkptT8/zvLmwgd2FNPUUtaYyDnQqfxeD5a7ao8E8lpinLHsi2u2vdfPhyNvAw== @@ -1956,7 +1963,7 @@ "@rc-component/virtual-list" "^1.0.1" clsx "^2.1.1" -"@rc-component/tabs@~1.9.0": +"@rc-component/tabs@~1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@rc-component/tabs/-/tabs-1.9.1.tgz#52b9cb0392c718fba43e7d558f46f6c910d19acb" integrity sha512-6mY08Fce6aNOHuGsxbzT+f2ekgL9mg1cGGHkittMlVGymjGg+kGupu5v90sRxcUd/paRU9jclLLXtF/PkK1FUA== @@ -1987,17 +1994,17 @@ "@rc-component/util" "^1.7.0" clsx "^2.1.1" -"@rc-component/tree-select@~1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.9.0.tgz#13ea516478b6cb558e04181abb0a01ae6fbdd31f" - integrity sha512-GXcFe15a+trUl1/J3OHWQhsVWFpwFpGFK2cqYWZ1sK22Zs3KZTvMwDpzr75PIo1s6QVioVxpE/pRwRopkeDQ6w== +"@rc-component/tree-select@~1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.10.0.tgz#72e337fd58591f677404189cb3e9d3f718cd3332" + integrity sha512-E1U4pn2LAbXEhLJdzIzid7WYbIuFbkTIctuFoeC6weppf8UbPR3+YYB6/ay0c0ksand4gXMRQpa1Z60Auo7VJA== dependencies: - "@rc-component/select" "~1.6.0" + "@rc-component/select" "~1.7.0" "@rc-component/tree" "~1.3.0" "@rc-component/util" "^1.4.0" clsx "^2.1.1" -"@rc-component/tree@~1.3.0", "@rc-component/tree@~1.3.1": +"@rc-component/tree@~1.3.0", "@rc-component/tree@~1.3.2": version "1.3.2" resolved "https://registry.yarnpkg.com/@rc-component/tree/-/tree-1.3.2.tgz#4b0c13564314eff61ca948c18ef923b87c9d7e44" integrity sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA== @@ -2007,10 +2014,10 @@ "@rc-component/virtual-list" "^1.2.0" clsx "^2.1.1" -"@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.0": - version "3.9.0" - resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-3.9.0.tgz#d4d2df167e9aced1bf17672d9104a3297663f766" - integrity sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg== +"@rc-component/trigger@^3.0.0", "@rc-component/trigger@^3.6.15", "@rc-component/trigger@^3.7.1", "@rc-component/trigger@^3.9.1": + version "3.9.1" + resolved "https://registry.yarnpkg.com/@rc-component/trigger/-/trigger-3.9.1.tgz#3f730315c558bc392921a563ac9109a1d094ef23" + integrity sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q== dependencies: "@rc-component/motion" "^1.1.4" "@rc-component/portal" "^2.2.0" @@ -2018,7 +2025,7 @@ "@rc-component/util" "^1.2.1" clsx "^2.1.1" -"@rc-component/upload@~1.1.0": +"@rc-component/upload@~1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@rc-component/upload/-/upload-1.1.1.tgz#90d16edcdaeb104ffa9111fd0b428061de7c21ac" integrity sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA== @@ -2045,9 +2052,9 @@ clsx "^2.1.1" "@repeaterjs/repeater@^3.0.4", "@repeaterjs/repeater@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.6.tgz#be23df0143ceec3c69f8b6c2517971a5578fdaa2" - integrity sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA== + version "3.1.0" + resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.1.0.tgz#312b20a655e5b3ebfe8b1e04ec7df567bcb48d87" + integrity sha512-TaoVksZRSx2KWYYpyLQtMQXXeS98VsgZImzW65xmiVgbYhXLk+aEsmzPLirqVuE4/XuUapH2iMtxUzaBNDzdSQ== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -2061,7 +2068,7 @@ dependencies: tslib "^2.8.0" -"@tybys/wasm-util@^0.10.1": +"@tybys/wasm-util@^0.10.2": version "0.10.2" resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.10.2.tgz#12b3a1b33db1f9cad4ddff1f604ab7dd00bf464e" integrity sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg== @@ -2096,18 +2103,18 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/node@*": - version "25.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.1.tgz#3bda556db500ae4319c08e7fc9ab94f19013ba0b" - integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== + version "25.9.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.3.tgz#11dfe7a33e68fa5c560f0aa76cc5595621ef26b9" + integrity sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg== dependencies: undici-types ">=7.24.0 <7.24.7" "@types/node@^24.10.1": - version "24.12.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.4.tgz#2709745569811dcbdc57c097fafdd387c6330382" - integrity sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA== + version "24.13.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.13.2.tgz#3b9b280a7055128359f125eb1067d9a190f39854" + integrity sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA== dependencies: - undici-types "~7.16.0" + undici-types "~7.18.0" "@types/react-dom@^19.2.3": version "19.2.3" @@ -2115,9 +2122,9 @@ integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== "@types/react@^19.2.7": - version "19.2.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.15.tgz#9e2c6a4251a290f5c525c3143caa872fa6e01e0d" - integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== + version "19.2.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.17.tgz#dccac365baa0f1734ec270ff4b51c89465e8dc7f" + integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw== dependencies: csstype "^3.2.2" @@ -2128,100 +2135,100 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz#8fc1e0a950c43270eaf0212dc060f7edaa42f9cf" - integrity sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw== +"@typescript-eslint/eslint-plugin@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz#6e4b7fee21f1983308e9e9b634ecbaf702c86006" + integrity sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/type-utils" "8.60.0" - "@typescript-eslint/utils" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.1" + "@typescript-eslint/type-utils" "8.61.1" + "@typescript-eslint/utils" "8.61.1" + "@typescript-eslint/visitor-keys" "8.61.1" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.60.0.tgz#38d611b8e658cb10850d4975e8a175a222fbcd6a" - integrity sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg== +"@typescript-eslint/parser@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.61.1.tgz#881fba60b50636249cdeea2e547bf75715254c72" + integrity sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg== dependencies: - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.1" + "@typescript-eslint/types" "8.61.1" + "@typescript-eslint/typescript-estree" "8.61.1" + "@typescript-eslint/visitor-keys" "8.61.1" debug "^4.4.3" -"@typescript-eslint/project-service@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.60.0.tgz#b82ab12e64d005d0c7163d1240c432381f1bde0f" - integrity sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg== +"@typescript-eslint/project-service@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.61.1.tgz#fcd9739964a40867eed55f1ac318d3909f24b4af" + integrity sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.60.0" - "@typescript-eslint/types" "^8.60.0" + "@typescript-eslint/tsconfig-utils" "^8.61.1" + "@typescript-eslint/types" "^8.61.1" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz#7617a4617c043fe235dcf066f9a40f106cfd2fd5" - integrity sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw== +"@typescript-eslint/scope-manager@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz#2479921a40fdb0afa18f5838fae6167264b417b2" + integrity sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w== dependencies: - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/types" "8.61.1" + "@typescript-eslint/visitor-keys" "8.61.1" -"@typescript-eslint/tsconfig-utils@8.60.0", "@typescript-eslint/tsconfig-utils@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz#3af78c48956227a407dea9626b8db8ca53f130d2" - integrity sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ== +"@typescript-eslint/tsconfig-utils@8.61.1", "@typescript-eslint/tsconfig-utils@^8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz#ca88080e0cf191d49516d7f300b67aa090d2254f" + integrity sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg== -"@typescript-eslint/type-utils@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz#6971a61bc4f3a1b2df45dcc14e26a43a88a4cb6a" - integrity sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg== +"@typescript-eslint/type-utils@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz#8fa18f453ee140893b47d339d1a6b64cac9b08a1" + integrity sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw== dependencies: - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" - "@typescript-eslint/utils" "8.60.0" + "@typescript-eslint/types" "8.61.1" + "@typescript-eslint/typescript-estree" "8.61.1" + "@typescript-eslint/utils" "8.61.1" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.60.0", "@typescript-eslint/types@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.60.0.tgz#e77ad768e933263b1960b2fe79de75fe1cc6e7db" - integrity sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA== +"@typescript-eslint/types@8.61.1", "@typescript-eslint/types@^8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.61.1.tgz#0c51f518e4e6848371a1c988e859d59eb7522d5a" + integrity sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA== -"@typescript-eslint/typescript-estree@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz#c102196a44414481190041c99eea1d854e66001b" - integrity sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g== +"@typescript-eslint/typescript-estree@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz#febbe70365ac0bf7611262b61b338fc8797965c7" + integrity sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg== dependencies: - "@typescript-eslint/project-service" "8.60.0" - "@typescript-eslint/tsconfig-utils" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/project-service" "8.61.1" + "@typescript-eslint/tsconfig-utils" "8.61.1" + "@typescript-eslint/types" "8.61.1" + "@typescript-eslint/visitor-keys" "8.61.1" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.60.0.tgz#6110cddaef87606ae4ca6f8bf81bb5949fc8e098" - integrity sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA== +"@typescript-eslint/utils@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.61.1.tgz#ffd1054de7dd33b7873cd6c6713ec6b0366316d3" + integrity sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.1" + "@typescript-eslint/types" "8.61.1" + "@typescript-eslint/typescript-estree" "8.61.1" -"@typescript-eslint/visitor-keys@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz#f2c41eedd3d7b03b808369fb2e3fb40a93783ec2" - integrity sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg== +"@typescript-eslint/visitor-keys@8.61.1": + version "8.61.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz#546cf102b4efdb72a9a08e63a1b0d7d745eb66eb" + integrity sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w== dependencies: - "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/types" "8.61.1" eslint-visitor-keys "^5.0.0" "@umijs/route-utils@^4.0.0": @@ -2365,9 +2372,9 @@ urlpattern-polyfill "^10.0.0" "@whatwg-node/node-fetch@^0.8.3": - version "0.8.5" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.5.tgz#5ab2226866ae10a9d0403f9d44bd684178963781" - integrity sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ== + version "0.8.6" + resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.8.6.tgz#22c5235f49c69c6b2cfc63d4a759de407a8fc664" + integrity sha512-BDMdYFcerLQkwA2RTldxOqRCs6ZQD1S7UgP3pUdGUkcbgTrP/V5ko77ZkCww9DHmC4lpoYuwigGfQYj285gMvA== dependencies: "@fastify/busboy" "^3.1.1" "@whatwg-node/disposablestack" "^0.0.6" @@ -2420,9 +2427,9 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.16.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" - integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + version "8.17.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.17.0.tgz#1785adb84faf8d8add10369b93826fc2bd08f1fe" + integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg== ajv@^6.14.0: version "6.15.0" @@ -2459,53 +2466,53 @@ ansi-styles@^6.2.1, ansi-styles@^6.2.3: integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== antd@^6.3.3: - version "6.4.3" - resolved "https://registry.yarnpkg.com/antd/-/antd-6.4.3.tgz#80a7aab9c13c35daa0e0e7eea80585ba57cb7203" - integrity sha512-6H2avkxCGfxcF67r3J2mwm9Ck50el1pks/73vfM1wDsPL/tPtj5vHuauMgJFnrqmq7CH3g8aoZ0VBQbt+jpAsw== + version "6.4.4" + resolved "https://registry.yarnpkg.com/antd/-/antd-6.4.4.tgz#a422610959b37ac4d4b766dbaac67ea2d8fd0785" + integrity sha512-lgPz4KhfhiYddV/qPYo0ieqWimCVgV2OQF72mbeGNixE753JWNnmEc7UNGy08wBS/zZ7hxrmX0pc5aX7EUaIIg== dependencies: "@ant-design/colors" "^8.0.1" "@ant-design/cssinjs" "^2.1.2" "@ant-design/cssinjs-utils" "^2.1.2" "@ant-design/fast-color" "^3.0.1" - "@ant-design/icons" "^6.2.3" + "@ant-design/icons" "^6.2.5" "@ant-design/react-slick" "~2.0.0" "@babel/runtime" "^7.29.2" - "@rc-component/cascader" "~1.15.0" + "@rc-component/cascader" "~1.16.1" "@rc-component/checkbox" "~2.0.0" "@rc-component/collapse" "~1.2.0" "@rc-component/color-picker" "~3.1.1" "@rc-component/dialog" "~1.9.0" "@rc-component/drawer" "~1.4.2" "@rc-component/dropdown" "~1.0.2" - "@rc-component/form" "~1.8.1" + "@rc-component/form" "~1.8.3" "@rc-component/image" "~1.9.0" - "@rc-component/input" "~1.3.0" + "@rc-component/input" "~1.3.1" "@rc-component/input-number" "~1.6.2" "@rc-component/mentions" "~1.9.0" - "@rc-component/menu" "~1.3.0" - "@rc-component/motion" "^1.3.2" + "@rc-component/menu" "~1.3.1" + "@rc-component/motion" "^1.3.3" "@rc-component/mutate-observer" "^2.0.1" "@rc-component/notification" "~2.0.7" - "@rc-component/pagination" "~1.2.0" + "@rc-component/pagination" "~1.3.0" "@rc-component/picker" "~1.10.0" "@rc-component/progress" "~1.0.2" - "@rc-component/qrcode" "~1.1.1" + "@rc-component/qrcode" "~2.0.0" "@rc-component/rate" "~1.0.1" "@rc-component/resize-observer" "^1.1.2" "@rc-component/segmented" "~1.3.0" - "@rc-component/select" "~1.6.15" + "@rc-component/select" "~1.7.1" "@rc-component/slider" "~1.0.1" "@rc-component/steps" "~1.2.2" "@rc-component/switch" "~1.0.3" - "@rc-component/table" "~1.10.0" - "@rc-component/tabs" "~1.9.0" + "@rc-component/table" "~1.10.2" + "@rc-component/tabs" "~1.9.1" "@rc-component/tooltip" "~1.4.0" "@rc-component/tour" "~2.4.0" - "@rc-component/tree" "~1.3.1" - "@rc-component/tree-select" "~1.9.0" - "@rc-component/trigger" "^3.9.0" - "@rc-component/upload" "~1.1.0" - "@rc-component/util" "^1.11.0" + "@rc-component/tree" "~1.3.2" + "@rc-component/tree-select" "~1.10.0" + "@rc-component/trigger" "^3.9.1" + "@rc-component/upload" "~1.1.1" + "@rc-component/util" "^1.11.1" clsx "^2.1.1" dayjs "^1.11.11" scroll-into-view-if-needed "^3.1.0" @@ -2655,9 +2662,9 @@ available-typed-arrays@^1.0.7: possible-typed-array-names "^1.0.0" axe-core@^4.10.0: - version "4.11.4" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.11.4.tgz#5b535e381ff1e61ffdd615e5483d16186d3b46a5" - integrity sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA== + version "4.12.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.12.1.tgz#69fe2bf6dfc0b24973af91b1d8e945f79e1b7c44" + integrity sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA== axobject-query@^4.1.0: version "4.1.0" @@ -2675,9 +2682,9 @@ balanced-match@^4.0.2: integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== baseline-browser-mapping@^2.10.12, baseline-browser-mapping@^2.9.19: - version "2.10.32" - resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz#b6b553a4285fdd606327a617de36a5351e3aaa64" - integrity sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg== + version "2.10.37" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz#3e636475b6b293244e2b23e2c71a2ab9d9e6ba7d" + integrity sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig== brace-expansion@^1.1.7: version "1.1.15" @@ -2752,9 +2759,9 @@ camel-case@^4.1.2: tslib "^2.0.3" caniuse-lite@^1.0.30001579, caniuse-lite@^1.0.30001782: - version "1.0.30001793" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz#238887ddf5fcfc8c36d872394d0a78a517312a72" - integrity sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA== + version "1.0.30001799" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55" + integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw== capital-case@^1.0.4: version "1.0.4" @@ -2905,9 +2912,9 @@ concat-map@0.0.1: integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concurrently@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-10.0.0.tgz#ceb3782d36a620ae7c05ff8085076c0d96323479" - integrity sha512-DRrk10z3sVPpguNe8od2cGNqZGqbT15rwAnxD4dG3b78mdNNb/gJyr8T834Oj518WcBmTktrt4FhdwZn09ZWSg== + version "10.0.3" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-10.0.3.tgz#0ae4bf732e958b1a607b47896dcaa836b72514e8" + integrity sha512-hc3LH4UaKWd/bbyDK/IGVa4RB6PtQ3CUYwtrkzqHn+wIG3Hr5fhpRlk0L/gCa8ZE1L/Ufj50Zho69cI5w8SQBA== dependencies: chalk "5.6.2" rxjs "7.8.2" @@ -2946,9 +2953,9 @@ cosmiconfig@^8.1.0: path-type "^4.0.0" cosmiconfig@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.1.tgz#df110631a8547b5d1a98915271986f06e3011379" - integrity sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ== + version "9.0.2" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.2.tgz#9e5615163becf6a82211fb33d2f68947c25d0c5e" + integrity sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg== dependencies: env-paths "^2.2.1" import-fresh "^3.3.0" @@ -3135,9 +3142,9 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: gopd "^1.2.0" electron-to-chromium@^1.5.328: - version "1.5.363" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.363.tgz#6e9ee6bbb98d4aa12e96853e89993990d577eec8" - integrity sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA== + version "1.5.373" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.373.tgz#7a29b55c79bd9a8f47d0cd501985c4da2bad2f9e" + integrity sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA== emoji-regex@^10.3.0: version "10.6.0" @@ -3166,6 +3173,16 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract-get@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-abstract-get/-/es-abstract-get-1.0.0.tgz#1eae87101f42bedeb6a740e8c5051271aef89088" + integrity sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg== + dependencies: + es-errors "^1.3.0" + es-object-atoms "^1.1.2" + is-callable "^1.2.7" + object-inspect "^1.13.4" + es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9, es-abstract@^1.24.0, es-abstract@^1.24.2: version "1.24.2" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.2.tgz#2dbd38c180735ee983f77585140a2706a963ed9a" @@ -3237,9 +3254,9 @@ es-errors@^1.3.0: integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-iterator-helpers@^1.2.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz#8f4ff1f3603cbd09fbdb72c747a679779a65cc7f" - integrity sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw== + version "1.3.3" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz#0a229dc38ada0450b8cfaa85809fd73dbb34113c" + integrity sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g== dependencies: call-bind "^1.0.9" call-bound "^1.0.4" @@ -3258,7 +3275,7 @@ es-iterator-helpers@^1.2.1: iterator.prototype "^1.1.5" math-intrinsics "^1.1.0" -es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1, es-object-atoms@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b" integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw== @@ -3283,13 +3300,15 @@ es-shim-unscopables@^1.0.2, es-shim-unscopables@^1.1.0: hasown "^2.0.2" es-to-primitive@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" - integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + version "1.3.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.1.tgz#abd6ef5b12d7c25bcd9eb3a7ef63e568b451ba4a" + integrity sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g== dependencies: + es-abstract-get "^1.0.0" + es-errors "^1.3.0" is-callable "^1.2.7" - is-date-object "^1.0.5" - is-symbol "^1.0.4" + is-date-object "^1.1.0" + is-symbol "^1.1.1" escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" @@ -3307,11 +3326,11 @@ escape-string-regexp@^4.0.0: integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== eslint-config-next@^16.0.8: - version "16.2.6" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.6.tgz#1a04578efe6fde5cdb6265e98e88c9f61927e2c7" - integrity sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q== + version "16.2.9" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-16.2.9.tgz#cc3f63fe657cdb980078835f85f1f518cd549276" + integrity sha512-olGtBrs07bQchpaJWeqbk9GaMoU0oGmN/pYNEBXSbfgKngb5uHnPe37X6tVeh6DJfaWFQildvinGEOrolo5fmw== dependencies: - "@next/eslint-plugin-next" "16.2.6" + "@next/eslint-plugin-next" "16.2.9" eslint-import-resolver-node "^0.3.6" eslint-import-resolver-typescript "^3.5.2" eslint-plugin-import "^2.32.0" @@ -3344,9 +3363,9 @@ eslint-import-resolver-typescript@^3.5.2: unrs-resolver "^1.6.2" eslint-module-utils@^2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz#f76d3220bfb83c057651359295ab5854eaad75ff" - integrity sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw== + version "2.13.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz#882beaf64927567358816bf4dc0f050fd52e0fb9" + integrity sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ== dependencies: debug "^3.2.7" @@ -3452,16 +3471,16 @@ eslint-visitor-keys@^5.0.0, eslint-visitor-keys@^5.0.1: integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== eslint@^10.1.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.4.0.tgz#d86b6c405de0f19f3318c47139b8cb6771b3f592" - integrity sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ== + version "10.5.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.5.0.tgz#5fca69d6b41fe7e00ba22d4100b2e44efe439ad5" + integrity sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" "@eslint/config-array" "^0.23.5" "@eslint/config-helpers" "^0.6.0" "@eslint/core" "^1.2.1" - "@eslint/plugin-kit" "^0.7.1" + "@eslint/plugin-kit" "^0.7.2" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" @@ -3675,16 +3694,19 @@ function-bind@^1.1.2: integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.6, function.prototype.name@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" - integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + version "1.2.0" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.2.0.tgz#758f3e84fa542672454bd5e14cb081a5ce07f70c" + integrity sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.3" - define-properties "^1.2.1" + call-bind "^1.0.9" + call-bound "^1.0.4" + es-define-property "^1.0.1" + es-errors "^1.3.0" functions-have-names "^1.2.3" - hasown "^2.0.2" + has-property-descriptors "^1.0.2" + hasown "^2.0.4" is-callable "^1.2.7" + is-document.all "^1.0.0" functions-have-names@^1.2.3: version "1.2.3" @@ -3842,9 +3864,9 @@ graphql-ws@^6.0.6: integrity sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw== graphql@^16.12.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.14.0.tgz#f1128a74b16a34d1245c8436bb07b488d87b83e1" - integrity sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q== + version "16.14.2" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.14.2.tgz#83faf25869e3df727cc855161db5da85b0e5b2c0" + integrity sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA== has-bigints@^1.0.2: version "1.1.0" @@ -3882,10 +3904,10 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -hasown@^2.0.2, hasown@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.3.tgz#5e5c2b15b60370a4c7930c383dfb76bf17bc403c" - integrity sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg== +hasown@^2.0.2, hasown@^2.0.3, hasown@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" + integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== dependencies: function-bind "^1.1.2" @@ -3939,9 +3961,9 @@ ignore@^7.0.5: integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg== immutable@^5.1.5: - version "5.1.5" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.5.tgz#93ee4db5c2a9ab42a4a783069f3c5d8847d40165" - integrity sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A== + version "5.1.6" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.6.tgz#21639bc80f9a0713e141a5f5a154ef9fdabf36dd" + integrity sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ== import-fresh@^3.3.0: version "3.3.1" @@ -4066,7 +4088,7 @@ is-data-view@^1.0.1, is-data-view@^1.0.2: get-intrinsic "^1.2.6" is-typed-array "^1.1.13" -is-date-object@^1.0.5, is-date-object@^1.1.0: +is-date-object@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== @@ -4074,6 +4096,13 @@ is-date-object@^1.0.5, is-date-object@^1.1.0: call-bound "^1.0.2" has-tostringtag "^1.0.2" +is-document.all@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-document.all/-/is-document.all-1.0.0.tgz#163a4bfb362c6ed7b118ce46cdecc4e37dee3195" + integrity sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g== + dependencies: + call-bound "^1.0.4" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -4183,7 +4212,7 @@ is-string@^1.1.1: call-bound "^1.0.3" has-tostringtag "^1.0.2" -is-symbol@^1.0.4, is-symbol@^1.1.1: +is-symbol@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== @@ -4291,9 +4320,9 @@ jiti@^2.0.0, jiti@^2.3.0, jiti@^2.7.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + version "4.2.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.2.0.tgz#2bd9e85682dd91bd469afb809d816043b3d49524" + integrity sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw== dependencies: argparse "^2.0.1" @@ -4367,22 +4396,21 @@ keyv@^4.5.4: json-buffer "3.0.1" knip@^6.14.2: - version "6.14.2" - resolved "https://registry.yarnpkg.com/knip/-/knip-6.14.2.tgz#264f725dd2bf2104e70561b0fee7432d2c77fd76" - integrity sha512-Vg3JhIINjZew1I7qAFI4UHemW1mc4azP/BxJvsq9eGDfxpGO7oVCuD/bsWkog9TO/ZwwJeAeOMFZ1kd9jnY9+Q== + version "6.17.1" + resolved "https://registry.yarnpkg.com/knip/-/knip-6.17.1.tgz#5210aba651030cd1b1f8b29e7050de4a4d9b09c3" + integrity sha512-HcQsZSQ4Ymhuay4BVzJtM5pFZNDSomYYqcNCZOSITPQh9g18a09DqziWAxSt2G+BH9wGlG+0ZjWpEnaFlnKseQ== dependencies: fdir "^6.5.0" formatly "^0.3.0" get-tsconfig "4.14.0" jiti "^2.7.0" - minimist "^1.2.8" - oxc-parser "^0.130.0" - oxc-resolver "^11.19.1" + oxc-parser "^0.135.0" + oxc-resolver "^11.20.0" picomatch "^4.0.4" smol-toml "^1.6.1" strip-json-comments "5.0.3" - tinyglobby "^0.2.16" - unbash "^3.0.0" + tinyglobby "^0.2.17" + unbash "^4.0.1" yaml "^2.9.0" zod "^4.1.11" @@ -4554,7 +4582,7 @@ minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8: +minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -4571,10 +4599,10 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -mute-stream@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-4.0.0.tgz#3b04c237748aa7a2c93d8b5093cb001eaca1fbeb" - integrity sha512-gSrprq0fJ3EiOErzjdIZrjysVVmJ4uu1QWfCDss5LypA5OXvrMje5Ym5z6V6RLyJ2eF87lasX7t6a0AnFvZblg== +mute-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-3.0.0.tgz#cd8014dd2acb72e1e91bb67c74f0019e620ba2d1" + integrity sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw== nanoid@^3.3.6: version "3.3.12" @@ -4592,25 +4620,25 @@ natural-compare@^1.4.0: integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== next@^16.0.8: - version "16.2.6" - resolved "https://registry.yarnpkg.com/next/-/next-16.2.6.tgz#4564833d2865efc598b7c63541b5771792d3d811" - integrity sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw== + version "16.2.9" + resolved "https://registry.yarnpkg.com/next/-/next-16.2.9.tgz#320c5b5701deeb18c0b596d137ca5dccf976682c" + integrity sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww== dependencies: - "@next/env" "16.2.6" + "@next/env" "16.2.9" "@swc/helpers" "0.5.15" baseline-browser-mapping "^2.9.19" caniuse-lite "^1.0.30001579" postcss "8.4.31" styled-jsx "5.1.6" optionalDependencies: - "@next/swc-darwin-arm64" "16.2.6" - "@next/swc-darwin-x64" "16.2.6" - "@next/swc-linux-arm64-gnu" "16.2.6" - "@next/swc-linux-arm64-musl" "16.2.6" - "@next/swc-linux-x64-gnu" "16.2.6" - "@next/swc-linux-x64-musl" "16.2.6" - "@next/swc-win32-arm64-msvc" "16.2.6" - "@next/swc-win32-x64-msvc" "16.2.6" + "@next/swc-darwin-arm64" "16.2.9" + "@next/swc-darwin-x64" "16.2.9" + "@next/swc-linux-arm64-gnu" "16.2.9" + "@next/swc-linux-arm64-musl" "16.2.9" + "@next/swc-linux-x64-gnu" "16.2.9" + "@next/swc-linux-x64-musl" "16.2.9" + "@next/swc-win32-arm64-msvc" "16.2.9" + "@next/swc-win32-x64-msvc" "16.2.9" sharp "^0.34.5" no-case@^3.0.4: @@ -4651,9 +4679,9 @@ node-fetch@^3.3.2: formdata-polyfill "^4.0.10" node-releases@^2.0.36: - version "2.0.46" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.46.tgz#d188a129a83f5e03a101aacb58f260f2ee8faaa1" - integrity sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ== + version "2.0.47" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.47.tgz#521bb2786da8eb140b748841c0b3b3a75334ffc4" + integrity sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og== nopt@^4.0.1: version "4.0.3" @@ -4814,35 +4842,35 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -oxc-parser@^0.130.0: - version "0.130.0" - resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.130.0.tgz#b8f03385db908d9fdfff49c8f37b1748b1f41596" - integrity sha512-X0PJ+NmOok8qP3vK9uaW431ngkdM9UPEK7KG466urtIL2+EYTEgbZK2yqe2MWKJKBjRlFweP/pJPx0x9muMEVw== +oxc-parser@^0.135.0: + version "0.135.0" + resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.135.0.tgz#ccd3d046bac747540ce6a57072ce0bc05733d84a" + integrity sha512-/DaPStu0s2zzNSRRniKyTPM6Z/o+DapOp2JYNKDL8AsgaBGPK2IdZyB87SQjVH+xeQPz+Qr9mrjglfkYgtbVRA== dependencies: - "@oxc-project/types" "^0.130.0" + "@oxc-project/types" "^0.135.0" optionalDependencies: - "@oxc-parser/binding-android-arm-eabi" "0.130.0" - "@oxc-parser/binding-android-arm64" "0.130.0" - "@oxc-parser/binding-darwin-arm64" "0.130.0" - "@oxc-parser/binding-darwin-x64" "0.130.0" - "@oxc-parser/binding-freebsd-x64" "0.130.0" - "@oxc-parser/binding-linux-arm-gnueabihf" "0.130.0" - "@oxc-parser/binding-linux-arm-musleabihf" "0.130.0" - "@oxc-parser/binding-linux-arm64-gnu" "0.130.0" - "@oxc-parser/binding-linux-arm64-musl" "0.130.0" - "@oxc-parser/binding-linux-ppc64-gnu" "0.130.0" - "@oxc-parser/binding-linux-riscv64-gnu" "0.130.0" - "@oxc-parser/binding-linux-riscv64-musl" "0.130.0" - "@oxc-parser/binding-linux-s390x-gnu" "0.130.0" - "@oxc-parser/binding-linux-x64-gnu" "0.130.0" - "@oxc-parser/binding-linux-x64-musl" "0.130.0" - "@oxc-parser/binding-openharmony-arm64" "0.130.0" - "@oxc-parser/binding-wasm32-wasi" "0.130.0" - "@oxc-parser/binding-win32-arm64-msvc" "0.130.0" - "@oxc-parser/binding-win32-ia32-msvc" "0.130.0" - "@oxc-parser/binding-win32-x64-msvc" "0.130.0" - -oxc-resolver@^11.19.1: + "@oxc-parser/binding-android-arm-eabi" "0.135.0" + "@oxc-parser/binding-android-arm64" "0.135.0" + "@oxc-parser/binding-darwin-arm64" "0.135.0" + "@oxc-parser/binding-darwin-x64" "0.135.0" + "@oxc-parser/binding-freebsd-x64" "0.135.0" + "@oxc-parser/binding-linux-arm-gnueabihf" "0.135.0" + "@oxc-parser/binding-linux-arm-musleabihf" "0.135.0" + "@oxc-parser/binding-linux-arm64-gnu" "0.135.0" + "@oxc-parser/binding-linux-arm64-musl" "0.135.0" + "@oxc-parser/binding-linux-ppc64-gnu" "0.135.0" + "@oxc-parser/binding-linux-riscv64-gnu" "0.135.0" + "@oxc-parser/binding-linux-riscv64-musl" "0.135.0" + "@oxc-parser/binding-linux-s390x-gnu" "0.135.0" + "@oxc-parser/binding-linux-x64-gnu" "0.135.0" + "@oxc-parser/binding-linux-x64-musl" "0.135.0" + "@oxc-parser/binding-openharmony-arm64" "0.135.0" + "@oxc-parser/binding-wasm32-wasi" "0.135.0" + "@oxc-parser/binding-win32-arm64-msvc" "0.135.0" + "@oxc-parser/binding-win32-ia32-msvc" "0.135.0" + "@oxc-parser/binding-win32-x64-msvc" "0.135.0" + +oxc-resolver@^11.20.0: version "11.20.0" resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.20.0.tgz#2b2a7a76de753b34be1d6031da1604c8cb580b08" integrity sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g== @@ -4988,7 +5016,7 @@ picomatch@^4.0.3, picomatch@^4.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589" integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== -possible-typed-array-names@^1.0.0: +possible-typed-array-names@^1.0.0, possible-typed-array-names@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz#93e3582bc0e5426586d9d07b79ee40fc841de4ae" integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg== @@ -5008,9 +5036,9 @@ prelude-ls@^1.2.1: integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== prettier@^3.3.2: - version "3.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0" - integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw== + version "3.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.4.tgz#f334f013ac04a96676f24dabc23c1c4ae1bae411" + integrity sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q== prop-types@^15.8.1: version "15.8.1" @@ -5059,9 +5087,9 @@ react-cookie@^8.0.1: universal-cookie "^8.0.0" react-dom@^19.2.1: - version "19.2.6" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7" - integrity sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g== + version "19.2.7" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.7.tgz#0450dc9ae9ddbff76ef196401cd8b8c7fb466ccc" + integrity sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ== dependencies: scheduler "^0.27.0" @@ -5076,9 +5104,9 @@ react-is@^18.2.0: integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== react@^19.2.1: - version "19.2.6" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.6.tgz#3dadb8e12b2a7934c1d5317973e5dce1301f9a4d" - integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q== + version "19.2.7" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.7.tgz#1f47a1bfc06f8ec885752c6f4af14369a9f8260b" + integrity sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ== read-installed@~4.0.3: version "4.0.3" @@ -5114,7 +5142,7 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" -reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: +reflect.getprototypeof@^1.0.10, reflect.getprototypeof@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== @@ -5290,9 +5318,9 @@ semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.7.1, semver@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.1.tgz#bf4970b5e70fda0686363cc18bfe8805d5ed957e" - integrity sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg== + version "7.8.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.4.tgz#c73eceebae0616934be8dff28a7fd70757c8e696" + integrity sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA== sentence-case@^3.0.4: version "3.0.4" @@ -5385,7 +5413,7 @@ shell-quote@1.8.4, shell-quote@^1.7.3: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.4.tgz#2edd9a4dcefc96649e2e2cb12f637b1f1d92a190" integrity sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ== -side-channel-list@^1.0.0: +side-channel-list@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.1.tgz#c2e0b5a14a540aebee3bbc6c3f8666cc9b509127" integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w== @@ -5415,13 +5443,13 @@ side-channel-weakmap@^1.0.2: side-channel-map "^1.0.1" side-channel@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" - integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + version "1.1.1" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.1.tgz#ea02c62e05dc4bea67d4442f0fb71ee192f8e0ab" + integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ== dependencies: es-errors "^1.3.0" - object-inspect "^1.13.3" - side-channel-list "^1.0.0" + object-inspect "^1.13.4" + side-channel-list "^1.0.1" side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" @@ -5612,27 +5640,28 @@ string.prototype.repeat@^1.0.0: es-abstract "^1.17.5" string.prototype.trim@^1.2.10: - version "1.2.10" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" - integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + version "1.2.11" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz#e6bd19cda3985d05a42dda31f3ddf4d35d3430e3" + integrity sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" + call-bind "^1.0.9" + call-bound "^1.0.4" define-data-property "^1.1.4" define-properties "^1.2.1" - es-abstract "^1.23.5" - es-object-atoms "^1.0.0" + es-abstract "^1.24.2" + es-object-atoms "^1.1.2" has-property-descriptors "^1.0.2" + safe-regex-test "^1.1.0" string.prototype.trimend@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" - integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + version "1.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz#be6bcf4f3fe0460bdeccdb2cf4f971b310f8346e" + integrity sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw== dependencies: - call-bind "^1.0.8" - call-bound "^1.0.2" + call-bind "^1.0.9" + call-bound "^1.0.4" define-properties "^1.2.1" - es-object-atoms "^1.0.0" + es-object-atoms "^1.1.2" string.prototype.trimstart@^1.0.8: version "1.0.8" @@ -5728,10 +5757,10 @@ timeout-signal@^2.0.0: resolved "https://registry.yarnpkg.com/timeout-signal/-/timeout-signal-2.0.0.tgz#23207ea448d50258bb0defe3beea4a467643abba" integrity sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA== -tinyglobby@^0.2.13, tinyglobby@^0.2.15, tinyglobby@^0.2.16: - version "0.2.16" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" - integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== +tinyglobby@^0.2.13, tinyglobby@^0.2.15, tinyglobby@^0.2.17: + version "0.2.17" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631" + integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== dependencies: fdir "^6.5.0" picomatch "^4.0.4" @@ -5826,36 +5855,36 @@ typed-array-byte-offset@^1.0.4: reflect.getprototypeof "^1.0.9" typed-array-length@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" - integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + version "1.0.8" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.8.tgz#0b70e982c9e9dafe2def6d6458ff4b3f2d2b6d70" + integrity sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g== dependencies: - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - is-typed-array "^1.1.13" - possible-typed-array-names "^1.0.0" - reflect.getprototypeof "^1.0.6" + call-bind "^1.0.9" + for-each "^0.3.5" + gopd "^1.2.0" + is-typed-array "^1.1.15" + possible-typed-array-names "^1.1.0" + reflect.getprototypeof "^1.0.10" typescript-eslint@^8.46.0: - version "8.60.0" - resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.60.0.tgz#6686fecb1f4f367c0bf0075828e93b7ecacbc62b" - integrity sha512-9f65qWLZdAW9m1JaxBDUHcqRUfL8bkxxXL7XxEfI+F09q56PkBvIfCjLF3yInsDM/BBmwkqmCQdCZe/RYlIWEw== + version "8.61.1" + resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.61.1.tgz#7c224a9a643b7f42d295c67a75c1e30fee8c3eaa" + integrity sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw== dependencies: - "@typescript-eslint/eslint-plugin" "8.60.0" - "@typescript-eslint/parser" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" - "@typescript-eslint/utils" "8.60.0" + "@typescript-eslint/eslint-plugin" "8.61.1" + "@typescript-eslint/parser" "8.61.1" + "@typescript-eslint/typescript-estree" "8.61.1" + "@typescript-eslint/utils" "8.61.1" typescript@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== -unbash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unbash/-/unbash-3.0.0.tgz#57efd0217212ee4abcb820372c87b64ad3af687d" - integrity sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA== +unbash@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/unbash/-/unbash-4.0.1.tgz#efa4876b0f95ab86931546aa1e699e7e353de7ad" + integrity sha512-1ajSo3813sDoVIHx4inJdUS4l5L2ic5cFiddemPiyjb/PZEoBAhFwHtbaEdRDFxbAKy7FCG7s5ww3/uCFawuIA== unbox-primitive@^1.1.0: version "1.1.0" @@ -5877,10 +5906,10 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.24.6.tgz#61275b485d7fd4e9d269c7cf04ec2873c9cc0f91" integrity sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg== -undici-types@~7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" - integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== +undici-types@~7.18.0: + version "7.18.2" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.18.2.tgz#29357a89e7b7ca4aef3bf0fd3fd0cd73884229e9" + integrity sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w== universal-cookie@^8.0.0: version "8.1.2" @@ -6041,9 +6070,9 @@ which-collection@^1.0.2: is-weakset "^2.0.3" which-typed-array@^1.1.16, which-typed-array@^1.1.19: - version "1.1.21" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.21.tgz#ea7aab68168079646af06b4a36a6f7d7b72e1c0a" - integrity sha512-zbRA8cVm6io/d5W8uIe2hblzN76/Wm3v/yiythQvr+dpBWeqhPSWIDNj4zOyHi4zKbMK6DN34Xsr9jPHJERAEw== + version "1.1.22" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.22.tgz#8f3cc78aefb40b437346dd40a1dbfa5d1da43fe9" + integrity sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw== dependencies: available-typed-arrays "^1.0.7" call-bind "^1.0.9" From e210afba2d81f998863d094c86191f69b404519e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 19:28:32 +0200 Subject: [PATCH 827/878] Convert properly between C# TimeSpan and NodaTime Duration and correct time span logic for access control --- .../src/Configuration/GraphQlConfiguration.cs | 2 ++ .../Data/AccessPolicies/AccessPolicyBase.cs | 9 ++++----- .../Data/AccessPolicies/DataAccessPolicy.cs | 20 ++++++++++++++++++- ...ation.cs => UpperLimitPerDurationInput.cs} | 1 + 4 files changed, 26 insertions(+), 6 deletions(-) rename backend/src/Data/AccessPolicies/{UpperAccessLimitPerTimeDuration.cs => UpperLimitPerDurationInput.cs} (94%) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index f8330fd9..3bba1a3b 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -56,6 +56,8 @@ IWebHostEnvironment environment // Extensions .AddNodaTime() .BindRuntimeType() + .AddTypeConverter(_ => _.ToTimeSpan()) + .AddTypeConverter(_ => Duration.FromTimeSpan(_)) // .AddTypeConverter( // _ => _.ToDateTimeOffset() // ) diff --git a/backend/src/Data/AccessPolicies/AccessPolicyBase.cs b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs index 29d9a890..8bbb5cf4 100644 --- a/backend/src/Data/AccessPolicies/AccessPolicyBase.cs +++ b/backend/src/Data/AccessPolicies/AccessPolicyBase.cs @@ -19,10 +19,9 @@ public abstract class AccessPolicyBase [Projectable] public bool IsWithinAccessLimitInTimeSpan => UpperAccessLimitPerTimeDuration is null || ( UpperAccessLimitPerTimeDuration.UpperLimit > 0 && ( - AccessCountSinceStartTime is null || ( - IsWithinTimeSpan - && AccessCountSinceStartTime.AccessCount <= UpperAccessLimitPerTimeDuration.UpperLimit - ) + AccessCountSinceStartTime is null + || !IsWithinTimeSpan + || AccessCountSinceStartTime.AccessCount < UpperAccessLimitPerTimeDuration.UpperLimit ) ); @@ -31,7 +30,7 @@ public abstract class AccessPolicyBase AccessCountSinceStartTime is null || UpperAccessLimitPerTimeDuration is null || UpperAccessLimitPerTimeDuration.Duration is null - || AccessCountSinceStartTime.StartTime + UpperAccessLimitPerTimeDuration.Duration < DateTimeOffset.UtcNow; + || DateTimeOffset.UtcNow <= AccessCountSinceStartTime.StartTime + UpperAccessLimitPerTimeDuration.Duration; public void IncrementAccessCount(IClock clock) { diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index d0635607..9de62b34 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -37,7 +37,8 @@ about the institutions he*she represents. additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) @@ -60,6 +61,23 @@ A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `{nameof(DataAccessPolicy.Data)}`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`{nameof(AccessPolicyBase.AccessCountSinceStartTime)}` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. """)] public sealed class DataAccessPolicy() : AuditableEntity diff --git a/backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs b/backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs similarity index 94% rename from backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs rename to backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs index e6084b0f..143c08eb 100644 --- a/backend/src/Data/AccessPolicies/UpperAccessLimitPerTimeDuration.cs +++ b/backend/src/Data/AccessPolicies/UpperLimitPerDurationInput.cs @@ -1,4 +1,5 @@ using System; +using NodaTime; using Database.Data.AccessPolicies; namespace Database.GraphQl.AccessPolicies; From 1bc0e40a134606c78b1d66c9ab81cd896028c9ed Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 20:27:14 +0200 Subject: [PATCH 828/878] Show only the icon for copying a GnuPG link --- frontend/components/GnuPgKeyLink.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/components/GnuPgKeyLink.tsx b/frontend/components/GnuPgKeyLink.tsx index 0b98e78d..7d6080df 100644 --- a/frontend/components/GnuPgKeyLink.tsx +++ b/frontend/components/GnuPgKeyLink.tsx @@ -19,6 +19,8 @@ export default function GnuPgKeyLink({ return block ? ( {link} ) : ( - {link} + + {link} + ); } From 2c0084de8eb53f48b12f78246e4187a8e54c2d7d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 16 Jun 2026 20:31:43 +0200 Subject: [PATCH 829/878] Mention how durations are represented --- backend/src/Data/AccessPolicies/DataAccessPolicy.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 9de62b34..7da98f59 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -78,6 +78,9 @@ To determine whether access policies allow/restrict access in the way you number of accesses that were counted for the user, institution, or application policy since the start time. Both are reset on the first access after the end of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """)] public sealed class DataAccessPolicy() : AuditableEntity From 31c3227a2c1d797eccc6b06f9de1287d9702f2a2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Jun 2026 12:01:21 +0200 Subject: [PATCH 830/878] Move body-size check for resource upload into /api/resources location --- nginx/templates/default.conf.template | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index e602dc6e..9c36e3cc 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -139,6 +139,12 @@ server { } location /api/resources/ { + # Accept up to 10 gigabyte and 12 minute requests (needed for uploads via + # `POST`). Keep it here instead of within `@resource_post` because the + # branching happens after the body size is checked. + client_max_body_size 10G; + client_body_timeout 1200s; + # Route POST requests to the dedicated upload location if ($request_method = POST) { error_page 418 = @resource_post; @@ -156,8 +162,6 @@ server { location @resource_post { # Optimize for multi-gigabyte POST uploads proxy_pass http://backend; - client_max_body_size 10G; - client_body_timeout 300s; proxy_read_timeout 300s; proxy_send_timeout 300s; proxy_request_buffering off; From 3d075962280d9c931f9c1d07698230eef898b0aa Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Jun 2026 12:13:20 +0200 Subject: [PATCH 831/878] Set authentication type explicitly for `Principal.Identity.IsAuthenticated` to become `true` --- backend/src/Authentication/AuthenticationHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index 870f15f5..a49d72e1 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -182,6 +182,7 @@ private static AuthenticateResult CreateSuccessfulAuthenticateResult(string name new ClaimsPrincipal( new ClaimsIdentity( claims: [new Claim(ClaimTypes.NameIdentifier, nameIdentifier)], + authenticationType: "Metabase", nameType: ClaimTypes.Name, roleType: ClaimTypes.Role ) From cb38f44097151afc6c6cdd49980b547c7847f619 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Jun 2026 12:57:47 +0200 Subject: [PATCH 832/878] Fix permalink for entity summaries used on index pages --- frontend/components/entities/EntitySummary.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/components/entities/EntitySummary.tsx b/frontend/components/entities/EntitySummary.tsx index a0247b2b..a6954628 100644 --- a/frontend/components/entities/EntitySummary.tsx +++ b/frontend/components/entities/EntitySummary.tsx @@ -31,6 +31,10 @@ export default function EntitySummary({ breadcrumb, children, }: Props) { + const absoluteRoute = route + ? new URL(route(entity.uuid), new URL(window.location.href).origin) + : null; + return (
    {breadcrumb && ( @@ -71,7 +75,14 @@ export default function EntitySummary({ }} > window.location.href} + getText={ + () => + absoluteRoute && + absoluteRoute.pathname !== + new URL(window.location.href).pathname + ? absoluteRoute.toString() // use route if summary is not displayed on the entity page + : window.location.href // use URL (with URL parameters and all) if summary is displayed on the entity page + } copyIcon={} > Copy Permalink From cb1ebb37468292fa876a43c50af22f7252dcfc74 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Jun 2026 13:59:21 +0200 Subject: [PATCH 833/878] Add reload button to pagination and on empty results (to reload paginated entities if the user suspects a caching issue) --- .../Data/AccessPolicies/DataAccessPolicy.cs | 9 +++- .../DataAccessPolicyFilterType.cs | 1 + .../AccessPolicies/DataAccessPolicyType.cs | 31 +++++++++++ frontend/components/Pagination.tsx | 28 +++++++--- .../calorimetric/CalorimetricDataList.tsx | 3 ++ .../data/geometric/GeometricDataList.tsx | 3 ++ .../hygrothermal/HygrothermalDataList.tsx | 3 ++ .../data/lifeCycle/LifeCycleDataList.tsx | 3 ++ .../data/optical/OpticalDataList.tsx | 3 ++ .../photovoltaic/PhotovoltaicDataList.tsx | 3 ++ frontend/components/entities/EntityList.tsx | 14 +++-- frontend/lib/hooks/usePaginatedQuery.ts | 54 +++++++++++++------ 12 files changed, 129 insertions(+), 26 deletions(-) diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 7da98f59..ff5b1fad 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -55,7 +55,7 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `{nameof(DataAccessPolicy.Data)}`. It is @@ -232,6 +232,13 @@ private void AssertThatExactlyOneDataIdIsNonNull() [Projectable] public bool IsGlobal => DataId is null; + [Projectable] + public bool IsNobodyAllowed => + Combinator == LogicalCombinator.SOME + && UserAccessPolicies.Count == 0 + && InstitutionAccessPolicies.Count == 0 + && OpenIdConnectApplicationAccessPolicies.Count == 0; + [Projectable] public bool IsAnyoneAllowed => IsAccessAllowed(null, null, null); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs index b93fd099..3141019c 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -14,6 +14,7 @@ IFilterInputTypeDescriptor descriptor descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(DataAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.IsGlobal); + descriptor.Field(_ => _.IsNobodyAllowed); descriptor.Field(_ => _.IsAnyoneAllowed); descriptor.Field(_ => _.Combinator); descriptor.Field(_ => _.UserAccessPolicies); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index ad765160..3f31c643 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -91,6 +91,11 @@ IObjectTypeDescriptor descriptor .Field(_ => _.OpenIdConnectApplicationAccessPolicies) .Cost(1) .ResolveWith(_ => Resolvers.GetOpenIdConnectApplicationAccessPoliciesAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(_ => _.IsNobodyAllowed) + .Cost(0) + .ResolveWith(t => + Resolvers.IsNobodyAllowedAsync(default!, default!, default!, default!)); descriptor .Field(_ => _.IsAnyoneAllowed) .Cost(0) @@ -121,6 +126,32 @@ CancellationToken cancellationToken ); } + public static async Task IsNobodyAllowedAsync( + [Parent] DataAccessPolicy dataAccessPolicy, + IDataAccessPolicyByDataIdDataLoader policyByDataIdDataLoader, + ApplicationDbContext databaseContext, + CancellationToken cancellationToken + ) + { + if (dataAccessPolicy.DataId is null) + { + return await databaseContext.DataAccessPolicies.AsQueryable() + .Where(_ => _.IsNobodyAllowed) + .SingleOrDefaultAsync( + _ => _.DataId == null, + cancellationToken + ) + is not null; + } + return await policyByDataIdDataLoader + .Where(_ => _.IsNobodyAllowed) + .LoadAsync( + dataAccessPolicy.DataId ?? Guid.Empty, + cancellationToken + ) + is not null; + } + public static Task IsAnyoneAllowedAsync( [Parent] DataAccessPolicy dataAccessPolicy, IDataAccessPolicyByDataIdDataLoader policyByDataIdDataLoader, diff --git a/frontend/components/Pagination.tsx b/frontend/components/Pagination.tsx index f6dbacfd..ee60d564 100644 --- a/frontend/components/Pagination.tsx +++ b/frontend/components/Pagination.tsx @@ -3,6 +3,7 @@ import { LeftOutlined, RightOutlined, LoadingOutlined, + ReloadOutlined, } from "@ant-design/icons"; export const initialPageSize = 10; @@ -27,6 +28,7 @@ export interface PaginationProps { pageSize: number; hasPrevious: boolean; hasNext: boolean; + onReload: () => void; onPrevious: () => void; onNext: () => void; onPageSizeChange: (size: number) => void; @@ -37,10 +39,11 @@ export default function Pagination({ current, total, pageSize, - onPrevious, hasPrevious, - onNext, hasNext, + onReload, + onPrevious, + onNext, onPageSizeChange, }: PaginationProps) { return ( @@ -51,13 +54,12 @@ export default function Pagination({ disabled={fetching != null || !hasPrevious} type="text" icon={ - fetching == Fetching.PREVIOUS ? loadingIndicator : + fetching === Fetching.PREVIOUS ? loadingIndicator : } /> - Page {current} of{" "} - {fetching == Fetching.INITIAL ? loadingIndicator : total} + Page {current} of {fetching === Fetching.INITIAL ? "∞" : total} + + ); } return ( diff --git a/frontend/lib/hooks/usePaginatedQuery.ts b/frontend/lib/hooks/usePaginatedQuery.ts index 62180555..b2bcc70c 100644 --- a/frontend/lib/hooks/usePaginatedQuery.ts +++ b/frontend/lib/hooks/usePaginatedQuery.ts @@ -12,37 +12,34 @@ type QueryData = { connection: Connection | null; }; -type PropositionInput = - | TPropositionInput - | { and?: TPropositionInput[] }; +type FilterInput = TFilterInput | { and?: TFilterInput[] }; type SortInput = TSortInput | TSortInput[]; -export type QueryVariables = { +export type QueryVariables = { first: number; after?: string | null; - where?: PropositionInput; + where?: FilterInput; order?: SortInput; }; -export type QueryDocument = - TypedDocumentNode< - QueryData, - QueryVariables - >; +export type QueryDocument = TypedDocumentNode< + QueryData, + QueryVariables +>; // ensure `relayStylePagination(["first", "after", ...])` is set in `InMemoryCache` for the field `fieldName` -export function usePaginatedQuery( - queryDocument: QueryDocument, +export function usePaginatedQuery( + queryDocument: QueryDocument, { where, order, }: { - where?: PropositionInput; + where?: FilterInput; order?: SortInput; }, onQueryVariablesChange: ( - variables: QueryVariables, + variables: QueryVariables, ) => void, ): { loading: boolean; @@ -57,7 +54,7 @@ export function usePaginatedQuery( const variables = { first: pageSize, - after: null, + after: afterCursors[currentPage - 1], where: where, order: order, }; @@ -91,6 +88,29 @@ export function usePaginatedQuery( return edges?.slice(start, start + pageSize).map((e) => e.node); }, [edges, currentPage, pageSize]); + const handleReload = () => { + if (loading) return; + const variables = { + first: pageSize, + after: null, + where: where, + order: order, + }; + setFetching(Fetching.INITIAL); + refetch(variables) + .then(() => { + // note that `currentPage` or `afterCursors` may have changed when this + // callback fires + setCurrentPage(1); + setAfterCursors([null]); + onQueryVariablesChange(variables); + }) + .catch((error) => { + console.error("Reloading current page failed", error); + }) + .finally(() => setFetching(null)); + }; + const handlePrevious = () => { if (loading) return; if (currentPage <= 1) return; @@ -136,6 +156,7 @@ export function usePaginatedQuery( }; const handlePageSizeChange = (pageSize: number) => { + if (loading) return; setPageSize(pageSize); const variables = { first: pageSize, @@ -143,9 +164,9 @@ export function usePaginatedQuery( where: where, order: order, }; + setFetching(Fetching.INITIAL); refetch(variables) .then(() => { - setFetching(Fetching.INITIAL); // note that `currentPage` or `afterCursors` may have changed when this // callback fires setCurrentPage(1); @@ -170,6 +191,7 @@ export function usePaginatedQuery( !!connection?.pageInfo.hasNextPage || currentPage * pageSize < (edges?.length ?? 0), hasPrevious: currentPage > 1, + onReload: handleReload, onNext: handleNext, onPrevious: handlePrevious, onPageSizeChange: handlePageSizeChange, From e832801c327fe570fd7d85e657c69607096e07c4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 17 Jun 2026 15:05:51 +0200 Subject: [PATCH 834/878] Show formal signature of data approval only on demand --- frontend/components/CodeView.tsx | 7 +- frontend/components/Copyable.tsx | 8 +- frontend/components/CopyableBlock.tsx | 5 +- frontend/components/JsonView.tsx | 7 +- .../components/data/DataApprovalSummary.tsx | 89 +++++++++++++++++++ frontend/components/data/DataSummary.tsx | 52 ++--------- .../components/entities/PaginatedEntities.tsx | 16 +++- frontend/queries/data.graphql | 32 ++++--- 8 files changed, 147 insertions(+), 69 deletions(-) create mode 100644 frontend/components/data/DataApprovalSummary.tsx diff --git a/frontend/components/CodeView.tsx b/frontend/components/CodeView.tsx index fb828e3d..614fed5b 100644 --- a/frontend/components/CodeView.tsx +++ b/frontend/components/CodeView.tsx @@ -1,22 +1,25 @@ +import { CSSProperties } from "react"; import Copyable from "./Copyable"; import CopyableBlock from "./CopyableBlock"; export default function CodeView({ code, inline = false, + style, }: { code: string; inline?: boolean; + style?: CSSProperties; }) { if (inline) { return ( - + {code} ); } else { return ( - +
       );
     
    -  return {onlyIcon ? content : {content}};
    +  return (
    +    {onlyIcon ? content : {content}}
    +  );
     }
    diff --git a/frontend/components/CopyableBlock.tsx b/frontend/components/CopyableBlock.tsx
    index edc69062..c1dd917f 100644
    --- a/frontend/components/CopyableBlock.tsx
    +++ b/frontend/components/CopyableBlock.tsx
    @@ -1,14 +1,16 @@
     import { Button } from "antd";
     import { CheckOutlined, CopyOutlined } from "@ant-design/icons";
    -import { ReactNode, useState } from "react";
    +import { CSSProperties, ReactNode, useState } from "react";
     
     export default function CopyableBlock({
       text,
       color,
    +  style,
       children,
     }: {
       text: string;
       color?: "white";
    +  style?: CSSProperties;
       children: ReactNode;
     }) {
       const [copied, setCopied] = useState(false);
    @@ -16,6 +18,7 @@ export default function CopyableBlock({
       return (
         
    }> - {jsonString} + {jsonString} ); } else { const jsonString = JSON.stringify(data, null, 2); return ( - +
     ({
    +  uuid: id,
    +  name: id,
    +});
    +
    +export default function DataApprovalSummary({
    +  approval,
    +  data,
    +}: {
    +  approval: DataApprovalPartialFragment;
    +  data: DataPartialFragment;
    +}) {
    +  const [isProofOpen, setIsProofOpen] = useState(false);
    +
    +  return (
    +    
    + + The institution{" "} + {" "} + approved on that the data + satisfies the statement given by the reference + + setIsProofOpen(false)} + size="large" + open={isProofOpen} + > + This is proven by the GnuPG signature + + generated with the institution's GnuPG key{" "} + of the message + + The message contains the statement and the response of the query + + with the variables + + against the GraphQL endpoint of the database{" "} + + {data.database?.locator && ( + <> + {" "} + located at{" "} + + {data.database.locator} + + + )} + . + +
    + ); +} diff --git a/frontend/components/data/DataSummary.tsx b/frontend/components/data/DataSummary.tsx index 18bfab5b..b82a3e19 100644 --- a/frontend/components/data/DataSummary.tsx +++ b/frontend/components/data/DataSummary.tsx @@ -1,4 +1,3 @@ -import * as graphql from "graphql"; import { Breadcrumb, Divider, @@ -18,15 +17,13 @@ import { import InlineList from "../InlineList"; import EntityLink from "../entities/EntityLink"; import { Scalars } from "../../__generated__/graphql"; -import JsonViewer from "../JsonView"; import { useMemo, useState } from "react"; import Copyable from "../Copyable"; -import Reference from "../Reference"; import CodeViewer from "../CodeView"; import AppliedMethodViewer from "../methods/AppliedMethodView"; -import GnuPgKeyLink from "../GnuPgKeyLink"; import DateTimeX from "../DateTimeX"; import { Route } from "next"; +import DataApprovalSummary from "./DataApprovalSummary"; interface ResourceTreeNode { key: string; @@ -81,7 +78,7 @@ const getBreadcrumbItems = ( const node = flatNodes.find((node) => node.vertexId === currentId); if (!node) break; path.unshift({ - title: `${node.value.dataFormat?.name ?? node.value.dataFormatId} (${node.vertexId})`, + title: `${node.value.dataFormat?.name ?? node.value.dataFormatId} (vertex ${node.vertexId})`, }); if (node.__typename === "GetHttpsResourceTreeNonRootVertex") { currentId = node.parentId; @@ -261,46 +258,11 @@ export default function DataSummary({
    {entity.approvals.map((approval, index) => ( -
    - The institution{" "} - {" "} - approved on that the - data satisfies the statement given by the reference - - This is proven by the GnuPG signature - - generated with the institution's GnuPG key{" "} - of the - message - - The message contains the statement and the response of the - query - - with the variables - - against the GraphQL endpoint of the database{" "} - - {entity.database?.locator && ( - <> - {" "} - located at{" "} - - {entity.database.locator} - - - )} - . -
    + ))}
    diff --git a/frontend/components/entities/PaginatedEntities.tsx b/frontend/components/entities/PaginatedEntities.tsx index 42ee5b75..c6172946 100644 --- a/frontend/components/entities/PaginatedEntities.tsx +++ b/frontend/components/entities/PaginatedEntities.tsx @@ -31,7 +31,9 @@ import QueryToolbar from "../QueryToolbar"; import SlideDown from "../SlideDown"; import { notEmpty } from "../../lib/array"; -const reduceWhere = (where: { and: TPropositionInput[] }) => +const reduceWhere = (where: { + and: TPropositionInput[]; +}) => where.and.length == 0 ? null : where.and.length == 1 ? where.and[0] : where; const reduceOrder = ( @@ -65,7 +67,11 @@ type BaseProps< > = { entitiesQuery: QueryDocument; extra?: React.ReactNode; - list: (props: { loading: boolean; nodes: TNode[] | null }) => React.ReactNode; + list: (props: { + loading: boolean; + nodes: TNode[] | null; + onReload: () => void; + }) => React.ReactNode; filterDefinitions: readonly FilterDefinition[]; sortDefinitions: readonly SortDefinition[]; baseWhere?: TPropositionInput | null; @@ -365,7 +371,11 @@ export default function PaginatedEntities< onRemoveAll={removeAllFiltersAndSorts} /> )} - {props.list({ loading: props.loading || loading, nodes })} + {props.list({ + loading: props.loading || loading, + nodes, + onReload: paginationProps.onReload, + })} Date: Wed, 17 Jun 2026 16:57:04 +0200 Subject: [PATCH 835/878] Simplify /api/resources/* setup --- backend/src/Program.cs | 4 +- nginx/templates/default.conf.template | 338 ++++++++++++-------------- 2 files changed, 159 insertions(+), 183 deletions(-) diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 6146739d..db0c93a7 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -251,8 +251,8 @@ Uri openTelemetryHost }); builder.WebHost.ConfigureKestrel(_ => { - // matches the keep-alive timeout configured for `/api/resources/` in NGINX - _.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(300); + // Keep in sync with the `*_timeout`s for post requests to /api/resources/* configured in ./nginx/templates/default.conf.template + _.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(1200); _.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30); }); return builder; diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index 9c36e3cc..a3629597 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -9,8 +9,8 @@ resolver 127.0.0.11 valid=5s ipv6=off; resolver_timeout 5s; map $http_upgrade $connection_upgrade { - default upgrade; - '' keep-alive; + default upgrade; + '' keep-alive; } proxy_redirect off; @@ -58,7 +58,7 @@ send_timeout 10s; add_header X-Proxy-Layer "inner" always; types { - application/javascript js mjs; + application/javascript js mjs; } map $http_accept $error_type { @@ -67,191 +67,167 @@ map $http_accept $error_type { } upstream backend { - zone upstream_dynamic 64k; - server backend:8080 max_fails=0 resolve; + zone upstream_dynamic 64k; + server backend:8080 max_fails=0 resolve; } upstream frontend { - zone upstream_dynamic 64k; - server frontend:8080 max_fails=0 resolve; + zone upstream_dynamic 64k; + server frontend:8080 max_fails=0 resolve; } server { - listen *:80; - - set $maintenance 0; - if (-f $document_root/maintenance.html) { - set $maintenance 1; - } - if ($internal != 1) { - set $maintenance "${maintenance}1"; - } - if ($maintenance = 11) { - return 503; - } - - limit_req zone=general burst=10 nodelay; - - location / { - proxy_pass http://frontend; - limit_req zone=general burst=100 nodelay; - } - - location /_next/static { - proxy_pass http://frontend; - expires 365d; - access_log off; - limit_req zone=general burst=100 nodelay; - } - - location /_next/webpack-hmr { - proxy_pass http://frontend; - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; - limit_req zone=general burst=100 nodelay; - } - - location ~ /_next/static/.*/_clientMiddlewareManifest\.js$ { - proxy_pass http://frontend; - proxy_hide_header Content-Type; - add_header Content-Type application/javascript; - limit_req zone=general burst=100 nodelay; - } - - location /graphql { - proxy_pass http://backend; - limit_req zone=sensitive burst=12 delay=6; - } - - location /connect { - proxy_pass http://backend; - limit_req zone=sensitive burst=6 nodelay; - } - - location /antiforgery { - proxy_pass http://backend; - limit_req zone=sensitive burst=2 nodelay; - } - - location /api { - proxy_pass http://backend; - limit_req zone=sensitive burst=6 nodelay; - } - - location /api/resources/ { - # Accept up to 10 gigabyte and 12 minute requests (needed for uploads via - # `POST`). Keep it here instead of within `@resource_post` because the - # branching happens after the body size is checked. - client_max_body_size 10G; - client_body_timeout 1200s; - - # Route POST requests to the dedicated upload location - if ($request_method = POST) { - error_page 418 = @resource_post; - return 418; + listen *:80; + + set $maintenance 0; + if (-f $document_root/maintenance.html) { + set $maintenance 1; + } + if ($internal != 1) { + set $maintenance "${maintenance}1"; + } + if ($maintenance = 11) { + return 503; + } + + limit_req zone=general burst=10 nodelay; + + location / { + proxy_pass http://frontend; + limit_req zone=general burst=100 nodelay; + } + + location /_next/static { + proxy_pass http://frontend; + expires 365d; + access_log off; + limit_req zone=general burst=100 nodelay; + } + + location /_next/webpack-hmr { + proxy_pass http://frontend; + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + limit_req zone=general burst=100 nodelay; + } + + location ~ /_next/static/.*/_clientMiddlewareManifest\.js$ { + proxy_pass http://frontend; + proxy_hide_header Content-Type; + add_header Content-Type application/javascript; + limit_req zone=general burst=100 nodelay; + } + + location /graphql { + proxy_pass http://backend; + limit_req zone=sensitive burst=12 delay=6; + } + + location /connect { + proxy_pass http://backend; + limit_req zone=sensitive burst=6 nodelay; + } + + location /antiforgery { + proxy_pass http://backend; + limit_req zone=sensitive burst=2 nodelay; + } + + location /api { + proxy_pass http://backend; + limit_req zone=sensitive burst=6 nodelay; + } + + location /api/resources/ { + # Accept up to 10 gigabyte and 12 minute requests (for uploads via `POST`) + client_max_body_size 10G; + client_body_timeout 1200s; + proxy_read_timeout 1200s; + proxy_send_timeout 1200s; + proxy_request_buffering off; + proxy_buffering off; + proxy_cache off; + proxy_pass http://backend; + } + + location /openapi { + proxy_pass http://backend; + limit_req zone=general burst=10 nodelay; } - # Route GET requests to the dedicated download location - if ($request_method = GET) { - error_page 418 = @resource_get; - return 418; + + location /health { + proxy_pass http://backend; + limit_req zone=general burst=2 nodelay; + } + + error_page 404 @not_found; + location @not_found { + internal; + add_header Content-Type application/json always; + return 404 '{"errors": [{"code": "NOT_FOUND", "message": "The requested resource could not be found.", "path": []}]}'; } - # Fallback for other HTTP methods (PUT, DELETE, etc.) - proxy_pass http://backend; - } - - location @resource_post { - # Optimize for multi-gigabyte POST uploads - proxy_pass http://backend; - proxy_read_timeout 300s; - proxy_send_timeout 300s; - proxy_request_buffering off; - } - - location @resource_get { - # Optimize for multi-gigabyte GET streaming downloads - proxy_pass http://backend; - proxy_buffering off; - proxy_read_timeout 300s; - } - - location /openapi { - proxy_pass http://backend; - limit_req zone=general burst=10 nodelay; - } - - location /health { - proxy_pass http://backend; - limit_req zone=general burst=2 nodelay; - } - - error_page 404 @not_found; - location @not_found { - internal; - add_header Content-Type application/json always; - return 404 '{"errors": [{"code": "NOT_FOUND", "message": "The requested resource could not be found.", "path": []}]}'; - } - - error_page 408 @request_timeout; - location @request_timeout { - internal; - add_header Content-Type application/json always; - return 408 '{"errors": [{"code": "REQUEST_TIMEOUT", "message": "The server timed out waiting for the request. Please check your network connection and try again.", "path": []}]}'; - } - - error_page 413 @content_too_large; - location @content_too_large { - internal; - add_header Content-Type application/json always; - return 413 '{"errors": [{"code": "CONTENT_TOO_LARGE", "message": "The uploaded file is too large. Please reduce the size and try again.", "path": []}]}'; - } - - error_page 429 @too_many_requests; - location @too_many_requests { - internal; - add_header Retry-After 60 always; - add_header Content-Type application/json always; - return 429 '{"errors": [{"code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded. Please slow down and try again later.", "path": []}]}'; - } - - error_page 501 @not_implemented; - location @not_implemented { - internal; - add_header Content-Type application/json always; - return 501 '{"errors": [{"code": "NOT_IMPLEMENTED", "message": "The HTTP method used is not supported.", "path": []}]}'; - } - - error_page 502 @bad_gateway; - location @bad_gateway { - internal; - add_header Retry-After 120 always; - add_header Content-Type application/json always; - return 502 '{"errors": [{"code": "BAD_GATEWAY", "message": "The upstream server is temporarily unable to handle the request. Please try again in 2 minutes.", "path": []}]}'; - } - - error_page 504 @gateway_timeout; - location @gateway_timeout { - internal; - add_header Retry-After 120 always; - add_header Content-Type application/json always; - return 504 '{"errors": [{"code": "GATEWAY_TIMEOUT", "message": "The upstream server took too long to respond. This may be due to high load. Please try again in 2 minutes.", "path": []}]}'; - } - - error_page 503 @maintenance; - location @maintenance { - internal; - if ($error_type = "application/json") { - error_page 503 @service_unavailable; - return 503; + + error_page 408 @request_timeout; + location @request_timeout { + internal; + add_header Content-Type application/json always; + return 408 '{"errors": [{"code": "REQUEST_TIMEOUT", "message": "The server timed out waiting for the request. Please check your network connection and try again.", "path": []}]}'; + } + + error_page 413 @content_too_large; + location @content_too_large { + internal; + add_header Content-Type application/json always; + return 413 '{"errors": [{"code": "CONTENT_TOO_LARGE", "message": "The uploaded file is too large. Please reduce the size and try again.", "path": []}]}'; + } + + error_page 429 @too_many_requests; + location @too_many_requests { + internal; + add_header Retry-After 60 always; + add_header Content-Type application/json always; + return 429 '{"errors": [{"code": "TOO_MANY_REQUESTS", "message": "Rate limit exceeded. Please slow down and try again later.", "path": []}]}'; + } + + error_page 501 @not_implemented; + location @not_implemented { + internal; + add_header Content-Type application/json always; + return 501 '{"errors": [{"code": "NOT_IMPLEMENTED", "message": "The HTTP method used is not supported.", "path": []}]}'; + } + + error_page 502 @bad_gateway; + location @bad_gateway { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 502 '{"errors": [{"code": "BAD_GATEWAY", "message": "The upstream server is temporarily unable to handle the request. Please try again in 2 minutes.", "path": []}]}'; + } + + error_page 504 @gateway_timeout; + location @gateway_timeout { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 504 '{"errors": [{"code": "GATEWAY_TIMEOUT", "message": "The upstream server took too long to respond. This may be due to high load. Please try again in 2 minutes.", "path": []}]}'; + } + + error_page 503 @maintenance; + location @maintenance { + internal; + if ($error_type = "application/json") { + error_page 503 @service_unavailable; + return 503; + } + add_header Retry-After 120 always; + root /etc/nginx/html; + try_files /maintenance.html @service_unavailable; + } + + location @service_unavailable { + internal; + add_header Retry-After 120 always; + add_header Content-Type application/json always; + return 503 '{"errors": [{"code": "SERVICE_UNAVAILABLE", "message": "The system is under maintenance. Please try again in 2 minutes.", "path": []}]}'; } - add_header Retry-After 120 always; - root /etc/nginx/html; - try_files /maintenance.html @service_unavailable; - } - - location @service_unavailable { - internal; - add_header Retry-After 120 always; - add_header Content-Type application/json always; - return 503 '{"errors": [{"code": "SERVICE_UNAVAILABLE", "message": "The system is under maintenance. Please try again in 2 minutes.", "path": []}]}'; - } } From 9fb392f6c045ecb1e162f821c6c484f32fde2dd7 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 14:57:40 +0200 Subject: [PATCH 836/878] Map display name to name to match C# representation --- .../src/ApiRequests/Queries/OpenIdConnectApplications.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql index 8eb9c946..02ab6f06 100644 --- a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -3,7 +3,7 @@ query OpenIdConnectApplications($ids: [String!]!) { edges { node { id: clientId - displayName + name: displayName } } } From 72f52eca32fcec460d626713038020ee7a0e6c90 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 15:54:33 +0200 Subject: [PATCH 837/878] Protect mutations and policies additionally through scope requirements as a first level of defense and to aid in debugging when accessing without the necessary privileges --- .../ClearInstitutionAccessPoliciesMutation.cs | 4 +- ...onnectApplicationAccessPoliciesMutation.cs | 4 +- .../ClearUserAccessPoliciesMutation.cs | 4 +- .../ConfigureDataAccessPoliciesMutation.cs | 4 +- .../ConfigureDataAccessPolicyMutation.cs | 4 +- .../ResetDataAccessPoliciesMutation.cs | 4 +- .../ResetDataAccessPolicyMutation.cs | 4 +- .../SetInstitutionAccessPolicyMutation.cs | 2 + ...dConnectApplicationAccessPolicyMutation.cs | 4 +- .../SetUserAccessPolicyMutation.cs | 4 +- .../UnsetInstitutionAccessPolicyMutation.cs | 4 +- ...dConnectApplicationAccessPolicyMutation.cs | 4 +- .../UnsetUserAccessPolicyMutation.cs | 4 +- .../CreateCalorimetricDataMutation.cs | 4 +- .../DataApprovals/AddDataApprovalMutation.cs | 2 + .../RemoveDataApprovalMutation.cs | 4 +- backend/src/GraphQl/DataX/DataResolvers.cs | 45 ++++++++++++++++++- backend/src/GraphQl/DataX/DataTypeBase.cs | 8 +++- .../src/GraphQl/DataX/DeleteDataMutation.cs | 4 +- .../src/GraphQl/DataX/PublishDataMutation.cs | 2 + .../src/GraphQl/DataX/RetractDataMutation.cs | 4 +- .../src/GraphQl/DataX/UpdateDataMutation.cs | 4 +- .../Databases/UpdateDatabaseMutation.cs | 5 ++- .../Databases/VerifyDatabaseMutation.cs | 5 ++- .../CreateGeometricDataMutation.cs | 4 +- .../CreateGetHttpsResourceMutation.cs | 4 +- .../DeleteGetHttpsResourceMutation.cs | 4 +- .../GetHttpsResourceQueries.cs | 2 +- ...mputeGetHttpsResourceHashValuesMutation.cs | 4 +- .../SetGetHttpsResourceParentMutation.cs | 2 + .../UpdateChildGetHttpsResourceMutation.cs | 2 + .../UpdateRootGetHttpsResourceMutation.cs | 2 + .../CreateHygrothermalDataMutation.cs | 4 +- .../CreateLifeCycleDataMutation.cs | 4 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 5 ++- .../CreatePhotovoltaicDataMutation.cs | 4 +- .../CreateResponseApprovalsMutation.cs | 4 +- .../UpdateResponseApprovalsMutation.cs | 4 +- 38 files changed, 152 insertions(+), 34 deletions(-) diff --git a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs index 6802c6fe..a0f981cc 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs @@ -7,6 +7,7 @@ using Database.Data.AccessPolicies; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -52,6 +53,7 @@ protected override ClearInstitutionAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ClearInstitutionAccessPoliciesAsync( ClearInstitutionAccessPoliciesInput input, ApplicationDbContext context, @@ -94,4 +96,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs index d4e36922..a9719c16 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs @@ -7,6 +7,7 @@ using Database.Data.AccessPolicies; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -52,6 +53,7 @@ protected override ClearOpenIdConnectApplicationAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ClearOpenIdConnectApplicationAccessPoliciesAsync( ClearOpenIdConnectApplicationAccessPoliciesInput input, ApplicationDbContext context, @@ -94,4 +96,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs index 3df4bd50..0e8d5008 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs @@ -7,6 +7,7 @@ using Database.Data.AccessPolicies; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -52,6 +53,7 @@ protected override ClearUserAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ClearUserAccessPoliciesAsync( ClearUserAccessPoliciesInput input, ApplicationDbContext context, @@ -94,4 +96,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs index 8fdb2e4b..bedac5ff 100644 --- a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs @@ -11,6 +11,7 @@ using Database.GraphQl.AccessPolicies; using Database.GraphQl.Extensions; using HotChocolate.Data; +using HotChocolate.Authorization; using HotChocolate.Data.Filters; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -71,6 +72,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ConfigureDataAccessPoliciesAsync( ConfigureDataAccessPoliciesInput input, ApplicationDbContext context, @@ -102,4 +104,4 @@ await context.DataAccessPolicies await context.SaveChangesAsync(cancellationToken); return NewPayload(policies, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs index 5e16b99e..1371005d 100644 --- a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs @@ -9,6 +9,7 @@ using Database.Enumerations; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicy; @@ -55,6 +56,7 @@ protected override ConfigureDataAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ConfigureDataAccessPolicyAsync( ConfigureDataAccessPolicyInput input, ApplicationDbContext context, @@ -96,4 +98,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs index 9a6aac9c..9794a9d9 100644 --- a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs @@ -10,6 +10,7 @@ using Database.GraphQl.AccessPolicies; using Database.GraphQl.Extensions; using HotChocolate.Data; +using HotChocolate.Authorization; using HotChocolate.Data.Filters; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -65,6 +66,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ResetDataAccessPoliciesAsync( ApplicationDbContext context, CommonAuthorization authorization, @@ -98,4 +100,4 @@ await context.DataAccessPolicies await context.SaveChangesAsync(cancellationToken); return NewPayload(policies, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs index b70094c4..385b3d21 100644 --- a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs @@ -6,6 +6,7 @@ using Database.Data; using Database.Data.AccessPolicies; using Database.Extensions; +using HotChocolate.Authorization; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -52,6 +53,7 @@ protected override ResetDataAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task ResetDataAccessPolicyAsync( ResetDataAccessPolicyInput input, ApplicationDbContext context, @@ -96,4 +98,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(dataAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs index 758ff23b..5837e70e 100644 --- a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs @@ -10,6 +10,7 @@ using Database.Extensions; using Database.ApiRequests; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; using GraphQL.Client.Abstractions.Utilities; @@ -59,6 +60,7 @@ protected override SetInstitutionAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task SetInstitutionAccessPolicyAsync( SetInstitutionAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs index 8b705147..c32395fc 100644 --- a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -8,6 +8,7 @@ using Database.Data.AccessPolicies; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; using GraphQL.Client.Abstractions.Utilities; using Database.ApiRequests; @@ -58,6 +59,7 @@ protected override SetOpenIdConnectApplicationAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task SetOpenIdConnectApplicationAccessPolicyAsync( SetOpenIdConnectApplicationAccessPolicyInput input, ApplicationDbContext context, @@ -134,4 +136,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(applicationAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs index f4aaa8b0..b759be4b 100644 --- a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs @@ -10,6 +10,7 @@ using Database.Extensions; using Database.ApiRequests; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; using GraphQL.Client.Abstractions.Utilities; @@ -59,6 +60,7 @@ protected override SetUserAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task SetUserAccessPolicyAsync( SetUserAccessPolicyInput input, ApplicationDbContext context, @@ -135,4 +137,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(userAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs index 37f955ef..7ea334d8 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs @@ -9,6 +9,7 @@ using Database.Extensions; using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -56,6 +57,7 @@ protected override UnsetInstitutionAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UnsetInstitutionAccessPolicyAsync( UnsetInstitutionAccessPolicyInput input, ApplicationDbContext context, @@ -114,4 +116,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(institutionAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs index b3c0fd09..9032139c 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -9,6 +9,7 @@ using Database.Extensions; using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -56,6 +57,7 @@ protected override UnsetOpenIdConnectApplicationAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UnsetOpenIdConnectApplicationAccessPolicyAsync( UnsetOpenIdConnectApplicationAccessPolicyInput input, ApplicationDbContext context, @@ -115,4 +117,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(applicationAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs index a7f34da0..13bde561 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs @@ -10,6 +10,7 @@ using Database.Extensions; using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.AccessPolicies; @@ -57,6 +58,7 @@ protected override UnsetUserAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UnsetUserAccessPolicyAsync( UnsetUserAccessPolicyInput input, ApplicationDbContext context, @@ -116,4 +118,4 @@ CancellationToken cancellationToken await context.SaveChangesAsync(cancellationToken); return NewPayload(userAccessPolicy, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 3cb7b635..9408a381 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -12,6 +12,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -99,6 +100,7 @@ protected override CreateCalorimetricDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, @@ -172,4 +174,4 @@ CancellationToken cancellationToken return NewPayload(calorimetricData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 99d87d68..a9fc0fa9 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -18,6 +18,7 @@ using GraphQL; using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.Extensions.Logging; using NodaTime; @@ -98,6 +99,7 @@ protected override AddDataApprovalError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task AddDataApprovalAsync( AddDataApprovalInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs index abe9a716..f4fa7780 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -12,6 +12,7 @@ using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; +using HotChocolate.Authorization; namespace Database.GraphQl.DataApprovals; @@ -60,6 +61,7 @@ protected override RemoveDataApprovalError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task RemoveDataApprovalAsync( RemoveDataApprovalInput input, ApplicationDbContext context, @@ -127,4 +129,4 @@ CancellationToken cancellationToken return NewPayload(approval, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index afb3bbd3..31f76358 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -17,6 +17,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using System.Linq; +using HotChocolate.Authorization; namespace Database.GraphQl.DataX; @@ -43,6 +44,7 @@ [Parent] IData data return new GetHttpsResourceTree(data); } + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task GetDataAccessPolicyAsync( [Parent] IData data, IResolverContext resolverContext, @@ -86,24 +88,65 @@ IInstitutionByIdDataLoader byId return byId.LoadAsync(data.CreatorId); } + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public static Task IsNobodyAllowedAsync( + [Parent] IData data, + ApplicationDbContext databaseContext, + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return false; + } + return await databaseContext.Data(data.Kind) + .Where(_ => _.Id == data.Id) + .Where(_ => + ( + _.AccessPolicy != null + && _.AccessPolicy.IsNobodyAllowed + ) + || + ( + !databaseContext.DataAccessPolicies.Any(_ => + _.DataId == null + && !_.IsNobodyAllowed + ) + ) + ) + .SingleOrDefaultAsync(cancellationToken) + is not null; + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public static Task IsAnyoneAllowedAsync( [Parent] IData data, ApplicationDbContext databaseContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return IsAccessAllowedAsync(data, null, null, null, databaseContext, cancellationToken); + return IsAccessAllowedAsync(data, null, null, null, databaseContext, authorization, cancellationToken); } + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public static async Task IsAccessAllowedAsync( [Parent] IData data, Guid? userId, Guid[]? institutionIds, string? openIdConnectClientId, ApplicationDbContext databaseContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { + if (!await authorization.IsDatabaseOperator(cancellationToken)) + { + authorization.ReportUnauthorizedError(resolverContext); + return false; + } IQueryable policedQuery = data.Kind switch { DataKind.CALORIMETRIC_DATA => AccessPolicyService.PoliceData(databaseContext.CalorimetricData, userId, institutionIds, openIdConnectClientId, databaseContext), diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index c5f7e688..9e1a5587 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -40,14 +40,18 @@ IObjectTypeDescriptor descriptor .Field(_ => _.AccessPolicy) .Cost(1) .ResolveWith(_ => _.GetDataAccessPolicyAsync(default!, default!, default!, default!, default!)); + descriptor + .Field(nameof(DataResolvers.IsNobodyAllowedAsync)[..^"Async".Length]) + .Cost(0) + .ResolveWith(_ => DataResolvers.IsNobodyAllowedAsync(default!, default!, default!, default!)); descriptor .Field(nameof(DataResolvers.IsAnyoneAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!)); descriptor .Field(nameof(DataResolvers.IsAccessAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!, default!)); descriptor .Field(_ => _.Approval) .Cost(3) diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index feb39b02..1c94ebf6 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -8,6 +8,7 @@ using Database.Enumerations; using Database.Extensions; using HotChocolate.Types; +using HotChocolate.Authorization; namespace Database.GraphQl.DataX; @@ -52,6 +53,7 @@ IReadOnlyList path ) => new(code, message, path); // [GraphQLType>] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task DeleteDataAsync( DeleteDataInput input, CommonAuthorization authorization, @@ -109,4 +111,4 @@ await context.Entry(data) await context.SaveChangesAsync(cancellationToken); return NewPayload(data, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index 1fa13bd8..27bf171f 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -10,6 +10,7 @@ using Database.Extensions; using Database.Services; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.DataX; @@ -57,6 +58,7 @@ protected override PublishDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task PublishDataAsync( PublishDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs index 5201745b..4a6024c7 100644 --- a/backend/src/GraphQl/DataX/RetractDataMutation.cs +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -9,6 +9,7 @@ using Database.Extensions; using Database.Services; using HotChocolate.Types; +using HotChocolate.Authorization; namespace Database.GraphQl.DataX; @@ -53,6 +54,7 @@ protected override RetractDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task RetractDataAsync( RetractDataInput input, CommonAuthorization authorization, @@ -121,4 +123,4 @@ CancellationToken cancellationToken return NewPayload(data, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index 7fc0321c..c8927e38 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -10,6 +10,7 @@ using Database.Extensions; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; namespace Database.GraphQl.DataX; @@ -61,6 +62,7 @@ protected override UpdateDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UpdateDataAsync( UpdateDataInput input, CommonAuthorization authorization, @@ -140,4 +142,4 @@ CancellationToken cancellationToken return NewPayload(data, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index 7b0172bb..051ad5db 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -2,14 +2,17 @@ using System.Threading.Tasks; using Database.ApiRequests; using HotChocolate.Resolvers; +using HotChocolate.Authorization; using HotChocolate.Types; using static Database.ApiRequests.UpdateDatabase; +using Database.Authorization; namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateDatabaseMutation { + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, UpdateDatabase updateDatabase, @@ -40,4 +43,4 @@ CancellationToken cancellationToken } return databasePayload; } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs index 663a72d6..15a39a40 100644 --- a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs @@ -2,13 +2,16 @@ using System.Threading.Tasks; using Database.ApiRequests; using HotChocolate.Resolvers; +using HotChocolate.Authorization; using static Database.ApiRequests.VerifyDatabase; +using Database.Authorization; namespace Database.GraphQl.Databases; // TODO [ExtendObjectType(nameof(Mutation))] public sealed class VerifyDatabaseMutation { + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task VerifyDatabaseAsync( VerifyDatabaseInput input, VerifyDatabase verifyDatabase, @@ -39,4 +42,4 @@ CancellationToken cancellationToken } return databasePayload; } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 4eb732d4..6c328383 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -12,6 +12,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -102,6 +103,7 @@ protected override CreateGeometricDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, @@ -175,4 +177,4 @@ CancellationToken cancellationToken return NewPayload(geometricData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 54ace539..6d9c1ad2 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -14,6 +14,7 @@ using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -90,6 +91,7 @@ protected override CreateGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, @@ -199,4 +201,4 @@ await context.GetHttpsResources return NewPayload(getHttpsResource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs index 4a7a69d9..001d9dc2 100644 --- a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -11,6 +11,7 @@ using Database.Services; using GraphQL.Client.Abstractions.Utilities; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -60,6 +61,7 @@ protected override DeleteGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task DeleteGetHttpsResourceAsync( DeleteGetHttpsResourceInput input, ApplicationDbContext context, @@ -155,4 +157,4 @@ CancellationToken cancellationToken return NewPayload(resource, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 42d0bf24..3e69dbac 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async ValueTask> GetPendingGetHttpsResources( ApplicationDbContext context, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 6e232dc3..86b45c26 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -12,6 +12,7 @@ using Database.Services; using GreenDonut.Data; using HotChocolate.Data; +using HotChocolate.Authorization; using HotChocolate.Data.Filters; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -79,6 +80,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task RecomputeGetHttpsResourceHashValuesAsync( ApplicationDbContext context, IResolverContext resolverContext, @@ -147,4 +149,4 @@ await Parallel.ForEachAsync( } return NewPayload(resources, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs index c772ec4b..7611e16b 100644 --- a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -12,6 +12,7 @@ using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -63,6 +64,7 @@ protected override SetGetHttpsResourceParentError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task SetGetHttpsResourceParentAsync( SetGetHttpsResourceParentInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index ef90b146..c9da321d 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -12,6 +12,7 @@ using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -64,6 +65,7 @@ protected override UpdateChildGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UpdateChildGetHttpsResourceAsync( UpdateChildGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index 7d85cdba..cb7bf2eb 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -12,6 +12,7 @@ using GraphQL.Client.Abstractions.Utilities; using GreenDonut.Data; using HotChocolate.Types; +using HotChocolate.Authorization; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GetHttpsResources; @@ -62,6 +63,7 @@ protected override UpdateRootGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UpdateRootGetHttpsResourceAsync( UpdateRootGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 344ed259..5acd6ae7 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -12,6 +12,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -95,6 +96,7 @@ protected override CreateHygrothermalDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, @@ -168,4 +170,4 @@ CancellationToken cancellationToken return NewPayload(hygrothermalData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs index fecfc5e5..84ec7bf8 100644 --- a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -12,6 +12,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -95,6 +96,7 @@ protected override CreateLifeCycleDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateLifeCycleDataAsync( CreateLifeCycleDataInput input, ApplicationDbContext context, @@ -168,4 +170,4 @@ CancellationToken cancellationToken return NewPayload(lifeCycleData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 65c9215b..064d8073 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -14,6 +14,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -117,7 +118,7 @@ protected override CreateOpticalDataError NewError( IReadOnlyList path ) => new(code, message, path); - // [UseUserManager] [Authorize(Policy = Configuration.AuthConfiguration.WritePolicy)] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, @@ -191,4 +192,4 @@ CancellationToken cancellationToken return NewPayload(opticalData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 6b8503e0..9f79630f 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -12,6 +12,7 @@ using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Authorization; using HotChocolate.Types; using NodaTime; @@ -95,6 +96,7 @@ protected override CreatePhotovoltaicDataError NewError( IReadOnlyList path ) => new(code, message, path); + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, @@ -168,4 +170,4 @@ CancellationToken cancellationToken return NewPayload(photovoltaicData, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index e10dd56d..5333d9a7 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -12,6 +12,7 @@ using Database.Services; using GreenDonut.Data; using HotChocolate.Data; +using HotChocolate.Authorization; using HotChocolate.Data.Filters; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -79,6 +80,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task CreateResponseApprovalsAsync( IResolverContext resolverContext, ApplicationDbContext context, @@ -131,4 +133,4 @@ await Parallel.ForEachAsync( } return NewPayload(dataSets, null); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 7c59e799..4fe4063c 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -11,6 +11,7 @@ using Database.Services; using GreenDonut.Data; using HotChocolate.Data; +using HotChocolate.Authorization; using HotChocolate.Data.Filters; using HotChocolate.Resolvers; using HotChocolate.Types; @@ -77,6 +78,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] + [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] public async Task UpdateResponseApprovalsAsync( IResolverContext resolverContext, ApplicationDbContext context, @@ -130,4 +132,4 @@ await Parallel.ForEachAsync( } return NewPayload(dataSets, null); } -} \ No newline at end of file +} From 48e508d703c9baf816f4ec16a09040d1a098b180 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 17:02:19 +0200 Subject: [PATCH 838/878] Rewrite file upload minimizing resource usage on multi-gigabyte uploads and allowing plain-text post requests --- .../GetHttpsResourcesController.cs | 177 ++++++------------ ...onnectApplicationAccessPolicyFilterType.cs | 2 +- backend/src/GraphQl/DataX/DataResolvers.cs | 7 +- backend/src/GraphQl/DataX/DataTypeBase.cs | 6 +- backend/src/Services/FileManager.cs | 139 ++++++++++++++ backend/src/Startup.cs | 1 + backend/src/Utilities/FileHelpers.cs | 104 ---------- .../src/Utilities/MultipartRequestHelper.cs | 53 ++---- 8 files changed, 220 insertions(+), 269 deletions(-) create mode 100644 backend/src/Services/FileManager.cs delete mode 100644 backend/src/Utilities/FileHelpers.cs diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index b547eb54..16a9a0b0 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -1,10 +1,7 @@ using System; using System.IO; using System.Linq; -using System.Net; using System.Net.Mime; -using System.Reflection; -using System.Text; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; @@ -15,29 +12,15 @@ using Database.Services; using Database.Utilities; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.WebUtilities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; namespace Database.Controllers; public static partial class Log { - [LoggerMessage( - Level = LogLevel.Information, - Message = "Uploaded file '{FileNameForDisplay}' to '{FilePath}'.")] - public static partial void SavedUploadedFile( - this ILogger logger, - string? fileNameForDisplay, - string filePath - ); - [LoggerMessage( Level = LogLevel.Error, Message = "Failed to create response approval for data with ID {DataId}.")] @@ -52,13 +35,10 @@ Exception exception // and https://github.com/dotnet/AspNetCore.Docs/blob/b4599432690b8753fc2eac23d52957f47e01997a/aspnetcore/mvc/models/file-uploads/samples/3.x/SampleApp/ [ApiController] public sealed class GetHttpsResourcesController( + FileManager fileManager, ILogger logger ) : Controller { - // Get the default form options so that we can use them to set the default - // limits for request body data. - private static readonly FormOptions s_defaultFormOptions = new(); - private bool _disposed; protected override void Dispose(bool disposing) @@ -122,7 +102,7 @@ public async Task Get( { return Problem( title: "Resource Not Found", - detail: $"There is no GET HTTPS resource with ID `{id:D}`.", + detail: $"There is no GET HTTPS resource with ID '{id:D}'.", statusCode: StatusCodes.Status404NotFound, instance: HttpContext.Request.Path ); @@ -131,7 +111,7 @@ public async Task Get( { return Problem( title: "Extension Not Found", - detail: $"There is no GET HTTPS resource with ID `{id:D}` and the extension `{extension}`. The resource's extension is `{getHttpsResource.FileExtension}`.", + detail: $"There is no GET HTTPS resource with ID '{id:D}' and the extension '{extension}'. The resource's extension is '{getHttpsResource.FileExtension}'.", statusCode: StatusCodes.Status404NotFound, instance: HttpContext.Request.Path ); @@ -140,7 +120,7 @@ public async Task Get( { return Problem( title: "Content Not Found", - detail: $"The GET HTTPS resource with ID `{id:D}` and the extension `{getHttpsResource.FileExtension}` does not have content.", + detail: $"The GET HTTPS resource with ID '{id:D}' and the extension '{getHttpsResource.FileExtension}' does not have content.", statusCode: StatusCodes.Status404NotFound, instance: HttpContext.Request.Path ); @@ -149,7 +129,7 @@ public async Task Get( { ModelState.AddModelError( nameof(id), - $"There is no data set associated with the GET HTTPS resource with ID `{id:D}`." + $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'." ); return BadRequest(ModelState); } @@ -174,7 +154,7 @@ public async Task Get( { ModelState.AddModelError( nameof(id), - $"Failed to fetch the content type of the GET HTTPS resource with ID `{id:D}`. Please try again in a few minutes." + $"Failed to fetch the content type of the GET HTTPS resource with ID '{id:D}'. Please try again in a few minutes." ); return BadRequest(ModelState); } @@ -186,6 +166,13 @@ public async Task Get( ); } + private enum MyContentType + { + MULTIPART, + PIPE, + OTHER + } + public const string UploadRouteName = "UploadResource"; public static object CreateUploadActionRouteValues(GetHttpsResource getHttpsResource) => @@ -199,8 +186,8 @@ public static object CreateUploadActionRouteValues(GetHttpsResource getHttpsReso [DisableFormValueModelBinding] [DisableRequestSizeLimit] [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] - [Consumes(MediaTypeNames.Multipart.FormData)] - [AcceptsMultipartFormFile] + [Consumes(MediaTypeNames.Multipart.FormData, MediaTypeNames.Text.Plain)] + // [AcceptsMultipartFormFile] [Produces(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -218,14 +205,6 @@ CancellationToken cancellationToken { return Unauthorized(); } - if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) - { - ModelState.AddModelError( - "file", - "The request content is not multipart." - ); - return BadRequest(ModelState); - } var getHttpsResource = await context.GetHttpsResourcesWithData.AsQueryable() .Where(_ => _.Id == id) .SingleOrDefaultAsync(cancellationToken); @@ -233,7 +212,7 @@ CancellationToken cancellationToken { return Problem( title: "Not Found", - detail: $"There is no GET HTTPS resource with ID `{id:D}`.", + detail: $"There is no GET HTTPS resource with ID '{id:D}'.", statusCode: StatusCodes.Status404NotFound, instance: HttpContext.Request.Path ); @@ -242,59 +221,43 @@ CancellationToken cancellationToken { ModelState.AddModelError( nameof(id), - $"There is no data set associated with the GET HTTPS resource with ID `{id:D}`." + $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'." ); return BadRequest(ModelState); } Directory.CreateDirectory(GetHttpsResource.FilesDirectoryPath); - var boundary = MultipartRequestHelper.GetBoundary( - MediaTypeHeaderValue.Parse(Request.ContentType), - s_defaultFormOptions.MultipartBoundaryLengthLimit); - var reader = new MultipartReader(boundary, HttpContext.Request.Body); - var section = await reader.ReadNextSectionAsync(cancellationToken); - while (section is not null) + var contentType = + MultipartRequestHelper.IsMultipartContentType(Request.ContentType) ? MyContentType.MULTIPART + : Request.HasFormContentType ? MyContentType.PIPE + : MyContentType.OTHER; + if (contentType is MyContentType.OTHER) { - var hasContentDispositionHeader = - ContentDispositionHeaderValue.TryParse( - section.ContentDisposition, out var contentDisposition); - if (hasContentDispositionHeader) - { - // This check assumes that there's a file - // present without form data. If form data - // is present, this method immediately fails - // and returns the model error. - if (!MultipartRequestHelper.HasFileContentDisposition( - contentDisposition ?? throw new ArgumentException("Impossible (because `hasContentDispositionHeader` is `true`)") - ) - ) + return BadRequest("The request does neither contain valid multipart form data nor a valid form."); + } + switch (contentType) + { + case MyContentType.MULTIPART: + var boundary = MultipartRequestHelper.GetBoundary(Request.ContentType); + if (string.IsNullOrWhiteSpace(boundary)) { - ModelState.AddModelError("file", "There is no content-disposition header."); - return BadRequest(ModelState); + return BadRequest("Missing boundary in multipart form data."); } - var streamedFileContent = await FileHelpers.ProcessStreamedFile( - section, - contentDisposition, - ModelState + await fileManager.SaveViaMultipartReaderAsync( + getHttpsResource.FilePath, + boundary, + Request.Body, + cancellationToken ); - if (!ModelState.IsValid) - { - return BadRequest(ModelState); - } - using var targetStream = System.IO.File.Create(getHttpsResource.FilePath); - await targetStream.WriteAsync(streamedFileContent, cancellationToken); - // Don't trust the file name sent by the client. To display the - // file name, HTML-encode the value. -#pragma warning disable CA1873 // Evaluation of this argument may be expensive and unnecessary if logging is disabled (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1873) - logger.SavedUploadedFile( - WebUtility.HtmlEncode(contentDisposition.FileName.Value), - getHttpsResource.FilePath + break; + case MyContentType.PIPE: + await fileManager.SaveViaPipeReaderAsync( + getHttpsResource.FilePath, + Request.BodyReader, + cancellationToken ); -#pragma warning restore CA1873 break; - } - // Drain any remaining section body that hasn't been consumed and - // read the headers for the next section. - section = await reader.ReadNextSectionAsync(cancellationToken); + default: + return BadRequest($"Unsupported content type '{contentType}'"); } if (getHttpsResource.IsRoot()) { @@ -322,45 +285,25 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( ); } - private static Encoding GetEncoding(MultipartSection section) - { - var hasMediaTypeHeader = - MediaTypeHeaderValue.TryParse(section.ContentType, out var mediaType); - - // UTF-7 is insecure and shouldn't be honored. UTF-8 succeeds in most - // cases. -#pragma warning disable SYSLIB0001 - if (!hasMediaTypeHeader || Encoding.UTF7.Equals( - mediaType?.Encoding ?? - throw new ArgumentException("Impossible (because `hasMediaTypeHeader` is `true`)!")) - ) - { -#pragma warning restore SYSLIB0001 - return Encoding.UTF8; - } - - return mediaType.Encoding; - } - // solely for the OpenAPI documentation - private sealed record FormFile(IFormFile File); + // private sealed record FormFile(IFormFile File); // solely for the OpenAPI documentation - [AttributeUsage(AttributeTargets.Method)] - internal sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider - { - public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) - { - } + // [AttributeUsage(AttributeTargets.Method)] + // internal sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider + // { + // public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) + // { + // } - public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) - { - builder.Metadata.Add( - new AcceptsMetadata( - [MediaTypeNames.Multipart.FormData], - typeof(FormFile) - ) - ); - } - } + // public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) + // { + // builder.Metadata.Add( + // new AcceptsMetadata( + // [MediaTypeNames.Multipart.FormData], + // typeof(FormFile) + // ) + // ); + // } + // } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs index c3f8a4bf..37af53ab 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs @@ -4,7 +4,7 @@ namespace Database.GraphQl.AccessPolicies; -public class OpenIdConnectApplicationAccessPolicyFilterType +public sealed class OpenIdConnectApplicationAccessPolicyFilterType : AuditableEntityFilterType { protected override void Configure( diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index 31f76358..cdcd0fff 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -89,10 +89,11 @@ IInstitutionByIdDataLoader byId } [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public static Task IsNobodyAllowedAsync( + public static async Task IsNobodyAllowedAsync( [Parent] IData data, ApplicationDbContext databaseContext, CommonAuthorization authorization, + IResolverContext resolverContext, CancellationToken cancellationToken ) { @@ -125,10 +126,11 @@ public static Task IsAnyoneAllowedAsync( [Parent] IData data, ApplicationDbContext databaseContext, CommonAuthorization authorization, + IResolverContext resolverContext, CancellationToken cancellationToken ) { - return IsAccessAllowedAsync(data, null, null, null, databaseContext, authorization, cancellationToken); + return IsAccessAllowedAsync(data, null, null, null, databaseContext, authorization, resolverContext, cancellationToken); } [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] @@ -139,6 +141,7 @@ public static async Task IsAccessAllowedAsync( string? openIdConnectClientId, ApplicationDbContext databaseContext, CommonAuthorization authorization, + IResolverContext resolverContext, CancellationToken cancellationToken ) { diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index 9e1a5587..32ad7487 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -43,15 +43,15 @@ IObjectTypeDescriptor descriptor descriptor .Field(nameof(DataResolvers.IsNobodyAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsNobodyAllowedAsync(default!, default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsNobodyAllowedAsync(default!, default!, default!, default!, default!)); descriptor .Field(nameof(DataResolvers.IsAnyoneAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!, default!)); descriptor .Field(nameof(DataResolvers.IsAccessAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsAccessAllowedAsync(default!, default!, default!, default!, default!, default!, default!, default!)); descriptor .Field(_ => _.Approval) .Cost(3) diff --git a/backend/src/Services/FileManager.cs b/backend/src/Services/FileManager.cs new file mode 100644 index 00000000..8c4a5810 --- /dev/null +++ b/backend/src/Services/FileManager.cs @@ -0,0 +1,139 @@ +using System.IO; +using System.IO.Pipelines; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Logging; +using Microsoft.Net.Http.Headers; + +namespace Database.Services; + +public static partial class Log +{ + [LoggerMessage( + Level = LogLevel.Information, + Message = "Processing file '{FileName}'")] + public static partial void ProcessingFile( + this ILogger logger, + string? fileName + ); + + [LoggerMessage( + Level = LogLevel.Information, + Message = "Received metadata: {Key} = {Value}")] + public static partial void ReceivedMetadata( + this ILogger logger, + string? key, + string value + ); + + [LoggerMessage( + Level = LogLevel.Information, + Message = "File upload completed (via {Type}). Total bytes read: {TotalBytesRead} bytes.")] + public static partial void UploadedFile( + this ILogger logger, + string type, + long totalBytesRead + ); + + [LoggerMessage( + Level = LogLevel.Debug, + Message = "Removed existing file '{FilePath}'")] + public static partial void RemovedExistingFile( + this ILogger logger, + string filePath + ); +} + +// Inspired by https://github.com/dotnet/AspNetCore.Docs/blob/main/aspnetcore/mvc/models/file-uploads/samples/9.x/FileManagerSample/Services/FileManagerService.cs +public sealed class FileManager( + ILogger logger +) +{ + private const int BufferSize = 16 * 1024 * 1024; // 16 MB buffer size + + public async Task SaveViaMultipartReaderAsync( + string targetFilePath, + string boundary, + Stream contentStream, + CancellationToken cancellationToken + ) + { + DeleteFileIfExisting(targetFilePath); + using var targetFileStream = new FileStream( + path: targetFilePath, + mode: FileMode.OpenOrCreate, + access: FileAccess.Write, + share: FileShare.None, + bufferSize: BufferSize, + useAsync: true + ); + var reader = new MultipartReader(boundary, contentStream); + MultipartSection? section; + long totalBytesRead = 0; + // Process each section in the multipart body + while ((section = await reader.ReadNextSectionAsync(cancellationToken)) is not null) + { + // Check if the section is a file + var contentDisposition = section.GetContentDispositionHeader(); + if (contentDisposition is not null && contentDisposition.IsFileDisposition()) + { + logger.ProcessingFile(contentDisposition.FileName.Value); + // Write the file content to the target file + await section.Body.CopyToAsync(targetFileStream, cancellationToken); + totalBytesRead += section.Body.Length; + } + else if (contentDisposition is not null && contentDisposition.IsFormDisposition()) + { + // Handle metadata (form fields) + using var streamReader = new StreamReader(section.Body); + var value = await streamReader.ReadToEndAsync(cancellationToken); + logger.ReceivedMetadata(contentDisposition.Name.Value, value); + } + } + logger.UploadedFile(nameof(MultipartReader), totalBytesRead); + } + + public async Task SaveViaPipeReaderAsync( + string targetFilePath, + PipeReader contentReader, + CancellationToken cancellationToken + ) + { + DeleteFileIfExisting(targetFilePath); + long totalBytesRead = 0; + using var targetFileStream = new FileStream( + path: targetFilePath, + mode: FileMode.OpenOrCreate, + access: FileAccess.Write, + share: FileShare.None, + bufferSize: BufferSize, + useAsync: true + ); + while (true) + { + var readResult = await contentReader.ReadAsync(cancellationToken); + var buffer = readResult.Buffer; + foreach (var memory in buffer) + { + await targetFileStream.WriteAsync(memory, cancellationToken); + totalBytesRead += memory.Length; + } + contentReader.AdvanceTo(buffer.End); + if (readResult.IsCompleted) + { + break; + } + } + logger.UploadedFile(nameof(PipeReader), totalBytesRead); + } + + private void DeleteFileIfExisting(string filePath) + { + if (File.Exists(filePath)) + { + File.Delete(filePath); + logger.RemovedExistingFile(filePath); + } + } +} \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 44a5b01b..7c43e086 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -294,6 +294,7 @@ public static void ConfigureCustomServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); } public static void ConfigureApiRequests(IServiceCollection services) diff --git a/backend/src/Utilities/FileHelpers.cs b/backend/src/Utilities/FileHelpers.cs deleted file mode 100644 index 1bca5e53..00000000 --- a/backend/src/Utilities/FileHelpers.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Net; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.WebUtilities; -using Microsoft.Net.Http.Headers; - -namespace Database.Utilities; - -public static class FileHelpers -{ - public static async Task ProcessFormFile( - IFormFile formFile, - ModelStateDictionary modelState - ) - { - var fieldDisplayName = string.Empty; - // Use reflection to obtain the display name for the model - // property associated with this IFormFile. If a display - // name isn't found, error messages simply won't show - // a display name. - var property = - typeof(T).GetProperty( - formFile.Name[(formFile.Name.IndexOf('.') + 1)..]); - if (property is not null) - { - if (property.GetCustomAttribute() is - DisplayAttribute displayAttribute) - { - fieldDisplayName = $"{displayAttribute.Name} "; - } - } - // Don't trust the file name sent by the client. To display - // the file name, HTML-encode the value. - var trustedFileNameForDisplay = WebUtility.HtmlEncode( - formFile.FileName); - // Check the file length. This check doesn't catch files that only have - // a BOM as their content. - if (formFile.Length is 0) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - return []; - } - try - { - using var memoryStream = new MemoryStream(); - await formFile.CopyToAsync(memoryStream); - // Check the content length in case the file's only - // content was a BOM and the content is actually - // empty after removing the BOM. - if (memoryStream.Length is 0) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) is empty."); - } - else - { - return memoryStream.ToArray(); - } - } - catch (Exception exception) - { - modelState.AddModelError(formFile.Name, - $"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " + - $"Please contact the Help Desk for support. Error: {exception.HResult}"); - // Log the exception - } - return []; - } - - public static async Task ProcessStreamedFile( - MultipartSection section, - ContentDispositionHeaderValue contentDisposition, - ModelStateDictionary modelState - ) - { - try - { - using var memoryStream = new MemoryStream(); - await section.Body.CopyToAsync(memoryStream); - if (memoryStream.Length is 0) - { - modelState.AddModelError("File", "The file is empty."); - } - else - { - return memoryStream.ToArray(); - } - } - catch (Exception exception) - { - modelState.AddModelError("File", - "The upload failed. Please contact the Help Desk " + - $" for support. Error: {exception.HResult}"); - // Log the exception - } - return []; - } -} \ No newline at end of file diff --git a/backend/src/Utilities/MultipartRequestHelper.cs b/backend/src/Utilities/MultipartRequestHelper.cs index 88a79f7f..11d6f8cc 100644 --- a/backend/src/Utilities/MultipartRequestHelper.cs +++ b/backend/src/Utilities/MultipartRequestHelper.cs @@ -1,5 +1,5 @@ using System; -using System.IO; +using System.Diagnostics.Contracts; using Microsoft.Net.Http.Headers; namespace Database.Utilities; @@ -8,45 +8,14 @@ public static class MultipartRequestHelper { // Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq" // The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit. - public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit) - { - var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value; - - if (string.IsNullOrWhiteSpace(boundary)) - { - throw new InvalidDataException("Missing content-type boundary."); - } - - if (boundary.Length > lengthLimit) - { - throw new InvalidDataException( - $"Multipart boundary length limit {lengthLimit} exceeded."); - } - - return boundary; - } - - public static bool IsMultipartContentType(string? contentType) - { - return !string.IsNullOrEmpty(contentType) - && contentType.Contains("multipart/", StringComparison.OrdinalIgnoreCase); - } - - public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition) - { - // Content-Disposition: form-data; name="key"; - return contentDisposition is not null - && contentDisposition.DispositionType.Equals("form-data") - && string.IsNullOrEmpty(contentDisposition.FileName.Value) - && string.IsNullOrEmpty(contentDisposition.FileNameStar.Value); - } - - public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition) - { - // Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg" - return contentDisposition is not null - && contentDisposition.DispositionType.Equals("form-data") - && (!string.IsNullOrEmpty(contentDisposition.FileName.Value) - || !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value)); - } + [Pure] + public static string? GetBoundary(string? contentType) => + HeaderUtilities.RemoveQuotes( + MediaTypeHeaderValue.Parse(contentType).Boundary + ).Value; + + [Pure] + public static bool IsMultipartContentType(string? contentType) => + !string.IsNullOrEmpty(contentType) + && contentType.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase); } \ No newline at end of file From 1e0042cf4dbfee4ecd35b60d064c8ed6f0fc755c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 18:07:24 +0200 Subject: [PATCH 839/878] Update the GraphQL schema --- .../GraphQlSchemaTests.IsUnchanged.snap | 155 +++++++++++++++--- frontend/type-defs.graphqls | 155 +++++++++++++++--- 2 files changed, 270 insertions(+), 40 deletions(-) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index c6a6aeda..51eea88f 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -560,6 +560,7 @@ type CalorimetricData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -817,15 +818,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -834,12 +836,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ type DataAccessPolicy { combinator: LogicalCombinator! @@ -857,6 +879,7 @@ type DataAccessPolicy { ): Boolean! isAnyoneAllowed: Boolean! isGlobal: Boolean! + isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput @@ -992,6 +1015,7 @@ type GeometricData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1105,6 +1129,7 @@ type HygrothermalData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1210,6 +1235,7 @@ type LifeCycleData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1326,6 +1352,7 @@ type OpticalData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1396,6 +1423,7 @@ type PhotovoltaicData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1966,15 +1994,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -1983,12 +2012,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] @@ -2001,6 +2050,7 @@ input ConfigureDataAccessPoliciesPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2175,15 +2225,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -2192,12 +2243,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] @@ -2210,6 +2281,7 @@ input DataAccessPolicyPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2244,15 +2316,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -2261,12 +2334,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicySortInput { dataId: SortEnumType @cost(weight: "10") @@ -3045,15 +3138,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -3062,12 +3156,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] @@ -3080,6 +3194,7 @@ input ResetDataAccessPoliciesPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index c6a6aeda..51eea88f 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -560,6 +560,7 @@ type CalorimetricData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -817,15 +818,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -834,12 +836,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ type DataAccessPolicy { combinator: LogicalCombinator! @@ -857,6 +879,7 @@ type DataAccessPolicy { ): Boolean! isAnyoneAllowed: Boolean! isGlobal: Boolean! + isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput @@ -992,6 +1015,7 @@ type GeometricData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1105,6 +1129,7 @@ type HygrothermalData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1210,6 +1235,7 @@ type LifeCycleData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1326,6 +1352,7 @@ type OpticalData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1396,6 +1423,7 @@ type PhotovoltaicData implements Node & Data { userId: Uuid ): Boolean! IsAnyoneAllowed: Boolean! + IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! name: String @@ -1966,15 +1994,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -1983,12 +2012,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] @@ -2001,6 +2050,7 @@ input ConfigureDataAccessPoliciesPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2175,15 +2225,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -2192,12 +2243,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] @@ -2210,6 +2281,7 @@ input DataAccessPolicyPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput @@ -2244,15 +2316,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -2261,12 +2334,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicySortInput { dataId: SortEnumType @cost(weight: "10") @@ -3045,15 +3138,16 @@ than exactly that number of GraphQL or REST API accesses are allowed. And if an additional duration is given, from the time of the first access until the duration passed, the given number of accesses are allowed; the start time and the access count are reset on the first access after the duration passed (a -sliding window of time). +sliding window of time). Note that if the limit is 0, access is explicitly +denied for the respective user, institution, or application. The individual decisions based on user, institution, and OpenID Connect application, can be combined conjuctively ('and' or 'all' need to be positive) -or disjunctively ('or' or 'any one'/'at least one' needs to be positive). This +or disjunctively ('or' or 'someone'/'at least one' needs to be positive). This is configured through the combinator and the mutation `ConfigureDataAccessPolicyMutation`. If there are no user access policies at all, then, in the 'all' case, no restrictions based on the user -itself are imposed, and in the 'any' case, no allowances based on the user +itself are imposed, and in the 'some' case, no allowances based on the user itself are given; and analogously for institution and application policies. Put another way, an empty list of user access policies is `true` in the 'and' case and `false` in the 'or' case, and analogously for institution and application @@ -3062,12 +3156,32 @@ access policies. In particular, a data access policy with the combinator 'all' and empty user, institution, and application policies allows access to anyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to -noone, no matter if authenticated or not. +nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a specific data entry, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. + +There are mutations to +* reset a data access policy to its original state, in which anyone is allowed + access; +* configure the logical combinator of a data access policy (as explained above); +* clear user, institution, and applicatoin policies of a data access policy; +* set and unset a user, institution, or applicatoin policy for a specific user, + institution, or application, and a specific data access policy (global or + associated with a specific data entry). + +To determine whether access policies allow/restrict access in the way you +expected, use their `is*Allowed` fields, passing the applicable user ID, +institution IDs, and/or client ID as argument(s). The field +`AccessCountSinceStartTime` informs you about the +number of accesses that were counted for the user, institution, or application +policy since the start time. Both are reset on the first access after the end +of the time span or when a policy is updated via the respective 'set' mutation. + +Note that durations are represented in the +[ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] @@ -3080,6 +3194,7 @@ input ResetDataAccessPoliciesPropositionInput { institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput + isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput opticalData: OpticalDataPropositionInput From 49ecbc0e50f454347e26db81322862df8d6ca882 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 19:58:52 +0200 Subject: [PATCH 840/878] Do not use the write policy for now but just the authenticated policy (the protection level is still the same because we later check whether the user is a database operator representative) --- .../AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs | 2 +- .../ClearOpenIdConnectApplicationAccessPoliciesMutation.cs | 2 +- .../GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs | 2 +- .../AccessPolicies/ConfigureDataAccessPoliciesMutation.cs | 2 +- .../AccessPolicies/ConfigureDataAccessPolicyMutation.cs | 2 +- .../GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs | 2 +- .../GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs | 2 +- .../AccessPolicies/SetInstitutionAccessPolicyMutation.cs | 2 +- .../SetOpenIdConnectApplicationAccessPolicyMutation.cs | 2 +- .../src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs | 2 +- .../AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs | 2 +- .../UnsetOpenIdConnectApplicationAccessPolicyMutation.cs | 2 +- .../GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs | 2 +- .../CalorimetricDataX/CreateCalorimetricDataMutation.cs | 2 +- backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs | 2 +- .../src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs | 2 +- backend/src/GraphQl/DataX/DeleteDataMutation.cs | 2 +- backend/src/GraphQl/DataX/PublishDataMutation.cs | 2 +- backend/src/GraphQl/DataX/RetractDataMutation.cs | 2 +- backend/src/GraphQl/DataX/UpdateDataMutation.cs | 2 +- backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs | 2 +- backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs | 2 +- .../src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs | 2 +- .../GetHttpsResources/CreateGetHttpsResourceMutation.cs | 2 +- .../GetHttpsResources/DeleteGetHttpsResourceMutation.cs | 2 +- .../src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs | 4 ++-- .../RecomputeGetHttpsResourceHashValuesMutation.cs | 2 +- .../GetHttpsResources/SetGetHttpsResourceParentMutation.cs | 2 +- .../GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs | 2 +- .../GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs | 2 +- .../HygrothermalDataX/CreateHygrothermalDataMutation.cs | 2 +- .../src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs | 2 +- backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs | 2 +- .../PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs | 2 +- .../ResponseApprovals/CreateResponseApprovalsMutation.cs | 2 +- .../ResponseApprovals/UpdateResponseApprovalsMutation.cs | 2 +- 36 files changed, 37 insertions(+), 37 deletions(-) diff --git a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs index a0f981cc..7ae8363a 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearInstitutionAccessPoliciesMutation.cs @@ -53,7 +53,7 @@ protected override ClearInstitutionAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ClearInstitutionAccessPoliciesAsync( ClearInstitutionAccessPoliciesInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs index a9719c16..411da690 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearOpenIdConnectApplicationAccessPoliciesMutation.cs @@ -53,7 +53,7 @@ protected override ClearOpenIdConnectApplicationAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ClearOpenIdConnectApplicationAccessPoliciesAsync( ClearOpenIdConnectApplicationAccessPoliciesInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs index 0e8d5008..7d19f425 100644 --- a/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ClearUserAccessPoliciesMutation.cs @@ -53,7 +53,7 @@ protected override ClearUserAccessPoliciesError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ClearUserAccessPoliciesAsync( ClearUserAccessPoliciesInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs index bedac5ff..f15ffe9a 100644 --- a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPoliciesMutation.cs @@ -72,7 +72,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ConfigureDataAccessPoliciesAsync( ConfigureDataAccessPoliciesInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs index 1371005d..ee189f03 100644 --- a/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ConfigureDataAccessPolicyMutation.cs @@ -56,7 +56,7 @@ protected override ConfigureDataAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ConfigureDataAccessPolicyAsync( ConfigureDataAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs index 9794a9d9..d651be14 100644 --- a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPoliciesMutation.cs @@ -66,7 +66,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ResetDataAccessPoliciesAsync( ApplicationDbContext context, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs index 385b3d21..8cd7c22a 100644 --- a/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/ResetDataAccessPolicyMutation.cs @@ -53,7 +53,7 @@ protected override ResetDataAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task ResetDataAccessPolicyAsync( ResetDataAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs index 5837e70e..471b67d2 100644 --- a/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetInstitutionAccessPolicyMutation.cs @@ -60,7 +60,7 @@ protected override SetInstitutionAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task SetInstitutionAccessPolicyAsync( SetInstitutionAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs index c32395fc..d055d66e 100644 --- a/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -59,7 +59,7 @@ protected override SetOpenIdConnectApplicationAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task SetOpenIdConnectApplicationAccessPolicyAsync( SetOpenIdConnectApplicationAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs index b759be4b..e34c567f 100644 --- a/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/SetUserAccessPolicyMutation.cs @@ -60,7 +60,7 @@ protected override SetUserAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task SetUserAccessPolicyAsync( SetUserAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs index 7ea334d8..fa46aa1b 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetInstitutionAccessPolicyMutation.cs @@ -57,7 +57,7 @@ protected override UnsetInstitutionAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UnsetInstitutionAccessPolicyAsync( UnsetInstitutionAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs index 9032139c..4fdf0a4d 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetOpenIdConnectApplicationAccessPolicyMutation.cs @@ -57,7 +57,7 @@ protected override UnsetOpenIdConnectApplicationAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UnsetOpenIdConnectApplicationAccessPolicyAsync( UnsetOpenIdConnectApplicationAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs index 13bde561..f387a52b 100644 --- a/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs +++ b/backend/src/GraphQl/AccessPolicies/UnsetUserAccessPolicyMutation.cs @@ -58,7 +58,7 @@ protected override UnsetUserAccessPolicyError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UnsetUserAccessPolicyAsync( UnsetUserAccessPolicyInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs index 9408a381..2c2eeb8a 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CreateCalorimetricDataMutation.cs @@ -100,7 +100,7 @@ protected override CreateCalorimetricDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateCalorimetricDataAsync( CreateCalorimetricDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index a9fc0fa9..43f92532 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -99,7 +99,7 @@ protected override AddDataApprovalError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task AddDataApprovalAsync( AddDataApprovalInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs index f4fa7780..b3c0f598 100644 --- a/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/RemoveDataApprovalMutation.cs @@ -61,7 +61,7 @@ protected override RemoveDataApprovalError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task RemoveDataApprovalAsync( RemoveDataApprovalInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/DataX/DeleteDataMutation.cs b/backend/src/GraphQl/DataX/DeleteDataMutation.cs index 1c94ebf6..15056ad5 100644 --- a/backend/src/GraphQl/DataX/DeleteDataMutation.cs +++ b/backend/src/GraphQl/DataX/DeleteDataMutation.cs @@ -53,7 +53,7 @@ IReadOnlyList path ) => new(code, message, path); // [GraphQLType>] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task DeleteDataAsync( DeleteDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/DataX/PublishDataMutation.cs b/backend/src/GraphQl/DataX/PublishDataMutation.cs index 27bf171f..bc891acd 100644 --- a/backend/src/GraphQl/DataX/PublishDataMutation.cs +++ b/backend/src/GraphQl/DataX/PublishDataMutation.cs @@ -58,7 +58,7 @@ protected override PublishDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task PublishDataAsync( PublishDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/DataX/RetractDataMutation.cs b/backend/src/GraphQl/DataX/RetractDataMutation.cs index 4a6024c7..475c5fd4 100644 --- a/backend/src/GraphQl/DataX/RetractDataMutation.cs +++ b/backend/src/GraphQl/DataX/RetractDataMutation.cs @@ -54,7 +54,7 @@ protected override RetractDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task RetractDataAsync( RetractDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/DataX/UpdateDataMutation.cs b/backend/src/GraphQl/DataX/UpdateDataMutation.cs index c8927e38..89ea232f 100644 --- a/backend/src/GraphQl/DataX/UpdateDataMutation.cs +++ b/backend/src/GraphQl/DataX/UpdateDataMutation.cs @@ -62,7 +62,7 @@ protected override UpdateDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UpdateDataAsync( UpdateDataInput input, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs index 051ad5db..b1211b29 100644 --- a/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/UpdateDatabaseMutation.cs @@ -12,7 +12,7 @@ namespace Database.GraphQl.Databases; [ExtendObjectType(nameof(Mutation))] public sealed class UpdateDatabaseMutation { - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UpdateDatabaseAsync( UpdateDatabaseInput input, UpdateDatabase updateDatabase, diff --git a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs index 15a39a40..4ffca930 100644 --- a/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs +++ b/backend/src/GraphQl/Databases/VerifyDatabaseMutation.cs @@ -11,7 +11,7 @@ namespace Database.GraphQl.Databases; // TODO [ExtendObjectType(nameof(Mutation))] public sealed class VerifyDatabaseMutation { - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task VerifyDatabaseAsync( VerifyDatabaseInput input, VerifyDatabase verifyDatabase, diff --git a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs index 6c328383..b30ef610 100644 --- a/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs +++ b/backend/src/GraphQl/GeometricDataX/CreateGeometricDataMutation.cs @@ -103,7 +103,7 @@ protected override CreateGeometricDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateGeometricDataAsync( CreateGeometricDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs index 6d9c1ad2..552851be 100644 --- a/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/CreateGetHttpsResourceMutation.cs @@ -91,7 +91,7 @@ protected override CreateGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateGetHttpsResourceAsync( CreateGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs index 001d9dc2..adecda71 100644 --- a/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/DeleteGetHttpsResourceMutation.cs @@ -61,7 +61,7 @@ protected override DeleteGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task DeleteGetHttpsResourceAsync( DeleteGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs index 3e69dbac..c199197a 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceQueries.cs @@ -42,7 +42,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async ValueTask> GetPendingGetHttpsResources( ApplicationDbContext context, CommonAuthorization authorization, @@ -88,4 +88,4 @@ CancellationToken cancellationToken .Where(_ => _.PhotovoltaicData == null || _.PhotovoltaicData.PublishingState != PublishingState.PENDING) .LoadAsync(id, cancellationToken); } -} \ No newline at end of file +} diff --git a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs index 86b45c26..332dc1c5 100644 --- a/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/RecomputeGetHttpsResourceHashValuesMutation.cs @@ -80,7 +80,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task RecomputeGetHttpsResourceHashValuesAsync( ApplicationDbContext context, IResolverContext resolverContext, diff --git a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs index 7611e16b..0e02a888 100644 --- a/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/SetGetHttpsResourceParentMutation.cs @@ -64,7 +64,7 @@ protected override SetGetHttpsResourceParentError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task SetGetHttpsResourceParentAsync( SetGetHttpsResourceParentInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs index c9da321d..3938e911 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateChildGetHttpsResourceMutation.cs @@ -65,7 +65,7 @@ protected override UpdateChildGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UpdateChildGetHttpsResourceAsync( UpdateChildGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs index cb7bf2eb..092665ea 100644 --- a/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs +++ b/backend/src/GraphQl/GetHttpsResources/UpdateRootGetHttpsResourceMutation.cs @@ -63,7 +63,7 @@ protected override UpdateRootGetHttpsResourceError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UpdateRootGetHttpsResourceAsync( UpdateRootGetHttpsResourceInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs index 5acd6ae7..4d8b2ff4 100644 --- a/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs +++ b/backend/src/GraphQl/HygrothermalDataX/CreateHygrothermalDataMutation.cs @@ -96,7 +96,7 @@ protected override CreateHygrothermalDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateHygrothermalDataAsync( CreateHygrothermalDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs index 84ec7bf8..05b17ab3 100644 --- a/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs +++ b/backend/src/GraphQl/LifeCycleDataX/CreateLifeCycleDataMutation.cs @@ -96,7 +96,7 @@ protected override CreateLifeCycleDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateLifeCycleDataAsync( CreateLifeCycleDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 064d8073..5f753448 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -118,7 +118,7 @@ protected override CreateOpticalDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateOpticalDataAsync( CreateOpticalDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs index 9f79630f..dc8398e4 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/CreatePhotovoltaicDataMutation.cs @@ -96,7 +96,7 @@ protected override CreatePhotovoltaicDataError NewError( IReadOnlyList path ) => new(code, message, path); - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreatePhotovoltaicDataAsync( CreatePhotovoltaicDataInput input, ApplicationDbContext context, diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index 5333d9a7..06b6e35e 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -80,7 +80,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task CreateResponseApprovalsAsync( IResolverContext resolverContext, ApplicationDbContext context, diff --git a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs index 4fe4063c..90f188a2 100644 --- a/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/UpdateResponseApprovalsMutation.cs @@ -78,7 +78,7 @@ IReadOnlyList path ) => new(code, message, path); [UseFiltering] - [Authorize(Policy = AuthorizationPolicies.WriteScopePolicy)] + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] public async Task UpdateResponseApprovalsAsync( IResolverContext resolverContext, ApplicationDbContext context, From a53f3178bab0dd901ed24f6815e18f601d6eeead Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 20:18:59 +0200 Subject: [PATCH 841/878] Report post-processing errors after a file was uploaded --- .../GetHttpsResourcesController.cs | 52 ++++++++++++++++--- .../Data/AccessPolicies/DataAccessPolicy.cs | 6 +-- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 16a9a0b0..793714ab 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -8,6 +8,7 @@ using Database.Authentication; using Database.Authorization; using Database.Data; +using Database.Enumerations; using Database.Filters; using Database.Services; using Database.Utilities; @@ -23,10 +24,25 @@ public static partial class Log { [LoggerMessage( Level = LogLevel.Error, - Message = "Failed to create response approval for data with ID {DataId}.")] + Message = "Failed to extract and set values from file '{FilaPath}' of GET HTTPS resource {GetHttpsResourceId} of data {DataId} of kind {DataKind}." + )] + public static partial void FailedToExtractAndSetValuesFromFile( + this ILogger logger, + string filePath, + Guid getHttpsResourceId, + Guid dataId, + DataKind dataKind, + Exception exception + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to create response approval for data with ID {DataId} of kind {DataKind}." + )] public static partial void FailedToCreateResponseApproval( this ILogger logger, Guid dataId, + DataKind dataKind, Exception exception ); } @@ -261,10 +277,21 @@ await fileManager.SaveViaPipeReaderAsync( } if (getHttpsResource.IsRoot()) { - await getHttpsResource.Data.ExtractAndSetValuesFromFile( - getHttpsResource.FilePath, - getHttpsResource.DataFormatId - ); + try + { + await getHttpsResource.Data.ExtractAndSetValuesFromFile( + getHttpsResource.FilePath, + getHttpsResource.DataFormatId + ); + } + catch (Exception exception) + { + logger.FailedToExtractAndSetValuesFromFile(getHttpsResource.FilePath, getHttpsResource.Id, getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); + ModelState.AddModelError( + "file", + $"Failed to extract values for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind} from file: {exception.Message}" + ); + } } await getHttpsResource.RecomputeHashValue(cancellationToken); await context.SaveChangesAsync(cancellationToken); @@ -275,8 +302,19 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( } catch (Exception exception) { - // TODO How can this be reported to the user? - logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, exception); + logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); + ModelState.AddModelError( + "data", + $"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}." + ); + } + if (ModelState.ErrorCount > 0) + { + ModelState.AddModelError( + nameof(id), + "The file was uploaded and if a previous file existed it was replaced. However, some post-processing step failed. See the other error(s) for details." + ); + return BadRequest(ModelState); } return CreatedAtRoute( ConstructGetActionRouteName(getHttpsResource), diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index ff5b1fad..5999a92e 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -58,7 +58,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `{nameof(DataAccessPolicy.Data)}`. It is +specific data set, see the field `{nameof(DataAccessPolicy.Data)}`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -69,7 +69,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -303,4 +303,4 @@ LogicalCombinator combinator { Combinator = combinator; } -} \ No newline at end of file +} From 51fcef58d8b657eb30fcd6864d1f3f9e39ccdfef Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 18 Jun 2026 20:26:23 +0200 Subject: [PATCH 842/878] Correct logging parameter name --- backend/src/Controllers/GetHttpsResourcesController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 793714ab..0bcb3b03 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -24,7 +24,7 @@ public static partial class Log { [LoggerMessage( Level = LogLevel.Error, - Message = "Failed to extract and set values from file '{FilaPath}' of GET HTTPS resource {GetHttpsResourceId} of data {DataId} of kind {DataKind}." + Message = "Failed to extract and set values from file '{FilePath}' of GET HTTPS resource {GetHttpsResourceId} of data {DataId} of kind {DataKind}." )] public static partial void FailedToExtractAndSetValuesFromFile( this ILogger logger, From 03fa1d42381f49aa46a48ab42dcd4e379155ce67 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 19 Jun 2026 14:38:37 +0200 Subject: [PATCH 843/878] Use `Problem` instead of `ModelState` for consistent error reporting --- .../GetHttpsResourcesController.cs | 132 ++++++++++-------- 1 file changed, 75 insertions(+), 57 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 0bcb3b03..23ac7c15 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Mime; @@ -22,6 +23,37 @@ namespace Database.Controllers; public static partial class Log { + [LoggerMessage( + Level = LogLevel.Error, + Message = "There is not file at the file path {FilePath} of the GET HTTPS resource {GetHttpsResourceId}." + )] + public static partial void MissingFile( + this ILogger logger, + Guid getHttpsResourceId, + string filePath + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "The GET HTTPS resource {GetHttpsResourceId} seems not to have a data set, it says to belong to data {DataId} of kind {DataKind}." + )] + public static partial void MissingDataSet( + this ILogger logger, + Guid getHttpsResourceId, + Guid dataId, + DataKind dataKind + ); + + [LoggerMessage( + Level = LogLevel.Error, + Message = "Failed to load the data format {DataFormatId} of the GET HTTPS resource {GetHttpsResourceId}." + )] + public static partial void FailedToLoadDataFormat( + this ILogger logger, + Guid getHttpsResourceId, + Guid dataFormatId + ); + [LoggerMessage( Level = LogLevel.Error, Message = "Failed to extract and set values from file '{FilePath}' of GET HTTPS resource {GetHttpsResourceId} of data {DataId} of kind {DataKind}." @@ -97,7 +129,7 @@ getHttpsResource.FileExtension is null [HttpGet("~/api/resources/{id:guid}.{extension}", Name = GetByIdAndExtensionRouteName)] [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [AllowAnonymous] - [EndpointDescription("Get an HTTP resource")] + [EndpointDescription("Get an HTTP resource in the media type of its data format.")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] @@ -134,6 +166,7 @@ public async Task Get( } if (!getHttpsResource.DoesFileExist()) { + logger.MissingFile(getHttpsResource.Id, getHttpsResource.FilePath); return Problem( title: "Content Not Found", detail: $"The GET HTTPS resource with ID '{id:D}' and the extension '{getHttpsResource.FileExtension}' does not have content.", @@ -143,11 +176,13 @@ public async Task Get( } if (getHttpsResource.Data is null) { - ModelState.AddModelError( - nameof(id), - $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'." + logger.MissingDataSet(getHttpsResource.Id, getHttpsResource.DataId, getHttpsResource.DataKind); + return Problem( + title: "Data Set Not Found", + detail: $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path ); - return BadRequest(ModelState); } if (!await accessPolicyService.Apply( context.Data(getHttpsResource.Data.Kind).AsNoTracking() @@ -168,11 +203,13 @@ public async Task Get( var dataFormat = await dataFormatByIdDataLoader.LoadAsync(getHttpsResource.DataFormatId); if (dataFormat is null) { - ModelState.AddModelError( - nameof(id), - $"Failed to fetch the content type of the GET HTTPS resource with ID '{id:D}'. Please try again in a few minutes." + logger.FailedToLoadDataFormat(getHttpsResource.Id, getHttpsResource.DataFormatId); + return Problem( + title: "Failed To Load Data Format", + detail: $"Failed to fetch the content type of the GET HTTPS resource with ID '{id:D}'. Please try again in a few minutes.", + statusCode: StatusCodes.Status400BadRequest, + instance: HttpContext.Request.Path ); - return BadRequest(ModelState); } return PhysicalFile( physicalPath: getHttpsResource.AbsoluteFilePath, @@ -198,12 +235,11 @@ public static object CreateUploadActionRouteValues(GetHttpsResource getHttpsReso }; [HttpPost("~/api/resources/{id:guid}", Name = UploadRouteName)] - [EndpointDescription("Upload file for a GET HTTP resource")] + [EndpointDescription("Upload file for a GET HTTP resource as 'multipart/form-data' or 'text/plain'. The file must conform to the resource's data format.")] [DisableFormValueModelBinding] [DisableRequestSizeLimit] [Authorize(AuthenticationSchemes = AuthenticationConstants.CookieAndBearerTokenAuthenticationScheme)] [Consumes(MediaTypeNames.Multipart.FormData, MediaTypeNames.Text.Plain)] - // [AcceptsMultipartFormFile] [Produces(MediaTypeNames.Application.Json)] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] @@ -227,7 +263,7 @@ CancellationToken cancellationToken if (getHttpsResource is null) { return Problem( - title: "Not Found", + title: "Resource Not Found", detail: $"There is no GET HTTPS resource with ID '{id:D}'.", statusCode: StatusCodes.Status404NotFound, instance: HttpContext.Request.Path @@ -235,28 +271,31 @@ CancellationToken cancellationToken } if (getHttpsResource.Data is null) { - ModelState.AddModelError( - nameof(id), - $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'." + logger.MissingDataSet(getHttpsResource.Id, getHttpsResource.DataId, getHttpsResource.DataKind); + return Problem( + title: "Data Set Not Found", + detail: $"There is no data set associated with the GET HTTPS resource with ID '{id:D}'.", + statusCode: StatusCodes.Status404NotFound, + instance: HttpContext.Request.Path ); - return BadRequest(ModelState); } Directory.CreateDirectory(GetHttpsResource.FilesDirectoryPath); var contentType = MultipartRequestHelper.IsMultipartContentType(Request.ContentType) ? MyContentType.MULTIPART : Request.HasFormContentType ? MyContentType.PIPE : MyContentType.OTHER; - if (contentType is MyContentType.OTHER) - { - return BadRequest("The request does neither contain valid multipart form data nor a valid form."); - } switch (contentType) { case MyContentType.MULTIPART: var boundary = MultipartRequestHelper.GetBoundary(Request.ContentType); if (string.IsNullOrWhiteSpace(boundary)) { - return BadRequest("Missing boundary in multipart form data."); + return Problem( + title: "Missing Boundary", + detail: "Missing boundary in multipart form data.", + statusCode: StatusCodes.Status400BadRequest, + instance: HttpContext.Request.Path + ); } await fileManager.SaveViaMultipartReaderAsync( getHttpsResource.FilePath, @@ -273,8 +312,14 @@ await fileManager.SaveViaPipeReaderAsync( ); break; default: - return BadRequest($"Unsupported content type '{contentType}'"); + return Problem( + title: "Unsupported Content Type", + detail: "The request does neither contain valid multipart form data nor a valid form.", + statusCode: StatusCodes.Status400BadRequest, + instance: HttpContext.Request.Path + ); } + List errors = []; if (getHttpsResource.IsRoot()) { try @@ -287,10 +332,7 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( catch (Exception exception) { logger.FailedToExtractAndSetValuesFromFile(getHttpsResource.FilePath, getHttpsResource.Id, getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); - ModelState.AddModelError( - "file", - $"Failed to extract values for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind} from file: {exception.Message}" - ); + errors.Append($"Failed to extract values for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind} from file: '{exception.Message}'"); } } await getHttpsResource.RecomputeHashValue(cancellationToken); @@ -303,18 +345,16 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( catch (Exception exception) { logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); - ModelState.AddModelError( - "data", - $"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}." - ); + errors.Append($"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}: '{exception.Message}'"); } - if (ModelState.ErrorCount > 0) + if (errors.Count > 0) { - ModelState.AddModelError( - nameof(id), - "The file was uploaded and if a previous file existed it was replaced. However, some post-processing step failed. See the other error(s) for details." + return Problem( + title: "Post-Processing Failed", + detail: $"The file was uploaded and if a previous file existed it was replaced. However, the following post-processing step(s) failed: {string.Join("; ", errors)}", + statusCode: StatusCodes.Status400BadRequest, + instance: HttpContext.Request.Path ); - return BadRequest(ModelState); } return CreatedAtRoute( ConstructGetActionRouteName(getHttpsResource), @@ -322,26 +362,4 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( null ); } - - // solely for the OpenAPI documentation - // private sealed record FormFile(IFormFile File); - - // solely for the OpenAPI documentation - // [AttributeUsage(AttributeTargets.Method)] - // internal sealed class AcceptsMultipartFormFileAttribute : Attribute, IEndpointParameterMetadataProvider - // { - // public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) - // { - // } - - // public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder) - // { - // builder.Metadata.Add( - // new AcceptsMetadata( - // [MediaTypeNames.Multipart.FormData], - // typeof(FormFile) - // ) - // ); - // } - // } } \ No newline at end of file From dfdeca89a7454b603c3a12bc85ee6b202fb6c6fb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 19 Jun 2026 14:39:10 +0200 Subject: [PATCH 844/878] Add long description to GraphQL schema of the locale scalar --- backend/src/GraphQl/Scalars/LocaleType.cs | 52 +++++++++++------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/backend/src/GraphQl/Scalars/LocaleType.cs b/backend/src/GraphQl/Scalars/LocaleType.cs index 1b6f9c86..2e873745 100644 --- a/backend/src/GraphQl/Scalars/LocaleType.cs +++ b/backend/src/GraphQl/Scalars/LocaleType.cs @@ -4,35 +4,18 @@ // TODO Maybe use an enumeration as runtime type instead of string (and fallback to english when the given one does not exist).namespace Database.GraphQl namespace Database.GraphQl.Scalars; -/// -/// [BCP 47](https://tools.ietf.org/html/bcp47) -/// compliant -/// [Language Tag](https://tools.ietf.org/html/bcp47#section-2) -/// string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language -/// part is essentially an -/// [ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), -/// the script an -/// [ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), -/// and the region an -/// [ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). -/// Note that the -/// [Internet Assigned Numbers Authority (IANA)](https://www.iana.org) -/// maintains the -/// [Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). -/// -/// -/// Initializes a new instance of the class. -/// public sealed class LocaleType( string name, string? description = null, - BindingBehavior bind = BindingBehavior.Explicit) - : RegexType( - name, - ValidationPattern, - description, - RegexOptions.Compiled | RegexOptions.IgnoreCase, - bind) + BindingBehavior bind = BindingBehavior.Explicit +) +: RegexType( + name, + ValidationPattern, + description, + RegexOptions.Compiled | RegexOptions.IgnoreCase, + bind +) { private const string ValidationPattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; @@ -43,7 +26,22 @@ public sealed class LocaleType( public LocaleType() : this( nameof(LocaleType)[..^"Type".Length], - "BCP 47 compliant Language Tag string" + """ + [BCP 47](https://tools.ietf.org/html/bcp47) + compliant + [Language Tag](https://tools.ietf.org/html/bcp47#section-2) + string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language + part is essentially an + [ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), + the script an + [ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), + and the region an + [ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). + Note that the + [Internet Assigned Numbers Authority (IANA)](https://www.iana.org) + maintains the + [Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). + """ ) { } From 5f90db8534c997001180cff199bc698d3575b1e8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 19 Jun 2026 16:12:52 +0200 Subject: [PATCH 845/878] Define `TARGET` as empty if it is non-existent to not get a warning in development --- docker.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/docker.mk b/docker.mk index ecb96b82..acfe2f5a 100755 --- a/docker.mk +++ b/docker.mk @@ -185,6 +185,7 @@ list-services : ## List all services specified in the docker-compose file (used # file system under /app/data), it is not available within the management # container. So, we pass the required variables explicitly. manage : COMMAND = bash +manage : TARGET ?= manage : TARGET := $(or ${TARGET},$(shell git symbolic-ref --quiet --short HEAD || git rev-parse --verify HEAD)) manage : GNUPG_SECRET_SIGNING_KEY_FINGERPRINT := $(or ${GNUPG_SECRET_SIGNING_KEY_FINGERPRINT},unknown) manage : ## Run a one-time command in a fresh management service or enter a shell within, for example, `make manage COMMAND='./gpg.mk key NAME="Anna Smith" COMMENT=first EMAIL=anna.smith@fraunhofer.de'` to create a GnuPG key or simply `make manage` to enter bash inside the container From f2c4962cbb4eb6e05da8b26bcd3beae4d774b3f9 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 19 Jun 2026 18:32:34 +0200 Subject: [PATCH 846/878] Make pagination parameters and fields Relay compliant (meaning use `Int` for `first` and `last` and `String` for `after` and `before` parameters and use `Int` for `totalCount` and `String` for `*Cursor` fields), remove unused input `DataPropositionInput` and unused types `DataConnection` and `DataEdge`, and remove prefixes `NullableOf` and `FilterInputTypeOf` --- .../QueryCurrentUserOrInstitution.cs | 10 +- .../src/Configuration/GraphQlConfiguration.cs | 88 +- .../GetHttpsResourcesController.cs | 4 +- backend/src/Data/ApplicationDbContext.cs | 37 +- backend/src/Data/DataApproval.cs | 10 +- backend/src/Data/IApproval.cs | 6 +- backend/src/Data/OpticalData.cs | 6 +- backend/src/Data/Publication.cs | 16 +- backend/src/Data/ResponseApproval.cs | 8 +- .../CalorimetricDataFilterType.cs | 7 +- backend/src/GraphQl/Connection.cs | 16 +- backend/src/GraphQl/DataX/DataConnection.cs | 17 - .../src/GraphQl/DataX/DataConnectionBase.cs | 19 - backend/src/GraphQl/DataX/DataEdge.cs | 14 - backend/src/GraphQl/DataX/DataEdgeBase.cs | 6 - backend/src/GraphQl/DataX/DataFilterType.cs | 7 +- .../src/GraphQl/DataX/DataFilterTypeBase.cs | 5 +- .../DataX/IGetHttpsResourceTreeVertex.cs | 2 +- .../GeometricDataX/GeometricDataFilterType.cs | 7 +- .../HygrothermalDataFilterType.cs | 7 +- .../LifeCycleDataX/LifeCycleDataFilterType.cs | 7 +- .../OpticalDataX/CreateOpticalDataMutation.cs | 4 +- .../OpticalDataX/OpticalDataFilterType.cs | 7 +- .../PhotovoltaicDataFilterType.cs | 7 +- .../GraphQl/Publications/PublicationInput.cs | 8 +- .../src/GraphQl/References/ReferenceInput.cs | 2 + .../ResponseApprovalFilterType.cs | 7 +- backend/src/GraphQl/Scalars/ArXivType.cs | 31 + backend/src/GraphQl/Scalars/DoiType.cs | 40 + .../src/GraphQl/Scalars/GraphQlQueryType.cs | 30 + backend/src/GraphQl/Scalars/LocaleType.cs | 3 - ...edSideOfOpticalDataNonNullable.Designer.cs | 3556 +++++++++++++++++ ..._MakeCoatedSideOfOpticalDataNonNullable.cs | 39 + .../ApplicationDbContextModelSnapshot.cs | 28 +- backend/src/Migrations/migrate.sql | 27 +- ...1010164110_UpdateCoatedSideEnumeration.sql | 2 +- ...MakeCoatedSideOfOpticalDataNonNullable.sql | 20 + ...130041_AddApproverIdToResponseApproval.sql | 2 +- ...ccessPolicyAndRootResourceInPostgreSql.sql | 10 + .../GraphQlSchemaTests.IsUnchanged.snap | 385 +- frontend/codegen.ts | 3 + frontend/type-defs.graphqls | 385 +- 42 files changed, 4374 insertions(+), 521 deletions(-) delete mode 100644 backend/src/GraphQl/DataX/DataConnection.cs delete mode 100644 backend/src/GraphQl/DataX/DataConnectionBase.cs delete mode 100644 backend/src/GraphQl/DataX/DataEdge.cs delete mode 100644 backend/src/GraphQl/DataX/DataEdgeBase.cs create mode 100644 backend/src/GraphQl/Scalars/ArXivType.cs create mode 100644 backend/src/GraphQl/Scalars/DoiType.cs create mode 100644 backend/src/GraphQl/Scalars/GraphQlQueryType.cs create mode 100644 backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.Designer.cs create mode 100644 backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.cs create mode 100644 backend/src/Migrations/migrate_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260620114038_MakeCoatedSideOfOpticalDataNonNullable.sql create mode 100644 backend/src/Migrations/rollback_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql diff --git a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs index a011868e..119ea64e 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs @@ -52,7 +52,7 @@ public bool IsAtLeastAssistantManagerOfDatabaseOperator() public sealed record UserRepresentedInstitutionConnection( IReadOnlyList Edges, - uint TotalCount + int TotalCount ); public enum InstitutionRepresentativeRole @@ -74,7 +74,7 @@ InstitutionManagedInstitutionConnection ManagedInstitutions public sealed record InstitutionManagedInstitutionConnection( IReadOnlyList Edges, - uint TotalCount + int TotalCount ); public sealed record InstitutionManagedInstitutionEdge( @@ -101,11 +101,11 @@ public bool IsDatabaseOperator() }; public sealed record DatabaseOperatingDatabaseConnection( - uint TotalCount + int TotalCount ); public sealed record DatabaseOperatingManagedInstitutionConnection( - uint TotalCount + int TotalCount ); public sealed record CurrentUserOrInstitution( @@ -145,4 +145,4 @@ await GraphQlQueryHelpers.Construct(QueryFileName), } return response.Data; } -} +} \ No newline at end of file diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 3bba1a3b..3aaa0aa9 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -1,8 +1,9 @@ using System; +using System.Text.RegularExpressions; using Database.Authentication; using Database.Data; +using Database.Enumerations; using Database.GraphQl; -using Database.GraphQl.DataX; using Database.GraphQl.Filters; using Database.GraphQl.Scalars; using HotChocolate.AspNetCore; @@ -163,10 +164,11 @@ await httpContext.RequestServices .AddType(new UuidType("Uuid", defaultFormat: 'D')) // https://chillicream.com/docs/hotchocolate/defining-a-schema/scalars#uuid-type .AddType(new MyUriType()) .AddType(new AnyType("Any")) + .AddType() + .AddType() + .AddType() .AddType() .BindRuntimeType() - // Object Types - .AddType() // Query, Mutation, Subscription, Object, and Input Types .AddQueryType(_ => _.Name(nameof(Query))) .AddMutationType(_ => _.Name(nameof(Mutation))) @@ -232,17 +234,30 @@ public override void OnAfterCompleteType(ITypeCompletionContext completionContex } } -public sealed class CustomNamingConventions +public sealed partial class CustomNamingConventions : DefaultNamingConventions { + public static string CorrectPlural(string name) => name + .Replace("Policys", "Policies") + .Replace($"{nameof(FileMetaInformation)}s", "FilesMetaInformation"); + + // `(?!...)` is a negative lookahead. It is a zero-width assertion, meaning + // that it does not move the regex engine's cursor (nor consume text). + [GeneratedRegex(@$"^({nameof(FilterInputType)}Of|{nameof(Nullable)}Of(?!{nameof(DataKind)}))")] + private static partial Regex PrefixesToRemoveRegex(); + + public override string GetTypeName(Type type) => + PrefixesToRemoveRegex().Replace( + base.GetTypeName(type), + string.Empty + ); } // See https://chillicream.com/docs/hotchocolate/fetching-data/filtering/#filter-conventions -public partial class CustomFilterConvention : FilterConvention +public sealed partial class CustomFilterConvention : FilterConvention { - - private const string InputPostFix = "FilterInput"; - private const string InputTypePostFix = "FilterInputType"; + [GeneratedRegex(@$"({nameof(FilterInputType)}|{GraphQlConstants.FilterInputSuffix}|FilterType|FilterInput)$")] + private static partial Regex FilterInputSuffixesToReplaceRegex(); private readonly CustomNamingConventions _namingConventions = new(); @@ -273,61 +288,48 @@ protected override void Configure(IFilterConventionDescriptor descriptor) ); } - // For the base implementation see https://github.com/ChilliCream/hotchocolate/blob/f0dff93a14cb7ddecc7b3a0530a687a5bc4bad71/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 - public override string GetTypeName(Type runtimeType) - { - // return base.GetTypeName(runtimeType); - return GetTypeName(runtimeType, plural: false); - } + // For the base implementation see https://github.com/ChilliCream/graphql-platform/blob/develop/src/HotChocolate/Data/src/Data/Filters/Convention/FilterConvention.cs#L129 + public override string GetTypeName(Type runtimeType) => + CustomNamingConventions.CorrectPlural( + GetTypeName(runtimeType, plural: false) + ); - public string GetTypeName(Type runtimeType, bool plural) + private string GetTypeName(Type runtimeType, bool plural) { ArgumentNullException.ThrowIfNull(runtimeType); var pluralSuffix = plural ? "s" : ""; if (typeof(IEnumOperationFilterInputType).IsAssignableFrom(runtimeType) && runtimeType.GenericTypeArguments.Length == 1 - && runtimeType.GetGenericTypeDefinition() == typeof(EnumOperationFilterInputType<>)) + && runtimeType.GetGenericTypeDefinition() == typeof(EnumOperationFilterInputType<>) + ) { var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); - return $"{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; ; - // return genericName + "OperationFilterInput"; + return $"{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; } if (typeof(IComparableOperationFilterInputType).IsAssignableFrom(runtimeType) && runtimeType.GenericTypeArguments.Length == 1 && runtimeType.GetGenericTypeDefinition() - == typeof(ComparableOperationFilterInputType<>)) + == typeof(ComparableOperationFilterInputType<>) + ) { var genericName = _namingConventions.GetTypeName(runtimeType.GenericTypeArguments[0]); return $"Comparable{genericName}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; - // return $"Comparable{genericName}OperationFilterInput"; } if (typeof(IListFilterInputType).IsAssignableFrom(runtimeType) - && runtimeType.GenericTypeArguments.Length == 1) + && runtimeType.GenericTypeArguments.Length == 1 + ) { var genericType = runtimeType.GenericTypeArguments[0]; - var genericName = typeof(FilterInputType).IsAssignableFrom(genericType) + return typeof(FilterInputType).IsAssignableFrom(genericType) ? GetTypeName(genericType, plural: true) - : "List" + _namingConventions.GetTypeName(genericType); - return $"{genericName}"; - // return "List" + genericName; + : $"{_namingConventions.GetTypeName(genericType)}{pluralSuffix}"; } var name = _namingConventions.GetTypeName(runtimeType); - var isInputObjectType = typeof(FilterInputType).IsAssignableFrom(runtimeType); - var isEndingInput = name.EndsWith(InputPostFix, StringComparison.Ordinal); - var isEndingInputType = name.EndsWith(InputTypePostFix, StringComparison.Ordinal); - if (isInputObjectType && isEndingInputType) - { - return $"{name[..^"FilterInputType".Length]}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; - } - if (isInputObjectType && !isEndingInput && !isEndingInputType) + if (typeof(FilterInputType).IsAssignableFrom(runtimeType) || !name.EndsWith("FilterInput", StringComparison.InvariantCulture)) { - return $"{name}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; + return $"{FilterInputSuffixesToReplaceRegex().Replace(name, string.Empty)}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; } - if (!isInputObjectType && !isEndingInput) - { - return $"{name}{pluralSuffix}{GraphQlConstants.FilterInputSuffix}"; - } - return name; + return $"{name}{pluralSuffix}"; } } @@ -434,10 +436,16 @@ this IFilterConventionDescriptor descriptor } // See https://chillicream.com/docs/hotchocolate/fetching-data/sorting/#sorting-conventions -public partial class CustomSortConvention : SortConvention +public sealed partial class CustomSortConvention : SortConvention { protected override void Configure(ISortConventionDescriptor descriptor) { descriptor.AddDefaults(); } + + // For the base implementation see https://github.com/ChilliCream/graphql-platform/blob/develop/src/HotChocolate/Data/src/Data/Sorting/Convention/SortConvention.cs#L134C5 + public override string GetTypeName(Type runtimeType) => + CustomNamingConventions.CorrectPlural( + base.GetTypeName(runtimeType) + ); } \ No newline at end of file diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 23ac7c15..c07e1240 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -332,7 +332,7 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( catch (Exception exception) { logger.FailedToExtractAndSetValuesFromFile(getHttpsResource.FilePath, getHttpsResource.Id, getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); - errors.Append($"Failed to extract values for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind} from file: '{exception.Message}'"); + errors.Add($"Failed to extract values for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind} from file: '{exception.Message}'"); } } await getHttpsResource.RecomputeHashValue(cancellationToken); @@ -345,7 +345,7 @@ await getHttpsResource.Data.ExtractAndSetValuesFromFile( catch (Exception exception) { logger.FailedToCreateResponseApproval(getHttpsResource.Data.Id, getHttpsResource.Data.Kind, exception); - errors.Append($"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}: '{exception.Message}'"); + errors.Add($"Failed to create response approval for data {getHttpsResource.Data.Id:D} of kind {getHttpsResource.Data.Kind}: '{exception.Message}'"); } if (errors.Count > 0) { diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 151b8c9a..53266231 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -400,7 +400,7 @@ string dataIdColumn { // In the generated migration turn `CREATE TRIGGER` into `CREATE // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before - // `FOR EACH ...` . + // `FOR EACH ...`. builder .AfterInsert(trigger => trigger .SetTriggerName(triggerName) @@ -419,9 +419,30 @@ IF NOT EXISTS ( ) ) ); + // The migration generated for calorimetric data creates the following function and trigger: + // + // CREATE FUNCTION "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ + // BEGIN + // IF NOT EXISTS ( + // SELECT 1 FROM database."get_https_resource" + // WHERE "ParentId" IS NULL + // AND "CalorimetricDataId" = NEW."Id" + // ) + // THEN + // RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.'; + // END IF; + // RETURN NEW; + // END; + // $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql; + // CREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT + // ON "database"."calorimetric_data" + // FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"(); return builder; } + public bool AssertExistenceOfRootResource() => + throw new NotSupportedException("This method can only be called within a database query."); + private EntityTypeBuilder AddTriggerThatCreatesDataAccessPolicyIfNecessary( @@ -457,6 +478,20 @@ string dataIdColumn // ) ) ); + // The migration generated for calorimetric data creates the following function and trigger: + // + // CREATE FUNCTION "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ + // BEGIN + // INSERT INTO database."data_access_policy" + // ("CalorimetricDataId", "Combinator") + // VALUES (NEW."Id", 'all') + // ON CONFLICT ("CalorimetricDataId") WHERE "CalorimetricDataId" IS NOT NULL DO NOTHING; + // RETURN NEW; + // END; + // $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql; + // CREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT + // ON "database"."calorimetric_data" + // FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"(); return builder; } diff --git a/backend/src/Data/DataApproval.cs b/backend/src/Data/DataApproval.cs index 8eebb3cd..e223da1b 100644 --- a/backend/src/Data/DataApproval.cs +++ b/backend/src/Data/DataApproval.cs @@ -1,5 +1,8 @@ using System; using System.Text.Json; +using Database.GraphQl.Scalars; +using HotChocolate; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using NodaTime; @@ -15,8 +18,8 @@ public sealed class DataApproval( string message, Guid approverId, Reference statement - ) - : IApproval +) + : IApproval { // Constructor for EF Core because navigation properties cannot be set using a constructor: https://learn.microsoft.com/en-us/ef/core/modeling/constructors#binding-to-mapped-properties private DataApproval( @@ -45,7 +48,10 @@ Guid approverId public OffsetDateTime Timestamp { get; private set; } = timestamp; public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; + + [GraphQLType>] public string Query { get; private set; } = query; + public JsonElement Variables { get; private set; } = variables; public string Message { get; private set; } = message; public Reference Statement { get; private set; } = statement; diff --git a/backend/src/Data/IApproval.cs b/backend/src/Data/IApproval.cs index e2dd197f..4064e5b0 100644 --- a/backend/src/Data/IApproval.cs +++ b/backend/src/Data/IApproval.cs @@ -2,6 +2,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using Database.GraphQl; +using Database.GraphQl.Scalars; +using HotChocolate; using HotChocolate.Types; using NodaTime; @@ -16,7 +18,9 @@ public interface IApproval OffsetDateTime Timestamp { get; } string Signature { get; } string KeyFingerprint { get; } - string Query { get; } + + [GraphQLType>] string Query { get; } + JsonElement Variables { get; } string Message { get; } public Guid ApproverId { get; } diff --git a/backend/src/Data/OpticalData.cs b/backend/src/Data/OpticalData.cs index 32d7f713..eef0e132 100644 --- a/backend/src/Data/OpticalData.cs +++ b/backend/src/Data/OpticalData.cs @@ -38,7 +38,7 @@ public OpticalData( DateTimeOffset createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, - CoatedSide? coatedSide, + CoatedSide coatedSide, AppliedMethod appliedMethod, double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, @@ -83,7 +83,7 @@ public OpticalData( DateTimeOffset createdAt, OpticalComponentType? type, OpticalComponentSubtype? subtype, - CoatedSide? coatedSide, + CoatedSide coatedSide, double[] nearnormalHemisphericalVisibleTransmittances, double[] nearnormalHemisphericalVisibleReflectances, double[] nearnormalHemisphericalSolarTransmittances, @@ -123,7 +123,7 @@ double[] colorRenderingIndices public OpticalComponentType? Type { get; private set; } public OpticalComponentSubtype? Subtype { get; private set; } - public CoatedSide? CoatedSide { get; private set; } + public CoatedSide CoatedSide { get; private set; } public double[] NearnormalHemisphericalVisibleTransmittances { get; private set; } public double[] NearnormalHemisphericalVisibleReflectances { get; private set; } public double[] NearnormalHemisphericalSolarTransmittances { get; private set; } diff --git a/backend/src/Data/Publication.cs b/backend/src/Data/Publication.cs index 0cc7f2f9..611efcb4 100644 --- a/backend/src/Data/Publication.cs +++ b/backend/src/Data/Publication.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using Database.GraphQl.Scalars; using HotChocolate; using Microsoft.EntityFrameworkCore; @@ -22,25 +23,24 @@ public sealed class Publication( [MinLength(1)] [RegularExpression("^(10[.][0-z/.]*)$")] - [GraphQLDescription( - "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] + [GraphQLDescription("The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication.")] + [GraphQLType] public string? Doi { get; private set; } = doi; [MinLength(1)] [RegularExpression("^(arXiv:)[0-z./]*$")] - [GraphQLDescription( - "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] + [GraphQLDescription("The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication.")] + [GraphQLType] public string? ArXiv { get; private set; } = arXiv; [MinLength(1)] [RegularExpression("^(urn:)[0-z:./-]*$")] - [GraphQLDescription( - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string.")] + [GraphQLDescription("A Uniform Resource Name (URN) can be used to define a publication.")] + [GraphQLType] public string? Urn { get; private set; } = urn; [Url] - [GraphQLDescription( - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] + [GraphQLDescription("If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long.")] public Uri? WebAddress { get; private set; } = webAddress; [MinLength(1)] public string? Title { get; private set; } = title; diff --git a/backend/src/Data/ResponseApproval.cs b/backend/src/Data/ResponseApproval.cs index 2390b901..7e002f80 100644 --- a/backend/src/Data/ResponseApproval.cs +++ b/backend/src/Data/ResponseApproval.cs @@ -1,5 +1,8 @@ using System; using System.Text.Json; +using Database.GraphQl.Scalars; +using HotChocolate; +using HotChocolate.Types; using Microsoft.EntityFrameworkCore; using NodaTime; @@ -14,13 +17,16 @@ public sealed class ResponseApproval( JsonElement variables, string message, Guid approverId - ) +) : IApproval { public OffsetDateTime Timestamp { get; private set; } = timestamp; public string Signature { get; private set; } = signature; public string KeyFingerprint { get; private set; } = keyFingerprint; + + [GraphQLType>] public string Query { get; private set; } = query; + public JsonElement Variables { get; private set; } = variables; public string Message { get; private set; } = message; public Guid ApproverId { get; private set; } = approverId; diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs index e8d6b9e4..98fe7809 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.CalorimetricDataX; @@ -30,7 +31,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Connection.cs b/backend/src/GraphQl/Connection.cs index 7d737d3b..9b73bbaa 100644 --- a/backend/src/GraphQl/Connection.cs +++ b/backend/src/GraphQl/Connection.cs @@ -22,19 +22,17 @@ QueryContext queryContext protected TSubject Subject { get; } = subject; [Cost(0)] - public async Task GetTotalCountAsync( + public async Task GetTotalCountAsync( TAssociationsByOneIdDataLoader dataLoader, CancellationToken cancellationToken ) { - return (uint)( - ( - await dataLoader - .With(queryContext) - .LoadAsync(Subject.Id, cancellationToken) - ) - ?.Length ?? 0 - ); + return ( + await dataLoader + .With(queryContext) + .LoadAsync(Subject.Id, cancellationToken) + ) + ?.Length ?? 0; } [Cost(0)] diff --git a/backend/src/GraphQl/DataX/DataConnection.cs b/backend/src/GraphQl/DataX/DataConnection.cs deleted file mode 100644 index 23315c1f..00000000 --- a/backend/src/GraphQl/DataX/DataConnection.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using HotChocolate.Types.Pagination; - -namespace Database.GraphQl.DataX; - -public sealed class DataConnection( - IReadOnlyList edges, - uint totalCount, - ConnectionPageInfo pageInfo -) -: DataConnectionBase( - edges, - totalCount, - pageInfo -) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataConnectionBase.cs b/backend/src/GraphQl/DataX/DataConnectionBase.cs deleted file mode 100644 index 2d7a0aa0..00000000 --- a/backend/src/GraphQl/DataX/DataConnectionBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using Database.GraphQl.Scalars; -using HotChocolate; -using HotChocolate.Types.Pagination; - -namespace Database.GraphQl.DataX; - -public abstract class DataConnectionBase( - IReadOnlyList edges, - uint totalCount, - ConnectionPageInfo pageInfo -) -{ - public IReadOnlyList Edges { get; } = edges; - - [GraphQLType] public uint TotalCount { get; } = totalCount; - - public ConnectionPageInfo PageInfo { get; } = pageInfo; -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdge.cs b/backend/src/GraphQl/DataX/DataEdge.cs deleted file mode 100644 index 4a34bb9a..00000000 --- a/backend/src/GraphQl/DataX/DataEdge.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Database.Data; - -namespace Database.GraphQl.DataX; - -public sealed record DataEdge( - string Cursor, - IData Node - ) - : DataEdgeBase( - Cursor, - Node - ) -{ -} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataEdgeBase.cs b/backend/src/GraphQl/DataX/DataEdgeBase.cs deleted file mode 100644 index eb214b4c..00000000 --- a/backend/src/GraphQl/DataX/DataEdgeBase.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Database.GraphQl.DataX; - -public abstract record DataEdgeBase( - string Cursor, - TData Node -); \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterType.cs b/backend/src/GraphQl/DataX/DataFilterType.cs index e0edc296..7cca1a4a 100644 --- a/backend/src/GraphQl/DataX/DataFilterType.cs +++ b/backend/src/GraphQl/DataX/DataFilterType.cs @@ -1,4 +1,5 @@ using Database.Data; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.DataX; @@ -27,7 +28,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index c367bd91..fe8f38b9 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.Entities; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.DataX; @@ -21,7 +22,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } diff --git a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs index e1f177c6..448e1df3 100644 --- a/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs +++ b/backend/src/GraphQl/DataX/IGetHttpsResourceTreeVertex.cs @@ -7,7 +7,7 @@ namespace Database.GraphQl.DataX; [InterfaceType("GetHttpsResourceTreeVertex")] public interface IGetHttpsResourceTreeVertex { - [GraphQLType] string VertexId { get; } + [GraphQLType>] string VertexId { get; } GetHttpsResource Value { get; } } \ No newline at end of file diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs index 85e345d2..f6c75a11 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.GeometricDataX; @@ -29,7 +30,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs index 6c383166..e8605312 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.HygrothermalDataX; @@ -28,7 +29,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs index cf3a9940..4a470170 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.LifeCycleDataX; @@ -28,7 +29,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs index 5f753448..196764df 100644 --- a/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs +++ b/backend/src/GraphQl/OpticalDataX/CreateOpticalDataMutation.cs @@ -58,7 +58,7 @@ public OpticalData ToDomainModel( CreatedAt, Type, Subtype, - CoatedSide, + CoatedSide ?? Enumerations.CoatedSide.UNKNOWN, AppliedMethod.ToDomainModel(), NearnormalHemisphericalVisibleTransmittances, NearnormalHemisphericalVisibleReflectances, @@ -192,4 +192,4 @@ CancellationToken cancellationToken return NewPayload(opticalData, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs index fdd7b111..e114ffa9 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.OpticalDataX; @@ -38,7 +39,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs index efc18378..bf535430 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.DataX; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.PhotovoltaicDataX; @@ -28,7 +29,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Publications/PublicationInput.cs b/backend/src/GraphQl/Publications/PublicationInput.cs index 5303b1bc..dce40aae 100644 --- a/backend/src/GraphQl/Publications/PublicationInput.cs +++ b/backend/src/GraphQl/Publications/PublicationInput.cs @@ -1,5 +1,7 @@ using System; using Database.Data; +using Database.GraphQl.Scalars; +using HotChocolate; namespace Database.GraphQl.Publications; @@ -8,9 +10,9 @@ public sealed record PublicationInput( string? Abstract, string? Section, string[]? Authors, - string? Doi, - string? ArXiv, - string? Urn, + [property: GraphQLType] string? Doi, + [property: GraphQLType] string? ArXiv, + [property: GraphQLType] string? Urn, Uri? WebAddress ) { diff --git a/backend/src/GraphQl/References/ReferenceInput.cs b/backend/src/GraphQl/References/ReferenceInput.cs index 40583fea..150c4544 100644 --- a/backend/src/GraphQl/References/ReferenceInput.cs +++ b/backend/src/GraphQl/References/ReferenceInput.cs @@ -2,9 +2,11 @@ using Database.Data; using Database.GraphQl.Publications; using Database.GraphQl.Standards; +using HotChocolate.Types; namespace Database.GraphQl.References; +[OneOf] public sealed record ReferenceInput( StandardInput? Standard, PublicationInput? Publication diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index e5a75ca1..b0d16e2a 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -1,5 +1,6 @@ using Database.Data; using Database.GraphQl.Entities; +using Database.GraphQl.GetHttpsResources; using HotChocolate.Data.Filters; namespace Database.GraphQl.ResponseApprovals; @@ -21,7 +22,9 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.CreatorId); descriptor.Field(_ => _.AppliedMethod); descriptor.Field(_ => _.Approvals); - descriptor.Field(_ => _.Resources); + descriptor + .Field(_ => _.Resources) + .Type>(); descriptor.Field(_ => _.Warnings); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/Scalars/ArXivType.cs b/backend/src/GraphQl/Scalars/ArXivType.cs new file mode 100644 index 00000000..50e3f37e --- /dev/null +++ b/backend/src/GraphQl/Scalars/ArXivType.cs @@ -0,0 +1,31 @@ +using System.Text.RegularExpressions; +using HotChocolate.Types; + +namespace Database.GraphQl.Scalars; + +public sealed class ArXivType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit +) +: RegexType( + name, + ValidationPattern, + description, + RegexOptions.Compiled | RegexOptions.IgnoreCase, + bind +) +{ + private const string ValidationPattern = "^(arXiv:)[0-z./]+$"; + + public ArXivType() + : this( + nameof(ArXivType)[..^"Type".Length], + """ + arXiv identifier, that is, a string of the format + [Understanding the arXiv identifier](https://info.arxiv.org/help/arxiv_identifier.html). + """ + ) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Scalars/DoiType.cs b/backend/src/GraphQl/Scalars/DoiType.cs new file mode 100644 index 00000000..842cd5fd --- /dev/null +++ b/backend/src/GraphQl/Scalars/DoiType.cs @@ -0,0 +1,40 @@ +using System.Text.RegularExpressions; +using HotChocolate.Types; + +namespace Database.GraphQl.Scalars; + +public sealed class DoiType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit +) +: RegexType( + name, + ValidationPattern, + description, + RegexOptions.Compiled | RegexOptions.IgnoreCase, + bind +) +{ + private const string ValidationPattern = "^(10[.][0-z/.]*)$"; + + public DoiType() + : this( + nameof(DoiType)[..^"Type".Length], + """ + Digital Object Identifier (DOI) name, that is, a string compliant with [ISO + 26324:2025](https://www.iso.org/standard/88862.html). + + See also + [What is a DOI?](https://www.doi.org/the-identifier/what-is-a-doi/). In short, + a DOI name is a digital identifier of an object, any object — physical, + digital, or abstract. DOIs solve a common problem: keeping track of things. + Things can be matter, material, content, or activities. Designed to be used by + humans as well as machines, DOIs identify objects persistently. They allow + things to be uniquely identified and accessed reliably. You know what you have, + where it is, and others can track it too. + """ + ) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Scalars/GraphQlQueryType.cs b/backend/src/GraphQl/Scalars/GraphQlQueryType.cs new file mode 100644 index 00000000..210f909e --- /dev/null +++ b/backend/src/GraphQl/Scalars/GraphQlQueryType.cs @@ -0,0 +1,30 @@ +using HotChocolate.Types; + +namespace Database.GraphQl.Scalars; + +public sealed class GraphQlQueryType( + string name, + string? description = null, + BindingBehavior bind = BindingBehavior.Explicit +) +: StringType( + name, + description, + bind +) +{ + public GraphQlQueryType() + : this( + nameof(GraphQlQueryType)[..^"Type".Length], + """ + [September 2025 GraphQL](https://spec.graphql.org/September2025/) + compliant + [query](https://spec.graphql.org/September2025/#sec-Language.Operations) + string without + [variables](http://spec.graphql.org/September2025/#sec-Language.Variables) + and without unnecessary white-space for the present schema. + """ + ) + { + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Scalars/LocaleType.cs b/backend/src/GraphQl/Scalars/LocaleType.cs index 2e873745..aff2a176 100644 --- a/backend/src/GraphQl/Scalars/LocaleType.cs +++ b/backend/src/GraphQl/Scalars/LocaleType.cs @@ -20,9 +20,6 @@ public sealed class LocaleType( private const string ValidationPattern = "^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)?(-[a-zA-Z0-9]+)?$"; - /// - /// Initializes a new instance of the class. - /// public LocaleType() : this( nameof(LocaleType)[..^"Type".Length], diff --git a/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.Designer.cs b/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.Designer.cs new file mode 100644 index 00000000..b457c6fa --- /dev/null +++ b/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.Designer.cs @@ -0,0 +1,3556 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260620114038_MakeCoatedSideOfOpticalDataNonNullable")] + partial class MakeCoatedSideOfOpticalDataNonNullable + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"); + + t.HasTrigger("calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_calorimetric_data_access_policy_if_necessary"); + }); + + b + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") WHERE \"CalorimetricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_geometric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("geometric_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") WHERE \"GeometricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") WHERE \"HygrothermalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("life_cycle_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") WHERE \"LifeCycleDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_optical_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("optical_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") WHERE \"OpticalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("photovoltaic_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") WHERE \"PhotovoltaicDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.cs b/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.cs new file mode 100644 index 00000000..d3a92afd --- /dev/null +++ b/backend/src/Migrations/20260620114038_MakeCoatedSideOfOpticalDataNonNullable.cs @@ -0,0 +1,39 @@ +using Database.Enumerations; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class MakeCoatedSideOfOpticalDataNonNullable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data", + type: "database.coated_side", + nullable: false, + defaultValue: CoatedSide.UNKNOWN, + oldClrType: typeof(CoatedSide), + oldType: "database.coated_side", + oldNullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "CoatedSide", + schema: "database", + table: "optical_data", + type: "database.coated_side", + nullable: true, + oldClrType: typeof(CoatedSide), + oldType: "database.coated_side"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 37c0c396..cc268f2b 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -21,7 +21,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("database") - .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("ProductVersion", "10.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); @@ -332,8 +332,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") - .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") WHERE \"CalorimetricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); }); modelBuilder.Entity("Database.Data.GeometricData", b => @@ -414,8 +414,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") - .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") WHERE \"GeometricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.GetHttpsResource", b => @@ -597,8 +597,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") - .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") WHERE \"HygrothermalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.LifeCycleData", b => @@ -667,8 +667,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") - .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") WHERE \"LifeCycleDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.OpticalData", b => @@ -678,7 +678,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("uuid") .HasDefaultValueSql("uuidv7()"); - b.Property("CoatedSide") + b.Property("CoatedSide") .HasColumnType("database.coated_side"); b.PrimitiveCollection("ColorRenderingIndices") @@ -770,8 +770,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") - .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") WHERE \"OpticalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.PhotovoltaicData", b => @@ -840,8 +840,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO \"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") DO NOTHING;\nRETURN NEW;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") - .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.Id\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") WHERE \"PhotovoltaicDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); }); modelBuilder.Entity("Database.Data.User", b => diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index f5435907..f0385d51 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1345,7 +1345,7 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN ALTER TYPE database.coated_side RENAME TO coated_side_old; - CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); + CREATE TYPE database.coated_side AS ENUM ('both', 'neither', 'non_prime', 'not_applicable', 'prime', 'unknown'); ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; DROP TYPE database.coated_side_old; @@ -3473,3 +3473,28 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260620114038_MakeCoatedSideOfOpticalDataNonNullable') THEN + ALTER TYPE database.coated_side RENAME TO coated_side_old; + CREATE TYPE database.coated_side AS ENUM ('both', 'neither', 'non_prime', 'not_applicable', 'prime', 'unknown'); + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; + DROP TYPE database.coated_side_old; + + UPDATE database.optical_data SET "CoatedSide" = 'unknown'::database.coated_side WHERE "CoatedSide" IS NULL; + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" SET NOT NULL; + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" SET DEFAULT 'unknown'::database.coated_side; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260620114038_MakeCoatedSideOfOpticalDataNonNullable') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260620114038_MakeCoatedSideOfOpticalDataNonNullable', '10.0.9'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql b/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql index 8fdfe408..3ac8fa68 100644 --- a/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql +++ b/backend/src/Migrations/migrate_from_20250907130041_AddApproverIdToResponseApproval_to_20251010164110_UpdateCoatedSideEnumeration.sql @@ -5,7 +5,7 @@ BEGIN IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN ALTER TYPE database.coated_side RENAME TO coated_side_old; - CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); + CREATE TYPE database.coated_side AS ENUM ('both', 'neither', 'non_prime', 'not_applicable', 'prime', 'unknown'); ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; DROP TYPE database.coated_side_old; diff --git a/backend/src/Migrations/migrate_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260620114038_MakeCoatedSideOfOpticalDataNonNullable.sql b/backend/src/Migrations/migrate_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260620114038_MakeCoatedSideOfOpticalDataNonNullable.sql new file mode 100644 index 00000000..931ae01d --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql_to_20260620114038_MakeCoatedSideOfOpticalDataNonNullable.sql @@ -0,0 +1,20 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260620114038_MakeCoatedSideOfOpticalDataNonNullable') THEN + UPDATE database.optical_data SET "CoatedSide" = 'unknown'::database.coated_side WHERE "CoatedSide" IS NULL; + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" SET NOT NULL; + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" SET DEFAULT 'unknown'::database.coated_side; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260620114038_MakeCoatedSideOfOpticalDataNonNullable') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260620114038_MakeCoatedSideOfOpticalDataNonNullable', '10.0.9'); + END IF; +END $EF$; +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql b/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql index 41c8fdce..646866f5 100644 --- a/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql +++ b/backend/src/Migrations/rollback_from_20251010164110_UpdateCoatedSideEnumeration_to_20250907130041_AddApproverIdToResponseApproval.sql @@ -5,7 +5,7 @@ BEGIN IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20251010164110_UpdateCoatedSideEnumeration') THEN ALTER TYPE database.coated_side RENAME TO coated_side_old; - CREATE TYPE database.coated_side AS ENUM ('both', 'neither', 'non_prime', 'not_applicable', 'prime', 'unknown'); + CREATE TYPE database.coated_side AS ENUM ('back', 'both', 'front', 'neither'); ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" TYPE database.coated_side USING "CoatedSide"::text::database.coated_side; DROP TYPE database.coated_side_old; diff --git a/backend/src/Migrations/rollback_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql b/backend/src/Migrations/rollback_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql new file mode 100644 index 00000000..2c7d8c0e --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql.sql @@ -0,0 +1,10 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260620114038_MakeCoatedSideOfOpticalDataNonNullable') THEN + ALTER TABLE database.optical_data ALTER COLUMN "CoatedSide" DROP NOT NULL; + END IF; +END $EF$; + +COMMIT; diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 51eea88f..d6c01981 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -839,7 +839,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -850,7 +850,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -915,7 +915,7 @@ type DataApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! statement: Reference! @cost(weight: "10") timestamp: DateTime! @@ -935,28 +935,17 @@ type Database { } type DatabaseOperatingDatabaseConnection { - totalCount: NonNegativeInt! + totalCount: Int! } type DatabaseOperatingManagedInstitutionConnection { - totalCount: NonNegativeInt! + totalCount: Int! } type DatabaseOperatorEdge { node: Institution! } -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - type DataFormat { extension: String mediaType: String! @@ -1203,7 +1192,7 @@ type InstitutionAccessPolicyEdge { type InstitutionManagedInstitutionConnection { edges: [InstitutionManagedInstitutionEdge!]! - totalCount: NonNegativeInt! + totalCount: Int! } type InstitutionManagedInstitutionEdge { @@ -1334,7 +1323,7 @@ type OpticalData implements Node & Data { approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! cielabColors: [CielabColor!]! - coatedSide: CoatedSide + coatedSide: CoatedSide! colorRenderingIndices: [Float!]! component: Component @cost(weight: "3") componentId: Uuid! @@ -1459,16 +1448,16 @@ type PhotovoltaicDataEdge { type Publication { abstract: String "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: String + arXiv: ArXiv authors: [String!] "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: String + doi: Doi "Referenced section" section: String title: String - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: String - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "A Uniform Resource Name (URN) can be used to define a publication." + urn: Url + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: Url } @@ -1536,7 +1525,7 @@ type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! timestamp: DateTime! variables: Any! @@ -1780,7 +1769,7 @@ type UserInfo { type UserRepresentedInstitutionConnection { edges: [UserRepresentedInstitutionEdge!]! - totalCount: NonNegativeInt! + totalCount: Int! } type UserRepresentedInstitutionEdge { @@ -1798,7 +1787,7 @@ interface Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! timestamp: DateTime! variables: Any! @@ -1831,7 +1820,7 @@ interface Data { interface GetHttpsResourceTreeVertex { value: GetHttpsResource! - vertexId: ID + vertexId: ID! } "The node interface is implemented by entities that have a global unique identifier." @@ -1867,10 +1856,10 @@ input AppliedMethodInput { input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] - arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + arguments: NamedMethodArgumentsPropositionInput methodId: UuidPropositionInput or: [AppliedMethodPropositionInput!] - sources: FilterInputTypeOfNamedMethodSourcesPropositionInput + sources: NamedMethodSourcesPropositionInput } input AppliedMethodSortInput { @@ -1900,7 +1889,7 @@ input BytePropositionInput { input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -1910,7 +1899,7 @@ input CalorimetricDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [CalorimetricDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput uValues: FloatsPropositionInput @@ -1929,6 +1918,13 @@ input CalorimetricDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input CalorimetricObserverPropositionInput { + equalTo: CalorimetricObserver @cost(weight: "10") + in: [CalorimetricObserver] @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") + notIn: [CalorimetricObserver] @cost(weight: "10") +} + input CielabColorInput { aStar: Float! bStar: Float! @@ -1941,12 +1937,19 @@ input CielabColorPropositionInput { and: [CielabColorPropositionInput!] aStar: FloatPropositionInput bStar: FloatPropositionInput - illuminant: NullableOfIlluminantPropositionInput + illuminant: IlluminantPropositionInput lStar: FloatPropositionInput - observer: NullableOfCalorimetricObserverPropositionInput + observer: CalorimetricObserverPropositionInput or: [CielabColorPropositionInput!] } +input CielabColorsPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") +} + input ClearInstitutionAccessPoliciesInput { data: DataReferenceInput } @@ -1964,6 +1967,13 @@ input ClosedIntervalInput { upperBound: Float! } +input CoatedSidePropositionInput { + equalTo: CoatedSide @cost(weight: "10") + in: [CoatedSide!] @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") + notIn: [CoatedSide!] @cost(weight: "10") +} + input ConfigureDataAccessPoliciesInput { combinator: LogicalCombinator! } @@ -2015,7 +2025,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2026,7 +2036,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2047,16 +2057,16 @@ input ConfigureDataAccessPoliciesPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [ConfigureDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } input ConfigureDataAccessPolicyInput { @@ -2171,14 +2181,14 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsPropositionInput { and: [CreateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput description: StringPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [CreateResponseApprovalsPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2246,7 +2256,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2257,7 +2267,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2278,16 +2288,16 @@ input DataAccessPolicyPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [DataAccessPolicyPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } """ @@ -2337,7 +2347,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2348,7 +2358,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2379,6 +2389,13 @@ input DataApprovalPropositionInput { variables: JsonElementPropositionInput } +input DataApprovalsPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") +} + input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") in: [DataKind!] @cost(weight: "10") @@ -2389,7 +2406,7 @@ input DataKindPropositionInput { input DataPropositionInput { and: [DataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2398,7 +2415,7 @@ input DataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [DataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2487,69 +2504,13 @@ input FileMetaInformationPropositionInput { path: StringsPropositionInput } -input FilterInputTypeOfCielabColorsPropositionInput { - all: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: CielabColorPropositionInput @cost(weight: "10") - some: CielabColorPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfDataApprovalsPropositionInput { - all: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: DataApprovalPropositionInput @cost(weight: "10") - some: DataApprovalPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfFileMetaInformationsPropositionInput { +input FilesMetaInformationPropositionInput { all: FileMetaInformationPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") none: FileMetaInformationPropositionInput @cost(weight: "10") some: FileMetaInformationPropositionInput @cost(weight: "10") } -input FilterInputTypeOfGetHttpsResourcesPropositionInput { - all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfInstitutionAccessPolicysPropositionInput { - all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") - some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfNamedMethodArgumentsPropositionInput { - all: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: NamedMethodArgumentPropositionInput @cost(weight: "10") - some: NamedMethodArgumentPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfNamedMethodSourcesPropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput { - all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") - some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfUserAccessPolicysPropositionInput { - all: UserAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: UserAccessPolicyPropositionInput @cost(weight: "10") - some: UserAccessPolicyPropositionInput @cost(weight: "10") -} - input FloatPropositionInput { equalTo: Float @cost(weight: "10") greaterThan: Float @cost(weight: "10") @@ -2576,7 +2537,7 @@ input FloatsPropositionInput { input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2585,7 +2546,7 @@ input GeometricDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [GeometricDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput thicknesses: FloatsPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput @@ -2607,7 +2568,7 @@ input GeometricDataSortInput { input GetHttpsResourcePropositionInput { and: [GetHttpsResourcePropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput @@ -2635,10 +2596,17 @@ input GetHttpsResourceSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input GetHttpsResourcesPropositionInput { + all: GetHttpsResourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: GetHttpsResourcePropositionInput @cost(weight: "10") + some: GetHttpsResourcePropositionInput @cost(weight: "10") +} + input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -2663,7 +2631,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2672,7 +2640,7 @@ input HygrothermalDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [HygrothermalDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2690,6 +2658,20 @@ input HygrothermalDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input IlluminantPropositionInput { + equalTo: Illuminant @cost(weight: "10") + in: [Illuminant] @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") + notIn: [Illuminant] @cost(weight: "10") +} + +input InstitutionAccessPoliciesPropositionInput { + all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") +} + input InstitutionAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] @@ -2737,7 +2719,7 @@ input JsonValueKindPropositionInput { input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2746,7 +2728,7 @@ input LifeCycleDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [LifeCycleDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2846,6 +2828,13 @@ input NamedMethodArgumentSortInput { name: SortEnumType @cost(weight: "10") } +input NamedMethodArgumentsPropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") +} + input NamedMethodSourceInput { name: String! value: CrossDatabaseDataReferenceInput! @@ -2858,6 +2847,13 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NamedMethodSourcesPropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") +} + input NonNegativeIntPropositionInput { equalTo: NonNegativeInt @cost(weight: "10") greaterThan: NonNegativeInt @cost(weight: "10") @@ -2873,20 +2869,6 @@ input NonNegativeIntPropositionInput { notLessThanOrEqualTo: NonNegativeInt @cost(weight: "10") } -input NullableOfCalorimetricObserverPropositionInput { - equalTo: CalorimetricObserver @cost(weight: "10") - in: [CalorimetricObserver] @cost(weight: "10") - notEqualTo: CalorimetricObserver @cost(weight: "10") - notIn: [CalorimetricObserver] @cost(weight: "10") -} - -input NullableOfCoatedSidePropositionInput { - equalTo: CoatedSide @cost(weight: "10") - in: [CoatedSide] @cost(weight: "10") - notEqualTo: CoatedSide @cost(weight: "10") - notIn: [CoatedSide] @cost(weight: "10") -} - input NullableOfDataKindPropositionInput { equalTo: DataKind @cost(weight: "10") in: [DataKind] @cost(weight: "10") @@ -2894,27 +2876,6 @@ input NullableOfDataKindPropositionInput { notIn: [DataKind] @cost(weight: "10") } -input NullableOfIlluminantPropositionInput { - equalTo: Illuminant @cost(weight: "10") - in: [Illuminant] @cost(weight: "10") - notEqualTo: Illuminant @cost(weight: "10") - notIn: [Illuminant] @cost(weight: "10") -} - -input NullableOfOpticalComponentSubtypePropositionInput { - equalTo: OpticalComponentSubtype @cost(weight: "10") - in: [OpticalComponentSubtype] @cost(weight: "10") - notEqualTo: OpticalComponentSubtype @cost(weight: "10") - notIn: [OpticalComponentSubtype] @cost(weight: "10") -} - -input NullableOfOpticalComponentTypePropositionInput { - equalTo: OpticalComponentType @cost(weight: "10") - in: [OpticalComponentType] @cost(weight: "10") - notEqualTo: OpticalComponentType @cost(weight: "10") - notIn: [OpticalComponentType] @cost(weight: "10") -} - input NumerationInput { mainNumber: String! prefix: String @@ -2935,6 +2896,13 @@ input NumerationSortInput { suffix: SortEnumType @cost(weight: "10") } +input OpenIdConnectApplicationAccessPoliciesPropositionInput { + all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") +} + input OpenIdConnectApplicationAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] @@ -2951,12 +2919,26 @@ input OpenIdConnectApplicationAccessPolicySortInput { clientId: SortEnumType @cost(weight: "10") } +input OpticalComponentSubtypePropositionInput { + equalTo: OpticalComponentSubtype @cost(weight: "10") + in: [OpticalComponentSubtype] @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") + notIn: [OpticalComponentSubtype] @cost(weight: "10") +} + +input OpticalComponentTypePropositionInput { + equalTo: OpticalComponentType @cost(weight: "10") + in: [OpticalComponentType] @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") + notIn: [OpticalComponentType] @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput - cielabColors: FilterInputTypeOfCielabColorsPropositionInput - coatedSide: NullableOfCoatedSidePropositionInput + approvals: DataApprovalsPropositionInput + cielabColors: CielabColorsPropositionInput + coatedSide: CoatedSidePropositionInput colorRenderingIndices: FloatsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput @@ -2971,9 +2953,9 @@ input OpticalDataPropositionInput { nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput or: [OpticalDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - subtype: NullableOfOpticalComponentSubtypePropositionInput - type: NullableOfOpticalComponentTypePropositionInput + resources: GetHttpsResourcesPropositionInput + subtype: OpticalComponentSubtypePropositionInput + type: OpticalComponentTypePropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2994,7 +2976,7 @@ input OpticalDataSortInput { input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -3003,7 +2985,7 @@ input PhotovoltaicDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [PhotovoltaicDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -3023,12 +3005,12 @@ input PhotovoltaicDataSortInput { input PublicationInput { abstract: String - arXiv: String + arXiv: ArXiv authors: [String!] - doi: String + doi: Doi section: String title: String - urn: String + urn: Url webAddress: Url } @@ -3044,9 +3026,9 @@ input PublicationPropositionInput { "Referenced section" section: StringPropositionInput title: StringPropositionInput - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + "A Uniform Resource Name (URN) can be used to define a publication." urn: StringPropositionInput - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UrlPropositionInput } @@ -3059,9 +3041,9 @@ input PublicationSortInput { "Referenced section" section: SortEnumType @cost(weight: "10") title: SortEnumType @cost(weight: "10") - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + "A Uniform Resource Name (URN) can be used to define a publication." urn: SortEnumType @cost(weight: "10") - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UriSortInput @cost(weight: "10") } @@ -3073,7 +3055,7 @@ input PublishDataInput { input RecomputeGetHttpsResourceHashValuesPropositionInput { and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput @@ -3089,7 +3071,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { photovoltaicData: PhotovoltaicDataPropositionInput } -input ReferenceInput { +input ReferenceInput @oneOf { publication: PublicationInput standard: StandardInput } @@ -3159,7 +3141,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -3170,7 +3152,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -3191,16 +3173,16 @@ input ResetDataAccessPoliciesPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [ResetDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } input ResetDataAccessPolicyInput { @@ -3338,7 +3320,7 @@ input ToTreeVertexAppliedConversionMethodInput { input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] - arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + arguments: NamedMethodArgumentsPropositionInput methodId: UuidPropositionInput or: [ToTreeVertexAppliedConversionMethodPropositionInput!] sourceName: StringPropositionInput @@ -3394,14 +3376,14 @@ input UpdateDataInput { input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput description: StringPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [UpdateResponseApprovalsPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -3464,6 +3446,13 @@ input UrlPropositionInput { notLessThanOrEqualTo: Url @cost(weight: "10") } +input UserAccessPoliciesPropositionInput { + all: UserAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: UserAccessPolicyPropositionInput @cost(weight: "10") + some: UserAccessPolicyPropositionInput @cost(weight: "10") +} + input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] @@ -3950,6 +3939,12 @@ enum UpdateRootGetHttpsResourceErrorCode { scalar Any @specifiedBy(url: "https://scalars.graphql.org/chillicream/any.html") +""" +arXiv identifier, that is, a string of the format +[Understanding the arXiv identifier](https://info.arxiv.org/help/arxiv_identifier.html). +""" +scalar ArXiv + "The `Byte` scalar type represents a signed 8-bit integer." scalar Byte @specifiedBy(url: "https://scalars.graphql.org/chillicream/byte.html") @@ -3962,10 +3957,35 @@ scalar DateTime scalar Decimal @specifiedBy(url: "https://scalars.graphql.org/chillicream/decimal.html") +""" +Digital Object Identifier (DOI) name, that is, a string compliant with [ISO +26324:2025](https://www.iso.org/standard/88862.html). + +See also +[What is a DOI?](https://www.doi.org/the-identifier/what-is-a-doi/). In short, +a DOI name is a digital identifier of an object, any object — physical, +digital, or abstract. DOIs solve a common problem: keeping track of things. +Things can be matter, material, content, or activities. Designed to be used by +humans as well as machines, DOIs identify objects persistently. They allow +things to be uniquely identified and accessed reliably. You know what you have, +where it is, and others can track it too. +""" +scalar Doi + "The `Duration` scalar type represents a duration of time." scalar Duration @specifiedBy(url: "https://scalars.graphql.org/chillicream/duration.html") +""" +[September 2025 GraphQL](https://spec.graphql.org/September2025/) +compliant +[query](https://spec.graphql.org/September2025/#sec-Language.Operations) +string without +[variables](http://spec.graphql.org/September2025/#sec-Language.Variables) +and without unnecessary white-space for the present schema. +""" +scalar GraphQlQuery + "The `LocalDate` scalar type represents a date without time or time zone information." scalar LocalDate @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-date.html") @@ -3976,7 +3996,22 @@ scalar LocalDateTime url: "https://scalars.graphql.org/chillicream/local-date-time.html" ) -"BCP 47 compliant Language Tag string" +""" +[BCP 47](https://tools.ietf.org/html/bcp47) +compliant +[Language Tag](https://tools.ietf.org/html/bcp47#section-2) +string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language +part is essentially an +[ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), +the script an +[ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), +and the region an +[ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). +Note that the +[Internet Assigned Numbers Authority (IANA)](https://www.iana.org) +maintains the +[Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). +""" scalar Locale "The `LocalTime` scalar type represents a time of day without date or time zone information." diff --git a/frontend/codegen.ts b/frontend/codegen.ts index 1619c237..352775a7 100644 --- a/frontend/codegen.ts +++ b/frontend/codegen.ts @@ -6,12 +6,15 @@ const scalars = { output: "string", }, Any: "object", + ArXiv: "string", Byte: "number", DateTime: "string", DateTimeZone: "string", Decimal: "string", + Doi: "string", Duration: "string", EmailAddress: "string", + GraphQlQuery: "string", LocalDate: "string", LocalDateTime: "string", LocalTime: "string", diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 51eea88f..d6c01981 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -839,7 +839,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -850,7 +850,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -915,7 +915,7 @@ type DataApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! statement: Reference! @cost(weight: "10") timestamp: DateTime! @@ -935,28 +935,17 @@ type Database { } type DatabaseOperatingDatabaseConnection { - totalCount: NonNegativeInt! + totalCount: Int! } type DatabaseOperatingManagedInstitutionConnection { - totalCount: NonNegativeInt! + totalCount: Int! } type DatabaseOperatorEdge { node: Institution! } -type DataConnection { - edges: [DataEdge!]! - pageInfo: PageInfo! - totalCount: NonNegativeInt -} - -type DataEdge { - cursor: String! - node: Data! -} - type DataFormat { extension: String mediaType: String! @@ -1203,7 +1192,7 @@ type InstitutionAccessPolicyEdge { type InstitutionManagedInstitutionConnection { edges: [InstitutionManagedInstitutionEdge!]! - totalCount: NonNegativeInt! + totalCount: Int! } type InstitutionManagedInstitutionEdge { @@ -1334,7 +1323,7 @@ type OpticalData implements Node & Data { approval: ResponseApproval! @cost(weight: "3") approvals: [DataApproval!]! cielabColors: [CielabColor!]! - coatedSide: CoatedSide + coatedSide: CoatedSide! colorRenderingIndices: [Float!]! component: Component @cost(weight: "3") componentId: Uuid! @@ -1459,16 +1448,16 @@ type PhotovoltaicDataEdge { type Publication { abstract: String "The website arXiv.org is a free and open-access archive for publications. The arXiv identifier can be used to define a publication." - arXiv: String + arXiv: ArXiv authors: [String!] "The Digital Object Identifier (DOI) is a very important persistent identifier for publications. It MUST be defined here if it is available for a publication." - doi: String + doi: Doi "Referenced section" section: String title: String - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." - urn: String - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "A Uniform Resource Name (URN) can be used to define a publication." + urn: Url + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: Url } @@ -1536,7 +1525,7 @@ type ResponseApproval implements Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! timestamp: DateTime! variables: Any! @@ -1780,7 +1769,7 @@ type UserInfo { type UserRepresentedInstitutionConnection { edges: [UserRepresentedInstitutionEdge!]! - totalCount: NonNegativeInt! + totalCount: Int! } type UserRepresentedInstitutionEdge { @@ -1798,7 +1787,7 @@ interface Approval { approverId: Uuid! keyFingerprint: String! message: String! - query: String! + query: GraphQlQuery! signature: String! timestamp: DateTime! variables: Any! @@ -1831,7 +1820,7 @@ interface Data { interface GetHttpsResourceTreeVertex { value: GetHttpsResource! - vertexId: ID + vertexId: ID! } "The node interface is implemented by entities that have a global unique identifier." @@ -1867,10 +1856,10 @@ input AppliedMethodInput { input AppliedMethodPropositionInput { and: [AppliedMethodPropositionInput!] - arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + arguments: NamedMethodArgumentsPropositionInput methodId: UuidPropositionInput or: [AppliedMethodPropositionInput!] - sources: FilterInputTypeOfNamedMethodSourcesPropositionInput + sources: NamedMethodSourcesPropositionInput } input AppliedMethodSortInput { @@ -1900,7 +1889,7 @@ input BytePropositionInput { input CalorimetricDataPropositionInput { and: [CalorimetricDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -1910,7 +1899,7 @@ input CalorimetricDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [CalorimetricDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput uValues: FloatsPropositionInput @@ -1929,6 +1918,13 @@ input CalorimetricDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input CalorimetricObserverPropositionInput { + equalTo: CalorimetricObserver @cost(weight: "10") + in: [CalorimetricObserver] @cost(weight: "10") + notEqualTo: CalorimetricObserver @cost(weight: "10") + notIn: [CalorimetricObserver] @cost(weight: "10") +} + input CielabColorInput { aStar: Float! bStar: Float! @@ -1941,12 +1937,19 @@ input CielabColorPropositionInput { and: [CielabColorPropositionInput!] aStar: FloatPropositionInput bStar: FloatPropositionInput - illuminant: NullableOfIlluminantPropositionInput + illuminant: IlluminantPropositionInput lStar: FloatPropositionInput - observer: NullableOfCalorimetricObserverPropositionInput + observer: CalorimetricObserverPropositionInput or: [CielabColorPropositionInput!] } +input CielabColorsPropositionInput { + all: CielabColorPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: CielabColorPropositionInput @cost(weight: "10") + some: CielabColorPropositionInput @cost(weight: "10") +} + input ClearInstitutionAccessPoliciesInput { data: DataReferenceInput } @@ -1964,6 +1967,13 @@ input ClosedIntervalInput { upperBound: Float! } +input CoatedSidePropositionInput { + equalTo: CoatedSide @cost(weight: "10") + in: [CoatedSide!] @cost(weight: "10") + notEqualTo: CoatedSide @cost(weight: "10") + notIn: [CoatedSide!] @cost(weight: "10") +} + input ConfigureDataAccessPoliciesInput { combinator: LogicalCombinator! } @@ -2015,7 +2025,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2026,7 +2036,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2047,16 +2057,16 @@ input ConfigureDataAccessPoliciesPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [ConfigureDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } input ConfigureDataAccessPolicyInput { @@ -2171,14 +2181,14 @@ input CreatePhotovoltaicDataInput { input CreateResponseApprovalsPropositionInput { and: [CreateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput description: StringPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [CreateResponseApprovalsPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2246,7 +2256,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2257,7 +2267,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2278,16 +2288,16 @@ input DataAccessPolicyPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [DataAccessPolicyPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } """ @@ -2337,7 +2347,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -2348,7 +2358,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -2379,6 +2389,13 @@ input DataApprovalPropositionInput { variables: JsonElementPropositionInput } +input DataApprovalsPropositionInput { + all: DataApprovalPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: DataApprovalPropositionInput @cost(weight: "10") + some: DataApprovalPropositionInput @cost(weight: "10") +} + input DataKindPropositionInput { equalTo: DataKind @cost(weight: "10") in: [DataKind!] @cost(weight: "10") @@ -2389,7 +2406,7 @@ input DataKindPropositionInput { input DataPropositionInput { and: [DataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2398,7 +2415,7 @@ input DataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [DataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2487,69 +2504,13 @@ input FileMetaInformationPropositionInput { path: StringsPropositionInput } -input FilterInputTypeOfCielabColorsPropositionInput { - all: CielabColorPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: CielabColorPropositionInput @cost(weight: "10") - some: CielabColorPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfDataApprovalsPropositionInput { - all: DataApprovalPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: DataApprovalPropositionInput @cost(weight: "10") - some: DataApprovalPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfFileMetaInformationsPropositionInput { +input FilesMetaInformationPropositionInput { all: FileMetaInformationPropositionInput @cost(weight: "10") any: Boolean @cost(weight: "10") none: FileMetaInformationPropositionInput @cost(weight: "10") some: FileMetaInformationPropositionInput @cost(weight: "10") } -input FilterInputTypeOfGetHttpsResourcesPropositionInput { - all: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") - some: GetHttpsResourceTreeNonRootVertexPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfInstitutionAccessPolicysPropositionInput { - all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") - some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfNamedMethodArgumentsPropositionInput { - all: NamedMethodArgumentPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: NamedMethodArgumentPropositionInput @cost(weight: "10") - some: NamedMethodArgumentPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfNamedMethodSourcesPropositionInput { - all: NamedMethodSourcePropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: NamedMethodSourcePropositionInput @cost(weight: "10") - some: NamedMethodSourcePropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput { - all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") - some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") -} - -input FilterInputTypeOfUserAccessPolicysPropositionInput { - all: UserAccessPolicyPropositionInput @cost(weight: "10") - any: Boolean @cost(weight: "10") - none: UserAccessPolicyPropositionInput @cost(weight: "10") - some: UserAccessPolicyPropositionInput @cost(weight: "10") -} - input FloatPropositionInput { equalTo: Float @cost(weight: "10") greaterThan: Float @cost(weight: "10") @@ -2576,7 +2537,7 @@ input FloatsPropositionInput { input GeometricDataPropositionInput { and: [GeometricDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2585,7 +2546,7 @@ input GeometricDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [GeometricDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput thicknesses: FloatsPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput @@ -2607,7 +2568,7 @@ input GeometricDataSortInput { input GetHttpsResourcePropositionInput { and: [GetHttpsResourcePropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput @@ -2635,10 +2596,17 @@ input GetHttpsResourceSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input GetHttpsResourcesPropositionInput { + all: GetHttpsResourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: GetHttpsResourcePropositionInput @cost(weight: "10") + some: GetHttpsResourcePropositionInput @cost(weight: "10") +} + input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -2663,7 +2631,7 @@ input GetHttpsResourceTreeNonRootVertSortInput { input HygrothermalDataPropositionInput { and: [HygrothermalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2672,7 +2640,7 @@ input HygrothermalDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [HygrothermalDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2690,6 +2658,20 @@ input HygrothermalDataSortInput { updatedAt: SortEnumType @cost(weight: "10") } +input IlluminantPropositionInput { + equalTo: Illuminant @cost(weight: "10") + in: [Illuminant] @cost(weight: "10") + notEqualTo: Illuminant @cost(weight: "10") + notIn: [Illuminant] @cost(weight: "10") +} + +input InstitutionAccessPoliciesPropositionInput { + all: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: InstitutionAccessPolicyPropositionInput @cost(weight: "10") + some: InstitutionAccessPolicyPropositionInput @cost(weight: "10") +} + input InstitutionAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] @@ -2737,7 +2719,7 @@ input JsonValueKindPropositionInput { input LifeCycleDataPropositionInput { and: [LifeCycleDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -2746,7 +2728,7 @@ input LifeCycleDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [LifeCycleDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2846,6 +2828,13 @@ input NamedMethodArgumentSortInput { name: SortEnumType @cost(weight: "10") } +input NamedMethodArgumentsPropositionInput { + all: NamedMethodArgumentPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: NamedMethodArgumentPropositionInput @cost(weight: "10") + some: NamedMethodArgumentPropositionInput @cost(weight: "10") +} + input NamedMethodSourceInput { name: String! value: CrossDatabaseDataReferenceInput! @@ -2858,6 +2847,13 @@ input NamedMethodSourcePropositionInput { value: CrossDatabaseDataReferencePropositionInput } +input NamedMethodSourcesPropositionInput { + all: NamedMethodSourcePropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: NamedMethodSourcePropositionInput @cost(weight: "10") + some: NamedMethodSourcePropositionInput @cost(weight: "10") +} + input NonNegativeIntPropositionInput { equalTo: NonNegativeInt @cost(weight: "10") greaterThan: NonNegativeInt @cost(weight: "10") @@ -2873,20 +2869,6 @@ input NonNegativeIntPropositionInput { notLessThanOrEqualTo: NonNegativeInt @cost(weight: "10") } -input NullableOfCalorimetricObserverPropositionInput { - equalTo: CalorimetricObserver @cost(weight: "10") - in: [CalorimetricObserver] @cost(weight: "10") - notEqualTo: CalorimetricObserver @cost(weight: "10") - notIn: [CalorimetricObserver] @cost(weight: "10") -} - -input NullableOfCoatedSidePropositionInput { - equalTo: CoatedSide @cost(weight: "10") - in: [CoatedSide] @cost(weight: "10") - notEqualTo: CoatedSide @cost(weight: "10") - notIn: [CoatedSide] @cost(weight: "10") -} - input NullableOfDataKindPropositionInput { equalTo: DataKind @cost(weight: "10") in: [DataKind] @cost(weight: "10") @@ -2894,27 +2876,6 @@ input NullableOfDataKindPropositionInput { notIn: [DataKind] @cost(weight: "10") } -input NullableOfIlluminantPropositionInput { - equalTo: Illuminant @cost(weight: "10") - in: [Illuminant] @cost(weight: "10") - notEqualTo: Illuminant @cost(weight: "10") - notIn: [Illuminant] @cost(weight: "10") -} - -input NullableOfOpticalComponentSubtypePropositionInput { - equalTo: OpticalComponentSubtype @cost(weight: "10") - in: [OpticalComponentSubtype] @cost(weight: "10") - notEqualTo: OpticalComponentSubtype @cost(weight: "10") - notIn: [OpticalComponentSubtype] @cost(weight: "10") -} - -input NullableOfOpticalComponentTypePropositionInput { - equalTo: OpticalComponentType @cost(weight: "10") - in: [OpticalComponentType] @cost(weight: "10") - notEqualTo: OpticalComponentType @cost(weight: "10") - notIn: [OpticalComponentType] @cost(weight: "10") -} - input NumerationInput { mainNumber: String! prefix: String @@ -2935,6 +2896,13 @@ input NumerationSortInput { suffix: SortEnumType @cost(weight: "10") } +input OpenIdConnectApplicationAccessPoliciesPropositionInput { + all: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") + some: OpenIdConnectApplicationAccessPolicyPropositionInput @cost(weight: "10") +} + input OpenIdConnectApplicationAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] @@ -2951,12 +2919,26 @@ input OpenIdConnectApplicationAccessPolicySortInput { clientId: SortEnumType @cost(weight: "10") } +input OpticalComponentSubtypePropositionInput { + equalTo: OpticalComponentSubtype @cost(weight: "10") + in: [OpticalComponentSubtype] @cost(weight: "10") + notEqualTo: OpticalComponentSubtype @cost(weight: "10") + notIn: [OpticalComponentSubtype] @cost(weight: "10") +} + +input OpticalComponentTypePropositionInput { + equalTo: OpticalComponentType @cost(weight: "10") + in: [OpticalComponentType] @cost(weight: "10") + notEqualTo: OpticalComponentType @cost(weight: "10") + notIn: [OpticalComponentType] @cost(weight: "10") +} + input OpticalDataPropositionInput { and: [OpticalDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput - cielabColors: FilterInputTypeOfCielabColorsPropositionInput - coatedSide: NullableOfCoatedSidePropositionInput + approvals: DataApprovalsPropositionInput + cielabColors: CielabColorsPropositionInput + coatedSide: CoatedSidePropositionInput colorRenderingIndices: FloatsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput @@ -2971,9 +2953,9 @@ input OpticalDataPropositionInput { nearnormalHemisphericalVisibleReflectances: FloatsPropositionInput nearnormalHemisphericalVisibleTransmittances: FloatsPropositionInput or: [OpticalDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput - subtype: NullableOfOpticalComponentSubtypePropositionInput - type: NullableOfOpticalComponentTypePropositionInput + resources: GetHttpsResourcesPropositionInput + subtype: OpticalComponentSubtypePropositionInput + type: OpticalComponentTypePropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -2994,7 +2976,7 @@ input OpticalDataSortInput { input PhotovoltaicDataPropositionInput { and: [PhotovoltaicDataPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput createdAt: DateTimePropositionInput creatorId: UuidPropositionInput @@ -3003,7 +2985,7 @@ input PhotovoltaicDataPropositionInput { locale: StringPropositionInput name: StringPropositionInput or: [PhotovoltaicDataPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput @@ -3023,12 +3005,12 @@ input PhotovoltaicDataSortInput { input PublicationInput { abstract: String - arXiv: String + arXiv: ArXiv authors: [String!] - doi: String + doi: Doi section: String title: String - urn: String + urn: Url webAddress: Url } @@ -3044,9 +3026,9 @@ input PublicationPropositionInput { "Referenced section" section: StringPropositionInput title: StringPropositionInput - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + "A Uniform Resource Name (URN) can be used to define a publication." urn: StringPropositionInput - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UrlPropositionInput } @@ -3059,9 +3041,9 @@ input PublicationSortInput { "Referenced section" section: SortEnumType @cost(weight: "10") title: SortEnumType @cost(weight: "10") - "A Uniform Resource Name (URN) can be used to define a publication. TODO: Improve the regex pattern to further restrict the string." + "A Uniform Resource Name (URN) can be used to define a publication." urn: SortEnumType @cost(weight: "10") - "If a persistent identifiert like DOI is defined above, this webAdress can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." + "If a persistent identifier like DOI is defined above, this web address can define a convenient web address to access the publication. However, if no persistent identifier exist, this web address is the only identifier of this publication. In this case, it is important to choose a web address with a high probability to persist long." webAddress: UriSortInput @cost(weight: "10") } @@ -3073,7 +3055,7 @@ input PublishDataInput { input RecomputeGetHttpsResourceHashValuesPropositionInput { and: [RecomputeGetHttpsResourceHashValuesPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput - archivedFilesMetaInformation: FilterInputTypeOfFileMetaInformationsPropositionInput + archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput @@ -3089,7 +3071,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { photovoltaicData: PhotovoltaicDataPropositionInput } -input ReferenceInput { +input ReferenceInput @oneOf { publication: PublicationInput standard: StandardInput } @@ -3159,7 +3141,7 @@ access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. A data access policy is either the one-and-only global one or associated with a -specific data entry, see the field `Data`. It is +specific data set, see the field `Data`. It is global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. @@ -3170,7 +3152,7 @@ There are mutations to * clear user, institution, and applicatoin policies of a data access policy; * set and unset a user, institution, or applicatoin policy for a specific user, institution, or application, and a specific data access policy (global or - associated with a specific data entry). + associated with a specific data set). To determine whether access policies allow/restrict access in the way you expected, use their `is*Allowed` fields, passing the applicable user ID, @@ -3191,16 +3173,16 @@ input ResetDataAccessPoliciesPropositionInput { dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput - institutionAccessPolicies: FilterInputTypeOfInstitutionAccessPolicysPropositionInput + institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput - openIdConnectApplicationAccessPolicies: FilterInputTypeOfOpenIdConnectApplicationAccessPolicysPropositionInput + openIdConnectApplicationAccessPolicies: OpenIdConnectApplicationAccessPoliciesPropositionInput opticalData: OpticalDataPropositionInput or: [ResetDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput - userAccessPolicies: FilterInputTypeOfUserAccessPolicysPropositionInput + userAccessPolicies: UserAccessPoliciesPropositionInput } input ResetDataAccessPolicyInput { @@ -3338,7 +3320,7 @@ input ToTreeVertexAppliedConversionMethodInput { input ToTreeVertexAppliedConversionMethodPropositionInput { and: [ToTreeVertexAppliedConversionMethodPropositionInput!] - arguments: FilterInputTypeOfNamedMethodArgumentsPropositionInput + arguments: NamedMethodArgumentsPropositionInput methodId: UuidPropositionInput or: [ToTreeVertexAppliedConversionMethodPropositionInput!] sourceName: StringPropositionInput @@ -3394,14 +3376,14 @@ input UpdateDataInput { input UpdateResponseApprovalsPropositionInput { and: [UpdateResponseApprovalsPropositionInput!] appliedMethod: AppliedMethodPropositionInput - approvals: FilterInputTypeOfDataApprovalsPropositionInput + approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput creatorId: UuidPropositionInput description: StringPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [UpdateResponseApprovalsPropositionInput!] - resources: FilterInputTypeOfGetHttpsResourcesPropositionInput + resources: GetHttpsResourcesPropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -3464,6 +3446,13 @@ input UrlPropositionInput { notLessThanOrEqualTo: Url @cost(weight: "10") } +input UserAccessPoliciesPropositionInput { + all: UserAccessPolicyPropositionInput @cost(weight: "10") + any: Boolean @cost(weight: "10") + none: UserAccessPolicyPropositionInput @cost(weight: "10") + some: UserAccessPolicyPropositionInput @cost(weight: "10") +} + input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] @@ -3950,6 +3939,12 @@ enum UpdateRootGetHttpsResourceErrorCode { scalar Any @specifiedBy(url: "https://scalars.graphql.org/chillicream/any.html") +""" +arXiv identifier, that is, a string of the format +[Understanding the arXiv identifier](https://info.arxiv.org/help/arxiv_identifier.html). +""" +scalar ArXiv + "The `Byte` scalar type represents a signed 8-bit integer." scalar Byte @specifiedBy(url: "https://scalars.graphql.org/chillicream/byte.html") @@ -3962,10 +3957,35 @@ scalar DateTime scalar Decimal @specifiedBy(url: "https://scalars.graphql.org/chillicream/decimal.html") +""" +Digital Object Identifier (DOI) name, that is, a string compliant with [ISO +26324:2025](https://www.iso.org/standard/88862.html). + +See also +[What is a DOI?](https://www.doi.org/the-identifier/what-is-a-doi/). In short, +a DOI name is a digital identifier of an object, any object — physical, +digital, or abstract. DOIs solve a common problem: keeping track of things. +Things can be matter, material, content, or activities. Designed to be used by +humans as well as machines, DOIs identify objects persistently. They allow +things to be uniquely identified and accessed reliably. You know what you have, +where it is, and others can track it too. +""" +scalar Doi + "The `Duration` scalar type represents a duration of time." scalar Duration @specifiedBy(url: "https://scalars.graphql.org/chillicream/duration.html") +""" +[September 2025 GraphQL](https://spec.graphql.org/September2025/) +compliant +[query](https://spec.graphql.org/September2025/#sec-Language.Operations) +string without +[variables](http://spec.graphql.org/September2025/#sec-Language.Variables) +and without unnecessary white-space for the present schema. +""" +scalar GraphQlQuery + "The `LocalDate` scalar type represents a date without time or time zone information." scalar LocalDate @specifiedBy(url: "https://scalars.graphql.org/chillicream/local-date.html") @@ -3976,7 +3996,22 @@ scalar LocalDateTime url: "https://scalars.graphql.org/chillicream/local-date-time.html" ) -"BCP 47 compliant Language Tag string" +""" +[BCP 47](https://tools.ietf.org/html/bcp47) +compliant +[Language Tag](https://tools.ietf.org/html/bcp47#section-2) +string like `"de-AT"`, `"sr-Latn-RS"`, `"en-US"`, or `"en-GB"`, where the language +part is essentially an +[ISO 639 Language Code](https://www.iso.org/iso-639-language-codes.html), +the script an +[ISO 15924:2004 Script Code](https://www.iso.org/standard/29546.html), +and the region an +[ISO 3166-1 Country Code](https://www.iso.org/iso-3166-country-codes.html). +Note that the +[Internet Assigned Numbers Authority (IANA)](https://www.iana.org) +maintains the +[Language Subtag Registry](https://www.iana.org/assignments/lang-subtags-templates/lang-subtags-templates.xhtml). +""" scalar Locale "The `LocalTime` scalar type represents a time of day without date or time zone information." From abd62e43ae28a60b6f2a1c5c3761f249daa407e3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 20 Jun 2026 22:17:47 +0200 Subject: [PATCH 847/878] Make sure that distinct database contexts are used for data queries involving `AccessPolicyService` (because the latter has side-effects. it increments access counters) --- .../GetHttpsResourcesController.cs | 9 +++--- .../CalorimetricDataQueries.cs | 25 ++++++++------- backend/src/GraphQl/DataX/DataQueries.cs | 6 ++-- backend/src/GraphQl/DataX/DataQueriesBase.cs | 32 +++++++++---------- .../GeometricDataX/GeometricDataQueries.cs | 27 ++++++++-------- .../HygrothermalDataQueries.cs | 25 ++++++++------- .../LifeCycleDataX/LifeCycleDataQueries.cs | 25 ++++++++------- .../OpticalDataX/OpticalDataQueries.cs | 25 ++++++++------- .../PhotovoltaicDataQueries.cs | 25 ++++++++------- backend/src/Services/AccessPolicyService.cs | 7 ++-- 10 files changed, 107 insertions(+), 99 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index c07e1240..60395a97 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -136,14 +136,15 @@ getHttpsResource.FileExtension is null [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task Get( [FromRoute] Guid id, - [FromServices] ApplicationDbContext context, + [FromServices] IDbContextFactory databaseContextFactory, [FromServices] AccessPolicyService accessPolicyService, [FromServices] IDataFormatByIdDataLoader dataFormatByIdDataLoader, CancellationToken cancellationToken, [FromRoute] string? extension = null ) { - var getHttpsResource = await context.GetHttpsResourcesWithData.AsQueryable() + using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + var getHttpsResource = await databaseContext.GetHttpsResourcesWithData.AsQueryable() .Where(_ => _.Id == id) .SingleOrDefaultAsync(cancellationToken); if (getHttpsResource is null) @@ -185,7 +186,7 @@ public async Task Get( ); } if (!await accessPolicyService.Apply( - context.Data(getHttpsResource.Data.Kind).AsNoTracking() + databaseContext => databaseContext.Data(getHttpsResource.Data.Kind).AsNoTracking() .Where(_ => _.Id == getHttpsResource.DataId), async policedData => { @@ -194,7 +195,7 @@ public async Task Get( ? ([], false) : ([node], true); }, - context, + databaseContextFactory, cancellationToken )) { diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 759f1f1c..7bf1b2d1 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -10,6 +10,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.CalorimetricDataX; @@ -22,16 +23,16 @@ public sealed class CalorimetricDataQueries [UseSorting] public Task> GetAllCalorimetricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.CalorimetricData, + databaseContext => databaseContext.CalorimetricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -43,7 +44,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingCalorimetricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -51,9 +52,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.CalorimetricData, + databaseContext => databaseContext.CalorimetricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -64,16 +65,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasCalorimetricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.CalorimetricData, + databaseContext => databaseContext.CalorimetricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -83,7 +84,7 @@ CancellationToken cancellationToken public Task GetCalorimetricDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) @@ -91,8 +92,8 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.CalorimetricData, - context, + databaseContext => databaseContext.CalorimetricData, + databaseContextFactory, accessPolicyService, cancellationToken ); diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 9a3e18b7..91633929 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -19,20 +19,20 @@ public sealed class DataQueries Guid id, DataKind dataKind, [GraphQLType] string? locale, - ApplicationDbContext databaseContext, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return accessPolicyService.Apply( - databaseContext.Data(dataKind).AsNoTracking() + databaseContext => databaseContext.Data(dataKind).AsNoTracking() .Where(_ => _.Id == id), async policedData => { var node = await policedData.SingleOrDefaultAsync(cancellationToken); return (node is null ? [] : [node], node); }, - databaseContext, + databaseContextFactory, cancellationToken ); } diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index 3ffd2967..a63958f6 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -19,16 +19,16 @@ public abstract class DataQueriesBase where TData : class, IData { protected Task> GetAllDataAsync( - DbSet data, + Func> getData, [GraphQLType] string? locale, - ApplicationDbContext databaseContext, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return accessPolicyService.Apply( - data.AsNoTracking() + databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), async policedData => @@ -39,15 +39,15 @@ CancellationToken cancellationToken var nodes = connection.Edges.Select(_ => _.Node).ToList().AsReadOnly(); return (nodes, connection); }, - databaseContext, + databaseContextFactory, cancellationToken ); } protected async Task> GetAllPendingDataAsync( - DbSet data, + Func> getData, [GraphQLType] string? locale, - ApplicationDbContext databaseContext, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -63,7 +63,7 @@ CancellationToken cancellationToken .ToConnectionAsync(); } return await accessPolicyService.Apply( - data.AsNoTracking() + databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), async policedData => @@ -74,22 +74,22 @@ CancellationToken cancellationToken var nodes = connection.Edges.Select(_ => _.Node).ToList().AsReadOnly(); return (nodes, connection); }, - databaseContext, + databaseContextFactory, cancellationToken ); } protected Task HasDataAsync( - DbSet data, + Func> getData, [GraphQLType] string? locale, - ApplicationDbContext databaseContext, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return accessPolicyService.Apply( - data.AsNoTracking() + databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), async policedData => @@ -97,7 +97,7 @@ CancellationToken cancellationToken var nodes = (await policedData.ToListAsync(cancellationToken)).AsReadOnly(); return (nodes, nodes.Count > 0); }, - databaseContext, + databaseContextFactory, cancellationToken ); } @@ -105,21 +105,21 @@ CancellationToken cancellationToken protected Task GetDataAsync( Guid id, [GraphQLType] string? locale, - DbSet data, - ApplicationDbContext databaseContext, + Func> getData, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) { return accessPolicyService.Apply( - data.AsNoTracking() + databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.Id == id), async policedData => { var node = await policedData.SingleOrDefaultAsync(cancellationToken); return (node is null ? [] : [node], node); }, - databaseContext, + databaseContextFactory, cancellationToken ); } diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index e13ff683..9b3f9201 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -10,6 +10,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.GeometricDataX; @@ -22,16 +23,16 @@ public sealed class GeometricDataQueries [UseSorting] public Task> GetAllGeometricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.GeometricData, + databaseContext => databaseContext.GeometricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -43,7 +44,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingGeometricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -51,9 +52,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.GeometricData, + databaseContext => databaseContext.GeometricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -64,16 +65,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasGeometricDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.GeometricData, + databaseContext => databaseContext.GeometricData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -83,7 +84,7 @@ CancellationToken cancellationToken public Task GetGeometricDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) @@ -91,10 +92,10 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.GeometricData, - context, + databaseContext => databaseContext.GeometricData, + databaseContextFactory, accessPolicyService, cancellationToken ); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index 2561d7a4..a1cb4744 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -10,6 +10,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.HygrothermalDataX; @@ -22,16 +23,16 @@ public sealed class HygrothermalDataQueries [UseSorting] public Task> GetAllHygrothermalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.HygrothermalData, + databaseContext => databaseContext.HygrothermalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -43,7 +44,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingHygrothermalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -51,9 +52,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.HygrothermalData, + databaseContext => databaseContext.HygrothermalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -64,16 +65,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasHygrothermalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.HygrothermalData, + databaseContext => databaseContext.HygrothermalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -83,7 +84,7 @@ CancellationToken cancellationToken public Task GetHygrothermalDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) @@ -91,8 +92,8 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.HygrothermalData, - context, + databaseContext => databaseContext.HygrothermalData, + databaseContextFactory, accessPolicyService, cancellationToken ); diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index e653df6e..f1d172c8 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -10,6 +10,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.LifeCycleDataX; @@ -22,16 +23,16 @@ public sealed class LifeCycleDataQueries [UseSorting] public Task> GetAllLifeCycleDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.LifeCycleData, + databaseContext => databaseContext.LifeCycleData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -43,7 +44,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingLifeCycleDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -51,9 +52,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.LifeCycleData, + databaseContext => databaseContext.LifeCycleData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -64,16 +65,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasLifeCycleDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.LifeCycleData, + databaseContext => databaseContext.LifeCycleData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -83,7 +84,7 @@ CancellationToken cancellationToken public Task GetLifeCycleDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) @@ -91,8 +92,8 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.LifeCycleData, - context, + databaseContext => databaseContext.LifeCycleData, + databaseContextFactory, accessPolicyService, cancellationToken ); diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 57f2322f..34271bea 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -10,6 +10,7 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.OpticalDataX; @@ -22,16 +23,16 @@ public sealed class OpticalDataQueries [UseSorting] public Task> GetAllOpticalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.OpticalData, + databaseContext => databaseContext.OpticalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -43,7 +44,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingOpticalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -51,9 +52,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.OpticalData, + databaseContext => databaseContext.OpticalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -64,16 +65,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasOpticalDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.OpticalData, + databaseContext => databaseContext.OpticalData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -83,7 +84,7 @@ CancellationToken cancellationToken public Task GetOpticalDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, CancellationToken cancellationToken ) @@ -91,8 +92,8 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.OpticalData, - context, + databaseContext => databaseContext.OpticalData, + databaseContextFactory, accessPolicyService, cancellationToken ); diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index b7c6bf98..13b2b069 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -11,6 +11,7 @@ using HotChocolate.Resolvers; using HotChocolate.Types; using NodaTime; +using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.PhotovoltaicDataX; @@ -23,16 +24,16 @@ public sealed class PhotovoltaicDataQueries [UseSorting] public Task> GetAllPhotovoltaicDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return GetAllDataAsync( - context.PhotovoltaicData, + databaseContext => databaseContext.PhotovoltaicData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -44,7 +45,7 @@ CancellationToken cancellationToken [UseSorting] public Task> GetAllPendingPhotovoltaicDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CommonAuthorization authorization, @@ -52,9 +53,9 @@ CancellationToken cancellationToken ) { return GetAllPendingDataAsync( - context.PhotovoltaicData, + databaseContext => databaseContext.PhotovoltaicData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, authorization, @@ -65,16 +66,16 @@ CancellationToken cancellationToken [UseFiltering] public Task HasPhotovoltaicDataAsync( [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IResolverContext resolverContext, CancellationToken cancellationToken ) { return HasDataAsync( - context.PhotovoltaicData, + databaseContext => databaseContext.PhotovoltaicData, locale, - context, + databaseContextFactory, accessPolicyService, resolverContext, cancellationToken @@ -84,7 +85,7 @@ CancellationToken cancellationToken public Task GetPhotovoltaicDataAsync( Guid id, [GraphQLType] string? locale, - ApplicationDbContext context, + IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, IClock clock, CancellationToken cancellationToken @@ -93,8 +94,8 @@ CancellationToken cancellationToken return GetDataAsync( id, locale, - context.PhotovoltaicData, - context, + databaseContext => databaseContext.PhotovoltaicData, + databaseContextFactory, accessPolicyService, cancellationToken ); diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index 0f94f09c..e8f98e1c 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -30,13 +30,14 @@ ILogger logger ) { public async Task Apply( - IQueryable data, + Func> getData, Func, Task<(IReadOnlyList Data, TResult Result)>> then, - ApplicationDbContext databaseContext, + IDbContextFactory databaseContextFactory, CancellationToken cancellationToken ) where TData : class, IData { + using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectApplicationClientId(); var (currentUser, currentInstitution) = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); Guid[]? institutionIds = null; @@ -49,7 +50,7 @@ CancellationToken cancellationToken institutionIds = [currentInstitution.Uuid]; } logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); - var policedData = PoliceData(data, currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); + var policedData = PoliceData(getData(databaseContext), currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); var (postProcessedData, result) = await then(policedData); await IncrementAccessCounts( postProcessedData, From 2d1a9f24c6749cdd01b4fcba4e772d9521cf30af Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 20 Jun 2026 22:30:12 +0200 Subject: [PATCH 848/878] Add test that checks whether the C# domain model matches the PostgreSQL database schema --- for a list of limitations see https://github.com/JonPSmith/EfCore.SchemaCompare#List-of-limitations --- backend/Makefile | 7 +- backend/src/Program.cs | 6 +- backend/src/Startup.cs | 2 +- backend/test/Database.Tests.csproj | 5 + .../CustomWebApplicationFactory.cs | 33 +++---- .../test/Integration/DatabaseSchemaTests.cs | 92 +++++++++++++++++++ 6 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 backend/test/Integration/DatabaseSchemaTests.cs diff --git a/backend/Makefile b/backend/Makefile index 5bfc71d1..fe60aae8 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -88,7 +88,7 @@ analyze : ## Analyze with Roslynator # https://www.jetbrains.com/help/resharper/ReSharper_Command_Line_Tools.html#install-and-use-resharper-command-line-tools-as-net-core-tools inspect : ## Inspect with ReSharper, for example, `make inspect OUTPUT=./inspection.xml` - dotnet tool run jb inspectcode --build ./*.sln -o ${OUTPUT} + dotnet tool run jb inspectcode --build --output "${OUTPUT}" ./*.sln .PHONY : inspect editorconfig : ## Debug editorconfig @@ -183,6 +183,11 @@ schema : ## Fetch GraphQL schema using `hasura/graphqurl` > ./schema.graphql .PHONY : schema +check-database : ## Check whether there have been any model changes made since the last migration was added and whether the C# domain model matches the PostgreSQL database schema + $(MAKE) test ARGS='--filter "FullyQualifiedName~EnsureMigrationsAreUpToDate"' + $(MAKE) test ARGS='--filter "FullyQualifiedName~EnsureDomainModelMatchesDatabaseSchema"' +.PHONY : check-database + has-pending-model-changes : ## Check whether there have been any model changes made since the last migration was added dotnet ef \ --project ./src/*.csproj \ diff --git a/backend/src/Program.cs b/backend/src/Program.cs index db0c93a7..0b5fef15 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -47,6 +47,7 @@ Exception exception public sealed class Program { public const string TestEnvironment = "test"; + public const string DevelopmentEnvironment = "development"; private const string ProductionEnvironment = "production"; private const string LogsPath = "./logs/serilog.json"; @@ -258,7 +259,7 @@ Uri openTelemetryHost return builder; } - public static void ConfigureAppConfiguration( + private static void ConfigureAppConfiguration( IConfigurationBuilder configuration, IHostEnvironment environment, string[] commandLineArguments @@ -278,8 +279,7 @@ string[] commandLineArguments !environment.IsEnvironment(TestEnvironment) ) .AddEnvironmentVariables() - .AddEnvironmentVariables( - "XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables + .AddEnvironmentVariables("XBASE_") // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1#environment-variables .AddCommandLine(commandLineArguments); } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 7c43e086..82bb3093 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -42,7 +42,7 @@ namespace Database; public sealed class Startup( IWebHostEnvironment environment, IConfiguration configuration - ) +) { private readonly AppSettings _appSettings = configuration.Get(_ => diff --git a/backend/test/Database.Tests.csproj b/backend/test/Database.Tests.csproj index 65ccc2c7..e2e52aa6 100644 --- a/backend/test/Database.Tests.csproj +++ b/backend/test/Database.Tests.csproj @@ -9,10 +9,15 @@ + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/backend/test/Integration/CustomWebApplicationFactory.cs b/backend/test/Integration/CustomWebApplicationFactory.cs index 7a42c80c..dd53fa37 100644 --- a/backend/test/Integration/CustomWebApplicationFactory.cs +++ b/backend/test/Integration/CustomWebApplicationFactory.cs @@ -65,34 +65,12 @@ public AppSettings AppSettings } } - protected override IHost CreateHost(IHostBuilder builder) - { - builder.UseEnvironment(Database.Program.TestEnvironment); - builder.UseSerilog((context, services, configuration) => - { - configuration - .ReadFrom.Configuration(context.Configuration) // appsettings.test.json - .WriteTo.NUnitOutput(formatProvider: CultureInfo.InvariantCulture); - }); - return base.CreateHost(builder); - } - protected override void ConfigureWebHost(IWebHostBuilder builder) { var databaseName = Guid.NewGuid().ToString().Replace("-", ""); // var schemaName = $"database_{Guid.NewGuid().ToString().Replace("-", "")}"; // builder.ConfigureAppConfiguration(_ => _.AddInMemoryCollection([new KeyValuePair("Database__Name", databaseName)])); // "Database__SchemaName" builder.UseEnvironment(Database.Program.TestEnvironment); - builder.ConfigureAppConfiguration((webHostBuilderContext, configurationBuilder) => - { - configurationBuilder.Sources.Clear(); - Database.Program.ConfigureAppConfiguration( - configurationBuilder, - webHostBuilderContext.HostingEnvironment, - [] - ); - } - ); builder.ConfigureServices(serviceCollection => { using var scope = serviceCollection.BuildServiceProvider().CreateScope(); @@ -128,6 +106,17 @@ protected override void ConfigureWebHost(IWebHostBuilder builder) ); } + protected override IHost CreateHost(IHostBuilder builder) + { + builder.UseSerilog((context, services, configuration) => + { + configuration + .ReadFrom.Configuration(context.Configuration) // appsettings.test.json + .WriteTo.NUnitOutput(formatProvider: CultureInfo.InvariantCulture); + }); + return base.CreateHost(builder); + } + // https://docs.microsoft.com/en-us/dotnet/standard/managed-code // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose ~CustomWebApplicationFactory() diff --git a/backend/test/Integration/DatabaseSchemaTests.cs b/backend/test/Integration/DatabaseSchemaTests.cs new file mode 100644 index 00000000..201da275 --- /dev/null +++ b/backend/test/Integration/DatabaseSchemaTests.cs @@ -0,0 +1,92 @@ +using System; +using System.Globalization; +using EfSchemaCompare; +using FluentAssertions; +using Database.Data; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NUnit.Framework; +using Serilog; + +namespace Database.Tests.Integration; + +[TestFixture] +public sealed class DatabaseSchemaTests + : IDisposable +{ + private sealed class CustomWebApplicationFactory + : WebApplicationFactory + { + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.UseEnvironment(Database.Program.DevelopmentEnvironment); + } + + protected override IHost CreateHost(IHostBuilder builder) + { + builder.UseSerilog((context, services, configuration) => + { + configuration + .ReadFrom.Configuration(context.Configuration) // appsettings.test.json + .WriteTo.NUnitOutput(formatProvider: CultureInfo.InvariantCulture); + }); + return base.CreateHost(builder); + } + } + + private bool _disposed; + private CustomWebApplicationFactory Factory { get; } + + public DatabaseSchemaTests() + { + Factory = new CustomWebApplicationFactory(); + } + + public void Dispose() + { + // Dispose of unmanaged resources. + Dispose(true); + GC.SuppressFinalize(this); + } + + // https://docs.microsoft.com/en-us/dotnet/standard/managed-code + // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose + ~DatabaseSchemaTests() + { + Dispose(false); + } + + public void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + Factory.Dispose(); + } + _disposed = true; + } + } + + // For a list of limitations see https://github.com/JonPSmith/EfCore.SchemaCompare#List-of-limitations + [Test] + public void EnsureDomainModelMatchesDatabaseSchema() + { + // Arrange + using var scope = Factory.Services.CreateScope(); + var appSettings = scope.ServiceProvider.GetRequiredService(); + var databaseContext = scope.ServiceProvider.GetRequiredService(); + var connectionString = databaseContext.Database.GetConnectionString() + $";Password={appSettings.Database.Password}"; + var comparer = new CompareEfSql(); + // Act + // Compare C# domain model with PostgreSQL database schema, that is, + // compare EntityFramework Core model of the database with the database + // that the database context's connection points to. + var hasErrors = comparer.CompareEfWithDb(connectionString, databaseContext); + // Assert + hasErrors.Should().BeFalse(comparer.GetAllErrors); + } +} \ No newline at end of file From 32954cc7f4b1f6944ca00f1c368411ada40c9842 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 23 Jun 2026 17:51:22 +0200 Subject: [PATCH 849/878] Fall back to the component ID if for some reason the component itself could not be fetched from the metabase --- frontend/components/data/DataSummary.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/components/data/DataSummary.tsx b/frontend/components/data/DataSummary.tsx index b82a3e19..3203e4c7 100644 --- a/frontend/components/data/DataSummary.tsx +++ b/frontend/components/data/DataSummary.tsx @@ -132,12 +132,10 @@ export default function DataSummary({
    Data for component{" "} - {entity.component && ( - - )}{" "} + {" "} created by{" "} Date: Wed, 24 Jun 2026 17:26:40 +0200 Subject: [PATCH 850/878] Use fibonacci numbers for page sizes --- frontend/components/Pagination.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/components/Pagination.tsx b/frontend/components/Pagination.tsx index ee60d564..5db78571 100644 --- a/frontend/components/Pagination.tsx +++ b/frontend/components/Pagination.tsx @@ -6,7 +6,7 @@ import { ReloadOutlined, } from "@ant-design/icons"; -export const initialPageSize = 10; +export const initialPageSize = 8; export enum Fetching { INITIAL, @@ -16,7 +16,7 @@ export enum Fetching { const loadingIndicator = } />; -const pageSizeOptions = [3, initialPageSize, 20, 50, 100].map((size) => ({ +const pageSizeOptions = [3, initialPageSize, 21, 55, 89].map((size) => ({ label: `${size} per page`, value: size, })); From 2dd8e600abd5053908ce02fe3e6d1fc0afe8d126 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Jun 2026 20:22:24 +0200 Subject: [PATCH 851/878] Make program startup cancellable and use `DisposeAsync` instead of `Dispose` where possible --- .../GetHttpsResourcesController.cs | 2 +- backend/src/Data/DbSeeder.cs | 4 +- .../DataApprovals/AddDataApprovalMutation.cs | 4 +- backend/src/GraphQl/DataLoaders.cs | 15 ++--- backend/src/Program.cs | 60 +++++++++++-------- backend/src/Services/AccessPolicyService.cs | 2 +- .../src/Services/ResponseApprovalService.cs | 4 +- backend/src/Services/SigningService.cs | 59 +++++++++++------- backend/test/AuditableTests.cs | 4 +- 9 files changed, 90 insertions(+), 64 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 60395a97..94722053 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -143,7 +143,7 @@ public async Task Get( [FromRoute] string? extension = null ) { - using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); var getHttpsResource = await databaseContext.GetHttpsResourcesWithData.AsQueryable() .Where(_ => _.Id == id) .SingleOrDefaultAsync(cancellationToken); diff --git a/backend/src/Data/DbSeeder.cs b/backend/src/Data/DbSeeder.cs index e7fa1723..7a42f435 100644 --- a/backend/src/Data/DbSeeder.cs +++ b/backend/src/Data/DbSeeder.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -18,7 +19,8 @@ this ILogger logger public sealed class DbSeeder { public static async Task DoAsync( - IServiceProvider services + IServiceProvider services, + CancellationToken cancellationToken ) { var logger = services.GetRequiredService>(); diff --git a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs index 43f92532..4dc6df76 100644 --- a/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs +++ b/backend/src/GraphQl/DataApprovals/AddDataApprovalMutation.cs @@ -140,7 +140,7 @@ CancellationToken cancellationToken ) ); } - var sigantureVerificationResult = await signingService.VerifySignature(input.Message, input.Signature, input.KeyFingerprint); + var sigantureVerificationResult = await signingService.VerifySignatureAsync(input.Message, input.Signature, input.KeyFingerprint, cancellationToken); switch (sigantureVerificationResult) { case SigantureVerificationResult.FAILED_RECEIVING_KEY: @@ -341,4 +341,4 @@ CancellationToken cancellationToken return NewPayload(approval, null); } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/DataLoaders.cs b/backend/src/GraphQl/DataLoaders.cs index e3f59548..13595ec4 100644 --- a/backend/src/GraphQl/DataLoaders.cs +++ b/backend/src/GraphQl/DataLoaders.cs @@ -23,8 +23,7 @@ CancellationToken cancellationToken ) where TEntity : class, IEntity, IAuditable { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); return await getEntities(databaseContext) .AsNoTrackingWithIdentityResolution() .Where(_ => ids.Contains(_.Id)) @@ -42,8 +41,7 @@ CancellationToken cancellationToken ) where TMany : class, IEntity, IAuditable { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); return await getMany(databaseContext) .AsExpandable() @@ -66,8 +64,7 @@ CancellationToken cancellationToken ) where TMany : class, IEntity, IAuditable { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); return await getMany(databaseContext) .AsExpandable() @@ -88,8 +85,7 @@ CancellationToken cancellationToken ) where TAssociation : class, IAssociation, IAuditable { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); return await getAssociations(databaseContext) .AsExpandable() @@ -113,8 +109,7 @@ CancellationToken cancellationToken ) where TAssociation : class, IAssociation, IAuditable { - await using var databaseContext = - databaseContextFactory.CreateDbContext(); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); return await getAssociations(databaseContext) .AsExpandable() diff --git a/backend/src/Program.cs b/backend/src/Program.cs index 0b5fef15..2f8fc4db 100644 --- a/backend/src/Program.cs +++ b/backend/src/Program.cs @@ -3,6 +3,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Database.Data; using Database.Services; @@ -49,6 +50,7 @@ public sealed class Program public const string TestEnvironment = "test"; public const string DevelopmentEnvironment = "development"; private const string ProductionEnvironment = "production"; + private const string LogsPath = "./logs/serilog.json"; public static async Task Main( @@ -82,21 +84,27 @@ string[] commandLineArguments startup.Configure(application); using (var scope = application.Services.CreateScope()) { + var lifetime = scope.ServiceProvider.GetRequiredService(); if (!builder.Environment.IsEnvironment(TestEnvironment)) { - EnsureDatabaseIsUpToDate(scope.ServiceProvider); + await EnsureDatabaseIsUpToDateAsync(scope.ServiceProvider, lifetime.ApplicationStopping); // Inspired by https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro#initialize-db-with-test-data } if (builder.Environment.IsEnvironment(TestEnvironment)) { - await CreateDatabase(scope.ServiceProvider); + await CreateDatabaseAsync(scope.ServiceProvider, lifetime.ApplicationStopping); } - await SeedDatabase(scope.ServiceProvider); - await AssertGnuPgKeyExistence(scope.ServiceProvider); + await SeedDatabaseAsync(scope.ServiceProvider, lifetime.ApplicationStopping); + await AssertGnuPgKeyExistence(scope.ServiceProvider, lifetime.ApplicationStopping); } // dotnet run -- schema export --output ./schema.graphql return await application.RunWithGraphQLCommandsAsync(commandLineArguments); } + catch (OperationCanceledException exception) + { + Serilog.Log.Information(exception, "[System] App execution cancelled cleanly."); + return 1; + } catch (Exception exception) when (exception is not HostAbortedException && exception.Source != "Microsoft.EntityFrameworkCore.Design") // see https://github.com/dotnet/efcore/issues/29923 { Serilog.Log.Fatal(exception, "Host terminated unexpectedly"); @@ -157,30 +165,32 @@ Uri openTelemetryHost } } - private static void EnsureDatabaseIsUpToDate( - IServiceProvider services + private static async Task EnsureDatabaseIsUpToDateAsync( + IServiceProvider services, + CancellationToken cancellationToken ) { - using var databaseContext = - services.GetRequiredService>() - .CreateDbContext(); - var pendingMigrations = databaseContext.Database.GetPendingMigrations(); + await using var databaseContext = + await services.GetRequiredService>() + .CreateDbContextAsync(cancellationToken); + var pendingMigrations = await databaseContext.Database.GetPendingMigrationsAsync(cancellationToken); if (pendingMigrations.Any()) { throw new InvalidOperationException($"The database is not up to date. The pending migrations are: {string.Join(", ", pendingMigrations)}. Apply them by running `./database.mk migrate`."); } } - private static async Task CreateDatabase( - IServiceProvider services + private static async Task CreateDatabaseAsync( + IServiceProvider services, + CancellationToken cancellationToken ) { try { - using var databaseContext = - services.GetRequiredService>() - .CreateDbContext(); - await databaseContext.Database.EnsureCreatedAsync(); + await using var databaseContext = + await services.GetRequiredService>() + .CreateDbContextAsync(cancellationToken); + await databaseContext.Database.EnsureCreatedAsync(cancellationToken); } catch (Exception exception) { @@ -189,13 +199,14 @@ IServiceProvider services } } - private static async Task SeedDatabase( - IServiceProvider services + private static async Task SeedDatabaseAsync( + IServiceProvider services, + CancellationToken cancellationToken ) { try { - await DbSeeder.DoAsync(services); + await DbSeeder.DoAsync(services, cancellationToken); } catch (Exception exception) { @@ -204,13 +215,14 @@ IServiceProvider services } } - private static async Task AssertGnuPgKeyExistence( - IServiceProvider services + private static Task AssertGnuPgKeyExistence( + IServiceProvider services, + CancellationToken cancellationToken ) { - await services + return services .GetRequiredService() - .AssertGnuPgKeyExistence(); + .AssertGnuPgKeyExistenceAsync(cancellationToken); } // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host @@ -260,7 +272,7 @@ Uri openTelemetryHost } private static void ConfigureAppConfiguration( - IConfigurationBuilder configuration, + ConfigurationManager configuration, IHostEnvironment environment, string[] commandLineArguments ) diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index e8f98e1c..c8a76f6b 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -37,7 +37,7 @@ CancellationToken cancellationToken ) where TData : class, IData { - using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); + await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectApplicationClientId(); var (currentUser, currentInstitution) = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); Guid[]? institutionIds = null; diff --git a/backend/src/Services/ResponseApprovalService.cs b/backend/src/Services/ResponseApprovalService.cs index 94471a73..16ab97a1 100644 --- a/backend/src/Services/ResponseApprovalService.cs +++ b/backend/src/Services/ResponseApprovalService.cs @@ -96,7 +96,7 @@ public async Task CreateResponseApproval(IData dataObject, Can { throw new GraphQLException($"The GraphQL response contains the following errors: {string.Join(", ", typedResponse.Errors.Select(_ => $"'{_.Message}' [{string.Join(", ", _.Extensions?.Select(e => $"{e.Key}: '{e.Value}'") ?? [])}] ({string.Join(".", _.Path?.Select(p => p.ToString()) ?? [])})"))}"); } - var (signature, fingerprint) = await signingService.SignData(response); + var (signature, fingerprint) = await signingService.SignDataAsync(response, cancellationToken); return new ResponseApproval( clock.GetUtcNow(), signature, @@ -140,4 +140,4 @@ CancellationToken cancellationToken var response = executionResult.ToJson(withIndentations: false); return (query, JsonSerializer.SerializeToElement(variables), response); } -} +} \ No newline at end of file diff --git a/backend/src/Services/SigningService.cs b/backend/src/Services/SigningService.cs index 08b297d1..26d816f5 100644 --- a/backend/src/Services/SigningService.cs +++ b/backend/src/Services/SigningService.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -64,10 +65,12 @@ public sealed class SigningService( { public static readonly Uri KeyServerUrl = new("hkps://keys.openpgp.org/", UriKind.Absolute); - public async Task AssertGnuPgKeyExistence() + public async Task AssertGnuPgKeyExistenceAsync(CancellationToken cancellationToken) { - var (success, output, diagnostics) = await ExecuteCommand( - $"gpg --batch --with-colons --list-keys | grep '{appSettings.GnupgSecretSigningKey.Fingerprint}'" + var (success, output, diagnostics) = await ExecuteCommandAsync( + $"gpg --batch --with-colons --list-keys | grep '{appSettings.GnupgSecretSigningKey.Fingerprint}'", + null, + cancellationToken ); if (!success) { @@ -75,11 +78,15 @@ public async Task AssertGnuPgKeyExistence() } } - public async Task<(string Signature, string Fingerprint)> SignData(string data) + public async Task<(string Signature, string Fingerprint)> SignDataAsync( + string data, + CancellationToken cancellationToken + ) { - var (success, signature, diagnostics) = await ExecuteCommand( + var (success, signature, diagnostics) = await ExecuteCommandAsync( $"gpg --pinentry-mode loopback --batch --passphrase '{appSettings.GnupgSecretSigningKey.Passphrase}' --detach-sig --armor --local-user '{appSettings.GnupgSecretSigningKey.Fingerprint}'", - data + data, + cancellationToken ); if (!success) { @@ -89,11 +96,14 @@ public async Task AssertGnuPgKeyExistence() return (signature, appSettings.GnupgSecretSigningKey.Fingerprint); } - public async Task VerifySignature( - string message, string signature, string fingerprint + public async Task VerifySignatureAsync( + string message, + string signature, + string fingerprint, + CancellationToken cancellationToken ) { - if (!await ReceiveKey(fingerprint)) + if (!await ReceiveKeyAsync(fingerprint, cancellationToken)) { return SigantureVerificationResult.FAILED_RECEIVING_KEY; } @@ -101,9 +111,10 @@ public async Task VerifySignature( try { File.WriteAllText(temporaryMessageFilePath, message); - var (success, output, diagnostics) = await ExecuteCommand( + var (success, output, diagnostics) = await ExecuteCommandAsync( $"gpg --pinentry-mode loopback --batch --always-trust --assert-signer '{fingerprint}' --verify - '{temporaryMessageFilePath}'", - signature + signature, + cancellationToken ); if (!success) { @@ -122,10 +133,15 @@ public async Task VerifySignature( } } - private async Task ReceiveKey(string fingerprint) + private async Task ReceiveKeyAsync( + string fingerprint, + CancellationToken cancellationToken + ) { - var (success, output, diagnostics) = await ExecuteCommand( - $"gpg --pinentry-mode loopback --batch --keyserver {KeyServerUrl.AbsoluteUri} --receive-keys {fingerprint}" + var (success, output, diagnostics) = await ExecuteCommandAsync( + $"gpg --pinentry-mode loopback --batch --keyserver {KeyServerUrl.AbsoluteUri} --receive-keys {fingerprint}", + null, + cancellationToken ); if (!success) { @@ -134,9 +150,10 @@ private async Task ReceiveKey(string fingerprint) return success; } - private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommand( + private async Task<(bool Success, string Output, string Diagnostics)> ExecuteCommandAsync( string command, - string? input = null + string? input, + CancellationToken cancellationToken ) { logger.ExecuteCommand(command); @@ -161,13 +178,13 @@ private async Task ReceiveKey(string fingerprint) ) ?? throw new InvalidOperationException($"The process for command '{command}' with the input '{input}' failed to start."); if (input is not null) { - await process.StandardInput.WriteLineAsync(input); - await process.StandardInput.FlushAsync(); + await process.StandardInput.WriteLineAsync(input.AsMemory(), cancellationToken); + await process.StandardInput.FlushAsync(cancellationToken); } process.StandardInput.Close(); - var output = process.StandardOutput.ReadToEnd(); - var diagnostics = process.StandardError.ReadToEnd(); - await process.WaitForExitAsync(); + var output = await process.StandardOutput.ReadToEndAsync(cancellationToken); + var diagnostics = await process.StandardError.ReadToEndAsync(cancellationToken); + await process.WaitForExitAsync(cancellationToken); logger.ExecuteCommandOutput(output); logger.ExecuteCommandDiagnostics(diagnostics); logger.ExecuteCommandExitCode(process.ExitCode); diff --git a/backend/test/AuditableTests.cs b/backend/test/AuditableTests.cs index a699a184..f7f5ea36 100644 --- a/backend/test/AuditableTests.cs +++ b/backend/test/AuditableTests.cs @@ -29,7 +29,7 @@ public async Task SaveChanges_SetsAndUpdatesTimestamps_UsingFakeClock() // Arrange var startInstant = Instant.FromUtc(2024, 1, 1, 10, 0); var fakeClock = new FakeClock(startInstant); - using var context = new ApplicationDbContext(_options, fakeClock); + await using var context = new ApplicationDbContext(_options, fakeClock); var entity = new User("Subject", "Name"); // Act context.Add(entity); @@ -59,7 +59,7 @@ public async Task SaveChanges_SetsAndUpdatesTimestamps_UsingFakeClock() // { // var deleteTime = Instant.FromUtc(2024, 1, 1, 15, 0); // var fakeClock = new FakeClock(deleteTime); - // using var context = new ApplicationDbContext(_options, fakeClock); + // await using var context = new ApplicationDbContext(_options, fakeClock); // var entity = new YourModel { Name = "To Be Deleted" }; // context.Add(entity); // await context.SaveChangesAsync(); From 61b93da04a5bfffb4375afb6e09c00f15c33fb47 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Jun 2026 20:23:16 +0200 Subject: [PATCH 852/878] Add navigations form policy to user, institution, or OpenID Connect application client --- .../InstitutionAccessPolicyType.cs | 20 +++++++++++++++++++ ...penIdConnectApplicationAccessPolicyType.cs | 20 +++++++++++++++++++ .../AccessPolicies/UserAccessPolicyType.cs | 20 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs index ee59bfcf..c192bcaf 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs @@ -1,4 +1,8 @@ +using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate; using HotChocolate.Types; namespace Database.GraphQl.AccessPolicies; @@ -11,5 +15,21 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(nameof(InstitutionAccessPolicy.InstitutionId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetInstitutionAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetInstitutionAsync( + [Parent] InstitutionAccessPolicy parent, + IInstitutionByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.InstitutionId); + } } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs index 970240d6..baf1a902 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs @@ -1,4 +1,8 @@ +using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate; using HotChocolate.Types; namespace Database.GraphQl.AccessPolicies; @@ -11,5 +15,21 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(nameof(OpenIdConnectApplicationAccessPolicy.ClientId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetOpenIdConnectApplicationAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetOpenIdConnectApplicationAsync( + [Parent] OpenIdConnectApplicationAccessPolicy parent, + IOpenIdConnectApplicationByClientIdDataLoader byClientId + ) + { + return byClientId.LoadAsync(parent.ClientId); + } } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs index a28cdaee..cdd0c528 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs @@ -1,4 +1,8 @@ +using System.Threading.Tasks; +using Database.ApiRequests; using Database.Data.AccessPolicies; +using Database.Extensions; +using HotChocolate; using HotChocolate.Types; namespace Database.GraphQl.AccessPolicies; @@ -11,5 +15,21 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(nameof(UserAccessPolicy.UserId)[..^2].FirstCharToLower()) + .Type>() + .Cost(3) + .ResolveWith(_ => Resolvers.GetUserAsync(default!, default!)); + } + + private sealed class Resolvers + { + public static Task GetUserAsync( + [Parent] UserAccessPolicy parent, + IUserByIdDataLoader byId + ) + { + return byId.LoadAsync(parent.UserId); + } } } \ No newline at end of file From 07553973b1cc47d7708044619a4beda38d83b02e Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Jun 2026 20:37:04 +0200 Subject: [PATCH 853/878] Jump directly on select --- frontend/components/JumpToId.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/components/JumpToId.tsx b/frontend/components/JumpToId.tsx index ea0f2c01..0e7d2fc9 100644 --- a/frontend/components/JumpToId.tsx +++ b/frontend/components/JumpToId.tsx @@ -16,7 +16,7 @@ export default function JumpToId({ query, route, style }: JumpToIdProps) { const router = useRouter(); const [id, setId] = useState(undefined); - const handleJump = () => { + const jump = (id: string | undefined) => { if (id && isUuid(id)) { router.push(route(id)); } @@ -29,7 +29,7 @@ export default function JumpToId({ query, route, style }: JumpToIdProps) { ) : ( setId(e.target.value)} /> )} - From 8d9917346ae4f59540fca0aa8c16d4ea18261853 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Wed, 24 Jun 2026 22:25:01 +0200 Subject: [PATCH 854/878] Access the current institution through the current OpenID Connect application (by doing so we gain access to the client ID which may be needed for access policies --- .../OpenIdConnectApplicationDataLoader.cs | 2 +- .../Queries/CurrentUserOrApplication.graphql | 86 +++++++++++++++++++ .../Queries/CurrentUserOrInstitution.graphql | 81 ----------------- ...on.cs => QueryCurrentUserOrApplication.cs} | 30 ++++--- .../Authentication/AuthenticationHandler.cs | 20 +++-- .../src/Authorization/CommonAuthorization.cs | 29 +++---- .../Institutions/InstitutionQueries.cs | 8 +- backend/src/GraphQl/MutationBase.cs | 12 +-- .../OpenIdConnectionApplicationQueries.cs | 25 ++++++ backend/src/GraphQl/Users/UserQueries.cs | 6 +- backend/src/GraphQl/Users/UserType.cs | 16 ---- backend/src/Services/AccessPolicyService.cs | 6 +- backend/src/Services/CacheService.cs | 12 +-- backend/src/Services/UserService.cs | 32 +++---- backend/src/Startup.cs | 2 +- .../GraphQlSchemaTests.IsUnchanged.snap | 21 +++-- frontend/type-defs.graphqls | 21 +++-- 17 files changed, 230 insertions(+), 179 deletions(-) create mode 100644 backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql delete mode 100644 backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql rename backend/src/ApiRequests/{QueryCurrentUserOrInstitution.cs => QueryCurrentUserOrApplication.cs} (82%) create mode 100644 backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs delete mode 100644 backend/src/GraphQl/Users/UserType.cs diff --git a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs index fc13548c..0da826b3 100644 --- a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -18,7 +18,7 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => public sealed record OpenIdConnectApplication( [property: GraphQLIgnore] string Id, - string Name + string? Name ) : IIdNode { string ClientId => Id; diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql new file mode 100644 index 00000000..dcb0acfb --- /dev/null +++ b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql @@ -0,0 +1,86 @@ +query CurrentUserOrApplication($databaseId: Uuid!) { + currentUser { + uuid + name + representedInstitutions { + edges { + node { + uuid + name + managedInstitutions { + edges { + node { + uuid + name + } + } + totalCount + } + } + role + } + totalCount + } + databaseOperatingRepresentedInstitutions: representedInstitutions( + where: { + or: [ + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + operatedDatabases: { some: { id: { equalTo: $databaseId } } } + } + } + ] + } + { + and: [ + { role: { in: [ASSISTANT, OWNER] } } + { + institution: { + managedInstitutions: { + some: { + operatedDatabases: { + some: { id: { equalTo: $databaseId } } + } + } + } + } + } + ] + } + ] + } + ) { + edges { + node { + uuid + name + } + role + } + totalCount + } + } + currentApplication: currentOpenIdConnectApplication { + clientId + displayName + owner { + node { + databaseOperatingDatabases: operatedDatabases( + where: { id: { equalTo: $databaseId } } + ) { + totalCount + } + databaseOperatingManagedInstitutions: managedInstitutions( + where: { + operatedDatabases: { some: { id: { equalTo: $databaseId } } } + } + ) { + totalCount + } + } + } + } +} diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql deleted file mode 100644 index 82aa1122..00000000 --- a/backend/src/ApiRequests/Queries/CurrentUserOrInstitution.graphql +++ /dev/null @@ -1,81 +0,0 @@ -query CurrentUserOrInstitution($databaseId: Uuid!) { - currentUser { - uuid - name - representedInstitutions { - edges { - node { - uuid - name - managedInstitutions { - edges { - node { - uuid - name - } - } - totalCount - } - } - role - } - totalCount - } - databaseOperatingRepresentedInstitutions: representedInstitutions( - where: { - or: [ - { - and: [ - { role: { in: [ASSISTANT, OWNER] } } - { - institution: { - operatedDatabases: { some: { id: { equalTo: $databaseId } } } - } - } - ] - } - { - and: [ - { role: { in: [ASSISTANT, OWNER] } } - { - institution: { - managedInstitutions: { - some: { - operatedDatabases: { - some: { id: { equalTo: $databaseId } } - } - } - } - } - } - ] - } - ] - } - ) { - edges { - node { - uuid - name - } - role - } - totalCount - } - } - currentInstitution { - uuid - name - databaseOperatingDatabases: operatedDatabases( - where: { id: { equalTo: $databaseId } } - ) { - totalCount - } - databaseOperatingManagedInstitutions: managedInstitutions( - where: { operatedDatabases: { some: { id: { equalTo: $databaseId } } } } - ) { - totalCount - } - } -} - diff --git a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs similarity index 82% rename from backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs rename to backend/src/ApiRequests/QueryCurrentUserOrApplication.cs index 119ea64e..154118ed 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrInstitution.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -16,7 +16,7 @@ public static partial class Log Message = "Response contains errors.") ] internal static partial void ResponseErrors( - this ILogger logger, + this ILogger logger, [TagProvider(typeof(GraphQlErrorsTagProvider), nameof(GraphQlErrorsTagProvider.RecordTags))] GraphQLError[] errors ); } @@ -24,15 +24,15 @@ internal static partial void ResponseErrors( /// /// Request current user or institution from metabase. /// -public sealed class QueryCurrentUserOrInstitution( +public sealed class QueryCurrentUserOrApplication( AppSettings appSettings, ApiRequestService apiRequestService, - ILogger logger + ILogger logger ) { - private const string QueryFileName = "CurrentUserOrInstitution.graphql"; + private const string QueryFileName = "CurrentUserOrApplication.graphql"; - public static readonly CurrentUserOrInstitution Empty = new(null, null); + public static readonly CurrentUserOrApplication Empty = new(null, null); public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; @@ -108,26 +108,32 @@ public sealed record DatabaseOperatingManagedInstitutionConnection( int TotalCount ); - public sealed record CurrentUserOrInstitution( + public sealed record CurrentOpenIdConnectApplication( + string ClientId, + string? DisplayName, + CurrentInstitution Owner + ); + + public sealed record CurrentUserOrApplication( CurrentUser? CurrentUser, - CurrentInstitution? CurrentInstitution + CurrentOpenIdConnectApplication? CurrentApplication ) { public void Deconstruct( out CurrentUser? currentUser, - out CurrentInstitution? currentInstitution + out CurrentOpenIdConnectApplication? currentApplication ) { currentUser = CurrentUser; - currentInstitution = CurrentInstitution; + currentApplication = CurrentApplication; } }; - public async Task Do( + public async Task Do( CancellationToken cancellationToken ) { - var response = (await apiRequestService.QueryGraphQl( + var response = (await apiRequestService.QueryGraphQl( GetGraphQlEndpoint, new GraphQLRequest( await GraphQlQueryHelpers.Construct(QueryFileName), @@ -135,7 +141,7 @@ await GraphQlQueryHelpers.Construct(QueryFileName), { databaseId = appSettings.DatabaseId }, - "CurrentUserOrInstitution" + "CurrentUserOrApplication" ), cancellationToken )); diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index a49d72e1..42afcbf9 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -7,6 +8,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; +using OpenIddict.Abstractions; using OpenIddict.Client; using OpenIddict.Validation.AspNetCore; @@ -163,9 +165,9 @@ CancellationToken cancellationToken // bearer token from the metabase for its own OpenID Connect Client // (another one than `appSettings.OpenIdConnectClient.Id`) and uses // this token to authenticate (and authorize) access to this database. - var metabaseAuthenticateResult = await userService.SwitchUserOrInstitutionAsync( - user => Task.FromResult(user is null ? null : CreateSuccessfulAuthenticateResult($"user:{user.Uuid.ToString("D")}")), - institution => Task.FromResult(CreateSuccessfulAuthenticateResult($"institution:{institution.Uuid.ToString("D")}")), + var metabaseAuthenticateResult = await userService.SwitchUserOrApplicationAsync( + user => Task.FromResult(user is null ? null : CreateSuccessfulAuthenticateResult($"user:{user.Uuid.ToString("D")}", null)), + application => Task.FromResult(CreateSuccessfulAuthenticateResult($"client:{application.ClientId}", application.ClientId)), cancellationToken ); if (metabaseAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) @@ -175,13 +177,21 @@ CancellationToken cancellationToken return AuthenticateResult.Fail("Neither authenticated through cookie nor bearer token."); } - private static AuthenticateResult CreateSuccessfulAuthenticateResult(string nameIdentifier) + private static AuthenticateResult CreateSuccessfulAuthenticateResult( + string nameIdentifier, + string? clientId + ) { + var claims = new[] { new Claim(ClaimTypes.NameIdentifier, nameIdentifier) } + .If( + clientId is not null, + _ => _.Append(new Claim(OpenIddictConstants.Claims.ClientId, clientId!)) + ); return AuthenticateResult.Success( new AuthenticationTicket( new ClaimsPrincipal( new ClaimsIdentity( - claims: [new Claim(ClaimTypes.NameIdentifier, nameIdentifier)], + claims: claims, authenticationType: "Metabase", nameType: ClaimTypes.Name, roleType: ClaimTypes.Role diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index f01d4352..3d5cc236 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -16,53 +16,52 @@ UserService userService // This is the same code that HotChocolate returns when autheorization via the attribute `[Authorize(Policy = ...)]` fails. private const string UNAUTHORIZED_CODE = "AUTH_NOT_AUTHENTICATED"; - public Task SwitchUserOrInstitutionAsync( - Func> handleUser, - Func> handleInstitution, + public Task SwitchUserOrApplicationAsync( + Func> handleUser, + Func> handleApplication, CancellationToken cancellationToken ) { - return userService.SwitchUserOrInstitutionAsync(handleUser, handleInstitution, cancellationToken); + return userService.SwitchUserOrApplicationAsync(handleUser, handleApplication, cancellationToken); } - public Task FetchCurrentUserOrInstitutionAsync(CancellationToken cancellationToken) + public Task FetchCurrentUserOrApplicationAsync(CancellationToken cancellationToken) { - return userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); + return userService.FetchCurrentUserOrApplicationAsync(cancellationToken); } public Task IsAuthenticated(CancellationToken cancellationToken) { - return userService.SwitchUserOrInstitutionAsync( + return userService.SwitchUserOrApplicationAsync( user => Task.FromResult(user is not null), - institution => Task.FromResult(institution is not null), + application => Task.FromResult(application is not null), cancellationToken ); } public Task IsDatabaseOperator(CancellationToken cancellationToken) { - return userService.SwitchUserOrInstitutionAsync( + return userService.SwitchUserOrApplicationAsync( user => Task.FromResult( user is not null && user.IsAtLeastAssistantManagerOfDatabaseOperator() ), - institution => Task.FromResult( - institution is not null - && institution.IsDatabaseOperator() + application => Task.FromResult( + application.Owner.IsDatabaseOperator() ), cancellationToken ); } public bool IsCurrentUserAtLeastAssistantManagerOfVerifiedInstitution( - QueryCurrentUserOrInstitution.CurrentUser currentUser, + QueryCurrentUserOrApplication.CurrentUser currentUser, Guid institutionId ) { return currentUser.RepresentedInstitutions.Edges.Any(t => ( - t.Role is QueryCurrentUserOrInstitution.InstitutionRepresentativeRole.ASSISTANT - || t.Role is QueryCurrentUserOrInstitution.InstitutionRepresentativeRole.OWNER + t.Role is QueryCurrentUserOrApplication.InstitutionRepresentativeRole.ASSISTANT + || t.Role is QueryCurrentUserOrApplication.InstitutionRepresentativeRole.OWNER ) && ( diff --git a/backend/src/GraphQl/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs index 411adfe6..fb39759c 100644 --- a/backend/src/GraphQl/Institutions/InstitutionQueries.cs +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.cs @@ -11,14 +11,14 @@ namespace Database.GraphQl.Institutions; public sealed class InstitutionQueries { [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public Task GetCurrentInstitutionAsync( + public Task GetCurrentInstitutionAsync( CommonAuthorization authorization, CancellationToken cancellationToken ) { - return authorization.SwitchUserOrInstitutionAsync( - user => Task.FromResult(null), - institution => Task.FromResult(institution), + return authorization.SwitchUserOrApplicationAsync( + user => Task.FromResult(null), + application => Task.FromResult(application.Owner), cancellationToken ); } diff --git a/backend/src/GraphQl/MutationBase.cs b/backend/src/GraphQl/MutationBase.cs index 18ab1b8b..4acb8937 100644 --- a/backend/src/GraphQl/MutationBase.cs +++ b/backend/src/GraphQl/MutationBase.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Database.Authorization; -using static Database.ApiRequests.QueryCurrentUserOrInstitution; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.GraphQl; @@ -13,7 +13,7 @@ public abstract class MutationBase protected abstract TPayload NewPayload(TData? data, IReadOnlyCollection? errors); protected abstract TError NewError(TErrorCode errorCode, string message, IReadOnlyList path); - protected async Task> AuthorizeAsync( + protected async Task> AuthorizeAsync( TErrorCode unauthenticatedErrorCode, TErrorCode unauthorizedErrorCode, CommonAuthorization authorization, @@ -22,7 +22,7 @@ CancellationToken cancellationToken { if (!await authorization.IsAuthenticated(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthenticatedErrorCode, @@ -34,7 +34,7 @@ CancellationToken cancellationToken } if (!await authorization.IsDatabaseOperator(cancellationToken)) { - return new Result.Error( + return new Result.Error( NewPayload(null, [ NewError( unauthorizedErrorCode, @@ -44,8 +44,8 @@ CancellationToken cancellationToken ]) ); } - return new Result.Data( - await authorization.FetchCurrentUserOrInstitutionAsync(cancellationToken) + return new Result.Data( + await authorization.FetchCurrentUserOrApplicationAsync(cancellationToken) ); } } \ No newline at end of file diff --git a/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs b/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs new file mode 100644 index 00000000..0ea6c8ad --- /dev/null +++ b/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs @@ -0,0 +1,25 @@ +using System.Threading; +using System.Threading.Tasks; +using HotChocolate.Types; +using Database.Authorization; +using Database.ApiRequests; +using HotChocolate.Authorization; + +namespace Database.GraphQl.Applications; + +[ExtendObjectType(nameof(Query))] +public sealed class ApplicationQueries +{ + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentOpenIdConnectApplicationAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrApplicationAsync( + user => Task.FromResult(null), + application => Task.FromResult(application), + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index d6b839b3..203a9409 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -13,14 +13,14 @@ namespace Database.GraphQl.Users; public sealed class UserQueries { [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public Task GetCurrentUserAsync( + public Task GetCurrentUserAsync( CommonAuthorization authorization, CancellationToken cancellationToken ) { - return authorization.SwitchUserOrInstitutionAsync( + return authorization.SwitchUserOrApplicationAsync( user => Task.FromResult(user), - institution => Task.FromResult(null), + application => Task.FromResult(null), cancellationToken ); } diff --git a/backend/src/GraphQl/Users/UserType.cs b/backend/src/GraphQl/Users/UserType.cs deleted file mode 100644 index d05d62c0..00000000 --- a/backend/src/GraphQl/Users/UserType.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Database.Data; -using Database.GraphQl.Entities; -using HotChocolate.Types; - -namespace Database.GraphQl.Users; - -public sealed class UserType - : EntityType -{ - protected override void Configure( - IObjectTypeDescriptor descriptor - ) - { - base.Configure(descriptor); - } -} \ No newline at end of file diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index c8a76f6b..c7140037 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -39,15 +39,15 @@ CancellationToken cancellationToken { await using var databaseContext = await databaseContextFactory.CreateDbContextAsync(cancellationToken); var openIdConnectClientId = userService.GetOpenIdConnectApplicationClientId(); - var (currentUser, currentInstitution) = await userService.FetchCurrentUserOrInstitutionAsync(cancellationToken); + var (currentUser, currentApplication) = await userService.FetchCurrentUserOrApplicationAsync(cancellationToken); Guid[]? institutionIds = null; if (currentUser is not null) { institutionIds = currentUser.RepresentedInstitutions.Edges.Select(e => e.Node.Uuid).ToArray(); } - else if (currentInstitution is not null) + else if (currentApplication is not null) { - institutionIds = [currentInstitution.Uuid]; + institutionIds = [currentApplication.Owner.Uuid]; } logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); var policedData = PoliceData(getData(databaseContext), currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); diff --git a/backend/src/Services/CacheService.cs b/backend/src/Services/CacheService.cs index 79a57c91..c25d53c1 100644 --- a/backend/src/Services/CacheService.cs +++ b/backend/src/Services/CacheService.cs @@ -1,24 +1,24 @@ using System; using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Caching.Memory; -using static Database.ApiRequests.QueryCurrentUserOrInstitution; +using static Database.ApiRequests.QueryCurrentUserOrApplication; namespace Database.Services; public sealed class CacheService( - IMemoryCache currentUserOrInstitutionCache + IMemoryCache currentUserOrApplicationCache ) { - public CurrentUserOrInstitution? SetCurrentUserOrInstitution(string token, CurrentUserOrInstitution userOrInstitution) + public CurrentUserOrApplication? SetCurrentUserOrApplication(string token, CurrentUserOrApplication userOrApplication) { var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromHours(1)); - return currentUserOrInstitutionCache.Set(token, userOrInstitution, cacheEntryOptions); + return currentUserOrApplicationCache.Set(token, userOrApplication, cacheEntryOptions); } - public bool TryGetCurrentUserOrInstitution(string token, [NotNullWhen(true)] out CurrentUserOrInstitution? cachedUserOrInstitution) + public bool TryGetCurrentUserOrApplication(string token, [NotNullWhen(true)] out CurrentUserOrApplication? cachedUserOrApplication) { - return currentUserOrInstitutionCache.TryGetValue(token, out cachedUserOrInstitution); + return currentUserOrApplicationCache.TryGetValue(token, out cachedUserOrApplication); } } \ No newline at end of file diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index 40590b85..d35f233e 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -12,7 +12,7 @@ namespace Database.Services; /// Service to fetch current user or institution from the metabase or the local cache /// public sealed class UserService( - QueryCurrentUserOrInstitution queryCurrentUserOrInstitution, + QueryCurrentUserOrApplication queryCurrentUserOrApplication, IHttpContextAccessor httpContextAccessor, CacheService cacheService ) @@ -30,56 +30,56 @@ CacheService cacheService // ?? httpContextAccessor.HttpContext?.User.GetClaim(OpenIddictConstants.Claims.Private.Presenter); } - public async Task SwitchUserOrInstitutionAsync( - Func> handleUser, - Func> handleInstitution, + public async Task SwitchUserOrApplicationAsync( + Func> handleUser, + Func> handleApplication, CancellationToken cancellationToken ) { - var userOrInstitution = await FetchCurrentUserOrInstitutionAsync(cancellationToken); - if (userOrInstitution.CurrentInstitution is not null) + var userOrApplication = await FetchCurrentUserOrApplicationAsync(cancellationToken); + if (userOrApplication.CurrentApplication is not null) { - return await handleInstitution(userOrInstitution.CurrentInstitution); + return await handleApplication(userOrApplication.CurrentApplication); } else { - return await handleUser(userOrInstitution.CurrentUser); + return await handleUser(userOrApplication.CurrentUser); } } - public async Task FetchCurrentUserOrInstitutionAsync( + public async Task FetchCurrentUserOrApplicationAsync( CancellationToken cancellationToken ) { var httpContext = httpContextAccessor.HttpContext; if (httpContext is null) { - return QueryCurrentUserOrInstitution.Empty; + return QueryCurrentUserOrApplication.Empty; } var token = httpContext.ExtractBearerToken(); if (token is null) { - return QueryCurrentUserOrInstitution.Empty; + return QueryCurrentUserOrApplication.Empty; } // If there is no authenticated user, then the bearer token may be // invalid or expired, so we cannot trust a possibly-cached result. if (httpContext.User is null) { - return await queryCurrentUserOrInstitution.Do( + return await queryCurrentUserOrApplication.Do( cancellationToken ); } // If there is an authenticated user, then the bearer token is valid and // we try to get the cached user or application. - if (!cacheService.TryGetCurrentUserOrInstitution(token, out var cachedUserOrInstitution)) + if (!cacheService.TryGetCurrentUserOrApplication(token, out var cachedUserOrApplication)) { // If it is not cached, fetch it ... - cachedUserOrInstitution = await queryCurrentUserOrInstitution.Do( + cachedUserOrApplication = await queryCurrentUserOrApplication.Do( cancellationToken ); // ... and store it in the cache. - cacheService.SetCurrentUserOrInstitution(token, cachedUserOrInstitution); + cacheService.SetCurrentUserOrApplication(token, cachedUserOrApplication); } - return cachedUserOrInstitution; + return cachedUserOrApplication; } } \ No newline at end of file diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 82bb3093..15233951 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -301,7 +301,7 @@ public static void ConfigureApiRequests(IServiceCollection services) { services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddScoped(); } diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index d6c01981..2433c89f 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -265,6 +265,8 @@ type Query { calorimetricData(id: Uuid!, locale: Locale): CalorimetricData @cost(weight: "10") currentInstitution: CurrentInstitution @cost(weight: "10") + currentOpenIdConnectApplication: CurrentOpenIdConnectApplication + @cost(weight: "10") currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") @@ -784,6 +786,12 @@ type CurrentInstitution { uuid: Uuid! } +type CurrentOpenIdConnectApplication { + clientId: String! + displayName: String + owner: CurrentInstitution! +} + type CurrentUser { databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnection! isAtLeastAssistantManagerOfDatabaseOperator: Boolean! @@ -1162,6 +1170,7 @@ type InstitutionAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! + institution: Institution @cost(weight: "3") institutionId: Uuid! isAccessAllowed(institutionIds: [Uuid!]): Boolean! isAlwaysAllowed: Boolean! @@ -1283,8 +1292,13 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } +type OpenIdConnectApplication { + name: String +} + type OpenIdConnectApplicationAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime + client: OpenIdConnectApplication @cost(weight: "3") clientId: String! createdAt: DateTime! dataAccessPolicy: DataAccessPolicy @@ -1712,12 +1726,8 @@ type UpperLimitPerDuration { upperLimit: NonNegativeInt! } -type User implements Node { - createdAt: DateTime! - id: ID! +type User { name: String! - subject: String! - updatedAt: DateTime! uuid: Uuid! } @@ -1733,6 +1743,7 @@ type UserAccessPolicy implements Node { isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + user: User @cost(weight: "3") userId: Uuid! uuid: Uuid! } diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index d6c01981..2433c89f 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -265,6 +265,8 @@ type Query { calorimetricData(id: Uuid!, locale: Locale): CalorimetricData @cost(weight: "10") currentInstitution: CurrentInstitution @cost(weight: "10") + currentOpenIdConnectApplication: CurrentOpenIdConnectApplication + @cost(weight: "10") currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") @@ -784,6 +786,12 @@ type CurrentInstitution { uuid: Uuid! } +type CurrentOpenIdConnectApplication { + clientId: String! + displayName: String + owner: CurrentInstitution! +} + type CurrentUser { databaseOperatingRepresentedInstitutions: UserRepresentedInstitutionConnection! isAtLeastAssistantManagerOfDatabaseOperator: Boolean! @@ -1162,6 +1170,7 @@ type InstitutionAccessPolicy implements Node { dataAccessPolicy: DataAccessPolicy dataAccessPolicyId: Uuid! id: ID! + institution: Institution @cost(weight: "3") institutionId: Uuid! isAccessAllowed(institutionIds: [Uuid!]): Boolean! isAlwaysAllowed: Boolean! @@ -1283,8 +1292,13 @@ type OpenEndedDateTimeRange { until: DateTime @cost(weight: "10") } +type OpenIdConnectApplication { + name: String +} + type OpenIdConnectApplicationAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime + client: OpenIdConnectApplication @cost(weight: "3") clientId: String! createdAt: DateTime! dataAccessPolicy: DataAccessPolicy @@ -1712,12 +1726,8 @@ type UpperLimitPerDuration { upperLimit: NonNegativeInt! } -type User implements Node { - createdAt: DateTime! - id: ID! +type User { name: String! - subject: String! - updatedAt: DateTime! uuid: Uuid! } @@ -1733,6 +1743,7 @@ type UserAccessPolicy implements Node { isWithinTimeSpan: Boolean! updatedAt: DateTime! upperAccessLimitPerTimeDuration: UpperLimitPerDuration + user: User @cost(weight: "3") userId: Uuid! uuid: Uuid! } From 9faf1533b0ee5b50c44c20515403f41a67d0fe82 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Thu, 25 Jun 2026 18:45:19 +0200 Subject: [PATCH 855/878] Use pg-schema-diff to compare the domain-model derived PostgreSQL schema with the migrate.sql derived one --- backend/Dockerfile.development | 22 ++ backend/Makefile | 3 +- backend/src/AppSettings.cs | 13 ++ backend/src/Migrations/migrate.sql | 2 - backend/src/Startup.cs | 9 +- .../test/Integration/DatabaseSchemaTests.cs | 193 ++++++++++++------ backend/test/Integration/MigrationTests.cs | 9 +- database.mk | 2 +- .../components/methods/AppliedMethodView.tsx | 3 +- 9 files changed, 172 insertions(+), 84 deletions(-) diff --git a/backend/Dockerfile.development b/backend/Dockerfile.development index f5e22925..00c1538d 100644 --- a/backend/Dockerfile.development +++ b/backend/Dockerfile.development @@ -144,6 +144,28 @@ RUN \ --force \ /var/lib/apt/lists/* +# for available versions see the tags on https://github.com/stripe/pg-schema-diff +ENV PG_SCHEMA_DIFF_VERSION=v1.0.5 +RUN \ + apt-get update && \ + apt-get install \ + --assume-yes \ + --no-install-recommends \ + golang-go && \ + go install \ + github.com/stripe/pg-schema-diff/cmd/pg-schema-diff@${PG_SCHEMA_DIFF_VERSION} && \ + cp /root/go/bin/pg-schema-diff /usr/local/bin/ && \ + rm --recursive --force \ + /root/go \ + /root/.cache && \ + apt-get purge \ + --assume-yes \ + golang-go && \ + rm \ + --recursive \ + --force \ + /var/lib/apt/lists/* + ENV HOME=/home/me RUN mkdir --parents "${HOME}/app" \ && chown \ diff --git a/backend/Makefile b/backend/Makefile index fe60aae8..eb0258aa 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -184,8 +184,7 @@ schema : ## Fetch GraphQL schema using `hasura/graphqurl` .PHONY : schema check-database : ## Check whether there have been any model changes made since the last migration was added and whether the C# domain model matches the PostgreSQL database schema - $(MAKE) test ARGS='--filter "FullyQualifiedName~EnsureMigrationsAreUpToDate"' - $(MAKE) test ARGS='--filter "FullyQualifiedName~EnsureDomainModelMatchesDatabaseSchema"' + $(MAKE) test ARGS='--filter "FullyQualifiedName~EnsureMigrationsAreUpToDate|FullyQualifiedName~EnsureDomainModelMatchesDatabaseSchema"' .PHONY : check-database has-pending-model-changes : ## Check whether there have been any model changes made since the last migration was added diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index af155b5f..5c93fb04 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,6 +1,7 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited using System; +using Npgsql; namespace Database; @@ -51,6 +52,18 @@ public sealed record DatabaseSettings public string UserName { get; init; } = ""; public string Password { get; init; } = ""; public string SchemaName { get; init; } = ""; + + public string ConnectionString(string? customDatabaseName = null) + { + var connectionStringBuilder = new NpgsqlConnectionStringBuilder(); + connectionStringBuilder.Host = Host; + connectionStringBuilder.Port = Port; + connectionStringBuilder.Database = customDatabaseName ?? Name; + connectionStringBuilder.Username = UserName; + connectionStringBuilder.Password = Password; + connectionStringBuilder.MaxPoolSize = 90; + return connectionStringBuilder.ConnectionString; + } }; public sealed record OpenTelemetrySettings diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index f0385d51..d1b8a30e 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -1,5 +1,3 @@ -\set ON_ERROR_STOP on - CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" ( "MigrationId" character varying(150) NOT NULL, "ProductVersion" character varying(32) NOT NULL, diff --git a/backend/src/Startup.cs b/backend/src/Startup.cs index 15233951..571b5b39 100644 --- a/backend/src/Startup.cs +++ b/backend/src/Startup.cs @@ -206,16 +206,9 @@ private void ConfigureDatabaseContext( DbContextOptionsBuilder options ) { - var connectionStringBuilder = new NpgsqlConnectionStringBuilder(); - connectionStringBuilder.Host = _appSettings.Database.Host; - connectionStringBuilder.Port = _appSettings.Database.Port; - connectionStringBuilder.Database = _appSettings.Database.Name; - connectionStringBuilder.Username = _appSettings.Database.UserName; - connectionStringBuilder.Password = _appSettings.Database.Password; - connectionStringBuilder.MaxPoolSize = 90; options .UseNpgsql( - connectionStringBuilder.ConnectionString, + _appSettings.Database.ConnectionString(), _ => _ // Keep version in sync with the one in ./docker-compose.*.yaml .SetPostgresVersion(18, 4) diff --git a/backend/test/Integration/DatabaseSchemaTests.cs b/backend/test/Integration/DatabaseSchemaTests.cs index 201da275..08a59329 100644 --- a/backend/test/Integration/DatabaseSchemaTests.cs +++ b/backend/test/Integration/DatabaseSchemaTests.cs @@ -1,92 +1,149 @@ using System; -using System.Globalization; -using EfSchemaCompare; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; using FluentAssertions; -using Database.Data; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; +using Npgsql; using NUnit.Framework; -using Serilog; namespace Database.Tests.Integration; [TestFixture] public sealed class DatabaseSchemaTests - : IDisposable + : IntegrationTests { - private sealed class CustomWebApplicationFactory - : WebApplicationFactory - { - protected override void ConfigureWebHost(IWebHostBuilder builder) - { - builder.UseEnvironment(Database.Program.DevelopmentEnvironment); - } - - protected override IHost CreateHost(IHostBuilder builder) - { - builder.UseSerilog((context, services, configuration) => - { - configuration - .ReadFrom.Configuration(context.Configuration) // appsettings.test.json - .WriteTo.NUnitOutput(formatProvider: CultureInfo.InvariantCulture); - }); - return base.CreateHost(builder); - } - } - - private bool _disposed; - private CustomWebApplicationFactory Factory { get; } - - public DatabaseSchemaTests() + /// + /// Compare the database schema wanted by `ApplicationDbContext` to the + /// database schema constructed by `./database.mk create migrate`. The + /// former is the database schema that matches the domain model and the + /// latter is the actual database schema being used. They can get + /// out-of-sync if manual changes to `migrate.sql` introduce + /// inconsistencies or if changes to the domain model were made but no + /// `make migration NAME=...` run yet. + /// + /// Do not use + /// [`EfCore.SchemaCompare`](https://github.com/JonPSmith/EfCore.SchemaCompare) + /// to reverse-engineer the domain model from the PostgreSQL database schema + /// and compare it with the actual one because of its + /// [limitations](https://github.com/JonPSmith/EfCore.SchemaCompare#List-of-limitations). + /// An alternative to + /// [`pg-schema-diff`](https://github.com/stripe/pg-schema-diff) + /// it + /// [`liquibase diff`](https://docs.liquibase.com/community/reference-guide-5-0-3/database-inspection-change-tracking-and-utility-commands/diff) + /// which can be used as follows + /// ``` + /// liquibase diff \ + /// --url="jdbc:postgresql://database:5432/xbase" --username="postgres" --password="postgres" --default-schema-name="database" \ + /// --reference-url="jdbc:postgresql://database:5432/test" --reference-username="postgres" --reference-password="postgres" --reference-default-schema-name="database" + /// ``` + /// and installed as explained on + /// [Install Liquibase on Linux with Debian or Ubuntu](https://docs.liquibase.com/community/get-started-5-0-3/install-liquibase-on-linux-with-debian-or-ubuntu) + /// + [Test] + public async Task EnsureDomainModelMatchesDatabaseSchema() { - Factory = new CustomWebApplicationFactory(); + // Arrange + var cancellationToken = TestContext.CurrentContext.CancellationToken; + var migrateSqlBasedDatabaseName = Guid.NewGuid().ToString().Replace("-", ""); + await ExecuteSqlScriptAsync( + AppSettings.Database.ConnectionString(), + $"""CREATE DATABASE "{migrateSqlBasedDatabaseName}";""", + cancellationToken + ); + await ExecuteSqlScriptAsync( + AppSettings.Database.ConnectionString(migrateSqlBasedDatabaseName), + // the current directory, that is, `./`, is `/home/me/app/test/bin/Debug/net*.0/` + await ReadSqlScriptAsync(Path.Combine("..", "..", "..", "..", "src", "Migrations", "migrate.sql"), cancellationToken), + cancellationToken + ); + // Act + var differenceResponse = await ExecuteCommandAsync( + $""" + pg-schema-diff plan \ + --from-dsn "postgres://{AppSettings.Database.UserName}:{AppSettings.Database.Password}@database/{AppSettings.Database.Name}?search_path={AppSettings.Database.SchemaName}" \ + --to-dsn "postgres://{AppSettings.Database.UserName}:{AppSettings.Database.Password}@database/{migrateSqlBasedDatabaseName}?search_path={AppSettings.Database.SchemaName}" + """, + null, + cancellationToken + ); + // Assert + differenceResponse.ExitCode.Should().Be( + 0, + because: differenceResponse.Output + "\n" + differenceResponse.Diagnostics + "\nIf the test `EnsureMigrationsAreUpToDate` also fails, then run `make migration NAME='...' in the shell `./docker.mk shell SERVICE=backend`. Otherwise, most probably, manual changes to ./migrate.sql introduced inconsistencies: Resolve them by creating an empty migration with `make migration NAME='...' in the shell `./docker.mk shell SERVICE=backend` and adapting the 4 files in ./backend/src/Migrations as needed." + ); } - public void Dispose() + private static Task ReadSqlScriptAsync( + string scriptPath, + CancellationToken cancellationToken + ) { - // Dispose of unmanaged resources. - Dispose(true); - GC.SuppressFinalize(this); + var absoluteScriptPath = Path.GetFullPath(scriptPath); + if (!File.Exists(absoluteScriptPath)) + { + throw new FileNotFoundException($"The script file was not found at: {absoluteScriptPath}"); + } + return File.ReadAllTextAsync(absoluteScriptPath, cancellationToken); } - // https://docs.microsoft.com/en-us/dotnet/standard/managed-code - // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose - ~DatabaseSchemaTests() + private static async Task ExecuteSqlScriptAsync( + string connectionString, + string script, + CancellationToken cancellationToken + ) { - Dispose(false); + await using var connection = new NpgsqlConnection(connectionString); + await connection.OpenAsync(cancellationToken); + // If necessary, the transactions can be started and committed within the script. + // await using var transaction = await connection.BeginTransactionAsync(cancellationToken); + try + { + await using var command = new NpgsqlCommand(script, connection); // , transaction + await command.ExecuteNonQueryAsync(cancellationToken); + // await transaction.CommitAsync(cancellationToken); + } + catch + { + // await transaction.RollbackAsync(cancellationToken); + throw; + } } - public void Dispose(bool disposing) + private static async Task<(int ExitCode, string Output, string Diagnostics)> ExecuteCommandAsync( + string command, + string? input, + CancellationToken cancellationToken + ) { - if (!_disposed) - { - if (disposing) + var process = Process.Start( + new ProcessStartInfo() { - Factory.Dispose(); + FileName = "bash", + ArgumentList = { + "-o", "errexit", + "-o", "errtrace", + "-o", "nounset", + "-o", "pipefail", + "-c", command + }, + ErrorDialog = false, + UseShellExecute = false, + CreateNoWindow = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, } - _disposed = true; + ) ?? throw new InvalidOperationException($"The process for command '{command}' with the input '{input}' failed to start."); + if (input is not null) + { + await process.StandardInput.WriteLineAsync(input.AsMemory(), cancellationToken); + await process.StandardInput.FlushAsync(cancellationToken); } - } - - // For a list of limitations see https://github.com/JonPSmith/EfCore.SchemaCompare#List-of-limitations - [Test] - public void EnsureDomainModelMatchesDatabaseSchema() - { - // Arrange - using var scope = Factory.Services.CreateScope(); - var appSettings = scope.ServiceProvider.GetRequiredService(); - var databaseContext = scope.ServiceProvider.GetRequiredService(); - var connectionString = databaseContext.Database.GetConnectionString() + $";Password={appSettings.Database.Password}"; - var comparer = new CompareEfSql(); - // Act - // Compare C# domain model with PostgreSQL database schema, that is, - // compare EntityFramework Core model of the database with the database - // that the database context's connection points to. - var hasErrors = comparer.CompareEfWithDb(connectionString, databaseContext); - // Assert - hasErrors.Should().BeFalse(comparer.GetAllErrors); + process.StandardInput.Close(); + var output = await process.StandardOutput.ReadToEndAsync(cancellationToken); + var diagnostics = await process.StandardError.ReadToEndAsync(cancellationToken); + await process.WaitForExitAsync(cancellationToken); + return (process.ExitCode, output, diagnostics); } } \ No newline at end of file diff --git a/backend/test/Integration/MigrationTests.cs b/backend/test/Integration/MigrationTests.cs index 26c7852b..d9c7e932 100644 --- a/backend/test/Integration/MigrationTests.cs +++ b/backend/test/Integration/MigrationTests.cs @@ -14,7 +14,14 @@ namespace Database.Tests.Integration; public sealed class MigrationTests : IntegrationTests { - // Inspired by https://www.meziantou.net/detect-missing-migrations-in-entity-framework-core.htm + /// + /// Compare the database snapshot `ApplicationDbContextModelSnapshot.cs` + /// with the database schema wanted by `ApplicationDbContext`. If they are + /// out-of-sync, then `make migration NAME=...` creates a migration and + /// updates the snapshot. + /// + /// Inspired by https://www.meziantou.net/detect-missing-migrations-in-entity-framework-core.htm + /// [Test] public Task EnsureMigrationsAreUpToDate() { diff --git a/database.mk b/database.mk index bf406f50..ce9f21de 100755 --- a/database.mk +++ b/database.mk @@ -88,7 +88,7 @@ sql : ## Run the SQL script in the file `${SCRIPT}` in the database service, for .PHONY : sql migrate : SCRIPT = ./backend/src/Migrations/migrate.sql -migrate : ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql +migrate : ## Migrate database by running the idempotent SQL script ./backend/src/Migrations/migrate.sql $(MAKE) --file="${SELF}" sql SCRIPT="${SCRIPT}" docker compose restart \ --no-deps \ diff --git a/frontend/components/methods/AppliedMethodView.tsx b/frontend/components/methods/AppliedMethodView.tsx index db959a5d..49c35682 100644 --- a/frontend/components/methods/AppliedMethodView.tsx +++ b/frontend/components/methods/AppliedMethodView.tsx @@ -7,7 +7,6 @@ import { AppliedMethodPartialFragment, ToTreeVertexAppliedConversionMethodPartialFragment, } from "../../queries/data.generated"; -import DateTimeX from "../DateTimeX"; const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ uuid: id, @@ -55,7 +54,7 @@ export default function AppliedMethodView({ )}`, + name: `data ${item.value.dataId} in database ${item.value.database?.name ?? item.value.databaseId} at timestamp ${item.value.dataTimestamp}`, }} route={(id) => paths.metabase.data( From 622cda4de3bd9827962b47c31fecbcea1ecb8ae3 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 26 Jun 2026 14:39:50 +0200 Subject: [PATCH 856/878] Adapt the help target such that it does not choke on the character `:` when used within a target description --- backend/Makefile | 2 +- database.mk | 2 +- deploy.mk | 2 +- docker.mk | 2 +- forge.mk | 2 +- frontend/Makefile | 2 +- gpg.mk | 2 +- tools.mk | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/Makefile b/backend/Makefile index eb0258aa..f9c8f072 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -7,7 +7,7 @@ MAKEFLAGS += --warn-undefined-variables # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/database.mk b/database.mk index ce9f21de..9e454f8c 100755 --- a/database.mk +++ b/database.mk @@ -14,7 +14,7 @@ files_archive_name = files.gz # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/deploy.mk b/deploy.mk index f3772492..9c51cf0a 100755 --- a/deploy.mk +++ b/deploy.mk @@ -20,7 +20,7 @@ dotenv_linter = \ # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/docker.mk b/docker.mk index acfe2f5a..1a2e6cf7 100755 --- a/docker.mk +++ b/docker.mk @@ -20,7 +20,7 @@ dotenv_linter = \ # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/forge.mk b/forge.mk index 0f099f37..7aff7f1d 100755 --- a/forge.mk +++ b/forge.mk @@ -10,7 +10,7 @@ TARGET = $(shell git rev-parse --verify HEAD) # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/frontend/Makefile b/frontend/Makefile index dc30d3ac..fda168a0 100644 --- a/frontend/Makefile +++ b/frontend/Makefile @@ -7,7 +7,7 @@ MAKEFLAGS += --warn-undefined-variables # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/gpg.mk b/gpg.mk index 6afa2452..5e1f0e1a 100755 --- a/gpg.mk +++ b/gpg.mk @@ -12,7 +12,7 @@ MAKEFLAGS += --warn-undefined-variables # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help diff --git a/tools.mk b/tools.mk index b8c25fbd..f6d82aaa 100755 --- a/tools.mk +++ b/tools.mk @@ -19,7 +19,7 @@ dotenv_linter = \ # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help - @awk -F ':|##' '/^[^\t].+?:.*?##/ {\ + @awk -F ':.*?## ' '/^[^\t].+?:.*?##/ {\ printf "\033[36m%-30s\033[0m %s\n", $$1, $$NF \ }' $(MAKEFILE_LIST) .PHONY : help From 21aad4d98e274c6c2ec7a0d1af0db7d469f7dbf0 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 26 Jun 2026 18:55:20 +0200 Subject: [PATCH 857/878] Correct error message when Makefile is improperly linked --- deploy.mk | 2 +- docker.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.mk b/deploy.mk index 9c51cf0a..dd74e59a 100755 --- a/deploy.mk +++ b/deploy.mk @@ -116,7 +116,7 @@ restart : ## Restart service `${SERVICE}` and await its health symlink : ## Confirm that ./Makefile links to ./docker.mk and that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml if [[ ! -L "./Makefile" ]] || [[ ! "./Makefile" -ef "./docker.mk" ]]; then \ - echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + echo "./Makefile does not link to ./docker.mk" >&2 ; \ exit 1 ; \ fi if [[ "${ENVIRONMENT}" == "staging" ]]; then \ diff --git a/docker.mk b/docker.mk index 1a2e6cf7..bdd9f79b 100755 --- a/docker.mk +++ b/docker.mk @@ -32,7 +32,7 @@ environment : ## Print value of variable `ENVIRONMENT` symlink : ## Confirm that ./Makefile links to ./docker.mk and that ./docker-compose.yaml links to the correct ./docker-compose.*.yaml if [[ ! -L "./Makefile" ]] || [[ ! "./Makefile" -ef "./docker.mk" ]]; then \ - echo "./docker-compose.yaml does not link to $${file}" >&2 ; \ + echo "./Makefile does not link to ./docker.mk" >&2 ; \ exit 1 ; \ fi if [[ "${ENVIRONMENT}" == "staging" ]]; then \ From 991ad3a274aacc1a58407643d042b80e5d20ae81 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 26 Jun 2026 19:19:55 +0200 Subject: [PATCH 858/878] Ask metabase for client ID or authorized party of access token to use it for applying access policies --- .../Queries/CurrentUserOrApplication.graphql | 1 + .../QueryCurrentUserOrApplication.cs | 3 +- .../Authentication/AuthenticationHandler.cs | 4 +- .../src/Authorization/CommonAuthorization.cs | 12 +++--- .../Enumerations/OpticalComponentSubtype.cs | 31 +++++++-------- .../Institutions/InstitutionQueries.cs | 4 +- .../OpenIdConnect/OpenIdConnectQueries.cs | 38 +++++++++++++++++++ .../OpenIdConnectionApplicationQueries.cs | 25 ------------ backend/src/GraphQl/Users/UserQueries.cs | 4 +- backend/src/Services/UserService.cs | 8 ++-- .../GraphQlSchemaTests.IsUnchanged.snap | 32 ++++++++-------- frontend/type-defs.graphqls | 32 ++++++++-------- 12 files changed, 107 insertions(+), 87 deletions(-) create mode 100644 backend/src/GraphQl/OpenIdConnect/OpenIdConnectQueries.cs delete mode 100644 backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs diff --git a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql index dcb0acfb..ffcb85f3 100644 --- a/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql +++ b/backend/src/ApiRequests/Queries/CurrentUserOrApplication.graphql @@ -1,4 +1,5 @@ query CurrentUserOrApplication($databaseId: Uuid!) { + currentOpenIdConnectTokenClientId currentUser { uuid name diff --git a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs index 154118ed..08894bae 100644 --- a/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs +++ b/backend/src/ApiRequests/QueryCurrentUserOrApplication.cs @@ -32,7 +32,7 @@ ILogger logger { private const string QueryFileName = "CurrentUserOrApplication.graphql"; - public static readonly CurrentUserOrApplication Empty = new(null, null); + public static readonly CurrentUserOrApplication Empty = new(null, null, null); public Uri GetGraphQlEndpoint => appSettings.MetabaseGraphQlEndpoint; @@ -115,6 +115,7 @@ CurrentInstitution Owner ); public sealed record CurrentUserOrApplication( + string? currentOpenIdConnectTokenClientId, CurrentUser? CurrentUser, CurrentOpenIdConnectApplication? CurrentApplication ) diff --git a/backend/src/Authentication/AuthenticationHandler.cs b/backend/src/Authentication/AuthenticationHandler.cs index 42afcbf9..a4247344 100644 --- a/backend/src/Authentication/AuthenticationHandler.cs +++ b/backend/src/Authentication/AuthenticationHandler.cs @@ -166,8 +166,8 @@ CancellationToken cancellationToken // (another one than `appSettings.OpenIdConnectClient.Id`) and uses // this token to authenticate (and authorize) access to this database. var metabaseAuthenticateResult = await userService.SwitchUserOrApplicationAsync( - user => Task.FromResult(user is null ? null : CreateSuccessfulAuthenticateResult($"user:{user.Uuid.ToString("D")}", null)), - application => Task.FromResult(CreateSuccessfulAuthenticateResult($"client:{application.ClientId}", application.ClientId)), + (user, clientId) => Task.FromResult(user is null ? null : CreateSuccessfulAuthenticateResult(user.Uuid.ToString("D"), clientId)), + (application, clientId) => Task.FromResult(CreateSuccessfulAuthenticateResult($"client:{application.ClientId}", clientId)), cancellationToken ); if (metabaseAuthenticateResult is { Succeeded: true, Principal.Identity.IsAuthenticated: true }) diff --git a/backend/src/Authorization/CommonAuthorization.cs b/backend/src/Authorization/CommonAuthorization.cs index 3d5cc236..bbcb4668 100644 --- a/backend/src/Authorization/CommonAuthorization.cs +++ b/backend/src/Authorization/CommonAuthorization.cs @@ -17,8 +17,8 @@ UserService userService private const string UNAUTHORIZED_CODE = "AUTH_NOT_AUTHENTICATED"; public Task SwitchUserOrApplicationAsync( - Func> handleUser, - Func> handleApplication, + Func> handleUser, + Func> handleApplication, CancellationToken cancellationToken ) { @@ -33,8 +33,8 @@ CancellationToken cancellationToken public Task IsAuthenticated(CancellationToken cancellationToken) { return userService.SwitchUserOrApplicationAsync( - user => Task.FromResult(user is not null), - application => Task.FromResult(application is not null), + (user, _) => Task.FromResult(user is not null), + (application, _) => Task.FromResult(application is not null), cancellationToken ); } @@ -42,11 +42,11 @@ public Task IsAuthenticated(CancellationToken cancellationToken) public Task IsDatabaseOperator(CancellationToken cancellationToken) { return userService.SwitchUserOrApplicationAsync( - user => Task.FromResult( + (user, _) => Task.FromResult( user is not null && user.IsAtLeastAssistantManagerOfDatabaseOperator() ), - application => Task.FromResult( + (application, _) => Task.FromResult( application.Owner.IsDatabaseOperator() ), cancellationToken diff --git a/backend/src/Enumerations/OpticalComponentSubtype.cs b/backend/src/Enumerations/OpticalComponentSubtype.cs index f0751192..fbeb869e 100644 --- a/backend/src/Enumerations/OpticalComponentSubtype.cs +++ b/backend/src/Enumerations/OpticalComponentSubtype.cs @@ -5,26 +5,27 @@ namespace Database.Enumerations; [SuppressMessage("Naming", "CA1707")] public enum OpticalComponentSubtype { - MONOLITHIC, - LAMINATE, - INTERLAYER, - EMBEDDED_COATING, + ACID_ETCHED_GLASS, + APPLIED_FILM, + CELLULAR_SHADE, + CHROMOGENIC, COATED, COATING, - APPLIED_FILM, - FILM, - VENETIAN_BLIND, DIFFUSING_SHADE, - ROLLER_SHADE, - WOVEN_SHADE, - VERTICAL_LOUVER, + EMBEDDED_COATING, + FILM, + FRITTED_GLASS, + INTERLAYER, + LAMINATE, + MONOLITHIC, PERFORATED_SCREEN, - CELLULAR_SHADE, PLEATED_SHADE, + ROLLER_SHADE, ROMAN_SHADE, - SHADE_MATERIAL, - FRITTED_GLASS, - ACID_ETCHED_GLASS, SANDBLASTED_GLASS, - CHROMOGENIC + SHADE_MATERIAL, + SOLAR_SCREEN, + VENETIAN_BLIND, + VERTICAL_LOUVER, + WOVEN_SHADE } \ No newline at end of file diff --git a/backend/src/GraphQl/Institutions/InstitutionQueries.cs b/backend/src/GraphQl/Institutions/InstitutionQueries.cs index fb39759c..f6c8e27f 100644 --- a/backend/src/GraphQl/Institutions/InstitutionQueries.cs +++ b/backend/src/GraphQl/Institutions/InstitutionQueries.cs @@ -17,8 +17,8 @@ CancellationToken cancellationToken ) { return authorization.SwitchUserOrApplicationAsync( - user => Task.FromResult(null), - application => Task.FromResult(application.Owner), + (user, _) => Task.FromResult(null), + (application, _) => Task.FromResult(application.Owner), cancellationToken ); } diff --git a/backend/src/GraphQl/OpenIdConnect/OpenIdConnectQueries.cs b/backend/src/GraphQl/OpenIdConnect/OpenIdConnectQueries.cs new file mode 100644 index 00000000..b3b4a5e1 --- /dev/null +++ b/backend/src/GraphQl/OpenIdConnect/OpenIdConnectQueries.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; +using HotChocolate.Types; +using Database.Authorization; +using Database.ApiRequests; +using HotChocolate.Authorization; + +namespace Database.GraphQl.OpenIdConnect; + +[ExtendObjectType(nameof(Query))] +public sealed class OpenIdConnectQueries +{ + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentOpenIdConnectTokenClientIdAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrApplicationAsync( + (_, clientId) => Task.FromResult(clientId), + (_, clientId) => Task.FromResult(clientId), + cancellationToken + ); + } + + [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] + public Task GetCurrentOpenIdConnectApplicationAsync( + CommonAuthorization authorization, + CancellationToken cancellationToken + ) + { + return authorization.SwitchUserOrApplicationAsync( + (user, _) => Task.FromResult(null), + (application, _) => Task.FromResult(application), + cancellationToken + ); + } +} \ No newline at end of file diff --git a/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs b/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs deleted file mode 100644 index 0ea6c8ad..00000000 --- a/backend/src/GraphQl/OpenIdConnectApplications/OpenIdConnectionApplicationQueries.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using HotChocolate.Types; -using Database.Authorization; -using Database.ApiRequests; -using HotChocolate.Authorization; - -namespace Database.GraphQl.Applications; - -[ExtendObjectType(nameof(Query))] -public sealed class ApplicationQueries -{ - [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public Task GetCurrentOpenIdConnectApplicationAsync( - CommonAuthorization authorization, - CancellationToken cancellationToken - ) - { - return authorization.SwitchUserOrApplicationAsync( - user => Task.FromResult(null), - application => Task.FromResult(application), - cancellationToken - ); - } -} \ No newline at end of file diff --git a/backend/src/GraphQl/Users/UserQueries.cs b/backend/src/GraphQl/Users/UserQueries.cs index 203a9409..b331edad 100644 --- a/backend/src/GraphQl/Users/UserQueries.cs +++ b/backend/src/GraphQl/Users/UserQueries.cs @@ -19,8 +19,8 @@ CancellationToken cancellationToken ) { return authorization.SwitchUserOrApplicationAsync( - user => Task.FromResult(user), - application => Task.FromResult(null), + (user, _) => Task.FromResult(user), + (application, _) => Task.FromResult(null), cancellationToken ); } diff --git a/backend/src/Services/UserService.cs b/backend/src/Services/UserService.cs index d35f233e..ecc6564d 100644 --- a/backend/src/Services/UserService.cs +++ b/backend/src/Services/UserService.cs @@ -31,19 +31,19 @@ CacheService cacheService } public async Task SwitchUserOrApplicationAsync( - Func> handleUser, - Func> handleApplication, + Func> handleUser, + Func> handleApplication, CancellationToken cancellationToken ) { var userOrApplication = await FetchCurrentUserOrApplicationAsync(cancellationToken); if (userOrApplication.CurrentApplication is not null) { - return await handleApplication(userOrApplication.CurrentApplication); + return await handleApplication(userOrApplication.CurrentApplication, userOrApplication.currentOpenIdConnectTokenClientId); } else { - return await handleUser(userOrApplication.CurrentUser); + return await handleUser(userOrApplication.CurrentUser, userOrApplication.currentOpenIdConnectTokenClientId); } } diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 2433c89f..6d054a21 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -267,6 +267,7 @@ type Query { currentInstitution: CurrentInstitution @cost(weight: "10") currentOpenIdConnectApplication: CurrentOpenIdConnectApplication @cost(weight: "10") + currentOpenIdConnectTokenClientId: String @cost(weight: "10") currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") @@ -3740,28 +3741,29 @@ enum LogicalCombinator { } enum OpticalComponentSubtype { - MONOLITHIC - LAMINATE - INTERLAYER - EMBEDDED_COATING + ACID_ETCHED_GLASS + APPLIED_FILM + CELLULAR_SHADE + CHROMOGENIC COATED COATING - APPLIED_FILM - FILM - VENETIAN_BLIND DIFFUSING_SHADE - ROLLER_SHADE - WOVEN_SHADE - VERTICAL_LOUVER + EMBEDDED_COATING + FILM + FRITTED_GLASS + INTERLAYER + LAMINATE + MONOLITHIC PERFORATED_SCREEN - CELLULAR_SHADE PLEATED_SHADE + ROLLER_SHADE ROMAN_SHADE - SHADE_MATERIAL - FRITTED_GLASS - ACID_ETCHED_GLASS SANDBLASTED_GLASS - CHROMOGENIC + SHADE_MATERIAL + SOLAR_SCREEN + VENETIAN_BLIND + VERTICAL_LOUVER + WOVEN_SHADE } enum OpticalComponentType { diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 2433c89f..6d054a21 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -267,6 +267,7 @@ type Query { currentInstitution: CurrentInstitution @cost(weight: "10") currentOpenIdConnectApplication: CurrentOpenIdConnectApplication @cost(weight: "10") + currentOpenIdConnectTokenClientId: String @cost(weight: "10") currentUser: CurrentUser @cost(weight: "10") currentUserInfo: UserInfo! @cost(weight: "10") data(dataKind: DataKind!, id: Uuid!, locale: Locale): Data @cost(weight: "10") @@ -3740,28 +3741,29 @@ enum LogicalCombinator { } enum OpticalComponentSubtype { - MONOLITHIC - LAMINATE - INTERLAYER - EMBEDDED_COATING + ACID_ETCHED_GLASS + APPLIED_FILM + CELLULAR_SHADE + CHROMOGENIC COATED COATING - APPLIED_FILM - FILM - VENETIAN_BLIND DIFFUSING_SHADE - ROLLER_SHADE - WOVEN_SHADE - VERTICAL_LOUVER + EMBEDDED_COATING + FILM + FRITTED_GLASS + INTERLAYER + LAMINATE + MONOLITHIC PERFORATED_SCREEN - CELLULAR_SHADE PLEATED_SHADE + ROLLER_SHADE ROMAN_SHADE - SHADE_MATERIAL - FRITTED_GLASS - ACID_ETCHED_GLASS SANDBLASTED_GLASS - CHROMOGENIC + SHADE_MATERIAL + SOLAR_SCREEN + VENETIAN_BLIND + VERTICAL_LOUVER + WOVEN_SHADE } enum OpticalComponentType { From 870acae8dbaa7cec0cc99a1821c0431f149839a1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 26 Jun 2026 19:24:39 +0200 Subject: [PATCH 859/878] Add migration that adds `SOLAR_SCREEN` to the component subtype enumeration --- ...dSolarScreenToComponentSubtype.Designer.cs | 3556 +++++++++++++++++ ...170958_AddSolarScreenToComponentSubtype.cs | 26 + .../ApplicationDbContextModelSnapshot.cs | 4 +- backend/src/Migrations/migrate.sql | 20 + ...70958_AddSolarScreenToComponentSubtype.sql | 19 + 5 files changed, 3623 insertions(+), 2 deletions(-) create mode 100644 backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs create mode 100644 backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.cs create mode 100644 backend/src/Migrations/migrate_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260626170958_AddSolarScreenToComponentSubtype.sql diff --git a/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs new file mode 100644 index 00000000..4739056b --- /dev/null +++ b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs @@ -0,0 +1,3556 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260626170958_AddSolarScreenToComponentSubtype")] + partial class AddSolarScreenToComponentSubtype + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "solar_screen", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"); + + t.HasTrigger("calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_calorimetric_data_access_policy_if_necessary"); + }); + + b + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") WHERE \"CalorimetricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_geometric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("geometric_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") WHERE \"GeometricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") WHERE \"HygrothermalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("life_cycle_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") WHERE \"LifeCycleDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_optical_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("optical_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") WHERE \"OpticalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("photovoltaic_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") WHERE \"PhotovoltaicDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.cs b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.cs new file mode 100644 index 00000000..6ae8c42b --- /dev/null +++ b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class AddSolarScreenToComponentSubtype : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,solar_screen,venetian_blind,vertical_louver,woven_shade") + .OldAnnotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,venetian_blind,vertical_louver,woven_shade") + .OldAnnotation("Npgsql:Enum:database.optical_component_subtype", "acid_etched_glass,applied_film,cellular_shade,chromogenic,coated,coating,diffusing_shade,embedded_coating,film,fritted_glass,interlayer,laminate,monolithic,perforated_screen,pleated_shade,roller_shade,roman_shade,sandblasted_glass,shade_material,solar_screen,venetian_blind,vertical_louver,woven_shade"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index cc268f2b..9002ee18 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; @@ -29,7 +29,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "solar_screen", "venetian_blind", "vertical_louver", "woven_shade" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index d1b8a30e..07befcdb 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -3469,6 +3469,7 @@ BEGIN VALUES ('20260611161712_AssertExistenceOfAccessPolicyAndRootResourceInPostgreSql', '10.0.8'); END IF; END $EF$; + COMMIT; START TRANSACTION; @@ -3496,3 +3497,22 @@ BEGIN END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260626170958_AddSolarScreenToComponentSubtype') THEN + ALTER TYPE database.optical_component_subtype ADD VALUE 'solar_screen' AFTER 'shade_material'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260626170958_AddSolarScreenToComponentSubtype') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260626170958_AddSolarScreenToComponentSubtype', '10.0.9'); + END IF; +END $EF$; + +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260626170958_AddSolarScreenToComponentSubtype.sql b/backend/src/Migrations/migrate_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260626170958_AddSolarScreenToComponentSubtype.sql new file mode 100644 index 00000000..14c31338 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260620114038_MakeCoatedSideOfOpticalDataNonNullable_to_20260626170958_AddSolarScreenToComponentSubtype.sql @@ -0,0 +1,19 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260626170958_AddSolarScreenToComponentSubtype') THEN + ALTER TYPE database.optical_component_subtype ADD VALUE 'solar_screen' AFTER 'shade_material'; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260626170958_AddSolarScreenToComponentSubtype') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260626170958_AddSolarScreenToComponentSubtype', '10.0.9'); + END IF; +END $EF$; + +COMMIT; + From 76a7e8878b15c5152444794b3492178bf58a1e71 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Fri, 26 Jun 2026 19:55:37 +0200 Subject: [PATCH 860/878] Make Git ignore deploy history files --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index d5a8ad8a..b47ab214 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ /machine/*.retry /nginx/html/maintenance.html /tags* -/.stored-target +/.deploy-history /.env # Compiled output From 7cd96892fd1c840454b90460c22c537fadb54b08 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 27 Jun 2026 13:37:36 +0200 Subject: [PATCH 861/878] Display data access policy on data page --- .../OpenIdConnectApplicationDataLoader.cs | 3 +- .../Queries/OpenIdConnectApplications.graphql | 1 + .../AccessPolicies/DataAccessPolicyType.cs | 3 +- .../InstitutionAccessPolicyType.cs | 13 ++ ...penIdConnectApplicationAccessPolicyType.cs | 16 +++ .../AccessPolicies/UserAccessPolicyType.cs | 13 ++ .../GraphQlSchemaTests.IsUnchanged.snap | 15 ++- frontend/components/LazyTabs.tsx | 74 +++++++++++ frontend/components/TabLabel.tsx | 21 +++ .../AccessPolicySummaryBase.tsx | 64 +++++++++ .../accessPolicies/DataAccessPolicy.tsx | 122 ++++++++++++++++++ .../DataAccessPolicySummary.tsx | 40 ++++++ .../InstitutionAccessPolicySummary.tsx | 23 ++++ ...dConnectApplicationAccessPolicySummary.tsx | 27 ++++ .../UserAccessPolicySummary.tsx | 23 ++++ .../data/calorimetric/CalorimetricData.tsx | 15 ++- .../data/geometric/GeometricData.tsx | 15 ++- .../data/hygrothermal/HygrothermalData.tsx | 15 ++- .../data/lifeCycle/LifeCycleData.tsx | 15 ++- .../components/data/optical/OpticalData.tsx | 14 +- .../data/photovoltaic/PhotovoltaicData.tsx | 15 ++- frontend/components/databases/Database.tsx | 9 +- frontend/pages/user-info.tsx | 9 +- frontend/paths.ts | 26 ++++ frontend/queries/accessPolicies.graphql | 112 ++++++++++++++++ frontend/type-defs.graphqls | 15 ++- 26 files changed, 671 insertions(+), 47 deletions(-) create mode 100644 frontend/components/LazyTabs.tsx create mode 100644 frontend/components/TabLabel.tsx create mode 100644 frontend/components/accessPolicies/AccessPolicySummaryBase.tsx create mode 100644 frontend/components/accessPolicies/DataAccessPolicy.tsx create mode 100644 frontend/components/accessPolicies/DataAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/UserAccessPolicySummary.tsx create mode 100644 frontend/queries/accessPolicies.graphql diff --git a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs index 0da826b3..e512b342 100644 --- a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -17,11 +17,12 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => QueryByIdDataLoader.GetGraphQlEndpoint(appSettings); public sealed record OpenIdConnectApplication( + Guid Uuid, [property: GraphQLIgnore] string Id, string? Name ) : IIdNode { - string ClientId => Id; + public string ClientId => Id; } private sealed record OpenIdConnectApplicationsData( diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql index 02ab6f06..ccd43d7d 100644 --- a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -2,6 +2,7 @@ query OpenIdConnectApplications($ids: [String!]!) { connection: openIdConnectApplications(where: { clientId: { in: $ids } }) { edges { node { + uuid id: clientId name: displayName } diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index 3f31c643..d2d920e1 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -8,6 +8,7 @@ using Database.Data.AccessPolicies; using Database.GraphQl.DataX; using Database.GraphQl.Extensions; +using Database.GraphQl.Entities; using GreenDonut; using GreenDonut.Data; using HotChocolate; @@ -18,7 +19,7 @@ namespace Database.GraphQl.AccessPolicies; public sealed class DataAccessPolicyType - : ObjectType + : EntityType { protected override void Configure( IObjectTypeDescriptor descriptor diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs index c192bcaf..deb645be 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyType.cs @@ -2,6 +2,7 @@ using Database.ApiRequests; using Database.Data.AccessPolicies; using Database.Extensions; +using GreenDonut; using HotChocolate; using HotChocolate.Types; @@ -15,6 +16,10 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(_ => _.DataAccessPolicy) + .Type>>() + .ResolveWith(_ => Resolvers.GetDataAccessPolicyAsync(default!, default!)); descriptor .Field(nameof(InstitutionAccessPolicy.InstitutionId)[..^2].FirstCharToLower()) .Type>() @@ -24,6 +29,14 @@ IObjectTypeDescriptor descriptor private sealed class Resolvers { + public static Task GetDataAccessPolicyAsync( + [Parent] InstitutionAccessPolicy parent, + IDataAccessPolicyByIdDataLoader byId + ) + { + return byId.LoadRequiredAsync(parent.DataAccessPolicyId); + } + public static Task GetInstitutionAsync( [Parent] InstitutionAccessPolicy parent, IInstitutionByIdDataLoader byId diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs index baf1a902..7dff3fb0 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyType.cs @@ -2,6 +2,7 @@ using Database.ApiRequests; using Database.Data.AccessPolicies; using Database.Extensions; +using GreenDonut; using HotChocolate; using HotChocolate.Types; @@ -15,6 +16,13 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(_ => _.DataAccessPolicyId) + .Ignore(); + descriptor + .Field(_ => _.DataAccessPolicy) + .Type>>() + .ResolveWith(_ => Resolvers.GetDataAccessPolicyAsync(default!, default!)); descriptor .Field(nameof(OpenIdConnectApplicationAccessPolicy.ClientId)[..^2].FirstCharToLower()) .Type>() @@ -24,6 +32,14 @@ IObjectTypeDescriptor descriptor private sealed class Resolvers { + public static Task GetDataAccessPolicyAsync( + [Parent] OpenIdConnectApplicationAccessPolicy parent, + IDataAccessPolicyByIdDataLoader byId + ) + { + return byId.LoadRequiredAsync(parent.DataAccessPolicyId); + } + public static Task GetOpenIdConnectApplicationAsync( [Parent] OpenIdConnectApplicationAccessPolicy parent, IOpenIdConnectApplicationByClientIdDataLoader byClientId diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs index cdd0c528..b76e42cb 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyType.cs @@ -2,6 +2,7 @@ using Database.ApiRequests; using Database.Data.AccessPolicies; using Database.Extensions; +using GreenDonut; using HotChocolate; using HotChocolate.Types; @@ -15,6 +16,10 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); + descriptor + .Field(_ => _.DataAccessPolicy) + .Type>>() + .ResolveWith(_ => Resolvers.GetDataAccessPolicyAsync(default!, default!)); descriptor .Field(nameof(UserAccessPolicy.UserId)[..^2].FirstCharToLower()) .Type>() @@ -24,6 +29,14 @@ IObjectTypeDescriptor descriptor private sealed class Resolvers { + public static Task GetDataAccessPolicyAsync( + [Parent] UserAccessPolicy parent, + IDataAccessPolicyByIdDataLoader byId + ) + { + return byId.LoadRequiredAsync(parent.DataAccessPolicyId); + } + public static Task GetUserAsync( [Parent] UserAccessPolicy parent, IUserByIdDataLoader byId diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 6d054a21..f3342da3 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -872,11 +872,11 @@ of the time span or when a policy is updated via the respective 'set' mutation. Note that durations are represented in the [ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ -type DataAccessPolicy { +type DataAccessPolicy implements Node { combinator: LogicalCombinator! createdAt: DateTime! data: Data @cost(weight: "0") - id: Uuid! + id: ID! institutionAccessPolicies( order: [InstitutionAccessPolicySortInput!] where: InstitutionAccessPolicyPropositionInput @@ -898,7 +898,7 @@ type DataAccessPolicy { order: [UserAccessPolicySortInput!] where: UserAccessPolicyPropositionInput ): [UserAccessPolicy!]! - version: NonNegativeInt! + uuid: Uuid! } "A connection to a list of items." @@ -1168,7 +1168,7 @@ type Institution { type InstitutionAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") dataAccessPolicyId: Uuid! id: ID! institution: Institution @cost(weight: "3") @@ -1294,7 +1294,9 @@ type OpenEndedDateTimeRange { } type OpenIdConnectApplication { + clientId: String! name: String + uuid: Uuid! } type OpenIdConnectApplicationAccessPolicy implements Node { @@ -1302,8 +1304,7 @@ type OpenIdConnectApplicationAccessPolicy implements Node { client: OpenIdConnectApplication @cost(weight: "3") clientId: String! createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy - dataAccessPolicyId: Uuid! + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") id: ID! isAccessAllowed(openIdConnectClientId: String): Boolean! isAlwaysAllowed: Boolean! @@ -1735,7 +1736,7 @@ type User { type UserAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") dataAccessPolicyId: Uuid! id: ID! isAccessAllowed(userId: Uuid): Boolean! diff --git a/frontend/components/LazyTabs.tsx b/frontend/components/LazyTabs.tsx new file mode 100644 index 00000000..f2a95afc --- /dev/null +++ b/frontend/components/LazyTabs.tsx @@ -0,0 +1,74 @@ +import React, { useState, useMemo } from "react"; +import { GetProp, Tabs, TabsProps } from "antd"; +import TabLabel from "./TabLabel"; + +type TabItem = GetProp[number]; + +type TabItemWithCount = TabItem & { + count?: number; +}; + +export type LazyTabsProps = Omit & { + items?: TabItemWithCount[]; +}; + +/** + * Wrapper that tracks if a tab has ever been "active". Once initialized, it + * stays mounted. + */ +function LazyWrapper({ + active, + children, +}: { + active: boolean; + children: React.ReactNode; +}) { + const [initialized, setInitialized] = useState(false); + if (active && !initialized) { + setInitialized(true); + } + return initialized ? children : null; +} + +export default function LazyTabs({ items, onChange, ...props }: LazyTabsProps) { + const [activeKey, setActiveKey] = useState(() => { + return ( + props.activeKey || + props.defaultActiveKey || + (items?.filter((item) => item.count && item.count > 0)?.[0] + ?.key as string) || + (items?.[0].key as string) + ); + }); + + const handleTabChange = (key: string) => { + setActiveKey(key); + onChange?.(key); + }; + + const lazyItems = useMemo(() => { + return items?.map((item) => ({ + ...item, + label: + item.count === undefined ? ( + item.label + ) : ( + + ), + children: ( + + {item.children} + + ), + })); + }, [items, activeKey]); + + return ( + + ); +} diff --git a/frontend/components/TabLabel.tsx b/frontend/components/TabLabel.tsx new file mode 100644 index 00000000..b8b366ed --- /dev/null +++ b/frontend/components/TabLabel.tsx @@ -0,0 +1,21 @@ +import { Space, Badge, Typography } from "antd"; + +export default function TabLabel({ + name, + count, +}: { + name: React.ReactNode; + count?: React.ReactNode; +}) { + return count == 0 ? ( + + {name} + + + ) : ( + + {name} + {count != null && } + + ); +} diff --git a/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx b/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx new file mode 100644 index 00000000..40c59eb0 --- /dev/null +++ b/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx @@ -0,0 +1,64 @@ +import { Statistic, Tag } from "antd"; +import { + InstitutionAccessPolicyPartialFragment, + OpenIdConnectApplicationAccessPolicyPartialFragment, + UserAccessPolicyPartialFragment, +} from "../../queries/accessPolicies.generated"; +import EntitySummary from "../entities/EntitySummary"; +import dayjs from "dayjs"; +import durationPlugin from "dayjs/plugin/duration"; +import { Route } from "next"; +import { Scalars } from "../../__generated__/graphql"; +import { isTruthy } from "../../lib/array"; + +dayjs.extend(durationPlugin); + +export default function AccessPolicySummaryBase({ + entity, + stakeholder, + route, +}: { + entity: + | UserAccessPolicyPartialFragment + | InstitutionAccessPolicyPartialFragment + | OpenIdConnectApplicationAccessPolicyPartialFragment; + stakeholder: { + uuid: Scalars["Uuid"]["output"]; + name?: string | null; + }; + route?: (id: Scalars["Uuid"]["output"]) => Route; +}) { + const duration = + entity.upperAccessLimitPerTimeDuration?.duration == null + ? null + : dayjs.duration(entity.upperAccessLimitPerTimeDuration?.duration); + const startTime = + entity.accessCountSinceStartTime?.startTime == null + ? null + : dayjs(entity.accessCountSinceStartTime?.startTime); + const endTime = duration == null ? null : startTime?.add(duration); + + return ( + + Always Allowed + + ), + ].filter(isTruthy)} + > +
    + + from {startTime?.format("YYYY-MM-DD HH:mm") ?? "-∞"} until{" "} + {endTime?.format("YYYY-MM-DD HH:mm") ?? "∞"} +
    +
    + ); +} diff --git a/frontend/components/accessPolicies/DataAccessPolicy.tsx b/frontend/components/accessPolicies/DataAccessPolicy.tsx new file mode 100644 index 00000000..156017ee --- /dev/null +++ b/frontend/components/accessPolicies/DataAccessPolicy.tsx @@ -0,0 +1,122 @@ +import { Scalars } from "../../__generated__/graphql"; +import { + DataAccessPolicyDocument, + DataAccessPolicyPartialFragment, +} from "../../queries/accessPolicies.generated"; +import { Skeleton, Result, Card, Button } from "antd"; +import { useQuery } from "@apollo/client/react"; +import { useQueryHandler } from "../../lib/hooks/useQueryHandler"; +import DataAccessPolicySummary from "./DataAccessPolicySummary"; +import EntityList from "../entities/EntityList"; +import EntityItem from "../entities/EntityItem"; +import LazyTabs, { LazyTabsProps } from "../LazyTabs"; +import UserAccessPolicySummary from "./UserAccessPolicySummary"; +import OpenIdConnectApplicationAccessPolicySummary from "./OpenIdConnectApplicationAccessPolicySummary"; +import InstitutionAccessPolicySummary from "./InstitutionAccessPolicySummary"; +import { useMemo } from "react"; + +const getTabs = ( + entity: DataAccessPolicyPartialFragment, +): LazyTabsProps["items"] => [ + { + key: "userAccessPolicies", + count: entity.userAccessPolicies.length, + label: "User Access Policies", + children: ( + {}} + renderItem={(node) => ( + + + + )} + /> + ), + }, + { + key: "institutionAccessPolicies", + count: entity.institutionAccessPolicies.length, + label: "Institution Access Policies", + children: ( + {}} + renderItem={(node) => ( + + + + )} + /> + ), + }, + { + key: "openIdConnectApplicationAccessPolicies", + count: entity.openIdConnectApplicationAccessPolicies.length, + label: "OpenID Connect Application Access Policies", + children: ( + {}} + renderItem={(node) => ( + + + + )} + /> + ), + }, +]; + +interface DataAccessPolicyProps { + dataId: Scalars["Uuid"]["input"] | null | undefined; +} + +export default function DataAccessPolicy({ dataId }: DataAccessPolicyProps) { + const queryVariables = { + dataId, + }; + const { loading, error, data, refetch } = useQuery(DataAccessPolicyDocument, { + variables: queryVariables, + }); + useQueryHandler({ error }); + + const entity = data?.data; + + const tabs = useMemo(() => (!entity ? null : getTabs(entity)), [entity]); + + if (loading) { + return ; + } + + if (!entity) { + return ( + refetch()}> + Reload + + } + /> + ); + } + + return ( +
    + + + + {/* */} + {tabs && } +
    + ); +} diff --git a/frontend/components/accessPolicies/DataAccessPolicySummary.tsx b/frontend/components/accessPolicies/DataAccessPolicySummary.tsx new file mode 100644 index 00000000..b6be76ae --- /dev/null +++ b/frontend/components/accessPolicies/DataAccessPolicySummary.tsx @@ -0,0 +1,40 @@ +import { Tag } from "antd"; +import { humanize } from "../../lib/string"; +import { DataAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; +import EntitySummary from "../entities/EntitySummary"; +import { isTruthy } from "../../lib/array"; + +export default function DataAccessPolicySummary({ + entity, +}: { + entity: DataAccessPolicyPartialFragment; +}) { + return ( + + {entity.data == null ? "global" : "local"} + {/* paths.data(entity.data)} */} + , + entity.isAnyoneAllowed && ( + + Anyone Allowed + + ), + entity.isNobodyAllowed && ( + + Nobody Allowed + + ), + ].filter(isTruthy)} + > +
    Combinator: {humanize(entity.combinator, "all-upper")}
    +
    + ); +} diff --git a/frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx b/frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx new file mode 100644 index 00000000..f4b42dfa --- /dev/null +++ b/frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx @@ -0,0 +1,23 @@ +import { Scalars } from "../../__generated__/graphql"; +import paths from "../../paths"; +import { InstitutionAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function InstitutionAccessPolicySummary({ + entity, +}: { + entity: InstitutionAccessPolicyPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx b/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx new file mode 100644 index 00000000..8c917076 --- /dev/null +++ b/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx @@ -0,0 +1,27 @@ +import { Scalars } from "../../__generated__/graphql"; +import paths from "../../paths"; +import { OpenIdConnectApplicationAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function OpenIdConnectApplicationAccessPolicySummary({ + entity, +}: { + entity: OpenIdConnectApplicationAccessPolicyPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/accessPolicies/UserAccessPolicySummary.tsx b/frontend/components/accessPolicies/UserAccessPolicySummary.tsx new file mode 100644 index 00000000..c3afc3bb --- /dev/null +++ b/frontend/components/accessPolicies/UserAccessPolicySummary.tsx @@ -0,0 +1,23 @@ +import { Scalars } from "../../__generated__/graphql"; +import paths from "../../paths"; +import { UserAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function UserAccessPolicySummary({ + entity, +}: { + entity: UserAccessPolicyPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/data/calorimetric/CalorimetricData.tsx b/frontend/components/data/calorimetric/CalorimetricData.tsx index abaee00a..f30415c3 100644 --- a/frontend/components/data/calorimetric/CalorimetricData.tsx +++ b/frontend/components/data/calorimetric/CalorimetricData.tsx @@ -1,6 +1,6 @@ import { Scalars } from "../../../__generated__/graphql"; import { CalorimetricDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Card } from "antd"; +import { Skeleton, Result, Card, Button } from "antd"; import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import CalorimetricDataSummary from "./CalorimetricDataSummary"; @@ -14,22 +14,27 @@ export default function CalorimetricData({ id }: CalorimetricDataProps) { const queryVariables = { id, }; - const { loading, error, data } = useQuery(CalorimetricDataDocument, { + const { loading, error, data, refetch } = useQuery(CalorimetricDataDocument, { variables: queryVariables, }); useQueryHandler({ error }); - const theData = data?.data; + const entity = data?.data; if (loading) { return ; } - if (!theData) { + if (!entity) { return ( refetch()}> + Reload + + } /> ); } @@ -37,7 +42,7 @@ export default function CalorimetricData({ id }: CalorimetricDataProps) { return (
    - + ; } - if (!theData) { + if (!entity) { return ( refetch()}> + Reload + + } /> ); } @@ -37,7 +42,7 @@ export default function GeometricData({ id }: GeometricDataProps) { return (
    - +
    diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index a0bd05c5..feb3e19c 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -1,6 +1,6 @@ import { Scalars } from "../../../__generated__/graphql"; import { HygrothermalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Card } from "antd"; +import { Skeleton, Result, Card, Button } from "antd"; import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import HygrothermalDataSummary from "./HygrothermalDataSummary"; @@ -14,22 +14,27 @@ export default function HygrothermalData({ id }: HygrothermalDataProps) { const queryVariables = { id, }; - const { loading, error, data } = useQuery(HygrothermalDataDocument, { + const { loading, error, data, refetch } = useQuery(HygrothermalDataDocument, { variables: queryVariables, }); useQueryHandler({ error }); - const theData = data?.data; + const entity = data?.data; if (loading) { return ; } - if (!theData) { + if (!entity) { return ( refetch()}> + Reload + + } /> ); } @@ -37,7 +42,7 @@ export default function HygrothermalData({ id }: HygrothermalDataProps) { return (
    - + ; } - if (!theData) { + if (!entity) { return ( refetch()}> + Reload + + } /> ); } @@ -37,7 +42,7 @@ export default function LifeCycleData({ id }: LifeCycleDataProps) { return (
    - +
    diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 0fefe975..9a5f1fe8 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,11 +1,12 @@ import { Scalars } from "../../../__generated__/graphql"; import { OpticalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Card } from "antd"; +import { Skeleton, Result, Card, Button, Divider, Typography } from "antd"; import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import OpticalDataSummary from "./OpticalDataSummary"; import OpticalDataRibbon from "./OpticalDataRibbon"; import QueryToolbar from "../../QueryToolbar"; +import DataAccessPolicy from "../../accessPolicies/DataAccessPolicy"; interface OpticalDataProps { id: Scalars["Uuid"]["input"]; @@ -15,7 +16,7 @@ export default function OpticalData({ id }: OpticalDataProps) { const queryVariables = { id, }; - const { loading, error, data } = useQuery(OpticalDataDocument, { + const { loading, error, data, refetch } = useQuery(OpticalDataDocument, { variables: queryVariables, }); useQueryHandler({ error }); @@ -31,6 +32,11 @@ export default function OpticalData({ id }: OpticalDataProps) { status="500" title="500" subTitle="Sorry, something went wrong." + extra={ + + } /> ); } @@ -43,6 +49,10 @@ export default function OpticalData({ id }: OpticalDataProps) { + + + +
    ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 1ece9281..5af056aa 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -1,6 +1,6 @@ import { Scalars } from "../../../__generated__/graphql"; import { PhotovoltaicDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Card } from "antd"; +import { Skeleton, Result, Card, Button } from "antd"; import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import PhotovoltaicDataSummary from "./PhotovoltaicDataSummary"; @@ -14,22 +14,27 @@ export default function PhotovoltaicData({ id }: PhotovoltaicDataProps) { const queryVariables = { id, }; - const { loading, error, data } = useQuery(PhotovoltaicDataDocument, { + const { loading, error, data, refetch } = useQuery(PhotovoltaicDataDocument, { variables: queryVariables, }); useQueryHandler({ error }); - const theData = data?.data; + const entity = data?.data; if (loading) { return ; } - if (!theData) { + if (!entity) { return ( refetch()}> + Reload + + } /> ); } @@ -37,7 +42,7 @@ export default function PhotovoltaicData({ id }: PhotovoltaicDataProps) { return (
    - + refetch()}> + Reload + + } /> ); } diff --git a/frontend/pages/user-info.tsx b/frontend/pages/user-info.tsx index c6c5134e..de01048d 100644 --- a/frontend/pages/user-info.tsx +++ b/frontend/pages/user-info.tsx @@ -1,13 +1,13 @@ import { useQuery } from "@apollo/client/react"; import Layout from "../components/Layout"; import { CurrentUserInfoDocument } from "../queries/currentUser.generated"; -import { Skeleton, Result, Descriptions, Typography } from "antd"; +import { Skeleton, Result, Descriptions, Typography, Button } from "antd"; import { PageHeader } from "@ant-design/pro-layout"; import paths from "../paths"; import { useQueryHandler } from "../lib/hooks/useQueryHandler"; function Page() { - const { loading, error, data } = useQuery(CurrentUserInfoDocument); + const { loading, error, data, refetch } = useQuery(CurrentUserInfoDocument); useQueryHandler({ error }); const currentUserInfo = data?.currentUserInfo; @@ -21,6 +21,11 @@ function Page() { status="500" title="500" subTitle="Sorry, something went wrong." + extra={ + + } /> ); } diff --git a/frontend/paths.ts b/frontend/paths.ts index f236da3c..abe3b13c 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -10,6 +10,30 @@ export default { antiforgeryToken: "/antiforgery/token" as Route, userInfo: "/user-info" as Route, database: "/database" as Route, + data({ + uuid, + dataKind, + }: { + uuid: Scalars["Uuid"]["output"]; + dataKind: DataKind; + }) { + switch (dataKind) { + case DataKind.CalorimetricData: + return this.calorimetricData(uuid); + case DataKind.GeometricData: + return this.geometricData(uuid); + case DataKind.HygrothermalData: + return this.hygrothermalData(uuid); + case DataKind.LifeCycleData: + return this.lifeCycleData(uuid); + case DataKind.OpticalData: + return this.opticalData(uuid); + case DataKind.PhotovoltaicData: + return this.photovoltaicData(uuid); + default: + return assertNever(dataKind); + } + }, allCalorimetricData: "/data/calorimetric" as Route, calorimetricData: (id: Scalars["Uuid"]["output"]) => `/data/calorimetric/${encodeURIComponent(String(id))}` as Route, @@ -67,6 +91,8 @@ export default { user: (id: Scalars["Uuid"]["output"]) => new URL(`/users/${encodeURIComponent(String(id))}`, metabaseUrl) .href as Route, + openIdConnectApplication: (id: Scalars["Uuid"]["output"]) => + `/open-id-connect/application/${encodeURIComponent(id)}` as Route, allData: "/data" as Route, data( databaseId: Scalars["Uuid"]["output"], diff --git a/frontend/queries/accessPolicies.graphql b/frontend/queries/accessPolicies.graphql new file mode 100644 index 00000000..c0797c50 --- /dev/null +++ b/frontend/queries/accessPolicies.graphql @@ -0,0 +1,112 @@ +fragment UserAccessPolicyPartial on UserAccessPolicy { + id + uuid + userId + user { + uuid + name + } + isAlwaysAllowed + isWithinAccessLimitInTimeSpan + isWithinTimeSpan + upperAccessLimitPerTimeDuration { + upperLimit + duration + } + accessCountSinceStartTime { + accessCount + startTime + } +} + +fragment InstitutionAccessPolicyPartial on InstitutionAccessPolicy { + id + uuid + institutionId + institution { + uuid + name + } + isAlwaysAllowed + isWithinAccessLimitInTimeSpan + isWithinTimeSpan + upperAccessLimitPerTimeDuration { + upperLimit + duration + } + accessCountSinceStartTime { + accessCount + startTime + } +} + +fragment OpenIdConnectApplicationAccessPolicyPartial on OpenIdConnectApplicationAccessPolicy { + id + uuid + clientId + client { + uuid + name + } + isAlwaysAllowed + isWithinAccessLimitInTimeSpan + isWithinTimeSpan + upperAccessLimitPerTimeDuration { + upperLimit + duration + } + accessCountSinceStartTime { + accessCount + startTime + } +} + +fragment DataAccessPoliciesPartial on DataAccessPolicy { + id + uuid + data { + uuid + kind + } + combinator + isAnyoneAllowed + isNobodyAllowed + userAccessPolicies { + ...UserAccessPolicyPartial + } + institutionAccessPolicies { + ...InstitutionAccessPolicyPartial + } + openIdConnectApplicationAccessPolicies { + ...OpenIdConnectApplicationAccessPolicyPartial + } +} + +fragment DataAccessPolicyPartial on DataAccessPolicy { + ...DataAccessPoliciesPartial +} + +query DataAccessPolicies( + $first: Int + $after: String + $where: DataAccessPolicyPropositionInput +) { + connection: dataAccessPolicies(first: $first, after: $after, where: $where) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...DataAccessPoliciesPartial + } + } + } +} + +query DataAccessPolicy($dataId: Uuid) { + data: dataAccessPolicy(dataId: $dataId) { + ...DataAccessPolicyPartial + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 6d054a21..f3342da3 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -872,11 +872,11 @@ of the time span or when a policy is updated via the respective 'set' mutation. Note that durations are represented in the [ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ -type DataAccessPolicy { +type DataAccessPolicy implements Node { combinator: LogicalCombinator! createdAt: DateTime! data: Data @cost(weight: "0") - id: Uuid! + id: ID! institutionAccessPolicies( order: [InstitutionAccessPolicySortInput!] where: InstitutionAccessPolicyPropositionInput @@ -898,7 +898,7 @@ type DataAccessPolicy { order: [UserAccessPolicySortInput!] where: UserAccessPolicyPropositionInput ): [UserAccessPolicy!]! - version: NonNegativeInt! + uuid: Uuid! } "A connection to a list of items." @@ -1168,7 +1168,7 @@ type Institution { type InstitutionAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") dataAccessPolicyId: Uuid! id: ID! institution: Institution @cost(weight: "3") @@ -1294,7 +1294,9 @@ type OpenEndedDateTimeRange { } type OpenIdConnectApplication { + clientId: String! name: String + uuid: Uuid! } type OpenIdConnectApplicationAccessPolicy implements Node { @@ -1302,8 +1304,7 @@ type OpenIdConnectApplicationAccessPolicy implements Node { client: OpenIdConnectApplication @cost(weight: "3") clientId: String! createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy - dataAccessPolicyId: Uuid! + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") id: ID! isAccessAllowed(openIdConnectClientId: String): Boolean! isAlwaysAllowed: Boolean! @@ -1735,7 +1736,7 @@ type User { type UserAccessPolicy implements Node { accessCountSinceStartTime: CountSinceTime createdAt: DateTime! - dataAccessPolicy: DataAccessPolicy + dataAccessPolicy: DataAccessPolicy! @cost(weight: "10") dataAccessPolicyId: Uuid! id: ID! isAccessAllowed(userId: Uuid): Boolean! From ffcf60f4eba559f28dca1db028eef9f3899e92c8 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sat, 27 Jun 2026 22:16:08 +0200 Subject: [PATCH 862/878] Improve presentation of data access policies --- .../OpenIdConnectApplicationDataLoader.cs | 2 +- .../Queries/OpenIdConnectApplications.graphql | 2 +- .../DataAccessPolicyFilterType.cs | 4 + .../DataAccessPolicySortType.cs | 5 +- .../AccessPolicies/DataAccessPolicyType.cs | 51 ++++++---- .../InstitutionAccessPolicyFilterType.cs | 4 + .../InstitutionAccessPolicySortType.cs | 5 +- ...onnectApplicationAccessPolicyFilterType.cs | 4 + ...dConnectApplicationAccessPolicySortType.cs | 5 +- .../UserAccessPolicyFilterType.cs | 4 + .../UserAccessPolicySortType.cs | 5 +- .../src/GraphQl/DataX/DataFilterTypeBase.cs | 4 + .../GetHttpsResourceFilterType.cs | 4 + .../ResponseApprovalFilterType.cs | 4 + .../GraphQlSchemaTests.IsUnchanged.snap | 98 +++++++++++++++++- frontend/components/DateTimeX.tsx | 14 ++- frontend/components/NavBar.tsx | 4 + .../AccessPolicySummaryBase.tsx | 33 +++++-- .../DataAccessPolicySummary.tsx | 40 -------- .../LocalAndGlobalDataAccessPolicies.tsx | 25 +++++ ...dConnectApplicationAccessPolicySummary.tsx | 27 ----- ...enIdConnectApplicationAccessPolicyList.tsx | 27 +++++ ...dConnectApplicationAccessPolicySummary.tsx | 23 +++++ ...OpenIdConnectApplicationAccessPolicies.tsx | 63 ++++++++++++ .../{ => data}/DataAccessPolicy.tsx | 79 +++++++-------- .../data/DataAccessPolicyList.tsx | 27 +++++ .../data/DataAccessPolicySummary.tsx | 69 +++++++++++++ .../data/PaginatedDataAccessPolicies.tsx | 54 ++++++++++ .../InstitutionAccessPolicyList.tsx | 27 +++++ .../InstitutionAccessPolicySummary.tsx | 10 +- .../PaginatedInstitutionAccessPolicies.tsx | 63 ++++++++++++ .../user/PaginatedUserAccessPolicies.tsx | 63 ++++++++++++ .../user/UserAccessPolicyList.tsx | 27 +++++ .../{ => user}/UserAccessPolicySummary.tsx | 10 +- .../data/calorimetric/CalorimetricData.tsx | 2 + .../data/geometric/GeometricData.tsx | 2 + .../data/hygrothermal/HygrothermalData.tsx | 2 + .../data/lifeCycle/LifeCycleData.tsx | 2 + .../components/data/optical/OpticalData.tsx | 17 ++-- .../data/photovoltaic/PhotovoltaicData.tsx | 2 + .../components/entities/EntitySummary.tsx | 11 ++- frontend/pages/access-policies/index.tsx | 71 +++++++++++++ frontend/paths.ts | 21 ++-- frontend/queries/accessPolicies.graphql | 99 +++++++++++++++++-- frontend/type-defs.graphqls | 98 +++++++++++++++++- 45 files changed, 1004 insertions(+), 209 deletions(-) delete mode 100644 frontend/components/accessPolicies/DataAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/LocalAndGlobalDataAccessPolicies.tsx delete mode 100644 frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicyList.tsx create mode 100644 frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/application/PaginatedOpenIdConnectApplicationAccessPolicies.tsx rename frontend/components/accessPolicies/{ => data}/DataAccessPolicy.tsx (50%) create mode 100644 frontend/components/accessPolicies/data/DataAccessPolicyList.tsx create mode 100644 frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx create mode 100644 frontend/components/accessPolicies/data/PaginatedDataAccessPolicies.tsx create mode 100644 frontend/components/accessPolicies/institution/InstitutionAccessPolicyList.tsx rename frontend/components/accessPolicies/{ => institution}/InstitutionAccessPolicySummary.tsx (53%) create mode 100644 frontend/components/accessPolicies/institution/PaginatedInstitutionAccessPolicies.tsx create mode 100644 frontend/components/accessPolicies/user/PaginatedUserAccessPolicies.tsx create mode 100644 frontend/components/accessPolicies/user/UserAccessPolicyList.tsx rename frontend/components/accessPolicies/{ => user}/UserAccessPolicySummary.tsx (52%) create mode 100644 frontend/pages/access-policies/index.tsx diff --git a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs index e512b342..3387bbf7 100644 --- a/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs +++ b/backend/src/ApiRequests/OpenIdConnectApplicationDataLoader.cs @@ -19,7 +19,7 @@ public static Uri GetGraphQlEndpoint(AppSettings appSettings) => public sealed record OpenIdConnectApplication( Guid Uuid, [property: GraphQLIgnore] string Id, - string? Name + string? DisplayName ) : IIdNode { public string ClientId => Id; diff --git a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql index ccd43d7d..f53b6d6d 100644 --- a/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql +++ b/backend/src/ApiRequests/Queries/OpenIdConnectApplications.graphql @@ -4,7 +4,7 @@ query OpenIdConnectApplications($ids: [String!]!) { node { uuid id: clientId - name: displayName + displayName } } } diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs index 3141019c..7a6d861f 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -13,6 +13,10 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(DataAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.IsGlobal); descriptor.Field(_ => _.IsNobodyAllowed); descriptor.Field(_ => _.IsAnyoneAllowed); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs index 023bd9fa..8c5c814b 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicySortType.cs @@ -1,16 +1,17 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.AccessPolicies; public sealed class DataAccessPolicySortType - : SortInputType + : AuditableEntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); descriptor.Name(nameof(DataAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); descriptor.Field(_ => _.DataId); descriptor.Field(_ => _.DataKind); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index d2d920e1..c49cab8c 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -192,12 +192,13 @@ CancellationToken cancellationToken is not null; } + [UsePaging] [UseFiltering] [UseSorting] - public static async Task GetUserAccessPoliciesAsync( + public static async ValueTask> GetUserAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, - IUserAccessPoliciesByDataAccessPolicyIdDataLoader byId, + ApplicationDbContext databaseContext, CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -205,20 +206,23 @@ CancellationToken cancellationToken if (!await authorization.IsDatabaseOperator(cancellationToken)) { authorization.ReportUnauthorizedError(resolverContext); - return Array.Empty(); + return HotChocolate.Types.Pagination.Connection.Empty(); } - return await byId - .With(resolverContext.GetQueryContext()) - .LoadAsync(dataAccessPolicy.Id, cancellationToken) - ?? []; + return await databaseContext.UserAccessPolicies + .AsNoTracking() + .Where(_ => _.DataAccessPolicyId == dataAccessPolicy.Id) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } + [UsePaging] [UseFiltering] [UseSorting] - public static async Task GetInstitutionAccessPoliciesAsync( + public static async ValueTask> GetInstitutionAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, - IInstitutionAccessPoliciesByDataAccessPolicyIdDataLoader byId, + ApplicationDbContext databaseContext, CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -226,20 +230,23 @@ CancellationToken cancellationToken if (!await authorization.IsDatabaseOperator(cancellationToken)) { authorization.ReportUnauthorizedError(resolverContext); - return Array.Empty(); + return HotChocolate.Types.Pagination.Connection.Empty(); } - return await byId - .With(resolverContext.GetQueryContext()) - .LoadAsync(dataAccessPolicy.Id, cancellationToken) - ?? []; + return await databaseContext.InstitutionAccessPolicies + .AsNoTracking() + .Where(_ => _.DataAccessPolicyId == dataAccessPolicy.Id) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } + [UsePaging] [UseFiltering] [UseSorting] - public static async Task GetOpenIdConnectApplicationAccessPoliciesAsync( + public static async ValueTask> GetOpenIdConnectApplicationAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, - IOpenIdConnectApplicationAccessPoliciesByDataAccessPolicyIdDataLoader byId, + ApplicationDbContext databaseContext, CommonAuthorization authorization, CancellationToken cancellationToken ) @@ -247,12 +254,14 @@ CancellationToken cancellationToken if (!await authorization.IsDatabaseOperator(cancellationToken)) { authorization.ReportUnauthorizedError(resolverContext); - return Array.Empty(); + return HotChocolate.Types.Pagination.Connection.Empty(); } - return await byId - .With(resolverContext.GetQueryContext()) - .LoadAsync(dataAccessPolicy.Id, cancellationToken) - ?? []; + return await databaseContext.OpenIdConnectApplicationAccessPolicies + .AsNoTracking() + .Where(_ => _.DataAccessPolicyId == dataAccessPolicy.Id) + .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder) + .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) + .ToConnectionAsync(); } } } \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs index 56cbb078..eb7121ac 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicyFilterType.cs @@ -13,6 +13,10 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(InstitutionAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.InstitutionId); descriptor.Field(_ => _.IsAlwaysAllowed); descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); diff --git a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs index 3bb8d1ad..28308513 100644 --- a/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs +++ b/backend/src/GraphQl/AccessPolicies/InstitutionAccessPolicySortType.cs @@ -1,16 +1,17 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.AccessPolicies; public sealed class InstitutionAccessPolicySortType - : SortInputType + : AuditableEntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); descriptor.Name(nameof(InstitutionAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); descriptor.Field(_ => _.InstitutionId); } diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs index 37af53ab..249a01f5 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicyFilterType.cs @@ -13,6 +13,10 @@ IFilterInputTypeDescriptor descriptor { descriptor.BindFieldsExplicitly(); descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.ClientId); descriptor.Field(_ => _.IsAlwaysAllowed); descriptor.Field(_ => _.IsWithinAccessLimitInTimeSpan); diff --git a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs index 8de450be..ee36eff1 100644 --- a/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs +++ b/backend/src/GraphQl/AccessPolicies/OpenIdConnectApplicationAccessPolicySortType.cs @@ -1,16 +1,17 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.AccessPolicies; public sealed class OpenIdConnectApplicationAccessPolicySortType - : SortInputType + : AuditableEntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); descriptor.Name(nameof(OpenIdConnectApplicationAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); descriptor.Field(_ => _.ClientId); } diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs index e9211b55..58cc2fdf 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicyFilterType.cs @@ -12,6 +12,10 @@ IFilterInputTypeDescriptor descriptor ) { descriptor.BindFieldsExplicitly(); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Name(nameof(UserAccessPolicyFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); descriptor.Field(_ => _.UserId); descriptor.Field(_ => _.IsAlwaysAllowed); diff --git a/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs b/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs index a0ce92e5..dffb57fe 100644 --- a/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs +++ b/backend/src/GraphQl/AccessPolicies/UserAccessPolicySortType.cs @@ -1,16 +1,17 @@ using Database.Data.AccessPolicies; +using Database.GraphQl.Entities; using HotChocolate.Data.Sorting; namespace Database.GraphQl.AccessPolicies; public sealed class UserAccessPolicySortType - : SortInputType + : AuditableEntitySortType { protected override void Configure( ISortInputTypeDescriptor descriptor ) { - descriptor.BindFieldsExplicitly(); + base.Configure(descriptor); descriptor.Name(nameof(UserAccessPolicySortType)[..^"SortType".Length] + GraphQlConstants.SortInputSuffix); descriptor.Field(_ => _.UserId); } diff --git a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs index fe8f38b9..feb4652d 100644 --- a/backend/src/GraphQl/DataX/DataFilterTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataFilterTypeBase.cs @@ -14,6 +14,10 @@ IFilterInputTypeDescriptor descriptor ) { base.Configure(descriptor); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.UserId); descriptor.Field(_ => _.Locale); descriptor.Field(_ => _.Name); diff --git a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs index b88be728..eb26df59 100644 --- a/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs +++ b/backend/src/GraphQl/GetHttpsResources/GetHttpsResourceFilterType.cs @@ -13,6 +13,10 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(GetHttpsResourceFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.Description); descriptor.Field(_ => _.HashValue); descriptor.Field(_ => _.DataFormatId); diff --git a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs index b0d16e2a..f29f5b4d 100644 --- a/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs +++ b/backend/src/GraphQl/ResponseApprovals/ResponseApprovalFilterType.cs @@ -14,6 +14,10 @@ IFilterInputTypeDescriptor descriptor { base.Configure(descriptor); descriptor.Name(nameof(ResponseApprovalFilterType)[..^"FilterType".Length] + GraphQlConstants.FilterInputSuffix); + // TODO Remove Id, CreatedAt, and UpdatedAt once the base.Configure is respected. + descriptor.Field(_ => _.Id); + descriptor.Field(_ => _.CreatedAt); + descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.UserId); descriptor.Field(_ => _.Locale); descriptor.Field(_ => _.Name); diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index f3342da3..303be59a 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -878,9 +878,24 @@ type DataAccessPolicy implements Node { data: Data @cost(weight: "0") id: ID! institutionAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [InstitutionAccessPolicySortInput!] where: InstitutionAccessPolicyPropositionInput - ): [InstitutionAccessPolicy!]! + ): InstitutionAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) isAccessAllowed( institutionIds: [Uuid!] openIdConnectClientId: String @@ -890,14 +905,44 @@ type DataAccessPolicy implements Node { isGlobal: Boolean! isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput - ): [OpenIdConnectApplicationAccessPolicy!]! + ): OpenIdConnectApplicationAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) updatedAt: DateTime! userAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [UserAccessPolicySortInput!] where: UserAccessPolicyPropositionInput - ): [UserAccessPolicy!]! + ): UserAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) uuid: Uuid! } @@ -1295,7 +1340,7 @@ type OpenEndedDateTimeRange { type OpenIdConnectApplication { clientId: String! - name: String + displayName: String uuid: Uuid! } @@ -2066,10 +2111,12 @@ input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -2079,6 +2126,7 @@ input ConfigureDataAccessPoliciesPropositionInput { opticalData: OpticalDataPropositionInput or: [ConfigureDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -2196,12 +2244,15 @@ input CreateResponseApprovalsPropositionInput { appliedMethod: AppliedMethodPropositionInput approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput + createdAt: DateTimePropositionInput creatorId: UuidPropositionInput description: StringPropositionInput + id: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [CreateResponseApprovalsPropositionInput!] resources: GetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2297,10 +2348,12 @@ input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -2310,6 +2363,7 @@ input DataAccessPolicyPropositionInput { opticalData: OpticalDataPropositionInput or: [DataAccessPolicyPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -2385,8 +2439,11 @@ Note that durations are represented in the [ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") dataId: SortEnumType @cost(weight: "10") dataKind: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input DataApprovalPropositionInput { @@ -2583,6 +2640,7 @@ input GetHttpsResourcePropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -2590,11 +2648,13 @@ input GetHttpsResourcePropositionInput { geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput or: [GetHttpsResourcePropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput } input GetHttpsResourceSortInput { @@ -2620,13 +2680,16 @@ input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput description: StringPropositionInput hashValue: StringPropositionInput + id: UuidPropositionInput or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput + updatedAt: DateTimePropositionInput } input GetHttpsResourceTreeNonRootVertSortInput { @@ -2688,17 +2751,23 @@ input InstitutionAccessPoliciesPropositionInput { input InstitutionAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput institutionId: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [InstitutionAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input InstitutionAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") institutionId: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input IntPropositionInput { @@ -2920,16 +2989,22 @@ input OpenIdConnectApplicationAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input OpenIdConnectApplicationAccessPolicySortInput { clientId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input OpticalComponentSubtypePropositionInput { @@ -3070,6 +3145,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -3077,11 +3153,13 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput } input ReferenceInput @oneOf { @@ -3182,10 +3260,12 @@ input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -3195,6 +3275,7 @@ input ResetDataAccessPoliciesPropositionInput { opticalData: OpticalDataPropositionInput or: [ResetDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -3391,12 +3472,15 @@ input UpdateResponseApprovalsPropositionInput { appliedMethod: AppliedMethodPropositionInput approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput + createdAt: DateTimePropositionInput creatorId: UuidPropositionInput description: StringPropositionInput + id: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [UpdateResponseApprovalsPropositionInput!] resources: GetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -3469,16 +3553,22 @@ input UserAccessPoliciesPropositionInput { input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [UserAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput } input UserAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") userId: SortEnumType @cost(weight: "10") } diff --git a/frontend/components/DateTimeX.tsx b/frontend/components/DateTimeX.tsx index fdd12a3e..5e3a7af9 100644 --- a/frontend/components/DateTimeX.tsx +++ b/frontend/components/DateTimeX.tsx @@ -2,12 +2,16 @@ import { Calendar, Popover } from "antd"; import dayjs from "dayjs"; import { Scalars } from "../__generated__/graphql"; -interface DateTimeProps { - value: Scalars["DateTime"]["output"]; -} +type DateTimeProps = + | { + value: Scalars["DateTime"]["output"]; + } + | { + parsedValue: dayjs.Dayjs; + }; -export default function DateTimeX({ value }: DateTimeProps) { - const parsedValue = dayjs(value); +export default function DateTimeX(props: DateTimeProps) { + const parsedValue = "value" in props ? dayjs(props.value) : props.parsedValue; const calendarContent = (
    diff --git a/frontend/components/NavBar.tsx b/frontend/components/NavBar.tsx index 27b8ad4d..4735fd6c 100644 --- a/frontend/components/NavBar.tsx +++ b/frontend/components/NavBar.tsx @@ -80,6 +80,10 @@ const userItems = (currentUser: CurrentUserPartialFragment) => [ key: paths.uploadFile, label: Upload File, }, + currentUser.isAtLeastAssistantManagerOfDatabaseOperator && { + key: paths.accessPolicies, + label: Access Policies, + }, { key: paths.logout, label: ( diff --git a/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx b/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx index 40c59eb0..742ad835 100644 --- a/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx +++ b/frontend/components/accessPolicies/AccessPolicySummaryBase.tsx @@ -1,17 +1,20 @@ import { Statistic, Tag } from "antd"; import { - InstitutionAccessPolicyPartialFragment, - OpenIdConnectApplicationAccessPolicyPartialFragment, - UserAccessPolicyPartialFragment, + InstitutionAccessPoliciesPartialFragment, + OpenIdConnectApplicationAccessPoliciesPartialFragment, + UserAccessPoliciesPartialFragment, } from "../../queries/accessPolicies.generated"; import EntitySummary from "../entities/EntitySummary"; import dayjs from "dayjs"; import durationPlugin from "dayjs/plugin/duration"; +import relativeTime from "dayjs/plugin/relativeTime"; import { Route } from "next"; import { Scalars } from "../../__generated__/graphql"; import { isTruthy } from "../../lib/array"; +import DateTimeX from "../DateTimeX"; dayjs.extend(durationPlugin); +dayjs.extend(relativeTime); export default function AccessPolicySummaryBase({ entity, @@ -19,9 +22,9 @@ export default function AccessPolicySummaryBase({ route, }: { entity: - | UserAccessPolicyPartialFragment - | InstitutionAccessPolicyPartialFragment - | OpenIdConnectApplicationAccessPolicyPartialFragment; + | UserAccessPoliciesPartialFragment + | InstitutionAccessPoliciesPartialFragment + | OpenIdConnectApplicationAccessPoliciesPartialFragment; stakeholder: { uuid: Scalars["Uuid"]["output"]; name?: string | null; @@ -40,12 +43,12 @@ export default function AccessPolicySummaryBase({ return ( - Always Allowed + always allowed ), ].filter(isTruthy)} @@ -56,8 +59,18 @@ export default function AccessPolicySummaryBase({ value={entity.accessCountSinceStartTime?.accessCount ?? 0} suffix={`of ${entity.upperAccessLimitPerTimeDuration?.upperLimit ?? "∞"}`} /> - from {startTime?.format("YYYY-MM-DD HH:mm") ?? "-∞"} until{" "} - {endTime?.format("YYYY-MM-DD HH:mm") ?? "∞"} +
    + {duration != null && startTime == null ? ( + <>within {duration.humanize()} + ) : ( + <> + from{" "} + {startTime == null ? "-∞" : }{" "} + until{" "} + {endTime == null ? "-∞" : } + + )} +
    ); diff --git a/frontend/components/accessPolicies/DataAccessPolicySummary.tsx b/frontend/components/accessPolicies/DataAccessPolicySummary.tsx deleted file mode 100644 index b6be76ae..00000000 --- a/frontend/components/accessPolicies/DataAccessPolicySummary.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Tag } from "antd"; -import { humanize } from "../../lib/string"; -import { DataAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; -import EntitySummary from "../entities/EntitySummary"; -import { isTruthy } from "../../lib/array"; - -export default function DataAccessPolicySummary({ - entity, -}: { - entity: DataAccessPolicyPartialFragment; -}) { - return ( - - {entity.data == null ? "global" : "local"} - {/* paths.data(entity.data)} */} - , - entity.isAnyoneAllowed && ( - - Anyone Allowed - - ), - entity.isNobodyAllowed && ( - - Nobody Allowed - - ), - ].filter(isTruthy)} - > -
    Combinator: {humanize(entity.combinator, "all-upper")}
    -
    - ); -} diff --git a/frontend/components/accessPolicies/LocalAndGlobalDataAccessPolicies.tsx b/frontend/components/accessPolicies/LocalAndGlobalDataAccessPolicies.tsx new file mode 100644 index 00000000..ab4e0bd2 --- /dev/null +++ b/frontend/components/accessPolicies/LocalAndGlobalDataAccessPolicies.tsx @@ -0,0 +1,25 @@ +import { Scalars } from "../../__generated__/graphql"; +import { Divider } from "antd"; +import { useQuery } from "@apollo/client/react"; +import DataAccessPolicy from "../accessPolicies/data/DataAccessPolicy"; +import { CurrentUserDocument } from "../../queries/currentUser.generated"; + +export default function LocalAndGlobalDataAccessPolicies({ + dataId, +}: { + dataId: Scalars["Uuid"]["input"]; +}) { + const currentUserData = useQuery(CurrentUserDocument)?.data; + const currentUser = currentUserData?.currentUser; + + return ( + currentUser?.isAtLeastAssistantManagerOfDatabaseOperator && ( + <> + + + + + + ) + ); +} diff --git a/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx b/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx deleted file mode 100644 index 8c917076..00000000 --- a/frontend/components/accessPolicies/OpenIdConnectApplicationAccessPolicySummary.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Scalars } from "../../__generated__/graphql"; -import paths from "../../paths"; -import { OpenIdConnectApplicationAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; -import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; - -const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ - uuid: id, - name: id, -}); - -export default function OpenIdConnectApplicationAccessPolicySummary({ - entity, -}: { - entity: OpenIdConnectApplicationAccessPolicyPartialFragment; -}) { - return ( - - ); -} diff --git a/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicyList.tsx b/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicyList.tsx new file mode 100644 index 00000000..bf69edc3 --- /dev/null +++ b/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicyList.tsx @@ -0,0 +1,27 @@ +import { OpenIdConnectApplicationAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import EntityItem from "../../entities/EntityItem"; +import EntityList from "../../entities/EntityList"; +import OpenIdConnectApplicationAccessPolicySummary from "./OpenIdConnectApplicationAccessPolicySummary"; + +export default function OpenIdConnectApplicationAccessPolicyList({ + loading, + nodes, + onReload, +}: { + loading: boolean; + nodes: OpenIdConnectApplicationAccessPoliciesPartialFragment[] | null; + onReload: () => void; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicySummary.tsx b/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicySummary.tsx new file mode 100644 index 00000000..46114211 --- /dev/null +++ b/frontend/components/accessPolicies/application/OpenIdConnectApplicationAccessPolicySummary.tsx @@ -0,0 +1,23 @@ +import { Scalars } from "../../../__generated__/graphql"; +import paths from "../../../paths"; +import { OpenIdConnectApplicationAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "../AccessPolicySummaryBase"; + +const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ + uuid: id, + name: id, +}); + +export default function OpenIdConnectApplicationAccessPolicySummary({ + entity, +}: { + entity: OpenIdConnectApplicationAccessPoliciesPartialFragment; +}) { + return ( + + ); +} diff --git a/frontend/components/accessPolicies/application/PaginatedOpenIdConnectApplicationAccessPolicies.tsx b/frontend/components/accessPolicies/application/PaginatedOpenIdConnectApplicationAccessPolicies.tsx new file mode 100644 index 00000000..6104c952 --- /dev/null +++ b/frontend/components/accessPolicies/application/PaginatedOpenIdConnectApplicationAccessPolicies.tsx @@ -0,0 +1,63 @@ +import { + OpenIdConnectApplicationAccessPoliciesDocument, + OpenIdConnectApplicationAccessPoliciesQueryVariables, + OpenIdConnectApplicationAccessPoliciesPartialFragment, +} from "../../../queries/accessPolicies.generated"; +import OpenIdConnectApplicationAccessPolicyList from "./OpenIdConnectApplicationAccessPolicyList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { + OpenIdConnectApplicationAccessPolicyPropositionInput, + OpenIdConnectApplicationAccessPolicySortInput, +} from "../../../__generated__/graphql"; + +export default function PaginatedOpenIdConnectApplicationAccessPolicies({ + where, + order, + extra, +}: { + where?: OpenIdConnectApplicationAccessPoliciesQueryVariables["where"]; + order?: OpenIdConnectApplicationAccessPoliciesQueryVariables["order"]; + extra?: React.ReactNode; +}) { + return ( + + entitiesQuery={OpenIdConnectApplicationAccessPoliciesDocument} + baseWhere={where} + defaultOrder={order} + showJump={false} + extra={extra} + list={(props) => } + filterDefinitions={[ + { + field: "clientId", + type: "uuid", + }, + { + field: "dataAccessPolicy", + type: "object", + items: [ + { + field: "dataId", + type: "uuid", + }, + // { + // field: "dataKind", + // type: "enum", + // enumObject: DataKind + // } + ], + }, + ]} + sortDefinitions={[ + { field: "clientId" }, + { field: "createdAt" }, + { field: "updatedAt" }, + { field: "id" }, + ]} + /> + ); +} diff --git a/frontend/components/accessPolicies/DataAccessPolicy.tsx b/frontend/components/accessPolicies/data/DataAccessPolicy.tsx similarity index 50% rename from frontend/components/accessPolicies/DataAccessPolicy.tsx rename to frontend/components/accessPolicies/data/DataAccessPolicy.tsx index 156017ee..57579d62 100644 --- a/frontend/components/accessPolicies/DataAccessPolicy.tsx +++ b/frontend/components/accessPolicies/data/DataAccessPolicy.tsx @@ -1,71 +1,63 @@ -import { Scalars } from "../../__generated__/graphql"; +import { Scalars, SortEnumType } from "../../../__generated__/graphql"; import { DataAccessPolicyDocument, DataAccessPolicyPartialFragment, -} from "../../queries/accessPolicies.generated"; +} from "../../../queries/accessPolicies.generated"; import { Skeleton, Result, Card, Button } from "antd"; import { useQuery } from "@apollo/client/react"; -import { useQueryHandler } from "../../lib/hooks/useQueryHandler"; +import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import DataAccessPolicySummary from "./DataAccessPolicySummary"; -import EntityList from "../entities/EntityList"; -import EntityItem from "../entities/EntityItem"; -import LazyTabs, { LazyTabsProps } from "../LazyTabs"; -import UserAccessPolicySummary from "./UserAccessPolicySummary"; -import OpenIdConnectApplicationAccessPolicySummary from "./OpenIdConnectApplicationAccessPolicySummary"; -import InstitutionAccessPolicySummary from "./InstitutionAccessPolicySummary"; +import LazyTabs, { LazyTabsProps } from "../../LazyTabs"; import { useMemo } from "react"; +import PaginatedUserAccessPolicies from "../user/PaginatedUserAccessPolicies"; +import PaginatedOpenIdConnectApplicationAccessPolicies from "../application/PaginatedOpenIdConnectApplicationAccessPolicies"; +import PaginatedInstitutionAccessPolicies from "../institution/PaginatedInstitutionAccessPolicies"; const getTabs = ( entity: DataAccessPolicyPartialFragment, ): LazyTabsProps["items"] => [ { key: "userAccessPolicies", - count: entity.userAccessPolicies.length, + count: entity.userAccessPolicies?.totalCount, label: "User Access Policies", children: ( - {}} - renderItem={(node) => ( - - - - )} + ), }, { key: "institutionAccessPolicies", - count: entity.institutionAccessPolicies.length, + count: entity.institutionAccessPolicies?.totalCount, label: "Institution Access Policies", children: ( - {}} - renderItem={(node) => ( - - - - )} + ), }, { key: "openIdConnectApplicationAccessPolicies", - count: entity.openIdConnectApplicationAccessPolicies.length, + count: entity.openIdConnectApplicationAccessPolicies?.totalCount, label: "OpenID Connect Application Access Policies", children: ( - {}} - renderItem={(node) => ( - - - - )} + ), }, @@ -73,9 +65,13 @@ const getTabs = ( interface DataAccessPolicyProps { dataId: Scalars["Uuid"]["input"] | null | undefined; + onDataPage?: boolean; } -export default function DataAccessPolicy({ dataId }: DataAccessPolicyProps) { +export default function DataAccessPolicy({ + dataId, + onDataPage, +}: DataAccessPolicyProps) { const queryVariables = { dataId, }; @@ -85,7 +81,6 @@ export default function DataAccessPolicy({ dataId }: DataAccessPolicyProps) { useQueryHandler({ error }); const entity = data?.data; - const tabs = useMemo(() => (!entity ? null : getTabs(entity)), [entity]); if (loading) { @@ -110,7 +105,7 @@ export default function DataAccessPolicy({ dataId }: DataAccessPolicyProps) { return (
    - + {/* void; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx b/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx new file mode 100644 index 00000000..3d60ee33 --- /dev/null +++ b/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx @@ -0,0 +1,69 @@ +import { Tag } from "antd"; +import { humanize } from "../../../lib/string"; +import { DataAccessPolicyPartialFragment } from "../../../queries/accessPolicies.generated"; +import EntitySummary from "../../entities/EntitySummary"; +import { isTruthy } from "../../../lib/array"; +import paths from "../../../paths"; +import EnumTag from "../../EnumTag"; +import Copyable from "../../Copyable"; + +export default function DataAccessPolicySummary({ + entity, + onDataPage = false, +}: { + entity: DataAccessPolicyPartialFragment; + onDataPage?: boolean; +}) { + return ( + paths.data(entity.data?.kind!, id) + } + tags={[ + (onDataPage || (!onDataPage && entity.isGlobal)) && ( + + {entity.isGlobal ? "global" : "local"} + + ), + entity.isAnyoneAllowed && ( + + anyone allowed + + ), + entity.isNobodyAllowed && ( + + nobody allowed + + ), + entity.data && ( + + {entity.data.kind} + + ), + ].filter(isTruthy)} + > +
    Combinator: {humanize(entity.combinator, "all-upper")}
    +
    + ); +} diff --git a/frontend/components/accessPolicies/data/PaginatedDataAccessPolicies.tsx b/frontend/components/accessPolicies/data/PaginatedDataAccessPolicies.tsx new file mode 100644 index 00000000..7f4d4601 --- /dev/null +++ b/frontend/components/accessPolicies/data/PaginatedDataAccessPolicies.tsx @@ -0,0 +1,54 @@ +import { + DataAccessPoliciesDocument, + DataAccessPoliciesQueryVariables, + DataAccessPoliciesPartialFragment, +} from "../../../queries/accessPolicies.generated"; +import DataAccessPolicyList from "./DataAccessPolicyList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { + DataAccessPolicyPropositionInput, + DataAccessPolicySortInput, +} from "../../../__generated__/graphql"; + +export default function PaginatedDataAccessPolicies({ + where, + order, + extra, +}: { + where?: DataAccessPoliciesQueryVariables["where"]; + order?: DataAccessPoliciesQueryVariables["order"]; + extra?: React.ReactNode; +}) { + return ( + + entitiesQuery={DataAccessPoliciesDocument} + baseWhere={where} + defaultOrder={order} + showJump={false} + extra={extra} + list={(props) => } + filterDefinitions={[ + { + field: "dataId", + type: "uuid", + }, + // { + // field: "dataKind", + // type: "enum", + // enumObject: DataKind + // }, + ]} + sortDefinitions={[ + { field: "dataId" }, + { field: "dataKind" }, + { field: "createdAt" }, + { field: "updatedAt" }, + { field: "id" }, + ]} + /> + ); +} diff --git a/frontend/components/accessPolicies/institution/InstitutionAccessPolicyList.tsx b/frontend/components/accessPolicies/institution/InstitutionAccessPolicyList.tsx new file mode 100644 index 00000000..2120b2fe --- /dev/null +++ b/frontend/components/accessPolicies/institution/InstitutionAccessPolicyList.tsx @@ -0,0 +1,27 @@ +import { InstitutionAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import EntityItem from "../../entities/EntityItem"; +import EntityList from "../../entities/EntityList"; +import InstitutionAccessPolicySummary from "./InstitutionAccessPolicySummary"; + +export default function InstitutionAccessPolicyList({ + loading, + nodes, + onReload, +}: { + loading: boolean; + nodes: InstitutionAccessPoliciesPartialFragment[] | null; + onReload: () => void; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx b/frontend/components/accessPolicies/institution/InstitutionAccessPolicySummary.tsx similarity index 53% rename from frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx rename to frontend/components/accessPolicies/institution/InstitutionAccessPolicySummary.tsx index f4b42dfa..4d37db10 100644 --- a/frontend/components/accessPolicies/InstitutionAccessPolicySummary.tsx +++ b/frontend/components/accessPolicies/institution/InstitutionAccessPolicySummary.tsx @@ -1,7 +1,7 @@ -import { Scalars } from "../../__generated__/graphql"; -import paths from "../../paths"; -import { InstitutionAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; -import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; +import { Scalars } from "../../../__generated__/graphql"; +import paths from "../../../paths"; +import { InstitutionAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "../AccessPolicySummaryBase"; const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ uuid: id, @@ -11,7 +11,7 @@ const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ export default function InstitutionAccessPolicySummary({ entity, }: { - entity: InstitutionAccessPolicyPartialFragment; + entity: InstitutionAccessPoliciesPartialFragment; }) { return ( + entitiesQuery={InstitutionAccessPoliciesDocument} + baseWhere={where} + defaultOrder={order} + showJump={false} + extra={extra} + list={(props) => } + filterDefinitions={[ + { + field: "institutionId", + type: "uuid", + }, + { + field: "dataAccessPolicy", + type: "object", + items: [ + { + field: "dataId", + type: "uuid", + }, + // { + // field: "dataKind", + // type: "enum", + // enumObject: DataKind + // } + ], + }, + ]} + sortDefinitions={[ + { field: "institutionId" }, + { field: "createdAt" }, + { field: "updatedAt" }, + { field: "id" }, + ]} + /> + ); +} diff --git a/frontend/components/accessPolicies/user/PaginatedUserAccessPolicies.tsx b/frontend/components/accessPolicies/user/PaginatedUserAccessPolicies.tsx new file mode 100644 index 00000000..f6a0951f --- /dev/null +++ b/frontend/components/accessPolicies/user/PaginatedUserAccessPolicies.tsx @@ -0,0 +1,63 @@ +import { + UserAccessPoliciesDocument, + UserAccessPoliciesQueryVariables, + UserAccessPoliciesPartialFragment, +} from "../../../queries/accessPolicies.generated"; +import UserAccessPolicyList from "./UserAccessPolicyList"; +import PaginatedEntities from "../../entities/PaginatedEntities"; +import { + UserAccessPolicyPropositionInput, + UserAccessPolicySortInput, +} from "../../../__generated__/graphql"; + +export default function PaginatedUserAccessPolicies({ + where, + order, + extra, +}: { + where?: UserAccessPoliciesQueryVariables["where"]; + order?: UserAccessPoliciesQueryVariables["order"]; + extra?: React.ReactNode; +}) { + return ( + + entitiesQuery={UserAccessPoliciesDocument} + baseWhere={where} + defaultOrder={order} + showJump={false} + extra={extra} + list={(props) => } + filterDefinitions={[ + { + field: "userId", + type: "uuid", + }, + { + field: "dataAccessPolicy", + type: "object", + items: [ + { + field: "dataId", + type: "uuid", + }, + // { + // field: "dataKind", + // type: "enum", + // enumObject: DataKind + // } + ], + }, + ]} + sortDefinitions={[ + { field: "userId" }, + { field: "createdAt" }, + { field: "updatedAt" }, + { field: "id" }, + ]} + /> + ); +} diff --git a/frontend/components/accessPolicies/user/UserAccessPolicyList.tsx b/frontend/components/accessPolicies/user/UserAccessPolicyList.tsx new file mode 100644 index 00000000..4ddff1ac --- /dev/null +++ b/frontend/components/accessPolicies/user/UserAccessPolicyList.tsx @@ -0,0 +1,27 @@ +import { UserAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import EntityItem from "../../entities/EntityItem"; +import EntityList from "../../entities/EntityList"; +import UserAccessPolicySummary from "./UserAccessPolicySummary"; + +export default function UserAccessPolicyList({ + loading, + nodes, + onReload, +}: { + loading: boolean; + nodes: UserAccessPoliciesPartialFragment[] | null; + onReload: () => void; +}) { + return ( + ( + + + + )} + /> + ); +} diff --git a/frontend/components/accessPolicies/UserAccessPolicySummary.tsx b/frontend/components/accessPolicies/user/UserAccessPolicySummary.tsx similarity index 52% rename from frontend/components/accessPolicies/UserAccessPolicySummary.tsx rename to frontend/components/accessPolicies/user/UserAccessPolicySummary.tsx index c3afc3bb..d3af4cb2 100644 --- a/frontend/components/accessPolicies/UserAccessPolicySummary.tsx +++ b/frontend/components/accessPolicies/user/UserAccessPolicySummary.tsx @@ -1,7 +1,7 @@ -import { Scalars } from "../../__generated__/graphql"; -import paths from "../../paths"; -import { UserAccessPolicyPartialFragment } from "../../queries/accessPolicies.generated"; -import AccessPolicySummaryBase from "./AccessPolicySummaryBase"; +import { Scalars } from "../../../__generated__/graphql"; +import paths from "../../../paths"; +import { UserAccessPoliciesPartialFragment } from "../../../queries/accessPolicies.generated"; +import AccessPolicySummaryBase from "../AccessPolicySummaryBase"; const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ uuid: id, @@ -11,7 +11,7 @@ const nameFallback = (id: Scalars["Uuid"]["output"]) => ({ export default function UserAccessPolicySummary({ entity, }: { - entity: UserAccessPolicyPartialFragment; + entity: UserAccessPoliciesPartialFragment; }) { return ( +
    ); } diff --git a/frontend/components/data/geometric/GeometricData.tsx b/frontend/components/data/geometric/GeometricData.tsx index 612eb347..7f9bdcf0 100644 --- a/frontend/components/data/geometric/GeometricData.tsx +++ b/frontend/components/data/geometric/GeometricData.tsx @@ -5,6 +5,7 @@ import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import GeometricDataSummary from "./GeometricDataSummary"; import QueryToolbar from "../../QueryToolbar"; +import LocalAndGlobalDataAccessPolicies from "../../accessPolicies/LocalAndGlobalDataAccessPolicies"; interface GeometricDataProps { id: Scalars["Uuid"]["input"]; @@ -45,6 +46,7 @@ export default function GeometricData({ id }: GeometricDataProps) { +
    ); } diff --git a/frontend/components/data/hygrothermal/HygrothermalData.tsx b/frontend/components/data/hygrothermal/HygrothermalData.tsx index feb3e19c..804b8caf 100644 --- a/frontend/components/data/hygrothermal/HygrothermalData.tsx +++ b/frontend/components/data/hygrothermal/HygrothermalData.tsx @@ -5,6 +5,7 @@ import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import HygrothermalDataSummary from "./HygrothermalDataSummary"; import QueryToolbar from "../../QueryToolbar"; +import LocalAndGlobalDataAccessPolicies from "../../accessPolicies/LocalAndGlobalDataAccessPolicies"; interface HygrothermalDataProps { id: Scalars["Uuid"]["input"]; @@ -48,6 +49,7 @@ export default function HygrothermalData({ id }: HygrothermalDataProps) { query={HygrothermalDataDocument} variables={queryVariables} /> +
    ); } diff --git a/frontend/components/data/lifeCycle/LifeCycleData.tsx b/frontend/components/data/lifeCycle/LifeCycleData.tsx index e1878848..60cb2f40 100644 --- a/frontend/components/data/lifeCycle/LifeCycleData.tsx +++ b/frontend/components/data/lifeCycle/LifeCycleData.tsx @@ -5,6 +5,7 @@ import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import LifeCycleDataSummary from "./LifeCycleDataSummary"; import QueryToolbar from "../../QueryToolbar"; +import LocalAndGlobalDataAccessPolicies from "../../accessPolicies/LocalAndGlobalDataAccessPolicies"; interface LifeCycleDataProps { id: Scalars["Uuid"]["input"]; @@ -45,6 +46,7 @@ export default function LifeCycleData({ id }: LifeCycleDataProps) { +
    ); } diff --git a/frontend/components/data/optical/OpticalData.tsx b/frontend/components/data/optical/OpticalData.tsx index 9a5f1fe8..cef1bf4f 100644 --- a/frontend/components/data/optical/OpticalData.tsx +++ b/frontend/components/data/optical/OpticalData.tsx @@ -1,12 +1,12 @@ import { Scalars } from "../../../__generated__/graphql"; import { OpticalDataDocument } from "../../../queries/data.generated"; -import { Skeleton, Result, Card, Button, Divider, Typography } from "antd"; +import { Skeleton, Result, Card, Button } from "antd"; import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import OpticalDataSummary from "./OpticalDataSummary"; import OpticalDataRibbon from "./OpticalDataRibbon"; import QueryToolbar from "../../QueryToolbar"; -import DataAccessPolicy from "../../accessPolicies/DataAccessPolicy"; +import LocalAndGlobalDataAccessPolicies from "../../accessPolicies/LocalAndGlobalDataAccessPolicies"; interface OpticalDataProps { id: Scalars["Uuid"]["input"]; @@ -20,13 +20,13 @@ export default function OpticalData({ id }: OpticalDataProps) { variables: queryVariables, }); useQueryHandler({ error }); - const theData = data?.data; + const entity = data?.data; if (loading) { return ; } - if (!theData) { + if (!entity) { return ( - + - + - - - - +
    ); } diff --git a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx index 5af056aa..31da6b10 100644 --- a/frontend/components/data/photovoltaic/PhotovoltaicData.tsx +++ b/frontend/components/data/photovoltaic/PhotovoltaicData.tsx @@ -5,6 +5,7 @@ import { useQuery } from "@apollo/client/react"; import { useQueryHandler } from "../../../lib/hooks/useQueryHandler"; import PhotovoltaicDataSummary from "./PhotovoltaicDataSummary"; import QueryToolbar from "../../QueryToolbar"; +import LocalAndGlobalDataAccessPolicies from "../../accessPolicies/LocalAndGlobalDataAccessPolicies"; interface PhotovoltaicDataProps { id: Scalars["Uuid"]["input"]; @@ -48,6 +49,7 @@ export default function PhotovoltaicData({ id }: PhotovoltaicDataProps) { query={PhotovoltaicDataDocument} variables={queryVariables} /> +
    ); } diff --git a/frontend/components/entities/EntitySummary.tsx b/frontend/components/entities/EntitySummary.tsx index a6954628..63b29097 100644 --- a/frontend/components/entities/EntitySummary.tsx +++ b/frontend/components/entities/EntitySummary.tsx @@ -9,7 +9,7 @@ import Link from "next/link"; interface Props { entity: { - uuid: Scalars["Uuid"]["output"]; + uuid?: Scalars["Uuid"]["output"]; name?: string | null; abbreviation?: string | null; description?: string | null; @@ -31,9 +31,10 @@ export default function EntitySummary({ breadcrumb, children, }: Props) { - const absoluteRoute = route - ? new URL(route(entity.uuid), new URL(window.location.href).origin) - : null; + const absoluteRoute = + route && entity.uuid + ? new URL(route(entity.uuid), new URL(window.location.href).origin) + : null; return (
    @@ -90,7 +91,7 @@ export default function EntitySummary({
    - {route ? ( + {route && entity.uuid ? ( {entity.name ?? "Unnamed"} diff --git a/frontend/pages/access-policies/index.tsx b/frontend/pages/access-policies/index.tsx new file mode 100644 index 00000000..ba5360d4 --- /dev/null +++ b/frontend/pages/access-policies/index.tsx @@ -0,0 +1,71 @@ +import { Divider, Tag, Typography } from "antd"; +import Layout from "../../components/Layout"; +import DataAccessPolicy from "../../components/accessPolicies/data/DataAccessPolicy"; +import PaginatedDataAccessPolicies from "../../components/accessPolicies/data/PaginatedDataAccessPolicies"; +import { SortEnumType } from "../../__generated__/graphql"; +import LazyTabs, { LazyTabsProps } from "../../components/LazyTabs"; +import PaginatedOpenIdConnectApplicationAccessPolicies from "../../components/accessPolicies/application/PaginatedOpenIdConnectApplicationAccessPolicies"; +import PaginatedInstitutionAccessPolicies from "../../components/accessPolicies/institution/PaginatedInstitutionAccessPolicies"; +import PaginatedUserAccessPolicies from "../../components/accessPolicies/user/PaginatedUserAccessPolicies"; + +const tabs: LazyTabsProps["items"] = [ + { + key: "userAccessPolicies", + label: "User Access Policies", + children: ( + + ), + }, + { + key: "institutionAccessPolicies", + label: "Institution Access Policies", + children: ( + + ), + }, + { + key: "openIdConnectApplicationAccessPolicies", + label: "OpenID Connect Application Access Policies", + children: ( + + ), + }, +]; + +export default function Page() { + return ( + + + + + Data Access Policies{" "} + + local + + + + the once that do not allow access to everyone + + + + + User, Institution, and OpenID Connect Application Policies + + + each belonging to some local data policy + + + + ); +} diff --git a/frontend/paths.ts b/frontend/paths.ts index abe3b13c..cfc628dc 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -10,26 +10,20 @@ export default { antiforgeryToken: "/antiforgery/token" as Route, userInfo: "/user-info" as Route, database: "/database" as Route, - data({ - uuid, - dataKind, - }: { - uuid: Scalars["Uuid"]["output"]; - dataKind: DataKind; - }) { + data(dataKind: DataKind, id: Scalars["Uuid"]["output"]) { switch (dataKind) { case DataKind.CalorimetricData: - return this.calorimetricData(uuid); + return this.calorimetricData(id); case DataKind.GeometricData: - return this.geometricData(uuid); + return this.geometricData(id); case DataKind.HygrothermalData: - return this.hygrothermalData(uuid); + return this.hygrothermalData(id); case DataKind.LifeCycleData: - return this.lifeCycleData(uuid); + return this.lifeCycleData(id); case DataKind.OpticalData: - return this.opticalData(uuid); + return this.opticalData(id); case DataKind.PhotovoltaicData: - return this.photovoltaicData(uuid); + return this.photovoltaicData(id); default: return assertNever(dataKind); } @@ -54,6 +48,7 @@ export default { `/data/photovoltaic/${encodeURIComponent(String(id))}` as Route, createData: "/data/create" as Route, uploadFile: "/upload-file" as Route, + accessPolicies: "/access-policies" as Route, resource: (id: Scalars["Uuid"]["output"]) => `/api/resources/${encodeURIComponent(String(id))}` as Route, login: "/connect/login" as Route, diff --git a/frontend/queries/accessPolicies.graphql b/frontend/queries/accessPolicies.graphql index c0797c50..7bd956b9 100644 --- a/frontend/queries/accessPolicies.graphql +++ b/frontend/queries/accessPolicies.graphql @@ -1,4 +1,4 @@ -fragment UserAccessPolicyPartial on UserAccessPolicy { +fragment UserAccessPoliciesPartial on UserAccessPolicy { id uuid userId @@ -19,7 +19,7 @@ fragment UserAccessPolicyPartial on UserAccessPolicy { } } -fragment InstitutionAccessPolicyPartial on InstitutionAccessPolicy { +fragment InstitutionAccessPoliciesPartial on InstitutionAccessPolicy { id uuid institutionId @@ -40,13 +40,13 @@ fragment InstitutionAccessPolicyPartial on InstitutionAccessPolicy { } } -fragment OpenIdConnectApplicationAccessPolicyPartial on OpenIdConnectApplicationAccessPolicy { +fragment OpenIdConnectApplicationAccessPoliciesPartial on OpenIdConnectApplicationAccessPolicy { id uuid clientId client { uuid - name + name: clientId } isAlwaysAllowed isWithinAccessLimitInTimeSpan @@ -67,18 +67,20 @@ fragment DataAccessPoliciesPartial on DataAccessPolicy { data { uuid kind + name } + isGlobal combinator isAnyoneAllowed isNobodyAllowed userAccessPolicies { - ...UserAccessPolicyPartial + totalCount } institutionAccessPolicies { - ...InstitutionAccessPolicyPartial + totalCount } openIdConnectApplicationAccessPolicies { - ...OpenIdConnectApplicationAccessPolicyPartial + totalCount } } @@ -90,8 +92,14 @@ query DataAccessPolicies( $first: Int $after: String $where: DataAccessPolicyPropositionInput + $order: [DataAccessPolicySortInput!] ) { - connection: dataAccessPolicies(first: $first, after: $after, where: $where) { + connection: dataAccessPolicies( + first: $first + after: $after + where: $where + order: $order + ) { totalCount pageInfo { hasNextPage @@ -110,3 +118,78 @@ query DataAccessPolicy($dataId: Uuid) { ...DataAccessPolicyPartial } } + +query UserAccessPolicies( + $first: Int + $after: String + $where: UserAccessPolicyPropositionInput + $order: [UserAccessPolicySortInput!] +) { + connection: userAccessPolicies( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...UserAccessPoliciesPartial + } + } + } +} + +query InstitutionAccessPolicies( + $first: Int + $after: String + $where: InstitutionAccessPolicyPropositionInput + $order: [InstitutionAccessPolicySortInput!] +) { + connection: institutionAccessPolicies( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...InstitutionAccessPoliciesPartial + } + } + } +} + +query OpenIdConnectApplicationAccessPolicies( + $first: Int + $after: String + $where: OpenIdConnectApplicationAccessPolicyPropositionInput + $order: [OpenIdConnectApplicationAccessPolicySortInput!] +) { + connection: openIdConnectApplicationAccessPolicies( + first: $first + after: $after + where: $where + order: $order + ) { + totalCount + pageInfo { + hasNextPage + endCursor + } + edges { + node { + ...OpenIdConnectApplicationAccessPoliciesPartial + } + } + } +} diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index f3342da3..303be59a 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -878,9 +878,24 @@ type DataAccessPolicy implements Node { data: Data @cost(weight: "0") id: ID! institutionAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [InstitutionAccessPolicySortInput!] where: InstitutionAccessPolicyPropositionInput - ): [InstitutionAccessPolicy!]! + ): InstitutionAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) isAccessAllowed( institutionIds: [Uuid!] openIdConnectClientId: String @@ -890,14 +905,44 @@ type DataAccessPolicy implements Node { isGlobal: Boolean! isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [OpenIdConnectApplicationAccessPolicySortInput!] where: OpenIdConnectApplicationAccessPolicyPropositionInput - ): [OpenIdConnectApplicationAccessPolicy!]! + ): OpenIdConnectApplicationAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) updatedAt: DateTime! userAccessPolicies( + "Returns the elements in the list that come after the specified cursor." + after: String + "Returns the elements in the list that come before the specified cursor." + before: String + "Returns the first _n_ elements from the list." + first: Int + "Returns the last _n_ elements from the list." + last: Int order: [UserAccessPolicySortInput!] where: UserAccessPolicyPropositionInput - ): [UserAccessPolicy!]! + ): UserAccessPolicyConnection + @listSize( + assumedSize: 100 + slicingArguments: ["first", "last"] + slicingArgumentDefaultValue: 100 + sizedFields: ["edges", "nodes"] + requireOneSlicingArgument: false + ) uuid: Uuid! } @@ -1295,7 +1340,7 @@ type OpenEndedDateTimeRange { type OpenIdConnectApplication { clientId: String! - name: String + displayName: String uuid: Uuid! } @@ -2066,10 +2111,12 @@ input ConfigureDataAccessPoliciesPropositionInput { and: [ConfigureDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -2079,6 +2126,7 @@ input ConfigureDataAccessPoliciesPropositionInput { opticalData: OpticalDataPropositionInput or: [ConfigureDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -2196,12 +2244,15 @@ input CreateResponseApprovalsPropositionInput { appliedMethod: AppliedMethodPropositionInput approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput + createdAt: DateTimePropositionInput creatorId: UuidPropositionInput description: StringPropositionInput + id: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [CreateResponseApprovalsPropositionInput!] resources: GetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -2297,10 +2348,12 @@ input DataAccessPolicyPropositionInput { and: [DataAccessPolicyPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -2310,6 +2363,7 @@ input DataAccessPolicyPropositionInput { opticalData: OpticalDataPropositionInput or: [DataAccessPolicyPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -2385,8 +2439,11 @@ Note that durations are represented in the [ISO 8601 duration format](https://scalars.graphql.org/chillicream/duration.html#sec-Result-spec) """ input DataAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") dataId: SortEnumType @cost(weight: "10") dataKind: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input DataApprovalPropositionInput { @@ -2583,6 +2640,7 @@ input GetHttpsResourcePropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -2590,11 +2648,13 @@ input GetHttpsResourcePropositionInput { geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput or: [GetHttpsResourcePropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput } input GetHttpsResourceSortInput { @@ -2620,13 +2680,16 @@ input GetHttpsResourceTreeNonRootVertexPropositionInput { and: [GetHttpsResourceTreeNonRootVertexPropositionInput!] appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput description: StringPropositionInput hashValue: StringPropositionInput + id: UuidPropositionInput or: [GetHttpsResourceTreeNonRootVertexPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput + updatedAt: DateTimePropositionInput } input GetHttpsResourceTreeNonRootVertSortInput { @@ -2688,17 +2751,23 @@ input InstitutionAccessPoliciesPropositionInput { input InstitutionAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [InstitutionAccessPolicyPropositionInput!] + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput institutionId: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [InstitutionAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input InstitutionAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") institutionId: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input IntPropositionInput { @@ -2920,16 +2989,22 @@ input OpenIdConnectApplicationAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [OpenIdConnectApplicationAccessPolicyPropositionInput!] clientId: StringPropositionInput + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [OpenIdConnectApplicationAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput } input OpenIdConnectApplicationAccessPolicySortInput { clientId: SortEnumType @cost(weight: "10") + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") } input OpticalComponentSubtypePropositionInput { @@ -3070,6 +3145,7 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { appliedConversionMethod: ToTreeVertexAppliedConversionMethodPropositionInput archivedFilesMetaInformation: FilesMetaInformationPropositionInput calorimetricData: CalorimetricDataPropositionInput + createdAt: DateTimePropositionInput dataFormatId: UuidPropositionInput dataId: UuidPropositionInput dataKind: DataKindPropositionInput @@ -3077,11 +3153,13 @@ input RecomputeGetHttpsResourceHashValuesPropositionInput { geometricData: GeometricDataPropositionInput hashValue: StringPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput lifeCycleData: LifeCycleDataPropositionInput opticalData: OpticalDataPropositionInput or: [RecomputeGetHttpsResourceHashValuesPropositionInput!] parent: GetHttpsResourceTreeNonRootVertexPropositionInput photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput } input ReferenceInput @oneOf { @@ -3182,10 +3260,12 @@ input ResetDataAccessPoliciesPropositionInput { and: [ResetDataAccessPoliciesPropositionInput!] calorimetricData: CalorimetricDataPropositionInput combinator: LogicalCombinatorPropositionInput + createdAt: DateTimePropositionInput dataId: UuidPropositionInput dataKind: NullableOfDataKindPropositionInput geometricData: GeometricDataPropositionInput hygrothermalData: HygrothermalDataPropositionInput + id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput isAnyoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput @@ -3195,6 +3275,7 @@ input ResetDataAccessPoliciesPropositionInput { opticalData: OpticalDataPropositionInput or: [ResetDataAccessPoliciesPropositionInput!] photovoltaicData: PhotovoltaicDataPropositionInput + updatedAt: DateTimePropositionInput userAccessPolicies: UserAccessPoliciesPropositionInput } @@ -3391,12 +3472,15 @@ input UpdateResponseApprovalsPropositionInput { appliedMethod: AppliedMethodPropositionInput approvals: DataApprovalsPropositionInput componentId: UuidPropositionInput + createdAt: DateTimePropositionInput creatorId: UuidPropositionInput description: StringPropositionInput + id: UuidPropositionInput locale: StringPropositionInput name: StringPropositionInput or: [UpdateResponseApprovalsPropositionInput!] resources: GetHttpsResourcesPropositionInput + updatedAt: DateTimePropositionInput userId: UuidPropositionInput warnings: StringsPropositionInput } @@ -3469,16 +3553,22 @@ input UserAccessPoliciesPropositionInput { input UserAccessPolicyPropositionInput { accessCountSinceStartTime: CountSinceTimePropositionInput and: [UserAccessPolicyPropositionInput!] + createdAt: DateTimePropositionInput dataAccessPolicy: DataAccessPolicyPropositionInput + id: UuidPropositionInput isAlwaysAllowed: BooleanPropositionInput isWithinAccessLimitInTimeSpan: BooleanPropositionInput isWithinTimeSpan: BooleanPropositionInput or: [UserAccessPolicyPropositionInput!] + updatedAt: DateTimePropositionInput upperAccessLimitPerTimeDuration: UpperLimitPerDurationPropositionInput userId: UuidPropositionInput } input UserAccessPolicySortInput { + createdAt: SortEnumType @cost(weight: "10") + id: SortEnumType @cost(weight: "10") + updatedAt: SortEnumType @cost(weight: "10") userId: SortEnumType @cost(weight: "10") } From dbf5fac1e079e920617d986f053a0e2856964a95 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 28 Jun 2026 13:33:15 +0200 Subject: [PATCH 863/878] Log non-sensitive variables in case a GraphQL query fails --- .../ErrorLoggingDiagnosticEventListener.cs | 83 +++++++++---------- 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs index d574dfd3..16773c2a 100644 --- a/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs +++ b/backend/src/GraphQl/ErrorLoggingDiagnosticEventListener.cs @@ -8,6 +8,8 @@ using HotChocolate.Resolvers; using Microsoft.Extensions.Logging; using HotChocolate.Language; +using System.Text; +using System.Globalization; namespace Database.GraphQl; @@ -97,7 +99,8 @@ public static partial void OperationError( this ILogger logger, IOperationDocument? document, string? variables, - [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error + [TagProvider(typeof(HotChocolateIErrorTagProvider), nameof(HotChocolateIErrorTagProvider.RecordTags))] IError error, + Exception? exception ); [LoggerMessage( @@ -211,44 +214,39 @@ private sealed partial class RequestScope(ILogger" - // : variableValue.Value.ToString() - // ); - // stringBuilder.Append('\''); - // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - // } - // catch (Exception exception) - // { - // // all input type records will land here. - // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Failed stringifying the value: {exception.Message}"); - // stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); - // } - // } - // } - // _variables = stringBuilder.ToString(); + if (_variables is not null) + { + return _variables; + } + var stringBuilder = new StringBuilder(); + foreach (var variableValueCollection in context.VariableValues) + { + foreach (var variableValue in variableValueCollection) + { + try + { + stringBuilder.AppendFormat( + CultureInfo.InvariantCulture, + $"{variableValue.Name} : {variableValue.Type} = " + ); + stringBuilder.Append('\''); + stringBuilder.Append( + SecretRegex().IsMatch(variableValue.Name) + ? "" + : variableValue.Value.ToString() + ); + stringBuilder.Append('\''); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + catch (Exception exception) + { + // all input type records will land here. + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"Failed stringifying the value: {exception.Message}"); + stringBuilder.AppendFormat(CultureInfo.InvariantCulture, $"{Environment.NewLine}"); + } + } + } + _variables = stringBuilder.ToString(); _variables = null; return _variables; } @@ -272,14 +270,9 @@ public void Dispose() { foreach (var error in operationResult.Errors) { - logger.OperationError(context.Request.Document, StringifyVariables(), error); + logger.OperationError(context.Request.Document, StringifyVariables(), error, error.Exception); } } - // TODO Where is the exception now? - // if (context.Exception is { }) - // { - // logger.UnexpectedExecutionException(context.Request.Document, StringifyVariables(), context.Exception); - // } } } } \ No newline at end of file From c74ebbd5628452ddabc0d7cad3e2ff0cc08e604b Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 28 Jun 2026 14:01:17 +0200 Subject: [PATCH 864/878] Link to OpenAPI schema and playground from uploading files --- frontend/pages/upload-file.tsx | 23 ++++++++++++++++++++++- frontend/paths.ts | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/frontend/pages/upload-file.tsx b/frontend/pages/upload-file.tsx index 0fd56dec..bac20382 100644 --- a/frontend/pages/upload-file.tsx +++ b/frontend/pages/upload-file.tsx @@ -52,7 +52,7 @@ function Page() { @@ -67,6 +67,27 @@ function Page() { + + The form sends a multipart/form post request to the{" "} + RESTful{" "} + endpoint{" "} + + {paths.resource("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")} + + . It is documented in the{" "} + + OpenAPI schema + {" "} + and can be played with on{" "} + + OpenAPI playground + + . + diff --git a/frontend/paths.ts b/frontend/paths.ts index cfc628dc..8ccd9cf4 100644 --- a/frontend/paths.ts +++ b/frontend/paths.ts @@ -54,6 +54,8 @@ export default { login: "/connect/login" as Route, logout: "/connect/logout" as Route, graphQl: "/graphql/" as Route, + openApiSchema: "/openapi/v1.json" as Route, + openApiPlayground: (tag?: string) => `/openapi/docs/${tag}` as Route, metabase: { home: metabaseUrl, graphQl: new URL("/graphql/", metabaseUrl).href as Route, From e2007597c097053097a6be29815f39dfcf1daaa2 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 28 Jun 2026 15:13:56 +0200 Subject: [PATCH 865/878] Pipe the query for meta information on data sets and the request for actual resource content through the metabase (because it adds additional tokens that may be needed to access some data/resources and it also forwards the given access token in a secure way to product-data databases) --- backend/src/ApiRequests/DataDataLoader.cs | 27 +++- backend/src/ApiRequests/Queries/Data.graphql | 27 +++- backend/src/AppSettings.cs | 13 ++ .../AccessPolicies/DataAccessPolicyType.cs | 6 +- backend/src/GraphQl/Methods/MethodQueries.cs | 138 ++++++++---------- backend/src/Methods/IMethod.cs | 1 - backend/src/Services/ApiRequestService.cs | 43 ++++-- backend/src/Utilities/Sha256FileHasher.cs | 3 + frontend/components/UuidFormItem.tsx | 2 +- 9 files changed, 147 insertions(+), 113 deletions(-) diff --git a/backend/src/ApiRequests/DataDataLoader.cs b/backend/src/ApiRequests/DataDataLoader.cs index 48875699..217c4c36 100644 --- a/backend/src/ApiRequests/DataDataLoader.cs +++ b/backend/src/ApiRequests/DataDataLoader.cs @@ -17,13 +17,34 @@ public static class DataDataLoader public static Uri GetGraphQlEndpoint(AppSettings appSettings) => appSettings.MetabaseGraphQlEndpoint; + public sealed record DataFormat( + Guid Id, + string MediaType + ); + + public sealed record Resource( + string HashValue, + Uri Locator, + DataFormat DataFormat + ); + + public sealed record Root( + string VertexId, + Resource Value + ); + + public sealed record ResourceTree( + Root Root + ); + public sealed record Data( - Guid Uuid, - DataKind Kind + Guid Id, + DataKind Kind, + ResourceTree ResourceTree ); public sealed record Database( - Guid Uuid, + Guid Id, Data? Data ); diff --git a/backend/src/ApiRequests/Queries/Data.graphql b/backend/src/ApiRequests/Queries/Data.graphql index 404bdabb..2038dc70 100644 --- a/backend/src/ApiRequests/Queries/Data.graphql +++ b/backend/src/ApiRequests/Queries/Data.graphql @@ -1,11 +1,22 @@ query Data($databaseId: Uuid!, $dataId: Uuid!, $dataKind: DataKind!) { - database(id: $databaseId) - { - uuid - data(id: $dataId, kind: $dataKind) - { - uuid - kind + database(id: $databaseId) { + id: uuid + data(id: $dataId, kind: $dataKind) { + id: uuid + kind + resourceTree { + root { + vertexId + value { + hashValue + locator + dataFormat { + id: uuid + mediaType + } + } } + } } -} \ No newline at end of file + } +} diff --git a/backend/src/AppSettings.cs b/backend/src/AppSettings.cs index 5c93fb04..76397a29 100644 --- a/backend/src/AppSettings.cs +++ b/backend/src/AppSettings.cs @@ -1,6 +1,8 @@ // Inspired by https://weblog.west-wind.com/posts/2017/dec/12/easy-configuration-binding-in-aspnet-core-revisited using System; +using System.Text.Encodings.Web; +using Database.Data; using Npgsql; namespace Database; @@ -19,6 +21,17 @@ public sealed record AppSettings public string MetabaseHost { private get; init; } = ""; public Uri MetabaseHostUri => new($"https://{MetabaseHost}", UriKind.Absolute); public Uri MetabaseGraphQlEndpoint => new UriBuilder(MetabaseHostUri) { Path = GraphQlPathSegment }.Uri; + public Uri MetabaseGetHttpsResourceEndpoint( + string vertexId, + CrossDatabaseDataReference data, + UrlEncoder urlEncoder + ) + => new UriBuilder(MetabaseHostUri) + { + Path = $"/api/resources/{urlEncoder.Encode(vertexId)}", + Query = $"?dataId={urlEncoder.Encode(data.DataId.ToString("D"))}&dataKind={urlEncoder.Encode(data.DataKind.ToString())}&databaseId={urlEncoder.Encode(data.DatabaseId.ToString("D"))}" + } + .Uri; public Guid DatabaseId { get; init; } public Guid OperatorId { get; init; } diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index c49cab8c..c0aa64eb 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -195,7 +195,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - public static async ValueTask> GetUserAccessPoliciesAsync( + public static async Task> GetUserAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, ApplicationDbContext databaseContext, @@ -219,7 +219,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - public static async ValueTask> GetInstitutionAccessPoliciesAsync( + public static async Task> GetInstitutionAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, ApplicationDbContext databaseContext, @@ -243,7 +243,7 @@ CancellationToken cancellationToken [UsePaging] [UseFiltering] [UseSorting] - public static async ValueTask> GetOpenIdConnectApplicationAccessPoliciesAsync( + public static async Task> GetOpenIdConnectApplicationAccessPoliciesAsync( [Parent] DataAccessPolicy dataAccessPolicy, IResolverContext resolverContext, ApplicationDbContext databaseContext, diff --git a/backend/src/GraphQl/Methods/MethodQueries.cs b/backend/src/GraphQl/Methods/MethodQueries.cs index f648729f..5b5e31aa 100644 --- a/backend/src/GraphQl/Methods/MethodQueries.cs +++ b/backend/src/GraphQl/Methods/MethodQueries.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Net.Mime; +using System.Text.Encodings.Web; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Database.ApiRequests; -using Database.Enumerations; using Database.Json; using Database.Services; -using GraphQL; using GraphQL.Client.Abstractions.Utilities; using HotChocolate; using HotChocolate.Resolvers; @@ -21,7 +21,10 @@ public enum CalculateMethodErrorCode { UNKNOWN_METHOD, UNKNOWN_DATABASE, - DATA_QUERY_FAILED + UNKNOWN_DATA, + UNSUPPORTED_DATA_FORMAT, + HASH_VALUE_CALCULATION_FAILED, + HASH_VALUE_MISMATHCH } public sealed record CalculateMethodError( @@ -39,28 +42,6 @@ public sealed record CalculateMethodPayload( [ExtendObjectType(nameof(Query))] public sealed class MethodQueries { - private sealed record DataData( - Data? Data - ); - - private sealed record Data( - ResourceTree ResourceTree - ); - - private sealed record ResourceTree( - Root Root - ); - - private sealed record Root( - Resource Value - ); - - private sealed record Resource( - string HashValue, - Uri Locator, - Guid DataFormatId - ); - public async Task CalculateMethodWithDataUploadAsync( Guid methodId, [GraphQLType>] IFile data, @@ -96,9 +77,10 @@ public async Task CalculateMethodAsync( AppSettings appSettings, MethodFactory methodFactory, ApiRequestService apiRequestService, - IDatabaseByIdDataLoader databaseById, + IDataByDatabaseAndIdAndKindDataLoader dataByDatabaseAndIdAndKind, QueryData queryData, IResolverContext resolverContext, + UrlEncoder urlEncoder, CancellationToken cancellationToken ) { @@ -114,9 +96,11 @@ [new CalculateMethodError( )] ); } + // We fetch the data through the metabase because, if possible, it adds + // sanitized authorization headers. var database = await GraphQlRequestHelper.TransformExceptionsAsync( - () => databaseById.LoadAsync( - dataReference.DatabaseId, + () => dataByDatabaseAndIdAndKind.LoadAsync( + (dataReference.DatabaseId, dataReference.DataId, dataReference.DataKind), cancellationToken ), resolverContext, @@ -133,68 +117,60 @@ [new CalculateMethodError( )] ); } - var query = ConstructQuery(dataReference.DataKind); - GraphQLResponse? response; - response = await GraphQlRequestHelper.TransformExceptionsAsync( - () => queryData.Do( - database.Locator, - dataReference.DataId, - query, - cancellationToken - ), - resolverContext, - database.Locator - ); - if (response.Data?.Data is null) + if (database.Data is null) { - if (response.Errors?.Length > 0) - { - foreach (var error in response.Errors) - { - var errorBuilder = ErrorBuilder.New() - .SetCode("DATABASE_QUERY_ERROR") - // .SetPath(error.Path) // TODO Add the error path. Just using `error.Path` does not work as it contains non-"GraphQlName"s according to HotChocolate sometimes. - .SetMessage($"The GraphQL response received from the database {database.Locator} for the query {query} reported the error {error.Message}."); - resolverContext.ReportError(errorBuilder.Build()); - } - } return new CalculateMethodPayload( null, [new CalculateMethodError( - CalculateMethodErrorCode.DATA_QUERY_FAILED, - $"Failed to query database {database.Locator} for the data.", + CalculateMethodErrorCode.UNKNOWN_DATA, + $"The data is unknown.", + [nameof(dataReference), nameof(dataReference.DataId).ToLowerFirst()] + )] + ); + } + // TODO Support non-JSON data formats. + var dataFormat = database.Data.ResourceTree.Root.Value.DataFormat; + if (dataFormat.MediaType != MediaTypeNames.Application.Json) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.UNSUPPORTED_DATA_FORMAT, + $"The data format {dataFormat.Id} of the root resource is unsupported because its media type is not '{MediaTypeNames.Application.Json}' but '{dataFormat.MediaType}'.", [nameof(dataReference)] )] ); } - var locator = response.Data.Data.ResourceTree.Root.Value.Locator; - // TODO The locator could also point to a non-JSON resource. Support those also: response.Data.Data.ResourceTree.Root.Value.DataFormatId - // TODO Check that the data has the hash value: response.Data.Data.ResourceTree.Root.Value.HashValue; - var data = await apiRequestService.PerformHttpGetRequest( + // We route to `response.Data.Data.ResourceTree.Root.Value.Locator` + // through the metabase because, if possible, it adds sanitized + // authorization headers. + var locator = appSettings.MetabaseGetHttpsResourceEndpoint(database.Data.ResourceTree.Root.VertexId, dataReference.ToDomainModel(), urlEncoder); + var (resourceContent, sha256HashValue) = await apiRequestService.PerformHttpGetRequest( locator, cancellationToken ); - var result = method.Calculate(data); + if (sha256HashValue is null) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.HASH_VALUE_CALCULATION_FAILED, + $"Could not calculate the SHA256 hash value of the root resource, which is needed to make sure that the data was not tempered with.", + [nameof(dataReference)] + )] + ); + } + if (sha256HashValue != database.Data.ResourceTree.Root.Value.HashValue) + { + return new CalculateMethodPayload( + null, + [new CalculateMethodError( + CalculateMethodErrorCode.HASH_VALUE_MISMATHCH, + $"The SHA256 hash value '{sha256HashValue}' of the received root-resource content is different from the expected value '{database.Data.ResourceTree.Root.Value.HashValue}'.", + [nameof(dataReference)] + )] + ); + } + var result = method.Calculate(resourceContent); return new CalculateMethodPayload(result, null); } - - private static string ConstructQuery(DataKind dataKind) - { - var name = Enum.GetName(dataKind)![..^"_DATA".Length].ToLowerInvariant(); - return - $$""" - query Data($id: Uuid!) { - data: {{name}}Data(id: $id) { - resourceTree { - root { - value { - hashValue - locator - dataFormatId - } - } - } - } - } - """; - } -} +} \ No newline at end of file diff --git a/backend/src/Methods/IMethod.cs b/backend/src/Methods/IMethod.cs index 6389d6af..cba4bedb 100644 --- a/backend/src/Methods/IMethod.cs +++ b/backend/src/Methods/IMethod.cs @@ -1,4 +1,3 @@ -using System; using System.Text.Json; namespace Database.Methods; diff --git a/backend/src/Services/ApiRequestService.cs b/backend/src/Services/ApiRequestService.cs index f9d2398a..15e4cfc2 100644 --- a/backend/src/Services/ApiRequestService.cs +++ b/backend/src/Services/ApiRequestService.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Net.Http.Headers; using System.Net.Mime; +using System.Security.Cryptography; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -82,7 +83,7 @@ CancellationToken cancellationToken ); } - public Task PerformHttpGetRequest( + public Task<(JsonElement Json, string? Sha256Hash)> PerformHttpGetRequest( Uri url, CancellationToken cancellationToken ) @@ -92,11 +93,15 @@ CancellationToken cancellationToken url, async httpResponseContent => { + using var contentStream = await httpResponseContent.ReadAsStreamAsync(); + using var sha256 = SHA256.Create(); + using var cryptoStream = new CryptoStream(contentStream, sha256, CryptoStreamMode.Read); using var document = await JsonDocument.ParseAsync( - await httpResponseContent.ReadAsStreamAsync(), + cryptoStream, JsonDocumentSettings.Lax ); - return document.RootElement.Clone(); + var sha256Hash = sha256.Hash is null ? null : Convert.ToHexString(sha256.Hash); + return (document.RootElement.Clone(), sha256Hash); }, null, cancellationToken @@ -158,23 +163,11 @@ private async Task Query( CancellationToken cancellationToken ) { - using var httpClient = httpClientFactory.CreateClient(CustomHttpClient); - var bearerToken = httpContextAccessor.HttpContext?.ExtractBearerToken(); + using var httpClient = CreateHttpClientAsync(); using var httpRequestMessage = new HttpRequestMessage( httpMethod, uri ); - httpRequestMessage.Headers.Add( - HeaderNames.Origin, - appSettings.Uri.AbsoluteUri - ); - if (bearerToken is not null) - { - httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue( - OpenIdConnectConstants.AuthorizationHeaderBearer, - bearerToken - ); - } httpRequestMessage.Content = httpContent; using var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage, cancellationToken); @@ -189,6 +182,24 @@ CancellationToken cancellationToken return await read(httpResponseMessage.Content); } + private HttpClient CreateHttpClientAsync() + { + var httpClient = httpClientFactory.CreateClient(CustomHttpClient); + var accessToken = httpContextAccessor.HttpContext?.ExtractBearerToken(); + httpClient.DefaultRequestHeaders.Add( + HeaderNames.Origin, + appSettings.Uri.AbsoluteUri + ); + if (accessToken is not null) + { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + OpenIdConnectConstants.AuthorizationHeaderBearer, + accessToken + ); + } + return httpClient; + } + private static ByteArrayContent MakeGraphQlJsonHttpContent( TContent content ) diff --git a/backend/src/Utilities/Sha256FileHasher.cs b/backend/src/Utilities/Sha256FileHasher.cs index 4401e838..d2d013e3 100644 --- a/backend/src/Utilities/Sha256FileHasher.cs +++ b/backend/src/Utilities/Sha256FileHasher.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.Contracts; using System.IO; using System.Security.Cryptography; using System.Text; @@ -9,6 +10,7 @@ namespace Database.Utilities; public static class Sha256FileHasher { + [Pure] public static async Task ComputeForFile(string filePath, CancellationToken cancellationToken) { if (!File.Exists(filePath)) @@ -22,6 +24,7 @@ await sha256.ComputeHashAsync(fileStream, cancellationToken) ); } + [Pure] public static string ComputeForString(string content) { return Convert.ToHexString( diff --git a/frontend/components/UuidFormItem.tsx b/frontend/components/UuidFormItem.tsx index 91e9aae8..f8abc5ac 100644 --- a/frontend/components/UuidFormItem.tsx +++ b/frontend/components/UuidFormItem.tsx @@ -31,7 +31,7 @@ export default function UuidFormItem({ ); From cb1858a7a53dbd3cc970cb486e7a5157751b2745 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Sun, 28 Jun 2026 15:56:06 +0200 Subject: [PATCH 866/878] Tell to keep in sync with reverse proxy of machine --- nginx/templates/default.conf.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nginx/templates/default.conf.template b/nginx/templates/default.conf.template index a3629597..5722387a 100644 --- a/nginx/templates/default.conf.template +++ b/nginx/templates/default.conf.template @@ -1,5 +1,5 @@ -# Inspired by https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#https-configuration -# and https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx?view=aspnetcore-3.1#configure-nginx +# Keep in sync with the NGINX config of the reverse proxy of the machine project +# ------------------------------------------------------------------------------ # `127.0.0.11` is the default IP address for Docker's embedded DNS server. You can verify it by running `cat /etc/resolv.conf` inside a container. # `valid=5s` forces NGINX to re-resolve hostnames every 30 seconds. This is critical in Docker because container IPs can change during a docker-compose restart. From e13e401684757229e4e518ca9dd2ca63e761fdb4 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 29 Jun 2026 14:50:08 +0200 Subject: [PATCH 867/878] Use `everyone allowed` instead of `anyone anyone` as it communicates its intend more clearly --- .../Data/AccessPolicies/DataAccessPolicy.cs | 8 ++++---- .../DataAccessPolicyFilterType.cs | 2 +- .../AccessPolicies/DataAccessPolicyType.cs | 6 +++--- backend/src/GraphQl/DataX/DataResolvers.cs | 2 +- backend/src/GraphQl/DataX/DataTypeBase.cs | 4 ++-- .../GraphQlSchemaTests.IsUnchanged.snap | 20 +++++++++---------- .../data/DataAccessPolicySummary.tsx | 6 +++--- frontend/pages/access-policies/index.tsx | 2 +- frontend/queries/accessPolicies.graphql | 2 +- frontend/type-defs.graphqls | 20 +++++++++---------- 10 files changed, 36 insertions(+), 36 deletions(-) diff --git a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs index 5999a92e..ecf9f211 100644 --- a/backend/src/Data/AccessPolicies/DataAccessPolicy.cs +++ b/backend/src/Data/AccessPolicies/DataAccessPolicy.cs @@ -53,7 +53,7 @@ is configured through the combinator and the mutation access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -63,7 +63,7 @@ A data access policy is either the one-and-only global one or associated with a conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -240,7 +240,7 @@ private void AssertThatExactlyOneDataIdIsNonNull() && OpenIdConnectApplicationAccessPolicies.Count == 0; [Projectable] - public bool IsAnyoneAllowed => IsAccessAllowed(null, null, null); + public bool IsEveryoneAllowed => IsAccessAllowed(null, null, null); [Projectable] public bool IsAccessAllowed( @@ -303,4 +303,4 @@ LogicalCombinator combinator { Combinator = combinator; } -} +} \ No newline at end of file diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs index 7a6d861f..0c5ebb03 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyFilterType.cs @@ -19,7 +19,7 @@ IFilterInputTypeDescriptor descriptor descriptor.Field(_ => _.UpdatedAt); descriptor.Field(_ => _.IsGlobal); descriptor.Field(_ => _.IsNobodyAllowed); - descriptor.Field(_ => _.IsAnyoneAllowed); + descriptor.Field(_ => _.IsEveryoneAllowed); descriptor.Field(_ => _.Combinator); descriptor.Field(_ => _.UserAccessPolicies); descriptor.Field(_ => _.InstitutionAccessPolicies); diff --git a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs index c0aa64eb..95ff4b63 100644 --- a/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs +++ b/backend/src/GraphQl/AccessPolicies/DataAccessPolicyType.cs @@ -98,10 +98,10 @@ IObjectTypeDescriptor descriptor .ResolveWith(t => Resolvers.IsNobodyAllowedAsync(default!, default!, default!, default!)); descriptor - .Field(_ => _.IsAnyoneAllowed) + .Field(_ => _.IsEveryoneAllowed) .Cost(0) .ResolveWith(t => - Resolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!)); + Resolvers.IsEveryoneAllowedAsync(default!, default!, default!, default!)); descriptor .Field(_ => _.IsAccessAllowed(default!, default!, default!)) .Cost(0) @@ -153,7 +153,7 @@ CancellationToken cancellationToken is not null; } - public static Task IsAnyoneAllowedAsync( + public static Task IsEveryoneAllowedAsync( [Parent] DataAccessPolicy dataAccessPolicy, IDataAccessPolicyByDataIdDataLoader policyByDataIdDataLoader, ApplicationDbContext databaseContext, diff --git a/backend/src/GraphQl/DataX/DataResolvers.cs b/backend/src/GraphQl/DataX/DataResolvers.cs index cdcd0fff..46c33753 100644 --- a/backend/src/GraphQl/DataX/DataResolvers.cs +++ b/backend/src/GraphQl/DataX/DataResolvers.cs @@ -122,7 +122,7 @@ CancellationToken cancellationToken } [Authorize(Policy = AuthorizationPolicies.AuthenticatedPolicy)] - public static Task IsAnyoneAllowedAsync( + public static Task IsEveryoneAllowedAsync( [Parent] IData data, ApplicationDbContext databaseContext, CommonAuthorization authorization, diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index 32ad7487..d76f4bb8 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -45,9 +45,9 @@ IObjectTypeDescriptor descriptor .Cost(0) .ResolveWith(_ => DataResolvers.IsNobodyAllowedAsync(default!, default!, default!, default!, default!)); descriptor - .Field(nameof(DataResolvers.IsAnyoneAllowedAsync)[..^"Async".Length]) + .Field(nameof(DataResolvers.IsEveryoneAllowedAsync)[..^"Async".Length]) .Cost(0) - .ResolveWith(_ => DataResolvers.IsAnyoneAllowedAsync(default!, default!, default!, default!, default!)); + .ResolveWith(_ => DataResolvers.IsEveryoneAllowedAsync(default!, default!, default!, default!, default!)); descriptor .Field(nameof(DataResolvers.IsAccessAllowedAsync)[..^"Async".Length]) .Cost(0) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index 303be59a..a88c862d 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -562,7 +562,7 @@ type CalorimetricData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -901,7 +901,7 @@ type DataAccessPolicy implements Node { openIdConnectClientId: String userId: Uuid ): Boolean! - isAnyoneAllowed: Boolean! + isEveryoneAllowed: Boolean! isGlobal: Boolean! isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( @@ -1057,7 +1057,7 @@ type GeometricData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1171,7 +1171,7 @@ type HygrothermalData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1278,7 +1278,7 @@ type LifeCycleData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1401,7 +1401,7 @@ type OpticalData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1472,7 +1472,7 @@ type PhotovoltaicData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -2118,7 +2118,7 @@ input ConfigureDataAccessPoliciesPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput @@ -2355,7 +2355,7 @@ input DataAccessPolicyPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput @@ -3267,7 +3267,7 @@ input ResetDataAccessPoliciesPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput diff --git a/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx b/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx index 3d60ee33..8fc7e8d4 100644 --- a/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx +++ b/frontend/components/accessPolicies/data/DataAccessPolicySummary.tsx @@ -46,9 +46,9 @@ export default function DataAccessPolicySummary({ {entity.isGlobal ? "global" : "local"} ), - entity.isAnyoneAllowed && ( - - anyone allowed + entity.isEveryoneAllowed && ( + + everyone allowed ), entity.isNobodyAllowed && ( diff --git a/frontend/pages/access-policies/index.tsx b/frontend/pages/access-policies/index.tsx index ba5360d4..76573b7c 100644 --- a/frontend/pages/access-policies/index.tsx +++ b/frontend/pages/access-policies/index.tsx @@ -54,7 +54,7 @@ export default function Page() { where={{ and: [ { isGlobal: { equalTo: false } }, - { isAnyoneAllowed: { equalTo: false } }, + { isEveryoneAllowed: { equalTo: false } }, ], }} /> diff --git a/frontend/queries/accessPolicies.graphql b/frontend/queries/accessPolicies.graphql index 7bd956b9..a6ac9616 100644 --- a/frontend/queries/accessPolicies.graphql +++ b/frontend/queries/accessPolicies.graphql @@ -71,7 +71,7 @@ fragment DataAccessPoliciesPartial on DataAccessPolicy { } isGlobal combinator - isAnyoneAllowed + isEveryoneAllowed isNobodyAllowed userAccessPolicies { totalCount diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index 303be59a..a88c862d 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -562,7 +562,7 @@ type CalorimetricData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -901,7 +901,7 @@ type DataAccessPolicy implements Node { openIdConnectClientId: String userId: Uuid ): Boolean! - isAnyoneAllowed: Boolean! + isEveryoneAllowed: Boolean! isGlobal: Boolean! isNobodyAllowed: Boolean! openIdConnectApplicationAccessPolicies( @@ -1057,7 +1057,7 @@ type GeometricData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1171,7 +1171,7 @@ type HygrothermalData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1278,7 +1278,7 @@ type LifeCycleData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1401,7 +1401,7 @@ type OpticalData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -1472,7 +1472,7 @@ type PhotovoltaicData implements Node & Data { openIdConnectClientId: String userId: Uuid ): Boolean! - IsAnyoneAllowed: Boolean! + IsEveryoneAllowed: Boolean! IsNobodyAllowed: Boolean! kind: DataKind! locale: Locale! @@ -2118,7 +2118,7 @@ input ConfigureDataAccessPoliciesPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput @@ -2355,7 +2355,7 @@ input DataAccessPolicyPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput @@ -3267,7 +3267,7 @@ input ResetDataAccessPoliciesPropositionInput { hygrothermalData: HygrothermalDataPropositionInput id: UuidPropositionInput institutionAccessPolicies: InstitutionAccessPoliciesPropositionInput - isAnyoneAllowed: BooleanPropositionInput + isEveryoneAllowed: BooleanPropositionInput isGlobal: BooleanPropositionInput isNobodyAllowed: BooleanPropositionInput lifeCycleData: LifeCycleDataPropositionInput From 4756d52b4dcbd21ee6c894094ff7e11b653cf760 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 29 Jun 2026 17:48:33 +0200 Subject: [PATCH 868/878] Inform user when a data set is not accessible due to data access policy --- .../GetHttpsResourcesController.cs | 2 +- .../CalorimetricDataQueries.cs | 6 ++++ backend/src/GraphQl/DataX/DataQueries.cs | 22 +++++++----- backend/src/GraphQl/DataX/DataQueriesBase.cs | 36 ++++++++++++------- .../GeometricDataX/GeometricDataQueries.cs | 6 ++++ .../HygrothermalDataQueries.cs | 6 ++++ .../LifeCycleDataX/LifeCycleDataQueries.cs | 6 ++++ .../OpticalDataX/OpticalDataQueries.cs | 6 ++++ .../PhotovoltaicDataQueries.cs | 8 +++-- backend/src/Services/AccessPolicyService.cs | 6 ++-- 10 files changed, 76 insertions(+), 28 deletions(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index 94722053..c063bade 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -185,7 +185,7 @@ public async Task Get( instance: HttpContext.Request.Path ); } - if (!await accessPolicyService.Apply( + if (!await accessPolicyService.ApplyAsync( databaseContext => databaseContext.Data(getHttpsResource.Data.Kind).AsNoTracking() .Where(_ => _.Id == getHttpsResource.DataId), async policedData => diff --git a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs index 7bf1b2d1..d1337e06 100644 --- a/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs +++ b/backend/src/GraphQl/CalorimetricDataX/CalorimetricDataQueries.cs @@ -86,6 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -95,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.CalorimetricData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/DataX/DataQueries.cs b/backend/src/GraphQl/DataX/DataQueries.cs index 91633929..5a73b80f 100644 --- a/backend/src/GraphQl/DataX/DataQueries.cs +++ b/backend/src/GraphQl/DataX/DataQueries.cs @@ -1,12 +1,13 @@ using System; -using System.Linq; using System.Threading; using System.Threading.Tasks; +using Database.Authorization; using Database.Data; using Database.Enumerations; using Database.GraphQl.Scalars; using Database.Services; using HotChocolate; +using HotChocolate.Resolvers; using HotChocolate.Types; using Microsoft.EntityFrameworkCore; @@ -21,18 +22,21 @@ public sealed class DataQueries [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return accessPolicyService.Apply( - databaseContext => databaseContext.Data(dataKind).AsNoTracking() - .Where(_ => _.Id == id), - async policedData => - { - var node = await policedData.SingleOrDefaultAsync(cancellationToken); - return (node is null ? [] : [node], node); - }, + return DataQueriesBase.GetDataAsync( + id, + locale, + databaseContext => databaseContext.Data(dataKind), databaseContextFactory, + accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/DataX/DataQueriesBase.cs b/backend/src/GraphQl/DataX/DataQueriesBase.cs index a63958f6..1b1616e1 100644 --- a/backend/src/GraphQl/DataX/DataQueriesBase.cs +++ b/backend/src/GraphQl/DataX/DataQueriesBase.cs @@ -16,10 +16,10 @@ namespace Database.GraphQl.DataX; public abstract class DataQueriesBase -where TData : class, IData + where TData : class, IData { - protected Task> GetAllDataAsync( - Func> getData, + protected static Task> GetAllDataAsync( + Func> getData, [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, @@ -27,7 +27,7 @@ public abstract class DataQueriesBase CancellationToken cancellationToken ) { - return accessPolicyService.Apply( + return accessPolicyService.ApplyAsync( databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), @@ -44,8 +44,8 @@ CancellationToken cancellationToken ); } - protected async Task> GetAllPendingDataAsync( - Func> getData, + protected static async Task> GetAllPendingDataAsync( + Func> getData, [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, @@ -62,7 +62,7 @@ CancellationToken cancellationToken .ToPageAsync(resolverContext.GetPagingArguments(), cancellationToken) .ToConnectionAsync(); } - return await accessPolicyService.Apply( + return await accessPolicyService.ApplyAsync( databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState == Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), @@ -79,8 +79,8 @@ CancellationToken cancellationToken ); } - protected Task HasDataAsync( - Func> getData, + protected static Task HasDataAsync( + Func> getData, [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, @@ -88,7 +88,7 @@ protected Task HasDataAsync( CancellationToken cancellationToken ) { - return accessPolicyService.Apply( + return accessPolicyService.ApplyAsync( databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.PublishingState != Enumerations.PublishingState.PENDING) .With(resolverContext.GetQueryContext(), Sorting.DefaultEntityOrder), @@ -102,21 +102,31 @@ CancellationToken cancellationToken ); } - protected Task GetDataAsync( + internal static async Task GetDataAsync( Guid id, [GraphQLType] string? locale, - Func> getData, + Func> getData, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { - return accessPolicyService.Apply( + var exists = await getData(databaseContext).AsNoTracking() + .Where(_ => _.Id == id) + .AnyAsync(cancellationToken); + return await accessPolicyService.ApplyAsync( databaseContext => getData(databaseContext).AsNoTracking() .Where(_ => _.Id == id), async policedData => { var node = await policedData.SingleOrDefaultAsync(cancellationToken); + if (exists && node is null) + { + authorization.ReportUnauthorizedError(resolverContext); + } return (node is null ? [] : [node], node); }, databaseContextFactory, diff --git a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs index 9b3f9201..8227f0f6 100644 --- a/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs +++ b/backend/src/GraphQl/GeometricDataX/GeometricDataQueries.cs @@ -86,6 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -95,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.GeometricData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs index a1cb4744..2989dd8c 100644 --- a/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs +++ b/backend/src/GraphQl/HygrothermalDataX/HygrothermalDataQueries.cs @@ -86,6 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -95,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.HygrothermalData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs index f1d172c8..1430575b 100644 --- a/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs +++ b/backend/src/GraphQl/LifeCycleDataX/LifeCycleDataQueries.cs @@ -86,6 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -95,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.LifeCycleData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs index 34271bea..085a0059 100644 --- a/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs +++ b/backend/src/GraphQl/OpticalDataX/OpticalDataQueries.cs @@ -86,6 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -95,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.OpticalData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs index 13b2b069..6688e878 100644 --- a/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs +++ b/backend/src/GraphQl/PhotovoltaicDataX/PhotovoltaicDataQueries.cs @@ -10,7 +10,6 @@ using HotChocolate.Data; using HotChocolate.Resolvers; using HotChocolate.Types; -using NodaTime; using Microsoft.EntityFrameworkCore; namespace Database.GraphQl.PhotovoltaicDataX; @@ -87,7 +86,9 @@ CancellationToken cancellationToken [GraphQLType] string? locale, IDbContextFactory databaseContextFactory, AccessPolicyService accessPolicyService, - IClock clock, + ApplicationDbContext databaseContext, + IResolverContext resolverContext, + CommonAuthorization authorization, CancellationToken cancellationToken ) { @@ -97,6 +98,9 @@ CancellationToken cancellationToken databaseContext => databaseContext.PhotovoltaicData, databaseContextFactory, accessPolicyService, + databaseContext, + resolverContext, + authorization, cancellationToken ); } diff --git a/backend/src/Services/AccessPolicyService.cs b/backend/src/Services/AccessPolicyService.cs index c7140037..70421590 100644 --- a/backend/src/Services/AccessPolicyService.cs +++ b/backend/src/Services/AccessPolicyService.cs @@ -29,7 +29,7 @@ public sealed class AccessPolicyService( ILogger logger ) { - public async Task Apply( + public async Task ApplyAsync( Func> getData, Func, Task<(IReadOnlyList Data, TResult Result)>> then, IDbContextFactory databaseContextFactory, @@ -52,7 +52,7 @@ CancellationToken cancellationToken logger.ApplyingAccessPolicy(currentUser?.Uuid, institutionIds, openIdConnectClientId); var policedData = PoliceData(getData(databaseContext), currentUser?.Uuid, institutionIds, openIdConnectClientId, databaseContext); var (postProcessedData, result) = await then(policedData); - await IncrementAccessCounts( + await IncrementAccessCountsAsync( postProcessedData, openIdConnectClientId, currentUser?.Uuid, @@ -87,7 +87,7 @@ ApplicationDbContext databaseContext ); } - private async Task IncrementAccessCounts( + private async Task IncrementAccessCountsAsync( IReadOnlyList allData, string? openIdConnectClientId, Guid? userId, From e5f626069a56eb5cdf479399b07ca607792d5c1d Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 29 Jun 2026 17:56:07 +0200 Subject: [PATCH 869/878] Make publishing state available via GraphQL --- backend/src/GraphQl/DataX/DataType.cs | 3 --- backend/src/GraphQl/DataX/DataTypeBase.cs | 3 --- .../ResponseApprovals/CreateResponseApprovalsMutation.cs | 3 +-- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/backend/src/GraphQl/DataX/DataType.cs b/backend/src/GraphQl/DataX/DataType.cs index 8f2423cd..d5657bb3 100644 --- a/backend/src/GraphQl/DataX/DataType.cs +++ b/backend/src/GraphQl/DataX/DataType.cs @@ -19,9 +19,6 @@ IInterfaceTypeDescriptor descriptor { // `1..` is a range as introduced in https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#indices-and-ranges descriptor.Name(nameof(IData)[1..]); - descriptor - .Field(_ => _.PublishingState) - .Ignore(); descriptor .Field(GraphQlConstants.IdFieldName) .Type>(); diff --git a/backend/src/GraphQl/DataX/DataTypeBase.cs b/backend/src/GraphQl/DataX/DataTypeBase.cs index d76f4bb8..7bd3eed1 100644 --- a/backend/src/GraphQl/DataX/DataTypeBase.cs +++ b/backend/src/GraphQl/DataX/DataTypeBase.cs @@ -19,9 +19,6 @@ IObjectTypeDescriptor descriptor ) { base.Configure(descriptor); - descriptor - .Field(_ => _.PublishingState) - .Ignore(); descriptor .Field(_ => _.UpdatedAt) .Name(DataType.TimestampFieldName); diff --git a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs index 06b6e35e..1e91b2b5 100644 --- a/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs +++ b/backend/src/GraphQl/ResponseApprovals/CreateResponseApprovalsMutation.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Database.Authorization; @@ -133,4 +132,4 @@ await Parallel.ForEachAsync( } return NewPayload(dataSets, null); } -} +} \ No newline at end of file From 3bd1e63a8da77fdbee72299ebb097cb4fca0353c Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Mon, 29 Jun 2026 19:32:36 +0200 Subject: [PATCH 870/878] Update GraphQL schema --- .../GraphQlSchemaTests.IsUnchanged.snap | 38 +++++++++++++------ frontend/type-defs.graphqls | 38 +++++++++++++------ 2 files changed, 54 insertions(+), 22 deletions(-) diff --git a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap index a88c862d..751e5e99 100644 --- a/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap +++ b/backend/test/Integration/GraphQl/__snapshots__/GraphQlSchemaTests.IsUnchanged.snap @@ -567,6 +567,7 @@ type CalorimetricData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -843,7 +844,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -853,7 +854,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -1062,6 +1063,7 @@ type GeometricData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1176,6 +1178,7 @@ type HygrothermalData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1283,6 +1286,7 @@ type LifeCycleData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1410,6 +1414,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1477,6 +1482,7 @@ type PhotovoltaicData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1868,6 +1874,7 @@ interface Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -2078,7 +2085,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2088,7 +2095,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -2315,7 +2322,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2325,7 +2332,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -2409,7 +2416,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2419,7 +2426,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -3227,7 +3234,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -3237,7 +3244,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -3618,7 +3625,10 @@ enum ApplyPolicy { enum CalculateMethodErrorCode { UNKNOWN_METHOD UNKNOWN_DATABASE - DATA_QUERY_FAILED + UNKNOWN_DATA + UNSUPPORTED_DATA_FORMAT + HASH_VALUE_CALCULATION_FAILED + HASH_VALUE_MISMATHCH } enum CalorimetricObserver { @@ -3872,6 +3882,12 @@ enum PublishDataErrorCode { RESOURCES_WITHOUT_FILE } +enum PublishingState { + PENDING + PUBLISHED + RETRACTED +} + enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED diff --git a/frontend/type-defs.graphqls b/frontend/type-defs.graphqls index a88c862d..751e5e99 100644 --- a/frontend/type-defs.graphqls +++ b/frontend/type-defs.graphqls @@ -567,6 +567,7 @@ type CalorimetricData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -843,7 +844,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -853,7 +854,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -1062,6 +1063,7 @@ type GeometricData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1176,6 +1178,7 @@ type HygrothermalData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1283,6 +1286,7 @@ type LifeCycleData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1410,6 +1414,7 @@ type OpticalData implements Node & Data { nearnormalHemisphericalSolarTransmittances: [Float!]! nearnormalHemisphericalVisibleReflectances: [Float!]! nearnormalHemisphericalVisibleTransmittances: [Float!]! + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1477,6 +1482,7 @@ type PhotovoltaicData implements Node & Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources( order: [GetHttpsResourceSortInput!] where: GetHttpsResourcePropositionInput @@ -1868,6 +1874,7 @@ interface Data { kind: DataKind! locale: Locale! name: String + publishingState: PublishingState! resources: [GetHttpsResource!]! resourceTree: GetHttpsResourceTree! timestamp: DateTime! @@ -2078,7 +2085,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2088,7 +2095,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -2315,7 +2322,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2325,7 +2332,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -2409,7 +2416,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -2419,7 +2426,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -3227,7 +3234,7 @@ and `false` in the 'or' case, and analogously for institution and application access policies. In particular, a data access policy with the combinator 'all' and empty user, -institution, and application policies allows access to anyone, also anonymous +institution, and application policies allows access to everyone, also anonymous access. And one with the combinator 'or' and empty policies allows access to nobody, no matter if authenticated or not. @@ -3237,7 +3244,7 @@ global if this field is `null`. The global and individual policies are combined conjunctively, meaning that for access both need to allow access. There are mutations to -* reset a data access policy to its original state, in which anyone is allowed +* reset a data access policy to its original state, in which everyone is allowed access; * configure the logical combinator of a data access policy (as explained above); * clear user, institution, and applicatoin policies of a data access policy; @@ -3618,7 +3625,10 @@ enum ApplyPolicy { enum CalculateMethodErrorCode { UNKNOWN_METHOD UNKNOWN_DATABASE - DATA_QUERY_FAILED + UNKNOWN_DATA + UNSUPPORTED_DATA_FORMAT + HASH_VALUE_CALCULATION_FAILED + HASH_VALUE_MISMATHCH } enum CalorimetricObserver { @@ -3872,6 +3882,12 @@ enum PublishDataErrorCode { RESOURCES_WITHOUT_FILE } +enum PublishingState { + PENDING + PUBLISHED + RETRACTED +} + enum RecomputeGetHttpsResourceHashValuesErrorCode { UNKNOWN UNAUTHORIZED From bcf86c9ae50fc551391b5dec383ce7c535ac4882 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 12:02:32 +0200 Subject: [PATCH 871/878] Fix the dotenv linter used to lint deployments --- deploy.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy.mk b/deploy.mk index dd74e59a..11657193 100755 --- a/deploy.mk +++ b/deploy.mk @@ -16,7 +16,7 @@ dotenv_linter = \ --volume "$(shell pwd):/mnt:ro" \ --pull "always" \ --quiet \ - dotenvlinter/dotenv-linter:latest + dotenvlinter/dotenv-linter:4.0.0 # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help From c48ef841650a24d047ccc0b15e592977fa1536cb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 14:04:10 +0200 Subject: [PATCH 872/878] Increase max-allowed GraphQL costs (again) --- one issue is that the default number of edges returned by paginated queries is 100, which results in high costs quickly, even for queries with actually only a handful of edges --- backend/src/Configuration/GraphQlConfiguration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/Configuration/GraphQlConfiguration.cs b/backend/src/Configuration/GraphQlConfiguration.cs index 3aaa0aa9..e3cd29c2 100644 --- a/backend/src/Configuration/GraphQlConfiguration.cs +++ b/backend/src/Configuration/GraphQlConfiguration.cs @@ -134,8 +134,8 @@ IWebHostEnvironment environment ) .ModifyCostOptions(_ => { - _.MaxFieldCost = 20000; - _.MaxTypeCost = 20000; + _.MaxFieldCost = 40000; + _.MaxTypeCost = 40000; } ) // Configure From 52f4fbb874c5226f77fdd8023b07097f9244677a Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 17:27:32 +0200 Subject: [PATCH 873/878] Backup files such that there is at most one copy of each file using the idea of content-addressable storage --- .gitignore | 1 + database.mk | 57 +++++++++++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index b47ab214..a4ad2604 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ coverage *.bak /backup /backups +/.content-addressable-storage # Image Diagrams /diagrams/*.png diff --git a/database.mk b/database.mk index 9e454f8c..0b80f6fa 100755 --- a/database.mk +++ b/database.mk @@ -10,7 +10,6 @@ MAKEFLAGS += --warn-undefined-variables COMPOSE_BAKE=true dump_archive_name = postgresql_dumpall.gz -files_archive_name = files.gz # Taken from https://www.client9.com/self-documenting-makefiles/ help : ## Print this help @@ -100,8 +99,10 @@ migrate : ## Migrate database by running the idempotent SQL script ./backend/src # Backup files with `tar` and `gzip` as suggested in https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes # We could have used `docker cp` as explained in https://docs.docker.com/engine/reference/commandline/cp/ backup : DIR = ./backup -backup : ## Backup database and related data to directory with absolute path `${DIR}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S")` +backup : CONTENT_ADDRESSABLE_STORAGE = ./.content-addressable-storage +backup : ## Backup database and related data to directory with absolute path `${DIR}` storing files across multiple backups content-addressed in the directory `${CONTENT_ADDRESSABLE_STORAGE}`, for example, `./database.mk backup DIR=/app/data/backups/$(date +"%Y-%m-%d_%H_%M_%S") CONTENT_ADDRESSABLE_STORAGE=/app/data/backups/.content-addressable-storage` mkdir --parents "${DIR}" + mkdir --parents "${CONTENT_ADDRESSABLE_STORAGE}" docker compose up \ --no-build \ --no-recreate \ @@ -122,18 +123,33 @@ backup : ## Backup database and related data to directory with absolute path `${ --no-deps \ --no-TTY \ --volume "${DIR}":/backup \ + --volume "${CONTENT_ADDRESSABLE_STORAGE}":/.content-addressable-storage \ backend \ - tar \ - --verbose \ - --create \ - --gzip \ - --file="/backup/${files_archive_name}" \ - --directory=/app \ - ./files + bash -o errexit -o errtrace -o nounset -o pipefail -c ' \ + if [[ "${ENVIRONMENT}" == "development" ]]; then \ + cd /app/src/files ; \ + else \ + cd /app/files ; \ + fi ; \ + mkdir --parents /backup/files ; \ + find . -type f -printf "%f\n" | while read -r original_file_name; do \ + echo "Backing up file: $${original_file_name}" ; \ + hash=$$(sha256sum ./"$${original_file_name}" | cut --delimiter=" " --fields=1) ; \ + content_addressed_file_path="/.content-addressable-storage/$${hash}" ; \ + if [[ ! -f "$${content_addressed_file_path}" ]]; then \ + cp "$${original_file_name}" "$${content_addressed_file_path}" ; \ + echo "Stored new content-addressed file: $${content_addressed_file_path}" ; \ + else \ + echo "File already exists in content-addressable storage: $${content_addressed_file_path}" ; \ + fi ; \ + ln --symbolic "$${content_addressed_file_path}" /backup/files/"$${original_file_name}" ; \ + done \ + ' .PHONY : backup restore : DIR = ./backup -restore : ## Restore database and related data from directory with absolute path `${DIR}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35` +restore : CONTENT_ADDRESSABLE_STORAGE = ./.content-addressable-storage +restore : ## Restore database and related data from directory with absolute path `${DIR}` and content-addressable storage directory `${CONTENT_ADDRESSABLE_STORAGE}` (dropping and recreating the database and clearing related files before to start cleanly), for example, `./database.mk restore DIR=/app/data/backups/2021-04-22_15_43_35 CONTENT_ADDRESSABLE_STORAGE=/app/data/backups/.content-addressable-storage` docker compose stop \ backend docker compose up \ @@ -169,25 +185,24 @@ restore : ## Restore database and related data from directory with absolute path --no-deps \ --no-TTY \ --volume "${DIR}":/backup \ + --volume "${CONTENT_ADDRESSABLE_STORAGE}":/.content-addressable-storage \ backend \ - bash -o errexit -o errtrace -o nounset -o pipefail -c " \ + bash -o errexit -o errtrace -o nounset -o pipefail -c ' \ if [[ "${ENVIRONMENT}" == "development" ]]; then \ cd /app/src/files ; \ else \ cd /app/files ; \ - fi \ - && rm \ + fi ; \ + rm \ --recursive \ --force \ --dir \ - * \ - && tar \ - --verbose \ - --extract \ - --gunzip \ - --strip-components=2 \ - --file='/backup/${files_archive_name}' \ - " + * ; \ + find /backup/files -type l -printf "%f\n" | while read -r backup_symlink_name; do \ + echo "Restoring file: $${backup_symlink_name}" from content-addressable storage $$(readlink --canonicalize /backup/files/$${backup_symlink_name}) ; \ + cp /backup/files/"$${backup_symlink_name}" ./"$${backup_symlink_name}" ; \ + done \ + ' docker compose start \ backend .PHONY : restore From d61805173df3ee4ccbcf084bd7ec7eebcdf28623 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 17:39:32 +0200 Subject: [PATCH 874/878] Use the latest file extension to store resource content and for the file name when downloading it --- backend/src/Controllers/GetHttpsResourcesController.cs | 1 + backend/src/Data/GetHttpsResource.cs | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/src/Controllers/GetHttpsResourcesController.cs b/backend/src/Controllers/GetHttpsResourcesController.cs index c063bade..faa45193 100644 --- a/backend/src/Controllers/GetHttpsResourcesController.cs +++ b/backend/src/Controllers/GetHttpsResourcesController.cs @@ -212,6 +212,7 @@ public async Task Get( instance: HttpContext.Request.Path ); } + getHttpsResource.UpdateFileExtension(dataFormat.Extension); return PhysicalFile( physicalPath: getHttpsResource.AbsoluteFilePath, contentType: dataFormat.MediaType, diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index 6d696ff9..bfed8500 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -235,7 +235,11 @@ public void UpdateFileExtension(string? fileExtension) { var oldFilePath = FilePath; FileExtension = fileExtension; - File.Move(oldFilePath, FilePath); + File.Move( + oldFilePath, + FilePath, + overwrite: true + ); } public bool DoesFileExist() From 5587519928cac523a69cdfeac9a44d1df2e70883 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 18:36:20 +0200 Subject: [PATCH 875/878] Remove `--detach` from `git switch` when used with a release tag (it does not work) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c6927384..ce65a2da 100644 --- a/README.md +++ b/README.md @@ -387,7 +387,7 @@ and the pages following it. 1. Switch to the Git branch, tag, or commit you want to deploy by either running `git switch ${BRANCH}`, - `git switch --detach release/${TAG}`, or + `git switch release/${TAG}`, or `git switch --detach ${COMMIT_HASH}`, where, for example, `${BRANCH}` is `develop` or `${TAG}` is `v1.0.0` or `${COMMIT_HASH}` is `5e14d7d0858f26c00c82ab9c248cd750606a24b6`. @@ -411,7 +411,7 @@ and the pages following it. a new release" on [Releases](https://github.com/building-envelope-data/database/releases). 1. Fetch the release branch by running `git fetch` and switch to it by running - `git switch --detach release/v*.*.*`, where `*.*.*` is the version. + `git switch release/v*.*.*`, where `*.*.*` is the version. 1. If the databases have not diverged, then apply pending migrations with `./database.mk migrate`. Otherwise, recreate the database by running `./database.mk drop create migrate`. @@ -423,7 +423,7 @@ and the pages following it. ### Deploying a release 1. Fetch the release branch by running `git fetch` and switch to it by running - `git switch --detach release/${TAG}`, where `${TAG}` is the release tag to + `git switch release/${TAG}`, where `${TAG}` is the release tag to be deployed, for example, `v1.0.0`. 1. Build and push the Docker images for the services `frontend` and `backend` by running `./forge.mk all USER=cloud HOST=${IP}`, where `${IP}` is the From e65d36e6aed713fbbbd449c687f3f6069ca2d3fb Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 18:55:51 +0200 Subject: [PATCH 876/878] Defer deletion triggers of entities that can only be deleted together until end of transaction (among other things, it repairs the mutation `deleteData`) --- backend/src/Data/ApplicationDbContext.cs | 10 +- backend/src/Data/GetHttpsResource.cs | 2 +- ...dSolarScreenToComponentSubtype.Designer.cs | 2 +- ...esThatCanOnlyBeDeletedTogether.Designer.cs | 3556 +++++++++++++++++ ...ForEntitiesThatCanOnlyBeDeletedTogether.cs | 34 + .../ApplicationDbContextModelSnapshot.cs | 6 +- backend/src/Migrations/migrate.sql | 100 + ...orEntitiesThatCanOnlyBeDeletedTogether.sql | 100 + ...70958_AddSolarScreenToComponentSubtype.sql | 94 + 9 files changed, 3897 insertions(+), 7 deletions(-) create mode 100644 backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.Designer.cs create mode 100644 backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.cs create mode 100644 backend/src/Migrations/migrate_from_20260626170958_AddSolarScreenToComponentSubtype_to_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.sql create mode 100644 backend/src/Migrations/rollback_from_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether_to_20260626170958_AddSolarScreenToComponentSubtype.sql diff --git a/backend/src/Data/ApplicationDbContext.cs b/backend/src/Data/ApplicationDbContext.cs index 53266231..442da3fa 100644 --- a/backend/src/Data/ApplicationDbContext.cs +++ b/backend/src/Data/ApplicationDbContext.cs @@ -365,8 +365,11 @@ IF COALESCE({string.Join(", ", GetHttpsResource.DataIdFieldAndDataTableNames.Sel ) ) ); + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...`. builder - .BeforeDelete(trigger => trigger + .AfterDelete(trigger => trigger .SetTriggerName(GetHttpsResource.RootCanOnlyBeDeletedAlongsideItsDataTriggerName) .Action(action => action .ExecuteRawSql( @@ -792,8 +795,11 @@ IF COALESCE({string.Join(", ", DataAccessPolicy.DataIdFieldAndDataTableNames.Sel ) ) ); + // In the generated migration turn `CREATE TRIGGER` into `CREATE + // CONSTRAINT TRIGGER` and add `DEFERRABLE INITIALLY DEFERRED` before + // `FOR EACH ...`. builder - .BeforeDelete(trigger => trigger + .AfterDelete(trigger => trigger .SetTriggerName(DataAccessPolicy.CanOnlyBeDeletedAlongsideCorrespondingDataTriggerName) .Action(action => action .ExecuteRawSql( diff --git a/backend/src/Data/GetHttpsResource.cs b/backend/src/Data/GetHttpsResource.cs index bfed8500..77988749 100644 --- a/backend/src/Data/GetHttpsResource.cs +++ b/backend/src/Data/GetHttpsResource.cs @@ -20,7 +20,7 @@ public sealed class GetHttpsResource public const string DataIdsMustMatchTriggerName = $"{TableName}_data_ids_must_match"; public const string DataIdCannotChangeTriggerName = $"{TableName}_data_id_cannot_change"; - public const string RootCanOnlyBeDeletedAlongsideItsDataTriggerName = $"{TableName}_root_can_only_be_deleted_alongside_its_data"; + public const string RootCanOnlyBeDeletedAlongsideItsDataTriggerName = $"resource_root_can_only_deleted_alongside_its_data"; public static readonly ImmutableArray TriggerNames = [ DataIdsMustMatchTriggerName, DataIdCannotChangeTriggerName, diff --git a/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs index 4739056b..933d836c 100644 --- a/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs +++ b/backend/src/Migrations/20260626170958_AddSolarScreenToComponentSubtype.Designer.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; diff --git a/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.Designer.cs b/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.Designer.cs new file mode 100644 index 00000000..169a2496 --- /dev/null +++ b/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.Designer.cs @@ -0,0 +1,3556 @@ +// +using System; +using System.Text.Json; +using Database.Data; +using Database.Enumerations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NodaTime; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Database.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether")] + partial class DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("database") + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "calorimetric_observer", new[] { "ten_degrees", "two_degrees" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "coated_side", new[] { "both", "neither", "non_prime", "not_applicable", "prime", "unknown" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "data_kind", new[] { "calorimetric_data", "geometric_data", "hygrothermal_data", "life_cycle_data", "optical_data", "photovoltaic_data" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "illuminant", new[] { "a", "d65" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "logical_combinator", new[] { "all", "some" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_subtype", new[] { "acid_etched_glass", "applied_film", "cellular_shade", "chromogenic", "coated", "coating", "diffusing_shade", "embedded_coating", "film", "fritted_glass", "interlayer", "laminate", "monolithic", "perforated_screen", "pleated_shade", "roller_shade", "roman_shade", "sandblasted_glass", "shade_material", "solar_screen", "venetian_blind", "vertical_louver", "woven_shade" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "optical_component_type", new[] { "glazing", "shading" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "publishing_state", new[] { "pending", "published", "retracted" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "database", "standardizer", new[] { "aerc", "agi", "ashrae", "breeam", "bs", "bsi", "cen", "cie", "dgnb", "din", "dvwg", "iec", "ies", "ift", "iso", "jis", "leed", "nfrc", "riba", "ul", "unece", "vdi", "vff", "well" }); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "pgcrypto"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("Combinator") + .HasColumnType("database.logical_combinator"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId") + .IsUnique(); + + NpgsqlIndexBuilderExtensions.AreNullsDistinct(b.HasIndex("CalorimetricDataId", "GeometricDataId", "HygrothermalDataId", "LifeCycleDataId", "OpticalDataId", "PhotovoltaicDataId"), false); + + b.ToTable("data_access_policy", "database", t => + { + t.HasTrigger("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted"); + + t.HasTrigger("data_access_policy_can_only_be_deleted_after_data"); + + t.HasTrigger("data_access_policy_data_id_cannot_change"); + + t.HasTrigger("data_access_policy_global_policy_cannot_be_deleted"); + + t.HasCheckConstraint("CK_DataAccessPolicy_At_Most_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") <= 1"); + }); + + b + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data AFTER DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("InstitutionId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "InstitutionId") + .IsUnique(); + + b.ToTable("institution_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ClientId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "ClientId") + .IsUnique(); + + b.ToTable("open_id_connect_application_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataAccessPolicyId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.HasIndex("DataAccessPolicyId", "UserId") + .IsUnique(); + + b.ToTable("user_access_policy", "database"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("GValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("UValues") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("calorimetric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary"); + + t.HasTrigger("calorimetric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_calorimetric_data_access_policy_if_necessary"); + }); + + b + .HasAnnotation("LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"CalorimetricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_calorimetric_data_assert_existence_of_root_resource\"();") + .HasAnnotation("LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"CalorimetricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"CalorimetricDataId\") WHERE \"CalorimetricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"calorimetric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_calorimetric_data_access_policy_if_necessary\"();"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("Heights") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.PrimitiveCollection("Thicknesses") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.PrimitiveCollection("Widths") + .IsRequired() + .HasColumnType("double precision[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("geometric_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_geometric_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_geometric_data_access_policy_if_necessary"); + + t.HasTrigger("geometric_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_geometric_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_geometric_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"GeometricDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"GeometricDataId\") WHERE \"GeometricDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_geometric_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_geometric_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_geometric_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_geometric_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_geometric_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"GeometricDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_geometric_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_geometric_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"geometric_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_geometric_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("DataFormatId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("FileExtension") + .HasColumnType("text"); + + b.Property("GeometricDataId") + .HasColumnType("uuid"); + + b.Property("HashValue") + .IsRequired() + .HasColumnType("text"); + + b.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b.Property("OpticalDataId") + .HasColumnType("uuid"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CalorimetricDataId") + .IsUnique() + .HasFilter("\"CalorimetricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("GeometricDataId") + .IsUnique() + .HasFilter("\"GeometricDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("HygrothermalDataId") + .IsUnique() + .HasFilter("\"HygrothermalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("LifeCycleDataId") + .IsUnique() + .HasFilter("\"LifeCycleDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("OpticalDataId") + .IsUnique() + .HasFilter("\"OpticalDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("ParentId"); + + b.HasIndex("PhotovoltaicDataId") + .IsUnique() + .HasFilter("\"PhotovoltaicDataId\" IS NOT NULL AND \"ParentId\" IS NULL"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("get_https_resource", "database", t => + { + t.HasTrigger("LC_TRIGGER_get_https_resource_data_id_cannot_change"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_data_ids_must_match"); + + t.HasTrigger("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasTrigger("get_https_resource_data_id_cannot_change"); + + t.HasTrigger("get_https_resource_data_ids_must_match"); + + t.HasTrigger("get_https_resource_root_can_only_be_deleted_alongside_its_data"); + + t.HasCheckConstraint("CK_GetHttpsResource_Exactly_One_Data_Set", "NUM_NONNULLS(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = 1"); + + t.HasCheckConstraint("CK_GetHttpsResource_Root_Or_Child", "(\"ParentId\" IS NULL AND \"AppliedConversionMethod_MethodId\" IS NULL)\nOR (\"ParentId\" IS NOT NULL AND \"AppliedConversionMethod_MethodId\" IS NOT NULL)"); + }); + + b + .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data AFTER DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("hygrothermal_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_hygrothermal_data_access_policy_if_necessary"); + + t.HasTrigger("hygrothermal_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"HygrothermalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"HygrothermalDataId\") WHERE \"HygrothermalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_hygrothermal_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"HygrothermalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"hygrothermal_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_hygrothermal_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("lifeCycle_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_life_cycle_data_access_policy_if_necessary"); + + t.HasTrigger("life_cycle_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"LifeCycleDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"LifeCycleDataId\") WHERE \"LifeCycleDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_life_cycle_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"LifeCycleDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"lifeCycle_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_life_cycle_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CoatedSide") + .HasColumnType("database.coated_side"); + + b.PrimitiveCollection("ColorRenderingIndices") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.PrimitiveCollection("InfraredEmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalSolarTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleReflectances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.PrimitiveCollection("NearnormalHemisphericalVisibleTransmittances") + .IsRequired() + .HasColumnType("double precision[]"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("Subtype") + .HasColumnType("database.optical_component_subtype"); + + b.Property("Type") + .HasColumnType("database.optical_component_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("optical_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_optical_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_optical_data_access_policy_if_necessary"); + + t.HasTrigger("optical_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_optical_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_optical_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"OpticalDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"OpticalDataId\") WHERE \"OpticalDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_optical_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_optical_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_optical_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_optical_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_optical_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"OpticalDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_optical_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_optical_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"optical_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_optical_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("ComponentId") + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreatorId") + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Locale") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("PublishingState") + .HasColumnType("database.publishing_state"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.PrimitiveCollection("Warnings") + .IsRequired() + .HasColumnType("text[]"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("photovoltaic_data", "database", t => + { + t.HasTrigger("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource"); + + t.HasTrigger("create_photovoltaic_data_access_policy_if_necessary"); + + t.HasTrigger("photovoltaic_data_assert_existence_of_root_resource"); + }); + + b + .HasAnnotation("LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary", "CREATE FUNCTION \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"() RETURNS trigger as $LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$\r\nBEGIN\r\n INSERT INTO database.\"data_access_policy\"\n(\"PhotovoltaicDataId\", \"Combinator\")\nVALUES (NEW.\"Id\", 'all')\nON CONFLICT (\"PhotovoltaicDataId\") WHERE \"PhotovoltaicDataId\" IS NOT NULL DO NOTHING;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_create_photovoltaic_data_access_policy_if_necessary\"();") + .HasAnnotation("LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource", "CREATE FUNCTION \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"() RETURNS trigger as $LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$\r\nBEGIN\r\n IF NOT EXISTS (\n SELECT 1 FROM database.\"get_https_resource\"\n WHERE \"ParentId\" IS NULL\n AND \"PhotovoltaicDataId\" = NEW.\"Id\"\n)\nTHEN\n RAISE EXCEPTION 'You cannot insert data without also inserting the corresponding root resource in the same transaction.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource AFTER INSERT\r\nON \"database\".\"photovoltaic_data\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_photovoltaic_data_assert_existence_of_root_resource\"();"); + }); + + modelBuilder.Entity("Database.Data.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasDefaultValueSql("uuidv7()"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt", "Id") + .IsUnique(); + + b.ToTable("user", "database"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("DataProtectionKeys", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ClientId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ClientSecret") + .HasColumnType("text"); + + b.Property("ClientType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("ConsentType") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("JsonWebKeySet") + .HasColumnType("text"); + + b.Property("Permissions") + .HasColumnType("text"); + + b.Property("PostLogoutRedirectUris") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedirectUris") + .HasColumnType("text"); + + b.Property("Requirements") + .HasColumnType("text"); + + b.Property("Settings") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ClientId") + .IsUnique(); + + b.ToTable("OpenIddictApplications", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Scopes") + .HasColumnType("text"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictAuthorizations", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Descriptions") + .HasColumnType("text"); + + b.Property("DisplayName") + .HasColumnType("text"); + + b.Property("DisplayNames") + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("Resources") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("OpenIddictScopes", "database"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("text"); + + b.Property("ApplicationId") + .HasColumnType("text"); + + b.Property("AuthorizationId") + .HasColumnType("text"); + + b.Property("ConcurrencyToken") + .IsConcurrencyToken() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("CreationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ExpirationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Payload") + .HasColumnType("text"); + + b.Property("Properties") + .HasColumnType("text"); + + b.Property("RedemptionDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReferenceId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("Status") + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("Subject") + .HasMaxLength(400) + .HasColumnType("character varying(400)"); + + b.Property("Type") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("Id"); + + b.HasIndex("AuthorizationId"); + + b.HasIndex("ReferenceId") + .IsUnique(); + + b.HasIndex("ApplicationId", "Status", "Subject", "Type"); + + b.ToTable("OpenIddictTokens", "database"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithOne("AccessPolicy") + .HasForeignKey("Database.Data.AccessPolicies.DataAccessPolicy", "PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.InstitutionAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("InstitutionAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("InstitutionAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("InstitutionAccessPolicyId"); + + b1.ToTable("institution_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("InstitutionAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.OpenIdConnectApplicationAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("OpenIdConnectApplicationAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("OpenIdConnectApplicationAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("OpenIdConnectApplicationAccessPolicyId"); + + b1.ToTable("open_id_connect_application_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("OpenIdConnectApplicationAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.UserAccessPolicy", b => + { + b.HasOne("Database.Data.AccessPolicies.DataAccessPolicy", "DataAccessPolicy") + .WithMany("UserAccessPolicies") + .HasForeignKey("DataAccessPolicyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("Database.Data.AccessPolicies.CountSinceTime", "AccessCountSinceStartTime", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("AccessCount") + .HasColumnType("bigint"); + + b1.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.OwnsOne("Database.Data.AccessPolicies.UpperLimitPerDuration", "UpperAccessLimitPerTimeDuration", b1 => + { + b1.Property("UserAccessPolicyId") + .HasColumnType("uuid"); + + b1.Property("Duration") + .HasColumnType("interval"); + + b1.Property("UpperLimit") + .HasColumnType("bigint"); + + b1.HasKey("UserAccessPolicyId"); + + b1.ToTable("user_access_policy", "database"); + + b1.WithOwner() + .HasForeignKey("UserAccessPolicyId"); + }); + + b.Navigation("AccessCountSinceStartTime"); + + b.Navigation("DataAccessPolicy"); + + b.Navigation("UpperAccessLimitPerTimeDuration"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodCalorimetricDataId", "Id"); + + b2.ToTable("calorimetric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodCalorimetricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + + b3.ToTable("calorimetric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodCalorimetricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId"); + + b1.ToTable("calorimetric_data", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("CalorimetricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("CalorimetricDataId", "Id"); + + b1.ToTable("calorimetric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("CalorimetricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.ToTable("calorimetric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalCalorimetricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("calorimetric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalCalorimetricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalCalorimetricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("calorimetric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalCalorimetricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodGeometricDataId", "Id"); + + b2.ToTable("geometric_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodGeometricDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + + b3.ToTable("geometric_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodGeometricDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId"); + + b1.ToTable("geometric_data", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("GeometricDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("GeometricDataId", "Id"); + + b1.ToTable("geometric_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("GeometricDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.ToTable("geometric_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalGeometricDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.ToTable("geometric_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalGeometricDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalGeometricDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("geometric_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalGeometricDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.HasOne("Database.Data.CalorimetricData", "CalorimetricData") + .WithMany("Resources") + .HasForeignKey("CalorimetricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GeometricData", "GeometricData") + .WithMany("Resources") + .HasForeignKey("GeometricDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.HygrothermalData", "HygrothermalData") + .WithMany("Resources") + .HasForeignKey("HygrothermalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.LifeCycleData", "LifeCycleData") + .WithMany("Resources") + .HasForeignKey("LifeCycleDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.OpticalData", "OpticalData") + .WithMany("Resources") + .HasForeignKey("OpticalDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Database.Data.GetHttpsResource", "Parent") + .WithMany("Children") + .HasForeignKey("ParentId"); + + b.HasOne("Database.Data.PhotovoltaicData", "PhotovoltaicData") + .WithMany("Resources") + .HasForeignKey("PhotovoltaicDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.OwnsMany("Database.Data.FileMetaInformation", "ArchivedFilesMetaInformation", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("DataFormatId") + .HasColumnType("uuid"); + + b1.PrimitiveCollection("Path") + .IsRequired() + .HasColumnType("text[]"); + + b1.HasKey("GetHttpsResourceId", "Id"); + + b1.ToTable("FileMetaInformation", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + }); + + b.OwnsOne("Database.Data.ToTreeVertexAppliedConversionMethod", "AppliedConversionMethod", b1 => + { + b1.Property("GetHttpsResourceId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.Property("SourceName") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("GetHttpsResourceId"); + + b1.ToTable("get_https_resource", "database"); + + b1.WithOwner() + .HasForeignKey("GetHttpsResourceId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("ToTreeVertexAppliedConversionMethodGetHttpsResourceId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId", "Id"); + + b2.ToTable("get_https_resource_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("ToTreeVertexAppliedConversionMethodGetHttpsResourceId"); + }); + + b1.Navigation("Arguments"); + }); + + b.Navigation("AppliedConversionMethod"); + + b.Navigation("ArchivedFilesMetaInformation"); + + b.Navigation("CalorimetricData"); + + b.Navigation("GeometricData"); + + b.Navigation("HygrothermalData"); + + b.Navigation("LifeCycleData"); + + b.Navigation("OpticalData"); + + b.Navigation("Parent"); + + b.Navigation("PhotovoltaicData"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodHygrothermalDataId", "Id"); + + b2.ToTable("hygrothermal_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodHygrothermalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + + b3.ToTable("hygrothermal_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodHygrothermalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId"); + + b1.ToTable("hygrothermal_data", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("HygrothermalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("HygrothermalDataId", "Id"); + + b1.ToTable("hygrothermal_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("HygrothermalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.ToTable("hygrothermal_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalHygrothermalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("hygrothermal_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalHygrothermalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalHygrothermalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("hygrothermal_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalHygrothermalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodLifeCycleDataId", "Id"); + + b2.ToTable("lifeCycle_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodLifeCycleDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + + b3.ToTable("lifeCycle_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodLifeCycleDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId"); + + b1.ToTable("lifeCycle_data", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("LifeCycleDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("LifeCycleDataId", "Id"); + + b1.ToTable("lifeCycle_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("LifeCycleDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.ToTable("lifeCycle_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalLifeCycleDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.ToTable("lifeCycle_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalLifeCycleDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalLifeCycleDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("lifeCycle_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalLifeCycleDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.OwnsMany("Database.Data.CielabColor", "CielabColors", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("AStar") + .HasColumnType("double precision"); + + b1.Property("BStar") + .HasColumnType("double precision"); + + b1.Property("Illuminant") + .HasColumnType("database.illuminant"); + + b1.Property("LStar") + .HasColumnType("double precision"); + + b1.Property("Observer") + .HasColumnType("database.calorimetric_observer"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("CielabColor", "database", t => + { + t.HasCheckConstraint("CK_OpticalData_CielabColors_LStar", "\"LStar\" >= 0.0 AND \"LStar\" <= 100.0"); + }); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodOpticalDataId", "Id"); + + b2.ToTable("optical_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodOpticalDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + + b3.ToTable("optical_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodOpticalDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId"); + + b1.ToTable("optical_data", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("OpticalDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("OpticalDataId", "Id"); + + b1.ToTable("optical_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("OpticalDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.ToTable("optical_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalOpticalDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.ToTable("optical_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalOpticalDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalOpticalDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("optical_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalOpticalDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + + b.Navigation("CielabColors"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.OwnsOne("Database.Data.AppliedMethod", "AppliedMethod", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("MethodId") + .HasColumnType("uuid"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsMany("Database.Data.NamedMethodArgument", "Arguments", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.Property("Value") + .HasColumnType("jsonb"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Arguments", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + }); + + b1.OwnsMany("Database.Data.NamedMethodSource", "Sources", b2 => + { + b2.Property("AppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b2.Property("Id")); + + b2.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b2.HasKey("AppliedMethodPhotovoltaicDataId", "Id"); + + b2.ToTable("photovoltaic_data_Sources", "database"); + + b2.WithOwner() + .HasForeignKey("AppliedMethodPhotovoltaicDataId"); + + b2.OwnsOne("Database.Data.CrossDatabaseDataReference", "Value", b3 => + { + b3.Property("NamedMethodSourceAppliedMethodPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("NamedMethodSourceId") + .HasColumnType("integer"); + + b3.Property("DataId") + .HasColumnType("uuid"); + + b3.Property("DataKind") + .HasColumnType("database.data_kind"); + + b3.Property("DataTimestamp") + .HasColumnType("timestamp with time zone"); + + b3.Property("DatabaseId") + .HasColumnType("uuid"); + + b3.HasKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + + b3.ToTable("photovoltaic_data_Sources", "database"); + + b3.WithOwner() + .HasForeignKey("NamedMethodSourceAppliedMethodPhotovoltaicDataId", "NamedMethodSourceId"); + }); + + b2.Navigation("Value") + .IsRequired(); + }); + + b1.Navigation("Arguments"); + + b1.Navigation("Sources"); + }); + + b.OwnsOne("Database.Data.ResponseApproval", "Approval", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId"); + + b1.ToTable("photovoltaic_data", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + }); + + b.OwnsMany("Database.Data.DataApproval", "Approvals", b1 => + { + b1.Property("PhotovoltaicDataId") + .HasColumnType("uuid"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("ApproverId") + .HasColumnType("uuid"); + + b1.Property("KeyFingerprint") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Query") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Signature") + .IsRequired() + .HasColumnType("text"); + + b1.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b1.Property("Variables") + .ValueGeneratedOnAdd() + .HasColumnType("jsonb") + .HasDefaultValueSql("'{}'"); + + b1.HasKey("PhotovoltaicDataId", "Id"); + + b1.ToTable("photovoltaic_data_Approvals", "database"); + + b1.WithOwner() + .HasForeignKey("PhotovoltaicDataId"); + + b1.OwnsOne("Database.Data.Reference", "Statement", b2 => + { + b2.Property("DataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b2.Property("DataApprovalId") + .HasColumnType("integer"); + + b2.Property("Exists") + .HasColumnType("boolean"); + + b2.HasKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.ToTable("photovoltaic_data_Approvals", "database"); + + b2.WithOwner() + .HasForeignKey("DataApprovalPhotovoltaicDataId", "DataApprovalId"); + + b2.OwnsOne("Database.Data.Publication", "Publication", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("ArXiv") + .HasColumnType("text"); + + b3.PrimitiveCollection("Authors") + .HasColumnType("text[]"); + + b3.Property("Doi") + .HasColumnType("text"); + + b3.Property("Exists") + .HasColumnType("boolean"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Urn") + .HasColumnType("text"); + + b3.Property("WebAddress") + .HasColumnType("text"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + }); + + b2.OwnsOne("Database.Data.Standard", "Standard", b3 => + { + b3.Property("ReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b3.Property("ReferenceDataApprovalId") + .HasColumnType("integer"); + + b3.Property("Abstract") + .HasColumnType("text"); + + b3.Property("Locator") + .HasColumnType("text"); + + b3.Property("Section") + .HasColumnType("text"); + + b3.PrimitiveCollection("Standardizers") + .IsRequired() + .HasColumnType("database.standardizer[]"); + + b3.Property("Title") + .HasColumnType("text"); + + b3.Property("Year") + .HasColumnType("integer"); + + b3.HasKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.ToTable("photovoltaic_data_Approvals", "database"); + + b3.WithOwner() + .HasForeignKey("ReferenceDataApprovalPhotovoltaicDataId", "ReferenceDataApprovalId"); + + b3.OwnsOne("Database.Data.Numeration", "Numeration", b4 => + { + b4.Property("StandardReferenceDataApprovalPhotovoltaicDataId") + .HasColumnType("uuid"); + + b4.Property("StandardReferenceDataApprovalId") + .HasColumnType("integer"); + + b4.Property("MainNumber") + .IsRequired() + .HasColumnType("text"); + + b4.Property("Prefix") + .HasColumnType("text"); + + b4.Property("Suffix") + .HasColumnType("text"); + + b4.HasKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + + b4.ToTable("photovoltaic_data_Approvals", "database"); + + b4.WithOwner() + .HasForeignKey("StandardReferenceDataApprovalPhotovoltaicDataId", "StandardReferenceDataApprovalId"); + }); + + b3.Navigation("Numeration") + .IsRequired(); + }); + + b2.Navigation("Publication"); + + b2.Navigation("Standard"); + }); + + b1.Navigation("Statement") + .IsRequired(); + }); + + b.Navigation("AppliedMethod") + .IsRequired(); + + b.Navigation("Approval"); + + b.Navigation("Approvals"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Authorizations") + .HasForeignKey("ApplicationId"); + + b.Navigation("Application"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreToken", b => + { + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", "Application") + .WithMany("Tokens") + .HasForeignKey("ApplicationId"); + + b.HasOne("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", "Authorization") + .WithMany("Tokens") + .HasForeignKey("AuthorizationId"); + + b.Navigation("Application"); + + b.Navigation("Authorization"); + }); + + modelBuilder.Entity("Database.Data.AccessPolicies.DataAccessPolicy", b => + { + b.Navigation("InstitutionAccessPolicies"); + + b.Navigation("OpenIdConnectApplicationAccessPolicies"); + + b.Navigation("UserAccessPolicies"); + }); + + modelBuilder.Entity("Database.Data.CalorimetricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GeometricData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.GetHttpsResource", b => + { + b.Navigation("Children"); + }); + + modelBuilder.Entity("Database.Data.HygrothermalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.LifeCycleData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.OpticalData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("Database.Data.PhotovoltaicData", b => + { + b.Navigation("AccessPolicy"); + + b.Navigation("Resources"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreApplication", b => + { + b.Navigation("Authorizations"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("OpenIddict.EntityFrameworkCore.Models.OpenIddictEntityFrameworkCoreAuthorization", b => + { + b.Navigation("Tokens"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.cs b/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.cs new file mode 100644 index 00000000..c9abea99 --- /dev/null +++ b/backend/src/Migrations/20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.cs @@ -0,0 +1,34 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Database.Migrations +{ + /// + public partial class DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid\"() CASCADE;"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data AFTER DELETE\r\nON \"database\".\"data_access_policy\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE CONSTRAINT TRIGGER LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data AFTER DELETE\r\nON \"database\".\"get_https_resource\"\r\nDEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data\"();"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() CASCADE;"); + + migrationBuilder.Sql("DROP FUNCTION \"database\".\"LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data\"() CASCADE;"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();"); + + migrationBuilder.Sql("CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid\"();"); + } + } +} \ No newline at end of file diff --git a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs index 9002ee18..3ade95b2 100644 --- a/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,4 @@ -// +// using System; using System.Text.Json; using Database.Data; @@ -132,7 +132,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) }); b - .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") + .HasAnnotation("LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$\r\nBEGIN\r\n IF (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\"\n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\"\n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\"\n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\"\n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\"\n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\"\n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data AFTER DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data\"();") .HasAnnotation("LC_TRIGGER_data_access_policy_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a data access policy.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_data_id_cannot_change\"();") .HasAnnotation("LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted", "CREATE FUNCTION \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"() RETURNS trigger as $LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$\r\nBEGIN\r\n IF OLD.\"CalorimetricDataId\" IS NULL AND OLD.\"GeometricDataId\" IS NULL AND OLD.\"HygrothermalDataId\" IS NULL AND OLD.\"LifeCycleDataId\" IS NULL AND OLD.\"OpticalDataId\" IS NULL AND OLD.\"PhotovoltaicDataId\" IS NULL\nTHEN\n RAISE EXCEPTION 'You cannot delete the global data access policy.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted BEFORE DELETE\r\nON \"database\".\"data_access_policy\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_data_access_policy_global_policy_cannot_be_deleted\"();"); }); @@ -528,7 +528,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b .HasAnnotation("LC_TRIGGER_get_https_resource_data_id_cannot_change", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_id_cannot_change$\r\nBEGIN\r\n IF COALESCE(OLD.\"CalorimetricDataId\", OLD.\"GeometricDataId\", OLD.\"HygrothermalDataId\", OLD.\"LifeCycleDataId\", OLD.\"OpticalDataId\", OLD.\"PhotovoltaicDataId\") <> COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\nTHEN\n RAISE EXCEPTION 'You cannot change the data ID of a resource.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_id_cannot_change$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_id_cannot_change BEFORE UPDATE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_id_cannot_change\"();") .HasAnnotation("LC_TRIGGER_get_https_resource_data_ids_must_match", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_data_ids_must_match$\r\nBEGIN\r\n IF NEW.\"ParentId\" IS NOT NULL\n AND (\n SELECT COUNT(\"Id\")\n FROM database.\"get_https_resource\"\n WHERE \"Id\" = NEW.\"ParentId\" AND COALESCE(\"CalorimetricDataId\", \"GeometricDataId\", \"HygrothermalDataId\", \"LifeCycleDataId\", \"OpticalDataId\", \"PhotovoltaicDataId\") = COALESCE(NEW.\"CalorimetricDataId\", NEW.\"GeometricDataId\", NEW.\"HygrothermalDataId\", NEW.\"LifeCycleDataId\", NEW.\"OpticalDataId\", NEW.\"PhotovoltaicDataId\")\n )\n <> 1\nTHEN\n RAISE EXCEPTION 'The new resource must have the same data ID as its parent.';\nEND IF;\r\nRETURN NEW;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_data_ids_must_match$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_data_ids_must_match BEFORE INSERT\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_data_ids_must_match\"();") - .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); + .HasAnnotation("LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data", "CREATE FUNCTION \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$\r\nBEGIN\r\n IF OLD.\"ParentId\" IS NULL AND (\n (OLD.\"CalorimetricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"calorimetric_data\" \n WHERE \"Id\" = OLD.\"CalorimetricDataId\"\n)) OR (OLD.\"GeometricDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"geometric_data\" \n WHERE \"Id\" = OLD.\"GeometricDataId\"\n)) OR (OLD.\"HygrothermalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"hygrothermal_data\" \n WHERE \"Id\" = OLD.\"HygrothermalDataId\"\n)) OR (OLD.\"LifeCycleDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"lifeCycle_data\" \n WHERE \"Id\" = OLD.\"LifeCycleDataId\"\n)) OR (OLD.\"OpticalDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"optical_data\" \n WHERE \"Id\" = OLD.\"OpticalDataId\"\n)) OR (OLD.\"PhotovoltaicDataId\" IS NOT NULL AND EXISTS (\n SELECT 1 FROM database.\"photovoltaic_data\" \n WHERE \"Id\" = OLD.\"PhotovoltaicDataId\"\n))\n) THEN\n RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.';\nEND IF;\r\nRETURN OLD;\r\nEND;\r\n$LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql;\r\nCREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data AFTER DELETE\r\nON \"database\".\"get_https_resource\"\r\nFOR EACH ROW EXECUTE PROCEDURE \"database\".\"LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data\"();"); }); modelBuilder.Entity("Database.Data.HygrothermalData", b => diff --git a/backend/src/Migrations/migrate.sql b/backend/src/Migrations/migrate.sql index 07befcdb..1da1ad46 100644 --- a/backend/src/Migrations/migrate.sql +++ b/backend/src/Migrations/migrate.sql @@ -3516,3 +3516,103 @@ END $EF$; COMMIT; +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data AFTER DELETE + ON "database"."data_access_policy" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data"() RETURNS trigger as $LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$ + BEGIN + IF OLD."ParentId" IS NULL AND ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data AFTER DELETE + ON "database"."get_https_resource" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether', '10.0.9'); + END IF; +END $EF$; + +COMMIT; + diff --git a/backend/src/Migrations/migrate_from_20260626170958_AddSolarScreenToComponentSubtype_to_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.sql b/backend/src/Migrations/migrate_from_20260626170958_AddSolarScreenToComponentSubtype_to_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.sql new file mode 100644 index 00000000..6df48cd2 --- /dev/null +++ b/backend/src/Migrations/migrate_from_20260626170958_AddSolarScreenToComponentSubtype_to_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether.sql @@ -0,0 +1,100 @@ +START TRANSACTION; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongsid"() CASCADE; + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data AFTER DELETE + ON "database"."data_access_policy" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data"() RETURNS trigger as $LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$ + BEGIN + IF OLD."ParentId" IS NULL AND ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data$ LANGUAGE plpgsql; + CREATE CONSTRAINT TRIGGER LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data AFTER DELETE + ON "database"."get_https_resource" + DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_resource_root_can_only_deleted_alongside_its_data"(); + END IF; +END $EF$; + +DO $EF$ +BEGIN + IF NOT EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion") + VALUES ('20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether', '10.0.9'); + END IF; +END $EF$; + +COMMIT; + diff --git a/backend/src/Migrations/rollback_from_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether_to_20260626170958_AddSolarScreenToComponentSubtype.sql b/backend/src/Migrations/rollback_from_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether_to_20260626170958_AddSolarScreenToComponentSubtype.sql new file mode 100644 index 00000000..b5dfa076 --- /dev/null +++ b/backend/src/Migrations/rollback_from_20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether_to_20260626170958_AddSolarScreenToComponentSubtype.sql @@ -0,0 +1,94 @@ +START TRANSACTION; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DROP FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"() CASCADE; + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"() RETURNS trigger as $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ + BEGIN + IF ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a data access policy without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data BEFORE DELETE + ON "database"."data_access_policy" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_data_access_policy_can_only_be_deleted_after_data"(); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + CREATE FUNCTION "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"() RETURNS trigger as $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ + BEGIN + IF OLD."ParentId" IS NULL AND ( + (OLD."CalorimetricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."calorimetric_data" + WHERE "Id" = OLD."CalorimetricDataId" + )) OR (OLD."GeometricDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."geometric_data" + WHERE "Id" = OLD."GeometricDataId" + )) OR (OLD."HygrothermalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."hygrothermal_data" + WHERE "Id" = OLD."HygrothermalDataId" + )) OR (OLD."LifeCycleDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."lifeCycle_data" + WHERE "Id" = OLD."LifeCycleDataId" + )) OR (OLD."OpticalDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."optical_data" + WHERE "Id" = OLD."OpticalDataId" + )) OR (OLD."PhotovoltaicDataId" IS NOT NULL AND EXISTS ( + SELECT 1 FROM database."photovoltaic_data" + WHERE "Id" = OLD."PhotovoltaicDataId" + )) + ) THEN + RAISE EXCEPTION 'You cannot delete a root resource without also deleting the corresponding data in the same transaction.'; + END IF; + RETURN OLD; + END; + $LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data$ LANGUAGE plpgsql; + CREATE TRIGGER LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data BEFORE DELETE + ON "database"."get_https_resource" + FOR EACH ROW EXECUTE PROCEDURE "database"."LC_TRIGGER_get_https_resource_root_can_only_be_deleted_alongside_its_data"(); + END IF; +END $EF$; +DO $EF$ +BEGIN + IF EXISTS(SELECT 1 FROM "__EFMigrationsHistory" WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether') THEN + DELETE FROM "__EFMigrationsHistory" + WHERE "MigrationId" = '20260630163409_DeferDeleteChecksForEntitiesThatCanOnlyBeDeletedTogether'; + END IF; +END $EF$; +COMMIT; + From 4b412a53c367a89cd37139cbfb4434dc4690dd49 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 30 Jun 2026 16:56:36 +0000 Subject: [PATCH 877/878] Prepare release v1.2.0 --- CHANGELOG.md | 29 +++++++++++++++++------------ frontend/package.json | 2 +- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaa6569d..d86082bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v1.2.0] - 2026-06-30 + ### Added -- Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) -- Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) -- Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) -- Login through the OpenId Connect endpoints of the metabase [#44](https://github.com/building-envelope-data/database/pull/44) +- Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) +- Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) +- Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) +- Login through the OpenId Connect endpoints of the metabase [#44](https://github.com/building-envelope-data/database/pull/44) - - - @@ -37,12 +39,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) -- Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) -- Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) -- Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) -- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make manage` and stored in the volume `gnupg`. -- Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. +- Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) +- Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) +- Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) +- Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) +- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make manage` and stored in the volume `gnupg`. +- Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. - - - @@ -93,7 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Remove explicit content-type as it results in requests from banana cake pop responding with 404 in commit [90ee575397544158a603cd7946bd6fec5e4c1407](https://github.com/building-envelope-data/database/commit/90ee575397544158a603cd7946bd6fec5e4c1407) +- Remove explicit content-type as it results in requests from banana cake pop responding with 404 in commit [90ee575397544158a603cd7946bd6fec5e4c1407](https://github.com/building-envelope-data/database/commit/90ee575397544158a603cd7946bd6fec5e4c1407) - - - @@ -121,5 +123,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [v1.0.0] - 2022-02-28 -[Unreleased]: https://github.com/building-envelope-data/database/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/building-envelope-data/database/compare/v1.2.0...HEAD + +[v1.2.0]: https://github.com/building-envelope-data/database/compare/v1.0.0...v1.2.0 + [v1.0.0]: https://github.com/building-envelope-data/database/compare/352f44e492ca66d7835970cdf007b2897e686e30...v1.0.0 diff --git a/frontend/package.json b/frontend/package.json index 11258900..68029a8c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "v2.0.0", + "version": "v1.2.0", "private": true, "author": "Simon Wacker", "license": "MIT", From 03d0cb809434c01196587c5af57034f4600fadb1 Mon Sep 17 00:00:00 2001 From: Simon Wacker Date: Tue, 30 Jun 2026 19:01:10 +0200 Subject: [PATCH 878/878] Prepare changelog for next release --- CHANGELOG.md | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d86082bb..1993b3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [v1.2.0] - 2026-06-30 - ### Added -- Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) -- Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) -- Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) -- Login through the OpenId Connect endpoints of the metabase [#44](https://github.com/building-envelope-data/database/pull/44) +- +- +- +- - - - @@ -39,12 +37,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) -- Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) -- Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) -- Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) -- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make manage` and stored in the volume `gnupg`. -- Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. +- +- +- +- +- +- - - - @@ -95,7 +93,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- Remove explicit content-type as it results in requests from banana cake pop responding with 404 in commit [90ee575397544158a603cd7946bd6fec5e4c1407](https://github.com/building-envelope-data/database/commit/90ee575397544158a603cd7946bd6fec5e4c1407) +- - - - @@ -121,10 +119,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - +## [v1.2.0] - 2026-06-30 + +### Added + +- Add GraphQL query `verificationCode` [5dd0a94bcfbd3de69a3b29f0a07be406617fbf83](https://github.com/building-envelope-data/database/commit/5dd0a94bcfbd3de69a3b29f0a07be406617fbf83) +- Add pages for individual data entries [6e1a5a484b2054e0eca168fe58b4860caef8d872](https://github.com/building-envelope-data/database/commit/6e1a5a484b2054e0eca168fe58b4860caef8d872) +- Enable automatic persisted queries [f1f7a73f3001bc89d429b103b360ccd36ea32e0d](https://github.com/building-envelope-data/database/commit/f1f7a73f3001bc89d429b103b360ccd36ea32e0d) +- Login through the OpenId Connect endpoints of the metabase [#44](https://github.com/building-envelope-data/database/pull/44) + +### Changed + +- Upgrade packages to latest compatible versions in backend and frontend [#29](https://github.com/building-envelope-data/database/pull/29) +- Persist data protection keys in database [bbac9c7e84a21dcc67bc5a9446032b862af51b5b](https://github.com/building-envelope-data/database/commit/bbac9c7e84a21dcc67bc5a9446032b862af51b5b) +- Build Docker images with BuildKit [4ce5fa2c4dbe67de14f5e1c9be22896921349b50](https://github.com/building-envelope-data/database/commit/4ce5fa2c4dbe67de14f5e1c9be22896921349b50) +- Improve OpenId Connect usage and security [#46](https://github.com/building-envelope-data/database/pull/46) +- Do not manage JWT keys and GnuPG keys in files. JWT keys are created and rotated by the C# webserver daily. GnuPG keys are manually managed within the shell `make manage` and stored in the volume `gnupg`. +- Use `forge.mk` to build and push images to the server noting the image tag, which is just the commit hash. On the server use `deploy.mk deploy TARGET=` to deploy backend and frontend based on those images. + +### Fixed + +- Remove explicit content-type as it results in requests from banana cake pop responding with 404 in commit [90ee575397544158a603cd7946bd6fec5e4c1407](https://github.com/building-envelope-data/database/commit/90ee575397544158a603cd7946bd6fec5e4c1407) + ## [v1.0.0] - 2022-02-28 [Unreleased]: https://github.com/building-envelope-data/database/compare/v1.2.0...HEAD - [v1.2.0]: https://github.com/building-envelope-data/database/compare/v1.0.0...v1.2.0 - [v1.0.0]: https://github.com/building-envelope-data/database/compare/352f44e492ca66d7835970cdf007b2897e686e30...v1.0.0